├── .gitattributes ├── server ├── public ├── views │ ├── index.jade │ └── layout.jade ├── routes │ ├── index.js │ ├── camera.js │ └── sony-camera.js └── app.js ├── client ├── app │ ├── .buildignore │ ├── camera │ │ └── photos │ │ │ └── .keep │ ├── styles │ │ ├── main.css │ │ └── main.scss │ ├── robots.txt │ ├── favicon.ico │ ├── images │ │ └── yeoman.png │ ├── scripts │ │ ├── app.js │ │ └── controllers │ │ │ └── main.js │ ├── views │ │ └── main.html │ ├── index.html │ ├── 404.html │ └── .htaccess ├── test │ ├── runner.html │ ├── spec │ │ └── controllers │ │ │ └── main.js │ └── .jshintrc └── karma-e2e.conf.js ├── .bowerrc ├── .gitignore ├── .travis.yml ├── .jshintrc ├── .editorconfig ├── bower.json ├── LICENSE ├── karma.conf.js ├── package.json ├── README.md └── Gruntfile.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /server/public: -------------------------------------------------------------------------------- 1 | ../client/app -------------------------------------------------------------------------------- /client/app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /client/app/camera/photos/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/app/styles/main.css: -------------------------------------------------------------------------------- 1 | ../../dist/styles/main.css -------------------------------------------------------------------------------- /client/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "client/app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /client/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eqot/RemoteCamera/HEAD/client/app/favicon.ico -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} -------------------------------------------------------------------------------- /client/app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eqot/RemoteCamera/HEAD/client/app/images/yeoman.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | client/dist 3 | .tmp 4 | .sass-cache 5 | client/app/bower_components 6 | client/app/camera/photos/*.JPG 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - 'npm install -g bower grunt-cli' 6 | - 'bower install' 7 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var publicPath = require('path').join(__dirname, '../public/'); 4 | 5 | /* 6 | * GET home page. 7 | */ 8 | 9 | exports.index = function(req, res){ 10 | res.sendfile(publicPath + 'index.html'); 11 | }; 12 | -------------------------------------------------------------------------------- /client/test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | End2end Test Runner 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/app/styles/main.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "/bower_components/sass-bootstrap/fonts/"; 2 | 3 | @import 'sass-bootstrap/lib/bootstrap'; 4 | 5 | /* Space out content a bit */ 6 | body { 7 | padding-top: 20px; 8 | padding-bottom: 20px; 9 | background-color: black; 10 | } 11 | 12 | #liveview { 13 | position: absolute; 14 | top: 0; 15 | z-index: -1; 16 | } 17 | -------------------------------------------------------------------------------- /client/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('clientApp', [ 4 | 'ngCookies', 5 | 'ngResource', 6 | 'ngSanitize', 7 | 'ngRoute' 8 | ]) 9 | .config(function ($routeProvider) { 10 | $routeProvider 11 | .when('/', { 12 | templateUrl: 'views/main.html', 13 | controller: 'MainCtrl' 14 | }) 15 | .otherwise({ 16 | redirectTo: '/' 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /.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 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "angular": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RemoteCamera", 3 | "version": "0.0.0", 4 | "appPath": "client/app", 5 | "distPath": "client/dist", 6 | "dependencies": { 7 | "angular": "~1.2.0", 8 | "json3": "~3.2.4", 9 | "es5-shim": "~2.1.0", 10 | "jquery": "~1.10.2", 11 | "sass-bootstrap": "~3.0.0", 12 | "angular-resource": "~1.2.0", 13 | "angular-cookies": "~1.2.0", 14 | "angular-sanitize": "~1.2.0", 15 | "angular-route": "~1.2.0" 16 | }, 17 | "devDependencies": { 18 | "angular-mocks": "~1.2.0", 19 | "angular-scenario": "~1.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('clientApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /client/app/views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | 7 | 8 | 9 |

10 | 11 | 12 |

13 | 14 |
15 | 16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /client/test/.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 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "after": false, 23 | "afterEach": false, 24 | "angular": false, 25 | "before": false, 26 | "beforeEach": false, 27 | "browser": false, 28 | "describe": false, 29 | "expect": false, 30 | "inject": false, 31 | "it": false, 32 | "spyOn": false 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /server/routes/camera.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var SonyCamera = require('../routes/sony-camera'); 4 | var camera = new SonyCamera('10.0.0.1', 10000, '/sony/camera'); 5 | camera.show(); 6 | 7 | exports.call = function(req, res) { 8 | var method = req.params.method; 9 | var params = req.query.p ? JSON.parse(req.query.p) : null; 10 | // console.log(method); 11 | // console.log(params); 12 | 13 | camera.call(method, params, function (err, output) { 14 | if (err) { 15 | console.log(err); 16 | res.send(err); 17 | return; 18 | } 19 | 20 | res.send(output.result); 21 | }); 22 | }; 23 | 24 | exports.startViewfinder = function (req, res) { 25 | camera.startViewfinder(req, res); 26 | }; 27 | 28 | exports.stopViewfinder = function (req, res) { 29 | camera.stopViewfinder(req, res); 30 | }; 31 | 32 | exports.takePhoto = function (req, res) { 33 | camera.takePhoto(req, res); 34 | }; 35 | 36 | exports.zoomIn = function (req, res) { 37 | camera.zoomIn(req, res); 38 | }; 39 | 40 | exports.zoomOut = function (req, res) { 41 | camera.zoomOut(req, res); 42 | }; 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ikuo Terado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('express'); 8 | var routes = require('./routes'); 9 | var camera = require('./routes/camera'); 10 | var http = require('http'); 11 | var path = require('path'); 12 | 13 | var app = express(); 14 | 15 | // all environments 16 | app.set('port', process.env.PORT || 3000); 17 | app.set('views', path.join(__dirname, 'views')); 18 | app.set('view engine', 'jade'); 19 | app.use(express.favicon()); 20 | app.use(express.logger('dev')); 21 | app.use(express.json()); 22 | app.use(express.urlencoded()); 23 | app.use(express.methodOverride()); 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | app.use(app.router); 26 | 27 | // development only 28 | if ('development' === app.get('env')) { 29 | app.use(express.errorHandler()); 30 | } 31 | 32 | app.get('/', routes.index); 33 | 34 | app.get('/camera/viewfinder/start', camera.startViewfinder); 35 | app.get('/camera/viewfinder/stop', camera.stopViewfinder); 36 | 37 | app.get('/camera/photos/take', camera.takePhoto); 38 | 39 | app.get('/camera/zoom/in', camera.zoomIn); 40 | app.get('/camera/zoom/out', camera.zoomOut); 41 | 42 | app.get('/camera/:method/:params', camera.call); 43 | app.get('/camera/:method', camera.call); 44 | app.get('/camera', camera.call); 45 | 46 | http.createServer(app).listen(app.get('port'), function(){ 47 | console.log('Express server listening on port ' + app.get('port')); 48 | }); 49 | -------------------------------------------------------------------------------- /client/app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('clientApp') 6 | .controller('MainCtrl', function ($scope, $http) { 7 | $scope.photos = []; 8 | 9 | function resize() { 10 | var height = window.innerHeight; 11 | $('#liveview').height(height + 'px'); 12 | 13 | var width = $('#liveview').width() || (height * 4 / 3); 14 | var left = (window.innerWidth - width) / 2; 15 | $('#liveview').css('left', left + 'px'); 16 | } 17 | window.onresize = resize; 18 | resize(); 19 | 20 | $scope.startLiveview = function () { 21 | $scope.liveview = '/camera/viewfinder/start'; 22 | }; 23 | 24 | $scope.stopLiveview = function () { 25 | call('viewfinder/stop'); 26 | }; 27 | 28 | $scope.takePhoto = function () { 29 | call('photos/take', null, function (result) { 30 | $scope.photos.push(result); 31 | }); 32 | }; 33 | 34 | $scope.zoomIn = function () { 35 | call('zoom/in'); 36 | }; 37 | 38 | $scope.zoomOut = function () { 39 | call('zoom/out'); 40 | }; 41 | 42 | function call (method, params, callback) { 43 | var CAMERA_API_URL = '/camera'; 44 | 45 | params = params ? '?p=' + JSON.stringify(params) : ''; 46 | var url = CAMERA_API_URL + '/' + method + params; 47 | $http.get(url).success(function (result) { 48 | // console.log(result); 49 | if (callback) { 50 | callback(result); 51 | } 52 | }); 53 | } 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /client/karma-e2e.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['ng-scenario'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'test/e2e/**/*.js' 15 | ], 16 | 17 | // list of files / patterns to exclude 18 | exclude: [], 19 | 20 | // web server port 21 | port: 8080, 22 | 23 | // level of logging 24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 25 | logLevel: config.LOG_INFO, 26 | 27 | 28 | // enable / disable watching file and executing tests whenever any file changes 29 | autoWatch: false, 30 | 31 | 32 | // Start these browsers, currently available: 33 | // - Chrome 34 | // - ChromeCanary 35 | // - Firefox 36 | // - Opera 37 | // - Safari (only Mac) 38 | // - PhantomJS 39 | // - IE (only Windows) 40 | browsers: ['Chrome'], 41 | 42 | 43 | // Continuous Integration mode 44 | // if true, it capture browsers, run tests and exit 45 | singleRun: false 46 | 47 | // Uncomment the following lines if you are using grunt's server to run the tests 48 | // proxies: { 49 | // '/': 'http://localhost:9000/' 50 | // }, 51 | // URL root prevent conflicts with the site root 52 | // urlRoot: '_karma_' 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['jasmine'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'app/bower_components/angular/angular.js', 15 | 'app/bower_components/angular-mocks/angular-mocks.js', 16 | 'app/bower_components/angular-resource/angular-resource.js', 17 | 'app/bower_components/angular-cookies/angular-cookies.js', 18 | 'app/bower_components/angular-sanitize/angular-sanitize.js', 19 | 'app/bower_components/angular-route/angular-route.js', 20 | 'app/scripts/*.js', 21 | 'app/scripts/**/*.js', 22 | 'test/mock/**/*.js', 23 | 'test/spec/**/*.js' 24 | ], 25 | 26 | // list of files / patterns to exclude 27 | exclude: [], 28 | 29 | // web server port 30 | port: 8080, 31 | 32 | // level of logging 33 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 34 | logLevel: config.LOG_INFO, 35 | 36 | 37 | // enable / disable watching file and executing tests whenever any file changes 38 | autoWatch: false, 39 | 40 | 41 | // Start these browsers, currently available: 42 | // - Chrome 43 | // - ChromeCanary 44 | // - Firefox 45 | // - Opera 46 | // - Safari (only Mac) 47 | // - PhantomJS 48 | // - IE (only Windows) 49 | browsers: ['Chrome'], 50 | 51 | 52 | // Continuous Integration mode 53 | // if true, it capture browsers, run tests and exit 54 | singleRun: false 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RemoteCamera", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "express": "3.4.8", 6 | "jade": "~1.1.4", 7 | "ahr2": "~2.3.2" 8 | }, 9 | "devDependencies": { 10 | "grunt": "~0.4.1", 11 | "grunt-autoprefixer": "~0.6.4", 12 | "grunt-concurrent": "~0.4.1", 13 | "grunt-contrib-clean": "~0.5.0", 14 | "grunt-contrib-coffee": "~0.8.0", 15 | "grunt-contrib-compass": "~0.7.0", 16 | "grunt-contrib-concat": "~0.3.0", 17 | "grunt-contrib-connect": "~0.6.0", 18 | "grunt-contrib-copy": "~0.5.0", 19 | "grunt-contrib-cssmin": "~0.7.0", 20 | "grunt-contrib-htmlmin": "~0.1.3", 21 | "grunt-contrib-imagemin": "~0.5.0", 22 | "grunt-contrib-jshint": "~0.8.0", 23 | "grunt-contrib-uglify": "~0.2.0", 24 | "grunt-contrib-watch": "~0.5.2", 25 | "grunt-google-cdn": "~0.2.0", 26 | "grunt-newer": "~0.6.1", 27 | "grunt-ngmin": "~0.0.2", 28 | "grunt-rev": "~0.1.0", 29 | "grunt-svgmin": "~0.3.0", 30 | "grunt-usemin": "~2.0.0", 31 | "jshint-stylish": "~0.1.5", 32 | "load-grunt-tasks": "~0.2.0", 33 | "time-grunt": "~0.2.8", 34 | "karma-ng-scenario": "~0.1.0", 35 | "grunt-karma": "~0.6.2", 36 | "karma-script-launcher": "~0.1.0", 37 | "karma-chrome-launcher": "~0.1.1", 38 | "karma-html2js-preprocessor": "~0.1.0", 39 | "karma-firefox-launcher": "~0.1.3", 40 | "karma-jasmine": "~0.1.5", 41 | "karma-coffee-preprocessor": "~0.1.2", 42 | "requirejs": "~2.1.10", 43 | "karma-requirejs": "~0.2.0", 44 | "karma-phantomjs-launcher": "~0.1.1", 45 | "karma": "~0.10.9", 46 | "karma-ng-html2js-preprocessor": "~0.1.0" 47 | }, 48 | "engines": { 49 | "node": ">=0.8.0" 50 | }, 51 | "scripts": { 52 | "start": "NODE_ENV=development node-dev server/app.js" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RemoteCamera 2 | 3 | [![Build Status](https://travis-ci.org/eqot/RemoteCamera.png?branch=master)](https://travis-ci.org/eqot/RemoteCamera) 4 | [![Dependency Status](https://gemnasium.com/eqot/RemoteCamera.png)](https://gemnasium.com/eqot/RemoteCamera) 5 | [![Code Climate](https://codeclimate.com/github/eqot/RemoteCamera.png)](https://codeclimate.com/github/eqot/RemoteCamera) 6 | [![Coverage Status](https://coveralls.io/repos/eqot/RemoteCamera/badge.png)](https://coveralls.io/r/eqot/RemoteCamera) 7 | 8 | This aims to communicate with Sony camera through [Camera Remote API SDK](http://developer.sony.com/develop/cameras/) from web app which is implemented in JavaScript. 9 | 10 | 11 | ## Technical details 12 | 13 | This contains two parts, server- and client-side code. 14 | 15 | Server-side code is implemented for client to communicate with Sony camera. 16 | Since Camera Remote API does not support [CORS](http://www.w3.org/TR/cors/) unfortunately, client-side code in browser cannot access to Sony camera directly. So this server-side code helps for API call from client to Sony camera to be bypassed. 17 | Also the server supports that liveview data from Sony camera is converted into motion JPEG so that the client can easily show the liveview in HTML file. 18 | 19 | Client-side code is a main part which has UI widgets for controlling Sony camera and showing liveview. 20 | 21 | 22 | ## How to use 23 | 24 | ``` 25 | git clone https://github.com/eqot/RemoteCamera.git 26 | cd RemoteCamera 27 | npm install && bower install 28 | grunt build 29 | npm start 30 | ``` 31 | 32 | 33 | ## Limitations 34 | 35 | * Since this has not supported device discovery yet, ip address and port number for Sony camera is hard-coded for [DSC-QX100](http://developer.sony.com/devices/cameras/sony-smartphone-attachable-lens-style-camera-dsc-qx100/) 36 | 37 | 38 | ## License 39 | 40 | Copyright © 2013-2014 Ikuo Terado. Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php). 41 | -------------------------------------------------------------------------------- /client/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 27 | 28 | 29 |
30 | 31 | 32 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /client/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

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

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /server/routes/sony-camera.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var url = require('url'); 5 | var http = require('http'); 6 | var request = require('ahr2'); 7 | 8 | (function () { 9 | 10 | var SonyCamera = function (url, port, path) { 11 | this.url = url || '10.0.0.1'; 12 | this.port = port || 10000; 13 | this.path = path || '/sony/camera'; 14 | 15 | this.rpcReq = { 16 | id: 1, 17 | version: '1.0' 18 | }; 19 | 20 | this.publicDir = require('path').join(__dirname, '../public/'); 21 | this.photoDir = 'camera/photos/'; 22 | }; 23 | 24 | SonyCamera.prototype.show = function () { 25 | console.log(this.url + ':' + this.port + this.path); 26 | }; 27 | 28 | SonyCamera.prototype.call = function (method, params, callback) { 29 | this.rpcReq.method = method; 30 | this.rpcReq.params = params || []; 31 | console.log(this.rpcReq); 32 | 33 | request({ 34 | method: 'POST', 35 | hostname: this.url, 36 | port: this.port, 37 | pathname: this.path, 38 | body: JSON.stringify(this.rpcReq) 39 | }).when(function (err, ahr, rpcRes) { 40 | if (callback) { 41 | callback(err, rpcRes); 42 | } 43 | }); 44 | }; 45 | 46 | SonyCamera.prototype.waitUntilIdle = function (callback) { 47 | this.call('getEvent', [false], function (err, result) { 48 | // console.log(err); 49 | if (!err) { 50 | var cameraStatus = result.result[1].cameraStatus; 51 | // console.log(cameraStatus); 52 | if (cameraStatus !== 'IDLE') { 53 | err = 'NOT_IDLE'; 54 | } 55 | } 56 | 57 | if (callback) { 58 | callback(err); 59 | } 60 | }); 61 | }; 62 | 63 | SonyCamera.prototype.startViewfinder = function (req, res) { 64 | this.call('startLiveview', null, function (err, output) { 65 | var liveviewUrl = url.parse(output.result[0]); 66 | // console.log(liveviewUrl); 67 | 68 | var COMMON_HEADER_SIZE = 8; 69 | var PAYLOAD_HEADER_SIZE = 128; 70 | var JPEG_SIZE_POSITION = 4; 71 | var PADDING_SIZE_POSITION = 7; 72 | 73 | var jpegSize = 0; 74 | var paddingSize = 0; 75 | 76 | var boundary = 'boundary'; 77 | 78 | var liveviewReq = http.request(liveviewUrl, function (liveviewRes) { 79 | // console.log(data); 80 | 81 | var contentType = liveviewRes.headers['content-type']; 82 | var boundaryBuffer = new Buffer('\n--' + boundary + '\nContent-Type: ' + contentType + '\n\n'); 83 | 84 | var buffer = new Buffer(0); 85 | 86 | res.writeHead(200, { 87 | 'Expires': 'Mon, 01 Jul 1980 00:00:00 GMT', 88 | 'Cache-Control': 'no-cache, no-store, must-revalidate', 89 | 'Pragma': 'no-cache', 90 | 'Content-Type': 'multipart/x-mixed-replace;boundary=' + boundary 91 | }); 92 | 93 | liveviewRes.on('data', function (chunk) { 94 | if (jpegSize === 0) { 95 | buffer = Buffer.concat([buffer, chunk]); 96 | 97 | if (buffer.length >= (COMMON_HEADER_SIZE + PAYLOAD_HEADER_SIZE)) { 98 | jpegSize = 99 | buffer.readUInt8(COMMON_HEADER_SIZE + JPEG_SIZE_POSITION) * 65536 + 100 | buffer.readUInt16BE(COMMON_HEADER_SIZE + JPEG_SIZE_POSITION + 1); 101 | // console.log(jpegSize); 102 | 103 | paddingSize = buffer.readUInt8(COMMON_HEADER_SIZE + PADDING_SIZE_POSITION); 104 | // console.log(paddingSize); 105 | 106 | res.write(boundaryBuffer); 107 | 108 | buffer = buffer.slice(8 + 128); 109 | if (buffer.length > 0) { 110 | res.write(buffer); 111 | } 112 | } 113 | } else { 114 | res.write(chunk); 115 | 116 | if (chunk.length < jpegSize) { 117 | jpegSize -= chunk.length; 118 | } else { 119 | buffer = chunk.slice(jpegSize + paddingSize); 120 | jpegSize = 0; 121 | } 122 | } 123 | }); 124 | 125 | liveviewRes.on('end', function () { 126 | console.log('End'); 127 | }); 128 | 129 | liveviewRes.on('close', function () { 130 | console.log('Close'); 131 | }); 132 | }); 133 | 134 | liveviewReq.on('error', function(e) { 135 | console.error('Error: ', e); 136 | }); 137 | 138 | liveviewReq.end(); 139 | }); 140 | }; 141 | 142 | SonyCamera.prototype.stopViewfinder = function (req, res) { 143 | this.call('stopLiveview'); 144 | res.send(null); 145 | }; 146 | 147 | SonyCamera.prototype.takePhoto = function (req, res) { 148 | var self = this; 149 | 150 | this.waitUntilIdle(function (err) { 151 | if (err) { 152 | res.send(null); 153 | return; 154 | } 155 | 156 | self.call('actTakePicture', null, function (err, output) { 157 | var url = output.result[0][0]; 158 | 159 | request.get(url).when(function (err, ahr, data) { 160 | var photoName = url.split('?')[0].split('/')[3]; 161 | // console.log(photoName); 162 | 163 | fs.writeFile(self.publicDir + self.photoDir + photoName, data, function (err) { 164 | if (err) { 165 | throw err; 166 | } 167 | 168 | res.send(self.photoDir + photoName); 169 | }); 170 | }); 171 | }); 172 | }); 173 | }; 174 | 175 | SonyCamera.prototype.zoomIn = function (req, res) { 176 | this.call('actZoom', ['in', 'start']); 177 | res.send(null); 178 | }; 179 | 180 | SonyCamera.prototype.zoomOut = function (req, res) { 181 | this.call('actZoom', ['out', 'start']); 182 | res.send(null); 183 | }; 184 | 185 | 186 | // Client-side export 187 | if (typeof window !== 'undefined' && window.SonyCamera) { window.SonyCamera = SonyCamera; } 188 | // Server-side export 189 | if (typeof module !== 'undefined') { module.exports = SonyCamera; } 190 | 191 | }()); 192 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2013-12-21 using generator-angular 0.6.0 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Load grunt tasks automatically 13 | require('load-grunt-tasks')(grunt); 14 | 15 | // Time how long tasks take. Can help when optimizing build times 16 | require('time-grunt')(grunt); 17 | 18 | // Define the configuration for all the tasks 19 | grunt.initConfig({ 20 | 21 | // Project settings 22 | yeoman: { 23 | // configurable paths 24 | app: require('./bower.json').appPath || 'app', 25 | dist: require('./bower.json').distPath || 'dist' 26 | }, 27 | 28 | // Watches files for changes and runs tasks based on the changed files 29 | watch: { 30 | js: { 31 | files: ['{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js'], 32 | tasks: ['newer:jshint:all'] 33 | }, 34 | jsTest: { 35 | files: ['test/spec/{,*/}*.js'], 36 | tasks: ['newer:jshint:test', 'karma'] 37 | }, 38 | compass: { 39 | files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], 40 | tasks: ['compass:server', 'autoprefixer'] 41 | }, 42 | styles: { 43 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 44 | tasks: ['newer:copy:styles', 'autoprefixer'] 45 | }, 46 | gruntfile: { 47 | files: ['Gruntfile.js'] 48 | }, 49 | livereload: { 50 | options: { 51 | livereload: '<%= connect.options.livereload %>' 52 | }, 53 | files: [ 54 | '<%= yeoman.app %>/{,*/}*.html', 55 | '.tmp/styles/{,*/}*.css', 56 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 57 | ] 58 | } 59 | }, 60 | 61 | // The actual grunt server settings 62 | connect: { 63 | options: { 64 | port: 9000, 65 | // Change this to '0.0.0.0' to access the server from outside. 66 | hostname: 'localhost', 67 | livereload: 35729 68 | }, 69 | livereload: { 70 | options: { 71 | open: true, 72 | base: [ 73 | '.tmp', 74 | '<%= yeoman.app %>' 75 | ] 76 | } 77 | }, 78 | test: { 79 | options: { 80 | port: 9001, 81 | base: [ 82 | '.tmp', 83 | 'test', 84 | '<%= yeoman.app %>' 85 | ] 86 | } 87 | }, 88 | dist: { 89 | options: { 90 | base: '<%= yeoman.dist %>' 91 | } 92 | } 93 | }, 94 | 95 | // Make sure code styles are up to par and there are no obvious mistakes 96 | jshint: { 97 | options: { 98 | jshintrc: '.jshintrc', 99 | reporter: require('jshint-stylish') 100 | }, 101 | all: [ 102 | 'Gruntfile.js', 103 | '<%= yeoman.app %>/scripts/{,*/}*.js', 104 | 'server/{,*/}*.js' 105 | ], 106 | test: { 107 | options: { 108 | jshintrc: 'client/test/.jshintrc' 109 | }, 110 | src: ['client/test/spec/{,*/}*.js'] 111 | } 112 | }, 113 | 114 | // Empties folders to start fresh 115 | clean: { 116 | dist: { 117 | files: [{ 118 | dot: true, 119 | src: [ 120 | '.tmp', 121 | '<%= yeoman.dist %>/*', 122 | '!<%= yeoman.dist %>/.git*' 123 | ] 124 | }] 125 | }, 126 | server: '.tmp' 127 | }, 128 | 129 | // Add vendor prefixed styles 130 | autoprefixer: { 131 | options: { 132 | browsers: ['last 1 version'] 133 | }, 134 | dist: { 135 | files: [{ 136 | expand: true, 137 | cwd: '.tmp/styles/', 138 | src: '{,*/}*.css', 139 | dest: '.tmp/styles/' 140 | }] 141 | } 142 | }, 143 | 144 | 145 | 146 | 147 | // Compiles Sass to CSS and generates necessary files if requested 148 | compass: { 149 | options: { 150 | sassDir: '<%= yeoman.app %>/styles', 151 | cssDir: '.tmp/styles', 152 | generatedImagesDir: '.tmp/images/generated', 153 | imagesDir: '<%= yeoman.app %>/images', 154 | javascriptsDir: '<%= yeoman.app %>/scripts', 155 | fontsDir: '<%= yeoman.app %>/styles/fonts', 156 | importPath: '<%= yeoman.app %>/bower_components', 157 | httpImagesPath: '/images', 158 | httpGeneratedImagesPath: '/images/generated', 159 | httpFontsPath: '/styles/fonts', 160 | relativeAssets: false, 161 | assetCacheBuster: false 162 | }, 163 | dist: { 164 | options: { 165 | generatedImagesDir: '<%= yeoman.dist %>/images/generated' 166 | } 167 | }, 168 | server: { 169 | options: { 170 | debugInfo: true 171 | } 172 | } 173 | }, 174 | 175 | // Renames files for browser caching purposes 176 | rev: { 177 | dist: { 178 | files: { 179 | src: [ 180 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 181 | '<%= yeoman.dist %>/styles/{,*/}*.css', 182 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 183 | '<%= yeoman.dist %>/styles/fonts/*' 184 | ] 185 | } 186 | } 187 | }, 188 | 189 | // Reads HTML for usemin blocks to enable smart builds that automatically 190 | // concat, minify and revision files. Creates configurations in memory so 191 | // additional tasks can operate on them 192 | useminPrepare: { 193 | html: '<%= yeoman.app %>/index.html', 194 | options: { 195 | dest: '<%= yeoman.dist %>' 196 | } 197 | }, 198 | 199 | // Performs rewrites based on rev and the useminPrepare configuration 200 | usemin: { 201 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 202 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 203 | options: { 204 | assetsDirs: ['<%= yeoman.dist %>'] 205 | } 206 | }, 207 | 208 | // The following *-min tasks produce minified files in the dist folder 209 | imagemin: { 210 | dist: { 211 | files: [{ 212 | expand: true, 213 | cwd: '<%= yeoman.app %>/images', 214 | src: '{,*/}*.{png,jpg,jpeg,gif}', 215 | dest: '<%= yeoman.dist %>/images' 216 | }] 217 | } 218 | }, 219 | svgmin: { 220 | dist: { 221 | files: [{ 222 | expand: true, 223 | cwd: '<%= yeoman.app %>/images', 224 | src: '{,*/}*.svg', 225 | dest: '<%= yeoman.dist %>/images' 226 | }] 227 | } 228 | }, 229 | htmlmin: { 230 | dist: { 231 | options: { 232 | // Optional configurations that you can uncomment to use 233 | // removeCommentsFromCDATA: true, 234 | // collapseBooleanAttributes: true, 235 | // removeAttributeQuotes: true, 236 | // removeRedundantAttributes: true, 237 | // useShortDoctype: true, 238 | // removeEmptyAttributes: true, 239 | // removeOptionalTags: true*/ 240 | }, 241 | files: [{ 242 | expand: true, 243 | cwd: '<%= yeoman.app %>', 244 | src: ['*.html', 'views/*.html'], 245 | dest: '<%= yeoman.dist %>' 246 | }] 247 | } 248 | }, 249 | 250 | // Allow the use of non-minsafe AngularJS files. Automatically makes it 251 | // minsafe compatible so Uglify does not destroy the ng references 252 | ngmin: { 253 | dist: { 254 | files: [{ 255 | expand: true, 256 | cwd: '.tmp/concat/scripts', 257 | src: '*.js', 258 | dest: '.tmp/concat/scripts' 259 | }] 260 | } 261 | }, 262 | 263 | // Replace Google CDN references 264 | cdnify: { 265 | dist: { 266 | html: ['<%= yeoman.dist %>/*.html'] 267 | } 268 | }, 269 | 270 | // Copies remaining files to places other tasks can use 271 | copy: { 272 | dist: { 273 | files: [{ 274 | expand: true, 275 | dot: true, 276 | cwd: '<%= yeoman.app %>', 277 | dest: '<%= yeoman.dist %>', 278 | src: [ 279 | '*.{ico,png,txt}', 280 | '.htaccess', 281 | 'bower_components/**/*', 282 | 'images/{,*/}*.{webp}', 283 | 'fonts/*' 284 | ] 285 | }, { 286 | expand: true, 287 | cwd: '.tmp/images', 288 | dest: '<%= yeoman.dist %>/images', 289 | src: [ 290 | 'generated/*' 291 | ] 292 | }] 293 | }, 294 | styles: { 295 | // expand: true, 296 | // cwd: '<%= yeoman.app %>/styles', 297 | // dest: '.tmp/styles/', 298 | // src: '{,*/}*.css' 299 | } 300 | }, 301 | 302 | // Run some tasks in parallel to speed up the build process 303 | concurrent: { 304 | server: [ 305 | 'compass:server', 306 | 'copy:styles' 307 | ], 308 | test: [ 309 | 'compass', 310 | 'copy:styles' 311 | ], 312 | dist: [ 313 | 'compass:dist', 314 | 'copy:styles', 315 | 'imagemin', 316 | 'svgmin', 317 | 'htmlmin' 318 | ] 319 | }, 320 | 321 | // By default, your `index.html`'s will take care of 322 | // minification. These next options are pre-configured if you do not wish 323 | // to use the Usemin blocks. 324 | // cssmin: { 325 | // dist: { 326 | // files: { 327 | // '<%= yeoman.dist %>/styles/main.css': [ 328 | // '.tmp/styles/{,*/}*.css', 329 | // '<%= yeoman.app %>/styles/{,*/}*.css' 330 | // ] 331 | // } 332 | // } 333 | // }, 334 | // uglify: { 335 | // dist: { 336 | // files: { 337 | // '<%= yeoman.dist %>/scripts/scripts.js': [ 338 | // '<%= yeoman.dist %>/scripts/scripts.js' 339 | // ] 340 | // } 341 | // } 342 | // }, 343 | // concat: { 344 | // dist: {} 345 | // }, 346 | 347 | // Test settings 348 | karma: { 349 | unit: { 350 | configFile: 'karma.conf.js', 351 | singleRun: true 352 | } 353 | } 354 | }); 355 | 356 | 357 | grunt.registerTask('serve', function (target) { 358 | if (target === 'dist') { 359 | return grunt.task.run(['build', 'connect:dist:keepalive']); 360 | } 361 | 362 | grunt.task.run([ 363 | 'clean:server', 364 | 'concurrent:server', 365 | 'autoprefixer', 366 | 'connect:livereload', 367 | 'watch' 368 | ]); 369 | }); 370 | 371 | grunt.registerTask('server', function () { 372 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 373 | grunt.task.run(['serve']); 374 | }); 375 | 376 | grunt.registerTask('test', [ 377 | 'clean:server', 378 | 'concurrent:test', 379 | 'autoprefixer', 380 | 'connect:test', 381 | 'karma' 382 | ]); 383 | 384 | grunt.registerTask('build', [ 385 | 'clean:dist', 386 | 'useminPrepare', 387 | 'concurrent:dist', 388 | 'autoprefixer', 389 | 'concat', 390 | 'ngmin', 391 | 'copy:dist', 392 | 'cdnify', 393 | 'cssmin', 394 | 'uglify', 395 | // 'rev', 396 | 'usemin' 397 | ]); 398 | 399 | grunt.registerTask('default', [ 400 | 'newer:jshint', 401 | 'test', 402 | 'build' 403 | ]); 404 | }; 405 | -------------------------------------------------------------------------------- /client/app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | --------------------------------------------------------------------------------