├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── extension └── src │ ├── background.html │ ├── background.js │ ├── flatui │ ├── bootstrap.css │ └── flat-ui.css │ ├── fonts │ ├── Flat-UI-Icons.eot │ ├── Flat-UI-Icons.svg │ ├── Flat-UI-Icons.ttf │ └── Flat-UI-Icons.woff │ ├── gravatar.js │ ├── icon.png │ ├── icon_16x16.png │ ├── inject.js │ ├── jquery-2.0.3.min.js │ ├── jquery.tagsinput.js │ ├── manifest.json │ ├── mousetrap.min.js │ ├── popup.html │ └── popup.js ├── requirements.txt └── webapp ├── api.py ├── api_test.py ├── app.yaml ├── fonts ├── Flat-UI-Icons.eot ├── Flat-UI-Icons.svg ├── Flat-UI-Icons.ttf └── Flat-UI-Icons.woff ├── frontend ├── about.jsx ├── backbonemixin.js ├── editor.jsx ├── faq.jsx ├── feed.jsx ├── follows.jsx ├── gravatar.js ├── header.jsx ├── models.js ├── review.jsx ├── site.jsx └── userheader.jsx ├── index.html ├── index.yaml ├── jsonify.py ├── main.py ├── models.py ├── package.json ├── search.py └── static ├── bootstrap.css ├── flat-ui.css ├── frontend.css ├── iloop.png ├── jquery.tagsinput.js └── reset.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | \#*\# 3 | webapp/static/frontend.js 4 | webapp/static/.module-cache 5 | webapp/static/.lock.pid 6 | node_modules 7 | memfinity_extension.zip 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frankenserver"] 2 | path = frankenserver 3 | url = https://github.com/Khan/frankenserver.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 kohlmeier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo '"make deps" installs dev software. Do this first!' 3 | @echo '"make server" runs the website.' 4 | @echo '"make test" runs unit tests.' 5 | 6 | deps: 7 | cd webapp && npm install 8 | pip install -r requirements.txt 9 | git submodule sync && git submodule update --init --recursive 10 | 11 | serve: 12 | cd webapp ; \ 13 | node_modules/.bin/watchify -t reactify frontend/* -o static/frontend.js & \ 14 | ../frankenserver/python/dev_appserver.py --host=0.0.0.0 . 15 | 16 | test: 17 | cd webapp ; \ 18 | nosetests --with-gae --gae-lib=../frankenserver/python 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Memfinity 2 | ==== 3 | 4 | Memfinity is a modern webapp and API to provide a social, spaced repetition system. You can easily create flash cards and practice them utilizing a spaced repetition algorithm. You can make your flash cards public or private. You can follow other users or search for topics of interest to discover new cards of interest. If you see a card you like, you can "take" (make a copy of) that card for yourself. 5 | 6 | A current, partial list of features: 7 | * Create, edit, and delete cards. 8 | * Support for Markdown syntax, including support for image links. 9 | * Chrome extension for even faster card creation. 10 | * Review cards using a spaced repetition algortihm (Leitner algorithm) 11 | * Follow/unfollow users. Your follows then populate a pesonalized "feed" of cards. 12 | * Full text search, including support for @usernames and #tags. 13 | * Authentication performed via Google accounts. 14 | * Open source and API-based architecture, for easy extension to mobile apps, etc. 15 | 16 | The site is developed on Google App Engine with the Python SDK. The frontend is written is React. Some desired features are listed as open issues, and pull requests are welcome! 17 | 18 | 19 | 20 | ## Installation Instructions 21 | # First, install Google App Engine SDK for Python. Clone the repo, and run: 22 | make deps 23 | make serve 24 | 25 | 26 | Primary application routes (TODO) 27 | ========================== 28 | 29 | ## Signed out 30 | 31 | / => Signup/Splash page 32 | /[user] => Specific [user] card list/stream 33 | 34 | ## Signed in 35 | 36 | / => Signed-in-user's card list/stream 37 | /feed => Signed-in-user's friends' chronological card-added feed 38 | /[user] => Specific [user] card list/stream 39 | -------------------------------------------------------------------------------- /extension/src/background.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /extension/src/background.js: -------------------------------------------------------------------------------- 1 | console.log('Background page!') 2 | 3 | var Login = { 4 | loggedIn: null, 5 | username: null, 6 | checkLogin: function(callback, ecallback){ 7 | var req = new XMLHttpRequest(); 8 | var self = this; 9 | req.open('GET', 'http://www.memfinity.org/api/user'); 10 | req.onload = function(e){ 11 | var user = JSON.parse(this.responseText); 12 | console.log("Login result:", user) 13 | if (user === null){ 14 | self.loggedIn = false; 15 | if (callback){ callback(false); } 16 | return; 17 | } 18 | self.loggedIn = true; 19 | self.username = user; 20 | if (callback){ callback(user); } 21 | }; 22 | req.onerror = function(){ 23 | console.error(arguments); 24 | if (ecallback) {ecallback.apply(this, arguments);} 25 | }; 26 | req.send(); 27 | return req; 28 | } 29 | } 30 | 31 | Login.checkLogin(); 32 | 33 | function uploadCard(card, callback){ 34 | var req = new XMLHttpRequest(); 35 | req.open('POST', 'http://www.memfinity.org/api/card'); 36 | var postdata = JSON.stringify(card); 37 | req.setRequestHeader("Content-type", "application/json"); 38 | req.onload = callback; 39 | req.onerror = function(){console.error(arguments);}; 40 | req.send(postdata); 41 | return req; 42 | } 43 | 44 | var currentCard = null; 45 | function onPopupClosed() { 46 | console.log('Popup closed! Current card:', currentCard); 47 | if (currentCard !== null){ 48 | uploadCard(currentCard, function(e){console.log(e, this.responseText)}); 49 | } 50 | } 51 | 52 | var PopupCloseMonitor = { 53 | timeoutId: 0, 54 | popupPing: function() { 55 | if(this.timeoutId != 0) { 56 | clearTimeout(this.timeoutId); 57 | } 58 | 59 | var self = this; 60 | this.timeoutId = setTimeout(function() { 61 | onPopupClosed(); 62 | self.timeoutId = 0; 63 | }, 1000); 64 | } 65 | } 66 | 67 | function gotCardFromContent(card){ 68 | if (Login.loggedIn){ 69 | // need some way of invalidating if the request comes 70 | // back bad! 71 | console.log('sending card to popup') 72 | currentCard = card; 73 | chrome.runtime.sendMessage({ 74 | origin: 'background', 75 | content: { 76 | initialize: 'addCard', 77 | data: {card: card, user: Login.username} 78 | } 79 | }); 80 | return; 81 | } 82 | function handleLoginResult(){ 83 | if (Login.loggedIn){ 84 | gotCardFromContent(card); 85 | return; 86 | } 87 | currentCard = null; 88 | chrome.runtime.sendMessage({ 89 | origin: 'background', 90 | content: { 91 | initialize: 'authenticate', 92 | data: null 93 | } 94 | }); 95 | } 96 | Login.checkLogin(handleLoginResult, handleLoginResult); 97 | } 98 | 99 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 100 | console.log('background got message', request); 101 | if (request.origin === 'content-script'){ 102 | sendResponse('ok'); 103 | gotCardFromContent(request.content); 104 | }else if (request.origin === 'popup'){ 105 | //console.log('Got updated card from popup', request.content); 106 | currentCard = request.content; 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /extension/src/fonts/Flat-UI-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlmeier/memfinity/3860985e29b203f0569f60eea68ffb22aaf34b1f/extension/src/fonts/Flat-UI-Icons.eot -------------------------------------------------------------------------------- /extension/src/fonts/Flat-UI-Icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/src/fonts/Flat-UI-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlmeier/memfinity/3860985e29b203f0569f60eea68ffb22aaf34b1f/extension/src/fonts/Flat-UI-Icons.ttf -------------------------------------------------------------------------------- /extension/src/fonts/Flat-UI-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlmeier/memfinity/3860985e29b203f0569f60eea68ffb22aaf34b1f/extension/src/fonts/Flat-UI-Icons.woff -------------------------------------------------------------------------------- /extension/src/gravatar.js: -------------------------------------------------------------------------------- 1 | function getGravatar(email, size) { 2 | email = email || 'example@example.com'; 3 | 4 | // MD5 (Message-Digest Algorithm) by WebToolkit 5 | // 6 | 7 | var MD5=function(s){function L(k,d){return(k<A social spaced-repetition system.
19 |Learn with your friends. Remember forever.
20 |Remember all the things! We all encounter facts and information that we'd love to remember permanently, and now it's possible. Enter Memfinity, a powerful tool for personalized and social learning.
33 |Memfinity lets you create your own flashcards right in your browser. In addition to the website, Memfinity offers a Chrome browser extension for even faster card creation. See a new vocab word while reading online? Wanna remember that incredible statistic? Stoked about an awesome keyboard shortcut, math concept, or piece of ridculous trivia? With card creation this simple, it takes just seconds to file information away for permanent recall.
36 |Spaced repetition algorithms are a game changer for personal learning. Each time you practice a card, you can rate it as "easy" or "hard". Memfinity will automatically adapt and schedule your next review of that card, maximizing your efficiency and making it possible to ensure all concepts will eventually become part of your permanent knowledge.
40 |Memfinity is social, so you can also follow the public learning activity of people that share your learning interests. See a feed of what others care about learning, and efforlessly create a copy of cards that you want to learn, too.
44 |Memfinity is built from the ground up as a web-service. That means the open source community can create new apps for phones, browsers, or any device. Also, with Memfinity your data is never held hostage. We're open source, and you're always free to host your own personal version of Memfinity. And by learning with Memfinity, you're not only helping yourself learn; you're opening up doors for world-class research on memory and how people learn.
49 |Still wanna know more? Check out our FAQ page.
52 |A: Memfinity uses a Leitner system algorithm. The current configuration for the algorithm uses five boxes with corresponding review intervals of 1, 5, 25, 125, and 625 days. In the future, we'd like to add the ability for each user to customize this configuration.
24 |A: Memfinity was made as a hackthon project with friends from Khan Academy. While other spaced repetition software exists, we were intrigued by making something that had social component. We also wanted something that was web-native and built on top of a web services API. Memfinity requires no downloads, and lives where you live and learn--in your browser.
28 |A: Yup, at least if the images are hosted somewhere else on the web. Memfinity supports Markdown syntax for your cards, and you can find the syntax for specifying an image in Markdown here. If your images are not already hosted on the web, you can use a product like a public Dropbox folder to create linkable URLs for your images.
32 |A: Right now, Memfinity requires a Google account to log in. You can search public cards without logging in, but in order to create and practice cards you will need to create a free Google account.
36 |A: Memfinity uses Gravatar for avatar support. To customize your avatar, create a Gravatar associated with the Gmail account you use for logging in to Memfinity.
40 |A: If you're a developer, you can find us on GitHub and help make the project better. We consider it a pretty basic proof-of-concept right now, and we'd love to keep improving it.
44 |you have no cards needing practice right now.
125 |126 | Create new cards or 127 | continue practicing 128 |
129 |