| @ -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 | |||||