├── .bowerrc ├── favicon.ico ├── favicon-easter-egg.png ├── src ├── fonts │ ├── penis │ │ ├── penis.eot │ │ ├── penis.otf │ │ ├── penis.ttf │ │ ├── penis.woff │ │ ├── stylesheet.scss │ │ └── penis.html │ ├── icomoon │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ ├── stylesheet.scss │ │ ├── icomoon.svg │ │ └── selection.json │ └── peax │ │ ├── peaxwebdesignfreeicons-webfont.eot │ │ ├── peaxwebdesignfreeicons-webfont.ttf │ │ ├── peaxwebdesignfreeicons-webfont.woff │ │ ├── peaxwebdesignfreeicons-webfont.woff2 │ │ └── stylesheet.scss ├── js │ ├── shared │ │ ├── get-range.js │ │ ├── create-array.js │ │ ├── create-element.js │ │ ├── once.js │ │ ├── get-random-char.js │ │ └── mediator.js │ ├── config │ │ ├── animation-end.js │ │ └── levels.js │ ├── main.js │ ├── app.js │ └── lib │ │ ├── tracking.js │ │ ├── locker.js │ │ ├── opening.js │ │ ├── back-button.js │ │ ├── easter-egg.js │ │ ├── card.js │ │ ├── next-level.js │ │ ├── pages.js │ │ └── game.js ├── sass │ ├── shared │ │ ├── _extends.sass │ │ ├── _mixins.sass │ │ ├── _no-modular.sass │ │ ├── _config.sass │ │ └── _animations.sass │ ├── _layout.sass │ ├── components │ │ ├── _social-list.sass │ │ ├── _back-button.sass │ │ ├── _locker.sass │ │ ├── _fonts-loader.sass │ │ ├── _footer.sass │ │ ├── _easter-egg.sass │ │ ├── _header.sass │ │ ├── _inputs.sass │ │ ├── _page.sass │ │ └── _board.sass │ ├── _base.sass │ └── main.sass ├── partials │ ├── google-analytics.ejs │ └── easter-egg.ejs ├── index.html └── images │ └── easter-egg.svg ├── Gruntfile.js ├── _grunt-tasks ├── _deploy.js ├── _develop.js ├── _build.js ├── gh-pages.js ├── connect.js ├── uglify.js ├── sass.js ├── htmlmin.js ├── postcss.js ├── requirejs.js ├── watch.js ├── render.js ├── copy.js ├── jshint.js └── replace.js ├── config-development.json ├── config-production.json ├── LICENSE ├── bower.json ├── .gitignore ├── package.json └── README.md /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/js/vendor" 3 | } 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/favicon.ico -------------------------------------------------------------------------------- /favicon-easter-egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/favicon-easter-egg.png -------------------------------------------------------------------------------- /src/fonts/penis/penis.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/penis/penis.eot -------------------------------------------------------------------------------- /src/fonts/penis/penis.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/penis/penis.otf -------------------------------------------------------------------------------- /src/fonts/penis/penis.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/penis/penis.ttf -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 'use strict'; 3 | grunt.loadTasks('_grunt-tasks'); 4 | }; 5 | -------------------------------------------------------------------------------- /src/fonts/penis/penis.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/penis/penis.woff -------------------------------------------------------------------------------- /src/fonts/icomoon/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/icomoon/icomoon.eot -------------------------------------------------------------------------------- /src/fonts/icomoon/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/icomoon/icomoon.ttf -------------------------------------------------------------------------------- /src/fonts/icomoon/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/icomoon/icomoon.woff -------------------------------------------------------------------------------- /src/fonts/peax/peaxwebdesignfreeicons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/peax/peaxwebdesignfreeicons-webfont.eot -------------------------------------------------------------------------------- /src/fonts/peax/peaxwebdesignfreeicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/peax/peaxwebdesignfreeicons-webfont.ttf -------------------------------------------------------------------------------- /src/fonts/peax/peaxwebdesignfreeicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/peax/peaxwebdesignfreeicons-webfont.woff -------------------------------------------------------------------------------- /src/js/shared/get-range.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return function (initial, to) { 3 | return Math.floor(Math.random() * to) + initial; 4 | }; 5 | }); 6 | -------------------------------------------------------------------------------- /src/fonts/peax/peaxwebdesignfreeicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weslleyaraujo/memory-match/HEAD/src/fonts/peax/peaxwebdesignfreeicons-webfont.woff2 -------------------------------------------------------------------------------- /src/js/shared/create-array.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return function (length) { 3 | return Array.apply(null, { 4 | length: length 5 | }); 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /src/js/shared/create-element.js: -------------------------------------------------------------------------------- 1 | define(['jquery'], function ($) { 2 | return function (tagname, data) { 3 | return $('<' + tagname + '/>', data || {}); 4 | }; 5 | }); 6 | -------------------------------------------------------------------------------- /_grunt-tasks/_deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.registerTask('deploy', [ 5 | 'build', 6 | 'gh-pages', 7 | ]); 8 | 9 | }; 10 | -------------------------------------------------------------------------------- /src/sass/shared/_extends.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // extends 3 | // *** 4 | 5 | %default-transition 6 | transition: 0.5s 7 | 8 | %longer-transition 9 | transition: 1s 10 | -------------------------------------------------------------------------------- /src/js/config/animation-end.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return [ 3 | 'webkitAnimationEnd', 4 | 'oanimationend', 5 | 'msAnimationEnd', 6 | 'animationend', 7 | ].join(' '); 8 | }); 9 | -------------------------------------------------------------------------------- /src/sass/shared/_mixins.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // mixins file 3 | // *** 4 | 5 | // media 6 | =media($type) 7 | @if $type == "mobile" 8 | @media screen and (max-width: 600px) 9 | @content 10 | -------------------------------------------------------------------------------- /src/sass/_layout.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // Base 3 | // *** 4 | 5 | .l-wrap 6 | width: 100% 7 | max-width: 1000px 8 | margin: 0 auto 9 | 10 | .l-container 11 | width: 100% 12 | clear: both 13 | 14 | .l-max-w 15 | width: 270px 16 | margin: 0 auto 17 | -------------------------------------------------------------------------------- /_grunt-tasks/_develop.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.registerTask('develop', [ 5 | 'jshint:all', 6 | 'sass', 7 | 'postcss', 8 | 'render', 9 | 'copy', 10 | 'connect', 11 | 'watch' 12 | ]); 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /src/sass/components/_social-list.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-social-list 3 | // *** 4 | 5 | $module-name: 'ui-social-list' 6 | 7 | .#{$module-name} 8 | font-size: 30px; 9 | 10 | .#{$module-name}-link 11 | transition: 0.4s 12 | &:hover 13 | color: $color-transparent-dark 14 | -------------------------------------------------------------------------------- /src/js/shared/once.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return function(fn, context) { 3 | var result; 4 | return function() { 5 | if(fn) { 6 | result = fn.apply(context || this, arguments); 7 | fn = null; 8 | } 9 | return result; 10 | }; 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /src/sass/components/_back-button.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-back-button 3 | // *** 4 | 5 | $module-name: 'ui-back-button' 6 | 7 | .#{$module-name} 8 | transform: translateX(0); 9 | opacity: 1 10 | transition: 0.3s 11 | &.is-hidden 12 | transform: translateX(-100%); 13 | opacity: 0; 14 | -------------------------------------------------------------------------------- /_grunt-tasks/_build.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.registerTask('build', [ 5 | 'jshint:all', 6 | 'sass', 7 | 'postcss', 8 | 'copy', 9 | 'requirejs', 10 | 'uglify', 11 | 'render', 12 | 'htmlmin', 13 | 'replace', 14 | ]); 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Main file for global configs 3 | * 4 | * */ 5 | requirejs.config({ 6 | baseUrl: 'src/js', 7 | paths: { 8 | jquery: 'vendor/jquery/dist/jquery', 9 | konami: 'vendor/konami-js/konami', 10 | fastclick: 'vendor/fastclick/lib/fastclick', 11 | } 12 | }); 13 | 14 | requirejs(['app']); 15 | -------------------------------------------------------------------------------- /config-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "hostname": "http://localhost:8180/", 3 | "js" : { 4 | "main": "/src/js/main.js", 5 | "vendor": "/src/js/vendor/requirejs/require.js" 6 | }, 7 | 8 | "css" : { 9 | "main": "/dist/css/main.css" 10 | }, 11 | 12 | "images" : { 13 | "src": "/src/images/" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/sass/components/_locker.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-locker 3 | // *** 4 | 5 | $module-name: 'ui-locker' 6 | 7 | .#{$module-name} 8 | width: 100% 9 | height: 100% 10 | position: fixed 11 | left: 0 12 | top: 0 13 | right: 0 14 | bottom: 0 15 | z-index: -1 16 | 17 | // states 18 | &.is-active 19 | z-index: 2 20 | -------------------------------------------------------------------------------- /src/js/shared/get-random-char.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return function (charSize) { 3 | charSize = charSize || 1; 4 | 5 | return Math.random() 6 | .toString(36) 7 | .replace(/[0-9]/g, '') 8 | .replace(/\./g, '') 9 | .slice(0, charSize) 10 | [(Math.random()<0.5) ? 'toUpperCase' : 'toString'](); 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /_grunt-tasks/gh-pages.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('gh-pages', { 5 | options: { 6 | base: 'dist' 7 | }, 8 | src: ['**'] 9 | }); 10 | 11 | grunt.registerTask('gh-pages', function() { 12 | grunt.task.run(['gh-pages']); 13 | }); 14 | 15 | grunt.loadNpmTasks('grunt-gh-pages'); 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /_grunt-tasks/connect.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('connect', { 5 | server: { 6 | options: { 7 | port: 8180, 8 | base: '.' 9 | } 10 | } 11 | }); 12 | 13 | grunt.registerTask('connect', function() { 14 | grunt.task.run(['connect']); 15 | }); 16 | 17 | grunt.loadNpmTasks('grunt-contrib-connect'); 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /config-production.json: -------------------------------------------------------------------------------- 1 | { 2 | "hostname": "http://weslleyaraujo.github.io/memory-match/", 3 | "js" : { 4 | "main": "/js/main.min.js", 5 | "vendor": "/js/require.min.js" 6 | }, 7 | 8 | "css" : { 9 | "main": "/css/main.css" 10 | }, 11 | 12 | "fonts": { 13 | "src": "http://weslleyaraujo.github.io/memory-match/fonts/" 14 | }, 15 | 16 | "images" : { 17 | "src": "/images/" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /_grunt-tasks/uglify.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('uglify', { 5 | app: { 6 | files: { 7 | 'dist/js/require.min.js' : ['src/js/vendor/requirejs/require.js'] 8 | } 9 | } 10 | }); 11 | 12 | grunt.registerTask('uglify', function() { 13 | grunt.task.run(['uglify']); 14 | }); 15 | 16 | grunt.loadNpmTasks('grunt-contrib-uglify'); 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /src/sass/_base.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // Base 3 | // *** 4 | 5 | body, html 6 | margin: 0 7 | padding: 0 8 | font-family: $font-family-default 9 | font-weight: 400 10 | background-color: map-get($colors, 3) 11 | color: $color-transparent-dark 12 | font-size: $font-size-default 13 | +media('mobile') 14 | font-size: $font-size-mobile-default 15 | 16 | a 17 | text-decoration: none 18 | color: inherit 19 | -------------------------------------------------------------------------------- /src/sass/components/_fonts-loader.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-fonts-loader 3 | // *** 4 | 5 | /* 6 | * FIXME: This only exists to "trigger" the font downloads 7 | */ 8 | 9 | $module-name: 'ui-fonts-loader' 10 | 11 | .#{$module-name} 12 | text-indent: -999999px 13 | font-size: 0 14 | 15 | .#{$module-name}__freak 16 | font-family: $font-family-icon 17 | 18 | .#{$module-name}__special 19 | font-family: $font-family-special 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------------------------------------------------------- 3 | * "THE BEER-WARE LICENSE" (Revision 42): 4 | * wrote this file. As long as you retain this notice you 5 | * can do whatever you want with this stuff. If we meet some day, and you think 6 | * this stuff is worth it, you can buy me a beer in return Weslley Araujo 7 | * ---------------------------------------------------------------------------- 8 | */ 9 | -------------------------------------------------------------------------------- /src/partials/google-analytics.ejs: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /_grunt-tasks/sass.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('sass', { 5 | options: { 6 | sourceMap: true, 7 | outputStyle: 'compressed' 8 | }, 9 | dist: { 10 | files: { 11 | 'dist/css/main.css': 'src/sass/main.sass' 12 | } 13 | } 14 | }); 15 | 16 | grunt.registerTask('sass', function() { 17 | grunt.task.run(['sass']); 18 | }); 19 | 20 | grunt.loadNpmTasks('grunt-sass'); 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /_grunt-tasks/htmlmin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('htmlmin', { 5 | dist: { 6 | options: { 7 | removeComments: true, 8 | collapseWhitespace: true 9 | }, 10 | files: { 11 | 'dist/index.html': 'index.html' 12 | } 13 | }, 14 | }); 15 | 16 | grunt.registerTask('htmlmin', function() { 17 | grunt.task.run(['htmlmin']); 18 | }); 19 | 20 | grunt.loadNpmTasks('grunt-contrib-htmlmin'); 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /src/js/app.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'fastclick', 3 | 'lib/opening', 4 | 'lib/game', 5 | 'lib/locker', 6 | 'lib/easter-egg', 7 | 'lib/back-button', 8 | 'lib/next-level', 9 | 'lib/tracking', 10 | ], function (fastclick, opening, game, locker, easterEgg, backButton, nextLevel, tracking) { 11 | fastclick.attach(document.body); 12 | 13 | Array.prototype.slice.call(arguments).forEach(function (component) { 14 | if (component.init) { 15 | component.init(); 16 | } 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /src/js/config/levels.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | return [ 3 | { 4 | index: 1, 5 | name: 'easy', 6 | size: 2, 7 | }, 8 | 9 | { 10 | index: 2, 11 | name: 'medium', 12 | size: 4, 13 | }, 14 | 15 | { 16 | index: 3, 17 | name: 'hard', 18 | size: 6, 19 | }, 20 | 21 | { 22 | index: 4, 23 | name: 'expert', 24 | size: 8, 25 | }, 26 | { 27 | index: 5, 28 | name: 'super-expert', 29 | size: 10, 30 | }, 31 | ]; 32 | }); 33 | -------------------------------------------------------------------------------- /src/fonts/penis/stylesheet.scss: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // penis font 3 | // *** 4 | 5 | $source-location: '/src/fonts/penis'; 6 | 7 | @font-face { 8 | font-family: "penis"; 9 | src: url("#{$source-location}/penis.eot"); 10 | src: url("#{$source-location}/penis.eot#iefix") format("embedded-opentype"), 11 | url("#{$source-location}/penis.woff") format("woff"), 12 | url("#{$source-location}/penis.ttf") format("truetype"), 13 | url("#{$source-location}/penis.svg") format("svg"); 14 | font-weight: normal; 15 | font-style: normal; 16 | } 17 | -------------------------------------------------------------------------------- /_grunt-tasks/postcss.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('postcss', { 5 | options: { 6 | map: true, 7 | processors: [ 8 | require('autoprefixer-core')({ 9 | browsers: 'last 4 versions' 10 | }), 11 | require('csswring') 12 | ] 13 | }, 14 | dist: { 15 | src: 'dist/css/*.css' 16 | } 17 | }); 18 | 19 | grunt.registerTask('postcss', function() { 20 | grunt.task.run(['postcss']); 21 | }); 22 | 23 | grunt.loadNpmTasks('grunt-postcss'); 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /src/sass/components/_footer.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-footer 3 | // *** 4 | 5 | $module-name: 'ui-footer' 6 | 7 | .#{$module-name} 8 | position: fixed 9 | bottom: 0 10 | z-index: 2 11 | text-align: right 12 | line-height: 10px 13 | font-size: $font-size-mini 14 | padding: 15px 10px 15 | box-sizing: border-box 16 | width: 100% 17 | +media('mobile') 18 | font-size: $font-size-mobile-mini 19 | 20 | .#{$module-name}-icon 21 | font-size: 10px 22 | 23 | .#{$module-name}-username 24 | transition: color 0.3s 25 | &:hover 26 | color: $color-light 27 | -------------------------------------------------------------------------------- /_grunt-tasks/requirejs.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('requirejs', { 5 | compile: { 6 | options: { 7 | baseUrl: 'src/js/', 8 | mainConfigFile: 'src/js/main.js', 9 | name: 'app', 10 | out: 'dist/js/main.min.js', 11 | include: ['main'], 12 | preserveLicenseComments: false 13 | } 14 | } 15 | }); 16 | 17 | grunt.registerTask('requirejs', function() { 18 | grunt.task.run(['requirejs']); 19 | }); 20 | 21 | grunt.loadNpmTasks('grunt-contrib-requirejs'); 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-match", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/weslleyaraujo/memory-match", 5 | "authors": [ 6 | "Weslley Araujo " 7 | ], 8 | "description": "A simple memory match game created with javascript.", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "devDependencies": { 18 | "konami-js": "*", 19 | "requirejs": "~2.1.18", 20 | "jquery": "~2.1.4", 21 | "fastclick": "~1.0.6" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /_grunt-tasks/watch.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('watch', { 5 | sass: { 6 | files: [ 7 | 'src/sass/*.sass', 8 | 'src/sass/**/*.sass', 9 | 'src/fonts/**/*.scss' 10 | ], 11 | tasks: ['sass', 'postcss'] 12 | }, 13 | ejs: { 14 | files: [ 15 | 'src/*.html', 16 | '*.json', 17 | ], 18 | tasks: ['render'] 19 | } 20 | }); 21 | 22 | grunt.registerTask('watch', function() { 23 | grunt.task.run(['watch']); 24 | }); 25 | 26 | grunt.loadNpmTasks('grunt-contrib-watch'); 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /_grunt-tasks/render.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | var CONFIG = 'config' + (grunt.option('config') ? '-' + grunt.option('config') : '-development') + '.json'; 5 | 6 | grunt.config('render', { 7 | app: { 8 | options: { 9 | data: { 10 | config: grunt.file.readJSON(CONFIG) 11 | }, 12 | }, 13 | 14 | files: { 15 | 'index.html': ['src/index.html'] 16 | } 17 | } 18 | }); 19 | 20 | grunt.registerTask('ejs-render', function() { 21 | grunt.task.run(['ejs-render']); 22 | }); 23 | 24 | grunt.loadNpmTasks('grunt-ejs-render'); 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /_grunt-tasks/copy.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('copy', { 5 | fonts: { 6 | expand: true, 7 | cwd: 'src/fonts/', 8 | src: '**', 9 | dest: 'dist/fonts', 10 | }, 11 | 12 | images: { 13 | expand: true, 14 | cwd: 'src/images/', 15 | src: '**', 16 | dest: 'dist/images', 17 | }, 18 | 19 | ico: { 20 | expand: true, 21 | cwd: '.', 22 | src: ['*.ico', '*.png'], 23 | dest: 'dist', 24 | } 25 | }); 26 | 27 | grunt.registerTask('copy', function() { 28 | grunt.task.run(['copy']); 29 | }); 30 | 31 | grunt.loadNpmTasks('grunt-contrib-copy'); 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /src/sass/shared/_no-modular.sass: -------------------------------------------------------------------------------- 1 | // ***** 2 | // No Modular 3 | // 4 | // *** 5 | 6 | // list helpers 7 | .g-clear-list 8 | list-style-type: none 9 | margin: 0 10 | padding: 0 11 | 12 | .g-inline-list 13 | display: inline-block 14 | 15 | // clearfix 16 | .g-clearfix 17 | clear: both 18 | overflow: hidden 19 | 20 | // hidden 21 | .g-is-hidden 22 | display: none 23 | 24 | // text position 25 | .g-text-center 26 | text-align: center 27 | 28 | .g-text-right 29 | text-align: right 30 | 31 | .g-text-left 32 | text-align: left 33 | 34 | // float 35 | .g-pull-right 36 | float: right 37 | 38 | .g-pull-left 39 | float: left 40 | 41 | // color 42 | .g-light-color 43 | color: $color-light 44 | -------------------------------------------------------------------------------- /src/js/lib/tracking.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'shared/mediator', 3 | ], function (mediator) { 4 | 5 | return { 6 | init: function () { 7 | this.bind(); 8 | }, 9 | 10 | prepare: function () { 11 | 12 | }, 13 | 14 | bind: function () { 15 | mediator.subscribe('game:start', $.proxy(this.track, this, 'game', 'start')); 16 | mediator.subscribe('game:win', $.proxy(this.track, this, 'game', 'win')); 17 | mediator.subscribe('game:abort', $.proxy(this.track, this, 'game', 'abort')); 18 | mediator.subscribe('easteregg:show', $.proxy(this.track, this, 'game', 'easteregg')); 19 | }, 20 | 21 | track: function (name, label) { 22 | ga('send', 'event', name, label); 23 | } 24 | }; 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /src/sass/components/_easter-egg.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-easter-egg 3 | // *** 4 | 5 | $module-name: 'ui-easter-egg' 6 | 7 | .#{$module-name} 8 | position: fixed 9 | z-index: $z-index-easter-egg 10 | background-color: $color-transparent-white 11 | width: 100% 12 | height: 100% 13 | left: 0 14 | top: 0 15 | display: none 16 | 17 | // state 18 | &.is-visible 19 | display: block 20 | animation: blink 0.1s infinite 21 | 22 | &.is-done 23 | animation: zoom-out .5s both ease-in 24 | 25 | .#{$module-name}-image 26 | position: fixed 27 | left: 0 28 | right: 0 29 | top: 0 30 | bottom: 0 31 | margin: auto 32 | animation: in-grow 1.5s both 33 | svg 34 | fill: $color-transparent-white 35 | -------------------------------------------------------------------------------- /src/js/shared/mediator.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | var subscriptions = {}, 4 | Mediator = function() {}; 5 | 6 | Mediator.prototype.subscribe = function(key, callback, context) { 7 | subscriptions[key] = subscriptions[key] || []; 8 | 9 | subscriptions[key].push({ 10 | callback: callback, 11 | context: context || window 12 | }); 13 | }; 14 | 15 | Mediator.prototype.publish = function(key, params) { 16 | var length; 17 | 18 | if(!subscriptions[key]) { 19 | return; 20 | } 21 | 22 | length = subscriptions[key].length; 23 | 24 | for(var i = 0; i < length; i += 1) { 25 | subscriptions[key][i].callback.call(subscriptions[key][i].context, params); 26 | } 27 | }; 28 | 29 | return new Mediator(); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /_grunt-tasks/jshint.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.config('jshint', { 5 | options: { 6 | debug: true 7 | }, 8 | all: { 9 | src: [ 10 | 'Gruntfile.js', 11 | 'src/js/**/*.js', 12 | 'src/js/*.js', 13 | '!src/js/vendor/**/*.js', 14 | ] 15 | }, 16 | specific: { 17 | options: { 18 | debug: true, 19 | expr: true 20 | }, 21 | src: [] 22 | } 23 | }); 24 | 25 | grunt.registerTask('jshint', function() { 26 | grunt.task.run(['jshint']); 27 | }); 28 | 29 | grunt.event.on('watch', function(action, filepath) { 30 | grunt.config('jshint.specific.src', filepath); 31 | }); 32 | 33 | grunt.loadNpmTasks('grunt-contrib-jshint'); 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /src/fonts/peax/stylesheet.scss: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // peax font 3 | // *** 4 | 5 | $source-location: '/src/fonts/peax'; 6 | 7 | @font-face { 8 | font-family: 'peaxwebdesignfreeiconsmedium'; 9 | src: url('#{$source-location}/peaxwebdesignfreeicons-webfont.eot'); 10 | src: url('#{$source-location}/peaxwebdesignfreeicons-webfont.eot?#iefix') format('embedded-opentype'), 11 | url('#{$source-location}/peaxwebdesignfreeicons-webfont.woff2') format('woff2'), 12 | url('#{$source-location}/peaxwebdesignfreeicons-webfont.woff') format('woff'), 13 | url('#{$source-location}/peaxwebdesignfreeicons-webfont.ttf') format('truetype'), 14 | url('#{$source-location}/peaxwebdesignfreeicons-webfont.svg#peaxwebdesignfreeiconsmedium') format('svg'); 15 | font-weight: normal; 16 | font-style: normal; 17 | } 18 | -------------------------------------------------------------------------------- /src/sass/main.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // main file 3 | // *** 4 | @import 'shared/config.sass' 5 | @import 'shared/mixins.sass' 6 | @import 'shared/no-modular.sass' 7 | @import 'shared/extends' 8 | @import 'shared/animations' 9 | 10 | // fonts 11 | @import '../fonts/peax/stylesheet.scss' 12 | @import '../fonts/icomoon/stylesheet.scss' 13 | @import '../fonts/penis/stylesheet.scss' 14 | 15 | // base 16 | @import 'base' 17 | 18 | // layout 19 | @import 'layout' 20 | 21 | // modules 22 | @import './components/page' 23 | @import './components/header' 24 | @import './components/inputs' 25 | @import './components/board' 26 | @import './components/locker' 27 | @import './components/easter-egg' 28 | @import './components/social-list' 29 | @import './components/back-button' 30 | @import './components/footer' 31 | @import './components/fonts-loader' 32 | -------------------------------------------------------------------------------- /_grunt-tasks/replace.js: -------------------------------------------------------------------------------- 1 | /* 2 | * FIXME: Think in some smarter way to do this 3 | */ 4 | module.exports = function(grunt) { 5 | 'use strict'; 6 | 7 | var CONFIG_FILE = grunt.file.readJSON('config-production.json'); 8 | 9 | grunt.config('replace', { 10 | dist: { 11 | options: { 12 | patterns: [ 13 | { 14 | match: /\/src\/fonts\//g, 15 | replacement: CONFIG_FILE.fonts.src 16 | } 17 | ] 18 | }, 19 | files: [ 20 | { 21 | expand: true, 22 | flatten: true, 23 | src: ['dist/css/*.css'], 24 | dest: 'dist/css/' 25 | } 26 | ] 27 | } 28 | }); 29 | 30 | grunt.registerTask('replace', function() { 31 | grunt.task.run(['replace']); 32 | }); 33 | 34 | grunt.loadNpmTasks('grunt-replace'); 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /src/js/lib/locker.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'shared/mediator', 3 | ], function(mediator) { 4 | return { 5 | config: { 6 | el: '[data-component="locker"]' 7 | }, 8 | 9 | init: function() { 10 | this.prepare(); 11 | this.bind(); 12 | }, 13 | 14 | prepare: function() { 15 | this.elements = {}; 16 | this.elements.$el = $(this.config.el); 17 | }, 18 | 19 | bind: function() { 20 | mediator.subscribe('locker:active', $.proxy(this.onLockerActive, this)); 21 | mediator.subscribe('locker:remove', $.proxy(this.onLockerRemove, this)); 22 | }, 23 | 24 | onLockerActive: function () { 25 | this.active(); 26 | }, 27 | 28 | onLockerRemove: function () { 29 | this.remove(); 30 | }, 31 | 32 | active: function () { 33 | this.elements.$el.addClass('is-active'); 34 | }, 35 | 36 | remove: function () { 37 | this.elements.$el.removeClass('is-active'); 38 | }, 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /src/sass/shared/_config.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // config file 3 | // *** 4 | 5 | // colors 6 | // 7 | // 0: soft-blue 8 | // 1: soft-red 9 | // 2: strong-blue 10 | // 3: strong-brown 11 | // 4: soft-brown 12 | 13 | $colors: (0: #85DB18, 1: #CDE855, 2: #F5F6D4, 3: #A7C520, 4: #493F0B) 14 | $color-light: #FFF 15 | $color-dark: #000 16 | $color-reading: #AAA 17 | $color-transparent-dark: rgba(0,0,0,.3) 18 | $color-transparent-dark-soft: rgba(0,0,0,.1) 19 | $color-transparent-white: rgba(255,255,255,.3) 20 | 21 | // themes 22 | $theme-special: 'penis-mode' 23 | 24 | // fonts 25 | $font-family-default: 'Lato' 26 | $font-family-icon: 'peaxwebdesignfreeiconsmedium' 27 | $font-family-special: 'penis' 28 | 29 | // text 30 | $main-text-color: #FFF 31 | $primary-font-family: Arial 32 | $font-size-default: 16px 33 | $font-size-mini: 14px 34 | $font-size-mobile-default: 14px 35 | $font-size-mobile-mini: 12px 36 | 37 | // z-index manager 38 | $z-index-header: 10 39 | $z-index-easter-egg: 999 40 | 41 | // transtion 42 | $transition-time: 0.3s 43 | -------------------------------------------------------------------------------- /src/sass/components/_header.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-header 3 | // *** 4 | 5 | $module-name: 'ui-header' 6 | $module-height: 45px 7 | $module-height-mobile: 35px 8 | 9 | .#{$module-name} 10 | position: fixed 11 | left: 0 12 | top: 0 13 | font-size: 16px 14 | display: block 15 | width: 100% 16 | height: $module-height 17 | line-height: $module-height 18 | box-sizing: border-box 19 | background-color: $color-transparent-white 20 | overflow: hidden 21 | z-index: $z-index-header 22 | p 23 | margin: 0 24 | padding-left: 10px 25 | +media('mobile') 26 | font-size: $font-size-mobile-default 27 | height: $module-height-mobile 28 | line-height: $module-height-mobile 29 | 30 | .#{$module-name}-content 31 | height: 100% 32 | position: relative 33 | 34 | .#{$module-name}-link 35 | display: inline-block 36 | height: 100% 37 | padding: 0 10px 38 | box-sizing: border-box 39 | transition: $transition-time 40 | &:hover 41 | background-color: $color-transparent-white 42 | 43 | .#{$module-name}-link-icon 44 | position: relative 45 | top: 1px 46 | -------------------------------------------------------------------------------- /src/sass/components/_inputs.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-inputs 3 | // *** 4 | 5 | $module-name: 'ui-inputs' 6 | $module-height: 45px 7 | 8 | .#{$module-name} 9 | border-radius: 0 10 | border: none 11 | padding: 10px 75px 12 | font-size: 16px 13 | color: $color-reading 14 | background-color: $color-light 15 | appearance: none 16 | box-shadow: 0 3px 0 $color-transparent-dark-soft 17 | height: $module-height 18 | font-family: $font-family-default 19 | width: 100% 20 | margin: 10px 0 21 | cursor: pointer 22 | outline: 0 23 | transition: background-color $transition-time, color $transition-time 24 | 25 | .#{$module-name}-wrap 26 | position: relative 27 | width: 100% 28 | height: 45px 29 | margin: 10px 0 30 | .#{$module-name} 31 | position: absolute 32 | left: 0 33 | top: 0 34 | margin: 0 35 | padding-left: 10px 36 | i 37 | position: absolute 38 | z-index: 1 39 | top: 15px 40 | right: 10px 41 | transition: color $transition-time 42 | 43 | .#{$module-name}__text-toggle 44 | color: $color-light 45 | &.is-active 46 | color: $color-reading 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Vim ### 4 | [._]*.s[a-w][a-z] 5 | [._]s[a-w][a-z] 6 | *.un~ 7 | Session.vim 8 | .netrwhist 9 | *~ 10 | 11 | 12 | ### Node ### 13 | # Logs 14 | logs 15 | *.log 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (http://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directory 38 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 39 | node_modules 40 | 41 | 42 | ### Bower ### 43 | src/js/vendor 44 | .bower-cache 45 | .bower-registry 46 | .bower-tmp 47 | 48 | ### Sass ### 49 | .sass-cache 50 | *.css.map 51 | 52 | ### Project ## 53 | dist/css/*.css 54 | dist/js/*.js 55 | dist/fonts/**/* 56 | dist/images/ 57 | dist/*.ico 58 | dist/*.png 59 | index.html 60 | !src/index.html 61 | *.DS_Store 62 | -------------------------------------------------------------------------------- /src/sass/components/_page.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-page 3 | // *** 4 | 5 | $module-name: 'ui-page' 6 | 7 | .#{$module-name} 8 | width: 100% 9 | height: 100% 10 | position: absolute 11 | top: 0 12 | left: 0 13 | visibility: hidden 14 | overflow: hidden 15 | backface-visibility: hidden 16 | transform: translate3d(0, 0, 0) 17 | transform-style: preserve-3d 18 | 19 | // state 20 | &.is-visible 21 | visibility: visible 22 | z-index: 1 23 | 24 | // modifier 25 | &.#{$module-name}--move-to-left 26 | animation: move-to-left .6s ease both 27 | 28 | &.#{$module-name}--move-to-right 29 | animation: move-to-right .6s ease both 30 | 31 | &.#{$module-name}--move-from-left 32 | animation: move-from-left .6s ease both 33 | 34 | &.#{$module-name}--move-from-right 35 | animation: move-from-right .6s ease both 36 | 37 | .#{$module-name}-content 38 | position: relative 39 | top: 50% 40 | transform: translateY(-50%) 41 | 42 | // modifier theme 43 | .#{$module-name}--primary 44 | background-color: map-get($colors, 0) 45 | 46 | .#{$module-name}--secondary 47 | background-color: map-get($colors, 1) 48 | 49 | .#{$module-name}--tertiary 50 | background-color: map-get($colors, 3) 51 | -------------------------------------------------------------------------------- /src/js/lib/opening.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lib/pages', 3 | 4 | 'config/levels', 5 | 'shared/mediator' 6 | 7 | ], function(pages, levels, mediator) { 8 | 9 | return $.extend(true, {}, pages, { 10 | config: { 11 | el: '[data-component="initial-form"]' 12 | }, 13 | 14 | init: function () { 15 | this.prepare(); 16 | this.bind(); 17 | }, 18 | 19 | prepare: function () { 20 | this.elements = {}; 21 | this.elements.$el = $(this.config.el); 22 | }, 23 | 24 | bind: function () { 25 | this.elements.$el.on('submit', $.proxy(this.onSubmit, this)); 26 | }, 27 | 28 | getLevelName: function () { 29 | return this.elements.$el.find('[name="level"]').val(); 30 | }, 31 | 32 | getLevel: function (value) { 33 | return levels.filter(function (item) { 34 | return item.name === value; 35 | })[0]; 36 | }, 37 | 38 | onSubmit: function (event) { 39 | this.changePage('game').done($.proxy(this.onPageChange, this)); 40 | event.preventDefault(); 41 | }, 42 | 43 | onPageChange: function () { 44 | var name = this.getLevelName(); 45 | mediator.publish('game:start', this.getLevel(name)); 46 | }, 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /src/js/lib/back-button.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 4 | 'lib/pages', 5 | 'shared/mediator', 6 | ], function (jquery, pages, mediator) { 7 | 8 | return $.extend(true, {}, pages, { 9 | config: { 10 | el: '[data-component="back-button"]', 11 | hiddenClass: 'is-hidden' 12 | }, 13 | 14 | init: function () { 15 | this.prepare(); 16 | this.bind(); 17 | }, 18 | 19 | prepare: function () { 20 | this.elements = {}; 21 | this.elements.$el = $(this.config.el); 22 | }, 23 | 24 | bind: function () { 25 | this.elements.$el.on('click', $.proxy(this.onClick, this)); 26 | mediator.subscribe('game:start', $.proxy(this.onGameStart, this)); 27 | }, 28 | 29 | onGameStart: function () { 30 | this.show(); 31 | }, 32 | 33 | hide: function () { 34 | this.elements.$el.addClass(this.config.hiddenClass); 35 | }, 36 | 37 | show: function () { 38 | this.elements.$el.removeClass(this.config.hiddenClass); 39 | }, 40 | 41 | onClick: function (event) { 42 | this.changePage('initial').done($.proxy(function () { 43 | mediator.publish('game:abort'); 44 | this.hide(); 45 | }, this)); 46 | 47 | event.preventDefault(); 48 | }, 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /src/sass/shared/_animations.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // animations 3 | // *** 4 | 5 | // move to left 6 | @keyframes move-to-left 7 | to 8 | transform: translateX(-100%) 9 | 10 | // move to right 11 | @keyframes move-to-right 12 | to 13 | transform: translateX(100%) 14 | 15 | // move from left 16 | @keyframes move-from-left 17 | from 18 | transform: translateX(-100%) 19 | to 20 | transform: translateX(0) 21 | 22 | // move from right 23 | @keyframes move-from-right 24 | from 25 | transform: translateX(100%) 26 | to 27 | transform: translateX(0) 28 | 29 | // slide in fade 30 | @keyframes slide-in-fade 31 | 0% 32 | transform: translate3d(0, 100%, 0) 33 | opacity: 0 34 | visibility: visible 35 | 100% 36 | opacity: 1 37 | transform: translate3d(0, 0, 0) 38 | 39 | // blink 40 | @keyframes blink 41 | from 42 | opacity: 0 43 | to 44 | opacity: 1 45 | 46 | // in grow 47 | @keyframes in-grow 48 | from 49 | opacity: 0 50 | transform: scale(5) 51 | to 52 | opacity: 1 53 | transform: scale(1) 54 | 55 | // zoom out 56 | @keyframes zoom-out 57 | 0% 58 | opacity: 1 59 | 60 | 50% 61 | opacity: 0 62 | transform: scale3d(.3, .3, .3) 63 | 64 | 100% 65 | opacity: 0 66 | transform: translateX(-100%) 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-match", 3 | "version": "0.0.1", 4 | "description": "A simple memory match game created with javascript.", 5 | "main": "memory-match.js", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/weslleyaraujo/memory-match.git" 10 | }, 11 | "keywords": [ 12 | "game", 13 | "javascript", 14 | "game", 15 | "js", 16 | "memory", 17 | "match" 18 | ], 19 | "author": "Weslley Araujo ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/weslleyaraujo/memory-match/issues" 23 | }, 24 | "homepage": "https://github.com/weslleyaraujo/memory-match", 25 | "devDependencies": { 26 | "autoprefixer-core": "^5.2.0", 27 | "csswring": "^3.0.5", 28 | "grunt": "^0.4.5", 29 | "grunt-contrib-connect": "^0.10.1", 30 | "grunt-contrib-copy": "^0.8.0", 31 | "grunt-contrib-htmlmin": "^0.4.0", 32 | "grunt-contrib-jshint": "^0.11.2", 33 | "grunt-contrib-requirejs": "^0.4.4", 34 | "grunt-contrib-uglify": "^0.9.1", 35 | "grunt-contrib-watch": "^0.6.1", 36 | "grunt-ejs-render": "^0.2.7", 37 | "grunt-gh-pages": "^0.10.0", 38 | "grunt-postcss": "^0.5.1", 39 | "grunt-replace": "^0.9.2", 40 | "grunt-sass": "^1.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/fonts/icomoon/stylesheet.scss: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // freakyregular font 3 | // *** 4 | 5 | $source-location: '/src/fonts/icomoon'; 6 | 7 | @font-face { 8 | font-family: 'icomoon'; 9 | src:url('#{$source-location}/icomoon.eot?-a3lqno'); 10 | src:url('#{$source-location}/icomoon.eot?#iefix-a3lqno') format('embedded-opentype'), 11 | url('#{$source-location}/icomoon.ttf?-a3lqno') format('truetype'), 12 | url('#{$source-location}/icomoon.woff?-a3lqno') format('woff'), 13 | url('#{$source-location}/icomoon.svg?-a3lqno#icomoon') format('svg'); 14 | font-weight: normal; 15 | font-style: normal; 16 | } 17 | 18 | [class^="ui-icon-"], [class*=" ui-icon-"] { 19 | font-family: 'icomoon'; 20 | speak: none; 21 | font-style: normal; 22 | font-weight: normal; 23 | font-variant: normal; 24 | text-transform: none; 25 | line-height: 1; 26 | 27 | /* Better Font Rendering =========== */ 28 | -webkit-font-smoothing: antialiased; 29 | -moz-osx-font-smoothing: grayscale; 30 | } 31 | .ui-icon-heart:before { 32 | content: "\e9da"; 33 | } 34 | .ui-icon-happy:before { 35 | content: "\e9df"; 36 | } 37 | .ui-icon-circle-down:before { 38 | content: "\ea43"; 39 | } 40 | .ui-icon-circle-left:before { 41 | content: "\ea44"; 42 | } 43 | .ui-icon-facebook:before { 44 | content: "\ea8c"; 45 | } 46 | .ui-icon-twitter:before { 47 | content: "\ea91"; 48 | } 49 | .ui-icon-github4:before { 50 | content: "\eab4"; 51 | } 52 | -------------------------------------------------------------------------------- /src/js/lib/easter-egg.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'shared/mediator', 4 | 'config/animation-end', 5 | ], function (jquery, mediator, animationEnd) { 6 | 7 | return { 8 | config: { 9 | el: '[data-component="easter-egg"]', 10 | favicon: { 11 | el: 'link[rel="icon"]', 12 | data: 'favicon-easter-egg.png' 13 | }, 14 | bodyActiveClass: 'penis-mode' 15 | }, 16 | 17 | init: function () { 18 | this.prepare(); 19 | this.bind(); 20 | }, 21 | 22 | prepare: function () { 23 | this.elements = {}; 24 | this.elements.$el = $(this.config.el); 25 | this.elements.$favicon = $(this.config.favicon.el); 26 | this.elements.$body = $('body'); 27 | }, 28 | 29 | bind: function () { 30 | this.elements.$el.find('.ui-easter-egg-image').one(animationEnd, $.proxy(this.onAnimationEnd, this)); 31 | mediator.subscribe('easteregg:show', this.onShow, this); 32 | }, 33 | 34 | onShow: function () { 35 | this.setFavicon(); 36 | this.elements.$el.addClass('is-visible'); 37 | this.elements.$body.addClass(this.config.bodyActiveClass); 38 | }, 39 | 40 | onAnimationEnd: function () { 41 | this.elements.$el.addClass('is-done'); 42 | }, 43 | 44 | setFavicon: function () { 45 | var icon = this.elements.$favicon.data('hostname') + this.config.favicon.data; 46 | this.elements.$favicon.attr('href', icon); 47 | }, 48 | }; 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /src/js/lib/card.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'shared/create-element', 3 | 'shared/get-random-char', 4 | 'shared/mediator', 5 | 6 | ], function(createElement, getRandomChar, mediator) { 7 | 8 | function Card(options) { 9 | this.options = options; 10 | this.prepare(); 11 | this.bind(); 12 | } 13 | 14 | Card.prototype.prepare = function () { 15 | this.name = this.options.name; 16 | this.id = getRandomChar(10) + this.name; 17 | 18 | this.elementsHandler(); 19 | }; 20 | 21 | Card.prototype.bind = function () { 22 | this.elements.$el.on('click', $.proxy(this.onClick, this)); 23 | }; 24 | 25 | Card.prototype.onClick = function (event) { 26 | event.preventDefault(); 27 | var $target = $(event.currentTarget); 28 | 29 | if($target.hasClass('is-matched')){ 30 | return; 31 | } 32 | 33 | $target.addClass('is-active'); 34 | this.afterClick(); 35 | }; 36 | 37 | Card.prototype.afterClick = function () { 38 | mediator.publish('card:click', { 39 | id: this.id, 40 | name: this.name 41 | }); 42 | }; 43 | 44 | Card.prototype.elementsHandler = function () { 45 | this.elements = {}; 46 | this.elements.$el = createElement('td', { 47 | id: this.id 48 | }); 49 | 50 | this.elements.$back = createElement('figure', { 51 | class: 'is-back ui-inputs', 52 | text: this.name 53 | }); 54 | this.elements.$front = createElement('figure', { 55 | class: 'is-front ui-inputs' 56 | }); 57 | 58 | // append into main element 59 | this.elements.$el.append(this.elements.$back, this.elements.$front); 60 | 61 | }; 62 | 63 | return Card; 64 | 65 | }); 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory Match 2 | 3 | A fully functional memory match game crafted with javascript + html + css 💪 4 | 5 | > You can play it and check it out here: [weslleyaraujo.github.io/memory-match](http://weslleyaraujo.github.io/memory-match) 6 | 7 | ## Installing 8 | 9 | It depends on npm, grunt and bower, and to install just run the following commands: 10 | 11 | ``` 12 | npm install 13 | bower install 14 | ``` 15 | 16 | ## Starting 17 | 18 | You can run locally using the development grunt task: 19 | 20 | ``` 21 | grunt develop 22 | ``` 23 | 24 | It will generate the necessary assets and run up a server into `http://localhost:8180` 25 | 26 | ## Building 27 | 28 | You can build it using the `build` task which requires the `--config` parameter (the config file for production/development) 29 | 30 | ``` 31 | grunt build --config=production 32 | ``` 33 | 34 | It will generate the project into a `dist` directory 35 | 36 | 37 | ## Deploy 38 | 39 | The deploy task runs the build and upload the `dist` dir into the `gh-pages` 40 | 41 | ``` 42 | grunt deploy --config=production 43 | ``` 44 | 45 | 46 | ### Contributing 47 | 48 | Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specific branch and request to pull into master! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process. 49 | I'm following [this](https://github.com/netshoes/styleguide/tree/master/scm) styleguide to name branches and pull requests, make sure you follow this too 😁 50 | 51 | ### License 52 | 53 | Memory Match's is licensed under the [Beerware License](https://en.wikipedia.org/wiki/Beerware). 54 | -------------------------------------------------------------------------------- /src/sass/components/_board.sass: -------------------------------------------------------------------------------- 1 | // ****************** 2 | // ui-board 3 | // 4 | // ...
5 | // *** 6 | 7 | $module-name: 'ui-board' 8 | $module-item-height: 50px 9 | $module-font-size-icon: 50px 10 | $module-font-size-mobile-icon: 43px 11 | $module-max-width: 600px 12 | 13 | .#{$module-name} 14 | margin: 0 auto 15 | width: 100% 16 | max-width: $module-max-width 17 | perspective: 100% 18 | height: 80% 19 | opacity: 0 20 | 21 | // modifier 22 | &.#{$module-name}--slide-in-fade 23 | animation: slide-in-fade .6s ease both 24 | td 25 | height: $module-item-height 26 | line-height: $module-item-height 27 | padding: 15px 20px 28 | position: relative 29 | text-align: center 30 | width: 30px 31 | transform-style: preserve-3d 32 | @extend %default-transition 33 | 34 | // states 35 | &.is-matched, &.is-flipped 36 | transform: rotateY(180deg) 37 | 38 | &.is-matched 39 | figure 40 | background-color: map-get($colors, 4) !important 41 | color: map-get($colors, 1) !important 42 | 43 | &.is-active 44 | figure 45 | background-color: map-get($colors, 3) 46 | color: #FFF 47 | 48 | &.is-previewed, &.is-revealed 49 | figure 50 | color: inherit 51 | 52 | &.is-revealed 53 | figure 54 | background-color: #E1E1E1 55 | 56 | figure 57 | bottom: 0 58 | display: block 59 | height: 100% 60 | left: 0 61 | margin: auto 62 | position: absolute 63 | right: 0 64 | top: 0 65 | width: 100% 66 | padding: 0 67 | backface-visibility: hidden 68 | font-family: $font-family-icon 69 | font-size: $module-font-size-icon 70 | @extend %default-transition 71 | +media('mobile') 72 | font-size: $module-font-size-mobile-icon 73 | 74 | // states 75 | &.is-back 76 | background-color: #DEDEDE 77 | transform: rotateY(180deg) 78 | 79 | // theme-penis 80 | .#{$theme-special} 81 | .#{$module-name} 82 | figure 83 | font-family: $font-family-special 84 | font-size: 40px 85 | -------------------------------------------------------------------------------- /src/js/lib/next-level.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lib/pages', 3 | 4 | 'config/levels', 5 | 'shared/mediator' 6 | 7 | ], function(pages, levels, mediator) { 8 | 9 | return $.extend(true, {}, pages, { 10 | config: { 11 | el: '[data-component="next-level"]', 12 | labels: { 13 | next: 'Play next level!', 14 | maximum: 'Play again!' 15 | } 16 | }, 17 | 18 | init: function () { 19 | this.prepare(); 20 | this.bind(); 21 | }, 22 | 23 | prepare: function () { 24 | this.elements = {}; 25 | this.elements.$el = $(this.config.el); 26 | }, 27 | 28 | bind: function () { 29 | this.elements.$el.on('click', $.proxy(this.onClick, this)); 30 | mediator.subscribe('game:win', $.proxy(this.onGameWin, this)); 31 | mediator.subscribe('game:start', $.proxy(this.onGameStart, this)); 32 | }, 33 | 34 | setLabel: function (name) { 35 | this.elements.$el.text(name).addClass('is-active'); 36 | }, 37 | 38 | getLastLevel: function () { 39 | var size = levels.length; 40 | return levels[size - 1]; 41 | }, 42 | 43 | onClick: function(event) { 44 | mediator.publish('game:abort'); 45 | 46 | this.changePage('game').done($.proxy(function() { 47 | mediator.publish('game:start', this.next ? this.next : this.getNextLevel()); 48 | }, this)); 49 | 50 | event.preventDefault(); 51 | }, 52 | 53 | getNextLevel: function() { 54 | return levels.reduce($.proxy(function (actual, next) { 55 | return ((this.options.index + 1) === next.index) ? next : actual; 56 | }, this), false); 57 | }, 58 | 59 | onGameStart: function () { 60 | this.hideText(); 61 | }, 62 | 63 | hideText: function () { 64 | this.elements.$el.removeClass('is-active'); 65 | }, 66 | 67 | onGameWin: function (data) { 68 | this.options = data; 69 | this.next = this.getNextLevel(); 70 | 71 | if (this.next) { 72 | this.setLabel(this.config.labels.next); 73 | return; 74 | } 75 | 76 | this.setLabel(this.config.labels.maximum); 77 | }, 78 | 79 | }); 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /src/js/lib/pages.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'config/animation-end', 4 | 'shared/once', 5 | ], function ($, animationEnd, once) { 6 | 7 | return $.extend({}, { 8 | 9 | config: { 10 | visibleClass: 'is-visible', 11 | toLeftClass: 'ui-page--move-to-left', 12 | toRightClass: 'ui-page--move-to-right', 13 | 14 | fromLeftClass: 'ui-page--move-from-left', 15 | fromRightClass: 'ui-page--move-from-right' 16 | }, 17 | 18 | getCurrentPage: function (data, filter) { 19 | return $('[data-page-name].is-visible'); 20 | }, 21 | 22 | getPage: function (name) { 23 | return $('[data-page-name="' + name + '"]'); 24 | }, 25 | 26 | hideCurrentPage: function (name) { 27 | var animation = this.isPrevious() ? this.config.toLeftClass : this.config.toRightClass; 28 | this.$currentPage.addClass(animation); 29 | }, 30 | 31 | showNextPage: function (callback) { 32 | var animation = this.isPrevious() ? this.config.fromRightClass : this.config.fromLeftClass; 33 | callback = once(callback, this); 34 | 35 | this.$nextPage 36 | .addClass(animation) 37 | .addClass(this.config.visibleClass) 38 | .one(animationEnd, callback); 39 | }, 40 | 41 | isPrevious: function() { 42 | return this.$currentPage.data('page-index') < this.$nextPage.data('page-index'); 43 | }, 44 | 45 | changePage: function(name) { 46 | this.deferred = $.Deferred(); 47 | this.$nextPage = this.getPage(name); 48 | this.$currentPage = this.getCurrentPage(); 49 | 50 | this.hideCurrentPage(); 51 | 52 | this.showNextPage(function () { 53 | this.removePageClasses(this.$nextPage); 54 | this.removePageClasses(this.$currentPage); 55 | this.$currentPage.removeClass(this.config.visibleClass); 56 | this.deferred.resolve(); 57 | }); 58 | 59 | return this.deferred.promise(); 60 | }, 61 | 62 | removePageClasses: function($target) { 63 | $target 64 | .removeClass(this.config.toLeftClass) 65 | .removeClass(this.config.toRightClass) 66 | .removeClass(this.config.fromLeftClass) 67 | .removeClass(this.config.fromRightClass); 68 | } 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /src/fonts/icomoon/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Memory Match game crafted with Javascript! 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 |
16 |
memory-macth
17 |
memory-match
18 |
19 | 20 | 21 | <% include partials/easter-egg %> 22 | 23 |
24 | 25 |
26 |
27 | 28 |
29 | 32 |
33 | 34 |
35 |
36 |

Memory Match

37 |
38 |

Select a level to start:

39 |
40 |
41 | 48 | 49 |
50 | 51 |
52 |
53 | 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 |
62 |
63 | 64 |
65 |
66 |

YOU WIN!

67 |

Share this game with your friends!

68 | 69 | 86 | 87 | 88 |
89 |
90 | 91 | 95 | 96 | 98 | 99 | 100 | <% include partials/google-analytics %> 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/js/lib/game.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'konami', 4 | 'lib/card', 5 | 'lib/pages', 6 | 'lib/easter-egg', 7 | 'shared/mediator', 8 | 'shared/create-array', 9 | 'shared/get-random-char', 10 | 'shared/get-range', 11 | 'shared/create-element', 12 | 13 | ], function(jquery, konami, Card, pages, easterEgg, mediator, createArray, getRandomChar, getRange, createElement) { 14 | 15 | return $.extend(true, {}, pages, { 16 | config: { 17 | el: '[data-component="board"]', 18 | delayTime: 800, 19 | animationClass: 'ui-board--slide-in-fade' 20 | }, 21 | 22 | init: function () { 23 | this.prepare(); 24 | this.bind(); 25 | 26 | // not assigning to any variable here :( 27 | new Konami($.proxy(this.onEasterEgg)); 28 | }, 29 | 30 | prepare: function () { 31 | this.matches = 0; 32 | this.characters = []; 33 | this.clicks = {}; 34 | this.elements = {}; 35 | this.elements.$el = $(this.config.el); 36 | this.elements.$window = $(window); 37 | }, 38 | 39 | bind: function () { 40 | mediator.subscribe('game:start', this.onGameStart, this); 41 | mediator.subscribe('game:abort', this.onGameAbort, this); 42 | mediator.subscribe('card:click', this.onCardClick, this); 43 | this.elements.$window.on('resize', $.proxy(this.onWindowResize, this)); 44 | }, 45 | 46 | onWindowResize: function() { 47 | this.setLineHeight(); 48 | }, 49 | 50 | /* 51 | * FIXME: Think in a smart way to do that 52 | */ 53 | setLineHeight: function() { 54 | this.elements.$el.find('figure').each(function() { 55 | var $target = $(this); 56 | var height = $target.height(); 57 | var width = $target.width(); 58 | var fontSize; 59 | 60 | $target.css('lineHeight', $target.height() + 'px'); 61 | 62 | if(height > width) { 63 | fontSize = height - (height * 0.30); 64 | $target.css('fontSize', fontSize + 'px'); 65 | return; 66 | } 67 | 68 | fontSize = width - (width * 0.50); 69 | $target.css('fontSize', fontSize + 'px'); 70 | }); 71 | }, 72 | 73 | isEqualClick: function (data) { 74 | try { 75 | return this.clicks.current.id === data.id; 76 | } catch(e) { 77 | return false; 78 | } 79 | }, 80 | 81 | onGameAbort: function () { 82 | this.abort(); 83 | }, 84 | 85 | onEasterEgg: function () { 86 | mediator.publish('easteregg:show'); 87 | }, 88 | 89 | onCardClick: function(data) { 90 | if (this.isEqualClick(data)) { 91 | this.clearClicks(); 92 | this.unflipCards(); 93 | 94 | return; 95 | } 96 | 97 | if(!this.isFirstClick()) { 98 | this.recordClick('current', data); 99 | this.reveal(data); 100 | return; 101 | } 102 | 103 | this.reveal(data); 104 | this.recordClick('last', data); 105 | this.onBothClicks(); 106 | }, 107 | 108 | reveal: function(data) { 109 | this.elements.$el.find('#' + data.id).addClass('is-flipped'); 110 | }, 111 | 112 | onBothClicks: function () { 113 | mediator.publish('locker:active'); 114 | 115 | setTimeout($.proxy(function () { 116 | if(this.isMatch()) { 117 | this.afterMatched(); 118 | return; 119 | } 120 | 121 | this.clearClicks(); 122 | this.unflipCards(); 123 | this.removeLocker(); 124 | }, this), this.config.delayTime); 125 | }, 126 | 127 | isFirstClick: function () { 128 | return !!this.clicks.current; 129 | }, 130 | 131 | recordClick: function (key, data) { 132 | this.clicks[key] = data; 133 | }, 134 | 135 | clearClicks: function () { 136 | this.clicks = {}; 137 | }, 138 | 139 | unflipCards: function () { 140 | this.elements.$el.find('td.is-flipped, td.is-active') 141 | .removeClass('is-flipped is-active'); 142 | }, 143 | 144 | addMatch: function () { 145 | this.matches++; 146 | }, 147 | 148 | afterMatched: function () { 149 | this.addMatch(); 150 | this.flagMatchs(); 151 | this.clearClicks(); 152 | this.removeLocker(); 153 | 154 | if(this.isWinner()) { 155 | this.onWinGame(); 156 | } 157 | }, 158 | 159 | removeLocker: function() { 160 | mediator.publish('locker:remove'); 161 | }, 162 | 163 | onWinGame: function() { 164 | this.changePage('win').done($.proxy(function () { 165 | mediator.publish('game:win', { 166 | index: this.options.index 167 | }); 168 | }, this)); 169 | }, 170 | 171 | isWinner: function() { 172 | return this.matches === (this.size / 2); 173 | }, 174 | 175 | flagMatchs: function () { 176 | this.elements.$el.find(this.getMatchSelector()).addClass('is-matched'); 177 | }, 178 | 179 | getMatchSelector: function () { 180 | return '#' + this.clicks.last.id + ' ,#' + this.clicks.current.id; 181 | }, 182 | 183 | isMatch: function () { 184 | return this.clicks.last.name === this.clicks.current.name; 185 | }, 186 | 187 | onGameStart: function (data) { 188 | this.options = data; 189 | this.start(); 190 | }, 191 | 192 | abort: function () { 193 | this.elements.$el 194 | .empty() 195 | .removeClass(this.config.animationClass); 196 | 197 | this.prepare(); 198 | }, 199 | 200 | start: function (data) { 201 | this.size = (this.options.size * this.options.size); 202 | this.generateCharacters(); 203 | this.generateBoard(); 204 | 205 | this.render(); 206 | this.setLineHeight(); 207 | }, 208 | 209 | generateBoard: function () { 210 | this.board = createArray(this.options.size).map($.proxy(this.createLine, this, this.options.size)); 211 | }, 212 | 213 | generateCharacters: function () { 214 | this.cards = createArray(this.size / 2).map($.proxy(function () { 215 | return { 216 | times: 0, 217 | name: this.getUniqueChar() 218 | }; 219 | }, this)); 220 | }, 221 | 222 | getUniqueChar: function () { 223 | var character = getRandomChar(); 224 | 225 | while (this.characters.indexOf(character) >= 0) { 226 | character = getRandomChar(); 227 | } 228 | 229 | this.characters.push(character); 230 | 231 | return character; 232 | }, 233 | 234 | createLine: function (size) { 235 | var $line = createElement('tr'); 236 | 237 | createArray(size).map($.proxy(function () { 238 | this.clearCardList(); 239 | this.actual = getRange(0, this.cards.length); 240 | this.increaseCardTimes(); 241 | $line.append(this.getCardElement()); 242 | }, this)); 243 | 244 | return $line; 245 | }, 246 | 247 | clearCardList: function (size) { 248 | this.cards = this.cards.filter(function (card) { 249 | return card.times < 2; 250 | }); 251 | }, 252 | 253 | render: function (size) { 254 | this.elements.$el.html(this.board).addClass(this.config.animationClass); 255 | }, 256 | 257 | increaseCardTimes: function () { 258 | this.cards[this.actual].times++; 259 | }, 260 | 261 | getActualCardName: function () { 262 | return this.cards[this.actual].name; 263 | }, 264 | 265 | getCardElement: function () { 266 | return new Card({ 267 | name: this.getActualCardName() 268 | }).elements.$el; 269 | } 270 | 271 | }); 272 | 273 | }); 274 | -------------------------------------------------------------------------------- /src/fonts/icomoon/selection.json: -------------------------------------------------------------------------------- 1 | { 2 | "IcoMoonType": "selection", 3 | "icons": [ 4 | { 5 | "icon": { 6 | "paths": [ 7 | "M755.188 64c-107.63 0-200.258 87.554-243.164 179-42.938-91.444-135.578-179-243.216-179-148.382 0-268.808 120.44-268.808 268.832 0 301.846 304.5 380.994 512.022 679.418 196.154-296.576 511.978-387.206 511.978-679.418 0-148.392-120.43-268.832-268.812-268.832z" 8 | ], 9 | "attrs": [], 10 | "tags": [ 11 | "heart", 12 | "like", 13 | "love", 14 | "favorite" 15 | ], 16 | "defaultCode": 58119, 17 | "grid": 16 18 | }, 19 | "attrs": [], 20 | "properties": { 21 | "id": 871, 22 | "order": 8, 23 | "prevSize": 32, 24 | "code": 59866, 25 | "ligatures": "heart, like", 26 | "name": "heart" 27 | }, 28 | "setIdx": 0, 29 | "setId": 0, 30 | "iconIdx": 218 31 | }, 32 | { 33 | "icon": { 34 | "paths": [ 35 | "M512 1024c282.77 0 512-229.23 512-512s-229.23-512-512-512-512 229.23-512 512 229.23 512 512 512zM512 96c229.75 0 416 186.25 416 416s-186.25 416-416 416-416-186.25-416-416 186.25-416 416-416zM512 598.76c115.95 0 226.23-30.806 320-84.92-14.574 178.438-153.128 318.16-320 318.16-166.868 0-305.422-139.872-320-318.304 93.77 54.112 204.050 85.064 320 85.064zM256 352c0-53.019 28.654-96 64-96s64 42.981 64 96c0 53.019-28.654 96-64 96s-64-42.981-64-96zM640 352c0-53.019 28.654-96 64-96s64 42.981 64 96c0 53.019-28.654 96-64 96s-64-42.981-64-96z" 36 | ], 37 | "attrs": [], 38 | "tags": [ 39 | "happy", 40 | "emoticon", 41 | "smiley", 42 | "face" 43 | ], 44 | "defaultCode": 58142, 45 | "grid": 16 46 | }, 47 | "attrs": [], 48 | "properties": { 49 | "id": 894, 50 | "order": 9, 51 | "prevSize": 32, 52 | "code": 59871, 53 | "ligatures": "happy, emoticon", 54 | "name": "happy" 55 | }, 56 | "setIdx": 0, 57 | "setId": 0, 58 | "iconIdx": 223 59 | }, 60 | { 61 | "icon": { 62 | "paths": [ 63 | "M1024 512c0-282.77-229.23-512-512-512s-512 229.23-512 512 229.23 512 512 512 512-229.23 512-512zM96 512c0-229.75 186.25-416 416-416s416 186.25 416 416-186.25 416-416 416-416-186.25-416-416z", 64 | "M317.256 354.744l-90.512 90.512 285.256 285.254 285.254-285.256-90.508-90.508-194.746 194.744z" 65 | ], 66 | "attrs": [], 67 | "tags": [ 68 | "circle-down", 69 | "down", 70 | "circle-bottom", 71 | "arrow" 72 | ], 73 | "defaultCode": 58883, 74 | "grid": 16 75 | }, 76 | "attrs": [], 77 | "properties": { 78 | "order": 4, 79 | "id": 1394, 80 | "prevSize": 32, 81 | "code": 59971, 82 | "ligatures": "circle-down, down3", 83 | "name": "circle-down" 84 | }, 85 | "setIdx": 0, 86 | "setId": 0, 87 | "iconIdx": 323 88 | }, 89 | { 90 | "icon": { 91 | "paths": [ 92 | "M512 1024c282.77 0 512-229.23 512-512s-229.23-512-512-512-512 229.23-512 512 229.23 512 512 512zM512 96c229.75 0 416 186.25 416 416s-186.25 416-416 416-416-186.25-416-416 186.25-416 416-416z", 93 | "M669.256 317.256l-90.512-90.512-285.254 285.256 285.256 285.254 90.508-90.508-194.744-194.746z" 94 | ], 95 | "attrs": [], 96 | "tags": [ 97 | "circle-left", 98 | "left", 99 | "circle-previous", 100 | "arrow" 101 | ], 102 | "defaultCode": 58883, 103 | "grid": 16 104 | }, 105 | "attrs": [], 106 | "properties": { 107 | "id": 5, 108 | "order": 5, 109 | "prevSize": 32, 110 | "code": 59972, 111 | "ligatures": "circle-left, left5", 112 | "name": "circle-left" 113 | }, 114 | "setIdx": 0, 115 | "setId": 0, 116 | "iconIdx": 324 117 | }, 118 | { 119 | "icon": { 120 | "paths": [ 121 | "M672 192c-88.366 0-160 71.634-160 160v96h-128v128h128v448h128v-448h144l32-128h-176v-96c0-17.672 14.326-32 32-32h160v-128h-160z" 122 | ], 123 | "attrs": [], 124 | "tags": [ 125 | "facebook", 126 | "brand", 127 | "social" 128 | ], 129 | "defaultCode": 58520, 130 | "grid": 16 131 | }, 132 | "attrs": [], 133 | "properties": { 134 | "id": 1297, 135 | "order": 11, 136 | "prevSize": 32, 137 | "code": 60044, 138 | "ligatures": "facebook, brand6", 139 | "name": "facebook" 140 | }, 141 | "setIdx": 0, 142 | "setId": 0, 143 | "iconIdx": 396 144 | }, 145 | { 146 | "icon": { 147 | "paths": [ 148 | "M1024 194.418c-37.676 16.708-78.164 28.002-120.66 33.080 43.372-26 76.686-67.17 92.372-116.23-40.596 24.078-85.556 41.56-133.41 50.98-38.32-40.83-92.922-66.34-153.346-66.34-116.022 0-210.088 94.058-210.088 210.078 0 16.466 1.858 32.5 5.44 47.878-174.6-8.764-329.402-92.4-433.018-219.506-18.084 31.028-28.446 67.116-28.446 105.618 0 72.888 37.088 137.192 93.46 174.866-34.438-1.092-66.832-10.542-95.154-26.278-0.020 0.876-0.020 1.756-0.020 2.642 0 101.788 72.418 186.696 168.522 206-17.626 4.8-36.188 7.372-55.348 7.372-13.538 0-26.698-1.32-39.528-3.772 26.736 83.46 104.32 144.206 196.252 145.896-71.9 56.35-162.486 89.934-260.916 89.934-16.958 0-33.68-0.994-50.116-2.94 92.972 59.61 203.402 94.394 322.042 94.394 386.422 0 597.736-320.124 597.736-597.744 0-9.108-0.206-18.168-0.61-27.18 41.056-29.62 76.672-66.62 104.836-108.748z" 149 | ], 150 | "attrs": [], 151 | "tags": [ 152 | "twitter", 153 | "brand", 154 | "tweet", 155 | "social" 156 | ], 157 | "defaultCode": 58525, 158 | "grid": 16 159 | }, 160 | "attrs": [], 161 | "properties": { 162 | "id": 1302, 163 | "order": 10, 164 | "prevSize": 32, 165 | "code": 60049, 166 | "ligatures": "twitter, brand11", 167 | "name": "twitter" 168 | }, 169 | "setIdx": 0, 170 | "setId": 0, 171 | "iconIdx": 401 172 | }, 173 | { 174 | "icon": { 175 | "paths": [ 176 | "M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512 512-229.23 512-512-229.23-512-512-512zM816.056 816.056c-39.518 39.516-85.512 70.532-136.708 92.186-13.006 5.5-26.214 10.328-39.6 14.492v-76.734c0-40.334-13.834-70-41.5-89 17.334-1.666 33.25-4 47.75-7s29.834-7.334 46-13 30.666-12.416 43.5-20.25 25.166-18 37-30.5 21.75-26.666 29.75-42.5 14.334-34.834 19-57 7-46.584 7-73.25c0-51.666-16.834-95.666-50.5-132 15.334-40 13.666-83.5-5-130.5l-12.5-1.5c-8.666-1-24.25 2.666-46.75 11s-47.75 22-75.75 41c-39.666-11-80.834-16.5-123.5-16.5-43 0-84 5.5-123 16.5-17.666-12-34.416-21.916-50.25-29.75s-28.5-13.166-38-16-18.334-4.584-26.5-5.25-13.416-0.834-15.75-0.5-4 0.666-5 1c-18.666 47.334-20.334 90.834-5 130.5-33.666 36.334-50.5 80.334-50.5 132 0 26.666 2.334 51.084 7 73.25s11 41.166 19 57 17.916 30 29.75 42.5 24.166 22.666 37 30.5 27.334 14.584 43.5 20.25 31.5 10 46 13 30.416 5.334 47.75 7c-27.334 18.666-41 48.334-41 89v78.23c-15.098-4.494-29.98-9.804-44.6-15.988-51.194-21.654-97.188-52.67-136.706-92.186-39.516-39.518-70.534-85.512-92.186-136.708-22.398-52.958-33.756-109.262-33.756-167.348s11.358-114.39 33.758-167.35c21.654-51.194 52.67-97.188 92.186-136.706s85.512-70.534 136.706-92.186c52.96-22.4 109.264-33.758 167.35-33.758s114.39 11.358 167.35 33.758c51.196 21.654 97.19 52.67 136.708 92.186 39.516 39.516 70.532 85.512 92.186 136.706 22.398 52.96 33.756 109.264 33.756 167.35s-11.358 114.39-33.758 167.35c-21.654 51.194-52.67 97.19-92.186 136.706z" 177 | ], 178 | "attrs": [], 179 | "tags": [ 180 | "github", 181 | "brand", 182 | "octacat", 183 | "social" 184 | ], 185 | "defaultCode": 58554, 186 | "grid": 16 187 | }, 188 | "attrs": [], 189 | "properties": { 190 | "id": 1335, 191 | "order": 1, 192 | "prevSize": 32, 193 | "code": 60084, 194 | "ligatures": "github4, brand43", 195 | "name": "github4" 196 | }, 197 | "setIdx": 0, 198 | "setId": 0, 199 | "iconIdx": 436 200 | } 201 | ], 202 | "height": 1024, 203 | "metadata": { 204 | "name": "icomoon" 205 | }, 206 | "preferences": { 207 | "showGlyphs": true, 208 | "showQuickUse": true, 209 | "showQuickUse2": true, 210 | "showSVGs": true, 211 | "fontPref": { 212 | "prefix": "ui-icon-", 213 | "metadata": { 214 | "fontFamily": "icomoon", 215 | "majorVersion": 1, 216 | "minorVersion": 0 217 | }, 218 | "metrics": { 219 | "emSize": 1024, 220 | "baseline": 6.25, 221 | "whitespace": 50 222 | }, 223 | "showSelector": false, 224 | "showMetrics": false 225 | }, 226 | "imagePref": { 227 | "prefix": "icon-", 228 | "png": true, 229 | "useClassSelector": true, 230 | "color": 4473924, 231 | "bgColor": 16777215, 232 | "classSelector": ".icon" 233 | }, 234 | "historySize": 100, 235 | "showCodes": false 236 | } 237 | } -------------------------------------------------------------------------------- /src/images/easter-egg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 130 | 153 | 164 | 166 | 169 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/partials/easter-egg.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 | 130 | 153 | 164 | 166 | 169 | 172 | 173 | 174 |
175 | -------------------------------------------------------------------------------- /src/fonts/penis/penis.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flaticon WebFont 6 | 7 | 8 | 207 | 208 | 209 | 210 |
211 |

212 | 213 | FLATICON 214 | Font Demo 215 |

216 |
217 | 218 |
219 |
220 | 221 |
Instructions:
222 | 223 |
    224 |
  • 225 |

    226 | 1Copy the "Fonts" files and CSS files to your website CSS folder. 227 |

  • 228 |
  • 229 |

    230 | 2Add the CSS link to your website source code on header. 231 |
    232 | <head> 233 |
    ... 234 |
    <link rel="stylesheet" type="text/css" href="your_website_domain/css_root/penis.css"> 235 |
    ... 236 |
    </head>
    237 |

  • 238 | 239 |
  • 240 |

    241 | 3Use the icon class on "display:inline" elements: 242 |
    243 | Use example: <i class="flaticon-airplane49"></i> or <span class="flaticon-airplane49"></span> 244 |

  • 245 |
246 |
247 | 248 |
249 | 250 |
251 |
252 |
.flaticon-.notdef
Author:
253 |
254 |
.flaticon-exclam
Author:
255 |
256 |
.flaticon-quotedbl
Author:
257 |
258 |
.flaticon-numbersign
Author:
259 |
260 |
.flaticon-dollar
Author: Freepik
261 |
262 |
.flaticon-percent
Author:
263 |
264 |
.flaticon-ampersand
Author:
265 |
266 |
.flaticon-quotesingle
Author:
267 |
268 |
.flaticon-parenleft
Author:
269 |
270 |
.flaticon-parenright
Author:
271 |
272 |
.flaticon-asterisk
273 |
274 |
.flaticon-plus
275 |
276 |
.flaticon-comma
Author:
277 |
278 |
.flaticon-hyphen
Author:
279 |
280 |
.flaticon-period
Author:
281 |
282 |
.flaticon-slash
Author: Freepik
283 |
284 |
.flaticon-zero
Author: Freepik
285 |
286 |
.flaticon-one
Author:
287 |
288 |
.flaticon-two
289 |
290 |
.flaticon-three
291 |
292 |
.flaticon-four
Author: Freepik
293 |
294 |
.flaticon-five
Author: Freepik
295 |
296 |
.flaticon-six
Author:
297 |
298 |
.flaticon-seven
Author:
299 |
300 |
.flaticon-eight
Author: Freepik
301 |
302 |
.flaticon-nine
Author:
303 |
304 |
.flaticon-colon
Author: Vecree
305 |
306 |
.flaticon-semicolon
Author:
307 |
308 |
.flaticon-less
Author:
309 |
310 |
.flaticon-equal
Author: Icons8
311 |
312 |
.flaticon-greater
Author: Freepik
313 |
314 |
.flaticon-question
Author: Amit Jakhu
315 |
316 |
.flaticon-at
317 |
318 |
.flaticon-A
Author: Freepik
319 |
320 |
.flaticon-B
Author: Daniel Bruce
321 |
322 |
.flaticon-C
Author: Freepik
323 |
324 |
.flaticon-D
Author: Freepik
325 |
326 |
.flaticon-E
Author: Freepik
327 |
328 |
.flaticon-F
Author: Freepik
329 |
330 |
.flaticon-G
Author: Freepik
331 |
332 |
.flaticon-H
Author:
333 |
334 |
.flaticon-I
Author: Freepik
335 |
336 |
.flaticon-J
Author:
337 |
338 |
.flaticon-K
Author:
339 |
340 |
.flaticon-L
Author:
341 |
342 |
.flaticon-M
Author: Freepik
343 |
344 |
.flaticon-N
Author:
345 |
346 |
.flaticon-O
Author:
347 |
348 |
.flaticon-P
Author:
349 |
350 |
.flaticon-Q
Author:
351 |
352 |
.flaticon-R
Author:
353 |
354 |
.flaticon-S
Author:
355 |
356 |
.flaticon-T
Author: Freepik
357 |
358 |
.flaticon-U
Author: Icomoon
359 |
360 |
.flaticon-V
Author:
361 |
362 |
.flaticon-W
Author: FadyUCF
363 |
364 |
.flaticon-X
365 |
366 |
.flaticon-Y
367 |
368 |
.flaticon-Z
Author:
369 |
370 |
.flaticon-bracketleft
Author:
371 |
372 |
.flaticon-backslash
Author:
373 |
374 |
.flaticon-bracketright
Author:
375 |
376 |
.flaticon-a
Author: Freepik
377 |
378 |
.flaticon-b
Author: Daniel Bruce
379 |
380 |
.flaticon-c
Author: Freepik
381 |
382 |
.flaticon-d
Author: Freepik
383 |
384 |
.flaticon-e
Author: Freepik
385 |
386 |
.flaticon-f
Author: Freepik
387 |
388 |
.flaticon-g
Author: Freepik
389 |
390 |
.flaticon-h
Author:
391 |
392 |
.flaticon-i
Author: Freepik
393 |
394 |
.flaticon-j
Author:
395 |
396 |
.flaticon-k
Author:
397 |
398 |
.flaticon-l
Author:
399 |
400 |
.flaticon-m
Author: Freepik
401 |
402 |
.flaticon-n
Author:
403 |
404 |
.flaticon-o
Author:
405 |
406 |
.flaticon-p
Author:
407 |
408 |
.flaticon-q
Author:
409 |
410 |
.flaticon-r
Author:
411 |
412 |
.flaticon-s
Author:
413 |
414 |
.flaticon-t
Author: Freepik
415 |
416 |
.flaticon-u
Author: Icomoon
417 |
418 |
.flaticon-v
Author:
419 |
420 |
.flaticon-w
Author: FadyUCF
421 |
422 |
.flaticon-x
423 |
424 |
.flaticon-y
425 |
426 |
.flaticon-z
Author:
427 |
428 |
.flaticon-braceleft
Author:
429 |
430 |
.flaticon-bar
Author: Freepik
431 |
432 |
.flaticon-braceright
Author:
433 |
434 |
.flaticon-aring
Author:
435 |
436 |
.flaticon-ocircumflex
Author:
437 |
438 |
.flaticon-cent
Author:
439 |
440 |
.flaticon-sterling
Author: Freepik
441 |
442 |
.flaticon-section
Author:
443 |
444 |
.flaticon-bullet
Author: Freepik
445 |
446 |
.flaticon-germandbls
Author:
447 | 448 |
.flaticon-copyright
Author:
449 |
450 |
.flaticon-AE
Author: FadyUCF
451 |
452 |
.flaticon-partialdiff
Author:
453 | 454 |
455 | 456 |
License and attribution:
Copy the Attribution License:
458 | 459 | 461 | 462 |
463 | 464 |
465 |
Examples:
466 |

<i class="flaticon-.notdef"></i>

<i class="flaticon-exclam"></i>

<i class="flaticon-quotedbl"></i>

<i class="flaticon-numbersign"></i>

<i class="flaticon-dollar"></i>

<span class="flaticon-percent"></span>

<span class="flaticon-ampersand"></span>

<span class="flaticon-quotesingle"></span>

<span class="flaticon-parenleft"></span>

<span class="flaticon-parenright"></span>

<span class="flaticon-asterisk"></span>

<i class="flaticon-plus"></i>

<i class="flaticon-comma"></i>

<i class="flaticon-hyphen"></i>

<i class="flaticon-period"></i>

<span class="flaticon-slash"></span>

<span class="flaticon-zero"></span>

<span class="flaticon-one"></span>

<span class="flaticon-two"></span>

<span class="flaticon-three"></span>

467 |
468 | 472 | 473 | --------------------------------------------------------------------------------