| @ -0,0 +1 @@ | |||
| node_modules | |||
| @ -0,0 +1,88 @@ | |||
| learncards | |||
| ========== | |||
| Learn Cards is a simple module which allows you to host a web server | |||
| of custom defined flash cards. | |||
| Simply install `learncards` and provide a simple json file defining | |||
| all of the questions and answers for the flash cards. | |||
| Why did I pick a stupid name like `learncards`? Because someone already | |||
| took the name `flashcard` in npm and using `flashcards` seems confusing. | |||
| So in come my limited creativity when it comes to naming projects. | |||
| ## Installing | |||
| ### Via Git | |||
| ```bash | |||
| git clone git://github.com/brettlangdon/learncards.git | |||
| cd ./learncards | |||
| npm install | |||
| ./bin/learncards my_flashcards.json | |||
| ``` | |||
| ### Via NPM | |||
| ```bash | |||
| npm install -g learncards | |||
| learncards my_flashcards.json | |||
| ``` | |||
| ## Flashcards | |||
| A Flashcard json file *MUST* contain a `cards` property where each card is | |||
| an object which *MUST* contain a `question` and `answer` property. | |||
| An optional top level property of `title` can be provided which sets the | |||
| title in the html site accordingly. | |||
| ### Example | |||
| ```javascript | |||
| { | |||
| "title": "My Flashcards", | |||
| "cards": { | |||
| { | |||
| "question": "Who is the greatest?", | |||
| "answer": "Brett Langdon" | |||
| }, | |||
| { | |||
| "question": "Who is terrible at naming projects?", | |||
| "answer": "Brett Langdon" | |||
| } | |||
| } | |||
| } | |||
| ``` | |||
| ## Running | |||
| There is a binary file included with the installation `learncards`. | |||
| If `learncards` is installed globally then the command should be available. | |||
| Otherwise you can run the binary directly from the `learncards` directory. | |||
| When running you must provide the location of the flashcards json file. | |||
| ### Example | |||
| ```bash | |||
| learncards my_flashcards.json | |||
| ``` | |||
| ## License | |||
| ``` | |||
| The MIT License (MIT) | |||
| Copyright (c) 2013 Brett Langdon <brett@blangdon.com> | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | |||
| this software and associated documentation files (the "Software"), to deal in | |||
| the Software without restriction, including without limitation the rights to | |||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |||
| the Software, and to permit persons to whom the Software is furnished to do so, | |||
| subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in all | |||
| copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |||
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |||
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
| ``` | |||
| @ -0,0 +1,6 @@ | |||
| #!/usr/bin/env node | |||
| try{ | |||
| require("learncards"); | |||
| } catch(e){ | |||
| require("../"); | |||
| } | |||
| @ -0,0 +1,96 @@ | |||
| var http = require("http"); | |||
| var path = require("path"); | |||
| var express = require("express"); | |||
| function shuffle(array) { | |||
| for (var i = array.length - 1; i > 0; i--) { | |||
| var j = Math.floor(Math.random() * (i + 1)); | |||
| var temp = array[i]; | |||
| array[i] = array[j]; | |||
| array[j] = temp; | |||
| } | |||
| return array; | |||
| } | |||
| var root = path.join(__dirname, ".."); | |||
| var argc = process.argv.length; | |||
| if(argc < 3){ | |||
| console.error("must provide data file, e.g. \"learncards data.json\""); | |||
| process.exit(1); | |||
| } | |||
| var data_file = path.join(process.cwd(), process.argv[argc - 1]); | |||
| var data = require(data_file); | |||
| if(typeof(data) !== "object" || !data.hasOwnProperty("cards")){ | |||
| console.error("data file ", data_file, "does not contain \"cards\" property"); | |||
| process.exit(1); | |||
| } | |||
| var num_cards = data.cards.length; | |||
| for(var i = 0; i < num_cards; ++i){ | |||
| var next = data.cards[i]; | |||
| if(!next.hasOwnProperty("question")){ | |||
| console.error("card", next, "requires \"question\" property"); | |||
| process.exit(1); | |||
| } | |||
| if(!next.hasOwnProperty("answer")){ | |||
| console.error("card", next, "requires \"answer\" property"); | |||
| process.exit(1); | |||
| } | |||
| } | |||
| var title = "Learn Cards"; | |||
| if(data.hasOwnProperty("title")){ | |||
| title = data.title + " - Learn Cards"; | |||
| } | |||
| var app = express(); | |||
| app.set("port", process.env.PORT || 8000); | |||
| app.set("views", path.join(root, "views")); | |||
| app.set("view engine", "jade"); | |||
| app.use(express.favicon()); | |||
| app.use(app.router); | |||
| app.use(express.static(path.join(root, "public"))); | |||
| app.use(express.errorHandler()); | |||
| app.get("/", function(req, res){ | |||
| res.render("index", {title: title}); | |||
| }); | |||
| app.get("/card.json", function(req, res){ | |||
| var index = 0; | |||
| if(req.query.next){ | |||
| index = parseInt(req.query.next); | |||
| } else if(req.query.rand){ | |||
| index = Math.floor(Math.random() * num_cards); | |||
| } | |||
| if(index >= num_cards){ | |||
| index = 0; | |||
| } | |||
| var card = {}; | |||
| card.question = data.cards[index].question; | |||
| card.answer = data.cards[index].answer; | |||
| if(req.query.swap){ | |||
| var swap = req.query.swap || ""; | |||
| swap = swap.toLowerCase(); | |||
| if(Boolean(swap)){ | |||
| if(swap == "true" || (swap == "random" && Math.random() < 0.5)){ | |||
| var q = card.question; | |||
| card.question = card.answer; | |||
| card.answer = q; | |||
| } | |||
| } | |||
| } | |||
| res.json(200, {index: index, card: card}); | |||
| }); | |||
| var server = http.createServer(app); | |||
| server.listen(app.get("port"), function(){ | |||
| console.log("learncards is listening on port", app.get("port")); | |||
| }); | |||
| @ -0,0 +1,33 @@ | |||
| { | |||
| "name": "learncards", | |||
| "version": "0.1.0", | |||
| "description": "Easily create flash cards for learning anything", | |||
| "main": "lib/index.js", | |||
| "scripts": { | |||
| "test": "echo \"Error: no test specified\" && exit 1" | |||
| }, | |||
| "repository": { | |||
| "type": "git", | |||
| "url": "git://github.com/brettlangdon/learncards.git" | |||
| }, | |||
| "keywords": [ | |||
| "learn", | |||
| "cards", | |||
| "flash", | |||
| "flashcard", | |||
| "flashcards", | |||
| "server" | |||
| ], | |||
| "author": "Brett Langdon <brett@blangdon.com> (http://brett.is/)", | |||
| "license": "MIT", | |||
| "bugs": { | |||
| "url": "https://github.com/brettlangdon/learncards/issues" | |||
| }, | |||
| "dependencies": { | |||
| "express": "~3.4.4", | |||
| "jade": "~0.35.0" | |||
| }, | |||
| "bin": { | |||
| "learncards": "bin/learncards" | |||
| } | |||
| } | |||
| @ -0,0 +1,113 @@ | |||
| var factor = 4; | |||
| var next_index = 0; | |||
| var swap = false; | |||
| var resize = function(name){ | |||
| var node = document.getElementById(name); | |||
| node.style.width = window.innerWidth + "px"; | |||
| node.style.height = window.innerHeight + "px"; | |||
| node.childNodes[0].style["font-size"] = Math.round(window.innerHeight / factor) + "px"; | |||
| node.childNodes[0].style["margin-top"] = Math.round(-node.childNodes[0].clientHeight / 2) + "px"; | |||
| }; | |||
| var resize_buttons = function(){ | |||
| var new_height = Math.round(window.innerHeight / (factor * 3)); | |||
| var middle = window.innerWidth / 2; | |||
| var options = document.getElementById("options"); | |||
| options.style["font-size"] = Math.round(new_height / 2) + "px"; | |||
| options.style.left = Math.round(middle - (options.clientWidth / 2)) + "px"; | |||
| }; | |||
| var toggle = function(name, visibility){ | |||
| var node = document.getElementById(name); | |||
| if(visibility === undefined){ | |||
| visibility = node.style.display === "none" ? "block" : "none"; | |||
| } | |||
| node.style.display = visibility; | |||
| resize(name); | |||
| }; | |||
| var set_text = function(name, text){ | |||
| var node = document.getElementById(name); | |||
| node.childNodes[0].innerText = text; | |||
| resize(name); | |||
| }; | |||
| var next_card = function(random){ | |||
| random = random || false; | |||
| var req = new XMLHttpRequest(); | |||
| var url = "/card.json?next=" + next_index; | |||
| if(random){ | |||
| url = "/card.json?rand=true"; | |||
| } | |||
| url += "&swap=" + swap; | |||
| req.open("GET", url, true); | |||
| req.onreadystatechange = function () { | |||
| if(req.readyState != 4 || req.status != 200) return; | |||
| var response = JSON.parse(req.responseText); | |||
| toggle("question", "block"); | |||
| toggle("answer", "none"); | |||
| set_text("question", response.card.question); | |||
| set_text("answer", response.card.answer); | |||
| next_index = response.index; | |||
| }; | |||
| req.send(); | |||
| }; | |||
| window.onresize = function(){ | |||
| resize("question"); | |||
| resize("answer"); | |||
| resize_buttons(); | |||
| } | |||
| window.onresize(); | |||
| next_card(); | |||
| $(document).click(function(){ | |||
| toggle("question"); | |||
| toggle("answer"); | |||
| }); | |||
| $("#next").click(function(evt){ | |||
| evt.stopPropagation(); | |||
| next_index += 1; | |||
| next_card(); | |||
| }); | |||
| $("#last").click(function(evt){ | |||
| evt.stopPropagation(); | |||
| next_index -= 1; | |||
| if(next_index < 0){ | |||
| next_index = 0; | |||
| } | |||
| next_card(); | |||
| }); | |||
| $("#random").click(function(evt){ | |||
| evt.stopPropagation(); | |||
| next_card(true); | |||
| }); | |||
| $("#swap").click(function(evt){ | |||
| evt.stopPropagation(); | |||
| var self = $(this); | |||
| if(self.hasClass("normal")){ | |||
| swap = true; | |||
| self.removeClass("normal"); | |||
| self.addClass("swap"); | |||
| self.text("Swap"); | |||
| } else if(self.hasClass("swap")){ | |||
| swap = "random"; | |||
| self.removeClass("swap"); | |||
| self.addClass("random"); | |||
| self.text("Random"); | |||
| } else{ | |||
| swap = false; | |||
| self.removeClass("swap"); | |||
| self.removeClass("random"); | |||
| self.addClass("normal"); | |||
| self.text("Normal"); | |||
| } | |||
| }); | |||
| @ -0,0 +1,44 @@ | |||
| @import url(http://fonts.googleapis.com/css?family=Fondamento); | |||
| body, #question, #answer{ | |||
| font-family:Fondamento,cursive; | |||
| padding: 0; | |||
| margin: 0; | |||
| width: 100%; | |||
| height: 100%; | |||
| max-height: 100%; | |||
| max-width: 100%; | |||
| -webkit-touch-callout: none; | |||
| -webkit-user-select: none; | |||
| -khtml-user-select: none; | |||
| -moz-user-select: none; | |||
| -ms-user-select: none; | |||
| user-select: none; | |||
| } | |||
| #options { | |||
| position: absolute; | |||
| z-index: 100; | |||
| top: 0; | |||
| } | |||
| .button { | |||
| z-index: 200; | |||
| } | |||
| #question, #answer { | |||
| text-align: center; | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| } | |||
| #answer { | |||
| display: none; | |||
| } | |||
| .text { | |||
| top: 50%; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| @ -0,0 +1,5 @@ | |||
| #footer | |||
| script(type="text/javascript", language="javascript" src="/zepto.js") | |||
| script(type="text/javascript", language="javascript" src="/foundation.min.js") | |||
| script(type="text/javascript", language="javascript" src="/app.js") | |||
| script(type="text/javascript", language="javascript")="$(document).foundation();" | |||
| @ -0,0 +1,7 @@ | |||
| head | |||
| meta(charset="utf-8") | |||
| meta(name="viewport", content="width=device-width") | |||
| title=title | |||
| link(rel="stylesheet", href="/foundation.min.css") | |||
| link(rel="stylesheet", href="/style.css") | |||
| script(type="text/javascript", language="javascript", src="/modernizr.js") | |||
| @ -0,0 +1,13 @@ | |||
| extends layout | |||
| block content | |||
| #options.row | |||
| #container.small-12.large-12.small-centered.large-centered.columns.last | |||
| #last.button.small-3.large-3.columns Prev Card | |||
| #next.button.small-3.large-3.columns Next Card | |||
| #random.button.small-3.large-3.columns Random Card | |||
| #swap.button.small-3.large-3.columns.normal Normal | |||
| #question.row.card | |||
| .text.small-12.large-12.columns | |||
| #answer.row.card | |||
| .text.small-12.large-12.columns | |||
| @ -0,0 +1,7 @@ | |||
| !!! 5 | |||
| html | |||
| include header | |||
| body | |||
| block content | |||
| include footer | |||