├── .gitattributes ├── app ├── robots.txt ├── styles │ └── main.css ├── test │ ├── tests.html │ └── index.html ├── scripts │ └── app.js ├── index.html ├── elements │ ├── questions │ │ ├── answer-form.html │ │ ├── answer.html │ │ ├── view.html │ │ └── list.html │ └── app.html ├── 404.html ├── favicon.ico └── .htaccess ├── .bowerrc ├── .yo-rc.json ├── .gitignore ├── bower.json ├── security.json ├── .editorconfig ├── .jshintrc ├── README.md ├── package.json ├── LICENSE └── Gruntfile.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-polymer": { 3 | "includeSass": false 4 | } 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | test/temp 4 | .sass-cache 5 | app/bower_components 6 | .tmp 7 | test/bower_components/ 8 | .divshot-cache -------------------------------------------------------------------------------- /app/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fafafa; 3 | font-family: RobotoDraft, "Helvetica Neue", Helvetica, Arial; 4 | color: #333; 5 | } 6 | 7 | html /deep/ paper-action-dialog paper-input { 8 | width: 100%; 9 | } 10 | 11 | html /deep/ paper-action-dialog paper-autogrow-textarea textarea { 12 | font-size: 14px; 13 | } -------------------------------------------------------------------------------- /app/test/tests.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | (function(document) { 2 | 'use strict'; 3 | 4 | document.addEventListener('polymer-ready', function() { 5 | // Perform some behaviour 6 | console.log('Polymer is ready to rock!'); 7 | }); 8 | 9 | // wrap document so it plays nice with other libraries 10 | // http://www.polymer-project.org/platform/shadow-dom.html#wrappers 11 | })(wrap(document)); 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polymer-firebase-qa", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "core-elements": "Polymer/core-elements#~0.5.0", 6 | "paper-elements": "Polymer/paper-elements#~0.5.0", 7 | "pvc-globals": "Divshot/pvc-globals", 8 | "firebase-element": "Polymer/firebase-element#~0.5.0", 9 | "app-router": "~2.0.2" 10 | }, 11 | "devDependencies": { 12 | "polymer-test-tools": "Polymer/polymer-test-tools#^0.4.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /security.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "questions": { 4 | ".read": true, 5 | "$question_id": { 6 | ".write": "(!data.exists() && newData.exists() && auth != null) || (data.child('user/uid').val() == auth.uid)", 7 | "answers": { 8 | "$answer_id": { 9 | ".write": "(!data.exists() && newData.exists() && auth != null) || (data.child('user/uid').val() == auth.uid)" 10 | } 11 | } 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /app/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Core Elements Test Runner 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "jquery": true, 21 | "globals": { 22 | "wrap": true, 23 | "unwrap": true, 24 | "Polymer": true, 25 | "Platform": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polymer + Firebase Q&A 2 | 3 | A simple Q&A app built with Polymer and Firebase. Covers routing, authentication, material design, and CRUD operations. 4 | 5 | - [Live Demo](http://polymer-qa.divshot.io/) 6 | - [Video Walkthrough](https://www.youtube.com/watch?v=gErWcBdd-F8) 7 | 8 | ## Running Locally 9 | 10 | Create a new app in Firebase. Set up a new GitHub application and [configure authentication](https://www.firebase.com/docs/web/guide/login/github.html) in Forge. Run the following command in your terminal: 11 | 12 | npm install && bower install 13 | 14 | Edit `/app/elements/app.html` and change `` to your Firebase URL: 15 | 16 | ```javascript 17 | Polymer({ 18 | ready: function() { 19 | this.globals.firebase = ''; 20 | }, 21 | ``` 22 | 23 | Run the server locally with Grunt: 24 | 25 | grunt serve -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "divshot-qa-walkthrough", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "grunt": "~0.4.1", 7 | "grunt-contrib-copy": "~0.5.0", 8 | "grunt-autoprefixer": "^1.0.0", 9 | "grunt-minify-html": "^0.1.0", 10 | "grunt-contrib-concat": "~0.5.0", 11 | "grunt-contrib-uglify": "~0.5.0", 12 | "grunt-contrib-jshint": "~0.10.0", 13 | "grunt-contrib-cssmin": "~0.10.0", 14 | "grunt-contrib-connect": "0.8.0", 15 | "grunt-contrib-clean": "0.5.0", 16 | "grunt-contrib-imagemin": "0.7.1", 17 | "grunt-contrib-watch": "~0.6.1", 18 | "grunt-usemin": "~2.3.0", 19 | "grunt-pagespeed": "~0.3.0", 20 | "grunt-rev": "~0.1.0", 21 | "grunt-open": "~0.2.3", 22 | "grunt-vulcanize": "~0.3.0", 23 | "connect-livereload": "~0.4.0", 24 | "time-grunt": "~0.4.0", 25 | "load-grunt-tasks": "~0.6.0", 26 | "jshint-stylish": "~0.4.0" 27 | }, 28 | "engines": { 29 | "node": ">=0.10.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Firebase Q&A 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Divshot, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /app/elements/questions/answer-form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 32 | 54 | -------------------------------------------------------------------------------- /app/elements/questions/answer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 60 | 81 | -------------------------------------------------------------------------------- /app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page Not Found :( 7 | 123 | 124 | 125 | 126 |
127 |

Not found 128 | :( 129 |

130 |

Sorry, but the page you were trying to view does not exist.

131 |

It looks like this was the result of either:

132 |
    133 |
  • a mistyped address
  • 134 |
  • an out-of-date link
  • 135 |
136 | 140 | 141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /app/elements/questions/view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 78 | 104 | -------------------------------------------------------------------------------- /app/elements/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 83 | 107 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- 1 |   �( @   -2Op"=p�Jt��Jt��b���������������������������������������������������b���Jt��Jt��"=p�Op-2O`O�O�O�O�O�O�O� $\�Jt��������������v���v���������������Jt�� $\�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� ;n�s���>���>���>���>���s��� ;n�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� $\�]���^n��^n��]��� $\�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O�O�n�*��*��n�O�O�O�O�O�O�O�O�O�O�O�  O�O�O�O�O�O�O�O�O�O�O�5>Y�5>Y�O�O�O�O�O�O�O�O�O�O�O�  -2O�O�O�O�O�O�O�O�O�O�&6e�&6e�O�O�O�O�O�O�O�O�O�O�-25r�4���E��� $\�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O� $\�E���4���5r�5r�E���M���M���v���0\��O�O�O�O�O�O�O� $\� $\�O�O�O�O�O�O�O�0\��v���M���M���E���5r�)��p&��p��&��������������b���Jt��Jt��Jt��0\��#i��.r��.r��#i��0\��Jt��Jt��Jt��b���������������&��p��&��)��p4���&��-���_������������������]���]�������7���p�����������p���7�������]���]�������������������_��-���-���4���qֈp��p��p����������������������p���7���#i��p�����������p���#i��7���p�����������������������p��&��-���qֈ8��(p��p��I���v���v���]���7���n���v���p���#i��]���v���v���]���#i��p���v���n���7���]���v���v���I���-���-���8��(;��`-���M���7���7���7���.r��R��E��R��E��7���7���7���7���E��R��E��R��.r��7���7���7���M���M���;��`���������������������������z��������������������������� 2 | �  ��� 3 | � 9� 9� 9� 9� 9� 9� 9� 9� 4 |  �n�n� 5 |  � 9� 9� 9� 9� 9� 9� 9� 9� 6 | ����*�x*��*��*��*��*��*��*��n�&��#��&��&��n�*��*��*��*��*��*��*��*�x*ݟ*��*��*��*��*��*��!��#��&��#��&��*��!��!��*��*��*��*��*��*��*ݟ*ݿ*��*��*��*��*��*��n�*��*�� 9� 9�*��*���*��*��*��*��*��*��*ݿ*��*��*��*��*��*��*��!��#��&��&��&��*��#��!��*��*��*��*��*��*��*��  ��������I�&��&��&��&��I���������  U��������� 7 |  �n�n� 8 |  ����������-2z����������������������z������������������������ 9 | ����������������������� 10 | ������������������������� 11 | ������������������������-2����������������������U�������������������z5r������������������-25r�U�����������z  ������������������������������?��� -------------------------------------------------------------------------------- /app/elements/questions/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 104 | 146 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var LIVERELOAD_PORT = 35729; 3 | var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); 4 | var mountFolder = function (connect, dir) { 5 | return connect.static(require('path').resolve(dir)); 6 | }; 7 | 8 | // # Globbing 9 | // for performance reasons we're only matching one level down: 10 | // 'test/spec/{,*/}*.js' 11 | // use this if you want to match all subfolders: 12 | // 'test/spec/**/*.js' 13 | 14 | module.exports = function (grunt) { 15 | // show elapsed time at the end 16 | require('time-grunt')(grunt); 17 | // load all grunt tasks 18 | require('load-grunt-tasks')(grunt); 19 | 20 | // configurable paths 21 | var yeomanConfig = { 22 | app: 'app', 23 | dist: 'dist' 24 | }; 25 | 26 | grunt.initConfig({ 27 | yeoman: yeomanConfig, 28 | watch: { 29 | options: { 30 | nospawn: true, 31 | livereload: { liveCSS: false } 32 | }, 33 | livereload: { 34 | options: { 35 | livereload: true 36 | }, 37 | files: [ 38 | '<%= yeoman.app %>/*.html', 39 | '<%= yeoman.app %>/elements/{,*/}*.html', 40 | '{.tmp,<%= yeoman.app %>}/elements/{,*/}*.css', 41 | '{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css', 42 | '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js', 43 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}' 44 | ] 45 | }, 46 | js: { 47 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], 48 | tasks: ['jshint'] 49 | }, 50 | styles: { 51 | files: [ 52 | '<%= yeoman.app %>/styles/{,*/}*.css', 53 | '<%= yeoman.app %>/elements/{,*/}*.css' 54 | ], 55 | tasks: ['copy:styles', 'autoprefixer:server'] 56 | } 57 | }, 58 | autoprefixer: { 59 | options: { 60 | browsers: ['last 2 versions'] 61 | }, 62 | server: { 63 | files: [{ 64 | expand: true, 65 | cwd: '.tmp', 66 | src: '**/*.css', 67 | dest: '.tmp' 68 | }] 69 | }, 70 | dist: { 71 | files: [{ 72 | expand: true, 73 | cwd: '<%= yeoman.dist %>', 74 | src: ['**/*.css', '!bower_components/**/*.css'], 75 | dest: '<%= yeoman.dist %>' 76 | }] 77 | } 78 | }, 79 | connect: { 80 | options: { 81 | port: 9000, 82 | // change this to '0.0.0.0' to access the server from outside 83 | hostname: 'localhost' 84 | }, 85 | livereload: { 86 | options: { 87 | middleware: function (connect) { 88 | return [ 89 | lrSnippet, 90 | mountFolder(connect, '.tmp'), 91 | mountFolder(connect, yeomanConfig.app) 92 | ]; 93 | } 94 | } 95 | }, 96 | test: { 97 | options: { 98 | open: { 99 | target: 'http://localhost:<%= connect.options.port %>/test' 100 | }, 101 | middleware: function (connect) { 102 | return [ 103 | mountFolder(connect, yeomanConfig.app) 104 | ]; 105 | }, 106 | keepalive: true 107 | } 108 | }, 109 | dist: { 110 | options: { 111 | middleware: function (connect) { 112 | return [ 113 | mountFolder(connect, yeomanConfig.dist) 114 | ]; 115 | } 116 | } 117 | } 118 | }, 119 | open: { 120 | server: { 121 | path: 'http://localhost:<%= connect.options.port %>' 122 | } 123 | }, 124 | clean: { 125 | dist: ['.tmp', '<%= yeoman.dist %>/*'], 126 | server: '.tmp' 127 | }, 128 | jshint: { 129 | options: { 130 | jshintrc: '.jshintrc', 131 | reporter: require('jshint-stylish') 132 | }, 133 | all: [ 134 | '<%= yeoman.app %>/scripts/{,*/}*.js', 135 | '!<%= yeoman.app %>/scripts/vendor/*', 136 | 'test/spec/{,*/}*.js' 137 | ] 138 | }, 139 | useminPrepare: { 140 | html: '<%= yeoman.app %>/index.html', 141 | options: { 142 | dest: '<%= yeoman.dist %>' 143 | } 144 | }, 145 | usemin: { 146 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 147 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 148 | options: { 149 | dirs: ['<%= yeoman.dist %>'], 150 | blockReplacements: { 151 | vulcanized: function (block) { 152 | return ''; 153 | } 154 | } 155 | } 156 | }, 157 | vulcanize: { 158 | default: { 159 | options: { 160 | strip: true 161 | }, 162 | files: { 163 | '<%= yeoman.dist %>/elements/elements.vulcanized.html': [ 164 | '<%= yeoman.dist %>/elements/elements.html' 165 | ] 166 | } 167 | } 168 | }, 169 | imagemin: { 170 | dist: { 171 | files: [{ 172 | expand: true, 173 | cwd: '<%= yeoman.app %>/images', 174 | src: '{,*/}*.{png,jpg,jpeg}', 175 | dest: '<%= yeoman.dist %>/images' 176 | }] 177 | } 178 | }, 179 | cssmin: { 180 | main: { 181 | files: { 182 | '<%= yeoman.dist %>/styles/main.css': [ 183 | '.tmp/concat/styles/{,*/}*.css' 184 | ] 185 | } 186 | }, 187 | elements: { 188 | files: [{ 189 | expand: true, 190 | cwd: '.tmp/elements', 191 | src: '{,*/}*.css', 192 | dest: '<%= yeoman.dist %>/elements' 193 | }] 194 | } 195 | }, 196 | minifyHtml: { 197 | options: { 198 | quotes: true, 199 | empty: true 200 | }, 201 | app: { 202 | files: [{ 203 | expand: true, 204 | cwd: '<%= yeoman.dist %>', 205 | src: '*.html', 206 | dest: '<%= yeoman.dist %>' 207 | }] 208 | } 209 | }, 210 | copy: { 211 | dist: { 212 | files: [{ 213 | expand: true, 214 | dot: true, 215 | cwd: '<%= yeoman.app %>', 216 | dest: '<%= yeoman.dist %>', 217 | src: [ 218 | '*.{ico,txt}', 219 | '.htaccess', 220 | '*.html', 221 | 'elements/**', 222 | '!elements/**/*.css', 223 | 'images/{,*/}*.{webp,gif}', 224 | 'bower_components/**' 225 | ] 226 | }] 227 | }, 228 | styles: { 229 | files: [{ 230 | expand: true, 231 | cwd: '<%= yeoman.app %>', 232 | dest: '.tmp', 233 | src: ['{styles,elements}/{,*/}*.css'] 234 | }] 235 | } 236 | }, 237 | // See this tutorial if you'd like to run PageSpeed 238 | // against localhost: http://www.jamescryer.com/2014/06/12/grunt-pagespeed-and-ngrok-locally-testing/ 239 | pagespeed: { 240 | options: { 241 | // By default, we use the PageSpeed Insights 242 | // free (no API key) tier. You can use a Google 243 | // Developer API key if you have one. See 244 | // http://goo.gl/RkN0vE for info 245 | nokey: true 246 | }, 247 | // Update `url` below to the public URL for your site 248 | mobile: { 249 | options: { 250 | url: "https://developers.google.com/web/fundamentals/", 251 | locale: "en_GB", 252 | strategy: "mobile", 253 | threshold: 80 254 | } 255 | } 256 | } 257 | }); 258 | 259 | grunt.registerTask('server', function (target) { 260 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 261 | grunt.task.run(['serve:' + target]); 262 | }); 263 | 264 | grunt.registerTask('serve', function (target) { 265 | if (target === 'dist') { 266 | return grunt.task.run(['build', 'open', 'connect:dist:keepalive']); 267 | } 268 | 269 | grunt.task.run([ 270 | 'clean:server', 271 | 'copy:styles', 272 | 'autoprefixer:server', 273 | 'connect:livereload', 274 | 'open', 275 | 'watch' 276 | ]); 277 | }); 278 | 279 | grunt.registerTask('test', [ 280 | 'clean:server', 281 | 'connect:test' 282 | ]); 283 | 284 | grunt.registerTask('build', [ 285 | 'clean:dist', 286 | 'copy', 287 | 'useminPrepare', 288 | 'imagemin', 289 | 'concat', 290 | 'autoprefixer', 291 | 'uglify', 292 | 'cssmin', 293 | 'vulcanize', 294 | 'usemin', 295 | 'minifyHtml' 296 | ]); 297 | 298 | grunt.registerTask('default', [ 299 | 'jshint', 300 | // 'test' 301 | 'build' 302 | ]); 303 | }; 304 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache configuration file 2 | # httpd.apache.org/docs/2.2/mod/quickreference.html 3 | 4 | # Note .htaccess files are an overhead, this logic should be in your Apache 5 | # config if possible: httpd.apache.org/docs/2.2/howto/htaccess.html 6 | 7 | # Techniques in here adapted from all over, including: 8 | # Kroc Camen: camendesign.com/.htaccess 9 | # perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/ 10 | # Sample .htaccess file of CMS MODx: modxcms.com 11 | 12 | 13 | # ---------------------------------------------------------------------- 14 | # Better website experience for IE users 15 | # ---------------------------------------------------------------------- 16 | 17 | # Force the latest IE version, in various cases when it may fall back to IE7 mode 18 | # github.com/rails/rails/commit/123eb25#commitcomment-118920 19 | # Use ChromeFrame if it's installed for a better experience for the poor IE folk 20 | 21 | 22 | Header set X-UA-Compatible "IE=Edge,chrome=1" 23 | # mod_headers can't match by content-type, but we don't want to send this header on *everything*... 24 | 25 | Header unset X-UA-Compatible 26 | 27 | 28 | 29 | 30 | # ---------------------------------------------------------------------- 31 | # Cross-domain AJAX requests 32 | # ---------------------------------------------------------------------- 33 | 34 | # Serve cross-domain Ajax requests, disabled by default. 35 | # enable-cors.org 36 | # code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 37 | 38 | # 39 | # Header set Access-Control-Allow-Origin "*" 40 | # 41 | 42 | 43 | # ---------------------------------------------------------------------- 44 | # CORS-enabled images (@crossorigin) 45 | # ---------------------------------------------------------------------- 46 | 47 | # Send CORS headers if browsers request them; enabled by default for images. 48 | # developer.mozilla.org/en/CORS_Enabled_Image 49 | # blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 50 | # hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 51 | # wiki.mozilla.org/Security/Reviews/crossoriginAttribute 52 | 53 | 54 | 55 | # mod_headers, y u no match by Content-Type?! 56 | 57 | SetEnvIf Origin ":" IS_CORS 58 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 59 | 60 | 61 | 62 | 63 | 64 | # ---------------------------------------------------------------------- 65 | # Webfont access 66 | # ---------------------------------------------------------------------- 67 | 68 | # Allow access from all domains for webfonts. 69 | # Alternatively you could only whitelist your 70 | # subdomains like "subdomain.example.com". 71 | 72 | 73 | 74 | Header set Access-Control-Allow-Origin "*" 75 | 76 | 77 | 78 | 79 | # ---------------------------------------------------------------------- 80 | # Proper MIME type for all files 81 | # ---------------------------------------------------------------------- 82 | 83 | # JavaScript 84 | # Normalize to standard type (it's sniffed in IE anyways) 85 | # tools.ietf.org/html/rfc4329#section-7.2 86 | AddType application/javascript js jsonp 87 | AddType application/json json 88 | 89 | # Audio 90 | AddType audio/ogg oga ogg 91 | AddType audio/mp4 m4a f4a f4b 92 | 93 | # Video 94 | AddType video/ogg ogv 95 | AddType video/mp4 mp4 m4v f4v f4p 96 | AddType video/webm webm 97 | AddType video/x-flv flv 98 | 99 | # SVG 100 | # Required for svg webfonts on iPad 101 | # twitter.com/FontSquirrel/status/14855840545 102 | AddType image/svg+xml svg svgz 103 | AddEncoding gzip svgz 104 | 105 | # Webfonts 106 | AddType application/vnd.ms-fontobject eot 107 | AddType application/x-font-ttf ttf ttc 108 | AddType font/opentype otf 109 | AddType application/x-font-woff woff 110 | 111 | # Assorted types 112 | AddType image/x-icon ico 113 | AddType image/webp webp 114 | AddType text/cache-manifest appcache manifest 115 | AddType text/x-component htc 116 | AddType application/xml rss atom xml rdf 117 | AddType application/x-chrome-extension crx 118 | AddType application/x-opera-extension oex 119 | AddType application/x-xpinstall xpi 120 | AddType application/octet-stream safariextz 121 | AddType application/x-web-app-manifest+json webapp 122 | AddType text/x-vcard vcf 123 | AddType application/x-shockwave-flash swf 124 | AddType text/vtt vtt 125 | 126 | 127 | # ---------------------------------------------------------------------- 128 | # Allow concatenation from within specific js and css files 129 | # ---------------------------------------------------------------------- 130 | 131 | # e.g. Inside of script.combined.js you could have 132 | # 133 | # 134 | # and they would be included into this single file. 135 | 136 | # This is not in use in the boilerplate as it stands. You may 137 | # choose to use this technique if you do not have a build process. 138 | 139 | # 140 | # Options +Includes 141 | # AddOutputFilterByType INCLUDES application/javascript application/json 142 | # SetOutputFilter INCLUDES 143 | # 144 | 145 | # 146 | # Options +Includes 147 | # AddOutputFilterByType INCLUDES text/css 148 | # SetOutputFilter INCLUDES 149 | # 150 | 151 | 152 | # ---------------------------------------------------------------------- 153 | # Gzip compression 154 | # ---------------------------------------------------------------------- 155 | 156 | 157 | 158 | # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/ 159 | 160 | 161 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 162 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 163 | 164 | 165 | 166 | # HTML, TXT, CSS, JavaScript, JSON, XML, HTC: 167 | 168 | FilterDeclare COMPRESS 169 | FilterProvider COMPRESS DEFLATE resp=Content-Type $text/html 170 | FilterProvider COMPRESS DEFLATE resp=Content-Type $text/css 171 | FilterProvider COMPRESS DEFLATE resp=Content-Type $text/plain 172 | FilterProvider COMPRESS DEFLATE resp=Content-Type $text/xml 173 | FilterProvider COMPRESS DEFLATE resp=Content-Type $text/x-component 174 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/javascript 175 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/json 176 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/xml 177 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/xhtml+xml 178 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/rss+xml 179 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/atom+xml 180 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/vnd.ms-fontobject 181 | FilterProvider COMPRESS DEFLATE resp=Content-Type $image/svg+xml 182 | FilterProvider COMPRESS DEFLATE resp=Content-Type $image/x-icon 183 | FilterProvider COMPRESS DEFLATE resp=Content-Type $application/x-font-ttf 184 | FilterProvider COMPRESS DEFLATE resp=Content-Type $font/opentype 185 | FilterChain COMPRESS 186 | FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no 187 | 188 | 189 | 190 | # Legacy versions of Apache 191 | AddOutputFilterByType DEFLATE text/html text/plain text/css application/json 192 | AddOutputFilterByType DEFLATE application/javascript 193 | AddOutputFilterByType DEFLATE text/xml application/xml text/x-component 194 | AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml 195 | AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype 196 | 197 | 198 | 199 | 200 | 201 | # ---------------------------------------------------------------------- 202 | # Expires headers (for better cache control) 203 | # ---------------------------------------------------------------------- 204 | 205 | # These are pretty far-future expires headers. 206 | # They assume you control versioning with filename-based cache busting 207 | # Additionally, consider that outdated proxies may miscache 208 | # www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/ 209 | 210 | # If you don't use filenames to version, lower the CSS and JS to something like 211 | # "access plus 1 week". 212 | 213 | 214 | ExpiresActive on 215 | 216 | # Perhaps better to whitelist expires rules? Perhaps. 217 | ExpiresDefault "access plus 1 month" 218 | 219 | # cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5) 220 | ExpiresByType text/cache-manifest "access plus 0 seconds" 221 | 222 | # Your document html 223 | ExpiresByType text/html "access plus 0 seconds" 224 | 225 | # Data 226 | ExpiresByType text/xml "access plus 0 seconds" 227 | ExpiresByType application/xml "access plus 0 seconds" 228 | ExpiresByType application/json "access plus 0 seconds" 229 | 230 | # Feed 231 | ExpiresByType application/rss+xml "access plus 1 hour" 232 | ExpiresByType application/atom+xml "access plus 1 hour" 233 | 234 | # Favicon (cannot be renamed) 235 | ExpiresByType image/x-icon "access plus 1 week" 236 | 237 | # Media: images, video, audio 238 | ExpiresByType image/gif "access plus 1 month" 239 | ExpiresByType image/png "access plus 1 month" 240 | ExpiresByType image/jpeg "access plus 1 month" 241 | ExpiresByType video/ogg "access plus 1 month" 242 | ExpiresByType audio/ogg "access plus 1 month" 243 | ExpiresByType video/mp4 "access plus 1 month" 244 | ExpiresByType video/webm "access plus 1 month" 245 | 246 | # HTC files (css3pie) 247 | ExpiresByType text/x-component "access plus 1 month" 248 | 249 | # Webfonts 250 | ExpiresByType application/x-font-ttf "access plus 1 month" 251 | ExpiresByType font/opentype "access plus 1 month" 252 | ExpiresByType application/x-font-woff "access plus 1 month" 253 | ExpiresByType image/svg+xml "access plus 1 month" 254 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 255 | 256 | # CSS and JavaScript 257 | ExpiresByType text/css "access plus 1 year" 258 | ExpiresByType application/javascript "access plus 1 year" 259 | 260 | 261 | 262 | 263 | # ---------------------------------------------------------------------- 264 | # Prevent mobile network providers from modifying your site 265 | # ---------------------------------------------------------------------- 266 | 267 | # The following header prevents modification of your code over 3G on some 268 | # European providers. 269 | # This is the official 'bypass' suggested by O2 in the UK. 270 | 271 | # 272 | # Header set Cache-Control "no-transform" 273 | # 274 | 275 | 276 | # ---------------------------------------------------------------------- 277 | # ETag removal 278 | # ---------------------------------------------------------------------- 279 | 280 | # FileETag None is not enough for every server. 281 | 282 | Header unset ETag 283 | 284 | 285 | # Since we're sending far-future expires, we don't need ETags for 286 | # static content. 287 | # developer.yahoo.com/performance/rules.html#etags 288 | FileETag None 289 | 290 | 291 | # ---------------------------------------------------------------------- 292 | # Stop screen flicker in IE on CSS rollovers 293 | # ---------------------------------------------------------------------- 294 | 295 | # The following directives stop screen flicker in IE on CSS rollovers - in 296 | # combination with the "ExpiresByType" rules for images (see above). 297 | 298 | # BrowserMatch "MSIE" brokenvary=1 299 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 300 | # BrowserMatch "Opera" !brokenvary 301 | # SetEnvIf brokenvary 1 force-no-vary 302 | 303 | 304 | # ---------------------------------------------------------------------- 305 | # Set Keep-Alive Header 306 | # ---------------------------------------------------------------------- 307 | 308 | # Keep-Alive allows the server to send multiple requests through one 309 | # TCP-connection. Be aware of possible disadvantages of this setting. Turn on 310 | # if you serve a lot of static content. 311 | 312 | # 313 | # Header set Connection Keep-Alive 314 | # 315 | 316 | 317 | # ---------------------------------------------------------------------- 318 | # Cookie setting from iframes 319 | # ---------------------------------------------------------------------- 320 | 321 | # Allow cookies to be set from iframes (for IE only) 322 | # If needed, specify a path or regex in the Location directive. 323 | 324 | # 325 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 326 | # 327 | 328 | 329 | # ---------------------------------------------------------------------- 330 | # Start rewrite engine 331 | # ---------------------------------------------------------------------- 332 | 333 | # Turning on the rewrite engine is necessary for the following rules and 334 | # features. FollowSymLinks must be enabled for this to work. 335 | 336 | # Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN 337 | # If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where 338 | # 'foo' is your directory. 339 | 340 | # If your web host doesn't allow the FollowSymlinks option, you may need to 341 | # comment it out and use `Options +SymLinksOfOwnerMatch`, but be aware of the 342 | # performance impact: http://goo.gl/Mluzd 343 | 344 | 345 | Options +FollowSymlinks 346 | # Options +SymLinksIfOwnerMatch 347 | Options +FollowSymlinks 348 | RewriteEngine On 349 | # RewriteBase / 350 | 351 | 352 | 353 | # ---------------------------------------------------------------------- 354 | # Suppress or force the "www." at the beginning of URLs 355 | # ---------------------------------------------------------------------- 356 | 357 | # The same content should never be available under two different URLs - 358 | # especially not with and without "www." at the beginning, since this can cause 359 | # SEO problems (duplicate content). That's why you should choose one of the 360 | # alternatives and redirect the other one. 361 | 362 | # By default option 1 (no "www.") is activated. 363 | # no-www.org/faq.php?q=class_b 364 | 365 | # If you'd prefer to use option 2, just comment out all option 1 lines 366 | # and uncomment option 2. 367 | 368 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 369 | 370 | # ---------------------------------------------------------------------- 371 | 372 | # Option 1: 373 | # Rewrite "www.example.com -> example.com". 374 | 375 | 376 | RewriteCond %{HTTPS} !=on 377 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 378 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 379 | 380 | 381 | # ---------------------------------------------------------------------- 382 | 383 | # Option 2: 384 | # Rewrite "example.com -> www.example.com". 385 | # Be aware that the following rule might not be a good idea if you use "real" 386 | # subdomains for certain parts of your website. 387 | 388 | # 389 | # RewriteCond %{HTTPS} !=on 390 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 391 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 392 | # 393 | 394 | 395 | # ---------------------------------------------------------------------- 396 | # Built-in filename-based cache busting 397 | # ---------------------------------------------------------------------- 398 | 399 | # If you're not using the build script to manage your filename version revving, 400 | # you might want to consider enabling this, which will route requests for 401 | # /css/style.20110203.css to /css/style.css 402 | 403 | # To understand why this is important and a better idea than all.css?v1231, 404 | # read: github.com/h5bp/html5-boilerplate/wiki/cachebusting 405 | 406 | # 407 | # RewriteCond %{REQUEST_FILENAME} !-f 408 | # RewriteCond %{REQUEST_FILENAME} !-d 409 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 410 | # 411 | 412 | 413 | # ---------------------------------------------------------------------- 414 | # Prevent SSL cert warnings 415 | # ---------------------------------------------------------------------- 416 | 417 | # Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent 418 | # https://www.example.com when your cert only allows https://secure.example.com 419 | 420 | # 421 | # RewriteCond %{SERVER_PORT} !^443 422 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 423 | # 424 | 425 | 426 | # ---------------------------------------------------------------------- 427 | # Prevent 404 errors for non-existing redirected folders 428 | # ---------------------------------------------------------------------- 429 | 430 | # without -MultiViews, Apache will give a 404 for a rewrite if a folder of the 431 | # same name does not exist. 432 | # webmasterworld.com/apache/3808792.htm 433 | 434 | Options -MultiViews 435 | 436 | 437 | # ---------------------------------------------------------------------- 438 | # Custom 404 page 439 | # ---------------------------------------------------------------------- 440 | 441 | # You can add custom pages to handle 500 or 403 pretty easily, if you like. 442 | # If you are hosting your site in subdirectory, adjust this accordingly 443 | # e.g. ErrorDocument 404 /subdir/404.html 444 | ErrorDocument 404 /404.html 445 | 446 | 447 | # ---------------------------------------------------------------------- 448 | # UTF-8 encoding 449 | # ---------------------------------------------------------------------- 450 | 451 | # Use UTF-8 encoding for anything served text/plain or text/html 452 | AddDefaultCharset utf-8 453 | 454 | # Force UTF-8 for a number of file formats 455 | AddCharset utf-8 .atom .css .js .json .rss .vtt .xml 456 | 457 | 458 | # ---------------------------------------------------------------------- 459 | # A little more security 460 | # ---------------------------------------------------------------------- 461 | 462 | # To avoid displaying the exact version number of Apache being used, add the 463 | # following to httpd.conf (it will not work in .htaccess): 464 | # ServerTokens Prod 465 | 466 | # "-Indexes" will have Apache block users from browsing folders without a 467 | # default document Usually you should leave this activated, because you 468 | # shouldn't allow everybody to surf through every folder on your server (which 469 | # includes rather private places like CMS system folders). 470 | 471 | Options -Indexes 472 | 473 | 474 | # Block access to "hidden" directories or files whose names begin with a 475 | # period. This includes directories used by version control systems such as 476 | # Subversion or Git. 477 | 478 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 479 | RewriteCond %{SCRIPT_FILENAME} -f 480 | RewriteRule "(^|/)\." - [F] 481 | 482 | 483 | # Block access to backup and source files. These files may be left by some 484 | # text/html editors and pose a great security danger, when anyone can access 485 | # them. 486 | 487 | Order allow,deny 488 | Deny from all 489 | Satisfy All 490 | 491 | 492 | # If your server is not already configured as such, the following directive 493 | # should be uncommented in order to set PHP's register_globals option to OFF. 494 | # This closes a major security hole that is abused by most XSS (cross-site 495 | # scripting) attacks. For more information: http://php.net/register_globals 496 | # 497 | # IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS: 498 | # 499 | # Your server does not allow PHP directives to be set via .htaccess. In that 500 | # case you must make this change in your php.ini file instead. If you are 501 | # using a commercial web host, contact the administrators for assistance in 502 | # doing this. Not all servers allow local php.ini files, and they should 503 | # include all PHP configurations (not just this one), or you will effectively 504 | # reset everything to PHP defaults. Consult www.php.net for more detailed 505 | # information about setting PHP directives. 506 | 507 | # php_flag register_globals Off 508 | 509 | # Rename session cookie to something else, than PHPSESSID 510 | # php_value session.name sid 511 | 512 | # Disable magic quotes (This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.) 513 | # php_flag magic_quotes_gpc Off 514 | 515 | # Do not show you are using PHP 516 | # Note: Move this line to php.ini since it won't work in .htaccess 517 | # php_flag expose_php Off 518 | 519 | # Level of log detail - log all errors 520 | # php_value error_reporting -1 521 | 522 | # Write errors to log file 523 | # php_flag log_errors On 524 | 525 | # Do not display errors in browser (production - Off, development - On) 526 | # php_flag display_errors Off 527 | 528 | # Do not display startup errors (production - Off, development - On) 529 | # php_flag display_startup_errors Off 530 | 531 | # Format errors in plain text 532 | # Note: Leave this setting 'On' for xdebug's var_dump() output 533 | # php_flag html_errors Off 534 | 535 | # Show multiple occurrence of error 536 | # php_flag ignore_repeated_errors Off 537 | 538 | # Show same errors from different sources 539 | # php_flag ignore_repeated_source Off 540 | 541 | # Size limit for error messages 542 | # php_value log_errors_max_len 1024 543 | 544 | # Don't precede error with string (doesn't accept empty string, use whitespace if you need) 545 | # php_value error_prepend_string " " 546 | 547 | # Don't prepend to error (doesn't accept empty string, use whitespace if you need) 548 | # php_value error_append_string " " 549 | 550 | # Increase cookie security 551 | 552 | php_value session.cookie_httponly true 553 | 554 | --------------------------------------------------------------------------------