├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── index.js ├── lib ├── addPasswordProtection.js └── createSections.js ├── package-lock.json ├── package.json ├── templates ├── .babelrc ├── .gitkeep ├── backend │ └── php │ │ └── static │ │ ├── index.php │ │ ├── json │ │ └── share.json │ │ └── lib │ │ └── Meta.php ├── base │ ├── .editorconfig │ ├── .eslintrc │ ├── .gitattributes │ ├── .jshintrc │ ├── COMPONENTS.md │ ├── README.md │ ├── SCRIPTS.md │ ├── STANDARDS.md │ ├── TESTS.md │ ├── config-preloader.json │ ├── config.json │ ├── gitignore │ ├── package.json │ ├── raw-assets │ │ ├── fonts │ │ │ └── .gitkeep │ │ ├── json │ │ │ ├── .gitkeep │ │ │ └── test.json │ │ ├── sounds │ │ │ ├── .gitkeep │ │ │ ├── button-click.mp3 │ │ │ ├── button-rollover.mp3 │ │ │ └── button-sprite.mp3 │ │ ├── svg │ │ │ ├── captions-off.svg │ │ │ ├── captions-on.svg │ │ │ ├── close.svg │ │ │ ├── enter-fullscreen.svg │ │ │ ├── exit-fullscreen.svg │ │ │ ├── loader.svg │ │ │ ├── muted.svg │ │ │ ├── pause.svg │ │ │ ├── play.svg │ │ │ ├── rotate.svg │ │ │ └── unmuted.svg │ │ └── videos │ │ │ ├── .gitkeep │ │ │ └── captions-test.vtt │ ├── src │ │ ├── data │ │ │ └── sounds.js │ │ └── util │ │ │ ├── audio.js │ │ │ ├── detect.js │ │ │ ├── seconds-to-minutes.js │ │ │ └── stats.js │ └── static │ │ ├── .htaccess │ │ ├── humans.txt │ │ ├── index.html │ │ └── robots.txt ├── bigwheel │ └── src │ │ ├── framework │ │ ├── index.js │ │ └── routes.js │ │ ├── index.js │ │ └── model │ │ └── index.js ├── react │ └── src │ │ ├── components │ │ ├── HamburgerButton │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ ├── MobileFullscreenVideo │ │ │ └── index.js │ │ ├── Preloader │ │ │ ├── Preloader.js │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ ├── Rotate │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ └── VideoPlayer │ │ │ ├── VideoPoster │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ │ ├── VideoTimeline │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ ├── decorators │ │ └── connectTransitionWrapper.js │ │ ├── framework │ │ └── index.js │ │ ├── index.js │ │ ├── routes │ │ └── index.js │ │ ├── sections │ │ ├── App │ │ │ ├── App.js │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ └── Landing │ │ │ ├── Landing.js │ │ │ ├── index.js │ │ │ └── style.{{css}} │ │ ├── store │ │ ├── actions │ │ │ ├── app.js │ │ │ └── preloader.js │ │ ├── index.js │ │ ├── keys.js │ │ └── reducers │ │ │ ├── app.js │ │ │ └── preloader.js │ │ └── test │ │ ├── components │ │ ├── HamburgerButton │ │ │ └── index.js │ │ ├── MobileFullscreenVideo │ │ │ └── index.js │ │ ├── Preloader │ │ │ └── index.js │ │ ├── SoundTest.js │ │ └── TestPage │ │ │ └── index.js │ │ └── index.js ├── scripts │ ├── clean.js │ ├── config.js │ ├── copy.js │ ├── dev.js │ ├── facebook.js │ ├── favicons │ │ ├── faviconDescription.json │ │ ├── favicon_template.png │ │ └── favicons.js │ ├── gzip.js │ ├── less │ │ └── style.js │ ├── live.js │ ├── lowercase.js │ ├── preloader.js │ ├── release.js │ ├── scss │ │ └── style.js │ ├── template.js │ └── timestamp.js ├── sections │ └── bigwheel │ │ ├── normal │ │ ├── index.js │ │ ├── style.css │ │ └── template.hbs │ │ └── preloader │ │ ├── index.js │ │ ├── style.css │ │ └── template.hbs ├── style │ ├── fonts.{{css}} │ ├── global.{{css}} │ ├── main.{{css}} │ ├── normalize.{{css}} │ └── vars.{{css}} └── unsupported │ ├── default │ ├── src │ │ └── util │ │ │ └── warning.js │ └── static │ │ ├── device-matrix.json │ │ └── unsupported.html │ ├── none │ └── src │ │ └── unsupported.js │ └── php │ └── static │ ├── index.php │ ├── lib │ └── whichbrowser.phar │ └── main.php └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | /output 7 | /test/output 8 | .idea 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | /test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Jam3 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nyg-jam3 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Jam3 project scaffold based on [nyg](https://www.npmjs.com/package/nyg). Used to create the basic files needed for a Jam3 project. 6 | 7 | ## Usage 8 | 9 | [![NPM](https://nodei.co/npm/nyg-jam3.png)](https://www.npmjs.com/package/nyg-jam3) 10 | 11 | The nyg generator is designed to function similar to yeoman. To get it running, simply follow these steps: 12 | 13 | ```bash 14 | npm i nyg -g 15 | npm i nyg-jam3 -g 16 | cd your-project-directory 17 | nyg nyg-jam3 18 | ``` 19 | 20 | You will then be prompted with a number of questions, which will define the project. The appropriate files will then be copied to the current directory and it will install all your needed dependencies. Congratulations, you are now setup with the basis of a Jam3 project. 21 | 22 | ## Prompts 23 | 24 | `What is your name? (Author)` 25 | Default: (empty string) 26 | A name used in the README.md, package.json and humans.txt. 27 | 28 | `What is your email? (Author Email)` 29 | Default: (empty string) 30 | An email used in the package.json and humans.txt. 31 | 32 | `Describe the project:` 33 | Default: (empty string) 34 | A brief description of the project that is used at the top of the README.md. 35 | 36 | `What is your git repository? (GitHub Repository)` 37 | Default: (empty string) 38 | The git repository used for the project, used within the package.json. 39 | 40 | `What framework will your project use?"` 41 | Default: React 42 | The framework that this project will be based on, all necessary files and modules will be downloaded to get you up and running with the selected framework. Currently supports React, Bigwheel, or None. In the case of None, source files won't be created, but all the common development scripts will still be setup. 43 | 44 | `Would you perfer ComponentName/ComponentName.js over ComponentName/index.js?` 45 | Default: false 46 | Whether file names will follow the convention of [folder name]/[folder name].js or [folder name]/index.js. 47 | 48 | `Would you like to use ES6?` 49 | Default: true 50 | Whether to use ES6 and babel transpilation. Sets up a .babelrc file and all necessary dependencies. 51 | 52 | `What css preprocessor will your project use?` 53 | Default: SCSS 54 | Which css preprocessor should be setup on the project, defaults to SASS, but LESS is also an option. 55 | 56 | `Separate common npm modules into vendor.js?` 57 | Default: true 58 | Whether to separate all npm modules into a separate vender.js file, limiting the bundle.js file to just custom code. 59 | 60 | `What backend language would you like to use?` 61 | Default: PHP 62 | Whether to copy over backend libraries that aid development such as automatic meta tag generation. 63 | 64 | `Would you like to include an unsupported page?` 65 | Default: true 66 | Whether to include automatic unsupported page redirection. 67 | 68 | `Choose the password to use for password protection. (leave blank to disable)` 69 | Default: (empty string) 70 | If you want to enable password protection via .htaccess, simply type the password you would like to use and the .htaccess and .htpasswd files will be created. 71 | 72 | `Where on the server will your .htpasswd be located?` 73 | Default: /var/www 74 | If you opted to add password protection, this will need to be set to the location of the .htpasswd file in your production environment. 75 | 76 | ### [Generated Project Documentation](templates/base/README.md) 77 | 78 | ## License 79 | 80 | MIT, see [LICENSE.md](http://github.com/Jam3/generator-jam3/blob/master/LICENSE.md) for details. 81 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var nyg = require('nyg'); 4 | var spawn = require('cross-spawn'); 5 | 6 | var createSections = require('./lib/createSections'); 7 | var Favicon = require('./templates/scripts/favicons/favicons.js'); 8 | var addPasswordProtection = require('./lib/addPasswordProtection.js'); 9 | 10 | var prompts = [{ 11 | type: "input", 12 | name: "author", 13 | message: "What is your name? (Author)", 14 | default: "" 15 | }, { 16 | type: "input", 17 | name: "email", 18 | message: "What is your email? (Author Email)", 19 | default: "" 20 | }, { 21 | type: "input", 22 | name: "description", 23 | message: "Describe the project:", 24 | default: "" 25 | }, { 26 | type: "input", 27 | name: "repo", 28 | message: "What is your git repository? (GitHub Repository)", 29 | default: "" 30 | }, { 31 | type: "list", 32 | message: "What framework will your project use?", 33 | name: "framework", 34 | choices: [{ 35 | name: "React", 36 | value: "react" 37 | },{ 38 | name: "Bigwheel / Handlebars", 39 | value: "bigwheel", 40 | checked: true 41 | }, 42 | { 43 | name: "None", 44 | value: "none" 45 | }] 46 | },{ 47 | type: "confirm", 48 | name: "sectionNames", 49 | message: "Would you prefer ComponentName/ComponentName.js over ComponentName/index.js?", 50 | default: false, 51 | when: function(answers) { return answers.framework!=='none'; } 52 | },{ 53 | type: "confirm", 54 | name: "useES6", 55 | message: "Would you like to use ES6?", 56 | default: true, 57 | when: function(answers) { return answers.framework==='bigwheel' || answers.framework==='none'; } 58 | },{ 59 | type: "list", 60 | message: "What css preprocessor will your project use?", 61 | name: "css", 62 | choices: [{ 63 | name: "SCSS", 64 | value: "scss", 65 | checked: true 66 | },{ 67 | name: "LESS", 68 | value: "less" 69 | }] 70 | },{ 71 | type: "confirm", 72 | name: "vendor", 73 | message: "Separate common npm modules into vendor.js?", 74 | default: true 75 | },{ 76 | type: "list", 77 | message: "What backend language would you like to use?", 78 | name: "backend", 79 | choices: [{ 80 | name: "PHP", 81 | value: "php" 82 | }, { 83 | name: "None", 84 | value: "none" 85 | }] 86 | },{ 87 | type: "confirm", 88 | name: "unsupported", 89 | message: "Would you like to include an unsupported page?", 90 | default: true 91 | },{ 92 | type: "input", 93 | name: "password", 94 | message: "Choose the password to use for password protection. (leave blank to disable)", 95 | default: "" 96 | },{ 97 | type: "input", 98 | name: "passLocation", 99 | message: "Where on the server will your .htpasswd be located?", 100 | default: "/var/www", 101 | when: function(answers) { return answers.password!==''; } 102 | }]; 103 | var globs = [ 104 | { base: 'templates/{{framework}}/' }, 105 | { base: 'templates/', glob: 'scripts/*' }, 106 | { base: 'templates/base/' }, 107 | { base: 'templates/style/', output: 'src/style/' }, 108 | { base: 'templates/scripts/{{css}}/', glob: '*', output: 'scripts/' }, 109 | { base: 'templates/scripts/favicons/', glob: '*', output: 'scripts/favicons/' }, 110 | { base: 'templates/backend/{{backend}}/'}, 111 | { base: 'templates/unsupported/default/', when: function(answers) { return answers.unsupported; }}, 112 | { base: 'templates/unsupported/{{backend}}/', when: function(answers) { return answers.unsupported; } } 113 | 114 | ]; 115 | var gen = nyg(prompts,globs,{ignore:[".phar"]}) 116 | .on('postprompt', onPostPrompt) 117 | .on('postcopy', onPostCopy) 118 | .on('postinstall', onPostInstall) 119 | .run(); 120 | 121 | //*************************** Event Handlers *************************** 122 | 123 | function onPostPrompt() { 124 | var repo = gen.config.get('repo').split('/'); 125 | repo = repo[repo.length-1].toLowerCase().replace('.git',''); 126 | gen.config.set('repoName', repo || ''); 127 | if (gen.config.get('framework')!=='none' && gen.config.get('framework')!=='bigwheel') gen.config.set('useES6',true); 128 | } 129 | 130 | function onPostCopy() { 131 | var done = gen.async(); 132 | fs.rename(path.join(gen.cwd,'gitignore'),path.join(gen.cwd,'.gitignore'),function() { 133 | if (gen.config.get('framework')!=='none') { 134 | if (gen.config.get('useES6')) { 135 | gen.copy('templates/.babelrc','.babelrc',function() { 136 | if (gen.config.get('sectionNames') && gen.config.get('framework')==='react') { 137 | var style = gen.config.get('css'); 138 | var files = [ 139 | [path.join(gen.cwd,'src/components/Preloader/style.'+style),path.join(gen.cwd,'src/components/Preloader/Preloader.'+style)], 140 | [path.join(gen.cwd,'src/components/Rotate/index.js'),path.join(gen.cwd,'src/components/Rotate/Rotate.js')], 141 | [path.join(gen.cwd,'src/components/Rotate/style.'+style),path.join(gen.cwd,'src/components/Rotate/Rotate.'+style)], 142 | [path.join(gen.cwd,'src/components/MobileFullscreenVideo/index.js'),path.join(gen.cwd,'src/components/MobileFullscreenVideo/MobileFullscreenVideo.js')], 143 | [path.join(gen.cwd,'src/components/MobileFullscreenVideo/style.'+style),path.join(gen.cwd,'src/components/MobileFullscreenVideo/MobileFullscreenVideo.'+style)], 144 | [path.join(gen.cwd,'src/components/VideoPlayer/index.js'),path.join(gen.cwd,'src/components/VideoPlayer/VideoPlayer.js')], 145 | [path.join(gen.cwd,'src/components/VideoPlayer/style.'+style),path.join(gen.cwd,'src/components/VideoPlayer/VideoPlayer.'+style)], 146 | [path.join(gen.cwd,'src/components/VideoPlayer/VideoPoster/index.js'),path.join(gen.cwd,'src/components/VideoPlayer/VideoPoster/VideoPoster.js')], 147 | [path.join(gen.cwd,'src/components/VideoPlayer/VideoPoster/style.'+style),path.join(gen.cwd,'src/components/VideoPlayer/VideoPoster/VideoPoster.'+style)], 148 | [path.join(gen.cwd,'src/components/VideoPlayer/VideoTimeline/index.js'),path.join(gen.cwd,'src/components/VideoPlayer/VideoTimeline/VideoTimeline.js')], 149 | [path.join(gen.cwd,'src/components/VideoPlayer/VideoTimeline/style.'+style),path.join(gen.cwd,'src/components/VideoPlayer/VideoTimeline/VideoTimeline.'+style)], 150 | [path.join(gen.cwd,'src/sections/App/style.'+style),path.join(gen.cwd,'src/sections/App/App.'+style)], 151 | [path.join(gen.cwd,'src/components/HamburgerButton/index.js'),path.join(gen.cwd,'src/components/HamburgerButton/HamburgerButton.js')], 152 | [path.join(gen.cwd,'src/components/HamburgerButton/style.'+style),path.join(gen.cwd,'src/components/HamburgerButton/HamburgerButton.'+style)] 153 | ]; 154 | renameFiles(files,function() { 155 | createSections(gen,done); 156 | }); 157 | } else { 158 | createSections(gen,done); 159 | } 160 | }); 161 | } else { 162 | createSections(gen,done); 163 | } 164 | } else { 165 | fs.writeFile(path.join(gen.cwd,'src/index.js'),'',function() { 166 | if (gen.config.get('useES6')) { 167 | gen.copy('templates/.babelrc','.babelrc',done); 168 | } else { 169 | done(); 170 | } 171 | }); 172 | } 173 | if (gen.config.get('password') !== '') { 174 | addPasswordProtection(gen.cwd, gen.config.get('password')); 175 | } 176 | }); 177 | } 178 | 179 | function renameFiles(arr,cb) { 180 | var total = arr.length; 181 | var count = 0; 182 | var done = function() { 183 | count++; 184 | if (count>=total) cb(); 185 | }; 186 | arr.forEach(function(cur) { 187 | fs.rename(cur[0],cur[1],done); 188 | }); 189 | } 190 | 191 | function onPostInstall() { 192 | var done = gen.async(); 193 | var npm = spawn('npm', ['run','favicons'], {cwd: gen.cwd, stdio: 'inherit'}); 194 | npm.on('error',function() { 195 | console.log(arguments); 196 | }); 197 | npm.on('close',function(code) { 198 | if (code!==0) console.log(new Error('npm run favicons exited with non-zero code ' + code + '. Please try running "npm run favicons" again as administrator.')); 199 | done(); 200 | }); 201 | } 202 | -------------------------------------------------------------------------------- /lib/addPasswordProtection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var md5 = require('md5'); 6 | var prependFile = require('prepend-file'); 7 | 8 | module.exports = function(location, password) { 9 | createHtPasswd(location, password); 10 | modifyHtAccess(location); 11 | } 12 | 13 | function createHtPasswd(location, password) { 14 | var htpasswdData = "# This file should be located in a private place in the server\n"; 15 | htpasswdData += "user:" + md5(password); 16 | 17 | fs.writeFile(path.join(location,'.htpasswd'), htpasswdData, function(err) { 18 | if (err) throw err; 19 | }); 20 | } 21 | 22 | function modifyHtAccess(location) { 23 | var htaccessData = '' + 24 | 'AuthName "Restricted Area"\n' + 25 | 'AuthType Basic\n' + 26 | 'AuthUserFile ' + path.join('{{htpasswd}}', '.htpasswd').replace('\\','/') + '\n' + 27 | 'Require valid-user\n\n'; 28 | 29 | prependFile(path.join(location, '/static/.htaccess'), htaccessData, function(err) { 30 | if (err) throw err; 31 | }); 32 | } -------------------------------------------------------------------------------- /lib/createSections.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | module.exports = function(gen,cb) { 5 | var sections; 6 | if (gen.config.get('framework')==='bigwheel') { 7 | var model = require(path.join(process.cwd(),'src/model/index.js')); 8 | sections = ['Preloader']; 9 | Object.keys(model).forEach(function(key) { 10 | if (key.charAt(0)==="/") sections.push(key.substr(1) || 'Landing'); 11 | }); 12 | } else { 13 | sections = ['Landing']; 14 | } 15 | nextSection(sections,gen,cb); 16 | }; 17 | 18 | function nextSection(arr,gen,cb) { 19 | if (arr.length>0) { 20 | createSection(arr.shift(),gen,function() { 21 | nextSection(arr,gen,cb); 22 | }); 23 | } else { 24 | if (cb) cb(); 25 | } 26 | } 27 | 28 | function createSection(cur,gen,cb) { 29 | var name = gen.config.get('sectionNames') ? '{{section}}.js' : 'index.js'; 30 | var style = gen.config.get('sectionNames') ? '{{section}}.{{css}}' : 'style.{{css}}'; 31 | var count = 0; 32 | var total = 0; 33 | var done = function() { 34 | count++; 35 | if (count>=total) cb(); 36 | }; 37 | fs.stat('src/sections/'+cur+'/',function(err,stat) { 38 | if (err) { 39 | gen.config.set('section',cur); 40 | if (gen.config.get('framework')==='bigwheel') { 41 | var type = cur==='Preloader' ? 'preloader' : 'normal'; 42 | gen.copy('templates/sections/{{framework}}/'+type+'/index.js','src/sections/{{section}}/'+name,done); 43 | gen.copy('templates/sections/{{framework}}/'+type+'/style.css','src/sections/{{section}}/'+style,done); 44 | gen.copy('templates/sections/{{framework}}/'+type+'/template.hbs','src/sections/{{section}}/template.hbs',done); 45 | gen.copy('templates/.gitkeep','src/ui/{{section}}/.gitkeep',done); 46 | total += 4; 47 | } else if (gen.config.get('framework')==='react') { 48 | gen.copy('templates/sections/{{framework}}/index.js','src/sections/{{section}}/'+name,done); 49 | gen.copy('templates/sections/{{framework}}/style.css','src/sections/{{section}}/'+style,done); 50 | total += 2; 51 | } 52 | } else { 53 | done(); 54 | } 55 | }); 56 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nyg-jam3", 3 | "version": "3.0.0", 4 | "description": "Jam3 project scaffold generator based on nyg", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Nick Poisson", 9 | "email": "nick@jam3.com", 10 | "url": "https://github.com/njam3" 11 | }, 12 | "dependencies": { 13 | "cross-spawn": "^4.0.0", 14 | "md5": "^2.1.0", 15 | "nyg": "^2.7.0", 16 | "prepend-file": "^1.3.0", 17 | "rfg-api": "^0.1.7" 18 | }, 19 | "devDependencies": {}, 20 | "scripts": { 21 | "test": "node test/" 22 | }, 23 | "keywords": [ 24 | "jam3", 25 | "project", 26 | "scaffold", 27 | "generator", 28 | "nyg" 29 | ], 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/Jam3/generator-jam3.git" 33 | }, 34 | "homepage": "https://github.com/Jam3/generator-jam3", 35 | "bugs": { 36 | "url": "https://github.com/Jam3/generator-jam3/issues" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /templates/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015"{{#is framework 'react'}}, 4 | "react", 5 | "stage-1"{{/is}} 6 | ], 7 | "plugins": [ 8 | {{#is framework 'react'}}"transform-decorators-legacy", 9 | "transform-object-assign"{{/is}} 10 | ], 11 | "env": { 12 | {{#is framework 'react'}} 13 | "production": { 14 | "presets": [ 15 | "react-optimize" 16 | ] 17 | }{{/is}} 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Experience-Monks/generator-jam3/c8c0f674245b4a2475de235e3cf2ddaa65b4c2f8/templates/.gitkeep -------------------------------------------------------------------------------- /templates/backend/php/static/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | write(); 8 | ?> 9 | 10 | 11 | 12 | 13 | 14 | \{{#if vendor}} 15 | \{{/if}} 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /templates/backend/php/static/json/share.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "image": "", 4 | "title": "{{repoName}}", 5 | "description": "{{description}}", 6 | "og:site_name": "", 7 | "twitter:site": "", 8 | "twitter:creator": "" 9 | }, 10 | "route/subroute": { 11 | "title": "", 12 | "description": "", 13 | "keywords": "" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /templates/backend/php/static/lib/Meta.php: -------------------------------------------------------------------------------- 1 | '', 12 | 'description' => '', 13 | 'keywords' => '', 14 | 'og:title' => '', 15 | 'og:type' => 'website', 16 | 'og:image' => '', 17 | 'og:url' => '', 18 | 'og:description' => '', 19 | 'og:site_name' => '', 20 | 'twitter:card' => 'summary_large_image', 21 | 'twitter:title' => '', 22 | 'twitter:image' => '', 23 | 'twitter:description' => '', 24 | 'twitter:site' => '', 25 | 'twitter:creator' => '' 26 | ); 27 | var $nonMeta = array( 28 | 'title' 29 | ); 30 | 31 | function Meta($json=NULL,$path=NULL) { 32 | $this->ua = strtolower($_SERVER['HTTP_USER_AGENT']); 33 | if (array_key_exists('HTTP_X_FORWARDED_PROTO',$_SERVER)) { 34 | $protocol = htmlspecialchars($_SERVER['HTTP_X_FORWARDED_PROTO'], ENT_QUOTES); 35 | } else { 36 | $protocol = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? 'https' : 'http' ); 37 | } 38 | $this->host = $protocol.'://'.htmlspecialchars($_SERVER['HTTP_HOST'], ENT_QUOTES); 39 | $this->path = htmlspecialchars(is_string($path) ? $path : $_SERVER['REQUEST_URI'], ENT_QUOTES); 40 | $this->url = $this->host."/".$this->path; 41 | $this->addTag('og:url',$this->url); 42 | 43 | if (!empty($json)) { 44 | $this->share = json_decode(file_get_contents($json),true); 45 | $this->setPath($this->path); 46 | } 47 | } 48 | 49 | function isBot() { 50 | $isBot = false; 51 | for ($i=0; $iuaList); $i++) { 52 | if (strpos($this->ua,$this->uaList[$i])!==false) $isBot = true; 53 | } 54 | return $isBot; 55 | } 56 | 57 | function setPath($path) { 58 | $this->path = $path; 59 | $data = $this->share["default"]; 60 | if (@$this->share[$path]) $data = array_merge($data, $this->share[$path]); 61 | $this->addTags($data); 62 | } 63 | 64 | function addTag($tag, $content) { 65 | $this->tags[$tag] = $content; 66 | if ($tag==='title' || $tag==='description' || $tag==='image') { 67 | $this->tags['twitter:'.$tag] = $content; 68 | $this->tags['og:'.$tag] = $content; 69 | } 70 | } 71 | 72 | function addTags($data) { 73 | foreach ($data as $key => $value) { 74 | $this->addTag($key,$value); 75 | } 76 | } 77 | 78 | function write() { 79 | foreach ($this->tags as $key => $value) { 80 | if (in_array($key,$this->nonMeta)) { 81 | echo '<'.$key.'>'.$value.''; 82 | } else { 83 | $type = (strpos($key,'og:')!==false) ? 'property' : 'name'; 84 | echo ''; 85 | } 86 | } 87 | } 88 | 89 | } 90 | 91 | ?> 92 | -------------------------------------------------------------------------------- /templates/base/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /templates/base/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "experimentalObjectRestSpread": true, 8 | "jsx": true 9 | } 10 | }, 11 | "globals": { 12 | "ASSET_PATH": true, 13 | "_":true, 14 | "__":true, 15 | "THREE": false, 16 | "Int8Array": false, 17 | "Int16Array": false, 18 | "Int32Array": false, 19 | "Uint8Array": false, 20 | "Uint16Array": false, 21 | "Uint32Array": false, 22 | "Float32Array": false, 23 | "Float64Array": false, 24 | "Array": false, 25 | "Uint8ClampedArray": false, 26 | "google": false, 27 | "Promise": false, 28 | "document": false, 29 | "navigator": false, 30 | "window": false, 31 | "TimelineLite": false, 32 | "TimelineMax": false, 33 | "TweenLite": false, 34 | "TweenMax": false, 35 | "Back": false, 36 | "Bounce": false, 37 | "Circ": false, 38 | "Cubic": false, 39 | "Ease": false, 40 | "EaseLookup": false, 41 | "Elastic": false, 42 | "Expo": false, 43 | "Linear": false, 44 | "Power0": false, 45 | "Power1": false, 46 | "Power2": false, 47 | "Power3": false, 48 | "Power4": false, 49 | "Quad": false, 50 | "Quart": false, 51 | "Quint": false, 52 | "RoughEase": false, 53 | "Sine": false, 54 | "SlowMo": false, 55 | "SteppedEase": false, 56 | "Strong": false, 57 | "Draggable": false, 58 | "SplitText": false, 59 | "VelocityTracker": false, 60 | "CSSPlugin": false, 61 | "ThrowPropsPlugin": false, 62 | "BezierPlugin": false 63 | }, 64 | "plugins": [ 65 | "react", 66 | "standard", 67 | "promise" 68 | ], 69 | "extends": [ 70 | ], 71 | "env": { 72 | "browser": true, 73 | "node": true 74 | }, 75 | "rules": { 76 | "no-console": 0, 77 | "no-extra-semi": 2, 78 | "semi-spacing": 2, 79 | "semi": 2, 80 | "guard-for-in": 1, 81 | "no-trailing-spaces": 1, 82 | "react/jsx-uses-vars": 1, 83 | "react/jsx-uses-react": 1, 84 | "react/jsx-wrap-multilines": 1, 85 | "react/react-in-jsx-scope": 1, 86 | "react/prop-types": 0, 87 | "react/sort-comp": [1, { 88 | "order": [ 89 | "static-methods", 90 | "lifecycle", 91 | "everything-else", 92 | "render" 93 | ], 94 | "groups": { 95 | "lifecycle": [ 96 | "displayName", 97 | "propTypes", 98 | "contextTypes", 99 | "childContextTypes", 100 | "mixins", 101 | "statics", 102 | "defaultProps", 103 | "constructor", 104 | "getDefaultProps", 105 | "getInitialState", 106 | "state", 107 | "getChildContext", 108 | "componentWillMount", 109 | "componentDidMount", 110 | "componentWillReceiveProps", 111 | "shouldComponentUpdate", 112 | "componentWillUpdate", 113 | "componentDidUpdate", 114 | "componentWillUnmount", 115 | "componentWillAppear", 116 | "componentWillEnter", 117 | "componentWillLeave" 118 | ] 119 | } 120 | } 121 | ], 122 | "no-multi-spaces": 1, 123 | "no-use-before-define": 1, 124 | "arrow-spacing": [2, { "before": true, "after": true }], 125 | "block-spacing": [2, "always"], 126 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 127 | "camelcase": [2, { "properties": "never" }], 128 | "comma-spacing": [2, { "before": false, "after": true }], 129 | "comma-dangle": 0, 130 | "id-length": [1, {"min":1, "max": 50}], 131 | "space-before-function-paren": 0, 132 | "comma-style": [2, "last"], 133 | "curly": [2, "multi-line"], 134 | "dot-location": [2, "property"], 135 | "eqeqeq": [2, "always", {"null": "ignore"}], 136 | "func-call-spacing": [2, "never"], 137 | "handle-callback-err": [2, "^(err|error)$" ], 138 | "indent": [2, 2, { "SwitchCase": 1 }], 139 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 140 | "keyword-spacing": [2, { "before": true, "after": true }], 141 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 142 | "new-parens": 2, 143 | "no-array-constructor": 2, 144 | "no-caller": 2, 145 | "no-class-assign": 2, 146 | "no-cond-assign": 2, 147 | "no-const-assign": 2, 148 | "no-constant-condition": [2, { "checkLoops": false }], 149 | "no-control-regex": 2, 150 | "no-dupe-args": 2, 151 | "no-dupe-class-members": 2, 152 | "no-dupe-keys": 2, 153 | "no-duplicate-case": 2, 154 | "no-duplicate-imports": 2, 155 | "no-empty-character-class": 2, 156 | "no-empty-pattern": 2, 157 | "no-eval": 2, 158 | "no-ex-assign": 2, 159 | "no-extend-native": 2, 160 | "no-extra-bind": 2, 161 | "no-extra-boolean-cast": 2, 162 | "no-extra-parens": [2, "functions"], 163 | "no-fallthrough": 2, 164 | "no-floating-decimal": 2, 165 | "no-func-assign": 2, 166 | "no-global-assign": 2, 167 | "no-implied-eval": 2, 168 | "no-inner-declarations": [2, "functions"], 169 | "no-invalid-regexp": 2, 170 | "no-irregular-whitespace": 2, 171 | "no-iterator": 2, 172 | "no-label-var": 2, 173 | "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], 174 | "no-lone-blocks": 2, 175 | "no-mixed-spaces-and-tabs": 2, 176 | "no-multi-str": 2, 177 | "no-negated-in-lhs": 2, 178 | "no-new": 2, 179 | "no-new-func": 2, 180 | "no-new-object": 2, 181 | "no-new-require": 2, 182 | "no-new-symbol": 2, 183 | "no-new-wrappers": 2, 184 | "no-obj-calls": 2, 185 | "no-octal": 2, 186 | "no-octal-escape": 2, 187 | "no-path-concat": 2, 188 | "no-proto": 2, 189 | "no-redeclare": 2, 190 | "no-regex-spaces": 2, 191 | "no-return-assign": [2, "except-parens"], 192 | "no-self-assign": 2, 193 | "no-self-compare": 2, 194 | "no-sequences": 2, 195 | "no-shadow-restricted-names": 2, 196 | "no-sparse-arrays": 2, 197 | "no-tabs": 2, 198 | "no-template-curly-in-string": 2, 199 | "no-this-before-super": 2, 200 | "no-throw-literal": 2, 201 | "no-undef": 2, 202 | "no-undef-init": 2, 203 | "no-unexpected-multiline": 2, 204 | "no-unmodified-loop-condition": 2, 205 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 206 | "no-unreachable": 2, 207 | "no-unsafe-finally": 2, 208 | "no-unsafe-negation": 2, 209 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 210 | "no-unused-expressions": 2, 211 | "no-useless-call": 2, 212 | "no-useless-computed-key": 2, 213 | "no-useless-escape": 1, 214 | "no-useless-rename": 2, 215 | "no-whitespace-before-property": 1, 216 | "no-with": 2, 217 | "object-property-newline": [2, { "allowMultiplePropertiesPerLine": true }], 218 | "one-var": [2, { "initialized": "never" }], 219 | "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], 220 | "padded-blocks": [2, "never"], 221 | "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], 222 | "rest-spread-spacing": [2, "never"], 223 | "space-in-parens": [1, "never"], 224 | "space-infix-ops": 1, 225 | "space-unary-ops": [1, { "words": true, "nonwords": false }], 226 | "template-curly-spacing": [2, "never"], 227 | "unicode-bom": [2, "never"], 228 | "use-isnan": 2, 229 | "valid-typeof": 2, 230 | "wrap-iife": [2, "any", { "functionPrototypeMethods": true }], 231 | "yield-star-spacing": [2, "both"], 232 | "yoda": [2, "never"], 233 | "standard/object-curly-even-spacing": [2, "either"], 234 | "standard/array-bracket-even-spacing": [2, "either"], 235 | "standard/computed-property-even-spacing": [2, "even"], 236 | "promise/param-names": 2 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /templates/base/.gitattributes: -------------------------------------------------------------------------------- 1 | raw-assets/fonts/**/* filter=lfs diff=lfs merge=lfs -text 2 | raw-assets/images/**/* filter=lfs diff=lfs merge=lfs -text 3 | raw-assets/sounds/**/* filter=lfs diff=lfs merge=lfs -text 4 | raw-assets/videos/**/* filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /templates/base/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true 17 | } 18 | -------------------------------------------------------------------------------- /templates/base/COMPONENTS.md: -------------------------------------------------------------------------------- 1 | [Back](README.md) | [Scripts](SCRIPTS.md) 2 | # Components 3 | 4 | ##### Table of Contents 5 | - [Preloader](#preloader) 6 | - [Rotate Screen](#rotate) 7 | - [Video Player](#video-player) 8 | - [Mobile Fullscreen Video](#mobile-fs-video) 9 | - [Hamburger Button](#hamburger-button) 10 | - [Device Detection](#device) 11 | - [Stats](#stats) 12 | - [Unsupported Page](#unsupported) 13 | - [Meta](#meta) 14 | - [Audio](#audio) 15 | 16 | 17 | ## Preloader (components/Preloader) 18 | The preloader component is built on the top of [preloader module](https://www.npmjs.com/package/preloader). Please refer to it for more information. 19 | 20 | #### Setup 21 | Specify files or folders (to be read recursively) using glob format in `config-preloader.json` in the root of the project. 22 | Example preloader json file: 23 | ``` 24 | [ 25 | "json/**/*" 26 | ] 27 | ``` 28 | 29 | On `npm start` the preloader script will be executed and `preloader-list.json` will be created in `raw-assets` folder with the list of files to be loaded, excluding any junk files. 30 | 31 | **Important:** 32 | * Do not include `assets` folder in the paths of config file 33 | * Newly added assets won't affect the preloader list until node script restarted 34 | 35 | #### Props 36 | - `assetsList` (Required) - an array of assets to be loaded. It automatically determines the loader to be used based on file extension (json, mp4, etc.) 37 | - `minDisplayTime` - min time (in milliseconds) for the preloader to be shown 38 | - `options` - an object that contains the following properties: 39 | 40 | - `xhrImages` - loads images via XHR and converts to a Blob instead of the image tag. Default: `false` 41 | - `loadFullAudio` - specifies is audio should be loaded in full instead of just to the point where they can play. Default: `false` 42 | - `loadFillVideo` - specifies is video should be loaded in full instead of just to the point where they can play. Default: `false` 43 | - `onComplete` - function to attach to the complete event 44 | - `onProgress` - function to attach to the progress event 45 | 46 | #### API 47 | Follows [preloader](https://www.npmjs.com/package/preloader) API for the corresponding methods: 48 | 49 | - `add` 50 | - `get` 51 | - `load` 52 | - `stopLoad` 53 | 54 | 55 | ## Rotate Screen (components/Rotate) 56 | 57 | The rotate screen component simply needs to be included on the page. It determines the device orientation via the detect utility. It will determine if it needs to be shown based the boolean prop. 58 | 59 | #### Props 60 | - `portrait` - Boolean which determines if the site is locked to portrait or not. If `true`, the rotate screen will display if the device is in landscape mode, otherwise it will display if the device is in portrait mode. Default: true 61 | 62 | 63 | ## Video Player (components/VideoPlayer) 64 | This is basic video player that includes `VideoTimeline` and `VideoPoster` components. 65 | It is accessible and allows to be controlled using keyboard. 66 | 67 | #### Props 68 | - `src` - *String* (required) - video source 69 | - `poster` - *String* - poster image 70 | - `id` - - *String* or *Number* - video ID that will be sent back with `onPlay` hook 71 | - `preload` - *String* - determines whether video should automatically preload. Valid options are `auto`, `metadata` or `none` 72 | - `captions` - *Object* - track data refer to https://developer.mozilla.org/en/docs/Web/HTML/Element/track 73 | - `disableBackgroundCover`- *Boolean* - disable video cover effect 74 | - `allowKeyboardControl` - *Boolean* - allow/disallow toggle play state with space bar when video element is focused 75 | - `autoPlay` - *Boolean* - determines whether video should automatically play on load 76 | - `muted` - *Boolean* - determines whether video should play sound 77 | - `loop` - *Boolean* - determines whether video should loop 78 | - `togglePlayOnClick` - *Boolean* - allow/disallow toggle play state on video click 79 | - `showControlsOnLoad` - *Boolean* - determines whether to show video controls on load without mouse interaction 80 | - `hasCloseButton` - *Boolean* - render close button that is synced with controls 81 | - `hasPlayButton` - *Boolean* - render play button for video poster 82 | - `showPosterOnEnd` - *Boolean* - determines whether poster should show up after video has ended 83 | - `hasControls` - *Boolean* - render controls 84 | - `playsInline` - *Boolean* - determines if video plays inline on iPhone 85 | - `autoPlayDelay` - *Number* - auto play delay (in milliseconds). Only works with `autoPlay: true` 86 | - `controlsTimeout` - *Number* - time (in milliseconds) after which controls and close button hide if the user was inactive 87 | - `windowWidth` - *Number* - window width. It is required when for background cover videos - `disableBackgroundCover: false` 88 | - `windowHeight` - *Number* - window height. It is required when for background cover videos - `disableBackgroundCover: false` 89 | - `volume` - *Number* - values from 0 to 1 90 | - `startTime` - *Number* - video start time (in seconds) 91 | - `posterFadeDuration` - *Number* - poster fade out duration (in milliseconds) 92 | - `onPlay` - *Function* - on video play hook 93 | - `onPause` - *Function* - on video pause hook 94 | - `onEnd` - *Function* - on video end hook 95 | - `onClose` - *Function* - on close button click hook 96 | 97 | #### API 98 | - `showControls` - force controls to show 99 | - `hideControls`- force controls to hide 100 | - `getVideoElement` - access HTML video 101 | - `play` 102 | - `pause` 103 | - `mute` 104 | - `unmute` 105 | - `togglePlay` 106 | - `toggleMute` 107 | - `toggleFullScreen` 108 | - `toggleCaptions` - show/hide captions 109 | 110 | 111 | ## Mobile Fullscreen Video (components/MobilFsVideo) 112 | Component that triggers video to go fullscreen on Android and play in native browser on iOS 113 | 114 | #### Props 115 | - `src` - *String* - video source 116 | - `onOpen` *Function* - fullscreen enter (native player open) hook 117 | - `onClose` *Function* - fullscreen exit (native player close) hook 118 | 119 | #### API 120 | - `play` 121 | - `pause` 122 | - `getVideoElement` - access HTML video 123 | 124 | 125 | 126 | ## Hamburger Button (components/HamburgerButton) 127 | 128 | #### Props 129 | - `className` - *String* - additional class name 130 | - `style` - *Object* - additional styles 131 | - `tabIndex` - *Number* - container's tab index 132 | - `state` - *String* - one of the 3 available states: `idle`, `close` or `back` 133 | - `activeState` - *String* - the active state, `close` or `back` 134 | - `isMouseOver` - *Boolean* - force mouse over state 135 | - `onClick` - *Function* - `click` hook 136 | - `onMouseEnter` - *Function* - `mouseenter` hook 137 | - `onMouseLeave` - *Function* - `mouseleave` hook 138 | 139 | 140 | ## Device Detection (util/detect.js) 141 | 142 | The device detection utility is a javascript object with numerous properties to help you build conditionals based on the current browser / device. It includes the following properties. 143 | - `isBot` - Detect if is a crawler bot, 144 | - `isFacebook` - Detect if in-app Facebook browser, 145 | - `isTwitter` - Detect if in-app Twitter browser, 146 | - `isInstagram` - Detect if in-app Instagram browser, 147 | - `isPinterest` - Detect if in-app Pinterest browser, 148 | - `isInAppBrowser` - Detect if browser is in-app, 149 | - `inAppBrowserVersion` - in-app browser version, 150 | - `device` - returns either `phone`, `tablet`, or `desktop` 151 | - `vendor` - vendor name of the browser (i.e. "google inc.") 152 | - `os` - Returns the current operating system 153 | - `osVersion` - Returns the version of the current operating system 154 | - `browser` - Returns the current browser 155 | - `browserVersion` - Returns the version of the current browser 156 | - `manufacturer` - Returns the manufacturer of the current device 157 | - `devicePixelRatio` - Returns the current devicePixelRatio of the current screen 158 | - `classes` - Classes returns an array of classes to add to the body / html element to allow for easy conditional logic in css. These include device type, browser, and pixel ratio 159 | - `isMobile` - Boolean that describes if the device is a phone or a tablet 160 | - `isPhone` - Boolean that describes if the device is a phone 161 | - `isTablet` - Boolean that describes if the device is a tablet 162 | - `isDesktop` - Boolean that describes if the device is a desktop computer 163 | - `isChrome` - Boolean that describes if browser is Chrome 164 | - `isIE` - Boolean that describes if browser is Internet Explorer 165 | - `isEdge` - Boolean that describes if browser is Microsoft Edge 166 | - `isFirefox` - Boolean that describes if browser is Firefox 167 | - `isSafari` - Boolean that describes if browser is Safari 168 | - `isOpera` - Boolean that describes if browser is Opera 169 | - `md` - The [mobile-detect](npmjs.com/mobile-detect) object used in the device detection 170 | - `bowser` - The [bowser](npmjs.com/bowser) object used in the device detection 171 | - `orientation` - String that returns either `portrait` or `landscape` 172 | 173 | 174 | ## Stats (util/stats.js) 175 | 176 | A simple utility that tracks browser framerate. The script simply needs to be included and executed. This is already done for you in `index.js` and is currently setup to only display in the development environment. 177 | 178 | 179 | ## Unsupported Page 180 | 181 | The unsupported page is automatically redirected to via PHP. This is determined via `device-matrix.json` in the static folder of the project. Specify your minimum browser versions in the json. You can style your page by modifying `unsupported.html` also in the static folder of the project. Here is an example of the device-matrix.json: 182 | 183 | ``` 184 | { 185 | "ie": 11, 186 | "safari": 8, 187 | "chrome": 47, 188 | "firefox": 42 189 | } 190 | ``` 191 | 192 | This will ensure the browser is at a minimum, Internet Explorer 11, Safari 8, Chrome 47 or Firefox 42, otherwise it will redirect to the unsupported page. 193 | 194 | 195 | ## Meta 196 | 197 | Meta.php takes care of automatically populating Open Graph, Twitter Card data, and regular meta tags. Simply specify your routes with the accompanying title, description, and image in the share.json in the static folder and the rest will be taken care of. title, image, and description will automatically be spread across Open Graph, Twitter Card and regular meta tags. You can add any meta property into the json and it will be parsed and added as a meta tag. Here is an example share.json: 198 | 199 | ``` 200 | { 201 | "default": { 202 | "image": "http://www.jam3.com/wp-content/uploads/2014/05/jam3_fb.jpg", 203 | "title": "Jam3 | Toronto Digital Design and Development Agency", 204 | "description": "Jam3 is one of the world’s top digital production and design agencies. We specialize in creating highly advanced, experiential works in both the advertising and entertainment industries.", 205 | "og:site_name": "Jam3.com", 206 | "twitter:site": "@Jam3", 207 | "twitter:creator": "@Jam3" 208 | }, 209 | "about": { 210 | "title": "About - Jam3.com", 211 | "description": "“It’s not wise to violate rules until you know how to observe them.” Keep reading" 212 | } 213 | } 214 | ``` 215 | 216 | 217 | ## Audio (util/audio.js) 218 | 219 | This is a Howler wrapper that reads sounds manifest - `data/sounds.js` and sets `Howl` instances for each manifest entry. 220 | 221 | Example manifest: 222 | ``` 223 | { 224 | 'button-rollover': '{{path}}/rollover.mp3', 225 | 'button-click': ['{{path}}/click.wav', '{path}/click.mp3'] 226 | 'sprite': { 227 | src: '{{path}}/sprite.mp3', 228 | sprite: { 229 | 'chunk-1': [0, 3000], 230 | 'chunk-1': [3000, 2000] 231 | } 232 | }, 233 | 'ambient': { 234 | src: '{{path}}/ambient.mp3', 235 | loop: true, 236 | autoplay: true 237 | } 238 | } 239 | ``` 240 | 241 | if manifest item is type of `String` or `Array`, then it will be used as source(s) and a `Howl` will be set up with it's default options. 242 | If manifest entry in type of `Object`, its property will be merged with Howler defaults. 243 | 244 | This utility is using Singleton pattern, so it will be initialized once upon first `import/require`. 245 | Thus, be careful with `preload` and `autoplay` options combination because if both are set to true for a sound, it will start playing on app initialization. 246 | 247 | **NOTE**: `preload` option is set to `false` for all sounds by default but can overridden via in manifest for specific sounds. 248 | You can also use `load` function for dynamic loading control. Refer to [Howler API](https://github.com/goldfire/howler.js#documentation). 249 | 250 | #### API 251 | - `sounds` (getter) - Get specific sound from the map by ID e.g. `audio.sounds['some-sound'].play()` 252 | - `extraData` (setter) - Update sound model. It won't replace the original data unless you overwrite existing in manifest keys e.g. `audio.extraData = {noise: '{path}noise.wav'}` 253 | - `play` - play sound or sprite by `ID` e.g. ```audio.play('button-click')``` or ```audio.play('chunk-1')``` 254 | 255 | #### Example 256 | Take a look at code example `test/components/SoundTest.js` 257 | 258 | See in action in your browser http://localhost:9966/test/SoundTest 259 | -------------------------------------------------------------------------------- /templates/base/README.md: -------------------------------------------------------------------------------- 1 | # {{repoName}} 2 | 3 | {{description}} 4 | 5 | ## Developers 6 | {{author}} 7 | 8 | ## Dependencies list 9 | [http://jam3-license.herokuapp.com/projects/{{repoName}}/licenses](http://jam3-license.herokuapp.com/projects/{{repoName}}/licenses) 10 | 11 | Note: If the link is broken use http://jam3-license.herokuapp.com/projects/[repo name]/licenses ? 12 | 13 | ## PROJECT DOCUMENTATION 14 | 15 | #### [Components](COMPONENTS.md) 16 | #### [Scripts](SCRIPTS.md) 17 | #### [Tests](TESTS.md) 18 | 19 | ## GIT 20 | 21 | ``` 22 | git checkout {{repo}} 23 | ``` 24 | 25 | ## Setup 26 | 27 | ```bash 28 | $ npm install 29 | ``` 30 | 31 | Folder Structure 32 | ```bash 33 | raw-assets/json/ // Any static json files 34 | raw-assets/images/ // Images that should not be texture packed 35 | raw-assets/videos/ 36 | raw-assets/sounds/ 37 | raw-assets/fonts/ 38 | raw-assets/tp/ // Folder for the .tps files 39 | raw-assets/tp/common/ // Folder containing images to be texture packed, 40 | // folder name should share the name of the tps file 41 | ``` 42 | 43 | ## Run 44 | 45 | ```bash 46 | $ npm start 47 | ``` 48 | 49 | ## Development 50 | 51 | Javascript Style Guide: https://github.com/Jam3/Javascript-Code-Conventions 52 | 53 | CSS Style Guide: https://github.com/Jam3/CSS-Style-Guide 54 | 55 | ### IMAGES 56 | 57 | Name images using dashes: 58 | - large-pixel-image.png 59 | - cute-yellow-pepper.png 60 | 61 | ### GLOBAL VARIABLES 62 | 63 | Always make the root path to assets (image/videos..) a variable, store it in your global settings file, in both Javascript code and CSS 64 | 65 | Because when the site goes live, those assets will come from a CDN and going in and changing all the paths the day before the site goes live is very annoying. 66 | 67 | ```less 68 | @{ASSET_PATH}: 'images/'; // This variable will be changed by a script when pushing to production or other environments 69 | .background { 70 | background: url('@{ASSET_PATH}/images/background.png') 71 | } 72 | ``` 73 | 74 | ```scss 75 | $ASSET_PATH: 'images/'; // This variable will be changed by a script when pushing to production or other environments 76 | .background { 77 | background: url('#{$ASSET_PATH}/images/background.png') 78 | } 79 | ``` 80 | 81 | ```javascript 82 | var filePath = settings.ASSET_PATH + 'fancy/fancy-graphic.png'; 83 | ``` 84 | {{#is framework 'react'}} 85 | To debug Redux, use this browser extension: http://zalmoxisus.github.io/redux-devtools-extension/ {{/is}} 86 | 87 | ## DEPLOYMENT 88 | 89 | ```bash 90 | $ npm run release 91 | ``` 92 | 93 | ## BROWSER SUPPORT 94 | 95 | [Browserify](https://www.npmjs.com/package/browserify) recently reduced support for older IE browsers (IE10 and below). This is due to the updated `Buffer` module. If you wish to support these browsers and are planning to use the `Buffer` module or use a seperate module that uses it, consider installing this [browser polyfill](https://github.com/inexorabletash/polyfill/blob/master/typedarray.js). 96 | 97 | Optionally you can install version 4 of the `Buffer` module by running this command. 98 | 99 | `npm install buffer@^4 --save-dev` 100 | 101 | You will also have to update the browserify bundle options using the require flag: 102 | 103 | `-r buffer/:buffer` 104 | 105 | This will require editing the `release.js` and `dev.js` scripts. 106 | 107 | For more information, check out: [https://github.com/substack/node-browserify/pull/1678](https://github.com/substack/node-browserify/pull/1678) 108 | 109 | ## RUN TESTS 110 | 111 | Use [Budo](http://npmjs.com/budo/) to develop and test your modules independently before integrating into the framework. 112 | 113 | ```bash 114 | $ cd {{repoName}} 115 | $ budo test/thingtotest/index.js [PORT] [-- browserify args] 116 | ``` 117 | 118 | ## NPM MODULES 119 | 120 | When installing modules be sure to use `npm install --save` for dependencies that will be used in the actual application deployed to the server. 121 | 122 | And `npm install --save-dev` for modules that are only used on your system for workflow and development, like automated tasks etc. 123 | 124 | [List of Jam3 Node modules](https://docs.google.com/a/jam3.com/spreadsheets/d/1bPImGwGLjqbOnBxMNmqGVz2mdfVb_R2FKaaoOw1IyP8/edit#gid=0) 125 | -------------------------------------------------------------------------------- /templates/base/SCRIPTS.md: -------------------------------------------------------------------------------- 1 | [Back](README.md) | [Components](COMPONENTS.md) 2 | # Scripts 3 | 4 | ##### Table of Contents 5 | [config.json](#config) 6 | [Favicons](#favicons) 7 | [Lowercase](#lowercase) 8 | [Facebook Open Graph](#facebook) 9 | 10 | 11 | ## config.json 12 | 13 | The config.json in the root of the project controls how all the scripts function. Here is the default setup for the config.json: 14 | 15 | ``` 16 | { 17 | "defaults": { 18 | "entry": "./src/index.js", 19 | "vendor": "vendor.js", 20 | "bundle": "bundle.js", 21 | "static": "./static/", 22 | "raw": "./raw-assets/", 23 | "style": "./src/style/main.scss", 24 | "ASSET_PATH": "./assets/", 25 | "BASENAME": "/", 26 | "JPEG_QUALITY": 80, 27 | "templateBlacklist": [], 28 | "htpasswd": false, 29 | "env": {} 30 | }, 31 | "development": { 32 | "output": "./build/" 33 | }, 34 | "production": { 35 | "timestamp": true, 36 | "output": "./release/", 37 | "minify": true, 38 | "removeLogs": true 39 | } 40 | } 41 | ``` 42 | 43 | All variables are able to be overridden, as you can see via the development and production properties. These properties relate to the NODE_ENV variable. By default `npm start` sets NODE_ENV=development and `npm run release` sets NODE_ENV=production. 44 | 45 | Here is a breakdown of what each properties controls. 46 | 47 | `entry` - The entry point for your app 48 | `vendor` - The name of your vendor.js file, all npm modules will be bundled into this file, setting it to `false` will put everything into the bundle.js 49 | `bundle` - The name of the bundle.js 50 | `static` - The folder containing static html / server files 51 | `raw` - The folder container raw-assets, such as images, asset related json files and 3d assets 52 | `style` - The entry point for your less / scss files 53 | `ASSET_PATH` - Where raw-assets will live in the end, useful to edit this if assets will live on a CDN 54 | `BASENAME` - If your project will live in a subfolder, specify that here, with a trailing slash 55 | `JPEG_QUALITY` - The quality to compress JPEG's in the raw-assets folder to, set to `false` to disable JPEG compression 56 | `templateBlacklist` - Files in the static folder to skip handlebars template compilation, this is only needed if a file breaks handlebars compilation 57 | `htpasswd` - Where the .htpasswd will live on your production environment 58 | `env` - Any custom variables to use via envify, allows you to access them via `process.env.MY_CUSTOM_VAR` 59 | `output` - The folder to place all processed files in 60 | `minify` - Whether to minify the released javascript files, useful for debugging 61 | `removeLogs` - Whether to remove console commands from the final build 62 | `timestamp` - Whether to add a build signature to assets and compiled javascript / css files. `true` will add a timestamp to the path, a string will simply append that string to the path, and false will not add a build signature 63 | 64 | To add a new environment, simply specify it in the config.json and then create a new entry for the script in the package.json. Here is an example adding a staging environment. 65 | 66 | config.json 67 | ``` 68 | { 69 | "defaults": {...}, 70 | "development": {...}, 71 | "production": {...}, 72 | "staging": { 73 | "timestamp": false, 74 | "minify": true, 75 | "removeLogs": true 76 | } 77 | } 78 | ``` 79 | 80 | package.json 81 | ``` 82 | "staging": "npm run preloader-release && npm run release-clean && node scripts/style.js --env=staging && node scripts/release.js --env=staging && npm run release-copy && npm run release-gzip" 83 | ``` 84 | 85 | 86 | ## Favicons `npm run favicons` 87 | 88 | #### Default setup 89 | After generating the scaffolding, you will have favicons setup for all the common devices (iOS, Android, Windows Phone and Desktop) 90 | 91 | #### Customize 92 | You can change the default configuration by modifying the file `/scripts/favicons/faviconDescription.json`. All the available option are described in (here)[http://realfavicongenerator.net/api/non_interactive_api](http://realfavicongenerator.net/api/non_interactive_api). 93 | If you want to change the image, you can replace the image located in `/scripts/favicons/favicon_template.png` for your new favicon image. The recommended size for this master image is `500x500`. 94 | After changing all the things you want, just run `npm run update-favicons` and you will get your new favicons ready. 95 | 96 | 97 | ## Lowercase `npm run lowercase` 98 | 99 | The lowercase script is a simple utility that will lowercase and replace spaces with dashes for all files within the raw-assets folder. This is useful if you have been handed assets from external sources with inconsistent naming conventions. 100 | 101 | 102 | ## Facebook Open Graph `npm run facebook` 103 | 104 | `npm run facebook -- --url=[YOUR URL]` 105 | 106 | The Facebook script will clear the Facebook sharing cache for all URL's in your share.json, as well as the base URL you specify in the script. Simply provide your base URL to the script and it will begin clearing the cache for that domain. Here is an example: 107 | 108 | share.json 109 | ``` 110 | { 111 | "default": {...}, 112 | "gallery: {...}, 113 | "gallery/grid": {...} 114 | } 115 | ``` 116 | 117 | `npm run facebook -- --url=http://my-page-url.com/` 118 | 119 | Running this command with this share.json will clear the following URLs from the facebook cache: 120 | 121 | ``` 122 | http://my-page-url.com/ 123 | http://my-page-url.com/gallery 124 | http://my-page-url.com/gallery/grid 125 | ``` -------------------------------------------------------------------------------- /templates/base/STANDARDS.md: -------------------------------------------------------------------------------- 1 | # Standards 2 | 3 | ##### Table of Contents 4 | [ESLint](#eslint) 5 | 6 | 7 | ## ESlint (.eslintrc) 8 | 9 | #### Required plugins (global or local) 10 | * eslint-plugin-react 11 | * eslint-plugin-standard 12 | * eslint-plugin-promise 13 | * babel-eslint 14 | -------------------------------------------------------------------------------- /templates/base/TESTS.md: -------------------------------------------------------------------------------- 1 | [Back](README.md) 2 | # Tests 3 | 4 | Tests are set up for dev environment and you can access the list of test components in your browser at `http://localhost:9966/test`. 5 | Each component will be available on it's own page like `http://localhost:9966/test/{componentName}`. 6 | 7 | To add a new item to the list, add a component to the `src/tests/manifest.js` and a create a wrapper component if necessary. 8 | Refer to existing test examples `src/tests/`; -------------------------------------------------------------------------------- /templates/base/config-preloader.json: -------------------------------------------------------------------------------- 1 | [ 2 | "json/**/*" 3 | ] -------------------------------------------------------------------------------- /templates/base/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaults": { 3 | "entry": "./src/index.js", 4 | "vendor": {{#if vendor}}"vendor.js"{{else}}false{{/if}}, 5 | "bundle": "bundle.js", 6 | "static": "./static/", 7 | "raw": "./raw-assets/", 8 | "style": "./src/style/main.{{css}}", 9 | "ASSET_PATH": "./assets/", 10 | "BASENAME": "/", 11 | "JPEG_QUALITY": 90, 12 | "templateBlacklist": [{{#if unsupported}}{{#is backend 'php'}}".phar"{{/is}}{{/if}}], 13 | "liveReloadClient": true, 14 | "htpasswd": {{#if password}}"{{passLocation}}"{{else}}false{{/if}}, 15 | "env": {} 16 | }, 17 | "development": { 18 | "vendor": false, 19 | "output": "./build/" 20 | }, 21 | "production": { 22 | "timestamp": false, 23 | "output": "./release/", 24 | "minify": true, 25 | "removeLogs": true 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /templates/base/gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | 4 | node_modules 5 | 6 | build 7 | release 8 | -------------------------------------------------------------------------------- /templates/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{repoName}}", 3 | "version": "0.0.0", 4 | "description": "{{description}}", 5 | "author": { 6 | "name": "{{author}}", 7 | "email": "{{email}}", 8 | "url": "https://github.com/Jam3" 9 | }, 10 | "scripts": { 11 | "start": "npm-run-all preloader copy style {{#if unsupported}}{{#is backend "none"}}unsupported {{/is}}{{/if}}dev", 12 | "build": "node scripts/timestamp.js && npm-run-all preloader clean style browserify {{#if unsupported}}{{#is backend "none"}}unsupported {{/is}}{{/if}}copy gzip && node scripts/timestamp.js --delete", 13 | "release": "cross-env NODE_ENV=production npm run build", 14 | "copy": "node scripts/copy.js", 15 | "style": "node scripts/style.js", 16 | "preloader": "node scripts/preloader.js", 17 | "dev": "node scripts/dev.js", 18 | "browserify": "node scripts/release.js",{{#if unsupported}}{{#is backend "none"}} 19 | "unsupported": "node scripts/release.js --unsupported",{{/is}}{{/if}} 20 | "gzip": "node scripts/gzip.js", 21 | "clean": "node scripts/clean.js", 22 | "lowercase": "node scripts/lowercase.js", 23 | "favicons": "node scripts/favicons/favicons.js", 24 | "facebook": "node scripts/facebook.js" 25 | }, 26 | "license": "ISC", 27 | "repository": "{{repo}}", 28 | "dependencies": { 29 | "gsap": "^1.19.1", 30 | "howler": "^2.0.4"{{#is framework 'bigwheel'}}, 31 | "bigwheel": "^3.0.0", 32 | "domready": "^1.0.8", 33 | "bw-analytics": "^1.1.1", 34 | "handlebars": "^2.0.0", 35 | "domify": "^1.4.0"{{/is}}{{#is framework 'react'}}, 36 | "babel-polyfill": "^6.23.0", 37 | "bowser": "^1.7.0", 38 | "domready": "^1.0.8", 39 | "fullscreen-handler": "^0.0.2", 40 | "mobile-detect": "^1.3.6", 41 | "preloader": "^4.0.1", 42 | "prop-types": "^15.5.10", 43 | "react": "^16.1.1", 44 | "react-dom": "^16.1.1", 45 | "react-redux": "^5.0.6", 46 | "react-router": "^4.2.0", 47 | "react-router-dom": "^4.2.2", 48 | "react-router-redux": "^5.0.0-alpha.8", 49 | "react-background-video-player": "^0.3.0", 50 | "react-svg-inline": "^2.0.0", 51 | "react-transition-group-plus": "^0.5.2", 52 | "redux": "^3.4.0", 53 | "stats.js": "0.17.0"{{/is}} 54 | }, 55 | "devDependencies": { 56 | "budo": "^10.0.3", 57 | "rimraf": "^2.5.2", 58 | "envify": "^3.4.0", 59 | "concat-stream": "^1.5.1", 60 | "graceful-fs": "^4.1.3", 61 | "handlebars": "^4.0.5", 62 | "glob": "^6.0.1", 63 | "junk": "^2.0.0", 64 | "merge": "^1.2.0", 65 | "isbinaryfile": "^3.0.0", 66 | "npm-run-all": "^4.0.0", 67 | "cross-env": "^5.0.0",{{#is css 'less'}} 68 | "less": "^2.5.3", 69 | "less-plugin-autoprefix": "^1.5.1", 70 | "less-plugin-glob": "^1.1.1",{{/is}}{{#is css 'scss'}} 71 | "autoprefixer": "^6.2.3", 72 | "node-sass": "^3.4.2", 73 | "postcss": "^5.0.14", 74 | "node-sass-glob": "^1.0.4",{{/is}} 75 | "minimist": "^1.2.0", 76 | "mkdirp": "^0.5.1", 77 | "rfg-api": "^0.2.0", 78 | "mozjpeg": "^4.1.1", 79 | "pngquant-bin": "^3.0.0", 80 | "uglify-js": "^2.6.1", 81 | "brfs": "^1.2.0",{{#is framework 'bigwheel'}} 82 | "browserify-shim": "^3.8.0",{{/is}} 83 | "browserify": "^14.1.0"{{#if useES6}}, 84 | "babelify": "^7.2.0", 85 | "babel-runtime": "^5.8.34", 86 | "babel-preset-es2015": "^6.3.13"{{#is framework 'react'}}, 87 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 88 | "babel-plugin-transform-object-assign": "^6.22.0", 89 | "babel-preset-react": "^6.5.0", 90 | "babel-preset-react-optimize": "^1.0.1", 91 | "babel-preset-stage-1": "^6.5.0", 92 | "stringify": "3.2.0"{{/is}}{{/if}} 93 | }, 94 | "browserify": { 95 | "transform": [{{#is framework 'bigwheel'}} 96 | "browserify-shim", 97 | "brfs",{{/is}}{{#if useES6}} 98 | "babelify",{{/if}} 99 | "envify"{{#is framework 'react'}}, 100 | ["stringify", {"extensions": [".svg"], "minify": true}]{{/is}} 101 | ] 102 | }{{#is framework 'bigwheel'}}, 103 | "browserify-shim": { 104 | 105 | }, 106 | "browser": {}{{/is}} 107 | } 108 | -------------------------------------------------------------------------------- /templates/base/raw-assets/fonts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Experience-Monks/generator-jam3/c8c0f674245b4a2475de235e3cf2ddaa65b4c2f8/templates/base/raw-assets/fonts/.gitkeep -------------------------------------------------------------------------------- /templates/base/raw-assets/json/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Experience-Monks/generator-jam3/c8c0f674245b4a2475de235e3cf2ddaa65b4c2f8/templates/base/raw-assets/json/.gitkeep -------------------------------------------------------------------------------- /templates/base/raw-assets/json/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Markup Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": [ 16 | "GML", 17 | "XML" 18 | ] 19 | }, 20 | "GlossSee": "markup" 21 | } 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /templates/base/raw-assets/sounds/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Experience-Monks/generator-jam3/c8c0f674245b4a2475de235e3cf2ddaa65b4c2f8/templates/base/raw-assets/sounds/.gitkeep -------------------------------------------------------------------------------- /templates/base/raw-assets/sounds/button-click.mp3: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:faf7241be50cec4c30b657b004b15829c5cbbe48db6f8fe881132e81efec7ae3 3 | size 1024 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/sounds/button-rollover.mp3: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:450cedff59402d82873eb2e80a24a0db7392fb5a1f0ebd4c96c6c30d34742f60 3 | size 1024 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/sounds/button-sprite.mp3: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e0544ca263dafe313ba6058a3c2116d78b46d9b39c411c815499724a5ef477bf 3 | size 1024 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/captions-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/captions-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/close.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/enter-fullscreen.svg: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/muted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/pause.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/rotate.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/base/raw-assets/svg/unmuted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/base/raw-assets/videos/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Experience-Monks/generator-jam3/c8c0f674245b4a2475de235e3cf2ddaa65b4c2f8/templates/base/raw-assets/videos/.gitkeep -------------------------------------------------------------------------------- /templates/base/raw-assets/videos/captions-test.vtt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f6182866c9eeb4f353eb6155588d98ab0fced3a6892a628fd3db267a18a052cc 3 | size 1024 4 | -------------------------------------------------------------------------------- /templates/base/src/data/sounds.js: -------------------------------------------------------------------------------- 1 | const path = process.env.ASSET_PATH + 'sounds/'; 2 | 3 | export default { 4 | 'button-rollover': `${path}button-rollover.mp3`, 5 | 'button-click': `${path}button-click.mp3`, 6 | }; 7 | -------------------------------------------------------------------------------- /templates/base/src/util/audio.js: -------------------------------------------------------------------------------- 1 | import Howl from 'howler'; 2 | import soundModel from '../data/sounds'; 3 | 4 | let soundMap = {}; 5 | 6 | function isObject(val) { 7 | return (typeof val === 'object') && !Array.isArray(val) && val !== null; 8 | } 9 | 10 | function parseData(val) { 11 | if (typeof val === 'string' || val instanceof String) { 12 | return [val]; 13 | } else if (Array.isArray(val)) { 14 | return val; 15 | } else if (isObject(val)) { 16 | if (val.src) { 17 | return parseData(val.src); 18 | } else { 19 | console.error('"soundModel" item missing field "src"', val); 20 | return ['']; 21 | } 22 | } else { 23 | console.error('Unrecognized type for "soundModel" item', val); 24 | return ['']; 25 | } 26 | } 27 | 28 | function getSprite(val) { 29 | let sprite; 30 | Object.keys(soundMap).forEach(key => { 31 | if (soundMap[key]._sprite[val]) { 32 | sprite = soundMap[key]; 33 | } 34 | }); 35 | return sprite; 36 | } 37 | 38 | function setMap(model) { 39 | Object.keys(model).forEach(item => { 40 | let data = model[item]; 41 | const defaults = { 42 | preload: false, 43 | }; 44 | const opts = Object.assign(defaults, isObject(data) ? data : {}, {src: parseData(data)}); 45 | soundMap[item] = new Howl.Howl(opts); 46 | }); 47 | } 48 | 49 | class AudioStore { 50 | constructor() { 51 | setMap(soundModel); 52 | } 53 | 54 | play = (id) => { 55 | if (Object.keys(soundMap).length === 0) { 56 | console.error('Can\'t play any sounds as "soundMap" is empty', soundMap); 57 | return; 58 | } 59 | 60 | let curr = soundMap[id]; 61 | let isSprite; 62 | 63 | if (!curr) { 64 | curr = getSprite(id); 65 | isSprite = true; 66 | } 67 | 68 | if (curr) { 69 | (curr.state() === 'unloaded') && curr.load(); 70 | isSprite ? curr.play(id) : curr.play(); 71 | } else { 72 | console.error(`"${id}" doesn't exist in sound map.`, soundMap); 73 | } 74 | }; 75 | 76 | set extraData(data) { 77 | setMap(data); 78 | } 79 | 80 | get sounds() { 81 | return soundMap; 82 | } 83 | } 84 | 85 | const instance = new AudioStore(); 86 | Object.freeze(instance); 87 | 88 | export default instance; 89 | -------------------------------------------------------------------------------- /templates/base/src/util/detect.js: -------------------------------------------------------------------------------- 1 | import browser from 'bowser'; 2 | import MobileDetect from 'mobile-detect'; 3 | 4 | const ua = navigator.userAgent.toLowerCase(); 5 | const md = new MobileDetect(ua); 6 | const bots = ['facebookexternalhit', 'linkedinbot', 'google (+https://developers.google.com/+/web/snippet/)', 'facebot', 'https://developers.google.com/+/web/snippet/', 'twitterbot', 'tumblr', 'googlebot']; 7 | 8 | function checkBot() { 9 | let isBot = false; 10 | bots.map(function(cur) { 11 | if (ua.toLowerCase().indexOf(cur) > -1) isBot = true; 12 | }); 13 | return isBot; 14 | } 15 | 16 | function checkFacebook() { 17 | return (ua.indexOf('fban') > -1) || (ua.indexOf('fbav') > -1); 18 | } 19 | 20 | function checkTwitter() { 21 | return (ua.indexOf('twitter') > -1); 22 | } 23 | 24 | function checkInstagram() { 25 | return (ua.indexOf('instagram') > -1); 26 | } 27 | 28 | function checkPinterest() { 29 | return (ua.indexOf('pinterest') > -1); 30 | } 31 | 32 | function checkInAppBrowser() { 33 | return checkFacebook() || checkTwitter() || checkInstagram() || checkPinterest(); 34 | } 35 | 36 | function checkInAppBrowserVersion() { 37 | // take iOS version for Apple 38 | if (browser.ios) { 39 | const OSApp = ua.match(/OS \s*(\d+)/i); 40 | return OSApp[1]; 41 | } 42 | 43 | // take Chrome version for Android 44 | const FBApp = ua.match(/(chrome)\/?\s*(\d+)/i); 45 | if (FBApp && FBApp[1] === 'chrome') { 46 | return FBApp[2]; 47 | } 48 | 49 | return 9999; 50 | } 51 | 52 | function checkOS() { 53 | if (browser.mac) return 'mac'; 54 | if (browser.windows) return 'windows'; 55 | if (browser.windowsphone) return 'windowsphone'; 56 | if (browser.linux) return 'linux'; 57 | if (browser.chromeos) return 'chromeos'; 58 | if (browser.android) return 'android'; 59 | if (browser.ios) return 'ios'; 60 | if (browser.blackberry) return 'blackberry'; 61 | if (browser.firefoxos) return 'firefoxos'; 62 | } 63 | 64 | const checkDevice = function() { 65 | let device; 66 | if (browser.mobile) { 67 | device = 'phone'; 68 | } else if (browser.tablet) { 69 | device = 'tablet'; 70 | } else { 71 | device = 'desktop'; 72 | } 73 | return device; 74 | }; 75 | 76 | const checkVendor = function() { 77 | return (navigator.vendor) ? navigator.vendor.toLowerCase() : ''; 78 | }; 79 | 80 | const checkDevicePixelRatio = function() { 81 | return window.devicePixelRatio; 82 | }; 83 | 84 | const checkManufacturer = function() { 85 | let man = 'unknown'; 86 | if (md.phone()) { 87 | man = md.phone(); 88 | } else if (md.tablet()) { 89 | man = md.tablet(); 90 | } 91 | return man.toLowerCase(); 92 | }; 93 | 94 | const getClasses = function() { 95 | const classes = [ 96 | browser.mobile || browser.tablet ? 'mobile' : '', 97 | checkDevice(), 98 | 'x' + checkDevicePixelRatio(), 99 | browser.name.toLocaleLowerCase(), 100 | 'v' + browser.version, 101 | ]; 102 | if (md.mobile()) classes.push(checkManufacturer()); 103 | return classes.filter(cur => Boolean(cur)); 104 | }; 105 | 106 | module.exports = { 107 | isBot: checkBot(), 108 | isFacebook: checkFacebook(), 109 | isTwitter: checkTwitter(), 110 | isInstagram: checkInstagram(), 111 | isPinterest: checkPinterest(), 112 | isInAppBrowser: checkInAppBrowser(), 113 | inAppBrowserVersion: checkInAppBrowserVersion(), 114 | device: checkDevice(), 115 | vendor: checkVendor(), 116 | os: checkOS(), 117 | osVersion: browser.osversion, 118 | browser: browser.name.toLocaleLowerCase(), 119 | browserVersion: browser.version, 120 | devicePixelRatio: checkDevicePixelRatio(), 121 | classes: getClasses(), 122 | isMobile: browser.mobile || browser.tablet, 123 | isPhone: browser.mobile, 124 | isTablet: browser.tablet, 125 | isDesktop: !browser.mobile && !browser.tablet, 126 | isChrome: browser.chrome, 127 | isIE: browser.msie, 128 | isEdge: browser.msedge, 129 | isFirefox: browser.firefox, 130 | isSafari: browser.safari, 131 | isOpera: browser.opera, 132 | md: md, 133 | bowser: browser, 134 | get orientation() { 135 | if (window.screen) { 136 | const orientation = window.screen.orientation || window.screen.mozOrientation || window.screen.msOrientation; 137 | if (orientation && orientation.type) { 138 | return orientation.type.split('-', 1)[0]; 139 | } 140 | } 141 | const w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); 142 | const h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); 143 | if (w < h) { 144 | return 'portrait'; 145 | } else { 146 | return 'landscape'; 147 | } 148 | } 149 | }; 150 | -------------------------------------------------------------------------------- /templates/base/src/util/seconds-to-minutes.js: -------------------------------------------------------------------------------- 1 | export default function(totalSeconds) { 2 | const totalSecondsFloat = parseFloat(totalSeconds); 3 | let minutes = Math.floor(totalSecondsFloat / 60); 4 | let seconds = Math.round(totalSecondsFloat - (minutes * 60)); 5 | 6 | if (minutes < 10) { 7 | minutes = `0${minutes}`; 8 | } 9 | 10 | if (seconds < 10) { 11 | seconds = `0${seconds}`; 12 | } 13 | 14 | return `${minutes}:${seconds}`; 15 | } 16 | -------------------------------------------------------------------------------- /templates/base/src/util/stats.js: -------------------------------------------------------------------------------- 1 | var Stats = require('stats.js'); 2 | module.exports = function() { 3 | var stats = new Stats(); 4 | stats.domElement.style.cssText = 'position:fixed;right:0;bottom:100px;z-index:10000'; 5 | document.body.appendChild(stats.domElement); 6 | var loop = function() { 7 | stats.update(); 8 | requestAnimationFrame(loop); 9 | }; 10 | requestAnimationFrame(loop); 11 | }; -------------------------------------------------------------------------------- /templates/base/static/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | 4 | # Force HTTPS 5 | # RewriteCond %{HTTP:X-Forwarded-Proto} =http [OR] 6 | # RewriteCond %{HTTP:X-Forwarded-Proto} ="" 7 | # RewriteCond %{HTTPS} !=on 8 | # RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] 9 | 10 | #Serve gzip compressed CSS files if they exist and the client accepts gzip. 11 | RewriteCond %{HTTP:Accept-encoding} gzip 12 | RewriteCond %{REQUEST_FILENAME}\.gz -s 13 | RewriteRule ^(.*)\.css $1\.css\.gz [QSA] 14 | 15 | # Serve gzip compressed JS files if they exist and the client accepts gzip. 16 | RewriteCond %{HTTP:Accept-encoding} gzip 17 | RewriteCond %{REQUEST_FILENAME}\.gz -s 18 | RewriteRule ^(.*)\.js $1\.js\.gz [QSA] 19 | 20 | 21 | # Serve correct content types, and prevent mod_deflate double gzip. 22 | RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=is_gzip:1] 23 | RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=is_gzip:1] 24 | Header set Content-Encoding "gzip" env=is_gzip 25 | 26 | 27 | # Push state support 28 | RewriteCond %{REQUEST_FILENAME} !-f 29 | RewriteCond %{REQUEST_FILENAME} !-d 30 | RewriteCond %{REQUEST_URI} !index 31 | RewriteRule (.*) index.{{#is backend 'php'}}php?u=$1{{else}}html{{/is}} [L,QSA] 32 | 33 | 34 | Header append X-FRAME-OPTIONS "SAMEORIGIN" 35 | 36 | DirectoryIndex index.php index.html 37 | -------------------------------------------------------------------------------- /templates/base/static/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | {{author}} - {{email}} 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | -------------------------------------------------------------------------------- /templates/base/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{#is backend "none"}}{{#if unsupported}} 4 | {{/if}}{{/is}} 7 | {{repoName}} 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {{#is backend "none"}}{{#if unsupported}} 35 | 36 | {{/if}}{{/is}} 37 | 38 | 39 | \{{#if vendor}} 40 | \{{/if}} 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /templates/base/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /cgi-bin/ 3 | Disallow: /tmp/ -------------------------------------------------------------------------------- /templates/bigwheel/src/framework/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var bigwheel = require('bigwheel'); 3 | var analytics = require('bw-analytics'); 4 | var model = require('../model'); 5 | module.exports = analytics(model.settings.UA, bigwheel(function(done) { 6 | done({ 7 | initSection: require('../sections/Preloader/{{#if sectionNames}}Preloader{{else}}index{{/if}}.js'), 8 | routes: require('./routes') 9 | }); 10 | })); 11 | -------------------------------------------------------------------------------- /templates/bigwheel/src/framework/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | 'pushState': true, 4 | '/': require('../sections/Landing/{{#if sectionNames}}Landing{{else}}index{{/if}}.js'), 5 | '404': '/' 6 | }; 7 | -------------------------------------------------------------------------------- /templates/bigwheel/src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var domready = require('domready'); 3 | var framework = require('./framework'); 4 | 5 | domready(function() { 6 | framework.init(); 7 | }); 8 | -------------------------------------------------------------------------------- /templates/bigwheel/src/model/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | settings: { 4 | UA: '', 5 | ASSET_PATH: process.env.ASSET_PATH, 6 | NODE_ENV: process.env.NODE_ENV 7 | }, 8 | '/': {} 9 | }; 10 | -------------------------------------------------------------------------------- /templates/react/src/components/HamburgerButton/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import animate from 'gsap'; 4 | 5 | const STATES = { 6 | idle: 'idle', 7 | close: 'close', 8 | back: 'back', 9 | }; 10 | 11 | export default class HamburgerButton extends PureComponent { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | buttonState: props.state, 16 | isMouseOver: props.isMouseOver, 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | this.bars = [...this.container.querySelectorAll('.bar')]; 22 | } 23 | 24 | componentWillReceiveProps(nextProps) { 25 | if (nextProps.buttonState !== this.props.buttonState) { 26 | this.setState({buttonState: nextProps.buttonState}); 27 | } 28 | 29 | if (nextProps.isMouseOver !== this.props.isMouseOver) { 30 | this.setState({isMouseOver: nextProps.isMouseOver}); 31 | } 32 | } 33 | 34 | componentDidUpdate(prevProps, prevState) { 35 | if (prevState.buttonState !== this.state.buttonState) { 36 | if (this.state.buttonState === STATES.close) { 37 | this.goToCloseState(); 38 | } else if (this.state.buttonState === STATES.back) { 39 | this.goToBackState(); 40 | } else { 41 | this.goToIdleState(); 42 | } 43 | } 44 | } 45 | 46 | checkIsIdleState = () => { 47 | return (this.state.buttonState !== STATES.close && this.state.buttonState !== STATES.back); 48 | }; 49 | 50 | goToCloseState = () => { 51 | const duration = 0.2; 52 | animate.killTweensOf(this.bars); 53 | animate.to(this.bars[0], duration, {rotation: 45, x: 1, y: 0}); 54 | animate.to(this.bars[1], duration, {scaleX: 0, autoAlpha: 0}); 55 | animate.to(this.bars[2], duration, {rotation: -45, y: 0}); 56 | }; 57 | 58 | goToBackState = () => { 59 | const duration = 0.2; 60 | animate.killTweensOf(this.bars); 61 | animate.to(this.bars[0], duration, {x: -1, y: 10, rotation: -45, scaleX: 0.8}); 62 | animate.to(this.bars[1], duration, {scaleX: 0, autoAlpha: 0}); 63 | animate.to(this.bars[2], duration, {x: 1, y: -9, rotation: 45, scaleX: 0.8}); 64 | }; 65 | 66 | goToIdleState = () => { 67 | animate.killTweensOf(this.bars); 68 | animate.to(this.bars, 0.2, {x: 0, y: 0, rotation: 0, scaleX: 1, autoAlpha: 1}); 69 | }; 70 | 71 | handleClick = (e) => { 72 | const buttonState = this.state.buttonState === STATES.idle ? this.props.activeState : STATES.idle; 73 | this.setState({buttonState}); 74 | this.props.onClick(buttonState); 75 | }; 76 | 77 | handleMouseEnter = (e) => { 78 | if (this.checkIsIdleState()) { 79 | animate.killTweensOf(this.bars); 80 | animate.to([this.bars[1], this.bars[3]], 0.2, {scaleX: 0.8}); 81 | } 82 | this.setState({isMouseOver: true}); 83 | this.props.onMouseEnter(); 84 | }; 85 | 86 | handleMouseLeave = (e) => { 87 | this.checkIsIdleState() && this.goToIdleState(); 88 | this.setState({isMouseOver: false}); 89 | this.props.onMouseLeave(); 90 | }; 91 | 92 | render() { 93 | const props = this.props; 94 | const state = this.state; 95 | 96 | const style = Object.assign({}, this.props.style); 97 | const buttonClass = `HamburgerButton ${props.className}`; 98 | 99 | return ( 100 | 115 | ); 116 | } 117 | } 118 | 119 | HamburgerButton.propTypes = { 120 | className: PropTypes.string, 121 | style: PropTypes.object, 122 | tabIndex: PropTypes.number, 123 | state: PropTypes.string, 124 | activeState: PropTypes.string, 125 | isMouseOver: PropTypes.bool, 126 | onClick: PropTypes.func, 127 | onMouseEnter: PropTypes.func, 128 | onMouseLeave: PropTypes.func, 129 | }; 130 | 131 | HamburgerButton.defaultProps = { 132 | className: '', 133 | style: {}, 134 | tabIndex: 0, 135 | state: STATES.idle, 136 | activeState: STATES.close, 137 | isMouseOver: false, 138 | onClick: () => f => f, 139 | onMouseEnter: f => f, 140 | onMouseLeave: f => f, 141 | }; 142 | -------------------------------------------------------------------------------- /templates/react/src/components/HamburgerButton/style.{{css}}: -------------------------------------------------------------------------------- 1 | .HamburgerButton { 2 | cursor: pointer; 3 | 4 | .bars-container { 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | position: relative; 9 | width: 25px; 10 | height: 20px; 11 | 12 | > .bar { 13 | width: 100%; 14 | height: 2px; 15 | background: #000; 16 | transform-origin: top left; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /templates/react/src/components/MobileFullscreenVideo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import fullScreen from 'fullscreen-handler'; 4 | 5 | import detect from '../../util/detect'; 6 | 7 | const isIOS = detect.isIOS; 8 | 9 | class MobileFullscreenVideo extends React.PureComponent { 10 | constructor(props) { 11 | super(props); 12 | } 13 | 14 | componentDidMount() { 15 | this.fullScreen = fullScreen(this.video, 16 | this.handleFsEnter, 17 | this.handleFsExit, 18 | ); 19 | } 20 | 21 | componentWillUnmount() { 22 | this.fullScreen.destroy(); 23 | } 24 | 25 | play = () => { 26 | process.nextTick(() => { 27 | isIOS && this.props.onOpen(); 28 | this.fullScreen.enter(); 29 | this.video.play(); 30 | }); 31 | }; 32 | 33 | pause = () => { 34 | this.video.pause(); 35 | }; 36 | 37 | getVideoElement = () => { 38 | return this.video; 39 | }; 40 | 41 | handleFsEnter = () => { 42 | !isIOS && this.props.onOpen(); 43 | }; 44 | 45 | handleFsExit = () => { 46 | this.pause(); 47 | this.props.onClose(); 48 | }; 49 | 50 | handleEnded = () => { 51 | this.fullScreen.exit(); 52 | }; 53 | 54 | render() { 55 | const style = Object.assign({}, { 56 | position: 'fixed', 57 | width: 0, 58 | height: 0, 59 | top: window.innerHeight / 2, 60 | left: window.innerWidth / 2, 61 | }, this.props.style); 62 | 63 | return ( 64 |