Commit d933cdde authored by Patrick Fenner's avatar Patrick Fenner

initial commit

parents
Copyright (c) 2010 Justin Windle, http://blog.soulwire.co.uk/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
# WTF Engine
Inspired by [WhatTheForkShouldIMakeForDinner.com](http://whattheforkshouldimakefordinner.com/), in 2011 I made [WhatTheForkIsMyMashup.com](http://whattheforkismymashup.com/) and released this simple tool, the [WTFEngine](https://github.com/soulwire/WTFEngine/), for generating similar websites.
It's very simple to use and now allows you to populate it using either vanilla JavaScript objects, JSON files or direct feeds from Google spreadsheets.
## How to use it
To make your own, simply [clone](github-mac://openRepo/https://github.com/soulwire/WTFEngine) or [download](https://github.com/soulwire/WTFEngine/archive/master.zip) this repository and start populating the engine with your content.
You can populate it using one of three different methods. Each is controlled from [`main.js`](https://github.com/soulwire/WTFEngine/blob/master/scripts/main.js) (where you will find commented examples of each technique.)
1. JavaScript Object literal
2. A JSON file
3. A live feed from a Google spreadsheet
If you choose to use a Google spreadsheet, you must [publish it first](https://support.google.com/drive/answer/37579?hl=en) and be sure to manually republish it after editing if you want changes to propagate immediately, otherwise they will take around 15 minutes.
Here's an [example spreadsheet](https://docs.google.com/a/soulwire.co.uk/spreadsheet/ccc?key=0AvG1Hx204EyydF9ub1M2cVJ3Z1VGdDhTSWg0ZV9LNGc), which you can clone and use a base for your data source.
_**Note**: For local testing (when using loaded JSON or Google spreadsheet data), you'll need to serve the site from a local webserver. You can easily do this using tools like [SimpleHTTPServer](http://www.linuxjournal.com/content/tech-tip-really-simple-http-server-python) for Python or [http-server](https://github.com/nodeapps/http-server) for Node._
## How it works
The process is very simple. The WTF Engine takes a sentence template and fills in different types of blanks with different types of words, much like the game [Mad Libs](http://en.wikipedia.org/wiki/Mad_Libs). You can nominate any amount of templates and as many different categories of words or phrases as you like.
For example, here is a basic corpus
{
template: [ "Big @color @animal", "Silly @animal with @color fur" ],
animal: [ "dog", "cat", "rabbit" ],
color: [ "red", "blue", "green", "yellow" ]
};
As you can see, in a template you use the __@__ symbol, followed by the type of word you wish to use to tell the WTF Engine to pick a random word of that type from the corpus and insert it at that point.
## Showcase
I've been pleasantly surprised to find several people using this template to create their own sites. Among them are:
- [WTF is my bot](http://wtfismybot.tech/)
- [WTF is my startup idea? ](http://whattheforkismystartup.tech)
- [Shirt UX Ideas](http://shirtuxideas.com/)
- [Nordic Larp Generator](http://www.messagefromtheinternet.com/nordiclarp/)
- [WTF Are My Weekend Plans](http://www.wtfplans.com/)
- [What The Fork Is My Hydration Strategy](http://theplan.co.uk/hydration/)
- [What The Fork Is Birmingham's Transport Strategy](http://toys.paradisecircus.com/transport/)
- [What's your forking weapon](http://scottyboy76567.github.io/WeaponGenerator/)
- [Who the Fork is my Superhero Character](http://vanor.co.il/heroes/)
- [What the Fork is my Quest](http://whattheforkismyquest.com/)
- [What the Fork is this Japanese Food](http://whattheforkisthisjapanesefood.com/)
- [What is my Pony](http://whoismypony.ponyfinder.net/)
- [Game of Thrones spoiler generator](http://takephive.com/got_spoilergen/)
- [Who the Fork is my D&D Character](http://whotheforkismydndcharacter.com/)
- [RPG Focused Character Idea Generator](http://enklave-23.de/WTF/)
- [Startup Pitch Generator](http://startuppitchperfect.sebastianruder.com/)
- [DevOps Vision generator](http://www.percussiverepair.net/devopsvision/)
- [What the Fork am I Bringing to Burning Man](http://whattheforkamibringingtoburningman.com/)
- [Insult Generator](http://www.omglmaowtf.com/insult-generator)
- [MasterChef Me](http://www.masterchef.me)
- [Bieber Blotter](http://www.linkalope.com/bieber-blotter)
- [What the Fork is my Wearable Strategy](http://whattheforkismywearablestrategy.com/)
- [What the Fork is my Next TV Format](http://www.whattheforkismynexttvformat.com/)
- [What the Fork is my Twitter Bio](http://whattheforkismytwitterbio.com/)
- [What the Fork is my Award Idea](http://whattheforkismyawardidea.com/)
- [What is my SEO Doing](http://www.clicksandclients.com/what-is-my-seo-doing/)
- [Forking Valentines](http://forkingvalentines.com/)
- [Tony Abbott Excuse](http://abbottexcuse.1apps.com/)
- [Why the Fork are you Running Late](http://www.whytheforkareyourunninglate.com/)
- [What the Fork is my Brief](http://www.whattheforkismybrief.com/)
- [Whip-o-matic](http://whipomatic.com/)
- [What the Fork is my SXSW Panel](http://wtfismypanel.com/)
- [Pitchfork Review Generator](http://pitchforkreviewgenerator.com/)
- [Shirt PR Ideas](http://shirtprideas.com/)
- [What the Fork is my Mashup](http://whattheforkismymashup.com/)
If you have made one, or know of any, please [let me know](https://github.com/soulwire/WTFEngine/issues/new).
## Notes
This was written simply as a bit of fun. Zach's site was so popular that it inspired references based on other subjects, such as [What The Fork Is My Social Media Strategy?](http://whattheforkismysocialmediastrategy.com) (and consequently [What The Fork Is My Mashup?](http://whattheforkismymashup.com/)). As far as I am aware, this idea was Zach's alone and so credit to him for the inspiration. As a meme, there are doubtlessly many topics that could do with the WTF treatment; which is why I decided to create this (very) simple platform.
<!doctype html>
<html>
<head>
<title>What The Fork Is&hellip;</title>
<meta name="description" content="A project name idea generator" />
<meta name="author" content="DefProc" />
<link rel="apple-touch-icon" href="images/custom_icon.png"/>
<link rel="shortcut icon" href="images/favicon.png"/>
<link rel="stylesheet" href="styles/main.css" />
</head>
<body>
<div id="wrapper">
<header>
<h1>What The Fork Is&hellip;</h1>
</header>
<article>
<div id="output">
<noscript>
<dl>
<dt>What the fork! You don't have JavaScript enabled!</dt>
<dd>You need to enable it to see all the forking cool shirt on this website...</dd>
</dl>
</noscript>
</div>
<a id="generate" href="#" title="Hit me again!"></a>
</article>
<aside>
<ul id="share">
<li id="share-facebook"><iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwhattheforkismymashup.com&amp;layout=button_count&amp;show_faces=true&amp;width=80&amp;action=like&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe></li>
<li id="share-twitter"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://whattheforkismymashup.com" data-count="horizontal" data-via="soulwire">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></li>
</ul>
</aside>
<footer>
<div id="about">If you make it, make sure you make it <a href="http://www.opensource.org/" target="blank">Open Source</a></div>
<div id="credit">Built with <a href="https://github.com/soulwire/WTFEngine" title="An HTML / CSS / JavaScript template for creating WhatTheForkIsMyMashup.com style web sites" target="_blank">WTF Engine</a> by <a href="http://blog.soulwire.co.uk/" title="Made by Justin Windle @soulwire">Justin</a></div>
<div id="total">There are <span id="templates"></span> and <span id="outcomes"></span> possibilities</div>
</footer>
<img class="loading" src="images/loading.gif">
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/zepto/1.1.2/zepto.min.js"></script>
<script>window.Zepto || document.write('<script src="scripts/vendor/zepto.min.js"><\/script>')</script>
<script src="scripts/wtf.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
{
"heading": [
"This is a forking great project:",
"Check this name out, it's forking brilliant:"
],
"response": [
"That's forking shirt",
"Give me a-forking-nother one",
"Don't really give a fork",
"Nobody's making that shirt"
],
"template": [
"@quantity @gen_opinion @spec_opinion @size @shape @age @colour @nationality @material @noun",
"Silly @noun with @colour fur"
],
"quantity": [
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"a couple",
"a few",
"several",
"many",
"lots of"
],
"gen_opinion": [
"good",
"bad",
"lovely",
"strange",
"beautiful",
"nice",
"brilliant",
"excellent",
"awful",
"important",
"wonderful",
"nasty"
],
"spec_opinion": [
"tasty",
"delicious",
"comfortable",
"uncomfortable",
"clever",
"intelligent",
"friendly"
],
"size": [
"big",
"small",
"medium"
],
"shape": [
"square",
"hexagon",
"rectangle",
"round"
],
"age": [
"old",
"new"
],
"colour": [
"blue",
"red",
"green",
"yellow",
"black",
"white"
],
"nationality": [
"English",
"British",
"Scottish",
"Welsh",
"Irish",
"European"
],
"material": [
"leather",
"wooden",
"rubber"
],
"noun": [
"duck",
"goose",
"fish"
]
}
$(function() {
// Populate using an object literal
// WTF.init({
// heading: [
// "It's a forking",
// "Check this shirt out, a forking"
// ],
// response: [
// "Already forking seen one",
// "Give me a-forking-nother one",
// "Don't really give a fork"
// ],
// template: [
// "Big @color @animal",
// "Silly @animal with @color fur"
// ],
// animal: [
// "dog",
// "cat",
// "rabbit"
// ],
// color: [
// "red",
// "blue",
// "green",
// "yellow"
// ]
// });
// Populate using a JSON file
// WTF.init( 'sample.json' );
// Populate using a Google spreadsheet ID (you must publish it first!)
// @see https://support.google.com/drive/answer/37579?hl=en
WTF.init( '1RF51na8fYGV9jqWdVjEKfeGxp-yYoUOeKwbj6ySmTNE' );
});
This diff is collapsed.
var WTF = (function() {
'use strict';
/*
------------------------------------------------------------
Constants & variables
------------------------------------------------------------
*/
var RE_QUOTE = /\"([^\"]+)\"/gi;
var RE_JSON = /\.json$/i;
var RE_COL = /^gsx\$(.+)$/i;
var RE_KEY = /[a-z0-9_-]{32,}/i;
var DOCS_PATH = "https://spreadsheets.google.com/feeds/list/{key}/od6/public/values?alt=json";
var templates;
var responses;
var headings;
var corpus;
var regex;
var dom;
/*
------------------------------------------------------------
Called once initialisation as finished
------------------------------------------------------------
*/
function start() {
// Copy out templates then remove from corpus
templates = corpus.template;
responses = corpus.response;
headings = corpus.heading;
delete corpus.template;
delete corpus.response;
delete corpus.heading;
// Enable UI and generate first idea
initUI();
buildRexExp();
generate();
$('#templates').html(templates.length + ' template' + (templates.length == 1 ? '' : 's'))
$('#outcomes').html(getTotalNumOptions().toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
}
function getTotalNumOptions() {
var num = 0
for(var i = 0; i < templates.length; i++) {
num += getNumTemplateOptions(templates[i])
}
return num
}
function getNumTemplateOptions( template ) {
var type, iter = 0, // Safety mechanism
item = regex.exec( template ),
copy = cloneCorpus();
var num = 0
while ( item && ++iter < 1000 ) {
type = item[ 0 ].substr(1);
var typeNum = corpus[type].length
if(num == 0) {
num = corpus[type].length
}
else {
num = num * corpus[type].length
}
item = regex.exec( template );
}
return num
}
/*
------------------------------------------------------------
Converts CSV to a regular corpus object
@see sample.json
------------------------------------------------------------
*/
function parseCSV( csv ) {
var i, j, k, n, m, cols, keys = {}, data = {}, rows = csv.split( '\n' );
for ( i = 0, n = rows.length; i < n; i++, j = i - 1 ) {
cols = rows[ i ].replace( RE_QUOTE, escape ).split( ',' );
for ( k = 0, m = cols.length; k < m; k++ ) {
if ( i === 0 ) {
data[ keys[ k ] = cols[ k ].toLowerCase() ] = [];
} else if ( cols[ k ] ) {
data[ keys[ k ] ][ j ] = unescape( cols[ k ] ).replace( /^\"|\"$/g, '' );
}
}
}
return data;
}
/*
------------------------------------------------------------
Converts JSON data to a regular corpus object
@see sample.json
------------------------------------------------------------
*/
function parseJSON( json ) {
var i, n, key, val, map = {}, keys = {}, data = {}, rows = json.feed.entry;
for ( key in rows[0] ) {
if ( RE_COL.test( key ) ) {
map[ key ] = key.match( RE_COL )[ 1 ].toLowerCase();
keys[ key ] = [];
}
}
for ( key in keys ) {
data[ map[ key ] ] = keys[ key ];
for ( i = 0, n = rows.length; i < n; i++ ) {
val = rows[ i ][ key ].$t;
if ( val && val.length ) {
keys[ key ].push( val );
}
}
}
return data;
}
/*
------------------------------------------------------------
Binds event handlers to control the interface
------------------------------------------------------------
*/
function initUI() {
$( '.loading' ).remove();
dom = {
generate: $( '#generate' ),
output: $( '#output' )
};
dom.generate.click( function() {
generate();
return false;
});
}
/*
------------------------------------------------------------
Builds a regular expression for all types in the corpus
------------------------------------------------------------
*/
function buildRexExp() {
var types = [];
for ( var type in corpus )
types.push( type );
types = types.sort(function (a, b) {
if (a.length == b.length) {
return 0
}
return a.length > b.length ? -1 : 1
})
var content = '@(type)'.replace( 'type', types.join( '|' ) );
regex = new RegExp( content, 'gi' );
}
/*
------------------------------------------------------------
Generates ideas based on the corpus
------------------------------------------------------------
*/
function generate() {
var type, text, part, iter = 0, // Safety mechanism
idea = randomItem( templates ),
item = regex.exec( idea ),
copy = cloneCorpus();
while ( item && ++iter < 1000 ) {
type = item[ 0 ];
text = item[ 1 ];
part = randomItem( copy[ text ], true );
idea = idea.replace( type, part );
regex.lastIndex = 0;
item = regex.exec( idea );
}
// Update output
dom.generate.text( randomItem( responses ) );
dom.output.html(
'<dl>' +
'<dt>' + randomItem( headings ) + '</dt>' +
'<dd>' + idea + '</dd>' +
'</dl>'
);
// Toggle animation
setTimeout( showOutput, 0 );
hideOutput();
}
function hideOutput() {
dom.output.removeClass( 'animate' ).css( 'opacity', 0 );
}
function showOutput() {
dom.output.addClass( 'animate' ).css( 'opacity', 1 );
}
function randomItem( list, remove ) {
var index = ~~( Math.random() * list.length );
var item = list[ index ];
if ( remove )
list.splice( index, 1 );
return item;
}
function cloneCorpus() {
var copy = {};
for ( var key in corpus )
copy[ key ] = corpus[ key ].concat();
return copy;
}
/*
------------------------------------------------------------
Public API
------------------------------------------------------------
*/
return {
/*
Expects one of the following:
1. An object with `templates` and any amount of keys for word types, for example:
{
templates: [ 'The @color @animal', 'The @animal was @color' ],
animal: [ 'dog', 'cat', 'rabbit' ],
color: [ 'red', 'green', 'blue' ],
}
2. A path to a JSON file with the same structure as above (see `sample.json`)
3. A Google spreadsheet key (e.g 0AvG1Hx204EyydF9ub1M2cVJ3Z1VGdDhTSWg0ZV9LNGc)
You must first publish the spreadsheet as a CSV
@see https://support.google.com/drive/answer/37579?hl=en
*/
init: function( data ) {
if ( !data ) throw data + ' is not a valid corpus';
if ( typeof data === 'string' ) {
if ( RE_JSON.test( data ) ) {
// JSON
$.ajax({
url: data,
dataType: 'json',
success: function( data, status, xhr ) {
corpus = data;
start();
},
error: function( xhr, errorType, error ) {
throw 'Cannot load JSON data: ' + error;
}
});
} else if ( RE_KEY.test( data ) ) {
// JSON
$.ajax({
url: DOCS_PATH.replace( '{key}', data ),
success: function( data, status, xhr ) {
corpus = parseJSON( data );
start();
},
error: function( xhr, errorType, error ) {
throw 'Cannot load spreadsheet. Is it published? (@see https://support.google.com/drive/answer/37579?hl=en)';
}
});
} else {