├── public ├── images │ └── logo2.png ├── css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ └── style.css ├── index.html ├── js │ ├── lib │ │ ├── jquery.highlight.js │ │ ├── energize.js │ │ ├── imagesloaded.min.js │ │ ├── jquery_ui.js │ │ └── jquery.tocify.js │ └── script.js └── swagger.yaml ├── package.json ├── server.js ├── README.md └── db.json /public/images/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokublac/the-wolf-among-us-api/HEAD/public/images/logo2.png -------------------------------------------------------------------------------- /public/css/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokublac/the-wolf-among-us-api/HEAD/public/css/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokublac/the-wolf-among-us-api/HEAD/public/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokublac/the-wolf-among-us-api/HEAD/public/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokublac/the-wolf-among-us-api/HEAD/public/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-server-heroku", 3 | "version": "1.0.0", 4 | "description": "Simple json-base database to deploy to Heroku", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "dev": "nodemon server.js" 9 | }, 10 | "keywords": [ 11 | "json-server,heroku, node, REST API" 12 | ], 13 | "author": "Jesper Orb", 14 | "license": "ISC", 15 | "dependencies": { 16 | "json-server": "^0.12.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const jsonServer = require('json-server'); 2 | const server = jsonServer.create(); 3 | const router = jsonServer.router('db.json'); 4 | const middlewares = jsonServer.defaults(); 5 | const port = process.env.PORT || 3000; 6 | 7 | server.use(middlewares); 8 | server.use(router); 9 | server.listen(port); 10 | 11 | // 404 error request 12 | router.render = (req, res) => { 13 | if (res.statusCode == 404){ 14 | res.status(404).jsonp({ 15 | errorCode: 404, 16 | description: "Not found" 17 | }); 18 | } else { 19 | res.jsonp({ 20 | statusCode: res.statusCode, 21 | body: res.locals.data 22 | }); 23 | } 24 | }; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Wolf Among Us API 5 | 6 | 7 | 8 | 9 | 10 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | The Wolf Among Us Logo 3 |
4 | 5 | 6 | # The Wolf Among Us API (sunsetted) 7 | Small test project working with Swagger and Redoc.
8 | This REST API provides the (current) characters, locations, and episodes within Telltale's The Wolf Among Us. 9 | 10 | 11 | ## Accessing the API 12 | Requests can be made with Curl or with your desired language's HTTP GET request method. For example, with Curl, to GET the data for Bigsby Wolf: 13 | `curl https://twauapi.herokuapp.com/characters/1` 14 | 15 | ```json 16 | { 17 | "statusCode": 200, 18 | "body": { 19 | "id": 1, 20 | "name": "Bigby Wolf", 21 | "species": "Wolf", 22 | "gender": "Male", 23 | "occupation": "Fabletown Sherrif", 24 | "hairColour": "Mahogany", 25 | "eyeColour": "Brown", 26 | "description": "The renowned Big Bad Wolf. He's known for tormenting pigs and girls in red hoods, but is trying to put those dark days behind him. Bigby now acts as Fabletown's sheriff and remains in his human form, mostly. However, due to his rough past, the citizens of Fabletown are slow to trust him. Bigby is determined to show that he's truly changed, but some instincts are just too hard to control", 27 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2b/CW_Bigby_Nerissa_Convo.png/revision/latest?cb=20140710174447" 28 | } 29 | } 30 | ``` 31 | 32 | ## Use cases 33 | No use cases really, besides cataloguing characters, locations and episodes...
34 | The JSON file is only around 40KB - so if you wanted to access the data you're better off just downloading it from this repository and implement it directly into your project(s). But if you want to access it with HTTP, here you go! 35 | 36 | ## Credits 37 | - [ReDoc](https://github.com/Rebilly/ReDoc) for the API reference documentation. 38 | - [json-server](https://github.com/typicode/json-server) for the REST API builder. 39 | - [Wolf Among Us wiki](http://fables.wikia.com/wiki/The_Wolf_Among_Us) contributors for the data and information. 40 | - [Telltale Games](https://telltale.com/) for the awesome game. 41 | -------------------------------------------------------------------------------- /public/js/lib/jquery.highlight.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Highlight plugin 3 | * 4 | * Based on highlight v3 by Johann Burkard 5 | * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html 6 | * 7 | * Code a little bit refactored and cleaned (in my humble opinion). 8 | * Most important changes: 9 | * - has an option to highlight only entire words (wordsOnly - false by default), 10 | * - has an option to be case sensitive (caseSensitive - false by default) 11 | * - highlight element tag and class names can be specified in options 12 | * 13 | * Usage: 14 | * // wrap every occurrance of text 'lorem' in content 15 | * // with (default options) 16 | * $('#content').highlight('lorem'); 17 | * 18 | * // search for and highlight more terms at once 19 | * // so you can save some time on traversing DOM 20 | * $('#content').highlight(['lorem', 'ipsum']); 21 | * $('#content').highlight('lorem ipsum'); 22 | * 23 | * // search only for entire word 'lorem' 24 | * $('#content').highlight('lorem', { wordsOnly: true }); 25 | * 26 | * // don't ignore case during search of term 'lorem' 27 | * $('#content').highlight('lorem', { caseSensitive: true }); 28 | * 29 | * // wrap every occurrance of term 'ipsum' in content 30 | * // with 31 | * $('#content').highlight('ipsum', { element: 'em', className: 'important' }); 32 | * 33 | * // remove default highlight 34 | * $('#content').unhighlight(); 35 | * 36 | * // remove custom highlight 37 | * $('#content').unhighlight({ element: 'em', className: 'important' }); 38 | * 39 | * 40 | * Copyright (c) 2009 Bartek Szopka 41 | * 42 | * Licensed under MIT license. 43 | * 44 | */ 45 | 46 | jQuery.extend({ 47 | highlight: function (node, re, nodeName, className) { 48 | if (node.nodeType === 3) { 49 | var match = node.data.match(re); 50 | if (match) { 51 | var highlight = document.createElement(nodeName || 'span'); 52 | highlight.className = className || 'highlight'; 53 | var wordNode = node.splitText(match.index); 54 | wordNode.splitText(match[0].length); 55 | var wordClone = wordNode.cloneNode(true); 56 | highlight.appendChild(wordClone); 57 | wordNode.parentNode.replaceChild(highlight, wordNode); 58 | return 1; //skip added node in parent 59 | } 60 | } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children 61 | !/(script|style)/i.test(node.tagName) && // ignore script and style nodes 62 | !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted 63 | for (var i = 0; i < node.childNodes.length; i++) { 64 | i += jQuery.highlight(node.childNodes[i], re, nodeName, className); 65 | } 66 | } 67 | return 0; 68 | } 69 | }); 70 | 71 | jQuery.fn.unhighlight = function (options) { 72 | var settings = { className: 'highlight', element: 'span' }; 73 | jQuery.extend(settings, options); 74 | 75 | return this.find(settings.element + "." + settings.className).each(function () { 76 | var parent = this.parentNode; 77 | parent.replaceChild(this.firstChild, this); 78 | parent.normalize(); 79 | }).end(); 80 | }; 81 | 82 | jQuery.fn.highlight = function (words, options) { 83 | var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false }; 84 | jQuery.extend(settings, options); 85 | 86 | if (words.constructor === String) { 87 | words = [words]; 88 | } 89 | words = jQuery.grep(words, function(word, i){ 90 | return word != ''; 91 | }); 92 | words = jQuery.map(words, function(word, i) { 93 | return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 94 | }); 95 | if (words.length == 0) { return this; }; 96 | 97 | var flag = settings.caseSensitive ? "" : "i"; 98 | var pattern = "(" + words.join("|") + ")"; 99 | if (settings.wordsOnly) { 100 | pattern = "\\b" + pattern + "\\b"; 101 | } 102 | var re = new RegExp(pattern, flag); 103 | 104 | return this.each(function () { 105 | jQuery.highlight(this, re, settings.element, settings.className); 106 | }); 107 | }; 108 | 109 | -------------------------------------------------------------------------------- /public/js/lib/energize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * energize.js v0.1.0 3 | * 4 | * Speeds up click events on mobile devices. 5 | * https://github.com/davidcalhoun/energize.js 6 | */ 7 | 8 | (function() { // Sandbox 9 | /** 10 | * Don't add to non-touch devices, which don't need to be sped up 11 | */ 12 | if(!('ontouchstart' in window)) return; 13 | 14 | var lastClick = {}, 15 | isThresholdReached, touchstart, touchmove, touchend, 16 | click, closest; 17 | 18 | /** 19 | * isThresholdReached 20 | * 21 | * Compare touchstart with touchend xy coordinates, 22 | * and only fire simulated click event if the coordinates 23 | * are nearby. (don't want clicking to be confused with a swipe) 24 | */ 25 | isThresholdReached = function(startXY, xy) { 26 | return Math.abs(startXY[0] - xy[0]) > 5 || Math.abs(startXY[1] - xy[1]) > 5; 27 | }; 28 | 29 | /** 30 | * touchstart 31 | * 32 | * Save xy coordinates when the user starts touching the screen 33 | */ 34 | touchstart = function(e) { 35 | this.startXY = [e.touches[0].clientX, e.touches[0].clientY]; 36 | this.threshold = false; 37 | }; 38 | 39 | /** 40 | * touchmove 41 | * 42 | * Check if the user is scrolling past the threshold. 43 | * Have to check here because touchend will not always fire 44 | * on some tested devices (Kindle Fire?) 45 | */ 46 | touchmove = function(e) { 47 | // NOOP if the threshold has already been reached 48 | if(this.threshold) return false; 49 | 50 | this.threshold = isThresholdReached(this.startXY, [e.touches[0].clientX, e.touches[0].clientY]); 51 | }; 52 | 53 | /** 54 | * touchend 55 | * 56 | * If the user didn't scroll past the threshold between 57 | * touchstart and touchend, fire a simulated click. 58 | * 59 | * (This will fire before a native click) 60 | */ 61 | touchend = function(e) { 62 | // Don't fire a click if the user scrolled past the threshold 63 | if(this.threshold || isThresholdReached(this.startXY, [e.changedTouches[0].clientX, e.changedTouches[0].clientY])) { 64 | return; 65 | } 66 | 67 | /** 68 | * Create and fire a click event on the target element 69 | * https://developer.mozilla.org/en/DOM/event.initMouseEvent 70 | */ 71 | var touch = e.changedTouches[0], 72 | evt = document.createEvent('MouseEvents'); 73 | evt.initMouseEvent('click', true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); 74 | evt.simulated = true; // distinguish from a normal (nonsimulated) click 75 | e.target.dispatchEvent(evt); 76 | }; 77 | 78 | /** 79 | * click 80 | * 81 | * Because we've already fired a click event in touchend, 82 | * we need to listed for all native click events here 83 | * and suppress them as necessary. 84 | */ 85 | click = function(e) { 86 | /** 87 | * Prevent ghost clicks by only allowing clicks we created 88 | * in the click event we fired (look for e.simulated) 89 | */ 90 | var time = Date.now(), 91 | timeDiff = time - lastClick.time, 92 | x = e.clientX, 93 | y = e.clientY, 94 | xyDiff = [Math.abs(lastClick.x - x), Math.abs(lastClick.y - y)], 95 | target = closest(e.target, 'A') || e.target, // needed for standalone apps 96 | nodeName = target.nodeName, 97 | isLink = nodeName === 'A', 98 | standAlone = window.navigator.standalone && isLink && e.target.getAttribute("href"); 99 | 100 | lastClick.time = time; 101 | lastClick.x = x; 102 | lastClick.y = y; 103 | 104 | /** 105 | * Unfortunately Android sometimes fires click events without touch events (seen on Kindle Fire), 106 | * so we have to add more logic to determine the time of the last click. Not perfect... 107 | * 108 | * Older, simpler check: if((!e.simulated) || standAlone) 109 | */ 110 | if((!e.simulated && (timeDiff < 500 || (timeDiff < 1500 && xyDiff[0] < 50 && xyDiff[1] < 50))) || standAlone) { 111 | e.preventDefault(); 112 | e.stopPropagation(); 113 | if(!standAlone) return false; 114 | } 115 | 116 | /** 117 | * Special logic for standalone web apps 118 | * See http://stackoverflow.com/questions/2898740/iphone-safari-web-app-opens-links-in-new-window 119 | */ 120 | if(standAlone) { 121 | window.location = target.getAttribute("href"); 122 | } 123 | 124 | /** 125 | * Add an energize-focus class to the targeted link (mimics :focus behavior) 126 | * TODO: test and/or remove? Does this work? 127 | */ 128 | if(!target || !target.classList) return; 129 | target.classList.add("energize-focus"); 130 | window.setTimeout(function(){ 131 | target.classList.remove("energize-focus"); 132 | }, 150); 133 | }; 134 | 135 | /** 136 | * closest 137 | * @param {HTMLElement} node current node to start searching from. 138 | * @param {string} tagName the (uppercase) name of the tag you're looking for. 139 | * 140 | * Find the closest ancestor tag of a given node. 141 | * 142 | * Starts at node and goes up the DOM tree looking for a 143 | * matching nodeName, continuing until hitting document.body 144 | */ 145 | closest = function(node, tagName){ 146 | var curNode = node; 147 | 148 | while(curNode !== document.body) { // go up the dom until we find the tag we're after 149 | if(!curNode || curNode.nodeName === tagName) { return curNode; } // found 150 | curNode = curNode.parentNode; // not found, so keep going up 151 | } 152 | 153 | return null; // not found 154 | }; 155 | 156 | /** 157 | * Add all delegated event listeners 158 | * 159 | * All the events we care about bubble up to document, 160 | * so we can take advantage of event delegation. 161 | * 162 | * Note: no need to wait for DOMContentLoaded here 163 | */ 164 | document.addEventListener('touchstart', touchstart, false); 165 | document.addEventListener('touchmove', touchmove, false); 166 | document.addEventListener('touchend', touchend, false); 167 | document.addEventListener('click', click, true); // TODO: why does this use capture? 168 | 169 | })(); -------------------------------------------------------------------------------- /public/js/lib/imagesloaded.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * imagesLoaded PACKAGED v3.1.8 3 | * JavaScript is all like "You images are done yet or what?" 4 | * MIT License 5 | */ 6 | 7 | (function(){function e(){}function t(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var i=e.prototype,r=this,o=r.EventEmitter;i.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},i.flattenListeners=function(e){var t,n=[];for(t=0;e.length>t;t+=1)n.push(e[t].listener);return n},i.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},i.addListener=function(e,n){var i,r=this.getListenersAsObject(e),o="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(o?n:{listener:n,once:!1});return this},i.on=n("addListener"),i.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},i.once=n("addOnceListener"),i.defineEvent=function(e){return this.getListeners(e),this},i.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},i.removeListener=function(e,n){var i,r,o=this.getListenersAsObject(e);for(r in o)o.hasOwnProperty(r)&&(i=t(o[r],n),-1!==i&&o[r].splice(i,1));return this},i.off=n("removeListener"),i.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},i.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},i.manipulateListeners=function(e,t,n){var i,r,o=e?this.removeListener:this.addListener,s=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)o.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?o.call(this,i,r):s.call(this,i,r));return this},i.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},i.removeAllListeners=n("removeEvent"),i.emitEvent=function(e,t){var n,i,r,o,s=this.getListenersAsObject(e);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].length;i--;)n=s[r][i],n.once===!0&&this.removeListener(e,n.listener),o=n.listener.apply(this,t||[]),o===this._getOnceReturnValue()&&this.removeListener(e,n.listener);return this},i.trigger=n("emitEvent"),i.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},i.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},i._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},i._getEvents=function(){return this._events||(this._events={})},e.noConflict=function(){return r.EventEmitter=o,e},"function"==typeof define&&define.amd?define("eventEmitter/EventEmitter",[],function(){return e}):"object"==typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){function t(t){var n=e.event;return n.target=n.target||n.srcElement||t,n}var n=document.documentElement,i=function(){};n.addEventListener?i=function(e,t,n){e.addEventListener(t,n,!1)}:n.attachEvent&&(i=function(e,n,i){e[n+i]=i.handleEvent?function(){var n=t(e);i.handleEvent.call(i,n)}:function(){var n=t(e);i.call(e,n)},e.attachEvent("on"+n,e[n+i])});var r=function(){};n.removeEventListener?r=function(e,t,n){e.removeEventListener(t,n,!1)}:n.detachEvent&&(r=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var o={bind:i,unbind:r};"function"==typeof define&&define.amd?define("eventie/eventie",o):e.eventie=o}(this),function(e,t){"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],function(n,i){return t(e,n,i)}):"object"==typeof exports?module.exports=t(e,require("wolfy87-eventemitter"),require("eventie")):e.imagesLoaded=t(e,e.EventEmitter,e.eventie)}(window,function(e,t,n){function i(e,t){for(var n in t)e[n]=t[n];return e}function r(e){return"[object Array]"===d.call(e)}function o(e){var t=[];if(r(e))t=e;else if("number"==typeof e.length)for(var n=0,i=e.length;i>n;n++)t.push(e[n]);else t.push(e);return t}function s(e,t,n){if(!(this instanceof s))return new s(e,t);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=o(e),this.options=i({},this.options),"function"==typeof t?n=t:i(this.options,t),n&&this.on("always",n),this.getImages(),a&&(this.jqDeferred=new a.Deferred);var r=this;setTimeout(function(){r.check()})}function f(e){this.img=e}function c(e){this.src=e,v[e]=this}var a=e.jQuery,u=e.console,h=u!==void 0,d=Object.prototype.toString;s.prototype=new t,s.prototype.options={},s.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);var i=n.nodeType;if(i&&(1===i||9===i||11===i))for(var r=n.querySelectorAll("img"),o=0,s=r.length;s>o;o++){var f=r[o];this.addImage(f)}}},s.prototype.addImage=function(e){var t=new f(e);this.images.push(t)},s.prototype.check=function(){function e(e,r){return t.options.debug&&h&&u.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var o=this.images[r];o.on("confirm",e),o.check()}},s.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded;var t=this;setTimeout(function(){t.emit("progress",t,e),t.jqDeferred&&t.jqDeferred.notify&&t.jqDeferred.notify(t,e)})},s.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";this.isComplete=!0;var t=this;setTimeout(function(){if(t.emit(e,t),t.emit("always",t),t.jqDeferred){var n=t.hasAnyBroken?"reject":"resolve";t.jqDeferred[n](t)}})},a&&(a.fn.imagesLoaded=function(e,t){var n=new s(this,e,t);return n.jqDeferred.promise(a(this))}),f.prototype=new t,f.prototype.check=function(){var e=v[this.img.src]||new c(this.img.src);if(e.isConfirmed)return this.confirm(e.isLoaded,"cached was confirmed"),void 0;if(this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this;e.on("confirm",function(e,n){return t.confirm(e.isLoaded,n),!0}),e.check()},f.prototype.confirm=function(e,t){this.isLoaded=e,this.emit("confirm",this,t)};var v={};return c.prototype=new t,c.prototype.check=function(){if(!this.isChecked){var e=new Image;n.bind(e,"load",this),n.bind(e,"error",this),e.src=this.src,this.isChecked=!0}},c.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},c.prototype.onload=function(e){this.confirm(!0,"onload"),this.unbindProxyEvents(e)},c.prototype.onerror=function(e){this.confirm(!1,"onerror"),this.unbindProxyEvents(e)},c.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit("confirm",this,t)},c.prototype.unbindProxyEvents=function(e){n.unbind(e.target,"load",this),n.unbind(e.target,"error",this)},s}); -------------------------------------------------------------------------------- /public/js/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Marcel Pociot 3 | Copyright 2008-2013 Concur Technologies, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | not use this file except in compliance with the License. You may obtain 7 | a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | License for the specific language governing permissions and limitations 15 | under the License. 16 | */ 17 | (function (global) { 18 | 'use strict'; 19 | 20 | var languages = []; 21 | 22 | global.setupLanguages = setupLanguages; 23 | global.activateLanguage = activateLanguage; 24 | 25 | function activateLanguage(language) { 26 | if (!language) return; 27 | if (language === "") return; 28 | 29 | $(".lang-selector a").removeClass('active'); 30 | $(".lang-selector a[data-language-name='" + language + "']").addClass('active'); 31 | for (var i=0; i < languages.length; i++) { 32 | $(".highlight." + languages[i]).parent().hide(); 33 | } 34 | $(".highlight." + language).parent().show(); 35 | 36 | global.toc.calculateHeights(); 37 | 38 | // scroll to the new location of the position 39 | if ($(window.location.hash).get(0)) { 40 | $(window.location.hash).get(0).scrollIntoView(true); 41 | } 42 | } 43 | 44 | // parseURL and stringifyURL are from https://github.com/sindresorhus/query-string 45 | // MIT licensed 46 | // https://github.com/sindresorhus/query-string/blob/7bee64c16f2da1a326579e96977b9227bf6da9e6/license 47 | function parseURL(str) { 48 | if (typeof str !== 'string') { 49 | return {}; 50 | } 51 | 52 | str = str.trim().replace(/^(\?|#|&)/, ''); 53 | 54 | if (!str) { 55 | return {}; 56 | } 57 | 58 | return str.split('&').reduce(function (ret, param) { 59 | var parts = param.replace(/\+/g, ' ').split('='); 60 | var key = parts[0]; 61 | var val = parts[1]; 62 | 63 | key = decodeURIComponent(key); 64 | // missing `=` should be `null`: 65 | // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters 66 | val = val === undefined ? null : decodeURIComponent(val); 67 | 68 | if (!ret.hasOwnProperty(key)) { 69 | ret[key] = val; 70 | } else if (Array.isArray(ret[key])) { 71 | ret[key].push(val); 72 | } else { 73 | ret[key] = [ret[key], val]; 74 | } 75 | 76 | return ret; 77 | }, {}); 78 | }; 79 | 80 | function stringifyURL(obj) { 81 | return obj ? Object.keys(obj).sort().map(function (key) { 82 | var val = obj[key]; 83 | 84 | if (Array.isArray(val)) { 85 | return val.sort().map(function (val2) { 86 | return encodeURIComponent(key) + '=' + encodeURIComponent(val2); 87 | }).join('&'); 88 | } 89 | 90 | return encodeURIComponent(key) + '=' + encodeURIComponent(val); 91 | }).join('&') : ''; 92 | }; 93 | 94 | // gets the language set in the query string 95 | function getLanguageFromQueryString() { 96 | if (location.search.length >= 1) { 97 | var language = parseURL(location.search).language 98 | if (language) { 99 | return language; 100 | } else if (jQuery.inArray(location.search.substr(1), languages) != -1) { 101 | return location.search.substr(1); 102 | } 103 | } 104 | 105 | return false; 106 | } 107 | 108 | // returns a new query string with the new language in it 109 | function generateNewQueryString(language) { 110 | var url = parseURL(location.search); 111 | if (url.language) { 112 | url.language = language; 113 | return stringifyURL(url); 114 | } 115 | return language; 116 | } 117 | 118 | // if a button is clicked, add the state to the history 119 | function pushURL(language) { 120 | if (!history) { return; } 121 | var hash = window.location.hash; 122 | if (hash) { 123 | hash = hash.replace(/^#+/, ''); 124 | } 125 | history.pushState({}, '', '?' + generateNewQueryString(language) + '#' + hash); 126 | 127 | // save language as next default 128 | localStorage.setItem("language", language); 129 | } 130 | 131 | function setupLanguages(l) { 132 | var defaultLanguage = localStorage.getItem("language"); 133 | 134 | languages = l; 135 | 136 | var presetLanguage = getLanguageFromQueryString(); 137 | if (presetLanguage) { 138 | // the language is in the URL, so use that language! 139 | activateLanguage(presetLanguage); 140 | 141 | localStorage.setItem("language", presetLanguage); 142 | } else if ((defaultLanguage !== null) && (jQuery.inArray(defaultLanguage, languages) != -1)) { 143 | // the language was the last selected one saved in localstorage, so use that language! 144 | activateLanguage(defaultLanguage); 145 | } else { 146 | // no language selected, so use the default 147 | activateLanguage(languages[0]); 148 | } 149 | } 150 | 151 | // if we click on a language tab, activate that language 152 | $(function() { 153 | $(".lang-selector a").on("click", function() { 154 | var language = $(this).data("language-name"); 155 | pushURL(language); 156 | activateLanguage(language); 157 | return false; 158 | }); 159 | window.onpopstate = function() { 160 | activateLanguage(getLanguageFromQueryString()); 161 | }; 162 | }); 163 | })(window); 164 | 165 | 166 | /** 167 | * TOC 168 | */ 169 | (function (global) { 170 | 'use strict'; 171 | 172 | var closeToc = function() { 173 | $(".tocify-wrapper").removeClass('open'); 174 | $("#nav-button").removeClass('open'); 175 | }; 176 | 177 | var makeToc = function() { 178 | global.toc = $("#toc").tocify({ 179 | selectors: 'h1, h2 , h3 , h4 , h5 , h6 ', 180 | extendPage: false, 181 | theme: 'none', 182 | smoothScroll: false, 183 | showEffectSpeed: 0, 184 | hideEffectSpeed: 180, 185 | ignoreSelector: '.toc-ignore', 186 | highlightOffset: 60, 187 | scrollTo: -1, 188 | scrollHistory: true, 189 | hashGenerator: function (text, element) { 190 | return element.prop('id'); 191 | } 192 | }).data('toc-tocify'); 193 | 194 | $("#nav-button").click(function() { 195 | $(".tocify-wrapper").toggleClass('open'); 196 | $("#nav-button").toggleClass('open'); 197 | return false; 198 | }); 199 | 200 | $(".page-wrapper").click(closeToc); 201 | $(".tocify-item").click(closeToc); 202 | }; 203 | 204 | // Hack to make already open sections to start opened, 205 | // instead of displaying an ugly animation 206 | function animate() { 207 | setTimeout(function() { 208 | toc.setOption('showEffectSpeed', 180); 209 | }, 50); 210 | } 211 | 212 | $(function() { 213 | makeToc(); 214 | animate(); 215 | $('.content').imagesLoaded( function() { 216 | global.toc.calculateHeights(); 217 | }); 218 | }); 219 | })(window); 220 | 221 | /** 222 | * Search 223 | */ 224 | (function () { 225 | 'use strict'; 226 | 227 | var content, searchResults; 228 | var highlightOpts = { element: 'span', className: 'search-highlight' }; 229 | 230 | var index = new lunr.Index(); 231 | 232 | index.ref('id'); 233 | index.field('title', { boost: 10 }); 234 | index.field('body'); 235 | index.pipeline.add(lunr.trimmer, lunr.stopWordFilter); 236 | 237 | $(populate); 238 | $(bind); 239 | 240 | function populate() { 241 | $('h1, h2 , h3 , h4 , h5 , h6 ').each(function() { 242 | var title = $(this); 243 | var body = title.nextUntil('h1, h2 , h3 , h4 , h5 , h6'); 244 | index.add({ 245 | id: title.prop('id'), 246 | title: title.text(), 247 | body: body.text() 248 | }); 249 | }); 250 | } 251 | 252 | function bind() { 253 | content = $('.content'); 254 | searchResults = $('.search-results'); 255 | 256 | $('#input-search').on('keyup', search); 257 | } 258 | 259 | function search(event) { 260 | unhighlight(); 261 | searchResults.addClass('visible'); 262 | 263 | // ESC clears the field 264 | if (event.keyCode === 27) this.value = ''; 265 | 266 | if (this.value) { 267 | var results = index.search(this.value).filter(function(r) { 268 | return r.score > 0.0001; 269 | }); 270 | 271 | if (results.length) { 272 | searchResults.empty(); 273 | $.each(results, function (index, result) { 274 | var elem = document.getElementById(result.ref); 275 | searchResults.append("
  • " + $(elem).text() + "
  • "); 276 | }); 277 | highlight.call(this); 278 | } else { 279 | searchResults.html('
  • '); 280 | $('.search-results li').text('No Results Found for "' + this.value + '"'); 281 | } 282 | } else { 283 | unhighlight(); 284 | searchResults.removeClass('visible'); 285 | } 286 | } 287 | 288 | function highlight() { 289 | if (this.value) content.highlight(this.value, highlightOpts); 290 | } 291 | 292 | function unhighlight() { 293 | content.unhighlight(highlightOpts); 294 | } 295 | })(); 296 | -------------------------------------------------------------------------------- /public/js/lib/jquery_ui.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.3 - 2015-02-12 2 | * http://jqueryui.com 3 | * Includes: widget.js 4 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function( factory ) { 7 | if ( typeof define === "function" && define.amd ) { 8 | 9 | // AMD. Register as an anonymous module. 10 | define([ "jquery" ], factory ); 11 | } else { 12 | 13 | // Browser globals 14 | factory( jQuery ); 15 | } 16 | }(function( $ ) { 17 | /*! 18 | * jQuery UI Widget 1.11.3 19 | * http://jqueryui.com 20 | * 21 | * Copyright jQuery Foundation and other contributors 22 | * Released under the MIT license. 23 | * http://jquery.org/license 24 | * 25 | * http://api.jqueryui.com/jQuery.widget/ 26 | */ 27 | 28 | 29 | var widget_uuid = 0, 30 | widget_slice = Array.prototype.slice; 31 | 32 | $.cleanData = (function( orig ) { 33 | return function( elems ) { 34 | var events, elem, i; 35 | for ( i = 0; (elem = elems[i]) != null; i++ ) { 36 | try { 37 | 38 | // Only trigger remove when necessary to save time 39 | events = $._data( elem, "events" ); 40 | if ( events && events.remove ) { 41 | $( elem ).triggerHandler( "remove" ); 42 | } 43 | 44 | // http://bugs.jquery.com/ticket/8235 45 | } catch ( e ) {} 46 | } 47 | orig( elems ); 48 | }; 49 | })( $.cleanData ); 50 | 51 | $.widget = function( name, base, prototype ) { 52 | var fullName, existingConstructor, constructor, basePrototype, 53 | // proxiedPrototype allows the provided prototype to remain unmodified 54 | // so that it can be used as a mixin for multiple widgets (#8876) 55 | proxiedPrototype = {}, 56 | namespace = name.split( "." )[ 0 ]; 57 | 58 | name = name.split( "." )[ 1 ]; 59 | fullName = namespace + "-" + name; 60 | 61 | if ( !prototype ) { 62 | prototype = base; 63 | base = $.Widget; 64 | } 65 | 66 | // create selector for plugin 67 | $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 68 | return !!$.data( elem, fullName ); 69 | }; 70 | 71 | $[ namespace ] = $[ namespace ] || {}; 72 | existingConstructor = $[ namespace ][ name ]; 73 | constructor = $[ namespace ][ name ] = function( options, element ) { 74 | // allow instantiation without "new" keyword 75 | if ( !this._createWidget ) { 76 | return new constructor( options, element ); 77 | } 78 | 79 | // allow instantiation without initializing for simple inheritance 80 | // must use "new" keyword (the code above always passes args) 81 | if ( arguments.length ) { 82 | this._createWidget( options, element ); 83 | } 84 | }; 85 | // extend with the existing constructor to carry over any static properties 86 | $.extend( constructor, existingConstructor, { 87 | version: prototype.version, 88 | // copy the object used to create the prototype in case we need to 89 | // redefine the widget later 90 | _proto: $.extend( {}, prototype ), 91 | // track widgets that inherit from this widget in case this widget is 92 | // redefined after a widget inherits from it 93 | _childConstructors: [] 94 | }); 95 | 96 | basePrototype = new base(); 97 | // we need to make the options hash a property directly on the new instance 98 | // otherwise we'll modify the options hash on the prototype that we're 99 | // inheriting from 100 | basePrototype.options = $.widget.extend( {}, basePrototype.options ); 101 | $.each( prototype, function( prop, value ) { 102 | if ( !$.isFunction( value ) ) { 103 | proxiedPrototype[ prop ] = value; 104 | return; 105 | } 106 | proxiedPrototype[ prop ] = (function() { 107 | var _super = function() { 108 | return base.prototype[ prop ].apply( this, arguments ); 109 | }, 110 | _superApply = function( args ) { 111 | return base.prototype[ prop ].apply( this, args ); 112 | }; 113 | return function() { 114 | var __super = this._super, 115 | __superApply = this._superApply, 116 | returnValue; 117 | 118 | this._super = _super; 119 | this._superApply = _superApply; 120 | 121 | returnValue = value.apply( this, arguments ); 122 | 123 | this._super = __super; 124 | this._superApply = __superApply; 125 | 126 | return returnValue; 127 | }; 128 | })(); 129 | }); 130 | constructor.prototype = $.widget.extend( basePrototype, { 131 | // TODO: remove support for widgetEventPrefix 132 | // always use the name + a colon as the prefix, e.g., draggable:start 133 | // don't prefix for widgets that aren't DOM-based 134 | widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name 135 | }, proxiedPrototype, { 136 | constructor: constructor, 137 | namespace: namespace, 138 | widgetName: name, 139 | widgetFullName: fullName 140 | }); 141 | 142 | // If this widget is being redefined then we need to find all widgets that 143 | // are inheriting from it and redefine all of them so that they inherit from 144 | // the new version of this widget. We're essentially trying to replace one 145 | // level in the prototype chain. 146 | if ( existingConstructor ) { 147 | $.each( existingConstructor._childConstructors, function( i, child ) { 148 | var childPrototype = child.prototype; 149 | 150 | // redefine the child widget using the same prototype that was 151 | // originally used, but inherit from the new version of the base 152 | $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); 153 | }); 154 | // remove the list of existing child constructors from the old constructor 155 | // so the old child constructors can be garbage collected 156 | delete existingConstructor._childConstructors; 157 | } else { 158 | base._childConstructors.push( constructor ); 159 | } 160 | 161 | $.widget.bridge( name, constructor ); 162 | 163 | return constructor; 164 | }; 165 | 166 | $.widget.extend = function( target ) { 167 | var input = widget_slice.call( arguments, 1 ), 168 | inputIndex = 0, 169 | inputLength = input.length, 170 | key, 171 | value; 172 | for ( ; inputIndex < inputLength; inputIndex++ ) { 173 | for ( key in input[ inputIndex ] ) { 174 | value = input[ inputIndex ][ key ]; 175 | if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 176 | // Clone objects 177 | if ( $.isPlainObject( value ) ) { 178 | target[ key ] = $.isPlainObject( target[ key ] ) ? 179 | $.widget.extend( {}, target[ key ], value ) : 180 | // Don't extend strings, arrays, etc. with objects 181 | $.widget.extend( {}, value ); 182 | // Copy everything else by reference 183 | } else { 184 | target[ key ] = value; 185 | } 186 | } 187 | } 188 | } 189 | return target; 190 | }; 191 | 192 | $.widget.bridge = function( name, object ) { 193 | var fullName = object.prototype.widgetFullName || name; 194 | $.fn[ name ] = function( options ) { 195 | var isMethodCall = typeof options === "string", 196 | args = widget_slice.call( arguments, 1 ), 197 | returnValue = this; 198 | 199 | if ( isMethodCall ) { 200 | this.each(function() { 201 | var methodValue, 202 | instance = $.data( this, fullName ); 203 | if ( options === "instance" ) { 204 | returnValue = instance; 205 | return false; 206 | } 207 | if ( !instance ) { 208 | return $.error( "cannot call methods on " + name + " prior to initialization; " + 209 | "attempted to call method '" + options + "'" ); 210 | } 211 | if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { 212 | return $.error( "no such method '" + options + "' for " + name + " widget instance" ); 213 | } 214 | methodValue = instance[ options ].apply( instance, args ); 215 | if ( methodValue !== instance && methodValue !== undefined ) { 216 | returnValue = methodValue && methodValue.jquery ? 217 | returnValue.pushStack( methodValue.get() ) : 218 | methodValue; 219 | return false; 220 | } 221 | }); 222 | } else { 223 | 224 | // Allow multiple hashes to be passed on init 225 | if ( args.length ) { 226 | options = $.widget.extend.apply( null, [ options ].concat(args) ); 227 | } 228 | 229 | this.each(function() { 230 | var instance = $.data( this, fullName ); 231 | if ( instance ) { 232 | instance.option( options || {} ); 233 | if ( instance._init ) { 234 | instance._init(); 235 | } 236 | } else { 237 | $.data( this, fullName, new object( options, this ) ); 238 | } 239 | }); 240 | } 241 | 242 | return returnValue; 243 | }; 244 | }; 245 | 246 | $.Widget = function( /* options, element */ ) {}; 247 | $.Widget._childConstructors = []; 248 | 249 | $.Widget.prototype = { 250 | widgetName: "widget", 251 | widgetEventPrefix: "", 252 | defaultElement: "
    ", 253 | options: { 254 | disabled: false, 255 | 256 | // callbacks 257 | create: null 258 | }, 259 | _createWidget: function( options, element ) { 260 | element = $( element || this.defaultElement || this )[ 0 ]; 261 | this.element = $( element ); 262 | this.uuid = widget_uuid++; 263 | this.eventNamespace = "." + this.widgetName + this.uuid; 264 | 265 | this.bindings = $(); 266 | this.hoverable = $(); 267 | this.focusable = $(); 268 | 269 | if ( element !== this ) { 270 | $.data( element, this.widgetFullName, this ); 271 | this._on( true, this.element, { 272 | remove: function( event ) { 273 | if ( event.target === element ) { 274 | this.destroy(); 275 | } 276 | } 277 | }); 278 | this.document = $( element.style ? 279 | // element within the document 280 | element.ownerDocument : 281 | // element is window or document 282 | element.document || element ); 283 | this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); 284 | } 285 | 286 | this.options = $.widget.extend( {}, 287 | this.options, 288 | this._getCreateOptions(), 289 | options ); 290 | 291 | this._create(); 292 | this._trigger( "create", null, this._getCreateEventData() ); 293 | this._init(); 294 | }, 295 | _getCreateOptions: $.noop, 296 | _getCreateEventData: $.noop, 297 | _create: $.noop, 298 | _init: $.noop, 299 | 300 | destroy: function() { 301 | this._destroy(); 302 | // we can probably remove the unbind calls in 2.0 303 | // all event bindings should go through this._on() 304 | this.element 305 | .unbind( this.eventNamespace ) 306 | .removeData( this.widgetFullName ) 307 | // support: jquery <1.6.3 308 | // http://bugs.jquery.com/ticket/9413 309 | .removeData( $.camelCase( this.widgetFullName ) ); 310 | this.widget() 311 | .unbind( this.eventNamespace ) 312 | .removeAttr( "aria-disabled" ) 313 | .removeClass( 314 | this.widgetFullName + "-disabled " + 315 | "ui-state-disabled" ); 316 | 317 | // clean up events and states 318 | this.bindings.unbind( this.eventNamespace ); 319 | this.hoverable.removeClass( "ui-state-hover" ); 320 | this.focusable.removeClass( "ui-state-focus" ); 321 | }, 322 | _destroy: $.noop, 323 | 324 | widget: function() { 325 | return this.element; 326 | }, 327 | 328 | option: function( key, value ) { 329 | var options = key, 330 | parts, 331 | curOption, 332 | i; 333 | 334 | if ( arguments.length === 0 ) { 335 | // don't return a reference to the internal hash 336 | return $.widget.extend( {}, this.options ); 337 | } 338 | 339 | if ( typeof key === "string" ) { 340 | // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 341 | options = {}; 342 | parts = key.split( "." ); 343 | key = parts.shift(); 344 | if ( parts.length ) { 345 | curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 346 | for ( i = 0; i < parts.length - 1; i++ ) { 347 | curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 348 | curOption = curOption[ parts[ i ] ]; 349 | } 350 | key = parts.pop(); 351 | if ( arguments.length === 1 ) { 352 | return curOption[ key ] === undefined ? null : curOption[ key ]; 353 | } 354 | curOption[ key ] = value; 355 | } else { 356 | if ( arguments.length === 1 ) { 357 | return this.options[ key ] === undefined ? null : this.options[ key ]; 358 | } 359 | options[ key ] = value; 360 | } 361 | } 362 | 363 | this._setOptions( options ); 364 | 365 | return this; 366 | }, 367 | _setOptions: function( options ) { 368 | var key; 369 | 370 | for ( key in options ) { 371 | this._setOption( key, options[ key ] ); 372 | } 373 | 374 | return this; 375 | }, 376 | _setOption: function( key, value ) { 377 | this.options[ key ] = value; 378 | 379 | if ( key === "disabled" ) { 380 | this.widget() 381 | .toggleClass( this.widgetFullName + "-disabled", !!value ); 382 | 383 | // If the widget is becoming disabled, then nothing is interactive 384 | if ( value ) { 385 | this.hoverable.removeClass( "ui-state-hover" ); 386 | this.focusable.removeClass( "ui-state-focus" ); 387 | } 388 | } 389 | 390 | return this; 391 | }, 392 | 393 | enable: function() { 394 | return this._setOptions({ disabled: false }); 395 | }, 396 | disable: function() { 397 | return this._setOptions({ disabled: true }); 398 | }, 399 | 400 | _on: function( suppressDisabledCheck, element, handlers ) { 401 | var delegateElement, 402 | instance = this; 403 | 404 | // no suppressDisabledCheck flag, shuffle arguments 405 | if ( typeof suppressDisabledCheck !== "boolean" ) { 406 | handlers = element; 407 | element = suppressDisabledCheck; 408 | suppressDisabledCheck = false; 409 | } 410 | 411 | // no element argument, shuffle and use this.element 412 | if ( !handlers ) { 413 | handlers = element; 414 | element = this.element; 415 | delegateElement = this.widget(); 416 | } else { 417 | element = delegateElement = $( element ); 418 | this.bindings = this.bindings.add( element ); 419 | } 420 | 421 | $.each( handlers, function( event, handler ) { 422 | function handlerProxy() { 423 | // allow widgets to customize the disabled handling 424 | // - disabled as an array instead of boolean 425 | // - disabled class as method for disabling individual parts 426 | if ( !suppressDisabledCheck && 427 | ( instance.options.disabled === true || 428 | $( this ).hasClass( "ui-state-disabled" ) ) ) { 429 | return; 430 | } 431 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 432 | .apply( instance, arguments ); 433 | } 434 | 435 | // copy the guid so direct unbinding works 436 | if ( typeof handler !== "string" ) { 437 | handlerProxy.guid = handler.guid = 438 | handler.guid || handlerProxy.guid || $.guid++; 439 | } 440 | 441 | var match = event.match( /^([\w:-]*)\s*(.*)$/ ), 442 | eventName = match[1] + instance.eventNamespace, 443 | selector = match[2]; 444 | if ( selector ) { 445 | delegateElement.delegate( selector, eventName, handlerProxy ); 446 | } else { 447 | element.bind( eventName, handlerProxy ); 448 | } 449 | }); 450 | }, 451 | 452 | _off: function( element, eventName ) { 453 | eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + 454 | this.eventNamespace; 455 | element.unbind( eventName ).undelegate( eventName ); 456 | 457 | // Clear the stack to avoid memory leaks (#10056) 458 | this.bindings = $( this.bindings.not( element ).get() ); 459 | this.focusable = $( this.focusable.not( element ).get() ); 460 | this.hoverable = $( this.hoverable.not( element ).get() ); 461 | }, 462 | 463 | _delay: function( handler, delay ) { 464 | function handlerProxy() { 465 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 466 | .apply( instance, arguments ); 467 | } 468 | var instance = this; 469 | return setTimeout( handlerProxy, delay || 0 ); 470 | }, 471 | 472 | _hoverable: function( element ) { 473 | this.hoverable = this.hoverable.add( element ); 474 | this._on( element, { 475 | mouseenter: function( event ) { 476 | $( event.currentTarget ).addClass( "ui-state-hover" ); 477 | }, 478 | mouseleave: function( event ) { 479 | $( event.currentTarget ).removeClass( "ui-state-hover" ); 480 | } 481 | }); 482 | }, 483 | 484 | _focusable: function( element ) { 485 | this.focusable = this.focusable.add( element ); 486 | this._on( element, { 487 | focusin: function( event ) { 488 | $( event.currentTarget ).addClass( "ui-state-focus" ); 489 | }, 490 | focusout: function( event ) { 491 | $( event.currentTarget ).removeClass( "ui-state-focus" ); 492 | } 493 | }); 494 | }, 495 | 496 | _trigger: function( type, event, data ) { 497 | var prop, orig, 498 | callback = this.options[ type ]; 499 | 500 | data = data || {}; 501 | event = $.Event( event ); 502 | event.type = ( type === this.widgetEventPrefix ? 503 | type : 504 | this.widgetEventPrefix + type ).toLowerCase(); 505 | // the original event may come from any element 506 | // so we need to reset the target on the new event 507 | event.target = this.element[ 0 ]; 508 | 509 | // copy original event properties over to the new event 510 | orig = event.originalEvent; 511 | if ( orig ) { 512 | for ( prop in orig ) { 513 | if ( !( prop in event ) ) { 514 | event[ prop ] = orig[ prop ]; 515 | } 516 | } 517 | } 518 | 519 | this.element.trigger( event, data ); 520 | return !( $.isFunction( callback ) && 521 | callback.apply( this.element[0], [ event ].concat( data ) ) === false || 522 | event.isDefaultPrevented() ); 523 | } 524 | }; 525 | 526 | $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 527 | $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 528 | if ( typeof options === "string" ) { 529 | options = { effect: options }; 530 | } 531 | var hasOptions, 532 | effectName = !options ? 533 | method : 534 | options === true || typeof options === "number" ? 535 | defaultEffect : 536 | options.effect || defaultEffect; 537 | options = options || {}; 538 | if ( typeof options === "number" ) { 539 | options = { duration: options }; 540 | } 541 | hasOptions = !$.isEmptyObject( options ); 542 | options.complete = callback; 543 | if ( options.delay ) { 544 | element.delay( options.delay ); 545 | } 546 | if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 547 | element[ method ]( options ); 548 | } else if ( effectName !== method && element[ effectName ] ) { 549 | element[ effectName ]( options.duration, options.easing, callback ); 550 | } else { 551 | element.queue(function( next ) { 552 | $( this )[ method ](); 553 | if ( callback ) { 554 | callback.call( element[ 0 ] ); 555 | } 556 | next(); 557 | }); 558 | } 559 | }; 560 | }); 561 | 562 | var widget = $.widget; 563 | 564 | 565 | 566 | })); 567 | -------------------------------------------------------------------------------- /public/swagger.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | title: The Wolf Among Us API 4 | description: | 5 | # Introduction 6 | Small test project working with Swagger and Redoc.
    7 | This REST API provides the (current) characters, locations, and episodes within Telltale's The Wolf Among Us. 8 | 9 | The main repository can be found at [https://github.com/rokublak/the-wolf-among-us-api](https://github.com/rokublak/the-wolf-among-us-api) 10 | 11 | # Access 12 | #### Authentication 13 | No authentication is required for this API. 14 | 15 | #### Base URL 16 | All resource calls must be made with this base URL: 17 | `https://twauapi.herokuapp.com` 18 | 19 | #### GET Requests 20 | Requests can be made with `Curl` or with your desired language's HTTP GET request method. For example, with Curl, to GET the data for ***Bigsby Wolf***: 21 | 22 | `curl https://twauapi.herokuapp.com/characters/1` 23 | 24 | Response: 25 | 26 | ```json 27 | { 28 | "statusCode": 200, 29 | "body": 30 | { 31 | "id": 1, 32 | "name": "Bigby Wolf", 33 | "species": "Wolf", 34 | "gender": "Male", 35 | "occupation": "Fabletown Sherrif", 36 | "hairColour": "Mahogany", 37 | "eyeColour": "Brown", 38 | "description": "The renowned Big Bad Wolf. He's known for tormenting pigs and girls in red hoods, but is trying to put those dark days behind him. Bigby now acts as Fabletown's sheriff and remains in his human form, mostly. However, due to his rough past, the citizens of Fabletown are slow to trust him. Bigby is determined to show that he's truly changed, but some instincts are just too hard to control", 39 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2b/CW_Bigby_Nerissa_Convo.png/revision/latest?cb=20140710174447" 40 | } 41 | } 42 | ``` 43 | # Use cases 44 | No use cases really, besides cataloguing characters, locations and episodes...
    45 | The JSON data is only ~40KB, so if you wanted to access the data you're better off just downloading it from main [github repo](https://github.com/rokublak/the-wolf-among-us-api) and implement it directly into your project(s). But if you want to access it with HTTP, here you go! 46 | 47 | version: "1.0.0" 48 | x-logo: 49 | url: "./images/logo2.png" 50 | backgroundColor: "#FFFF" 51 | # the domain of the service 52 | host: twauapi.herokuapp.com 53 | # array of all schemes that your API supports 54 | schemes: 55 | - https 56 | x-tagGroups: 57 | - name: Endpoints 58 | tags: 59 | - Characters 60 | - Locations 61 | - Episodes 62 | # will be prefixed to all paths 63 | basePath: / 64 | produces: 65 | - application/json 66 | paths: 67 | /characters: 68 | get: 69 | summary: All characters 70 | description: | 71 | `/characters` endpoint returns information for all the current characters. 72 | tags: 73 | - Characters 74 | parameters: 75 | - fields: 76 | name: fields 77 | in: query 78 | description: comma-separated list of fields to include in the response 79 | required: false 80 | type: string 81 | - limit: 82 | name: limit 83 | in: query 84 | required: false 85 | description: amount of results 86 | type: integer 87 | format: int64 88 | x-code-samples: 89 | - lang: Curl 90 | source: | 91 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/characters 92 | - lang: Python 93 | source: | 94 | import urllib2 95 | 96 | urllib2.urlopen('https://twauapi.herokuapp.com/characters').read() 97 | - lang: JavaScript 98 | source: | 99 | var xmlhttp = new XMLHttpRequest(); 100 | var url = 'https://twauapi.herokuapp.com/characters'; 101 | 102 | xmlhttp.onreadystatechange = function() { 103 | if (this.readyState == 4 && this.status == 200) { 104 | var characters = JSON.parse(this.responseText); 105 | console.log(characters); 106 | } 107 | }; 108 | xmlhttp.open("GET", url, true); 109 | xmlhttp.send(); 110 | - lang: Ruby 111 | source: | 112 | require 'net/http' 113 | 114 | uri = URI('https://twauapi.herokuapp.com/characters') 115 | Net::HTTP.get(uri) # => String 116 | 117 | responses: 118 | 200: 119 | description: OK Success 120 | schema: 121 | $ref: '#/definitions/Character' 122 | examples: 123 | application/json: 124 | { 125 | "statusCode": 200, 126 | "body": [ 127 | { 128 | "id": 1, 129 | "name": "Bigby Wolf", 130 | "species": "Wolf", 131 | "gender": "Male", 132 | "occupation": "Fabletown Sherrif", 133 | "hairColour": "Mahogany", 134 | "eyeColour": "Brown", 135 | "description": "The renowned Big Bad Wolf. He's known for tormenting pigs and girls in red hoods, but is trying to put those dark days behind him. Bigby now acts as Fabletown's sheriff and remains in his human form, mostly. However, due to his rough past, the citizens of Fabletown are slow to trust him. Bigby is determined to show that he's truly changed, but some instincts are just too hard to control", 136 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2b/CW_Bigby_Nerissa_Convo.png/revision/latest?cb=20140710174447" 137 | }, 138 | { 139 | "id": 2, 140 | "name": "Winter Wolf", 141 | "species": "Wolf", 142 | "gender": "Female", 143 | "occupation": "Unknown", 144 | "hairColour": "White", 145 | "eyeColour": "Unknown", 146 | "description": "Winter was the mother of Bigby Wolf and his brothers. Winter was a good and caring mother and Bigby regards her very highly and was displeased that his father, Mr. North, left her. Despite that, Winter loved and was devoted to him even to the moment of her death.", 147 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/e/e2/Winter_Wolf.png/revision/latest?cb=20140808191333" 148 | } 149 | ] 150 | } 151 | 152 | 404: 153 | description: Not found 154 | schema: 155 | $ref: '#/definitions/Error' 156 | examples: 157 | application/json: 158 | { 159 | "statusCode": 404, 160 | "message": "Not found" 161 | } 162 | 163 | /characters/{id}: 164 | get: 165 | summary: Character by ID 166 | description: | 167 | `/characters/{id}` endpoint returns information about a single character based on ID 168 | parameters: 169 | - name: id 170 | in: path 171 | description: character `id` 172 | required: true 173 | type: integer 174 | format: int64 175 | - name: fields 176 | in: query 177 | description: comma-separated list of fields to include in the response 178 | required: false 179 | type: string 180 | tags: 181 | - Characters 182 | x-code-samples: 183 | - lang: Curl 184 | source: | 185 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/characters/7 186 | - lang: Python 187 | source: | 188 | import urllib2 189 | 190 | urllib2.urlopen('https://twauapi.herokuapp.com/characters/7').read() 191 | - lang: JavaScript 192 | source: | 193 | var xmlhttp = new XMLHttpRequest(); 194 | var url = 'https://twauapi.herokuapp.com/characters/7'; 195 | 196 | xmlhttp.onreadystatechange = function() { 197 | if (this.readyState == 4 && this.status == 200) { 198 | var character = JSON.parse(this.responseText); 199 | console.log(character); 200 | } 201 | }; 202 | xmlhttp.open("GET", url, true); 203 | xmlhttp.send(); 204 | - lang: Ruby 205 | source: | 206 | require 'net/http' 207 | 208 | uri = URI('https://twauapi.herokuapp.com/characters/7') 209 | Net::HTTP.get(uri) # => String 210 | responses: 211 | 200: 212 | description: OK Success 213 | schema: 214 | $ref: '#/definitions/Character' 215 | examples: 216 | application/json: 217 | statusCode: 200 218 | body: 219 | id: 7 220 | name: "The Woodsman" 221 | species: "Human" 222 | gender: "Male" 223 | occupation: "Unknown" 224 | hairColour: "Brown" 225 | eyeColour: "Green" 226 | description: "The Woodsman is one of the few men who went toe to toe with Bigby in his Black Forest days and lived to tell the tale. In an attempt to save Little Red Riding Hood, he split the great wolf's belly open with his axe, filled him full of rocks, and threw the beast into a river. To his dismay his popularity faded. Even his name is forgotten, and he is only known as The Woodsman." 227 | imagePath: "https://vignette.wikia.nocookie.net/fables/images/4/47/ISC_Woody_Angry.png/revision/latest/scale-to-width-down/250?cb=20140528030339" 228 | '404': 229 | description: Not found 230 | schema: 231 | $ref: '#/definitions/Error' 232 | examples: 233 | application/json: 234 | { 235 | "statusCode": 404, 236 | "message": "Not found" 237 | } 238 | 239 | /locations: 240 | get: 241 | summary: All locations 242 | description: | 243 | `/locations` endpoint returns information about all the locations. 244 | tags: 245 | - Locations 246 | parameters: 247 | - fields: 248 | name: fields 249 | in: query 250 | description: comma-separated list of fields to include in the response 251 | required: false 252 | type: string 253 | - limit: 254 | name: limit 255 | in: query 256 | required: false 257 | description: amount of results 258 | type: integer 259 | format: int64 260 | x-code-samples: 261 | - lang: Curl 262 | source: | 263 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/locations 264 | - lang: Python 265 | source: | 266 | import urllib2 267 | 268 | urllib2.urlopen('https://twauapi.herokuapp.com/locations').read() 269 | - lang: JavaScript 270 | source: | 271 | var xmlhttp = new XMLHttpRequest(); 272 | var url = 'https://twauapi.herokuapp.com/locations'; 273 | 274 | xmlhttp.onreadystatechange = function() { 275 | if (this.readyState == 4 && this.status == 200) { 276 | var locations = JSON.parse(this.responseText); 277 | console.log(locations); 278 | } 279 | }; 280 | xmlhttp.open("GET", url, true); 281 | xmlhttp.send(); 282 | - lang: Ruby 283 | source: | 284 | require 'net/http' 285 | 286 | uri = URI('https:/twauapi.herokuapp.com/locations') 287 | Net::HTTP.get(uri) # => String 288 | responses: 289 | 200: 290 | description: OK Success 291 | schema: 292 | $ref: '#/definitions/Location' 293 | examples: 294 | application/json: 295 | { 296 | "statusCode": 200, 297 | "body":[ 298 | { 299 | "id": 1, 300 | "locationName": "New York City", 301 | "description": "Fabletown is situated in New York City", 302 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/43/FTH_Manhattan_Skyline.png/revision/latest/scale-to-width-down/1300?cb=20140826020954" 303 | }, 304 | { 305 | "id": 2, 306 | "locationName": "Toad's Tenement", 307 | "description": "A tenement building owned by Mr. Toad. The Woodsman owns an apartment in it.", 308 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/b/b5/FTH_Toad%27s_Tenement.png/revision/latest/scale-to-width-down/1300?cb=20140614235651" 309 | } 310 | ] 311 | } 312 | 313 | '404': 314 | description: Not found 315 | schema: 316 | $ref: '#/definitions/Error' 317 | examples: 318 | application/json: 319 | { 320 | "statusCode": 404, 321 | "message": "Not found" 322 | } 323 | 324 | /locations/{id}: 325 | get: 326 | summary: Location by ID 327 | description: | 328 | `/locations/{id}` endpoints returns information about a single location based on ID. 329 | parameters: 330 | - name: id 331 | in: path 332 | description: location `id` 333 | required: true 334 | type: integer 335 | format: int64 336 | - name: fields 337 | in: query 338 | description: comma-separated list of fields to include in the response 339 | required: false 340 | type: string 341 | tags: 342 | - Locations 343 | x-code-samples: 344 | - lang: Curl 345 | source: | 346 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/locations/19 347 | - lang: Python 348 | source: | 349 | import urllib2 350 | 351 | urllib2.urlopen('https://twauapi.herokuapp.com/locations/19').read() 352 | - lang: JavaScript 353 | source: | 354 | var xmlhttp = new XMLHttpRequest(); 355 | var url = 'https://twauapi.herokuapp.com/locations/19'; 356 | 357 | xmlhttp.onreadystatechange = function() { 358 | if (this.readyState == 4 && this.status == 200) { 359 | var location = JSON.parse(this.responseText); 360 | console.log(location); 361 | } 362 | }; 363 | xmlhttp.open("GET", url, true); 364 | xmlhttp.send(); 365 | - lang: Ruby 366 | source: | 367 | require 'net/http' 368 | 369 | uri = URI('https://twauapi.herokuapp.com/locations/19') 370 | Net::HTTP.get(uri) # => String 371 | responses: 372 | 200: 373 | description: OK Success 374 | schema: 375 | $ref: '#/definitions/Location' 376 | examples: 377 | application/json: 378 | statusCode: 200 379 | body: 380 | id: 19 381 | locationName: "Beauty and Beast's Apartment" 382 | description: "A lavish apartment in the Woodlands that Beauty and Beast live in." 383 | imagePath: "https://vignette.wikia.nocookie.net/fables/images/2/2d/ISC_Fishing_for_Sympathy.png/revision/latest/scale-to-width-down/1300?cb=20140531212737" 384 | '404': 385 | description: Not found 386 | schema: 387 | $ref: '#/definitions/Error' 388 | examples: 389 | application/json: 390 | { 391 | "statusCode": 404, 392 | "message": "Not found" 393 | } 394 | 395 | /episodes: 396 | get: 397 | summary: All episodes 398 | description: | 399 | `/episodes` endpoint returns information about the all current episodes. 400 | tags: 401 | - Episodes 402 | parameters: 403 | - fields: 404 | name: fields 405 | in: query 406 | description: comma-separated list of fields to include in the response 407 | required: false 408 | type: string 409 | - limit: 410 | name: limit 411 | in: query 412 | required: false 413 | description: amount of results 414 | type: integer 415 | format: int64 416 | x-code-samples: 417 | - lang: Curl 418 | source: | 419 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/episodes 420 | - lang: Ruby 421 | source: | 422 | require 'net/http' 423 | 424 | uri = URI('https://twauapi.herokuapp.com/episodes') 425 | Net::HTTP.get(uri) # => String 426 | - lang: Python 427 | source: | 428 | import urllib2 429 | 430 | urllib2.urlopen('https://twauapi.herokuapp.com/episodes').read() 431 | - lang: JavaScript 432 | source: | 433 | var xmlhttp = new XMLHttpRequest(); 434 | var url = 'https://twauapi.herokuapp.com/episodes'; 435 | 436 | xmlhttp.onreadystatechange = function() { 437 | if (this.readyState == 4 && this.status == 200) { 438 | var episodes = JSON.parse(this.responseText); 439 | console.log(episodes); 440 | } 441 | }; 442 | xmlhttp.open("GET", url, true); 443 | xmlhttp.send(); 444 | responses: 445 | 200: 446 | description: OK Success 447 | schema: 448 | $ref: '#/definitions/Episode' 449 | examples: 450 | application/json: 451 | { 452 | "statusCode": 200, 453 | "body":[ 454 | { 455 | "id": 1, 456 | "episodeName": "Faith", 457 | "episodeNum": 1, 458 | "season": 1, 459 | "description": "Bigby Wolf, Sheriff of Fabletown, must work a murder case when a Fable is killed and he must work with Snow White to find out who the murderer is. The two of them work together and the investigation goes deep, with a shocking ending.", 460 | "directedBy": "Nick Herman & Dennis Lenart", 461 | "writtenBy": "Pierre Shorette & Ryan Kaufman", 462 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/b/b6/FTH_Cover.png/revision/latest?cb=20140214011436" 463 | }, 464 | { 465 | "id": 2, 466 | "episodeName": "Smoke & Mirrors", 467 | "episodeNum": 2, 468 | "season": 1, 469 | "description": "The episode starts with Bigby being questioned by a female detective named Kelsey Brannigan. Bigby is reluctant to answer her questions. Suddenly, the detective and police officers in the room pass out, and it is shown that Crane had used a memory-wipe spell to let Bigby out.", 470 | "directedBy": "Jason Latino", 471 | "writtenBy": "Dave Grossman & Joe Pinney", 472 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/a/a2/SAM_Cover.png/revision/latest?cb=20140214011612" 473 | } 474 | ] 475 | } 476 | '404': 477 | description: Not found 478 | schema: 479 | $ref: '#/definitions/Error' 480 | examples: 481 | application/json: 482 | { 483 | "statusCode": 404, 484 | "message": "Not found" 485 | } 486 | 487 | /episodes/{id}: 488 | get: 489 | summary: Episode by ID 490 | description: | 491 | `/episodes` endpoint returns information about a single episode based on ID. 492 | tags: 493 | - Episodes 494 | parameters: 495 | - name: id 496 | in: path 497 | description: episodes `id` 498 | required: true 499 | type: integer 500 | format: int64 501 | - name: fields 502 | in: query 503 | description: comma-separated list of fields to include in the response 504 | required: false 505 | type: string 506 | x-code-samples: 507 | - lang: Curl 508 | source: | 509 | curl -X GET -H "Content-Type: application/json" https://twauapi.herokuapp.com/episodes/3 510 | - lang: Ruby 511 | source: | 512 | require 'net/http' 513 | 514 | uri = URI('https://twauapi.herokuapp.com/episodes/3') 515 | Net::HTTP.get(uri) # => String 516 | - lang: Python 517 | source: | 518 | import urllib2 519 | 520 | urllib2.urlopen('https://twauapi.herokuapp.com/episodes/3').read() 521 | - lang: JavaScript 522 | source: | 523 | var xmlhttp = new XMLHttpRequest(); 524 | var url = 'https://twauapi.herokuapp.com/episodes/3'; 525 | 526 | xmlhttp.onreadystatechange = function() { 527 | if (this.readyState == 4 && this.status == 200) { 528 | var episode = JSON.parse(this.responseText); 529 | console.log(episode); 530 | } 531 | }; 532 | xmlhttp.open("GET", url, true); 533 | xmlhttp.send(); 534 | responses: 535 | 200: 536 | description: OK Success 537 | schema: 538 | $ref: '#/definitions/Episode' 539 | examples: 540 | application/json: 541 | statusCode: 200 542 | body: 543 | id: 3 544 | episodeName: "A Crooked Mile" 545 | episodeNum: 3 546 | season: 1 547 | description: "Bigby has discovered that Crane is the murderer; a photo of him in bed with a murdered woman glamoured to look like Snow was found. He sees Beauty and Beast off to arrive at Lily's funeral, where Holly, Grendel, Prince Lawrence, Vivian and Nerissa are all attending. Eventually, Snow returns to the funeral and asks Bigby to wait at a distance." 548 | directedBy: " Martin Montgomery" 549 | writtenBy: "Adam Hines & Ryan Kaufman" 550 | imagePath: "https://vignette.wikia.nocookie.net/fables/images/0/03/ACM_Cover.png/revision/latest/?cb=20140214012342" 551 | '404': 552 | description: Not found 553 | schema: 554 | $ref: '#/definitions/Error' 555 | examples: 556 | application/json: 557 | { 558 | "statusCode": 404, 559 | "message": "Not found" 560 | } 561 | 562 | definitions: 563 | Character: 564 | type: object 565 | properties: 566 | statusCode: 567 | type: integer 568 | description: Response status code 569 | body: 570 | type: array 571 | description: Response body 572 | items: 573 | $ref: '#/definitions/Characters' 574 | Characters: 575 | type: object 576 | properties: 577 | id: 578 | type: integer 579 | description: Unique identifier representing a specific character 580 | name: 581 | type: string 582 | description: Name of the character 583 | species: 584 | type: string 585 | description: Species of the character 586 | gender: 587 | type: string 588 | description: Gender of the character 589 | occupation: 590 | type: string 591 | description: Occupation of the character (if known) 592 | hairColour: 593 | type: string 594 | description: Hair colour of the character 595 | eyeColour: 596 | type: string 597 | description: Eye colour of the character 598 | description: 599 | type: string 600 | description: Brief description of the character 601 | imagePath: 602 | type: string 603 | description: Image of the character URL 604 | Location: 605 | type: object 606 | properties: 607 | statusCode: 608 | type: integer 609 | description: Response status code 610 | body: 611 | type: array 612 | description: Response body 613 | items: 614 | $ref: '#/definitions/Locations' 615 | Locations: 616 | type: object 617 | properties: 618 | id: 619 | type: integer 620 | description: Unique identifier representing a specific location 621 | locationName: 622 | type: string 623 | description: Name of location 624 | description: 625 | type: string 626 | description: Brief description about the location 627 | imagePath: 628 | type: string 629 | description: Image of the location URL 630 | Episode: 631 | type: object 632 | properties: 633 | statusCode: 634 | type: integer 635 | description: Response status code 636 | body: 637 | type: array 638 | description: Response body 639 | items: 640 | $ref: '#/definitions/Episodes' 641 | Episodes: 642 | type: object 643 | properties: 644 | id: 645 | type: integer 646 | description: Unique identifier representing a specific episode 647 | episodeName: 648 | type: string 649 | description: Name of the episode 650 | episodeNum: 651 | type: integer 652 | description: Episode number in it's respected season 653 | season: 654 | type: integer 655 | description: Season number of episode 656 | description: 657 | type: string 658 | description: Brief description of the episode 659 | directedBy: 660 | type: string 661 | description: Episode director(s) 662 | writtenBy: 663 | type: string 664 | description: Episode writer(s) 665 | imagePath: 666 | type: string 667 | description: Image of the episode URL 668 | Error: 669 | type: object 670 | description: Error reponse 671 | properties: 672 | statusCode: 673 | type: integer 674 | format: int32 675 | description: HTTP status code 676 | message: 677 | type: string 678 | description: Status code message 679 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | 429 | html, 430 | body, 431 | .content h1, 432 | .content h2, 433 | .content h3, 434 | .content h4, 435 | .content h5, 436 | .content h6 { 437 | font-family: "Helvetica Neue", Helvetica, Arial, "Microsoft Yahei", "微软雅黑", STXihei, "华文细黑", sans-serif; 438 | font-size: 13px; 439 | } 440 | .content h1, 441 | .content h2, 442 | .content h3, 443 | .content h4, 444 | .content h5, 445 | .content h6 { 446 | font-weight: bold; 447 | } 448 | .content code, 449 | .content pre { 450 | font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif; 451 | font-size: 12px; 452 | line-height: 1.5; 453 | } 454 | .content code { 455 | word-break: break-all; 456 | /* Non standard for webkit */ 457 | word-break: break-word; 458 | hyphens: auto; 459 | } 460 | @font-face { 461 | font-family: FontAwesome; 462 | font-style: normal; 463 | font-weight: bold; 464 | src: url("fonts/fontawesome-webfont.eot?v=#4.0.3"); 465 | src: url("fonts/fontawesome-webfont.eot?#iefix&v=#4.0.3") format("embedded-opentype"), url("fonts/fontawesome-webfont.woff?v=#4.0.3") format("woff"), url("fonts/fontawesome-webfont.ttf?v=#4.0.3") format("truetype"), url("fonts/fontawesome-webfont.svg#fontawesomeregular?v=#4.0.3") format("svg"); 466 | } 467 | .tocify-wrapper > .search:before, 468 | .content aside.notice:before, 469 | .content aside.warning:before, 470 | .content aside.success:before { 471 | font-family: 'FontAwesome'; 472 | speak: none; 473 | font-style: normal; 474 | font-weight: normal; 475 | font-variant: normal; 476 | text-transform: none; 477 | line-height: 1; 478 | } 479 | .content aside.warning:before { 480 | content: "\f06a"; 481 | } 482 | .content aside.notice:before { 483 | content: "\f05a"; 484 | } 485 | .content aside.success:before { 486 | content: "\f058"; 487 | } 488 | .tocify-wrapper > .search:before { 489 | content: "\f002"; 490 | } 491 | .highlight .c, 492 | .highlight .cm, 493 | .highlight .c1, 494 | .highlight .cs { 495 | color: #909090; 496 | } 497 | .highlight, 498 | .highlight .w { 499 | background-color: #292929; 500 | } 501 | /* 502 | 503 | Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ 504 | 505 | */ 506 | 507 | .hljs { 508 | display: block; 509 | overflow-x: auto; 510 | padding: 0.5em; 511 | background: #23241f; 512 | } 513 | 514 | .hljs, 515 | .hljs-tag, 516 | .hljs-subst { 517 | color: #f8f8f2; 518 | } 519 | 520 | .hljs-strong, 521 | .hljs-emphasis { 522 | color: #a8a8a2; 523 | } 524 | 525 | .hljs-bullet, 526 | .hljs-quote, 527 | .hljs-number, 528 | .hljs-regexp, 529 | .hljs-literal, 530 | .hljs-link { 531 | color: #ae81ff; 532 | } 533 | 534 | .hljs-code, 535 | .hljs-title, 536 | .hljs-section, 537 | .hljs-selector-class { 538 | color: #a6e22e; 539 | } 540 | 541 | .hljs-strong { 542 | font-weight: bold; 543 | } 544 | 545 | .hljs-emphasis { 546 | font-style: italic; 547 | } 548 | 549 | .hljs-keyword, 550 | .hljs-selector-tag, 551 | .hljs-name, 552 | .hljs-attr { 553 | color: #f92672; 554 | } 555 | 556 | .hljs-symbol, 557 | .hljs-attribute { 558 | color: #66d9ef; 559 | } 560 | 561 | .hljs-params, 562 | .hljs-class .hljs-title { 563 | color: #f8f8f2; 564 | } 565 | 566 | .hljs-string, 567 | .hljs-type, 568 | .hljs-built_in, 569 | .hljs-builtin-name, 570 | .hljs-selector-id, 571 | .hljs-selector-attr, 572 | .hljs-selector-pseudo, 573 | .hljs-addition, 574 | .hljs-variable, 575 | .hljs-template-variable { 576 | color: #e6db74; 577 | } 578 | 579 | .hljs-comment, 580 | .hljs-deletion, 581 | .hljs-meta { 582 | color: #75715e; 583 | } 584 | 585 | /* 586 | Copyright 2008-2013 Concur Technologies, Inc. 587 | 588 | Licensed under the Apache License, Version 2.0 (the "License"); you may 589 | not use this file except in compliance with the License. You may obtain 590 | a copy of the License at 591 | 592 | http://www.apache.org/licenses/LICENSE-2.0 593 | 594 | Unless required by applicable law or agreed to in writing, software 595 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 596 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 597 | License for the specific language governing permissions and limitations 598 | under the License. 599 | */ 600 | html, 601 | body { 602 | color: #333; 603 | padding: 0; 604 | margin: 0; 605 | -webkit-font-smoothing: antialiased; 606 | -moz-osx-font-smoothing: grayscale; 607 | background-color: #eaf2f6; 608 | height: 100%; 609 | -webkit-text-size-adjust: none; 610 | /* Never autoresize text */ 611 | } 612 | #toc > ul > li > a > span { 613 | float: right; 614 | background-color: #2484ff; 615 | border-radius: 40px; 616 | width: 20px; 617 | } 618 | .tocify-wrapper { 619 | transition: left 0.3s ease-in-out; 620 | overflow-y: auto; 621 | overflow-x: hidden; 622 | position: fixed; 623 | z-index: 30; 624 | top: 0; 625 | left: 0; 626 | bottom: 0; 627 | width: 230px; 628 | background-color: #393939; 629 | font-size: 13px; 630 | font-weight: bold; 631 | } 632 | .tocify-wrapper .lang-selector { 633 | display: none; 634 | } 635 | .tocify-wrapper .lang-selector a { 636 | padding-top: 0.5em; 637 | padding-bottom: 0.5em; 638 | } 639 | .tocify-wrapper > img { 640 | display: block; 641 | } 642 | .tocify-wrapper > .search { 643 | position: relative; 644 | } 645 | .tocify-wrapper > .search input { 646 | background: #393939; 647 | border-width: 0 0 1px 0; 648 | border-color: #666; 649 | padding: 6px 0 6px 20px; 650 | box-sizing: border-box; 651 | margin: 10px 15px; 652 | width: 200px; 653 | outline: none; 654 | color: #fff; 655 | border-radius: 0; 656 | /* ios has a default border radius */ 657 | } 658 | .tocify-wrapper > .search:before { 659 | position: absolute; 660 | top: 17px; 661 | left: 15px; 662 | color: #fff; 663 | } 664 | .tocify-wrapper img+.tocify { 665 | margin-top: 20px; 666 | } 667 | .tocify-wrapper .search-results { 668 | margin-top: 0; 669 | box-sizing: border-box; 670 | height: 0; 671 | overflow-y: auto; 672 | overflow-x: hidden; 673 | transition-property: height, margin; 674 | transition-duration: 180ms; 675 | transition-timing-function: ease-in-out; 676 | background: linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to top, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to bottom, #000, rgba(0,0,0,0) 1.5px), linear-gradient(to top, #939393, rgba(147,147,147,0) 1.5px), #262626; 677 | } 678 | .tocify-wrapper .search-results.visible { 679 | height: 30%; 680 | margin-bottom: 1em; 681 | } 682 | .tocify-wrapper .search-results li { 683 | margin: 1em 15px; 684 | line-height: 1; 685 | } 686 | .tocify-wrapper .search-results a { 687 | color: #fff; 688 | text-decoration: none; 689 | } 690 | .tocify-wrapper .search-results a:hover { 691 | text-decoration: underline; 692 | } 693 | .tocify-wrapper .tocify-item>a, 694 | .tocify-wrapper .toc-footer li { 695 | padding: 0 15px 0 15px; 696 | display: block; 697 | overflow-x: hidden; 698 | white-space: nowrap; 699 | text-overflow: ellipsis; 700 | } 701 | .tocify-wrapper ul, 702 | .tocify-wrapper li { 703 | list-style: none; 704 | margin: 0; 705 | padding: 0; 706 | line-height: 28px; 707 | } 708 | .tocify-wrapper li { 709 | color: #fff; 710 | transition-property: background; 711 | transition-timing-function: linear; 712 | transition-duration: 230ms; 713 | } 714 | .tocify-wrapper .tocify-focus { 715 | box-shadow: 0px 1px 0px #000; 716 | background-color: #2467af; 717 | color: #fff; 718 | } 719 | .tocify-wrapper .tocify-subheader { 720 | display: none; 721 | background-color: #262626; 722 | font-weight: 500; 723 | background: linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to top, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to bottom, #000, rgba(0,0,0,0) 1.5px), linear-gradient(to top, #939393, rgba(147,147,147,0) 1.5px), #262626; 724 | } 725 | .tocify-wrapper .tocify-subheader .tocify-item>a { 726 | padding-left: 25px; 727 | font-size: 12px; 728 | } 729 | .tocify-wrapper .tocify-subheader > li:last-child { 730 | box-shadow: none; 731 | } 732 | .tocify-wrapper .tocify-subheader .tocify-subheader { 733 | display: none; 734 | background-color: #262626; 735 | font-weight: 500; 736 | } 737 | .tocify-wrapper .tocify-subheader .tocify-subheader .tocify-item>a { 738 | padding-left: 55px; 739 | font-size: 12px; 740 | background: linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to top, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to bottom, #000, rgba(0,0,0,0) 1.5px), linear-gradient(to top, #939393, rgba(147,147,147,0) 1.5px), #262626; 741 | } 742 | .tocify-wrapper .tocify-subheader .tocify-subheader .tocify-item>a > li:last-child { 743 | box-shadow: none; 744 | } 745 | .tocify-wrapper .tocify-subheader .tocify-subheader .tocify-subheader { 746 | display: none; 747 | background-color: #262626; 748 | font-weight: 500; 749 | } 750 | .tocify-wrapper .tocify-subheader .tocify-subheader .tocify-subheader .tocify-item>a { 751 | padding-left: 75px; 752 | font-size: 12px; 753 | background: linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to top, rgba(0,0,0,0.2), rgba(0,0,0,0) 8px), linear-gradient(to bottom, #000, rgba(0,0,0,0) 1.5px), linear-gradient(to top, #939393, rgba(147,147,147,0) 1.5px), #262626; 754 | } 755 | .tocify-wrapper .tocify-subheader .tocify-subheader .tocify-subheader .tocify-item>a > li:last-child { 756 | box-shadow: none; 757 | } 758 | .tocify-wrapper .toc-footer { 759 | padding: 1em 0; 760 | margin-top: 1em; 761 | border-top: 1px dashed #666; 762 | } 763 | .tocify-wrapper .toc-footer li, 764 | .tocify-wrapper .toc-footer a { 765 | color: #fff; 766 | text-decoration: none; 767 | } 768 | .tocify-wrapper .toc-footer a:hover { 769 | text-decoration: underline; 770 | } 771 | .tocify-wrapper .toc-footer li { 772 | font-size: 0.8em; 773 | line-height: 1.7; 774 | text-decoration: none; 775 | } 776 | #nav-button { 777 | padding: 0 1.5em 5em 0; 778 | display: none; 779 | position: fixed; 780 | top: 0; 781 | left: 0; 782 | z-index: 100; 783 | color: #000; 784 | text-decoration: none; 785 | font-weight: bold; 786 | opacity: 0.7; 787 | line-height: 16px; 788 | transition: left 0.3s ease-in-out; 789 | } 790 | #nav-button span { 791 | display: block; 792 | padding: 6px 6px 6px; 793 | background-color: rgba(234,242,246,0.7); 794 | transform-origin: 0 0; 795 | transform: rotate(-90deg) translate(-100%, 0); 796 | border-radius: 0 0 0 5px; 797 | } 798 | #nav-button img { 799 | height: 16px; 800 | vertical-align: bottom; 801 | } 802 | #nav-button:hover { 803 | opacity: 1; 804 | } 805 | #nav-button.open { 806 | left: 230px; 807 | } 808 | .page-wrapper { 809 | margin-left: 230px; 810 | position: relative; 811 | z-index: 10; 812 | background-color: #eaf2f6; 813 | min-height: 100%; 814 | padding-bottom: 1px; 815 | } 816 | .page-wrapper .dark-box { 817 | width: 50%; 818 | background-color: #393939; 819 | position: absolute; 820 | right: 0; 821 | top: 0; 822 | bottom: 0; 823 | } 824 | .page-wrapper .lang-selector { 825 | position: fixed; 826 | z-index: 50; 827 | border-bottom: 5px solid #393939; 828 | } 829 | .lang-selector { 830 | background-color: #222; 831 | width: 100%; 832 | font-weight: bold; 833 | } 834 | .lang-selector a { 835 | display: block; 836 | float: left; 837 | color: #fff; 838 | text-decoration: none; 839 | padding: 0 10px; 840 | line-height: 30px; 841 | outline: 0; 842 | } 843 | .lang-selector a:active, 844 | .lang-selector a:focus { 845 | background-color: #111; 846 | color: #fff; 847 | } 848 | .lang-selector a.active { 849 | background-color: #393939; 850 | color: #fff; 851 | } 852 | .lang-selector:after { 853 | content: ''; 854 | clear: both; 855 | display: block; 856 | } 857 | .content { 858 | position: relative; 859 | z-index: 30; 860 | } 861 | .content:after { 862 | content: ''; 863 | display: block; 864 | clear: both; 865 | } 866 | .content > h1, 867 | .content > h2, 868 | .content > h3, 869 | .content > h4, 870 | .content > h5, 871 | .content > h6, 872 | .content > p, 873 | .content > table, 874 | .content > ul, 875 | .content > ol, 876 | .content > aside, 877 | .content > dl { 878 | margin-right: 50%; 879 | padding: 0 28px; 880 | box-sizing: border-box; 881 | display: block; 882 | } 883 | .content > ul, 884 | .content > ol { 885 | padding-left: 43px; 886 | } 887 | .content > h1, 888 | .content > h2, 889 | .content > div { 890 | clear: both; 891 | } 892 | .content h1 { 893 | font-size: 30px; 894 | padding-top: 0.5em; 895 | padding-bottom: 0.5em; 896 | border-bottom: 1px solid #ccc; 897 | margin-bottom: 21px; 898 | margin-top: 2em; 899 | border-top: 1px solid #ddd; 900 | background-image: linear-gradient(to bottom, #fff, #f9f9f9); 901 | } 902 | .content h1:first-child, 903 | .content div:first-child + h1 { 904 | border-top-width: 0; 905 | margin-top: 0; 906 | } 907 | .content h2 { 908 | font-size: 20px; 909 | margin-top: 4em; 910 | margin-bottom: 0; 911 | border-top: 1px solid #ccc; 912 | padding-top: 1.2em; 913 | padding-bottom: 1.2em; 914 | background-image: linear-gradient(to bottom, rgba(255,255,255,0.4), rgba(255,255,255,0)); 915 | } 916 | .content h1 + h2, 917 | .content h1 + div + h2 { 918 | margin-top: -21px; 919 | border-top: none; 920 | } 921 | .content h3, 922 | .content h4, 923 | .content h5, 924 | .content h6 { 925 | font-size: 15px; 926 | margin-top: 2.5em; 927 | margin-bottom: 0.8em; 928 | } 929 | .content h4, 930 | .content h5, 931 | .content h6 { 932 | font-size: 10px; 933 | } 934 | .content hr { 935 | margin: 2em 0; 936 | border-top: 2px solid #393939; 937 | border-bottom: 2px solid #eaf2f6; 938 | } 939 | .content table { 940 | margin-bottom: 1em; 941 | overflow: auto; 942 | } 943 | .content table th, 944 | .content table td { 945 | text-align: left; 946 | vertical-align: top; 947 | line-height: 1.6; 948 | } 949 | .content table th { 950 | padding: 5px 10px; 951 | border-bottom: 1px solid #ccc; 952 | vertical-align: bottom; 953 | } 954 | .content table td { 955 | padding: 10px; 956 | } 957 | .content table tr:last-child { 958 | border-bottom: 1px solid #ccc; 959 | } 960 | .content table tr:nth-child(odd) > td { 961 | background-color: #ebf3f6; 962 | } 963 | .content table tr:nth-child(even) > td { 964 | background-color: #ebf2f6; 965 | } 966 | .content dt { 967 | font-weight: bold; 968 | } 969 | .content dd { 970 | margin-left: 15px; 971 | } 972 | .content p, 973 | .content li, 974 | .content dt, 975 | .content dd { 976 | line-height: 1.6; 977 | margin-top: 0; 978 | } 979 | .content img { 980 | max-width: 100%; 981 | } 982 | .content code { 983 | background-color: rgba(0,0,0,0.05); 984 | padding: 3px; 985 | border-radius: 3px; 986 | } 987 | .content pre>code { 988 | background-color: transparent; 989 | padding: 0; 990 | } 991 | .content aside { 992 | padding-top: 1em; 993 | padding-bottom: 1em; 994 | #a0c6da 995 | margin-top: 1.5em; 996 | margin-bottom: 1.5em; 997 | background: #8fbcd4; 998 | line-height: 1.6; 999 | } 1000 | .content aside.warning { 1001 | background-color: #c97a7e; 1002 | #d18e91 1003 | } 1004 | .content aside.success { 1005 | background-color: #6ac174; 1006 | #80ca89 1007 | } 1008 | .content aside:before { 1009 | vertical-align: middle; 1010 | padding-right: 0.5em; 1011 | font-size: 14px; 1012 | } 1013 | .content .search-highlight { 1014 | padding: 2px; 1015 | margin: -2px; 1016 | border-radius: 4px; 1017 | border: 1px solid #f7e633; 1018 | background: linear-gradient(to top left, #f7e633 0%, #f1d32f 100%); 1019 | } 1020 | .content pre, 1021 | .content blockquote { 1022 | background-color: #292929; 1023 | color: #fff; 1024 | padding: 2em 28px; 1025 | margin: 0; 1026 | width: 50%; 1027 | float: right; 1028 | clear: right; 1029 | box-sizing: border-box; 1030 | rgba(0,0,0,0.4) 1031 | } 1032 | .content pre > p, 1033 | .content blockquote > p { 1034 | margin: 0; 1035 | } 1036 | .content pre a, 1037 | .content blockquote a { 1038 | color: #fff; 1039 | text-decoration: none; 1040 | border-bottom: dashed 1px #ccc; 1041 | } 1042 | .content blockquote > p { 1043 | background-color: #1c1c1c; 1044 | border-radius: 5px; 1045 | padding: 13px; 1046 | color: #ccc; 1047 | border-top: 1px solid #000; 1048 | border-bottom: 1px solid #404040; 1049 | } 1050 | @media (max-width: 930px) { 1051 | .tocify-wrapper { 1052 | left: -230px; 1053 | } 1054 | .tocify-wrapper.open { 1055 | left: 0; 1056 | } 1057 | .page-wrapper { 1058 | margin-left: 0; 1059 | } 1060 | #nav-button { 1061 | display: block; 1062 | } 1063 | .tocify-wrapper .tocify-item > a { 1064 | padding-top: 0.3em; 1065 | padding-bottom: 0.3em; 1066 | } 1067 | } 1068 | @media (max-width: 700px) { 1069 | .dark-box { 1070 | display: none; 1071 | } 1072 | .tocify-wrapper .lang-selector { 1073 | display: block; 1074 | } 1075 | .page-wrapper .lang-selector { 1076 | display: none; 1077 | } 1078 | .content h1, 1079 | .content h2, 1080 | .content h3, 1081 | .content h4, 1082 | .content h5, 1083 | .content h6, 1084 | .content p, 1085 | .content table, 1086 | .content ul, 1087 | .content ol, 1088 | .content aside, 1089 | .content dl { 1090 | margin-right: 0; 1091 | } 1092 | .content pre, 1093 | .content blockquote { 1094 | float: none; 1095 | width: auto; 1096 | } 1097 | } 1098 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "characters": [ 3 | { 4 | "id": 1, 5 | "name": "Bigby Wolf", 6 | "species": "Wolf", 7 | "gender": "Male", 8 | "occupation": "Fabletown Sherrif", 9 | "hairColour": "Mahogany", 10 | "eyeColour": "Brown", 11 | "description": "The renowned Big Bad Wolf. He's known for tormenting pigs and girls in red hoods, but is trying to put those dark days behind him. Bigby now acts as Fabletown's sheriff and remains in his human form, mostly. However, due to his rough past, the citizens of Fabletown are slow to trust him. Bigby is determined to show that he's truly changed, but some instincts are just too hard to control", 12 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2b/CW_Bigby_Nerissa_Convo.png/revision/latest?cb=20140710174447" 13 | }, 14 | { 15 | "id": 2, 16 | "name": "Winter Wolf", 17 | "species": "Wolf", 18 | "gender": "Female", 19 | "occupation": "Unknown", 20 | "hairColour": "White", 21 | "eyeColour": "Unknown", 22 | "description": "Winter was the mother of Bigby Wolf and his brothers. Winter was a good and caring mother and Bigby regards her very highly and was displeased that his father, Mr. North, left her. Despite that, Winter loved and was devoted to him even to the moment of her death.", 23 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/e/e2/Winter_Wolf.png/revision/latest?cb=20140808191333" 24 | }, 25 | { 26 | "id": 3, 27 | "name": "Snow White", 28 | "species": "Human", 29 | "gender": "Female", 30 | "occupation": "Fabletown Deputy Mayor", 31 | "hairColour": "Black", 32 | "eyeColour": "Blue", 33 | "description": "Snow White may seem cold, but this stems from her life of mistreatment and abuse back in the Homelands. After escaping assault and imprisonment, not to mention an attempt on her life, she married Prince Charming. It wasn't long before Snow discovered that Charming cheated on her with her estranged sister, Rose Red, and she divorced him. After the Exodus, Snow focused her attention on setting up a safe haven for Fables in the New World. She now serves as Assistant to the Deputy Mayor of Fabletown.", 34 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/74/WD_E32013_PORTRAITS_snow_5uupawjwjx_.jpg/revision/latest?cb=20180528022235" 35 | }, 36 | { 37 | "id": 4, 38 | "name": "Ichabod Crane", 39 | "species": "Human", 40 | "gender": "Male", 41 | "occupation": "Fabletown Former Deputy Mayor", 42 | "hairColour": "Grey", 43 | "eyeColour": "Green", 44 | "description": "Hailing from the haunted town of Sleepy Hollow, Ichabod Crane has been Deputy Mayor of Fabletown for nearly 115 years. Crane is a bundle of nerves and takes his job very seriously. Though that doesn't mean he always does it well. As one of Fabletown's elite, Crane is often blind to the troubles of the less well-off citizens. Overall, Crane is authoritarian, cowardly, and always hiding something.", 45 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/58/SAM_Crane_Driving.png/revision/latest/scale-to-width-down/250?cb=20140407201736" 46 | }, 47 | { 48 | "id": 5, 49 | "name": "Bufkin", 50 | "species": "Monkey", 51 | "gender": "Male", 52 | "occupation": "Librarian", 53 | "hairColour": "Green", 54 | "eyeColour": "Black", 55 | "description": "Bufkin is the talking, winged monkey from the land of Oz. Now, as Fabletown's librarian, he spends his time reading and stealing the deputy mayor's booze. He's prone to mischief, so when something goes wrong he assumes he'll receive the lion's share of the blame. He's helpful when he wants to be, but most of the time he'd rather be drinking. Someone would have fired him a long time ago, but he's the only one who can make sense of the filing system.", 56 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/d/dc/ISC_Bufkin_Mirror.png/revision/latest/scale-to-width-down/250?cb=20140527224740" 57 | }, 58 | { 59 | "id": 6, 60 | "name": "Magic Mirror", 61 | "species": "Unknown", 62 | "gender": "Male", 63 | "occupation": "Unknown", 64 | "hairColour": "Bald", 65 | "eyeColour": "Black", 66 | "description": "The Magic Mirror speaks mostly in rhyme and demands that others do the same. He also requires the name of whatever object or person you wish to find. If you follow these rules, the mirror will show you a glimpse of whatever you want to see, but nothing more.", 67 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/e/ec/FTH_Mirror.png/revision/latest/scale-to-width-down/250?cb=20140331004413" 68 | }, 69 | { 70 | "id": 7, 71 | "name": "The Woodsman", 72 | "species": "Human", 73 | "gender": "Male", 74 | "occupation": "Unknown", 75 | "hairColour": "Brown", 76 | "eyeColour": "Green", 77 | "description": "The Woodsman is one of the few men who went toe to toe with Bigby in his Black Forest days and lived to tell the tale. In an attempt to save Little Red Riding Hood, he split the great wolf's belly open with his axe, filled him full of rocks, and threw the beast into a river. To his dismay his popularity faded. Even his name is forgotten, and he is only known as The Woodsman.", 78 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/47/ISC_Woody_Angry.png/revision/latest/scale-to-width-down/250?cb=20140528030339" 79 | }, 80 | { 81 | "id": 8, 82 | "name": "Mr. Toad", 83 | "species": "Toad", 84 | "gender": "Male", 85 | "occupation": "Tenement Superintendent", 86 | "hairColour": "None", 87 | "eyeColour": "Green", 88 | "description": "Mr. Toad is the superintendent for a defunct tenement on the edge of Fabletown proper. Because he's a three and a half foot talking amphibian, Toad is required by Fabletown law to keep his family and himself magically glamoured to appear human. The problem is Toad isn't too concerned with what the law is, and has to be reminded often.", 89 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/7c/CW_Toad_Truck.png/revision/latest/scale-to-width-down/250?cb=20140709085854" 90 | }, 91 | { 92 | "id": 9, 93 | "name": "Toad Jr.", 94 | "species": "Toad", 95 | "gender": "Male", 96 | "occupation": "Unknown", 97 | "hairColour": "None", 98 | "eyeColour": "Green", 99 | "description": "Not much is known about Toad Jr. before the events of in Fabletown, other than that after leaving the Homelands, he lives in an apartment with his father, Mr. Toad.", 100 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/51/CW_TJ_Leaving.png/revision/latest/scale-to-width-down/250?cb=20140708203540" 101 | }, 102 | { 103 | "id": 10, 104 | "name": "Lawrence", 105 | "species": "Human", 106 | "gender": "Male", 107 | "occupation": "Former Prince", 108 | "hairColour": "Black", 109 | "eyeColour": "Blue", 110 | "description": "After escaping the Homelands, Prince Lawrence and his wife, Faith, immediately fell victim to the harsh realities of the mundane world. They moved to New York hoping to find aid in a community of fellow Fables, but without enough money to live in Fabletown they had to settle on an apartment on the outskirts of the neighborhood. Unfortunately that meant they were out of sight and out of mind when it came to government assistance. Their prospects dwindling, Faith left Lawrence to try to make it on her own. Now, without his wife for support, Lawrence struggles to motivate himself and quickly sinks into depression.", 111 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/51/CW_Lawrence_Debate.png/revision/latest/scale-to-width-down/250?cb=20140709085922" 112 | }, 113 | { 114 | "id": 11, 115 | "name": "Faith", 116 | "species": "Human", 117 | "gender": "Female", 118 | "occupation": "Prostitute", 119 | "hairColour": "Black", 120 | "eyeColour": "Green", 121 | "description": "Faith, otherwise known as Donkeyskin girl, made it through the Exodus from the Homelands with the clothes on her back, her husband, and nothing else. She was once a beautiful princess, happily married to Prince Lawrence of a neighboring kingdom. Her life should have had a happy ending, but the mundane city of New York wasn't kind to her, or her marriage. With no money, Faith found herself turning tricks to make the rent for a cheap apartment on the outskirts of Fabletown. She had a difficult life, but she did what she could to survive in an unfamiliar world.", 122 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/59/FTH_Faith_Street.png/revision/latest/scale-to-width-down/250?cb=20140211211628" 123 | }, 124 | { 125 | "id": 12, 126 | "name": "Edward", 127 | "species": "Human", 128 | "gender": "Male", 129 | "occupation": "King", 130 | "hairColour": "Unknown", 131 | "eyeColour": "Grey", 132 | "description": "Father of Faith. Edward does not make a physical appearance in The Wolf Among Us. However, during a talk between Bigby and the Magic Mirror, Bigby asks him to show Faith's father, the Magic Mirror proceeds to show Bigby Edward's skeletal remains.", 133 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/23/I5_FTWAU_Edward.PNG/revision/latest/scale-to-width-down/250?cb=20150124195615" 134 | }, 135 | { 136 | "id": 13, 137 | "name": "Grimble", 138 | "species": "Troll", 139 | "gender": "Male", 140 | "occupation": "Security Guard", 141 | "hairColour": "Black", 142 | "eyeColour": "Green", 143 | "description": "Grimble is a Fable troll who works as a security guard at the Woodland building in Fabletown. Grimble uses glamour to appear human, and is often seen sleeping behind his desk.", 144 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/6/67/FTH_Grimble_Sleeping.png/revision/latest/scale-to-width-down/250?cb=20140504024554" 145 | }, 146 | { 147 | "id": 14, 148 | "name": "Cryer", 149 | "species": "Human", 150 | "gender": "Male", 151 | "occupation": "Unknown", 152 | "hairColour": "Ginger", 153 | "eyeColour": "Green", 154 | "description": "Cryer is a civilian in Fabletown", 155 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/5d/FTH_Cryer.png/revision/latest/scale-to-width-down/250?cb=20140223003727" 156 | }, 157 | { 158 | "id": 15, 159 | "name": "Bluebeard", 160 | "species": "Human", 161 | "gender": "Male", 162 | "occupation": "Former Serial Killer", 163 | "hairColour": "Blue", 164 | "eyeColour": "Tawny", 165 | "description": "Bluebeard managed to escape the Homelands with his riches intact, and continues to be one of the wealthiest Fables in New York. The Fabletown government depends on his generous contributions, and he often uses this influence for his own benefit. As a former serial killer, he claims his days of decapitating his brides are over. But even if he was able to leave his violent ways in the Homelands, that hasn't stopped him from making the occasional trip down Crooked Lane.", 166 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/cc/CW_Bluebeard_Debate.png/revision/latest/scale-to-width-down/250?cb=20140709090025" 167 | }, 168 | { 169 | "id": 16, 170 | "name": "Dr. Swineheart", 171 | "species": "Human", 172 | "gender": "Male", 173 | "occupation": "Doctor", 174 | "hairColour": "Grey", 175 | "eyeColour": "Blue", 176 | "description": "Dr. Swineheart is the resident Fabletown physician. So skilled in the art of instrumental surgery that he can safely operate on himself, he served as an army medic for years, sometimes using his talents to impress the locals. He currently runs the \"Special Research Section\" of the Knights of Malta Hospital, so named to discourage people for investigating what is actually a reserved, Fables-focused health facility.", 177 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/4d/ISC_Swineheart_Stern.png/revision/latest/scale-to-width-down/250?cb=20140601214447" 178 | }, 179 | { 180 | "id": 17, 181 | "name": "Beauty", 182 | "species": "Human", 183 | "gender": "Female", 184 | "occupation": "Hotel Clerk", 185 | "hairColour": "Blonde", 186 | "eyeColour": "Blue", 187 | "description": "Beauty and her husband, Beast, once lived in an enchanted castle, but they were forced to flee the Homelands during the Exodus, leaving all of their wealth behind. Now they live in a modest studio in Fabletown, New York. Though times are hard, with Beast working multiple jobs to pay the bills, the couple have the longest lasting relationship of all the Fables.", 188 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/c8/CW_Beauty_Trial.png/revision/latest/scale-to-width-down/250?cb=20140708203823" 189 | }, 190 | { 191 | "id": 18, 192 | "name": "Beast", 193 | "species": "Beast", 194 | "gender": "Male", 195 | "occupation": "Unknown", 196 | "hairColour": "Brown", 197 | "eyeColour": "Brown", 198 | "description": "Beast and his wife, Beauty, left everything behind when they escaped the Homelands in the Exodus. Without his former wealth, Beast must pick up extra work to make ends meet. He is able to get around Fabletown without a glamour most of the time, but if Beauty gets too angry with him he becomes more beastly by the minute, growing horns and large teeth. Despite the occasional bickering, the two are truly in love and have the longest lasting relationship of anyone in Fabletown.", 199 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2a/CW_Beast_Angry.png/revision/latest/scale-to-width-down/240?cb=20140709085946" 200 | }, 201 | { 202 | "id": 19, 203 | "name": "Colin", 204 | "species": "Pig", 205 | "gender": "Male", 206 | "occupation": "None", 207 | "hairColour": "Black", 208 | "eyeColour": "Unknown", 209 | "description": "Colin is better known as one of the Three Little Pigs. Back in the Homelands they were harassed by The Big Bad Wolf, who blew down Colin's house of straw. After the Exodus, Colin and the other Fables who couldn't pass for human were sent to live at The Farm in upstate New York. Unable to stand such a boring life, Colin constantly makes trips down to Fabletown to bother Bigby. He is always caught and sent back to The Farm, but he doesn't let that stop him.", 210 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/9/99/CW_Colin_Beer.png/revision/latest/scale-to-width-down/250?cb=20140708203958" 211 | }, 212 | { 213 | "id": 20, 214 | "name": "Flycatcher", 215 | "species": "Human", 216 | "gender": "Male", 217 | "occupation": "Janitor", 218 | "hairColour": "Brown", 219 | "eyeColour": "Green", 220 | "description": "A former prince turned to a frog by a witch, the friendly, genial Flycatcher now carries the nickname as an unsubtle reference to his propensity for catching and eating flies. His wife and their children were brutally murdered back in the Homelands, a fact that he attempts to deny himself by committing to a series of endless tasks and janitorial duties.", 221 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/c1/CW_Flycatcher_Hall.png/revision/latest/scale-to-width-down/250?cb=20140709090111" 222 | }, 223 | { 224 | "id": 21, 225 | "name": "Tweedle Dee", 226 | "species": "Human", 227 | "gender": "Male", 228 | "occupation": "Private Investigator", 229 | "hairColour": "Brown", 230 | "eyeColour": "Hazel", 231 | "description": "Tweedle Dee is a Fable and the twin brother of Tweedle Dum.", 232 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/53/CW_Dee_Lair.png/revision/latest/scale-to-width-down/250?cb=20140709090225" 233 | }, 234 | { 235 | "id": 22, 236 | "name": "Tweedle Dum", 237 | "species": "Human", 238 | "gender": "Male", 239 | "occupation": "Private Investigator", 240 | "hairColour": "Brown", 241 | "eyeColour": "Brown", 242 | "description": "Tweedle Dum is a Fable and the twin brother of Tweedle Dee.", 243 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/6/6e/CW_Dum_Lair.png/revision/latest/scale-to-width-down/250?cb=20140824201033" 244 | }, 245 | { 246 | "id": 23, 247 | "name": "Grendal", 248 | "species": "Unknown", 249 | "gender": "Male", 250 | "occupation": "Unknown", 251 | "hairColour": "Black", 252 | "eyeColour": "Green", 253 | "description": "Grendel just wants to be left alone. In the old days he terrorized Norse mead halls, but lately he can be found occupying a stool in various quiet, dumpy bars around New York. He hates the noise of the city, but must work there to afford his glamour. Despite his gruff bearing, he's fiercely loyal to those who've learned to offer him the space and silence he deserves. Talking to him is like watching a time bomb tick down, it's only a matter of time.", 254 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/1/19/CW_Gren_Neutral.png/revision/latest/scale-to-width-down/240?cb=20140709191400" 255 | }, 256 | { 257 | "id": 24, 258 | "name": "Jack Horner", 259 | "species": "Human", 260 | "gender": "Male", 261 | "occupation": "Unknown", 262 | "hairColour": "Blonde", 263 | "eyeColour": "Blue", 264 | "description": "Jack is always up to something, but he's not nearly as smart as he thinks he is. His plans to get rich quick often backfire, but his confidence never wavers. He thinks he's the most important person in Fabletown, but everyone knows him as a mostly harmless smart ass.", 265 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/d/da/ISC_Jack_Pawn_Shop.png/revision/latest/scale-to-width-down/250?cb=20140607011400" 266 | }, 267 | { 268 | "id": 25, 269 | "name": "Holly", 270 | "species": "Troll", 271 | "gender": "Female", 272 | "occupation": "Bar Owner", 273 | "hairColour": "White", 274 | "eyeColour": "Green", 275 | "description": "Holly is a no-nonsense kind of troll, and the owner of the Trip Trap Bar. She's glamoured to appear human, but her patrons know better. Holly takes good care of her regulars, often the downtrodden Fables with little to spare, but she has no patience for the Fabletown government that has done nothing to locate her missing sister.", 276 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/58/CW_Holly_Trial.png/revision/latest/scale-to-width-down/240?cb=20140708204204" 277 | }, 278 | { 279 | "id": 26, 280 | "name": "Lily", 281 | "species": "Troll", 282 | "gender": "Female", 283 | "occupation": "Stripper, Prostitute", 284 | "hairColour": "Black", 285 | "eyeColour": "Unknown", 286 | "description": "Lily and her sister Holly grew up in the Homelands together, but had a falling out shortly after moving to the mundane world. Aimless and increasingly destitute, Lily turned to prostitution, and now she's the second victim in an ongoing murder investigation.", 287 | "imagePath": "No-image-available" 288 | }, 289 | { 290 | "id": 27, 291 | "name": "Kelsey Brannigan", 292 | "species": "Human", 293 | "gender": "Female", 294 | "occupation": "Detective", 295 | "hairColour": "Brown", 296 | "eyeColour": "Green", 297 | "description": "Kelsey Brannigan is a Mundy (normal human) detective working for the New York City Police Department.", 298 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/1/1d/SAM_Brannigan_Questioning.png/revision/latest/scale-to-width-down/250?cb=20140214064105" 299 | }, 300 | { 301 | "id": 28, 302 | "name": "Nerissa", 303 | "species": "Human", 304 | "gender": "Female", 305 | "occupation": "Stripper, Prostitute", 306 | "hairColour": "Mahogany", 307 | "eyeColour": "Amber", 308 | "description": "Nerissa's story never had a happy ending. She's known as the Little Mermaid, the young girl who gave up her tail for a pair of legs in the hopes of winning the heart of a handsome prince. When he married a princess instead, Nerissa was left heartbroken. She made the journey to the mundane world hoping for a better life. Now she dances at the Pudding and Pie, but each step she takes feels like walking on shards of glass. She has very little left, but finds some comfort in the company of her fellow dancers.", 309 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/7b/CW_Nerissa_Worried.png/revision/latest/scale-to-width-down/250?cb=20140713054418" 310 | }, 311 | { 312 | "id": 29, 313 | "name": "Clever Hans", 314 | "species": "Human", 315 | "gender": "Male", 316 | "occupation": "Bouncer, Janitor, Handyman", 317 | "hairColour": "Blonde", 318 | "eyeColour": "Blue", 319 | "description": "Clever Hans always does exactly as he's told. However, he often misunderstands his instructions and ends up hurting himself or behaving oddly, as in the case of his noted fable, where he threw sheep's eyes at his wife. Unsurprisingly, she left him, and now Hans works as a bouncer at Georgie's club. He hopes to dance on stage one day, but for now he's content sweeping up and making sure the crowd doesn't get out of hand.", 320 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/0/03/FTH_Hans_Line.png/revision/latest/scale-to-width-down/250?cb=20140806132301" 321 | }, 322 | { 323 | "id": 30, 324 | "name": "Gwen", 325 | "species": "Human", 326 | "gender": "Female", 327 | "occupation": "Prostitute", 328 | "hairColour": "Brown", 329 | "eyeColour": "Amber", 330 | "description": "Gwen is a prostitute at the Pudding & Pie working under Georgie Porgie", 331 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/53/Gwen.png/revision/latest/scale-to-width-down/250?cb=20180316035623" 332 | }, 333 | { 334 | "id": 31, 335 | "name": "Georgie Porgie", 336 | "species": "Human", 337 | "gender": "Male", 338 | "occupation": "Pimp", 339 | "hairColour": "Black", 340 | "eyeColour": "Grey", 341 | "description": "Georgie runs the Pudding and Pie, a strip club that also caters to the unmentionable desires of Fabletown's citizens. He has tried just about everything there is to try in pursuit of worldly pleasures, but none of it satisfies him for long. He does seem to enjoy pushing people's buttons. He takes pride in his nightclub, and doesn't react well to anyone meddling in his affairs.", 342 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/e/ef/CW_Georgie_Betrayed.png/revision/latest/scale-to-width-down/250?cb=20140710023226" 343 | }, 344 | { 345 | "id": 32, 346 | "name": "Vivian", 347 | "species": "Human", 348 | "gender": "Female", 349 | "occupation": "Hostess", 350 | "hairColour": "Blonde", 351 | "eyeColour": "Green", 352 | "description": "Much of Vivian's past is unknown since she prefers not to talk about her life back in the Homelands. She wanted to start fresh in Fabletown, but she finds herself working for Georgie at the Pudding and Pie. It's not a terrible life. Georgie took a liking to Vivian, so he doesn't make her take jobs at the Open Arms. Instead she plays hostess and helps Georgie ensure complete customer satisfaction.", 353 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/1/1b/CW_Vivian_Deciding.png/revision/latest/scale-to-width-down/250?cb=20140708191031" 354 | }, 355 | { 356 | "id": 33, 357 | "name": "Aunty Greenleaf", 358 | "species": "Human", 359 | "gender": "Female", 360 | "occupation": "Witch", 361 | "hairColour": "Grey", 362 | "eyeColour": "Brown", 363 | "description": "Horticulturist, alchemist, and lover of animals, Auntie Greenleaf is one of the few rogue witches still living outside of the Thirteenth Floor, unsupervised and unrestricted. Rumored to have lost a daughter in the Homelands, she suffers paranoia and depressive mood swings, and will only venture outside at irregular hours under the guise of an ethereal, white deer, an oft-whispered specter of Brookhaven natives.", 364 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/f/f9/CW_Greenleaf_Woodlands.png/revision/latest/scale-to-width-down/240?cb=20140708205015" 365 | }, 366 | { 367 | "id": 34, 368 | "name": "Tiny Tim", 369 | "species": "Human", 370 | "gender": "Male", 371 | "occupation": "Taxi Driver, Doorman", 372 | "hairColour": "Brown", 373 | "eyeColour": "Green", 374 | "description": "While most Fables theorize that their longevity and overall wellbeing is improved by the Mundy world's knowledge of them, for a select few, that does not seem to apply. When a malady or injury is an integral part of a Fable's story, that noteriety can make recovery nearly impossible. That's what Tiny Tim thinks, at least. And no medical care or magic - rather, none that he can afford - can heal his leg.", 375 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/50/CW_Tim_Waiting.png/revision/latest/scale-to-width-down/250?cb=20140708205146" 376 | }, 377 | { 378 | "id": 35, 379 | "name": "Crooked Man", 380 | "species": "Human", 381 | "gender": "Male", 382 | "occupation": "Crime Lord", 383 | "hairColour": "Brown", 384 | "eyeColour": "Green", 385 | "description": "The Crooked Man has slowly built himself into one of the most powerful figures in Fabletown. His operation started with a crooked sixpence and a crooked house - two things he cared about more than his wife or children, whom he killed rather than let them stand in his way. In his rise, The Crooked Man has ensnared many Fables in his criminal web, providing them with what they need, but always at a high cost. He is cunning, persuasive, and ruthless.", 386 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/b/b8/CW_Crooked_Man_Office.png/revision/latest/scale-to-width-down/240?cb=20140720022122" 387 | }, 388 | { 389 | "id": 36, 390 | "name": "Bloody Mary", 391 | "species": "Unknwon", 392 | "gender": "Female", 393 | "occupation": "Crooked Man's Chief Lieutenant", 394 | "hairColour": "Black with Red Streaks", 395 | "eyeColour": "Amber", 396 | "description": "The true history of the person known as \"Bloody Mary\" is almost completely unknown, even to Fables most acquainted with its members. Her name Mary, at least, is not up for contention, nor is her penchant for shocking violence, an inlaid resistance to magic and spells, and a strange ability to use any reflective surface as a portal, effectively short-cutting space and time. Thought by Mundies to be the wailing apparition of a childless ghost, though any evidence of that is as yet unseen.", 397 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/0/06/CW_Mary_Workshop.png/revision/latest/scale-to-width-down/240?cb=20140708205330" 398 | }, 399 | { 400 | "id": 37, 401 | "name": "Jersey Devil", 402 | "species": "Cryptid", 403 | "gender": "Male", 404 | "occupation": "Shop Owner", 405 | "hairColour": "Mahogany", 406 | "eyeColour": "Unknown", 407 | "description": "Not all of the Fables who came to this world landed in Fabletown. There are those who scattered across the farthest corners of the Earth. And there are those who simply prefer the Garden State to the Empire State. Such is the Jersey Devil. Reports of its appearance have varied... although most accounts make mention of leathery wings. But an encounter with a certain axe of legend some years ago has temporarily rendered that feature absent.", 408 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/8/85/CW_Jersey_Lair.png/revision/latest/scale-to-width-down/240?cb=20140708205051" 409 | }, 410 | { 411 | "id": 38, 412 | "name": "Johann", 413 | "species": "Human", 414 | "gender": "Male", 415 | "occupation": "Butcher", 416 | "hairColour": "Grey", 417 | "eyeColour": "Brown", 418 | "description": "His name is often said in the same breath as that of the Baker and Candlestick Maker of Fabletown. And like those other tradesmen, Johann the Butcher's storefront has served Fabletown for ages: fresh cuts, exotic meats, and even full sides of beef for the vigorous appetites of ogres and trolls. But Johann's business has fallen in with the wrong crowd. As the quality of his products declined, and his business turned into a front operation for the Crooked Man, some have started to wonder if they ever really knew Johann.", 419 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/1/18/CW_Johann_Debate.png/revision/latest/scale-to-width-down/250?cb=20140709090409" 420 | } 421 | ], 422 | "locations": [ 423 | { 424 | "id": 1, 425 | "locationName": "New York City", 426 | "description": "Fabletown is situated in New York City", 427 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/43/FTH_Manhattan_Skyline.png/revision/latest/scale-to-width-down/1300?cb=20140826020954" 428 | }, 429 | { 430 | "id": 2, 431 | "locationName": "Toad's Tenement", 432 | "description": "A tenement building owned by Mr. Toad. The Woodsman owns an apartment in it.", 433 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/b/b5/FTH_Toad%27s_Tenement.png/revision/latest/scale-to-width-down/1300?cb=20140614235651" 434 | }, 435 | { 436 | "id": 3, 437 | "locationName": "Woodman's Apartment", 438 | "description": "An apartment in Toad's tenement owned by the Woodsman.", 439 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/42/FTH_Woody%27s_Apartment_Daytime.png/revision/latest/scale-to-width-down/1300?cb=20140826021035" 440 | }, 441 | { 442 | "id": 4, 443 | "locationName": "Woodland Luxury Apartments", 444 | "description": "The Woodland Luxury Apartments, most often referred to as the Woodlands, is the principal workplace, as well as the residence, of some of the authority figures of Fabletown on the Upper West Side in Manhattan, New York City.", 445 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/c6/FTH_Woodlands_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140614235831" 446 | }, 447 | { 448 | "id": 5, 449 | "locationName": "Bigby's Apartment", 450 | "description": "An apartment in the Woodlands, apparently the smallest in Fabletown. Bigby calls it home during the precious little downtime he has.", 451 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/0/09/ISC_Bigby%27s_Apartment.png/revision/latest/scale-to-width-down/1300?cb=20140614235911" 452 | }, 453 | { 454 | "id": 6, 455 | "locationName": "Business Office", 456 | "description": "A cavernous room in the Woodlands that is home to the Fabletown government and archives.", 457 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2b/ISC_Back_to_the_Office.png/revision/latest/scale-to-width-down/1300?cb=20140531214254" 458 | }, 459 | { 460 | "id": 7, 461 | "locationName": "Lawrence's Apartment", 462 | "description": "An apartment in The Bronx that is home to Lawrence and Faith.", 463 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/7a/FTH_Lawrence%27s_Apartment_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140615000003" 464 | }, 465 | { 466 | "id": 8, 467 | "locationName": "Trip Trap", 468 | "description": "A bar that generally serves Fabletown's less fortunate residents. It is owned by Holly and frequented by Grendel and the Woodman.", 469 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/c6/FTH_Trip_Trap_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140405000415" 470 | }, 471 | { 472 | "id": 9, 473 | "locationName": "Upper West Side Precinct", 474 | "description": "A NYPD precinct in the Upper West Side that Bigby is brought to after being arrested for entering the crime scene at the Woodlands.", 475 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/6/65/SAM_Questioning_Bigby.png/revision/latest/scale-to-width-down/1300?cb=20140511191712" 476 | }, 477 | { 478 | "id": 10, 479 | "locationName": "Woodland's Basement", 480 | "description": "A chamber in the Woodland Luxury Apartments", 481 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/e/eb/SAM_Bluebeard_Dee.png/revision/latest/scale-to-width-down/1300?cb=20140604234346" 482 | }, 483 | { 484 | "id": 11, 485 | "locationName": "Witching Well Chamber", 486 | "description": "The Witching Well is a magical well that acts as a portal to the lands of the deceased and was used by Fabletown to dispose of Fable bodies on the assumption that they couldn't return.", 487 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/3/38/SAM_Witching_Well_Chamber.png/revision/latest/scale-to-width-down/1300?cb=20140511195950" 488 | }, 489 | { 490 | "id": 12, 491 | "locationName": "Pudding & Pie", 492 | "description": "The Pudding & Pie, sometimes stylized as the Puddin' & Pie or Pudding N' Pie, is a strip club in Fabletown owned by Georgie Porgie and Vivian.", 493 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/8/80/ACM_Pudding_%26_Pie_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140610212324" 494 | }, 495 | { 496 | "id": 13, 497 | "locationName": "Open Arms Hotel", 498 | "description": "A rundown establishment down the road from the Pudding & Pie, it is used by its girls for prostitution appointments. It also also Beauty's current place of employment.", 499 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/0/07/SAM_Open_Arms_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140506000416" 500 | }, 501 | { 502 | "id": 14, 503 | "locationName": "Buckingham Bridge", 504 | "description": "A bridge connecting Manhattan and Brooklyn. Lily's funeral is held beneath it.", 505 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/3/31/ACM_Manhattan_Bridge.png/revision/latest/scale-to-width-down/1300?cb=20140416210818" 506 | }, 507 | { 508 | "id": 15, 509 | "locationName": "Crane's Apartment", 510 | "description": "A penthouse in the Woodlands that was home to Ichabod Crane.", 511 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/4/46/ACM_Crane%27s_Apartment_Ransacked.png/revision/latest/scale-to-width-down/1300?cb=20140416214218" 512 | }, 513 | { 514 | "id": 16, 515 | "locationName": "Tweedles' Office", 516 | "description": "An office that the Tweedle brothers operate their private investigation business out of. It is maintained by Flycatcher.", 517 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/8/83/ACM_Tweedle_Office.png/revision/latest/scale-to-width-down/1300?cb=20140416214409" 518 | }, 519 | { 520 | "id": 17, 521 | "locationName": "Aunty Greenleaf's Apartment", 522 | "description": "An apartment in the outskirts of Fabletown that Aunty Greenleaf lives in.", 523 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/7f/ACM_Greenleaf%27s_Apartment.png/revision/latest/scale-to-width-down/1300?cb=20140416215442" 524 | }, 525 | { 526 | "id": 18, 527 | "locationName": "Bigby's Office", 528 | "description": "The Woodlands' security office, and Bigby's base of operations as sheriff.", 529 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/6/6f/ISC_Comfort.png/revision/latest/scale-to-width-down/1300?cb=20140531211054" 530 | }, 531 | { 532 | "id": 19, 533 | "locationName": "Beauty and Beast's Apartment", 534 | "description": "A lavish apartment in the Woodlands that Beauty and Beast live in.", 535 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/2/2d/ISC_Fishing_for_Sympathy.png/revision/latest/scale-to-width-down/1300?cb=20140531212737" 536 | }, 537 | { 538 | "id": 20, 539 | "locationName": "Lucky Pawn", 540 | "description": "Jersey Devil's pawn shop next door to the Trip Trap. It is a front for the Crooked Man's criminal enterprise.", 541 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/f/f8/ISC_Lucky_Pawn_Exterior.png/revision/latest/scale-to-width-down/1300?cb=20140531212738" 542 | }, 543 | { 544 | "id": 21, 545 | "locationName": "The Cut Above", 546 | "description": "Johann's butcher shop. It was taken over by the Crooked Man's crew and is now used to create and ship black market magic and glamours.", 547 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/9/94/ISC_Cut_Above_Interior.png/revision/latest/scale-to-width-down/1300?cb=20140531213818" 548 | }, 549 | { 550 | "id": 22, 551 | "locationName": "Central Park", 552 | "description": "A large urban park in the center of Manhattan. Bigby finds the door to the Crooked Man's hideout under the Gothic Bridge within the park.", 553 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/c/c2/FTH_Central_Park.png/revision/latest/scale-to-width-down/1300?cb=20140826021233" 554 | }, 555 | { 556 | "id": 23, 557 | "locationName": "The Crooked Lair", 558 | "description": "The Crooked Lair is the headquarters for the Crooked Man and his criminal enterprise. Located inside a desanctified church, the only way to enter it is through a magic door that constantly moves around Fabletown. The entrance is manned by Tiny Tim.", 559 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/7/78/ISC_Crooked_Lair.png/revision/latest/scale-to-width-down/1300?cb=20140703051430" 560 | }, 561 | { 562 | "id": 24, 563 | "locationName": "Sheppard Metalworks", 564 | "description": "Shepard Metalworks is an old foundry located in the outskirts of Fabletown. It is used by the Crooked Man as a storage location for information about the various Fables he has observed over the years.", 565 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/f/ff/CW_Sheppard_Metalworks.png/revision/latest/scale-to-width-down/1300?cb=20140709053211" 566 | } 567 | ], 568 | "episodes": [ 569 | { 570 | "id": 1, 571 | "episodeName": "Faith", 572 | "episodeNum": 1, 573 | "season": 1, 574 | "description": "Bigby Wolf, Sheriff of Fabletown, must work a murder case when a Fable is killed and he must work with Snow White to find out who the murderer is. The two of them work together and the investigation goes deep, with a shocking ending.", 575 | "directedBy": "Nick Herman & Dennis Lenart", 576 | "writtenBy": "Pierre Shorette & Ryan Kaufman", 577 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/b/b6/FTH_Cover.png/revision/latest?cb=20140214011436" 578 | }, 579 | { 580 | "id": 2, 581 | "episodeName": "Smoke & Mirrors", 582 | "episodeNum": 2, 583 | "season": 1, 584 | "description": "The episode starts with Bigby being questioned by a female detective named Kelsey Brannigan. Bigby is reluctant to answer her questions. Suddenly, the detective and police officers in the room pass out, and it is shown that Crane had used a memory-wipe spell to let Bigby out.", 585 | "directedBy": "Jason Latino", 586 | "writtenBy": "Dave Grossman & Joe Pinney", 587 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/a/a2/SAM_Cover.png/revision/latest?cb=20140214011612" 588 | }, 589 | { 590 | "id": 3, 591 | "episodeName": "A Crooked Mile", 592 | "episodeNum": 3, 593 | "season": 1, 594 | "description": "Bigby has discovered that Crane is the murderer; a photo of him in bed with a murdered woman glamoured to look like Snow was found. He sees Beauty and Beast off to arrive at Lily's funeral, where Holly, Grendel, Prince Lawrence, Vivian and Nerissa are all attending. Eventually, Snow returns to the funeral and asks Bigby to wait at a distance.", 595 | "directedBy": " Martin Montgomery", 596 | "writtenBy": "Adam Hines & Ryan Kaufman", 597 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/0/03/ACM_Cover.png/revision/latest/?cb=20140214012342" 598 | }, 599 | { 600 | "id": 4, 601 | "episodeName": "In Sheep's Clothing", 602 | "episodeNum": 4, 603 | "season": 1, 604 | "description": "In a nightmare sequence, Bigby is about to be axed to death by Bloody Mary; he violently awakes to find himself being operated on by Doctor Swineheart, with Colin and Snow watching. The Silver Bullet Mary shot Bigby with in the previous episode is Bigby's key weakness requiring an important healing practice.", 605 | "directedBy": "Kent Mudle", 606 | "writtenBy": "Dane Martin & Aaron Casillas", 607 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/1/18/ISC_Cover.png/revision/latest?cb=20140311212241" 608 | }, 609 | { 610 | "id": 5, 611 | "episodeName": "Cry Wolf", 612 | "episodeNum": 5, 613 | "season": 1, 614 | "description": "Bigby stands before the Crooked Man and his crew consisting of Dum (if he was spared) and Dee, Georgie Porgie and Vivian, and the Jersey Devil. Bigby can either be rude and threatening to The Crooked Man or accept his hospitality. The henchman are nervous about Bigby's presence, and Bigby observes the Crooked Man's use of scare tactics to enforce his rule. When Bigby brings up the murders, he can either ask The Crooked Man outright who did it, or take a guess at the culprit himself.", 615 | "directedBy": "Vahram Antonian", 616 | "writtenBy": "Nicole Martinez & Joe Pinney", 617 | "imagePath": "https://vignette.wikia.nocookie.net/fables/images/5/5e/CW_Cover.png/revision/latest?cb=20140403210906" 618 | } 619 | ] 620 | } -------------------------------------------------------------------------------- /public/js/lib/jquery.tocify.js: -------------------------------------------------------------------------------- 1 | /* jquery Tocify - v1.8.0 - 2013-09-16 2 | * http://www.gregfranko.com/jquery.tocify.js/ 3 | * Copyright (c) 2013 Greg Franko; Licensed MIT 4 | * Modified lightly by Robert Lord to fix a bug I found, 5 | * and also so it adds ids to headers 6 | * also because I want height caching, since the 7 | * height lookup for h1s and h2s was causing serious 8 | * lag spikes below 30 fps */ 9 | 10 | // Immediately-Invoked Function Expression (IIFE) [Ben Alman Blog Post](http://benalman.com/news/2010/11/immediately-invoked-function-expression/) that calls another IIFE that contains all of the plugin logic. I used this pattern so that anyone viewing this code would not have to scroll to the bottom of the page to view the local parameters that were passed to the main IIFE. 11 | (function(tocify) { 12 | 13 | // ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/) 14 | "use strict"; 15 | 16 | // Calls the second IIFE and locally passes in the global jQuery, window, and document objects 17 | tocify(window.jQuery, window, document); 18 | 19 | } 20 | 21 | // Locally passes in `jQuery`, the `window` object, the `document` object, and an `undefined` variable. The `jQuery`, `window` and `document` objects are passed in locally, to improve performance, since javascript first searches for a variable match within the local variables set before searching the global variables set. All of the global variables are also passed in locally to be minifier friendly. `undefined` can be passed in locally, because it is not a reserved word in JavaScript. 22 | (function($, window, document, undefined) { 23 | 24 | // ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/) 25 | "use strict"; 26 | 27 | var tocClassName = "tocify", 28 | tocClass = "." + tocClassName, 29 | tocFocusClassName = "tocify-focus", 30 | tocHoverClassName = "tocify-hover", 31 | hideTocClassName = "tocify-hide", 32 | hideTocClass = "." + hideTocClassName, 33 | headerClassName = "tocify-header", 34 | headerClass = "." + headerClassName, 35 | subheaderClassName = "tocify-subheader", 36 | subheaderClass = "." + subheaderClassName, 37 | itemClassName = "tocify-item", 38 | itemClass = "." + itemClassName, 39 | extendPageClassName = "tocify-extend-page", 40 | extendPageClass = "." + extendPageClassName; 41 | 42 | // Calling the jQueryUI Widget Factory Method 43 | $.widget("toc.tocify", { 44 | 45 | //Plugin version 46 | version: "1.8.0", 47 | 48 | // These options will be used as defaults 49 | options: { 50 | 51 | // **context**: Accepts String: Any jQuery selector 52 | // The container element that holds all of the elements used to generate the table of contents 53 | context: "body", 54 | 55 | // **ignoreSelector**: Accepts String: Any jQuery selector 56 | // A selector to any element that would be matched by selectors that you wish to be ignored 57 | ignoreSelector: null, 58 | 59 | // **selectors**: Accepts an Array of Strings: Any jQuery selectors 60 | // The element's used to generate the table of contents. The order is very important since it will determine the table of content's nesting structure 61 | selectors: "h1, h2, h3", 62 | 63 | // **showAndHide**: Accepts a boolean: true or false 64 | // Used to determine if elements should be shown and hidden 65 | showAndHide: true, 66 | 67 | // **showEffect**: Accepts String: "none", "fadeIn", "show", or "slideDown" 68 | // Used to display any of the table of contents nested items 69 | showEffect: "slideDown", 70 | 71 | // **showEffectSpeed**: Accepts Number (milliseconds) or String: "slow", "medium", or "fast" 72 | // The time duration of the show animation 73 | showEffectSpeed: "medium", 74 | 75 | // **hideEffect**: Accepts String: "none", "fadeOut", "hide", or "slideUp" 76 | // Used to hide any of the table of contents nested items 77 | hideEffect: "slideUp", 78 | 79 | // **hideEffectSpeed**: Accepts Number (milliseconds) or String: "slow", "medium", or "fast" 80 | // The time duration of the hide animation 81 | hideEffectSpeed: "medium", 82 | 83 | // **smoothScroll**: Accepts a boolean: true or false 84 | // Determines if a jQuery animation should be used to scroll to specific table of contents items on the page 85 | smoothScroll: true, 86 | 87 | // **smoothScrollSpeed**: Accepts Number (milliseconds) or String: "slow", "medium", or "fast" 88 | // The time duration of the smoothScroll animation 89 | smoothScrollSpeed: "medium", 90 | 91 | // **scrollTo**: Accepts Number (pixels) 92 | // The amount of space between the top of page and the selected table of contents item after the page has been scrolled 93 | scrollTo: 0, 94 | 95 | // **showAndHideOnScroll**: Accepts a boolean: true or false 96 | // Determines if table of contents nested items should be shown and hidden while scrolling 97 | showAndHideOnScroll: true, 98 | 99 | // **highlightOnScroll**: Accepts a boolean: true or false 100 | // Determines if table of contents nested items should be highlighted (set to a different color) while scrolling 101 | highlightOnScroll: true, 102 | 103 | // **highlightOffset**: Accepts a number 104 | // The offset distance in pixels to trigger the next active table of contents item 105 | highlightOffset: 40, 106 | 107 | // **theme**: Accepts a string: "bootstrap", "jqueryui", or "none" 108 | // Determines if Twitter Bootstrap, jQueryUI, or Tocify classes should be added to the table of contents 109 | theme: "bootstrap", 110 | 111 | // **extendPage**: Accepts a boolean: true or false 112 | // If a user scrolls to the bottom of the page and the page is not tall enough to scroll to the last table of contents item, then the page height is increased 113 | extendPage: true, 114 | 115 | // **extendPageOffset**: Accepts a number: pixels 116 | // How close to the bottom of the page a user must scroll before the page is extended 117 | extendPageOffset: 100, 118 | 119 | // **history**: Accepts a boolean: true or false 120 | // Adds a hash to the page url to maintain history 121 | history: true, 122 | 123 | // **scrollHistory**: Accepts a boolean: true or false 124 | // Adds a hash to the page url, to maintain history, when scrolling to a TOC item 125 | scrollHistory: false, 126 | 127 | // **hashGenerator**: How the hash value (the anchor segment of the URL, following the 128 | // # character) will be generated. 129 | // 130 | // "compact" (default) - #CompressesEverythingTogether 131 | // "pretty" - #looks-like-a-nice-url-and-is-easily-readable 132 | // function(text, element){} - Your own hash generation function that accepts the text as an 133 | // argument, and returns the hash value. 134 | hashGenerator: "compact", 135 | 136 | // **highlightDefault**: Accepts a boolean: true or false 137 | // Set's the first TOC item as active if no other TOC item is active. 138 | highlightDefault: true 139 | 140 | }, 141 | 142 | // _Create 143 | // ------- 144 | // Constructs the plugin. Only called once. 145 | _create: function() { 146 | 147 | var self = this; 148 | 149 | self.tocifyWrapper = $('.tocify-wrapper'); 150 | self.extendPageScroll = true; 151 | 152 | // Internal array that keeps track of all TOC items (Helps to recognize if there are duplicate TOC item strings) 153 | self.items = []; 154 | 155 | // Generates the HTML for the dynamic table of contents 156 | self._generateToc(); 157 | 158 | // Caches heights and anchors 159 | self.cachedHeights = [], 160 | self.cachedAnchors = []; 161 | 162 | // Adds CSS classes to the newly generated table of contents HTML 163 | self._addCSSClasses(); 164 | 165 | self.webkit = (function() { 166 | 167 | for(var prop in window) { 168 | 169 | if(prop) { 170 | 171 | if(prop.toLowerCase().indexOf("webkit") !== -1) { 172 | 173 | return true; 174 | 175 | } 176 | 177 | } 178 | 179 | } 180 | 181 | return false; 182 | 183 | }()); 184 | 185 | // Adds jQuery event handlers to the newly generated table of contents 186 | self._setEventHandlers(); 187 | 188 | // Binding to the Window load event to make sure the correct scrollTop is calculated 189 | $(window).load(function() { 190 | 191 | // Sets the active TOC item 192 | self._setActiveElement(true); 193 | 194 | // Once all animations on the page are complete, this callback function will be called 195 | $("html, body").promise().done(function() { 196 | 197 | setTimeout(function() { 198 | 199 | self.extendPageScroll = false; 200 | 201 | },0); 202 | 203 | }); 204 | 205 | }); 206 | 207 | }, 208 | 209 | // _generateToc 210 | // ------------ 211 | // Generates the HTML for the dynamic table of contents 212 | _generateToc: function() { 213 | 214 | // _Local variables_ 215 | 216 | // Stores the plugin context in the self variable 217 | var self = this, 218 | 219 | // All of the HTML tags found within the context provided (i.e. body) that match the top level jQuery selector above 220 | firstElem, 221 | 222 | // Instantiated variable that will store the top level newly created unordered list DOM element 223 | ul, 224 | ignoreSelector = self.options.ignoreSelector; 225 | 226 | // If the selectors option has a comma within the string 227 | if(this.options.selectors.indexOf(",") !== -1) { 228 | 229 | // Grabs the first selector from the string 230 | firstElem = $(this.options.context).find(this.options.selectors.replace(/ /g,"").substr(0, this.options.selectors.indexOf(","))); 231 | 232 | } 233 | 234 | // If the selectors option does not have a comman within the string 235 | else { 236 | 237 | // Grabs the first selector from the string and makes sure there are no spaces 238 | firstElem = $(this.options.context).find(this.options.selectors.replace(/ /g,"")); 239 | 240 | } 241 | 242 | if(!firstElem.length) { 243 | 244 | self.element.addClass(hideTocClassName); 245 | 246 | return; 247 | 248 | } 249 | 250 | self.element.addClass(tocClassName); 251 | 252 | // Loops through each top level selector 253 | firstElem.each(function(index) { 254 | 255 | //If the element matches the ignoreSelector then we skip it 256 | if($(this).is(ignoreSelector)) { 257 | return; 258 | } 259 | 260 | // Creates an unordered list HTML element and adds a dynamic ID and standard class name 261 | ul = $("