mirror of
https://github.com/Ekokumppanit/Lentolaskuri.git
synced 2026-03-12 16:57:48 +00:00
Initial commit
This commit is contained in:
29
.editorconfig
Normal file
29
.editorconfig
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# Change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
|
||||||
|
# We recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.sublime-project]
|
||||||
|
indent_style = tab
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.tmp
|
||||||
|
app/components
|
||||||
|
*.sublime-workspace
|
||||||
26
.jshintrc
Normal file
26
.jshintrc
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"node": true,
|
||||||
|
"browser": true,
|
||||||
|
"es5": true,
|
||||||
|
"esnext": true,
|
||||||
|
"bitwise": true,
|
||||||
|
"camelcase": true,
|
||||||
|
"curly": true,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"immed": true,
|
||||||
|
"indent": 2,
|
||||||
|
"latedef": true,
|
||||||
|
"newcap": true,
|
||||||
|
"noarg": true,
|
||||||
|
"quotmark": "single",
|
||||||
|
"regexp": true,
|
||||||
|
"undef": true,
|
||||||
|
"unused": false,
|
||||||
|
"strict": true,
|
||||||
|
"trailing": true,
|
||||||
|
"smarttabs": true,
|
||||||
|
"globals": {
|
||||||
|
"$": true,
|
||||||
|
"define": true
|
||||||
|
}
|
||||||
|
}
|
||||||
320
Gruntfile.js
Normal file
320
Gruntfile.js
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
'use strict';
|
||||||
|
var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;
|
||||||
|
var mountFolder = function (connect, dir) {
|
||||||
|
return connect.static(require('path').resolve(dir));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function (grunt) {
|
||||||
|
// load all grunt tasks
|
||||||
|
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
|
||||||
|
|
||||||
|
// configurable paths
|
||||||
|
var yeomanConfig = {
|
||||||
|
app: 'app',
|
||||||
|
dist: 'dist'
|
||||||
|
};
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
yeoman: yeomanConfig,
|
||||||
|
watch: {
|
||||||
|
less: {
|
||||||
|
files: ['<%= yeoman.app %>/styles/*.less'],
|
||||||
|
tasks: ['less']
|
||||||
|
},
|
||||||
|
livereload: {
|
||||||
|
files: [
|
||||||
|
'<%= yeoman.app %>/*.html',
|
||||||
|
'{.tmp,<%= yeoman.app %>}/styles/*.css',
|
||||||
|
'{.tmp,<%= yeoman.app %>}/scripts/**/*.js',
|
||||||
|
'<%= yeoman.app %>/images/*.{png,jpg,jpeg,webp}'
|
||||||
|
],
|
||||||
|
tasks: ['livereload']
|
||||||
|
},
|
||||||
|
handlebars: {
|
||||||
|
files: [
|
||||||
|
'app/templates/**/*.hbs'
|
||||||
|
],
|
||||||
|
tasks: ['handlebars', 'livereload']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlebars: {
|
||||||
|
compile: {
|
||||||
|
files: {
|
||||||
|
'.tmp/scripts/templates.js': [
|
||||||
|
'app/templates/**/*.hbs'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
// namespace: false,
|
||||||
|
amd: true,
|
||||||
|
processName: function (filename) {
|
||||||
|
return filename.replace(/^app\/templates\//, '').replace(/\.hbs/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connect: {
|
||||||
|
options: {
|
||||||
|
port: 9000,
|
||||||
|
// change this to '0.0.0.0' to access the server from outside
|
||||||
|
hostname: 'localhost'
|
||||||
|
},
|
||||||
|
livereload: {
|
||||||
|
options: {
|
||||||
|
middleware: function (connect) {
|
||||||
|
return [
|
||||||
|
lrSnippet,
|
||||||
|
mountFolder(connect, '.tmp'),
|
||||||
|
mountFolder(connect, 'app')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
middleware: function (connect) {
|
||||||
|
return [
|
||||||
|
mountFolder(connect, 'dist')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open: {
|
||||||
|
server: {
|
||||||
|
url: 'http://localhost:<%= connect.options.port %>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clean: {
|
||||||
|
dist: ['.tmp', '<%= yeoman.dist %>/*'],
|
||||||
|
server: '.tmp'
|
||||||
|
},
|
||||||
|
jshint: {
|
||||||
|
options: {
|
||||||
|
jshintrc: '.jshintrc'
|
||||||
|
},
|
||||||
|
all: [
|
||||||
|
'Gruntfile.js',
|
||||||
|
'<%= yeoman.app %>/scripts/*.js'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
less: {
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
paths: ['app/components'],
|
||||||
|
yuicompress: true,
|
||||||
|
strictUnits: false,
|
||||||
|
strictMaths: false
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
'.tmp/styles/main.css': '<%= yeoman.app %>/styles/main.less'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
options: {
|
||||||
|
paths: ['app/components'],
|
||||||
|
strictUnits: false,
|
||||||
|
strictMaths: false
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
'.tmp/styles/main.css': '<%= yeoman.app %>/styles/main.less'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
requirejs: {
|
||||||
|
dist: {
|
||||||
|
// Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
|
||||||
|
options: {
|
||||||
|
// `name` and `out` is set by grunt-usemin
|
||||||
|
baseUrl: 'app/scripts',
|
||||||
|
paths: {
|
||||||
|
'bootstrap': '../../.tmp/scripts/bootstrap',
|
||||||
|
'Template': '../../.tmp/scripts/templates'
|
||||||
|
},
|
||||||
|
optimize: 'none',
|
||||||
|
// TODO: Figure out how to make sourcemaps work with grunt-usemin
|
||||||
|
// https://github.com/yeoman/grunt-usemin/issues/30
|
||||||
|
//generateSourceMaps: true,
|
||||||
|
// required to support SourceMaps
|
||||||
|
// http://requirejs.org/docs/errors.html#sourcemapcomments
|
||||||
|
preserveLicenseComments: false,
|
||||||
|
useStrict: true,
|
||||||
|
wrap: true,
|
||||||
|
include: [
|
||||||
|
'libs/gmaps'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useminPrepare: {
|
||||||
|
html: '<%= yeoman.app %>/index.html',
|
||||||
|
options: {
|
||||||
|
dest: '<%= yeoman.dist %>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
usemin: {
|
||||||
|
html: ['<%= yeoman.dist %>/*.html'],
|
||||||
|
css: ['<%= yeoman.dist %>/styles/*.css'],
|
||||||
|
options: {
|
||||||
|
dirs: ['<%= yeoman.dist %>']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imagemin: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= yeoman.app %>/images',
|
||||||
|
src: '*.{png,jpg,jpeg}',
|
||||||
|
dest: '<%= yeoman.dist %>/images'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cssmin: {
|
||||||
|
dist: {
|
||||||
|
files: {
|
||||||
|
'<%= yeoman.dist %>/styles/main.css': [
|
||||||
|
'.tmp/styles/*.css',
|
||||||
|
'<%= yeoman.app %>/styles/*.css'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
htmlmin: {
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
/*removeCommentsFromCDATA: true,
|
||||||
|
// https://github.com/yeoman/grunt-usemin/issues/44
|
||||||
|
//collapseWhitespace: true,
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
removeOptionalTags: true*/
|
||||||
|
},
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
src: '*.html',
|
||||||
|
dest: '<%= yeoman.dist %>'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copy: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
dot: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '<%= yeoman.dist %>',
|
||||||
|
src: [
|
||||||
|
'api/*',
|
||||||
|
'*.{ico,txt}',
|
||||||
|
'.htaccess',
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '<%= yeoman.dist %>/font',
|
||||||
|
src: [
|
||||||
|
'components/font-awesome/build/assets/font-awesome/font/*.{ttf,woff,otf,eot,svg}'
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '<%= yeoman.dist %>/styles',
|
||||||
|
src: [
|
||||||
|
'components/select2/*.{png,gif}'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
livereload: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '.tmp/font',
|
||||||
|
src: [
|
||||||
|
'components/font-awesome/build/assets/font-awesome/font/*.{ttf,woff,otf,eot,svg}'
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '.tmp/styles',
|
||||||
|
src: [
|
||||||
|
'components/select2/*.{png,gif}'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bower: {
|
||||||
|
all: {
|
||||||
|
rjsConfig: '<%= yeoman.app %>/scripts/main.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
concat: {
|
||||||
|
bootstrap: {
|
||||||
|
src: [
|
||||||
|
'app/components/bootstrap/js/bootstrap-transition.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-alert.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-button.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-carousel.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-collapse.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-dropdown.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-modal.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-tooltip.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-popover.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-scrollspy.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-tab.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-typeahead.js',
|
||||||
|
'app/components/bootstrap/js/bootstrap-affix.js'
|
||||||
|
],
|
||||||
|
dest: '.tmp/scripts/bootstrap.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.renameTask('regarde', 'watch');
|
||||||
|
|
||||||
|
grunt.registerTask('server', function (target) {
|
||||||
|
if (target === 'dist') {
|
||||||
|
return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
|
||||||
|
}
|
||||||
|
|
||||||
|
grunt.task.run([
|
||||||
|
'clean:server',
|
||||||
|
'less:server',
|
||||||
|
'handlebars',
|
||||||
|
'concat',
|
||||||
|
'copy:livereload',
|
||||||
|
'livereload-start',
|
||||||
|
'connect:livereload',
|
||||||
|
'open',
|
||||||
|
'watch'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('build', [
|
||||||
|
'clean:dist',
|
||||||
|
'less:dist',
|
||||||
|
'handlebars',
|
||||||
|
'concat',
|
||||||
|
'useminPrepare',
|
||||||
|
'requirejs',
|
||||||
|
'imagemin',
|
||||||
|
'htmlmin',
|
||||||
|
'concat',
|
||||||
|
'cssmin',
|
||||||
|
'uglify',
|
||||||
|
'copy',
|
||||||
|
'usemin'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('default', [
|
||||||
|
'jshint',
|
||||||
|
'build'
|
||||||
|
]);
|
||||||
|
};
|
||||||
543
app/.htaccess
Normal file
543
app/.htaccess
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
# Apache configuration file
|
||||||
|
# httpd.apache.org/docs/2.2/mod/quickreference.html
|
||||||
|
|
||||||
|
# Note .htaccess files are an overhead, this logic should be in your Apache
|
||||||
|
# config if possible: httpd.apache.org/docs/2.2/howto/htaccess.html
|
||||||
|
|
||||||
|
# Techniques in here adapted from all over, including:
|
||||||
|
# Kroc Camen: camendesign.com/.htaccess
|
||||||
|
# perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/
|
||||||
|
# Sample .htaccess file of CMS MODx: modxcms.com
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Better website experience for IE users
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Force the latest IE version, in various cases when it may fall back to IE7 mode
|
||||||
|
# github.com/rails/rails/commit/123eb25#commitcomment-118920
|
||||||
|
# Use ChromeFrame if it's installed for a better experience for the poor IE folk
|
||||||
|
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
Header set X-UA-Compatible "IE=Edge,chrome=1"
|
||||||
|
# mod_headers can't match by content-type, but we don't want to send this header on *everything*...
|
||||||
|
<FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webm|webp|woff|xml|xpi)$">
|
||||||
|
Header unset X-UA-Compatible
|
||||||
|
</FilesMatch>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Cross-domain AJAX requests
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Serve cross-domain Ajax requests, disabled by default.
|
||||||
|
# enable-cors.org
|
||||||
|
# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
|
||||||
|
|
||||||
|
# <IfModule mod_headers.c>
|
||||||
|
# Header set Access-Control-Allow-Origin "*"
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# CORS-enabled images (@crossorigin)
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Send CORS headers if browsers request them; enabled by default for images.
|
||||||
|
# developer.mozilla.org/en/CORS_Enabled_Image
|
||||||
|
# blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
|
||||||
|
# hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
|
||||||
|
# wiki.mozilla.org/Security/Reviews/crossoriginAttribute
|
||||||
|
|
||||||
|
<IfModule mod_setenvif.c>
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
# mod_headers, y u no match by Content-Type?!
|
||||||
|
<FilesMatch "\.(gif|ico|jpe?g|png|svg|svgz|webp)$">
|
||||||
|
SetEnvIf Origin ":" IS_CORS
|
||||||
|
Header set Access-Control-Allow-Origin "*" env=IS_CORS
|
||||||
|
</FilesMatch>
|
||||||
|
</IfModule>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Webfont access
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Allow access from all domains for webfonts.
|
||||||
|
# Alternatively you could only whitelist your
|
||||||
|
# subdomains like "subdomain.example.com".
|
||||||
|
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
<FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
|
||||||
|
Header set Access-Control-Allow-Origin "*"
|
||||||
|
</FilesMatch>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Proper MIME type for all files
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# JavaScript
|
||||||
|
# Normalize to standard type (it's sniffed in IE anyways)
|
||||||
|
# tools.ietf.org/html/rfc4329#section-7.2
|
||||||
|
AddType application/javascript js jsonp
|
||||||
|
AddType application/json json
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
AddType audio/mp4 m4a f4a f4b
|
||||||
|
AddType audio/ogg oga ogg
|
||||||
|
|
||||||
|
# Video
|
||||||
|
AddType video/mp4 mp4 m4v f4v f4p
|
||||||
|
AddType video/ogg ogv
|
||||||
|
AddType video/webm webm
|
||||||
|
AddType video/x-flv flv
|
||||||
|
|
||||||
|
# SVG
|
||||||
|
# Required for svg webfonts on iPad
|
||||||
|
# twitter.com/FontSquirrel/status/14855840545
|
||||||
|
AddType image/svg+xml svg svgz
|
||||||
|
AddEncoding gzip svgz
|
||||||
|
|
||||||
|
# Webfonts
|
||||||
|
AddType application/vnd.ms-fontobject eot
|
||||||
|
AddType application/x-font-ttf ttf ttc
|
||||||
|
AddType application/x-font-woff woff
|
||||||
|
AddType font/opentype otf
|
||||||
|
|
||||||
|
# Assorted types
|
||||||
|
AddType application/octet-stream safariextz
|
||||||
|
AddType application/x-chrome-extension crx
|
||||||
|
AddType application/x-opera-extension oex
|
||||||
|
AddType application/x-shockwave-flash swf
|
||||||
|
AddType application/x-web-app-manifest+json webapp
|
||||||
|
AddType application/x-xpinstall xpi
|
||||||
|
AddType application/xml rss atom xml rdf
|
||||||
|
AddType image/webp webp
|
||||||
|
AddType image/x-icon ico
|
||||||
|
AddType text/cache-manifest appcache manifest
|
||||||
|
AddType text/vtt vtt
|
||||||
|
AddType text/x-component htc
|
||||||
|
AddType text/x-vcard vcf
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Allow concatenation from within specific js and css files
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# e.g. Inside of script.combined.js you could have
|
||||||
|
# <!--#include file="libs/jquery-1.5.0.min.js" -->
|
||||||
|
# <!--#include file="plugins/jquery.idletimer.js" -->
|
||||||
|
# and they would be included into this single file.
|
||||||
|
|
||||||
|
# This is not in use in the boilerplate as it stands. You may
|
||||||
|
# choose to use this technique if you do not have a build process.
|
||||||
|
|
||||||
|
#<FilesMatch "\.combined\.js$">
|
||||||
|
# Options +Includes
|
||||||
|
# AddOutputFilterByType INCLUDES application/javascript application/json
|
||||||
|
# SetOutputFilter INCLUDES
|
||||||
|
#</FilesMatch>
|
||||||
|
|
||||||
|
#<FilesMatch "\.combined\.css$">
|
||||||
|
# Options +Includes
|
||||||
|
# AddOutputFilterByType INCLUDES text/css
|
||||||
|
# SetOutputFilter INCLUDES
|
||||||
|
#</FilesMatch>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Gzip compression
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
<IfModule mod_deflate.c>
|
||||||
|
|
||||||
|
# Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
|
||||||
|
<IfModule mod_setenvif.c>
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
|
||||||
|
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
|
||||||
|
</IfModule>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Compress all output labeled with one of the following MIME-types
|
||||||
|
# (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
|
||||||
|
# and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines as
|
||||||
|
# `AddOutputFilterByType` is still in the core directives)
|
||||||
|
<IfModule mod_filter.c>
|
||||||
|
AddOutputFilterByType DEFLATE application/atom+xml \
|
||||||
|
application/javascript \
|
||||||
|
application/json \
|
||||||
|
application/rss+xml \
|
||||||
|
application/vnd.ms-fontobject \
|
||||||
|
application/x-font-ttf \
|
||||||
|
application/xhtml+xml \
|
||||||
|
application/xml \
|
||||||
|
font/opentype \
|
||||||
|
image/svg+xml \
|
||||||
|
image/x-icon \
|
||||||
|
text/css \
|
||||||
|
text/html \
|
||||||
|
text/plain \
|
||||||
|
text/x-component \
|
||||||
|
text/xml
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Expires headers (for better cache control)
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# These are pretty far-future expires headers.
|
||||||
|
# They assume you control versioning with filename-based cache busting
|
||||||
|
# Additionally, consider that outdated proxies may miscache
|
||||||
|
# www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
|
||||||
|
|
||||||
|
# If you don't use filenames to version, lower the CSS and JS to something like
|
||||||
|
# "access plus 1 week".
|
||||||
|
|
||||||
|
<IfModule mod_expires.c>
|
||||||
|
ExpiresActive on
|
||||||
|
|
||||||
|
# Perhaps better to whitelist expires rules? Perhaps.
|
||||||
|
ExpiresDefault "access plus 1 month"
|
||||||
|
|
||||||
|
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
|
||||||
|
ExpiresByType text/cache-manifest "access plus 0 seconds"
|
||||||
|
|
||||||
|
# Your document html
|
||||||
|
ExpiresByType text/html "access plus 0 seconds"
|
||||||
|
|
||||||
|
# Data
|
||||||
|
ExpiresByType application/json "access plus 0 seconds"
|
||||||
|
ExpiresByType application/xml "access plus 0 seconds"
|
||||||
|
ExpiresByType text/xml "access plus 0 seconds"
|
||||||
|
|
||||||
|
# Feed
|
||||||
|
ExpiresByType application/atom+xml "access plus 1 hour"
|
||||||
|
ExpiresByType application/rss+xml "access plus 1 hour"
|
||||||
|
|
||||||
|
# Favicon (cannot be renamed)
|
||||||
|
ExpiresByType image/x-icon "access plus 1 week"
|
||||||
|
|
||||||
|
# Media: images, video, audio
|
||||||
|
ExpiresByType audio/ogg "access plus 1 month"
|
||||||
|
ExpiresByType image/gif "access plus 1 month"
|
||||||
|
ExpiresByType image/jpeg "access plus 1 month"
|
||||||
|
ExpiresByType image/png "access plus 1 month"
|
||||||
|
ExpiresByType video/mp4 "access plus 1 month"
|
||||||
|
ExpiresByType video/ogg "access plus 1 month"
|
||||||
|
ExpiresByType video/webm "access plus 1 month"
|
||||||
|
|
||||||
|
# HTC files (css3pie)
|
||||||
|
ExpiresByType text/x-component "access plus 1 month"
|
||||||
|
|
||||||
|
# Webfonts
|
||||||
|
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
|
||||||
|
ExpiresByType application/x-font-ttf "access plus 1 month"
|
||||||
|
ExpiresByType application/x-font-woff "access plus 1 month"
|
||||||
|
ExpiresByType font/opentype "access plus 1 month"
|
||||||
|
ExpiresByType image/svg+xml "access plus 1 month"
|
||||||
|
|
||||||
|
# CSS and JavaScript
|
||||||
|
ExpiresByType application/javascript "access plus 1 year"
|
||||||
|
ExpiresByType text/css "access plus 1 year"
|
||||||
|
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Prevent mobile network providers from modifying your site
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# The following header prevents modification of your code over 3G on some
|
||||||
|
# European providers.
|
||||||
|
# This is the official 'bypass' suggested by O2 in the UK.
|
||||||
|
|
||||||
|
# <IfModule mod_headers.c>
|
||||||
|
# Header set Cache-Control "no-transform"
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# ETag removal
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# FileETag None is not enough for every server.
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
Header unset ETag
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Since we're sending far-future expires, we don't need ETags for
|
||||||
|
# static content.
|
||||||
|
# developer.yahoo.com/performance/rules.html#etags
|
||||||
|
FileETag None
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Stop screen flicker in IE on CSS rollovers
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# The following directives stop screen flicker in IE on CSS rollovers - in
|
||||||
|
# combination with the "ExpiresByType" rules for images (see above).
|
||||||
|
|
||||||
|
# BrowserMatch "MSIE" brokenvary=1
|
||||||
|
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
|
||||||
|
# BrowserMatch "Opera" !brokenvary
|
||||||
|
# SetEnvIf brokenvary 1 force-no-vary
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Set Keep-Alive Header
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Keep-Alive allows the server to send multiple requests through one
|
||||||
|
# TCP-connection. Be aware of possible disadvantages of this setting. Turn on
|
||||||
|
# if you serve a lot of static content.
|
||||||
|
|
||||||
|
# <IfModule mod_headers.c>
|
||||||
|
# Header set Connection Keep-Alive
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Cookie setting from iframes
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Allow cookies to be set from iframes (for IE only)
|
||||||
|
# If needed, specify a path or regex in the Location directive.
|
||||||
|
|
||||||
|
# <IfModule mod_headers.c>
|
||||||
|
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Start rewrite engine
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Turning on the rewrite engine is necessary for the following rules and
|
||||||
|
# features. FollowSymLinks must be enabled for this to work.
|
||||||
|
|
||||||
|
# Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN
|
||||||
|
# If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where
|
||||||
|
# 'foo' is your directory.
|
||||||
|
|
||||||
|
# If your web host doesn't allow the FollowSymlinks option, you may need to
|
||||||
|
# comment it out and use `Options +SymLinksIfOwnerMatch`, but be aware of the
|
||||||
|
# performance impact: http://goo.gl/Mluzd
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
Options +FollowSymlinks
|
||||||
|
# Options +SymLinksIfOwnerMatch
|
||||||
|
RewriteEngine On
|
||||||
|
# RewriteBase /
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Suppress or force the "www." at the beginning of URLs
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# The same content should never be available under two different URLs -
|
||||||
|
# especially not with and without "www." at the beginning, since this can cause
|
||||||
|
# SEO problems (duplicate content). That's why you should choose one of the
|
||||||
|
# alternatives and redirect the other one.
|
||||||
|
|
||||||
|
# By default option 1 (no "www.") is activated.
|
||||||
|
# no-www.org/faq.php?q=class_b
|
||||||
|
|
||||||
|
# If you'd prefer to use option 2, just comment out all option 1 lines
|
||||||
|
# and uncomment option 2.
|
||||||
|
|
||||||
|
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Option 1:
|
||||||
|
# Rewrite "www.example.com -> example.com".
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteCond %{HTTPS} !=on
|
||||||
|
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||||
|
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Option 2:
|
||||||
|
# Rewrite "example.com -> www.example.com".
|
||||||
|
# Be aware that the following rule might not be a good idea if you use "real"
|
||||||
|
# subdomains for certain parts of your website.
|
||||||
|
|
||||||
|
# <IfModule mod_rewrite.c>
|
||||||
|
# RewriteCond %{HTTPS} !=on
|
||||||
|
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
|
||||||
|
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Built-in filename-based cache busting
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# If you're not using the build script to manage your filename version revving,
|
||||||
|
# you might want to consider enabling this, which will route requests for
|
||||||
|
# `/css/style.20110203.css` to `/css/style.css`.
|
||||||
|
|
||||||
|
# To understand why this is important and a better idea than all.css?v1231,
|
||||||
|
# please refer to the bundled documentation about `.htaccess`.
|
||||||
|
|
||||||
|
# <IfModule mod_rewrite.c>
|
||||||
|
# RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
# RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Prevent SSL cert warnings
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent
|
||||||
|
# https://www.example.com when your cert only allows https://secure.example.com
|
||||||
|
|
||||||
|
# <IfModule mod_rewrite.c>
|
||||||
|
# RewriteCond %{SERVER_PORT} !^443
|
||||||
|
# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Prevent 404 errors for non-existing redirected folders
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the
|
||||||
|
# same name does not exist.
|
||||||
|
# webmasterworld.com/apache/3808792.htm
|
||||||
|
|
||||||
|
Options -MultiViews
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Custom 404 page
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# You can add custom pages to handle 500 or 403 pretty easily, if you like.
|
||||||
|
# If you are hosting your site in subdirectory, adjust this accordingly
|
||||||
|
# e.g. ErrorDocument 404 /subdir/404.html
|
||||||
|
ErrorDocument 404 /404.html
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# UTF-8 encoding
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Use UTF-8 encoding for anything served text/plain or text/html
|
||||||
|
AddDefaultCharset utf-8
|
||||||
|
|
||||||
|
# Force UTF-8 for a number of file formats
|
||||||
|
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# A little more security
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# To avoid displaying the exact version number of Apache being used, add the
|
||||||
|
# following to httpd.conf (it will not work in .htaccess):
|
||||||
|
# ServerTokens Prod
|
||||||
|
|
||||||
|
# "-Indexes" will have Apache block users from browsing folders without a
|
||||||
|
# default document Usually you should leave this activated, because you
|
||||||
|
# shouldn't allow everybody to surf through every folder on your server (which
|
||||||
|
# includes rather private places like CMS system folders).
|
||||||
|
<IfModule mod_autoindex.c>
|
||||||
|
Options -Indexes
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Block access to "hidden" directories or files whose names begin with a
|
||||||
|
# period. This includes directories used by version control systems such as
|
||||||
|
# Subversion or Git.
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteCond %{SCRIPT_FILENAME} -d [OR]
|
||||||
|
RewriteCond %{SCRIPT_FILENAME} -f
|
||||||
|
RewriteRule "(^|/)\." - [F]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Block access to backup and source files. These files may be left by some
|
||||||
|
# text/html editors and pose a great security danger, when anyone can access
|
||||||
|
# them.
|
||||||
|
<FilesMatch "(\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|swp)|~)$">
|
||||||
|
Order allow,deny
|
||||||
|
Deny from all
|
||||||
|
Satisfy All
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# If your server is not already configured as such, the following directive
|
||||||
|
# should be uncommented in order to set PHP's register_globals option to OFF.
|
||||||
|
# This closes a major security hole that is abused by most XSS (cross-site
|
||||||
|
# scripting) attacks. For more information: http://php.net/register_globals
|
||||||
|
#
|
||||||
|
# IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS:
|
||||||
|
#
|
||||||
|
# Your server does not allow PHP directives to be set via .htaccess. In that
|
||||||
|
# case you must make this change in your php.ini file instead. If you are
|
||||||
|
# using a commercial web host, contact the administrators for assistance in
|
||||||
|
# doing this. Not all servers allow local php.ini files, and they should
|
||||||
|
# include all PHP configurations (not just this one), or you will effectively
|
||||||
|
# reset everything to PHP defaults. Consult www.php.net for more detailed
|
||||||
|
# information about setting PHP directives.
|
||||||
|
|
||||||
|
# php_flag register_globals Off
|
||||||
|
|
||||||
|
# Rename session cookie to something else, than PHPSESSID
|
||||||
|
# php_value session.name sid
|
||||||
|
|
||||||
|
# Disable magic quotes (This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.)
|
||||||
|
# php_flag magic_quotes_gpc Off
|
||||||
|
|
||||||
|
# Do not show you are using PHP
|
||||||
|
# Note: Move this line to php.ini since it won't work in .htaccess
|
||||||
|
# php_flag expose_php Off
|
||||||
|
|
||||||
|
# Level of log detail - log all errors
|
||||||
|
# php_value error_reporting -1
|
||||||
|
|
||||||
|
# Write errors to log file
|
||||||
|
# php_flag log_errors On
|
||||||
|
|
||||||
|
# Do not display errors in browser (production - Off, development - On)
|
||||||
|
# php_flag display_errors Off
|
||||||
|
|
||||||
|
# Do not display startup errors (production - Off, development - On)
|
||||||
|
# php_flag display_startup_errors Off
|
||||||
|
|
||||||
|
# Format errors in plain text
|
||||||
|
# Note: Leave this setting 'On' for xdebug's var_dump() output
|
||||||
|
# php_flag html_errors Off
|
||||||
|
|
||||||
|
# Show multiple occurrence of error
|
||||||
|
# php_flag ignore_repeated_errors Off
|
||||||
|
|
||||||
|
# Show same errors from different sources
|
||||||
|
# php_flag ignore_repeated_source Off
|
||||||
|
|
||||||
|
# Size limit for error messages
|
||||||
|
# php_value log_errors_max_len 1024
|
||||||
|
|
||||||
|
# Don't precede error with string (doesn't accept empty string, use whitespace if you need)
|
||||||
|
# php_value error_prepend_string " "
|
||||||
|
|
||||||
|
# Don't prepend to error (doesn't accept empty string, use whitespace if you need)
|
||||||
|
# php_value error_append_string " "
|
||||||
|
|
||||||
|
# Increase cookie security
|
||||||
|
<IfModule mod_php5.c>
|
||||||
|
php_value session.cookie_httponly true
|
||||||
|
</IfModule>
|
||||||
157
app/404.html
Normal file
157
app/404.html
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Page Not Found :(</title>
|
||||||
|
<style>
|
||||||
|
::-moz-selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
padding: 30px 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #737373;
|
||||||
|
background: #f0f0f0;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
input {
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
max-width: 500px;
|
||||||
|
_width: 500px;
|
||||||
|
padding: 30px 20px 50px;
|
||||||
|
border: 1px solid #b3b3b3;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
|
||||||
|
background: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0 10px;
|
||||||
|
font-size: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 span {
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 1.5em 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0 0 0 40px;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 380px;
|
||||||
|
_width: 380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* google search */
|
||||||
|
|
||||||
|
#goog-fixurl ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-fixurl form {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-wm-qt,
|
||||||
|
#goog-wm-sb {
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: normal;
|
||||||
|
vertical-align: top;
|
||||||
|
color: #444;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-wm-qt {
|
||||||
|
width: 220px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px 10px 0 0;
|
||||||
|
box-shadow: inset 0 1px 1px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-wm-sb {
|
||||||
|
display: inline-block;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||||
|
background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||||
|
background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||||
|
background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
*overflow: visible;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-wm-sb:hover,
|
||||||
|
#goog-wm-sb:focus {
|
||||||
|
border-color: #aaa;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goog-wm-qt:hover,
|
||||||
|
#goog-wm-qt:focus {
|
||||||
|
border-color: #105cb6;
|
||||||
|
outline: 0;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Not found <span>:(</span></h1>
|
||||||
|
<p>Sorry, but the page you were trying to view does not exist.</p>
|
||||||
|
<p>It looks like this was the result of either:</p>
|
||||||
|
<ul>
|
||||||
|
<li>a mistyped address</li>
|
||||||
|
<li>an out-of-date link</li>
|
||||||
|
</ul>
|
||||||
|
<script>
|
||||||
|
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
|
||||||
|
</script>
|
||||||
|
<script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
app/api/config.php
Normal file
20
app/api/config.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$config = array(
|
||||||
|
'server' => 'localhost',
|
||||||
|
'db' => 'lentolaskuri2',
|
||||||
|
'user' => 'lentolaskuri2',
|
||||||
|
'password' => '8@89z~UIwavn',
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
$mysqli = new mysqli($config['server'], $config['user'], $config['password'], $config['db']);
|
||||||
|
|
||||||
|
/* check connection */
|
||||||
|
if (mysqli_connect_errno()) {
|
||||||
|
printf("Connect failed: %s\n", mysqli_connect_error());
|
||||||
|
exit();
|
||||||
|
}
|
||||||
28
app/api/import.php
Normal file
28
app/api/import.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once('config.php');
|
||||||
|
|
||||||
|
if (($handle = fopen("http://openflights.svn.sourceforge.net/viewvc/openflights/openflights/data/airports.dat", "r")) !== FALSE) {
|
||||||
|
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
|
||||||
|
$id = $mysqli->real_escape_string($data[0]);
|
||||||
|
$name = $mysqli->real_escape_string($data[1]);
|
||||||
|
$city = $mysqli->real_escape_string($data[2]);
|
||||||
|
$country = $mysqli->real_escape_string($data[3]);
|
||||||
|
$iata = $mysqli->real_escape_string($data[4]);
|
||||||
|
$icao = $mysqli->real_escape_string($data[5]);
|
||||||
|
if (empty($iata)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$lat = $mysqli->real_escape_string($data[6]);
|
||||||
|
$long = $mysqli->real_escape_string($data[7]);
|
||||||
|
|
||||||
|
$query = "REPLACE INTO airports (id, name, city, country, iata, icao, lat, `long`) VALUES ('$id', '$name', '$city', '$country', '$iata', '$icao', '$lat', '$long')";
|
||||||
|
if (!$mysqli->query($query)) {
|
||||||
|
printf("Error: %s\n", $mysqli->sqlstate);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mysqli->close();
|
||||||
54
app/api/search.php
Normal file
54
app/api/search.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once('config.php');
|
||||||
|
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
|
||||||
|
$search = $mysqli->real_escape_string($_GET['s']);
|
||||||
|
$id = $mysqli->real_escape_string($_GET['i']);
|
||||||
|
if (empty($search) && empty($id)) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = "";
|
||||||
|
if (!empty($search)) {
|
||||||
|
$query = "
|
||||||
|
SELECT *,
|
||||||
|
CASE
|
||||||
|
WHEN iata LIKE '$search%' THEN 100
|
||||||
|
WHEN name LIKE '$search%' THEN 75
|
||||||
|
WHEN name LIKE '%$search%' THEN 74
|
||||||
|
WHEN city LIKE '$search%' THEN 70
|
||||||
|
WHEN country LIKE '$search%' THEN 65
|
||||||
|
WHEN city LIKE '%$search%' THEN 60
|
||||||
|
WHEN country LIKE '%$search%' THEN 55
|
||||||
|
ELSE 0 END
|
||||||
|
AS score
|
||||||
|
FROM airports
|
||||||
|
WHERE
|
||||||
|
name LIKE '$search%' OR
|
||||||
|
name LIKE '%$search%' OR
|
||||||
|
country LIKE '$search%' OR
|
||||||
|
country LIKE '%$search%' OR
|
||||||
|
city LIKE '$search%' OR
|
||||||
|
city LIKE '%$search%' OR
|
||||||
|
iata LIKE '$search%'
|
||||||
|
ORDER BY score DESC
|
||||||
|
LIMIT 10;
|
||||||
|
";
|
||||||
|
} else if (!empty($id)) {
|
||||||
|
$query ="SELECT * FROM airports WHERE id LIKE '$id';";
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
if ($result = $mysqli->query($query)) {
|
||||||
|
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$results[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json_encode($results));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("Error: %s\n", $mysqli->sqlstate);
|
||||||
|
}
|
||||||
BIN
app/favicon.ico
Normal file
BIN
app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
77
app/index.html
Normal file
77
app/index.html
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<title>Lentolaskuri 2</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<link rel="stylesheet" href="styles/main.css">
|
||||||
|
</head>
|
||||||
|
<body data-views="calculator maps">
|
||||||
|
|
||||||
|
<div class="container calculator">
|
||||||
|
<div class="row">
|
||||||
|
<div class="span12">
|
||||||
|
<h1>Lentolaskuri2</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span3 passengersWidget">
|
||||||
|
<legend>
|
||||||
|
Matkustajia
|
||||||
|
</legend>
|
||||||
|
<input type="number" placeholder="1" class="span3" />
|
||||||
|
</div>
|
||||||
|
<div class="span3 roundtripWidget">
|
||||||
|
<!-- <legend>Meno-paluu</legend> -->
|
||||||
|
<legend> </legend>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn active">Meno</button>
|
||||||
|
<button type="button" class="btn roundtrip">Meno-paluu</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span6">
|
||||||
|
<legend>Reitti</legend>
|
||||||
|
<div class="route"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls row-fluid">
|
||||||
|
<div class="span6 legInputWidget">
|
||||||
|
<button class="btn btn-block"><span>Valitse lentokenttä</span> <i class="caret"></i></button>
|
||||||
|
<input type="hidden"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span6 totalWidget"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span6">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active"><a href="#map" data-toggle="tab">Kartta</a></li>
|
||||||
|
<li><a href="#operation" data-toggle="tab">Laskurin toiminta</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="map">
|
||||||
|
<div id="gmap" class="map"></div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" id="operation">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- build:js scripts/main.js -->
|
||||||
|
<script data-main="scripts/main" src="components/requirejs/require.js"></script>
|
||||||
|
<!-- endbuild -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
app/robots.txt
Normal file
3
app/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# robotstxt.org
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
40
app/scripts/app.js
Normal file
40
app/scripts/app.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'collections/route',
|
||||||
|
'collections/results',
|
||||||
|
'views/route',
|
||||||
|
'views/leginput',
|
||||||
|
'views/roundtrip',
|
||||||
|
'views/passengers',
|
||||||
|
'views/map',
|
||||||
|
'views/total',
|
||||||
|
'views/operation'
|
||||||
|
], function (Route, Results, RouteView, LegInput, RoundtripInput, PassengersInput, MapView, TotalView, OperationView) {
|
||||||
|
|
||||||
|
var App = function () {
|
||||||
|
this.initialize = function () {
|
||||||
|
this.route = new Route();
|
||||||
|
|
||||||
|
this.legInput = new LegInput({collection: this.route});
|
||||||
|
this.roundtripInput = new RoundtripInput();
|
||||||
|
this.passengersInput = new PassengersInput();
|
||||||
|
|
||||||
|
this.routeView = new RouteView({collection: this.route});
|
||||||
|
this.mapView = new MapView({collection: this.route});
|
||||||
|
|
||||||
|
// Order is important. ResultsView requires that Route legs are
|
||||||
|
// on dom as ResultView references from and to legs to calculate
|
||||||
|
// position.
|
||||||
|
this.results = new Results([], {route: this.route});
|
||||||
|
this.totalView = new TotalView({model: this.results.total});
|
||||||
|
|
||||||
|
new OperationView();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initialize();
|
||||||
|
};
|
||||||
|
|
||||||
|
return App;
|
||||||
|
|
||||||
|
});
|
||||||
53
app/scripts/collections/results.js
Normal file
53
app/scripts/collections/results.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'models/result',
|
||||||
|
'models/total'
|
||||||
|
], function (Backbone, Result, Total) {
|
||||||
|
var Results = Backbone.Collection.extend({
|
||||||
|
model: Result,
|
||||||
|
initialize: function (model, opts) {
|
||||||
|
this.route = opts.route;
|
||||||
|
|
||||||
|
this.route.on('add', this.newLeg, this);
|
||||||
|
this.route.on('remove', this.calculate, this);
|
||||||
|
this.route.on('sort', this.calculate, this);
|
||||||
|
|
||||||
|
this.total = new Total();
|
||||||
|
},
|
||||||
|
newLeg: function (model, collection, options) {
|
||||||
|
if (this.route.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var from;
|
||||||
|
if (this.length > 0) {
|
||||||
|
from = this.route.get(this.last().get('to'));
|
||||||
|
} else {
|
||||||
|
from = this.route.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new Result({from: from, to: model});
|
||||||
|
this.push(result);
|
||||||
|
this.total.add(result);
|
||||||
|
},
|
||||||
|
calculate: function (model, collection, options) {
|
||||||
|
var models = [];
|
||||||
|
this.total.reset();
|
||||||
|
|
||||||
|
var prev = null;
|
||||||
|
this.route.forEach(function (leg) {
|
||||||
|
if (prev) {
|
||||||
|
var result = new Result({from: prev, to: leg});
|
||||||
|
models.push(result);
|
||||||
|
this.total.add(result);
|
||||||
|
}
|
||||||
|
prev = leg;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.reset(models);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Results;
|
||||||
|
});
|
||||||
19
app/scripts/collections/route.js
Normal file
19
app/scripts/collections/route.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
define([
|
||||||
|
'lodash',
|
||||||
|
'backbone',
|
||||||
|
'../models/leg'
|
||||||
|
], function (_, Backbone, Leg) {
|
||||||
|
|
||||||
|
// var stages = [];
|
||||||
|
// var data = {};
|
||||||
|
var num = 1;
|
||||||
|
|
||||||
|
var Route = Backbone.Collection.extend({
|
||||||
|
model: Leg,
|
||||||
|
comparator: function (model) {
|
||||||
|
return model.get('order');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Route;
|
||||||
|
});
|
||||||
64
app/scripts/config.js
Normal file
64
app/scripts/config.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define(function () {
|
||||||
|
return {
|
||||||
|
api: 'http://work.lentolaskuri.fi/api',
|
||||||
|
R: 6371,
|
||||||
|
radiativeForceFactor: function (dist) {
|
||||||
|
return (dist >= 500) ? 2.0 : 1.0;
|
||||||
|
},
|
||||||
|
distanceRanges: [{
|
||||||
|
name: '0',
|
||||||
|
}, {
|
||||||
|
name: '500',
|
||||||
|
min: 500
|
||||||
|
}, {
|
||||||
|
name: '1400',
|
||||||
|
min: 1400
|
||||||
|
}, {
|
||||||
|
name: '5500',
|
||||||
|
min: 5500,
|
||||||
|
}, {
|
||||||
|
name: '9000',
|
||||||
|
min: 9000
|
||||||
|
}],
|
||||||
|
parameters: [{
|
||||||
|
name: 'Suomi',
|
||||||
|
regex: /^EF/,
|
||||||
|
// Parameters for different distances
|
||||||
|
co2factor: [0.08, 0.07, 0.07, 0.07, 0.07],
|
||||||
|
ltoCycle: [15, 15, 15, 15, 15],
|
||||||
|
load: [0.6, 0.65, 0.65, 0.65, 0.65],
|
||||||
|
freight: [0.05, 0.05, 0.05, 0.05, 0.05]
|
||||||
|
}, {
|
||||||
|
name: 'Pohjois-Eurooppa',
|
||||||
|
regex: /^E/,
|
||||||
|
co2factor: [0.07, 0.06, 0.06, 0.06, 0.06],
|
||||||
|
ltoCycle: [22, 22, 22, 22, 22],
|
||||||
|
load: [0.65, 0.7, 0.75, 0.75, 0.75],
|
||||||
|
freight: [0.05, 0.1, 0.1, 0.1, 0.1]
|
||||||
|
}, {
|
||||||
|
name: 'Etelä-Eurooppa',
|
||||||
|
regex: /^L/,
|
||||||
|
co2factor: [0.07, 0.07, 0.07, 0.07, 0.07],
|
||||||
|
ltoCycle: [22, 22, 22, 22, 22],
|
||||||
|
load: [0.65, 0.7, 0.8, 0.8, 0.8],
|
||||||
|
freight: [0.05, 0.1, 0.1, 0.1, 0.1],
|
||||||
|
}, {
|
||||||
|
name: 'Euroopasta/aan',
|
||||||
|
co2factor: [0.07, 0.07, 0.08, 0.09, 0.09],
|
||||||
|
ltoCycle: [21, 24, 22, 26, 26], // 3rd value?
|
||||||
|
load: [0.7, 0.7, 0.8, 0.8, 0.8],
|
||||||
|
freight: [0.05, 0.15, 0.15, 0.15, 0.15]
|
||||||
|
}, {
|
||||||
|
name: 'Euroopan ulkopuoliset',
|
||||||
|
co2factor: [0.08, 0.07, 0.08, 0.08, 0.09], // 2th value?
|
||||||
|
ltoCycle: [21, 22, 23, 26, 26],
|
||||||
|
load: [0.7, 0.7, 0.8, 0.8, 0.8],
|
||||||
|
freight: [0.05, 0.1, 0.15, 0.2, 0.2]
|
||||||
|
}],
|
||||||
|
indirectRouteMultiplier: 1.09,
|
||||||
|
// http://co2-raportti.fi/?heading=EU:n-p%C3%A4%C3%A4st%C3%B6kaupan-arvo-laski-kolmanneksen-t%C3%A4n%C3%A4-vuonna&page=ilmastouutisia&news_id=3665
|
||||||
|
priceCO2: 7.5, // euros per tonne of CO2
|
||||||
|
};
|
||||||
|
});
|
||||||
75
app/scripts/libs/airports.js
Normal file
75
app/scripts/libs/airports.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'lodash',
|
||||||
|
'config'
|
||||||
|
], function ($, _, config) {
|
||||||
|
var airportById = function (element, callback) {
|
||||||
|
$.get('http://localhost:8000/search.php?i=' + element.val(), null, function (data) {
|
||||||
|
callback(data[0]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var data = function (term, page) {
|
||||||
|
return {
|
||||||
|
s: term
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = function (data, page) {
|
||||||
|
return {results: data};
|
||||||
|
};
|
||||||
|
|
||||||
|
var findArea = function (icao) {
|
||||||
|
for (var i = 0; i < config.parameters.length; ++i) {
|
||||||
|
var param = config.parameters[i];
|
||||||
|
if (param.regex && icao.match(param.regex)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.parameters.length - 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectArea = function (fromCode, toCode) {
|
||||||
|
var outside = config.parameters.length - 2;
|
||||||
|
if (fromCode === outside && toCode === outside) {
|
||||||
|
return config.parameters.length - 1;
|
||||||
|
}
|
||||||
|
return Math.max(fromCode, toCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectRange = function (dist) {
|
||||||
|
for (var i = 0; i < config.distanceRanges.length; ++i) {
|
||||||
|
if (config.distanceRanges[i].min && dist <= config.distanceRanges[i].min) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Should select "greater of two", finland to western europe -> west europe
|
||||||
|
// west europe to south europe -> south europe
|
||||||
|
var parameters = function (from, to, range) {
|
||||||
|
var i = selectArea(findArea(from.icao), findArea(to.icao));
|
||||||
|
var p = config.parameters[i];
|
||||||
|
return {
|
||||||
|
name: p.name,
|
||||||
|
co2factor: p.co2factor[range],
|
||||||
|
ltoCycle: p.ltoCycle[range],
|
||||||
|
load: p.load[range],
|
||||||
|
freight: p.freight[range]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
ajax: {
|
||||||
|
url: config.api + '/search.php',
|
||||||
|
dataType: 'json',
|
||||||
|
data: data,
|
||||||
|
results: results
|
||||||
|
},
|
||||||
|
airportById: airportById,
|
||||||
|
selectRange: selectRange,
|
||||||
|
parameters: parameters
|
||||||
|
};
|
||||||
|
});
|
||||||
9
app/scripts/libs/gmaps.js
Normal file
9
app/scripts/libs/gmaps.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// convert Google Maps into an AMD module
|
||||||
|
define([
|
||||||
|
'async!http://maps.google.com/maps/api/js?v=3&sensor=false'
|
||||||
|
], function() {
|
||||||
|
// return the gmaps namespace for brevity
|
||||||
|
return window.google.maps;
|
||||||
|
});
|
||||||
10
app/scripts/libs/handlebar_helpers.js
Normal file
10
app/scripts/libs/handlebar_helpers.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'handlebars'
|
||||||
|
], function (Handlebars) {
|
||||||
|
Handlebars.registerHelper('displayFloat', function (num, precision) {
|
||||||
|
precision = precision;
|
||||||
|
return ''+(num.toFixed(precision)).replace('.', ',');
|
||||||
|
});
|
||||||
|
});
|
||||||
120
app/scripts/libs/maps.js
Normal file
120
app/scripts/libs/maps.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'require',
|
||||||
|
'jquery',
|
||||||
|
'lodash',
|
||||||
|
'backbone'
|
||||||
|
], function (require, $, _, Backbone) {
|
||||||
|
var gmaps;
|
||||||
|
|
||||||
|
function glatlng(latlng) {
|
||||||
|
if (_.isArray(latlng)) {
|
||||||
|
return new gmaps.LatLng(latlng[0], latlng[1]);
|
||||||
|
} else if (latlng instanceof Backbone.Model) {
|
||||||
|
return new gmaps.LatLng(latlng.get('lat'), latlng.get('long'));
|
||||||
|
} else {
|
||||||
|
return new gmaps.LatLng(latlng.lat, latlng.long);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var maps = {};
|
||||||
|
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var Map = function (el, opts) {
|
||||||
|
var num = i;
|
||||||
|
deferred.done(function () {
|
||||||
|
maps[num] = new gmaps.Map(el, {
|
||||||
|
center: glatlng([0, 0]),
|
||||||
|
mapTypeId: gmaps.MapTypeId.ROADMAP,
|
||||||
|
zoom: 0,
|
||||||
|
streetViewControl: false,
|
||||||
|
mapTypeControl: false,
|
||||||
|
draggable: false,
|
||||||
|
scrollwheel: false,
|
||||||
|
zoomControl: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: i++
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var Line = function (attr) {
|
||||||
|
var data;
|
||||||
|
deferred.done(function () {
|
||||||
|
attr = attr || {};
|
||||||
|
attr.map = maps[attr.map.id];
|
||||||
|
|
||||||
|
data = new gmaps.Polyline(attr);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
clear: function () {
|
||||||
|
deferred.done(function () {
|
||||||
|
data.setPath([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
add: function (latlng) {
|
||||||
|
deferred.done(function () {
|
||||||
|
data.getPath().push(glatlng(latlng));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var Bounds = function (map) {
|
||||||
|
var map;
|
||||||
|
var i;
|
||||||
|
var data;
|
||||||
|
|
||||||
|
var reset = function () {
|
||||||
|
i = 0;
|
||||||
|
map.setZoom(1);
|
||||||
|
map.setCenter(glatlng([0, 0]));
|
||||||
|
};
|
||||||
|
|
||||||
|
deferred.done(function () {
|
||||||
|
data = new gmaps.LatLngBounds();
|
||||||
|
map = maps[map.id];
|
||||||
|
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
clear: function () {
|
||||||
|
deferred.done(function () {
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
add: function (latlng) {
|
||||||
|
deferred.done(function () {
|
||||||
|
data.extend(glatlng(latlng));
|
||||||
|
++i;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
use: function () {
|
||||||
|
deferred.done(function () {
|
||||||
|
if (i >= 2) {
|
||||||
|
map.fitBounds(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
require(['libs/gmaps'], function (_gmaps) {
|
||||||
|
gmaps = _gmaps;
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
Map: Map,
|
||||||
|
Line: Line,
|
||||||
|
Bounds: Bounds
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
26
app/scripts/libs/math.js
Normal file
26
app/scripts/libs/math.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
define([
|
||||||
|
'config',
|
||||||
|
'libs/airports'
|
||||||
|
], function (cfg, airports) {
|
||||||
|
var toRad = function toRad(n) {
|
||||||
|
return n * Math.PI / 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
var haversine = function haversine(from, to) {
|
||||||
|
var dLat = toRad(to.get('lat') - from.get('lat'));
|
||||||
|
var dLon = toRad(to.get('long') - from.get('long'));
|
||||||
|
var lat1 = toRad(from.get('lat'));
|
||||||
|
var lat2 = toRad(to.get('lat'));
|
||||||
|
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
|
||||||
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return cfg.R * c;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
toRad: toRad,
|
||||||
|
haversine: haversine
|
||||||
|
};
|
||||||
|
});
|
||||||
58
app/scripts/main.js
Normal file
58
app/scripts/main.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require.config({
|
||||||
|
map: {
|
||||||
|
'*': {
|
||||||
|
'underscore': 'lodash'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
jquery: '../components/jquery/jquery',
|
||||||
|
bootstrap: '../scripts/bootstrap',
|
||||||
|
select2: '../components/select2/select2',
|
||||||
|
lodash: '../components/lodash/lodash',
|
||||||
|
handlebars: '../components/handlebars/handlebars.runtime',
|
||||||
|
Template: '../scripts/templates',
|
||||||
|
'jquery-ui-core': '../components/jquery-ui/ui/jquery.ui.core',
|
||||||
|
'jquery-ui-mouse': '../components/jquery-ui/ui/jquery.ui.mouse',
|
||||||
|
'jquery-ui-widget': '../components/jquery-ui/ui/jquery.ui.widget',
|
||||||
|
'jquery-ui-sortable': '../components/jquery-ui/ui/jquery.ui.sortable',
|
||||||
|
async: '../components/requirejs-plugins/src/async',
|
||||||
|
backbone: '../components/backbone/backbone',
|
||||||
|
'backbone-mediator': '../components/Backbone-Mediator/backbone-mediator'
|
||||||
|
},
|
||||||
|
shim: {
|
||||||
|
backbone: {
|
||||||
|
deps: ['lodash'],
|
||||||
|
exports: 'Backbone'
|
||||||
|
},
|
||||||
|
bootstrap: {
|
||||||
|
deps: ['jquery'],
|
||||||
|
exports: 'jquery'
|
||||||
|
},
|
||||||
|
select2: {
|
||||||
|
deps: ['jquery'],
|
||||||
|
exports: 'jquery'
|
||||||
|
},
|
||||||
|
'jquery-ui-core': {
|
||||||
|
deps: ['jquery'],
|
||||||
|
exports: 'jquery'
|
||||||
|
},
|
||||||
|
'jquery-ui-sortable': {
|
||||||
|
deps: ['jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-mouse'],
|
||||||
|
exports: 'jquery'
|
||||||
|
},
|
||||||
|
handlebars: {
|
||||||
|
exports: 'Handlebars'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
require([
|
||||||
|
'jquery',
|
||||||
|
'app',
|
||||||
|
'bootstrap',
|
||||||
|
'libs/handlebar_helpers'
|
||||||
|
], function ($, App) {
|
||||||
|
var app = new App();
|
||||||
|
});
|
||||||
20
app/scripts/models/leg.js
Normal file
20
app/scripts/models/leg.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
define([
|
||||||
|
'backbone'
|
||||||
|
], function (Backbone) {
|
||||||
|
|
||||||
|
var num = 1;
|
||||||
|
|
||||||
|
var Leg = Backbone.Model.extend({
|
||||||
|
initialize: function () {
|
||||||
|
this.set('airportId', this.get('id'));
|
||||||
|
this.set('id', num);
|
||||||
|
++num;
|
||||||
|
},
|
||||||
|
destroy: function () {
|
||||||
|
this.trigger('destroy', this, this.collection, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Leg;
|
||||||
|
|
||||||
|
});
|
||||||
45
app/scripts/models/result.js
Normal file
45
app/scripts/models/result.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'backbone-mediator',
|
||||||
|
'libs/math',
|
||||||
|
'libs/airports',
|
||||||
|
'config'
|
||||||
|
], function (Backbone, BackboneMediator, Math, airports, config) {
|
||||||
|
var Result = Backbone.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
dist: 0,
|
||||||
|
total: 0,
|
||||||
|
lto: 0,
|
||||||
|
co2factor: 0,
|
||||||
|
ltoCycle: 0,
|
||||||
|
load: 0,
|
||||||
|
freight: 0
|
||||||
|
},
|
||||||
|
initialize: function (attr, options) {
|
||||||
|
if (this.get('from') && this.get('to')) {
|
||||||
|
this.set('dist', Math.haversine(this.get('from'), this.get('to')));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.calculate.apply(this);
|
||||||
|
},
|
||||||
|
calculate: function () {
|
||||||
|
var roundtrip = 0;
|
||||||
|
if (this.get('from') && this.get('to')) {
|
||||||
|
var roundtripFactor = (roundtrip) ? 2.0 : 1.0; // fuu
|
||||||
|
|
||||||
|
this.set('range', airports.selectRange(this.get('dist')));
|
||||||
|
this.set(airports.parameters(this.get('from').toJSON(), this.get('to').toJSON(), this.get('range')));
|
||||||
|
|
||||||
|
var dist = this.get('dist') * config.indirectRouteMultiplier;
|
||||||
|
|
||||||
|
var m = config.radiativeForceFactor(dist) * (1 - this.get('freight')) * (1 / this.get('load'));
|
||||||
|
this.set('lto', m * this.get('ltoCycle'));
|
||||||
|
this.set('total', m * this.get('co2factor') * dist + this.get('lto'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
});
|
||||||
54
app/scripts/models/total.js
Normal file
54
app/scripts/models/total.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'backbone-mediator',
|
||||||
|
'libs/math',
|
||||||
|
'libs/airports',
|
||||||
|
'config'
|
||||||
|
], function (Backbone, BackboneMediator, Math, airports, config) {
|
||||||
|
var Result = Backbone.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
dist: 0,
|
||||||
|
total: 0,
|
||||||
|
rawTotal: 0,
|
||||||
|
alone: true,
|
||||||
|
roundtrip: false,
|
||||||
|
passengers: 1,
|
||||||
|
price: 0
|
||||||
|
},
|
||||||
|
initialize: function () {
|
||||||
|
Backbone.Mediator.subscribe('roundtrip:change', this.set.bind(this, 'roundtrip'));
|
||||||
|
Backbone.Mediator.subscribe('passengers:change', this.set.bind(this, 'passengers'));
|
||||||
|
Backbone.Mediator.subscribe('passengers:change', function (num) {
|
||||||
|
this.set('alone', num === 1);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.on('change:roundtrip change:passengers', this.calc, this);
|
||||||
|
},
|
||||||
|
reset: function () {
|
||||||
|
this.set('dist', 0);
|
||||||
|
this.set('total', 0);
|
||||||
|
this.set('rawTotal', 0);
|
||||||
|
this.set('price', 0);
|
||||||
|
},
|
||||||
|
calc: function () {
|
||||||
|
var mult = this.get('roundtrip') ? 2 : 1;
|
||||||
|
this.set('total', mult * this.get('passengers') * this.get('rawTotal'));
|
||||||
|
this.set('price', this.get('total') / 1000 * config.priceCO2);
|
||||||
|
},
|
||||||
|
add: function (another) {
|
||||||
|
var keys = {
|
||||||
|
'dist': 'dist',
|
||||||
|
'total': 'rawTotal'
|
||||||
|
};
|
||||||
|
for (var i in keys) {
|
||||||
|
this.set(keys[i], this.get(keys[i]) + another.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.calc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
});
|
||||||
22
app/scripts/views/dropdown.js
Normal file
22
app/scripts/views/dropdown.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'select2'
|
||||||
|
], function (Backbone) {
|
||||||
|
var Dropdown = Backbone.View.extend({
|
||||||
|
events: {
|
||||||
|
'change': 'change'
|
||||||
|
},
|
||||||
|
initialize: function (opts) {
|
||||||
|
this.$el.select2(opts.select2);
|
||||||
|
},
|
||||||
|
open: function () {
|
||||||
|
this.$el.select2('open');
|
||||||
|
},
|
||||||
|
change: function () {
|
||||||
|
this.trigger('new-leg', this.$el.select2('data'));
|
||||||
|
this.$el.select2('data', {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Dropdown;
|
||||||
|
});
|
||||||
29
app/scripts/views/leg.js
Normal file
29
app/scripts/views/leg.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'Template'
|
||||||
|
], function (Backbone, Template) {
|
||||||
|
var LegView = Backbone.View.extend({
|
||||||
|
tagName: 'div',
|
||||||
|
className: 'leg',
|
||||||
|
events: {
|
||||||
|
'click a.delete': 'destroy'
|
||||||
|
},
|
||||||
|
initialize: function () {
|
||||||
|
this.model.on('change', this.render, this);
|
||||||
|
this.model.on('destroy', this.remove, this);
|
||||||
|
},
|
||||||
|
destroy: function () {
|
||||||
|
this.model.destroy();
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
this.$el.data('id', this.model.get('id'));
|
||||||
|
this.$el.attr('id', 'leg-' + this.model.get('id')); // fuu
|
||||||
|
this.$el.html(Template.leg(this.model.toJSON()));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return LegView;
|
||||||
|
});
|
||||||
39
app/scripts/views/leginput.js
Normal file
39
app/scripts/views/leginput.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'models/leg',
|
||||||
|
'views/dropdown',
|
||||||
|
'libs/airports',
|
||||||
|
'Template'
|
||||||
|
], function (Backbone, Leg, Dropdown, airports, Template) {
|
||||||
|
var LegInput = Backbone.View.extend({
|
||||||
|
el: '.legInputWidget',
|
||||||
|
events: {
|
||||||
|
'click button': 'openDropdown'
|
||||||
|
},
|
||||||
|
initialize: function () {
|
||||||
|
this.dropdown = new Dropdown({
|
||||||
|
el: this.$el.find('input'),
|
||||||
|
select2: {
|
||||||
|
initSelection: airports.airportById,
|
||||||
|
formatResult: Template.choice,
|
||||||
|
formatSelection: Template.choice,
|
||||||
|
minimumInputLength: 1,
|
||||||
|
ajax: airports.ajax
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dropdown.bind('new-leg', this.newLeg, this);
|
||||||
|
},
|
||||||
|
openDropdown: function () {
|
||||||
|
this.dropdown.open();
|
||||||
|
},
|
||||||
|
newLeg: function (data) {
|
||||||
|
this.collection.push(new Leg(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return LegInput;
|
||||||
|
});
|
||||||
|
|
||||||
41
app/scripts/views/map.js
Normal file
41
app/scripts/views/map.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'libs/maps'
|
||||||
|
], function (Backbone, Maps) {
|
||||||
|
|
||||||
|
var MapView = Backbone.View.extend({
|
||||||
|
el: '#gmap',
|
||||||
|
initialize: function () {
|
||||||
|
this.collection.on('sort', this.render, this);
|
||||||
|
this.collection.on('remove', this.render, this);
|
||||||
|
this.collection.on('add', this.add, this);
|
||||||
|
|
||||||
|
this.map = new Maps.Map(this.el);
|
||||||
|
|
||||||
|
this.route = new Maps.Line({
|
||||||
|
geodesic: true,
|
||||||
|
map: this.map,
|
||||||
|
strokeColor: '#000'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.bounds = new Maps.Bounds(this.map);
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
this.route.clear();
|
||||||
|
this.bounds.clear();
|
||||||
|
this.collection.forEach(this.route.add);
|
||||||
|
this.collection.forEach(this.bounds.add);
|
||||||
|
this.bounds.use();
|
||||||
|
},
|
||||||
|
add: function (model, collection, options) {
|
||||||
|
this.route.add(model);
|
||||||
|
this.bounds.add(model);
|
||||||
|
this.bounds.use();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return MapView;
|
||||||
|
|
||||||
|
});
|
||||||
22
app/scripts/views/operation.js
Normal file
22
app/scripts/views/operation.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'Template',
|
||||||
|
'config',
|
||||||
|
'bootstrap'
|
||||||
|
], function (Backbone, Template, config) {
|
||||||
|
var Operation = Backbone.View.extend({
|
||||||
|
el: '#operation',
|
||||||
|
initialize: function () {
|
||||||
|
this.render.apply(this);
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
this.$el.html(Template.operation(config));
|
||||||
|
this.$el.find('abbr').tooltip();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Operation;
|
||||||
|
});
|
||||||
28
app/scripts/views/passengers.js
Normal file
28
app/scripts/views/passengers.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'backbone-mediator'
|
||||||
|
], function (Backbone) {
|
||||||
|
var PassengersInput = Backbone.View.extend({
|
||||||
|
el: '.passengersWidget',
|
||||||
|
events: {
|
||||||
|
'change input': 'change',
|
||||||
|
'keyup input': 'change'
|
||||||
|
},
|
||||||
|
initialize: function () {
|
||||||
|
this.value = 1;
|
||||||
|
},
|
||||||
|
change: function (event) {
|
||||||
|
var val = this.$el.find('input').val();
|
||||||
|
if (!val) {
|
||||||
|
val = 1;
|
||||||
|
}
|
||||||
|
val = Number(val);
|
||||||
|
if (val !== this.value) {
|
||||||
|
this.value = val;
|
||||||
|
Backbone.Mediator.publish('passengers:change', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return PassengersInput;
|
||||||
|
});
|
||||||
22
app/scripts/views/roundtrip.js
Normal file
22
app/scripts/views/roundtrip.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'backbone-mediator'
|
||||||
|
], function (Backbone) {
|
||||||
|
var RoundtripInput = Backbone.View.extend({
|
||||||
|
el: '.roundtripWidget',
|
||||||
|
events: {
|
||||||
|
'click button': 'click'
|
||||||
|
},
|
||||||
|
click: function (event, el) {
|
||||||
|
this.$el.find('button').removeClass('active');
|
||||||
|
var current = this.$el.find(event.currentTarget);
|
||||||
|
current.addClass('active');
|
||||||
|
|
||||||
|
Backbone.Mediator.publish('roundtrip:change', current.hasClass('roundtrip'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return RoundtripInput;
|
||||||
|
});
|
||||||
49
app/scripts/views/route.js
Normal file
49
app/scripts/views/route.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'views/leg',
|
||||||
|
'jquery-ui-sortable'
|
||||||
|
], function (Backbone, LegView) {
|
||||||
|
|
||||||
|
var RouteView = Backbone.View.extend({
|
||||||
|
el: '.route',
|
||||||
|
events: {
|
||||||
|
'sortstart': 'sortStart',
|
||||||
|
'sortstop': 'sortStop'
|
||||||
|
},
|
||||||
|
initialize: function () {
|
||||||
|
this.collection.bind('add', this.add, this);
|
||||||
|
this.collection.bind('sort', this.render, this);
|
||||||
|
|
||||||
|
this.$el.sortable({
|
||||||
|
handle: 'div > div',
|
||||||
|
scroll: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sortStart: function () {
|
||||||
|
// fadeOut results
|
||||||
|
},
|
||||||
|
sortStop: function (event, ui) {
|
||||||
|
this.updateSort();
|
||||||
|
// fadeIn results
|
||||||
|
},
|
||||||
|
updateSort: function () {
|
||||||
|
// jQuery UI already updates DOM order,
|
||||||
|
// so we only have to update Backbone collection to match that
|
||||||
|
var self = this;
|
||||||
|
this.$el.find('.leg').each(function (index) {
|
||||||
|
// Get id of model from element
|
||||||
|
var id = $(this).data('id');
|
||||||
|
self.collection.get(id).set('order', index);
|
||||||
|
});
|
||||||
|
this.collection.sort();
|
||||||
|
},
|
||||||
|
add: function (model, collection, options) {
|
||||||
|
var item = new LegView({model: model});
|
||||||
|
this.$el.append(item.render().el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return RouteView;
|
||||||
|
});
|
||||||
19
app/scripts/views/total.js
Normal file
19
app/scripts/views/total.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'Template'
|
||||||
|
], function (Backbone, Template) {
|
||||||
|
var TotalView = Backbone.View.extend({
|
||||||
|
el: '.totalWidget',
|
||||||
|
initialize: function () {
|
||||||
|
this.render.apply(this);
|
||||||
|
this.model.bind('change', this.render, this);
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
this.$el.html(Template.total(this.model.toJSON()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return TotalView;
|
||||||
|
});
|
||||||
141
app/styles/main.less
Normal file
141
app/styles/main.less
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
@import 'bootstrap/less/bootstrap';
|
||||||
|
@import 'font-awesome/build/assets/font-awesome/less/font-awesome';
|
||||||
|
@import (less) 'select2/select2.css';
|
||||||
|
|
||||||
|
.calculator {
|
||||||
|
.inactive {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.nav-tabs {
|
||||||
|
a {
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container {
|
||||||
|
display: block;
|
||||||
|
.select2-choice {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-result {
|
||||||
|
abbr {
|
||||||
|
display: block;
|
||||||
|
width: 3em;
|
||||||
|
float: left;
|
||||||
|
color: #1657A1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-drop {
|
||||||
|
border-top: 1px solid #aaa;
|
||||||
|
.select2-search input {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
&.select2-drop-above {
|
||||||
|
border-bottom: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.leg {
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
padding: 10px 40px 10px 0;
|
||||||
|
margin: 0 0;
|
||||||
|
& > div {
|
||||||
|
& > div {
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.icon-sort, .delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -7px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
i.icon-sort {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.delete {
|
||||||
|
right: 0;
|
||||||
|
margin-top: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
abbr {
|
||||||
|
float: right;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
margin-top: -7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.route {
|
||||||
|
position: relative;
|
||||||
|
.leg {
|
||||||
|
position: relative;
|
||||||
|
left: 0 !important;
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.roundtripWidget {
|
||||||
|
.btn-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resultsWidget {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legInputWidget {
|
||||||
|
.btn {
|
||||||
|
text-align: left;
|
||||||
|
span {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.totalWidget {
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
padding: 20px 0;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
.distance {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.raw {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.co2 {
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
1
app/templates/choice.hbs
Normal file
1
app/templates/choice.hbs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{name}}<abbr>{{codes}}</abbr>
|
||||||
13
app/templates/leg.hbs
Normal file
13
app/templates/leg.hbs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<i class="icon-sort"></i>
|
||||||
|
<span class="">
|
||||||
|
<h2>{{name}}</h2>
|
||||||
|
<span class="city">{{city}}, </span>
|
||||||
|
<span class="country">{{country}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a href="#" class="btn btn-danger delete">
|
||||||
|
<i class="icon-remove"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
58
app/templates/operation.hbs
Normal file
58
app/templates/operation.hbs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<p>
|
||||||
|
Laskuri noutaa tietokannasta, jonka lähteenä on <a href="http://openflights.org/">http://openflights.org/</a>, lentokenttien sijainnit.
|
||||||
|
Etäisyys kahden koordinaatin välillä lasketaan <a href="http://en.wikipedia.org/wiki/Haversine_formula">Haversinen kaavalla</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Lähtö- ja kohdekentälle valitaan lentoalue niiden <abbr title="International Civil Aviation Organization">ICAO</abbr>-tunnusten perusteella.
|
||||||
|
Mikäli toinen kenttä on esimerkiksi Suomessa ja toinen Pohjois-Euroopassa, valitaan muuttujat jälkimmäisen mukaan.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>Säteilypakotekerroin</strong> <strong>1,0</strong> tai matkan ollessa yli 500km <strong>2,0</strong>.</li>
|
||||||
|
<li><strong>Epäsuoramatkakerroin</strong> – koska reitit eivät kulje suorinta mahdollista reittiä, kerrotaan matka kertoimella <strong>{{displayFloat indirectRouteMultiplier 2}}</strong>.
|
||||||
|
<li><strong>CO<sub>2</sub> päästötonnin hinta</strong> {{displayFloat priceCO2 2}}€</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Muuttujista rakennetaan kerroin <em>m</em> seuraavasti <em>säteilypakotekerroin * (1 - rahtikerroin) * (1 / kuormakerroin)</em>.
|
||||||
|
Lopulliset co<sub>2</sub> päästöt koostuvat kahdesta osasta: laskeutuminen ja nousu sekä lento. Lennon
|
||||||
|
päästöt lasketaan kertomalla kuljettu etäisyys kertoimilla <em>m</em> ja <em>co2</em>. Laskeutumisen ja nousut päästöt saadaan kertomalla <em>lto</em>-muuttuja kertoimella <em>m</em>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><strong>Etäisyys [km]</strong></i></td>
|
||||||
|
{{#each distanceRanges}}
|
||||||
|
<td><strong>{{name}}</strong></td>
|
||||||
|
{{/each}}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Lentoalue</strong></td>
|
||||||
|
<td><strong>Muuttuja</strong></td>
|
||||||
|
<td colspan="5"></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
{{#each parameters}}
|
||||||
|
<tr>
|
||||||
|
<td rowspan="4">{{name}}</td>
|
||||||
|
<td>co<sub>2</sub></td>
|
||||||
|
{{#each co2factor}}<td>{{displayFloat this 2}}</td>{{/each}}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>lto</td>
|
||||||
|
{{#each ltoCycle}}<td>{{this}}</td>{{/each}}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Paljonko lennoilla on matkustajia.">kuorma</abbr></td>
|
||||||
|
{{#each load}}<td>{{displayFloat this 2}}</td>{{/each}}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Paljonko koneet ottavat rahtia.">rahti</abbr></td>
|
||||||
|
{{#each freight}}<td>{{displayFloat this 2}}</td>{{/each}}
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</table>
|
||||||
1
app/templates/result.hbs
Normal file
1
app/templates/result.hbs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<span class="distance">{{displayFloat dist 2}} km</span><span class="co2"><abbr title="lto: {{displayFloat lto 2}}">{{displayFloat total 2}} Kg CO²</abbr></span>
|
||||||
15
app/templates/total.hbs
Normal file
15
app/templates/total.hbs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<p class="distance">Matkaa yhteensä {{displayFloat dist 1}} <em>km</em>.</p>
|
||||||
|
|
||||||
|
<p class="raw">{{#if alone}}
|
||||||
|
{{#if roundtrip}}
|
||||||
|
Päästöjä per suunta {{displayFloat rawTotal 0}} <em>Kg CO<sub>2</sub></em>.
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if roundtrip}}
|
||||||
|
Päästöjä per henkilö per suunta {{displayFloat rawTotal 0}} <em>Kg CO²</em>.
|
||||||
|
{{else}}
|
||||||
|
Päästöjä per henkilö {{displayFloat rawTotal 0}} <em>Kg CO<sub>2</sub></em>.
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}</p>
|
||||||
|
<p class="co2">{{displayFloat total 0}} <em>kg CO<sub>2</sub></em></p>
|
||||||
|
<p class="price">{{displayFloat price 2}} <em>€</em></p>
|
||||||
18
component.json
Normal file
18
component.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "lentolaskuri2",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"backbone": "git://github.com/documentcloud/backbone.git#1.0.0",
|
||||||
|
"bootstrap": "git://github.com/twitter/bootstrap.git#2.3.1",
|
||||||
|
"requirejs": "git://github.com/jrburke/requirejs#2.1.5",
|
||||||
|
"jquery": "git://github.com/components/jquery.git#1.9.1",
|
||||||
|
"select2": "git://github.com/ivaynberg/select2.git#~3.3.1",
|
||||||
|
"lodash": "git://github.com/bestiejs/lodash.git#1.0.1",
|
||||||
|
"font-awesome": "~3.1.1",
|
||||||
|
"handlebars": "git://github.com/components/handlebars.js#1.0.0-rc.3",
|
||||||
|
"jquery-ui": "git://github.com/components/jqueryui#1.10.1",
|
||||||
|
"requirejs-plugins": "git://github.com/millermedeiros/requirejs-plugins.git",
|
||||||
|
"Backbone-Mediator": "latest",
|
||||||
|
"i18next": "~1.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
lentolaskuri.sublime-project
Normal file
18
lentolaskuri.sublime-project
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"folders":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"path": "/home/juho/Source/tyo/lentolaskuri2",
|
||||||
|
"folder_exclude_patterns": ["node_modules", ".cache", ".tmp", "app/components"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ternjs": {
|
||||||
|
"exclude": ["node_modules/**", "app/components/**"],
|
||||||
|
"libs": ["jquery"],
|
||||||
|
"plugins": {
|
||||||
|
"requirejs": {
|
||||||
|
"baseURL": "app/components"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
package.json
Normal file
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "lentolaskuri2",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"grunt": "~0.4.1",
|
||||||
|
"grunt-contrib-copy": "~0.4.0",
|
||||||
|
"grunt-contrib-concat": "~0.1.2",
|
||||||
|
"grunt-contrib-coffee": "~0.4.0",
|
||||||
|
"grunt-contrib-uglify": "~0.1.1",
|
||||||
|
"grunt-contrib-less": "~0.5.0",
|
||||||
|
"grunt-contrib-jshint": "~0.1.1",
|
||||||
|
"grunt-contrib-cssmin": "~0.4.1",
|
||||||
|
"grunt-contrib-connect": "0.1.2",
|
||||||
|
"grunt-contrib-clean": "0.4.0",
|
||||||
|
"grunt-contrib-htmlmin": "0.1.1",
|
||||||
|
"grunt-contrib-imagemin": "0.1.2",
|
||||||
|
"grunt-contrib-livereload": "0.1.1",
|
||||||
|
"grunt-bower-hooks": "~0.2.0",
|
||||||
|
"grunt-usemin": "~0.1.9",
|
||||||
|
"grunt-regarde": "~0.1.1",
|
||||||
|
"grunt-requirejs": "~0.3.1",
|
||||||
|
"grunt-mocha": "~0.2.2",
|
||||||
|
"grunt-open": "~0.2.0",
|
||||||
|
"matchdep": "~0.1.1",
|
||||||
|
"grunt-contrib-handlebars": "~0.5.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user