├── __init__.py
├── templates
├── partials
│ ├── nav
│ │ └── header.html
│ └── fb
│ │ └── init.html
├── 404.html
├── about.html
├── base.html
└── index.html
├── static
├── sass
│ ├── _filters.scss
│ ├── _variables.scss
│ ├── _fonts.scss
│ ├── main.scss
│ ├── _footer.scss
│ ├── _header.scss
│ ├── _images.scss
│ └── _layout.scss
├── img
│ ├── filters
│ │ ├── kenya
│ │ │ ├── kenya-lg.png
│ │ │ ├── kenya-h200.png
│ │ │ └── kenya-h300.png
│ │ ├── syria
│ │ │ ├── syria-lg.png
│ │ │ ├── syria-h200.png
│ │ │ └── syria-h300.png
│ │ ├── lebanon
│ │ │ ├── lebanon-lg.png
│ │ │ ├── lebanon-h200.png
│ │ │ └── lebanon-h300.png
│ │ └── myanmar
│ │ │ ├── myanmar-lg.png
│ │ │ ├── myanmar-h200.png
│ │ │ └── myanmar-h300.png
│ └── avatars
│ │ ├── default-avatar-h200.jpg
│ │ └── default-avatar-h300.jpg
├── css
│ ├── build.css.map
│ └── build.css
└── js
│ ├── main.js
│ └── lib
│ ├── binaryajax.js
│ ├── jquery.canvasResize.js
│ ├── exif.js
│ └── jquery.exif.js
├── .gitignore
├── README.md
├── package.json
├── application.py
└── gruntfile.js
/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/partials/nav/header.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/sass/_filters.scss:
--------------------------------------------------------------------------------
1 | .beirut {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | node_modules/
3 | .sass-cache/
4 |
--------------------------------------------------------------------------------
/static/sass/_variables.scss:
--------------------------------------------------------------------------------
1 | $header_height: 60px;
2 |
3 |
--------------------------------------------------------------------------------
/static/sass/_fonts.scss:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Montserrat);
2 |
--------------------------------------------------------------------------------
/static/sass/main.scss:
--------------------------------------------------------------------------------
1 | @import 'header';
2 | @import 'footer';
3 | @import 'layout';
4 | @import 'images';
5 | @import 'filters';
6 |
--------------------------------------------------------------------------------
/static/img/filters/kenya/kenya-lg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/kenya/kenya-lg.png
--------------------------------------------------------------------------------
/static/img/filters/syria/syria-lg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/syria/syria-lg.png
--------------------------------------------------------------------------------
/static/img/filters/kenya/kenya-h200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/kenya/kenya-h200.png
--------------------------------------------------------------------------------
/static/img/filters/kenya/kenya-h300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/kenya/kenya-h300.png
--------------------------------------------------------------------------------
/static/img/filters/lebanon/lebanon-lg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/lebanon/lebanon-lg.png
--------------------------------------------------------------------------------
/static/img/filters/myanmar/myanmar-lg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/myanmar/myanmar-lg.png
--------------------------------------------------------------------------------
/static/img/filters/syria/syria-h200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/syria/syria-h200.png
--------------------------------------------------------------------------------
/static/img/filters/syria/syria-h300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/syria/syria-h300.png
--------------------------------------------------------------------------------
/static/img/avatars/default-avatar-h200.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/avatars/default-avatar-h200.jpg
--------------------------------------------------------------------------------
/static/img/avatars/default-avatar-h300.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/avatars/default-avatar-h300.jpg
--------------------------------------------------------------------------------
/static/img/filters/lebanon/lebanon-h200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/lebanon/lebanon-h200.png
--------------------------------------------------------------------------------
/static/img/filters/lebanon/lebanon-h300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/lebanon/lebanon-h300.png
--------------------------------------------------------------------------------
/static/img/filters/myanmar/myanmar-h200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/myanmar/myanmar-h200.png
--------------------------------------------------------------------------------
/static/img/filters/myanmar/myanmar-h300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fidiego/solidiaritize-v0.0.1/master/static/img/filters/myanmar/myanmar-h300.png
--------------------------------------------------------------------------------
/templates/404.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block content %}
3 |
4 |
Ooop!
5 |
It looks like whatver you're looking for is missing.
6 |
7 | {% endblock content %}
8 |
--------------------------------------------------------------------------------
/static/sass/_footer.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 | position: absolute;
3 | bottom: 0;
4 | left: 0;
5 | width: 100vw;
6 | height: 40px;
7 | .footer-inner {
8 | max-width: 300px;
9 | margin: 0 auto;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/static/sass/_header.scss:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 | .header {
3 | height: $header_height;
4 | width: 100vw;
5 | position: absolute;
6 | top: 0;
7 | left: 0;
8 | text-align: center;
9 | font-style: bold;
10 | font-size: 2em;
11 | font-family: 'Montserrat', sans-serif;
12 | padding-top: 15px;
13 | color: #55496D;
14 | }
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Solidaritize
2 | A super simple canvas-based application
3 |
4 | ## Setting up for development
5 |
6 | Requirements
7 |
8 | 1. pip + virtualenv(wrapper)
9 | 2. npm
10 |
11 | First time setup
12 |
13 | $ mkvirtualenv solidaritize
14 | $ workon solidaritize
15 | $ pip install -r requirements.txt
16 | $ npm install
17 |
18 | To run locally
19 |
20 | $ python application.py
21 |
22 | To build
23 |
24 | $ ./build.sh
25 |
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sppgen",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "build.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "grunt": "^0.4.5",
13 | "grunt-contrib-cssmin": "^0.14.0",
14 | "grunt-contrib-htmlmin": "^0.6.0",
15 | "grunt-processhtml": "^0.3.8",
16 | "grunt-shell": "^1.1.2",
17 | "node-sass": "^3.4.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/templates/about.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block page_title %}About{% endblock page_title %}
4 |
5 | {% block content %}
6 |
7 |
About
8 |
9 | This project was created in response to the lack of support, sympathy, and empathy towards the peoples of muslim countries whose recent tragedies are overlooked.
10 |
11 |
12 | {% endblock content %}
13 | {% block end_scripts %}{% endblock end_scripts %}
14 |
--------------------------------------------------------------------------------
/application.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | from flask import Flask
3 | from flask import render_template
4 | from flask_frozen import Freezer
5 |
6 | app = Flask(__name__)
7 | freezer = Freezer(app)
8 |
9 |
10 | @app.route('/')
11 | def index():
12 | return render_template('index.html')
13 |
14 | @app.route('/error/')
15 | def error():
16 | return render_template('404.html')
17 |
18 | @app.route('/about/')
19 | def about():
20 | return render_template('about.html')
21 |
22 | if __name__ == '__main__':
23 | if len(sys.argv) > 1 and sys.argv[1] == "build":
24 | os.system('rm -r build')
25 | freezer.freeze()
26 | else:
27 | app.run()
28 |
--------------------------------------------------------------------------------
/static/sass/_images.scss:
--------------------------------------------------------------------------------
1 | .avatar {
2 | width: 200px;
3 | height: 200px;
4 | border-radius: 4px;
5 | background-image: url(/static/img/avatars/default-avatar-h200.jpg);
6 | background-position: center center;
7 | background-size: cover;
8 | &.avatar-lg {
9 | width: 300px;
10 | height: 300px;
11 | background-image: url(/static/img/avatars/default-avatar-h300.jpg);
12 | }
13 | &.centered {
14 | margin: 0 auto;
15 | }
16 | }
17 |
18 | .sources {
19 | width: 200px;
20 | height: 40px;
21 | margin: 0 auto;
22 | margin-top: 15px;
23 | text-align: center;
24 | &.sources-lg {
25 | width: 300px;
26 | }
27 | .src {
28 | padding-top: 10px;
29 | height: 100%;
30 | float: left;
31 | color: #fff;
32 | width: 100%;
33 | &.src-fb {
34 | background-color: #3b5998;
35 | }
36 | &.src-tw {
37 | background-color: #4099FF;
38 | }
39 | &.src-ul {
40 | background-color: #665884;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 | {# HEAD #}
3 | {% block head %}
4 |
5 |
27 | {% block content %}
28 | {% endblock content %}
29 |
30 |
32 |
33 |
34 | {% block end_scripts %}
35 |
36 |
37 |
38 |
39 |
40 | {% endblock end_scripts %}
41 |
42 |
43 | {% endblock body %}
44 |
45 |
46 |
--------------------------------------------------------------------------------
/static/sass/_layout.scss:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 | @import 'fonts';
3 | html {
4 | overflow: hidden;
5 | font-family: 'Montserrat', sans-serif;
6 | }
7 |
8 | html,
9 | body {
10 | margin: 0px;
11 | padding: 0px;
12 | background-color: #f3f3f3;
13 | }
14 |
15 | .content {
16 | width: 100vw;
17 | margin-top: $header_height;
18 | height: calc(100vh - #{$header_height});
19 | }
20 |
21 | .content-small {
22 | max-width: 300px;
23 | &.centered {
24 | margin: 0 auto;
25 | }
26 | }
27 |
28 | .content-narrow {
29 | max-width: 500px;
30 | margin: 0 auto;
31 | }
32 |
33 | #simple-version-001 {
34 | $this_height: 384px;
35 | height: $this_height;
36 | margin-top: 90px;
37 | #filter-form {
38 | margin-top: 30px;
39 | select {
40 | width: 100%;
41 | }
42 | }
43 | }
44 |
45 | .hidden {
46 | display: none;
47 | }
48 |
49 | .text--center {
50 | text-align: center;
51 | }
52 |
53 | .padded {
54 | padding-top: 30px;
55 | }
56 |
57 | .avatar-container {
58 | position: relative;
59 | color: #fff;
60 | width: 100%;
61 | .upload-prompt {
62 | font-family: sans-serif, arial;
63 | width: calc(100% - 40px);
64 | background-color: rgba(0, 0, 0, .5);
65 | padding: 0px 20px;
66 | position: absolute;
67 | bottom: 0;
68 | left: 0;
69 | text-align: center;
70 | }
71 | }
72 | #download-button {
73 | text-decoration: none;
74 | }
75 | .download-button {
76 | width: 100%;
77 | height: 40px;
78 | text-align: center;
79 | text-decoration: none;
80 | text-transform: uppercase;
81 | font-size: 1.4em;
82 | border: 2px solid #55496D;
83 | background-color: #55496D;
84 | padding-top: 15px;
85 | color: #fff;
86 |
87 | }
88 | .download-instructions {
89 | text-align: center;
90 | }
91 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Upload an Image
11 | or click below to use your facebook profile picture.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
25 |
26 |
36 |
37 |
38 | Download
39 |
40 |
41 |
42 | If the download button doesn't work, right click or tap and hold to download the image.
43 |
44 |
45 | {% endblock content %}
46 |
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | grunt.initConfig({
4 | uglify: {
5 | build: {
6 | options: {
7 | mangle: true,
8 | exportAll: false,
9 | beautify: false
10 | },
11 | files: {
12 | 'build/static/js/build.min.js': ['build/static/js/lib/jquery-2.1.4.js', 'build/static/js/**/*.js']
13 | }
14 | }
15 | },
16 | processhtml: {
17 | dist: {
18 | files: {
19 | 'build/index.html': ['build/index.html']
20 | }
21 | }
22 | },
23 | htmlmin: {
24 | build: {
25 | options: {
26 | removeComments: true,
27 | collapseWhitespace: true
28 | },
29 | files: {
30 | 'build/index.html': 'build/index.html'
31 | }
32 | },
33 | },
34 | shell: {
35 | options: {
36 | stderr: false
37 | },
38 | target: {
39 | command: 'rm -r build/static/js/lib/ && rm -r build/static/sass/ && rm build/static/js/main.js'
40 | }
41 | },
42 | // DEVELOPMENT
43 | // sass: {
44 | // dist: {
45 | // files: {
46 | // 'static/styles/css/main.css': 'static/scss/main.scss'
47 | // }
48 | // }
49 | // },
50 | // watch: {
51 | // files: ['dev/scss/**/*.scss'],
52 | // tasks: ['sass']
53 | // },
54 | });
55 |
56 | grunt.loadNpmTasks('grunt-contrib-uglify');
57 | grunt.loadNpmTasks('grunt-processhtml');
58 | grunt.loadNpmTasks('grunt-contrib-htmlmin');
59 | grunt.loadNpmTasks('grunt-shell');
60 |
61 | grunt.registerTask('build', ['uglify', 'processhtml', 'htmlmin', 'shell']);
62 | grunt.registerTask('default', ['build']);
63 | };
64 |
--------------------------------------------------------------------------------
/static/css/build.css.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "mappings": "AAAQ,+DAAuD;ACC/D,OAAQ;EACJ,MAAM,ECFM,IAAI;EDGhB,KAAK,EAAE,KAAK;EACZ,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,CAAC;EACN,IAAI,EAAE,CAAC;EACP,UAAU,EAAE,MAAM;EAClB,UAAU,EAAE,IAAI;EAChB,SAAS,EAAE,GAAG;EACd,WAAW,EAAE,wBAAwB;EACrC,WAAW,EAAE,IAAI;EACjB,KAAK,EAAE,OAAO;;AEZlB,OAAQ;EACJ,QAAQ,EAAE,QAAQ;EAClB,MAAM,EAAE,CAAC;EACT,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,IAAI;EACZ,qBAAc;IACV,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,MAAM;;ACNtB,IAAK;EACD,QAAQ,EAAE,MAAM;EAChB,WAAW,EAAE,wBAAwB;;AAGzC;IACK;EACD,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,GAAG;EACZ,gBAAgB,EAAE,OAAO;;AAG7B,QAAS;EACL,KAAK,EAAE,KAAK;EACZ,UAAU,EFhBE,IAAI;EEiBhB,MAAM,EAAE,kBAA+B;;AAG3C,cAAe;EACX,SAAS,EAAE,KAAK;EAChB,uBAAW;IACP,MAAM,EAAE,MAAM;;AAItB,eAAgB;EACZ,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM;;AAGlB,mBAAoB;EAEhB,MAAM,EADQ,KAAK;EAEnB,UAAU,EAAE,IAAI;EAChB,gCAAa;IACT,UAAU,EAAE,IAAI;IAChB,uCAAO;MACH,KAAK,EAAE,IAAI;;AAKvB,OAAQ;EACJ,OAAO,EAAE,IAAI;;AAGjB,aAAc;EACV,UAAU,EAAE,MAAM;;AAGtB,OAAQ;EACJ,WAAW,EAAE,IAAI;;AAGrB,iBAAkB;EACd,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,IAAI;EACX,KAAK,EAAE,IAAI;EACX,gCAAe;IACX,WAAW,EAAE,iBAAiB;IAC9B,KAAK,EAAE,iBAAiB;IACxB,gBAAgB,EAAE,kBAAiB;IACnC,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;IACP,UAAU,EAAE,MAAM;;AAG1B,gBAAiB;EACb,eAAe,EAAE,IAAI;;AAEzB,gBAAiB;EACb,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,MAAM;EAClB,eAAe,EAAE,IAAI;EACrB,cAAc,EAAE,SAAS;EACzB,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,iBAAiB;EACzB,gBAAgB,EAAE,OAAO;EACzB,WAAW,EAAE,IAAI;EACjB,KAAK,EAAE,IAAI;;AAGf,sBAAuB;EACnB,UAAU,EAAE,MAAM;;ACxFtB,OAAQ;EACJ,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,KAAK;EACb,aAAa,EAAE,GAAG;EAClB,gBAAgB,EAAE,gDAAgD;EAClE,mBAAmB,EAAE,aAAa;EAClC,eAAe,EAAE,KAAK;EACtB,iBAAY;IACR,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,KAAK;IACb,gBAAgB,EAAE,gDAAgD;EAEtE,gBAAW;IACP,MAAM,EAAE,MAAM;;AAItB,QAAS;EACL,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,MAAM;EACd,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,MAAM;EAClB,mBAAa;IACT,KAAK,EAAE,KAAK;EAEhB,aAAK;IACD,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;IACX,oBAAS;MACL,gBAAgB,EAAE,OAAO;IAE7B,oBAAS;MACL,gBAAgB,EAAE,OAAO;IAE7B,oBAAS;MACL,gBAAgB,EAAE,OAAO",
4 | "sources": ["../sass/_fonts.scss","../sass/_header.scss","../sass/_variables.scss","../sass/_footer.scss","../sass/_layout.scss","../sass/_images.scss"],
5 | "names": [],
6 | "file": "build.css"
7 | }
--------------------------------------------------------------------------------
/templates/partials/fb/init.html:
--------------------------------------------------------------------------------
1 |
69 |
--------------------------------------------------------------------------------
/static/css/build.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Montserrat);
2 | .header {
3 | height: 60px;
4 | width: 100vw;
5 | position: absolute;
6 | top: 0;
7 | left: 0;
8 | text-align: center;
9 | font-style: bold;
10 | font-size: 2em;
11 | font-family: 'Montserrat', sans-serif;
12 | padding-top: 15px;
13 | color: #55496D; }
14 |
15 | .footer {
16 | position: absolute;
17 | bottom: 0;
18 | left: 0;
19 | width: 100vw;
20 | height: 40px; }
21 | .footer .footer-inner {
22 | max-width: 300px;
23 | margin: 0 auto; }
24 |
25 | html {
26 | overflow: hidden;
27 | font-family: 'Montserrat', sans-serif; }
28 |
29 | html,
30 | body {
31 | margin: 0px;
32 | padding: 0px;
33 | background-color: #f3f3f3; }
34 |
35 | .content {
36 | width: 100vw;
37 | margin-top: 60px;
38 | height: calc(100vh - 60px); }
39 |
40 | .content-small {
41 | max-width: 300px; }
42 | .content-small.centered {
43 | margin: 0 auto; }
44 |
45 | .content-narrow {
46 | max-width: 500px;
47 | margin: 0 auto; }
48 |
49 | #simple-version-001 {
50 | height: 384px;
51 | margin-top: 90px; }
52 | #simple-version-001 #filter-form {
53 | margin-top: 30px; }
54 | #simple-version-001 #filter-form select {
55 | width: 100%; }
56 |
57 | .hidden {
58 | display: none; }
59 |
60 | .text--center {
61 | text-align: center; }
62 |
63 | .padded {
64 | padding-top: 30px; }
65 |
66 | .avatar-container {
67 | position: relative;
68 | color: #fff;
69 | width: 100%; }
70 | .avatar-container .upload-prompt {
71 | font-family: sans-serif, arial;
72 | width: calc(100% - 40px);
73 | background-color: rgba(0, 0, 0, 0.5);
74 | padding: 0px 20px;
75 | position: absolute;
76 | bottom: 0;
77 | left: 0;
78 | text-align: center; }
79 |
80 | #download-button {
81 | text-decoration: none; }
82 |
83 | .download-button {
84 | width: 100%;
85 | height: 40px;
86 | text-align: center;
87 | text-decoration: none;
88 | text-transform: uppercase;
89 | font-size: 1.4em;
90 | border: 2px solid #55496D;
91 | background-color: #55496D;
92 | padding-top: 15px;
93 | color: #fff; }
94 |
95 | .download-instructions {
96 | text-align: center; }
97 |
98 | .avatar {
99 | width: 200px;
100 | height: 200px;
101 | border-radius: 4px;
102 | background-image: url(/static/img/avatars/default-avatar-h200.jpg);
103 | background-position: center center;
104 | background-size: cover; }
105 | .avatar.avatar-lg {
106 | width: 300px;
107 | height: 300px;
108 | background-image: url(/static/img/avatars/default-avatar-h300.jpg); }
109 | .avatar.centered {
110 | margin: 0 auto; }
111 |
112 | .sources {
113 | width: 200px;
114 | height: 40px;
115 | margin: 0 auto;
116 | margin-top: 15px;
117 | text-align: center; }
118 | .sources.sources-lg {
119 | width: 300px; }
120 | .sources .src {
121 | padding-top: 10px;
122 | height: 100%;
123 | float: left;
124 | color: #fff;
125 | width: 100%; }
126 | .sources .src.src-fb {
127 | background-color: #3b5998; }
128 | .sources .src.src-tw {
129 | background-color: #4099FF; }
130 | .sources .src.src-ul {
131 | background-color: #665884; }
132 |
133 | /*# sourceMappingURL=build.css.map */
134 |
--------------------------------------------------------------------------------
/static/js/main.js:
--------------------------------------------------------------------------------
1 | var IMAGE_CONTENT;
2 | var IMG;
3 | var FILE;
4 | $(document).ready(function() {
5 | function checkFBLogin() {
6 | FB.getLoginStatus(function(response) {
7 | if (response.status === 'connected') {
8 | return true;
9 | } else if (response.status === 'not_authorized') {
10 | FB.login()
11 | } else {
12 | return false
13 | }
14 | });
15 | }
16 |
17 | // IMAGE STUFF
18 |
19 | var profileImageInput = document.getElementById('profileImageInput');
20 | profileImageInput.addEventListener('change', handleImage, false);
21 | var CANVAS = document.getElementById("avatarCanvas");
22 | CANVAS.width = 300;
23 | CANVAS.height = 300;
24 | var CONTEXT = document.getElementById("avatarCanvas").getContext("2d");
25 |
26 | function setContext(imageFile, context) {
27 | var img = new Image();
28 | img.onload = function() {
29 | var w = img.width;
30 | var h = img.height;
31 | var crop_x = 0;
32 | var crop_y = 0;
33 | var crop_w = 0;
34 | var crop_h = 0;
35 | if (w < h){ // image is tall
36 | console.log('image is tall')
37 | crop_y = (h - w) / 2;
38 | crop_h = h - (h - w);
39 | crop_w = w;
40 | } else if (h < w) { // image is wide
41 | console.log('image is wide')
42 | crop_x = (w - h) / 2;
43 | crop_w = w - (w - h);
44 | crop_h = h
45 | } else {
46 | crop_w = w;
47 | crop_h = h;
48 | }
49 | console.log('NEW DIMENSIONS (w, h):', crop_w, crop_h)
50 | console.log(w, h, crop_x, crop_y, crop_w, crop_h)
51 | context.drawImage(img, crop_x, crop_y, crop_w, crop_h, 0, 0, CANVAS.width, CANVAS.height);
52 | }
53 | img.src = imageFile;
54 | img.setAttribute('crossOrigin', 'anonymous');
55 | IMAGE_CONTENT = imageFile;
56 | IMG = img;
57 | }
58 | // ON IMAGE INPUT FILE CHANGE
59 | function handleImage(e) {
60 | var reader = new FileReader();
61 | reader.onloadend = function(event) {
62 | setContext(event.target.result, CONTEXT)
63 | }
64 | FILE = e.target.files[0];
65 | reader.readAsDataURL(FILE);
66 | $('select').fadeIn('slow');
67 | $('.upload-prompt').fadeOut('fast');
68 | };
69 |
70 | // ON FETCHING IMAGE FROM FACEBOOK
71 | function paintWithURL(url) {
72 | var img = new Image();
73 | img.src = url;
74 | img.setAttribute('crossOrigin', 'anonymous');
75 | // $('canvas.avatar')
76 | // .css('background-image', 'url(' + url + ')')
77 | // .css('background-position', 'center center')
78 | // .css('background-size', 'cover')
79 | CONTEXT.drawImage(img, 0, 0, CANVAS.width, CANVAS.height);
80 | IMAGE_CONTENT = url;
81 | $('select').fadeIn('slow');
82 | $('.upload-prompt').fadeOut('fast');
83 | };
84 |
85 | // APPLY FILTER
86 | function applyFilter(name) {
87 | if (name === null || name === undefined || name === "null") {
88 | console.log('no name')
89 | return
90 | }
91 | $(CANVAS).fadeOut('fast', function() {
92 | console.log('removing old content');
93 | CONTEXT.clearRect(0, 0, CANVAS.width, CANVAS.height);
94 | setContext(IMAGE_CONTENT, CONTEXT)
95 |
96 | var filterImage = new Image();
97 | filterImage.src = "/static/img/filters/" + name + "/" + name + "-h300.png";
98 | filterImage.setAttribute('crossOrigin', 'anonymous');
99 | filterImage.onload = function() {
100 | CONTEXT.globalCompositeOperation = "soft-light";
101 | CONTEXT.drawImage(filterImage, 0, 0, CANVAS.width, CANVAS.height);
102 | };
103 | })
104 |
105 | $(CANVAS).fadeIn()
106 | $('.download-button').fadeIn();
107 | $('.download-instructions').fadeIn();
108 | };
109 |
110 | $('#filter-select').change(function() {
111 | var selectedValue = $(this).find(':selected')[0].value;
112 | applyFilter(selectedValue)
113 | });
114 |
115 | // FACEBOOK
116 | function getFaceBookImage() {
117 | FB.api(
118 | '/me/picture/?width=9999',
119 | function(response) {
120 | if (response && !response.error) {
121 | paintWithURL(response.data.url)
122 | } else {
123 | console.log('error')
124 | }
125 | }
126 | );
127 | };
128 |
129 | // ON FACEBOOK CLICK
130 | $('div.src.src-fb').click(function() {
131 | console.log('facebook clicked')
132 | FB.getLoginStatus(function(response) {
133 | if (response.status === 'connected') {
134 | getFaceBookImage();
135 | } else if (response.status === 'not_authorized') {
136 | // The person is logged into Facebook, but not your app.
137 | FB.login()
138 | } else {
139 | // The person is not logged into Facebook, so we're not sure if
140 | // they are logged into this app or not.
141 | alert('You must be logged into facebook to use this feature.')
142 | }
143 | });
144 | });
145 |
146 | // ON UPLOAD CLICK
147 | $('div.avatar-container').click(function() {
148 | $('input#profileImageInput').click()
149 | });
150 | $('div.src.src-ul').click(function() {
151 | $('input#profileImageInput').click()
152 | });
153 |
154 | var button = document.getElementById('download-button');
155 | button.addEventListener('click', function(e) {
156 | var dataURL = CANVAS.toDataURL('image/png');
157 | button.href = dataURL;
158 |
159 | });
160 |
161 | // ON HOVER
162 | // $('.avatar').hover(
163 | // function() {
164 | // if (IMAGE_CONTENTS) {
165 | // $('i#clear-avatar').fadeIn().removeClass('hidden')
166 | // }
167 | // },
168 | // function() {
169 | // $('i#clear-avatar').fadeOut().addClass('hidden')
170 | // }
171 | // );
172 | // $('i#clear-avatar').click(function() {
173 | // console.log(IMAGE_CONTENTS)
174 | // IMAGE_CONTENTS = null;
175 | // $('div.avatar').removeAttr('style');
176 | // console.log(IMAGE_CONTENTS)
177 | // })
178 |
179 | });
180 |
--------------------------------------------------------------------------------
/static/js/lib/binaryajax.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Binary Ajax 0.1.10
4 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/
5 | * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
6 | */
7 |
8 | var BinaryFile = function(strData, iDataOffset, iDataLength) {
9 | var data = strData;
10 | var dataOffset = iDataOffset || 0;
11 | var dataLength = 0;
12 |
13 | this.getRawData = function() {
14 | return data;
15 | }
16 |
17 | if (typeof strData == "string") {
18 | dataLength = iDataLength || data.length;
19 |
20 | this.getByteAt = function(iOffset) {
21 | return data.charCodeAt(iOffset + dataOffset) & 0xFF;
22 | }
23 |
24 | this.getBytesAt = function(iOffset, iLength) {
25 | var aBytes = [];
26 |
27 | for (var i = 0; i < iLength; i++) {
28 | aBytes[i] = data.charCodeAt((iOffset + i) + dataOffset) & 0xFF
29 | }
30 | ;
31 |
32 | return aBytes;
33 | }
34 | } else if (typeof strData == "unknown") {
35 | dataLength = iDataLength || IEBinary_getLength(data);
36 |
37 | this.getByteAt = function(iOffset) {
38 | return IEBinary_getByteAt(data, iOffset + dataOffset);
39 | }
40 |
41 | this.getBytesAt = function(iOffset, iLength) {
42 | return new VBArray(IEBinary_getBytesAt(data, iOffset + dataOffset, iLength)).toArray();
43 | }
44 | }
45 |
46 | this.getLength = function() {
47 | return dataLength;
48 | }
49 |
50 | this.getSByteAt = function(iOffset) {
51 | var iByte = this.getByteAt(iOffset);
52 | if (iByte > 127)
53 | return iByte - 256;
54 | else
55 | return iByte;
56 | }
57 |
58 | this.getShortAt = function(iOffset, bBigEndian) {
59 | var iShort = bBigEndian ?
60 | (this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1)
61 | : (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset)
62 | if (iShort < 0)
63 | iShort += 65536;
64 | return iShort;
65 | }
66 | this.getSShortAt = function(iOffset, bBigEndian) {
67 | var iUShort = this.getShortAt(iOffset, bBigEndian);
68 | if (iUShort > 32767)
69 | return iUShort - 65536;
70 | else
71 | return iUShort;
72 | }
73 | this.getLongAt = function(iOffset, bBigEndian) {
74 | var iByte1 = this.getByteAt(iOffset),
75 | iByte2 = this.getByteAt(iOffset + 1),
76 | iByte3 = this.getByteAt(iOffset + 2),
77 | iByte4 = this.getByteAt(iOffset + 3);
78 |
79 | var iLong = bBigEndian ?
80 | (((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4
81 | : (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
82 | if (iLong < 0)
83 | iLong += 4294967296;
84 | return iLong;
85 | }
86 | this.getSLongAt = function(iOffset, bBigEndian) {
87 | var iULong = this.getLongAt(iOffset, bBigEndian);
88 | if (iULong > 2147483647)
89 | return iULong - 4294967296;
90 | else
91 | return iULong;
92 | }
93 |
94 | this.getStringAt = function(iOffset, iLength) {
95 | var aStr = [];
96 |
97 | var aBytes = this.getBytesAt(iOffset, iLength);
98 | for (var j = 0; j < iLength; j++) {
99 | aStr[j] = String.fromCharCode(aBytes[j]);
100 | }
101 | return aStr.join("");
102 | }
103 |
104 | this.getCharAt = function(iOffset) {
105 | return String.fromCharCode(this.getByteAt(iOffset));
106 | }
107 | this.toBase64 = function() {
108 | return window.btoa(data);
109 | }
110 | this.fromBase64 = function(strBase64) {
111 | data = window.atob(strBase64);
112 | }
113 | }
114 |
115 |
116 | var BinaryAjax = (function() {
117 |
118 | function createRequest() {
119 | var oHTTP = null;
120 | if (window.ActiveXObject) {
121 | oHTTP = new ActiveXObject("Microsoft.XMLHTTP");
122 | } else if (window.XMLHttpRequest) {
123 | oHTTP = new XMLHttpRequest();
124 | }
125 | return oHTTP;
126 | }
127 |
128 | function getHead(strURL, fncCallback, fncError) {
129 | var oHTTP = createRequest();
130 | if (oHTTP) {
131 | if (fncCallback) {
132 | if (typeof(oHTTP.onload) != "undefined") {
133 | oHTTP.onload = function() {
134 | if (oHTTP.status == "200") {
135 | fncCallback(this);
136 | } else {
137 | if (fncError)
138 | fncError();
139 | }
140 | oHTTP = null;
141 | };
142 | } else {
143 | oHTTP.onreadystatechange = function() {
144 | if (oHTTP.readyState == 4) {
145 | if (oHTTP.status == "200") {
146 | fncCallback(this);
147 | } else {
148 | if (fncError)
149 | fncError();
150 | }
151 | oHTTP = null;
152 | }
153 | };
154 | }
155 | }
156 | oHTTP.open("HEAD", strURL, true);
157 | oHTTP.send(null);
158 | } else {
159 | if (fncError)
160 | fncError();
161 | }
162 | }
163 |
164 | function sendRequest(strURL, fncCallback, fncError, aRange, bAcceptRanges, iFileSize) {
165 | var oHTTP = createRequest();
166 | if (oHTTP) {
167 |
168 | var iDataOffset = 0;
169 | if (aRange && !bAcceptRanges) {
170 | iDataOffset = aRange[0];
171 | }
172 | var iDataLen = 0;
173 | if (aRange) {
174 | iDataLen = aRange[1] - aRange[0] + 1;
175 | }
176 |
177 | if (fncCallback) {
178 | if (typeof(oHTTP.onload) != "undefined") {
179 | oHTTP.onload = function() {
180 | if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") {
181 | oHTTP.binaryResponse = new BinaryFile(oHTTP.responseText, iDataOffset, iDataLen);
182 | oHTTP.fileSize = iFileSize || oHTTP.getResponseHeader("Content-Length");
183 | fncCallback(oHTTP);
184 | } else {
185 | if (fncError)
186 | fncError();
187 | }
188 | oHTTP = null;
189 | };
190 | } else {
191 | oHTTP.onreadystatechange = function() {
192 | if (oHTTP.readyState == 4) {
193 | if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") {
194 | // IE6 craps if we try to extend the XHR object
195 | var oRes = {
196 | status: oHTTP.status,
197 | // IE needs responseBody, Chrome/Safari needs responseText
198 | binaryResponse: new BinaryFile(
199 | typeof oHTTP.responseBody == "unknown" ? oHTTP.responseBody : oHTTP.responseText, iDataOffset, iDataLen
200 | ),
201 | fileSize: iFileSize || oHTTP.getResponseHeader("Content-Length")
202 | };
203 | fncCallback(oRes);
204 | } else {
205 | if (fncError)
206 | fncError();
207 | }
208 | oHTTP = null;
209 | }
210 | };
211 | }
212 | }
213 | oHTTP.open("GET", strURL, true);
214 |
215 | if (oHTTP.overrideMimeType)
216 | oHTTP.overrideMimeType('text/plain; charset=x-user-defined');
217 |
218 | if (aRange && bAcceptRanges) {
219 | oHTTP.setRequestHeader("Range", "bytes=" + aRange[0] + "-" + aRange[1]);
220 | }
221 |
222 | oHTTP.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 1970 00:00:00 GMT");
223 |
224 | oHTTP.send(null);
225 | } else {
226 | if (fncError)
227 | fncError();
228 | }
229 | }
230 |
231 | return function(strURL, fncCallback, fncError, aRange) {
232 |
233 | if (aRange) {
234 | getHead(
235 | strURL,
236 | function(oHTTP) {
237 | var iLength = parseInt(oHTTP.getResponseHeader("Content-Length"), 10);
238 | var strAcceptRanges = oHTTP.getResponseHeader("Accept-Ranges");
239 |
240 | var iStart, iEnd;
241 | iStart = aRange[0];
242 | if (aRange[0] < 0)
243 | iStart += iLength;
244 | iEnd = iStart + aRange[1] - 1;
245 |
246 | sendRequest(strURL, fncCallback, fncError, [iStart, iEnd], (strAcceptRanges == "bytes"), iLength);
247 | }
248 | );
249 |
250 | } else {
251 | sendRequest(strURL, fncCallback, fncError);
252 | }
253 | }
254 |
255 | }());
256 |
257 | /*
258 | document.write(
259 | "\r\n"
267 | );
268 | */
269 |
270 | document.write(
271 | "\r\n"
287 | );
--------------------------------------------------------------------------------
/static/js/lib/jquery.canvasResize.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery canvasResize plugin
3 | *
4 | * Version: 1.2.0
5 | * Date (d/m/y): 02/10/12
6 | * Update (d/m/y): 14/05/13
7 | * Original author: @gokercebeci
8 | * Licensed under the MIT license
9 | * - This plugin working with jquery.exif.js
10 | * (It's under the MPL License http://www.nihilogic.dk/licenses/mpl-license.txt)
11 | * Demo: http://ios6-image-resize.gokercebeci.com/
12 | *
13 | * - I fixed iOS6 Safari's image file rendering issue for large size image (over mega-pixel)
14 | * using few functions from https://github.com/stomita/ios-imagefile-megapixel
15 | * (detectSubsampling, )
16 | * And fixed orientation issue by edited http://blog.nihilogic.dk/2008/05/jquery-exif-data-plugin.html
17 | * Thanks, Shinichi Tomita and Jacob Seidelin
18 | */
19 |
20 | (function($) {
21 | var pluginName = 'canvasResize',
22 | methods = {
23 | newsize: function(w, h, W, H, C) {
24 | var c = C ? 'h' : '';
25 | if ((W && w > W) || (H && h > H)) {
26 | var r = w / h;
27 | if ((r >= 1 || H === 0) && W && !C) {
28 | w = W;
29 | h = (W / r) >> 0;
30 | } else if (C && r <= (W / H)) {
31 | w = W;
32 | h = (W / r) >> 0;
33 | c = 'w';
34 | } else {
35 | w = (H * r) >> 0;
36 | h = H;
37 | }
38 | }
39 | return {
40 | 'width': w,
41 | 'height': h,
42 | 'cropped': c
43 | };
44 | },
45 | dataURLtoBlob: function(data) {
46 | var mimeString = data.split(',')[0].split(':')[1].split(';')[0];
47 | var byteString = atob(data.split(',')[1]);
48 | var ab = new ArrayBuffer(byteString.length);
49 | var ia = new Uint8Array(ab);
50 | for (var i = 0; i < byteString.length; i++) {
51 | ia[i] = byteString.charCodeAt(i);
52 | }
53 | var bb = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder);
54 | if (bb) {
55 | // console.log('BlobBuilder');
56 | bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)();
57 | bb.append(ab);
58 | return bb.getBlob(mimeString);
59 | } else {
60 | // console.log('Blob');
61 | bb = new Blob([ab], {
62 | 'type': (mimeString)
63 | });
64 | return bb;
65 | }
66 | },
67 | /**
68 | * Detect subsampling in loaded image.
69 | * In iOS, larger images than 2M pixels may be subsampled in rendering.
70 | */
71 | detectSubsampling: function(img) {
72 | var iw = img.width, ih = img.height;
73 | if (iw * ih > 1048576) { // subsampling may happen over megapixel image
74 | var canvas = document.createElement('canvas');
75 | canvas.width = canvas.height = 1;
76 | var ctx = canvas.getContext('2d');
77 | ctx.drawImage(img, -iw + 1, 0);
78 | // subsampled image becomes half smaller in rendering size.
79 | // check alpha channel value to confirm image is covering edge pixel or not.
80 | // if alpha value is 0 image is not covering, hence subsampled.
81 | return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
82 | } else {
83 | return false;
84 | }
85 | },
86 | /**
87 | * Update the orientation according to the specified rotation angle
88 | */
89 | rotate: function(orientation, angle) {
90 | var o = {
91 | // nothing
92 | 1: {90: 6, 180: 3, 270: 8},
93 | // horizontal flip
94 | 2: {90: 7, 180: 4, 270: 5},
95 | // 180 rotate left
96 | 3: {90: 8, 180: 1, 270: 6},
97 | // vertical flip
98 | 4: {90: 5, 180: 2, 270: 7},
99 | // vertical flip + 90 rotate right
100 | 5: {90: 2, 180: 7, 270: 4},
101 | // 90 rotate right
102 | 6: {90: 3, 180: 8, 270: 1},
103 | // horizontal flip + 90 rotate right
104 | 7: {90: 4, 180: 5, 270: 2},
105 | // 90 rotate left
106 | 8: {90: 1, 180: 6, 270: 3}
107 | };
108 | return o[orientation][angle] ? o[orientation][angle] : orientation;
109 | },
110 | /**
111 | * Transform canvas coordination according to specified frame size and orientation
112 | * Orientation value is from EXIF tag
113 | */
114 | transformCoordinate: function(canvas, width, height, orientation) {
115 | //console.log(width, height);
116 | switch (orientation) {
117 | case 5:
118 | case 6:
119 | case 7:
120 | case 8:
121 | canvas.width = height;
122 | canvas.height = width;
123 | break;
124 | default:
125 | canvas.width = width;
126 | canvas.height = height;
127 | }
128 | var ctx = canvas.getContext('2d');
129 | switch (orientation) {
130 | case 1:
131 | // nothing
132 | break;
133 | case 2:
134 | // horizontal flip
135 | ctx.translate(width, 0);
136 | ctx.scale(-1, 1);
137 | break;
138 | case 3:
139 | // 180 rotate left
140 | ctx.translate(width, height);
141 | ctx.rotate(Math.PI);
142 | break;
143 | case 4:
144 | // vertical flip
145 | ctx.translate(0, height);
146 | ctx.scale(1, -1);
147 | break;
148 | case 5:
149 | // vertical flip + 90 rotate right
150 | ctx.rotate(0.5 * Math.PI);
151 | ctx.scale(1, -1);
152 | break;
153 | case 6:
154 | // 90 rotate right
155 | ctx.rotate(0.5 * Math.PI);
156 | ctx.translate(0, -height);
157 | break;
158 | case 7:
159 | // horizontal flip + 90 rotate right
160 | ctx.rotate(0.5 * Math.PI);
161 | ctx.translate(width, -height);
162 | ctx.scale(-1, 1);
163 | break;
164 | case 8:
165 | // 90 rotate left
166 | ctx.rotate(-0.5 * Math.PI);
167 | ctx.translate(-width, 0);
168 | break;
169 | default:
170 | break;
171 | }
172 | },
173 | /**
174 | * Detecting vertical squash in loaded image.
175 | * Fixes a bug which squash image vertically while drawing into canvas for some images.
176 | */
177 | detectVerticalSquash: function(img, iw, ih) {
178 | var canvas = document.createElement('canvas');
179 | canvas.width = 1;
180 | canvas.height = ih;
181 | var ctx = canvas.getContext('2d');
182 | ctx.drawImage(img, 0, 0);
183 | var data = ctx.getImageData(0, 0, 1, ih).data;
184 | // search image edge pixel position in case it is squashed vertically.
185 | var sy = 0;
186 | var ey = ih;
187 | var py = ih;
188 | while (py > sy) {
189 | var alpha = data[(py - 1) * 4 + 3];
190 | if (alpha === 0) {
191 | ey = py;
192 | } else {
193 | sy = py;
194 | }
195 | py = (ey + sy) >> 1;
196 | }
197 | var ratio = py / ih;
198 | return ratio === 0 ? 1 : ratio;
199 | },
200 | callback: function(d) {
201 | return d;
202 | }
203 | },
204 | defaults = {
205 | width: 300,
206 | height: 0,
207 | crop: false,
208 | quality: 80,
209 | 'callback': methods.callback
210 | };
211 | function Plugin(file, options) {
212 | this.file = file;
213 | this.options = $.extend({}, defaults, options);
214 | this._defaults = defaults;
215 | this._name = pluginName;
216 | this.init();
217 | }
218 | Plugin.prototype = {
219 | init: function() {
220 | //this.options.init(this);
221 | var $this = this;
222 | var file = this.file;
223 |
224 | var reader = new FileReader();
225 | reader.onloadend = function(e) {
226 | var dataURL = e.target.result;
227 | var img = new Image();
228 | img.onload = function(e) {
229 | // Read Orientation Data in EXIF
230 | $(img).exifLoadFromDataURL(function() {
231 | var orientation = $(img).exif('Orientation')[0] || 1;
232 | orientation = methods.rotate(orientation, $this.options.rotate);
233 |
234 | // CW or CCW ? replace width and height
235 | var size = (orientation >= 5 && orientation <= 8)
236 | ? methods.newsize(img.height, img.width, $this.options.width, $this.options.height, $this.options.crop)
237 | : methods.newsize(img.width, img.height, $this.options.width, $this.options.height, $this.options.crop);
238 |
239 | var iw = img.width, ih = img.height;
240 | var width = size.width, height = size.height;
241 |
242 | //console.log(iw, ih, size.width, size.height, orientation);
243 |
244 | var canvas = document.createElement("canvas");
245 | var ctx = canvas.getContext("2d");
246 | ctx.save();
247 | methods.transformCoordinate(canvas, width, height, orientation);
248 |
249 | // over image size
250 | if (methods.detectSubsampling(img)) {
251 | iw /= 2;
252 | ih /= 2;
253 | }
254 | var d = 1024; // size of tiling canvas
255 | var tmpCanvas = document.createElement('canvas');
256 | tmpCanvas.width = tmpCanvas.height = d;
257 | var tmpCtx = tmpCanvas.getContext('2d');
258 | var vertSquashRatio = methods.detectVerticalSquash(img, iw, ih);
259 | var sy = 0;
260 | while (sy < ih) {
261 | var sh = sy + d > ih ? ih - sy : d;
262 | var sx = 0;
263 | while (sx < iw) {
264 | var sw = sx + d > iw ? iw - sx : d;
265 | tmpCtx.clearRect(0, 0, d, d);
266 | tmpCtx.drawImage(img, -sx, -sy);
267 | var dx = Math.floor(sx * width / iw);
268 | var dw = Math.ceil(sw * width / iw);
269 | var dy = Math.floor(sy * height / ih / vertSquashRatio);
270 | var dh = Math.ceil(sh * height / ih / vertSquashRatio);
271 | ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
272 | sx += d;
273 | }
274 | sy += d;
275 | }
276 | ctx.restore();
277 | tmpCanvas = tmpCtx = null;
278 |
279 | // if cropped or rotated width and height data replacing issue
280 | var newcanvas = document.createElement('canvas');
281 | newcanvas.width = size.cropped === 'h' ? height : width;
282 | newcanvas.height = size.cropped === 'w' ? width : height;
283 | var x = size.cropped === 'h' ? (height - width) * .5 : 0;
284 | var y = size.cropped === 'w' ? (width - height) * .5 : 0;
285 | newctx = newcanvas.getContext('2d');
286 | newctx.drawImage(canvas, x, y, width, height);
287 |
288 | if (file.type === "image/png") {
289 | var data = newcanvas.toDataURL(file.type);
290 | } else {
291 | var data = newcanvas.toDataURL("image/jpeg", ($this.options.quality * .01));
292 | }
293 |
294 | // CALLBACK
295 | $this.options.callback(data, width, height);
296 |
297 | });
298 | };
299 | img.src = dataURL;
300 | // =====================================================
301 | };
302 | reader.readAsDataURL(file);
303 |
304 | }
305 | };
306 | $[pluginName] = function(file, options) {
307 | if (typeof file === 'string')
308 | return methods[file](options);
309 | else
310 | new Plugin(file, options);
311 | };
312 |
313 | })(jQuery);
--------------------------------------------------------------------------------
/static/js/lib/exif.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Javascript EXIF Reader 0.1.6
3 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/
4 | * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
5 | */
6 |
7 |
8 | var EXIF = (function() {
9 |
10 | var debug = false;
11 |
12 | var ExifTags = {
13 |
14 | // version tags
15 | 0x9000: "ExifVersion", // EXIF version
16 | 0xA000: "FlashpixVersion", // Flashpix format version
17 |
18 | // colorspace tags
19 | 0xA001: "ColorSpace", // Color space information tag
20 |
21 | // image configuration
22 | 0xA002: "PixelXDimension", // Valid width of meaningful image
23 | 0xA003: "PixelYDimension", // Valid height of meaningful image
24 | 0x9101: "ComponentsConfiguration", // Information about channels
25 | 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel
26 |
27 | // user information
28 | 0x927C: "MakerNote", // Any desired information written by the manufacturer
29 | 0x9286: "UserComment", // Comments by user
30 |
31 | // related file
32 | 0xA004: "RelatedSoundFile", // Name of related sound file
33 |
34 | // date and time
35 | 0x9003: "DateTimeOriginal", // Date and time when the original image was generated
36 | 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally
37 | 0x9290: "SubsecTime", // Fractions of seconds for DateTime
38 | 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
39 | 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
40 |
41 | // picture-taking conditions
42 | 0x829A: "ExposureTime", // Exposure time (in seconds)
43 | 0x829D: "FNumber", // F number
44 | 0x8822: "ExposureProgram", // Exposure program
45 | 0x8824: "SpectralSensitivity", // Spectral sensitivity
46 | 0x8827: "ISOSpeedRatings", // ISO speed rating
47 | 0x8828: "OECF", // Optoelectric conversion factor
48 | 0x9201: "ShutterSpeedValue", // Shutter speed
49 | 0x9202: "ApertureValue", // Lens aperture
50 | 0x9203: "BrightnessValue", // Value of brightness
51 | 0x9204: "ExposureBias", // Exposure bias
52 | 0x9205: "MaxApertureValue", // Smallest F number of lens
53 | 0x9206: "SubjectDistance", // Distance to subject in meters
54 | 0x9207: "MeteringMode", // Metering mode
55 | 0x9208: "LightSource", // Kind of light source
56 | 0x9209: "Flash", // Flash status
57 | 0x9214: "SubjectArea", // Location and area of main subject
58 | 0x920A: "FocalLength", // Focal length of the lens in mm
59 | 0xA20B: "FlashEnergy", // Strobe energy in BCPS
60 | 0xA20C: "SpatialFrequencyResponse", //
61 | 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
62 | 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
63 | 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
64 | 0xA214: "SubjectLocation", // Location of subject in image
65 | 0xA215: "ExposureIndex", // Exposure index selected on camera
66 | 0xA217: "SensingMethod", // Image sensor type
67 | 0xA300: "FileSource", // Image source (3 == DSC)
68 | 0xA301: "SceneType", // Scene type (1 == directly photographed)
69 | 0xA302: "CFAPattern", // Color filter array geometric pattern
70 | 0xA401: "CustomRendered", // Special processing
71 | 0xA402: "ExposureMode", // Exposure mode
72 | 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual
73 | 0xA404: "DigitalZoomRation", // Digital zoom ratio
74 | 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
75 | 0xA406: "SceneCaptureType", // Type of scene
76 | 0xA407: "GainControl", // Degree of overall image gain adjustment
77 | 0xA408: "Contrast", // Direction of contrast processing applied by camera
78 | 0xA409: "Saturation", // Direction of saturation processing applied by camera
79 | 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera
80 | 0xA40B: "DeviceSettingDescription", //
81 | 0xA40C: "SubjectDistanceRange", // Distance to subject
82 |
83 | // other tags
84 | 0xA005: "InteroperabilityIFDPointer",
85 | 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image
86 | };
87 |
88 | var TiffTags = {
89 | 0x0100: "ImageWidth",
90 | 0x0101: "ImageHeight",
91 | 0x8769: "ExifIFDPointer",
92 | 0x8825: "GPSInfoIFDPointer",
93 | 0xA005: "InteroperabilityIFDPointer",
94 | 0x0102: "BitsPerSample",
95 | 0x0103: "Compression",
96 | 0x0106: "PhotometricInterpretation",
97 | 0x0112: "Orientation",
98 | 0x0115: "SamplesPerPixel",
99 | 0x011C: "PlanarConfiguration",
100 | 0x0212: "YCbCrSubSampling",
101 | 0x0213: "YCbCrPositioning",
102 | 0x011A: "XResolution",
103 | 0x011B: "YResolution",
104 | 0x0128: "ResolutionUnit",
105 | 0x0111: "StripOffsets",
106 | 0x0116: "RowsPerStrip",
107 | 0x0117: "StripByteCounts",
108 | 0x0201: "JPEGInterchangeFormat",
109 | 0x0202: "JPEGInterchangeFormatLength",
110 | 0x012D: "TransferFunction",
111 | 0x013E: "WhitePoint",
112 | 0x013F: "PrimaryChromaticities",
113 | 0x0211: "YCbCrCoefficients",
114 | 0x0214: "ReferenceBlackWhite",
115 | 0x0132: "DateTime",
116 | 0x010E: "ImageDescription",
117 | 0x010F: "Make",
118 | 0x0110: "Model",
119 | 0x0131: "Software",
120 | 0x013B: "Artist",
121 | 0x8298: "Copyright"
122 | };
123 |
124 | var GPSTags = {
125 | 0x0000: "GPSVersionID",
126 | 0x0001: "GPSLatitudeRef",
127 | 0x0002: "GPSLatitude",
128 | 0x0003: "GPSLongitudeRef",
129 | 0x0004: "GPSLongitude",
130 | 0x0005: "GPSAltitudeRef",
131 | 0x0006: "GPSAltitude",
132 | 0x0007: "GPSTimeStamp",
133 | 0x0008: "GPSSatellites",
134 | 0x0009: "GPSStatus",
135 | 0x000A: "GPSMeasureMode",
136 | 0x000B: "GPSDOP",
137 | 0x000C: "GPSSpeedRef",
138 | 0x000D: "GPSSpeed",
139 | 0x000E: "GPSTrackRef",
140 | 0x000F: "GPSTrack",
141 | 0x0010: "GPSImgDirectionRef",
142 | 0x0011: "GPSImgDirection",
143 | 0x0012: "GPSMapDatum",
144 | 0x0013: "GPSDestLatitudeRef",
145 | 0x0014: "GPSDestLatitude",
146 | 0x0015: "GPSDestLongitudeRef",
147 | 0x0016: "GPSDestLongitude",
148 | 0x0017: "GPSDestBearingRef",
149 | 0x0018: "GPSDestBearing",
150 | 0x0019: "GPSDestDistanceRef",
151 | 0x001A: "GPSDestDistance",
152 | 0x001B: "GPSProcessingMethod",
153 | 0x001C: "GPSAreaInformation",
154 | 0x001D: "GPSDateStamp",
155 | 0x001E: "GPSDifferential"
156 | };
157 |
158 | var StringValues = {
159 | ExposureProgram: {
160 | 0: "Not defined",
161 | 1: "Manual",
162 | 2: "Normal program",
163 | 3: "Aperture priority",
164 | 4: "Shutter priority",
165 | 5: "Creative program",
166 | 6: "Action program",
167 | 7: "Portrait mode",
168 | 8: "Landscape mode"
169 | },
170 | MeteringMode: {
171 | 0: "Unknown",
172 | 1: "Average",
173 | 2: "CenterWeightedAverage",
174 | 3: "Spot",
175 | 4: "MultiSpot",
176 | 5: "Pattern",
177 | 6: "Partial",
178 | 255: "Other"
179 | },
180 | LightSource: {
181 | 0: "Unknown",
182 | 1: "Daylight",
183 | 2: "Fluorescent",
184 | 3: "Tungsten (incandescent light)",
185 | 4: "Flash",
186 | 9: "Fine weather",
187 | 10: "Cloudy weather",
188 | 11: "Shade",
189 | 12: "Daylight fluorescent (D 5700 - 7100K)",
190 | 13: "Day white fluorescent (N 4600 - 5400K)",
191 | 14: "Cool white fluorescent (W 3900 - 4500K)",
192 | 15: "White fluorescent (WW 3200 - 3700K)",
193 | 17: "Standard light A",
194 | 18: "Standard light B",
195 | 19: "Standard light C",
196 | 20: "D55",
197 | 21: "D65",
198 | 22: "D75",
199 | 23: "D50",
200 | 24: "ISO studio tungsten",
201 | 255: "Other"
202 | },
203 | Flash: {
204 | 0x0000: "Flash did not fire",
205 | 0x0001: "Flash fired",
206 | 0x0005: "Strobe return light not detected",
207 | 0x0007: "Strobe return light detected",
208 | 0x0009: "Flash fired, compulsory flash mode",
209 | 0x000D: "Flash fired, compulsory flash mode, return light not detected",
210 | 0x000F: "Flash fired, compulsory flash mode, return light detected",
211 | 0x0010: "Flash did not fire, compulsory flash mode",
212 | 0x0018: "Flash did not fire, auto mode",
213 | 0x0019: "Flash fired, auto mode",
214 | 0x001D: "Flash fired, auto mode, return light not detected",
215 | 0x001F: "Flash fired, auto mode, return light detected",
216 | 0x0020: "No flash function",
217 | 0x0041: "Flash fired, red-eye reduction mode",
218 | 0x0045: "Flash fired, red-eye reduction mode, return light not detected",
219 | 0x0047: "Flash fired, red-eye reduction mode, return light detected",
220 | 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
221 | 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
222 | 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
223 | 0x0059: "Flash fired, auto mode, red-eye reduction mode",
224 | 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
225 | 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
226 | },
227 | SensingMethod: {
228 | 1: "Not defined",
229 | 2: "One-chip color area sensor",
230 | 3: "Two-chip color area sensor",
231 | 4: "Three-chip color area sensor",
232 | 5: "Color sequential area sensor",
233 | 7: "Trilinear sensor",
234 | 8: "Color sequential linear sensor"
235 | },
236 | SceneCaptureType: {
237 | 0: "Standard",
238 | 1: "Landscape",
239 | 2: "Portrait",
240 | 3: "Night scene"
241 | },
242 | SceneType: {
243 | 1: "Directly photographed"
244 | },
245 | CustomRendered: {
246 | 0: "Normal process",
247 | 1: "Custom process"
248 | },
249 | WhiteBalance: {
250 | 0: "Auto white balance",
251 | 1: "Manual white balance"
252 | },
253 | GainControl: {
254 | 0: "None",
255 | 1: "Low gain up",
256 | 2: "High gain up",
257 | 3: "Low gain down",
258 | 4: "High gain down"
259 | },
260 | Contrast: {
261 | 0: "Normal",
262 | 1: "Soft",
263 | 2: "Hard"
264 | },
265 | Saturation: {
266 | 0: "Normal",
267 | 1: "Low saturation",
268 | 2: "High saturation"
269 | },
270 | Sharpness: {
271 | 0: "Normal",
272 | 1: "Soft",
273 | 2: "Hard"
274 | },
275 | SubjectDistanceRange: {
276 | 0: "Unknown",
277 | 1: "Macro",
278 | 2: "Close view",
279 | 3: "Distant view"
280 | },
281 | FileSource: {
282 | 3: "DSC"
283 | },
284 | Components: {
285 | 0: "",
286 | 1: "Y",
287 | 2: "Cb",
288 | 3: "Cr",
289 | 4: "R",
290 | 5: "G",
291 | 6: "B"
292 | }
293 | };
294 |
295 | function addEvent(element, event, handler) {
296 | if (element.addEventListener) {
297 | element.addEventListener(event, handler, false);
298 | } else if (element.attachEvent) {
299 | element.attachEvent("on" + event, handler);
300 | }
301 | }
302 |
303 | function imageHasData(img) {
304 | return !!(img.exifdata);
305 | }
306 |
307 | function getImageData(img, callback) {
308 | BinaryAjax(img.src, function(http) {
309 | var data = findEXIFinJPEG(http.binaryResponse);
310 | img.exifdata = data || {};
311 | if (callback) {
312 | callback.call(img)
313 | }
314 | });
315 | }
316 |
317 | function findEXIFinJPEG(file) {
318 | if (file.getByteAt(0) != 0xFF || file.getByteAt(1) != 0xD8) {
319 | return false; // not a valid jpeg
320 | }
321 |
322 | var offset = 2,
323 | length = file.getLength(),
324 | marker;
325 |
326 | while (offset < length) {
327 | if (file.getByteAt(offset) != 0xFF) {
328 | if (debug)
329 | console.log("Not a valid marker at offset " + offset + ", found: " + file.getByteAt(offset));
330 | return false; // not a valid marker, something is wrong
331 | }
332 |
333 | marker = file.getByteAt(offset + 1);
334 |
335 | // we could implement handling for other markers here,
336 | // but we're only looking for 0xFFE1 for EXIF data
337 |
338 | if (marker == 22400) {
339 | if (debug)
340 | console.log("Found 0xFFE1 marker");
341 |
342 | return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2);
343 |
344 | // offset += 2 + file.getShortAt(offset+2, true);
345 |
346 | } else if (marker == 225) {
347 | // 0xE1 = Application-specific 1 (for EXIF)
348 | if (debug)
349 | console.log("Found 0xFFE1 marker");
350 |
351 | return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2);
352 |
353 | } else {
354 | offset += 2 + file.getShortAt(offset + 2, true);
355 | }
356 |
357 | }
358 |
359 | }
360 |
361 |
362 | function readTags(file, tiffStart, dirStart, strings, bigEnd) {
363 | var entries = file.getShortAt(dirStart, bigEnd),
364 | tags = {},
365 | entryOffset, tag,
366 | i;
367 |
368 | for (i = 0; i < entries; i++) {
369 | entryOffset = dirStart + i * 12 + 2;
370 | tag = strings[file.getShortAt(entryOffset, bigEnd)];
371 | if (!tag && debug)
372 | console.log("Unknown tag: " + file.getShortAt(entryOffset, bigEnd));
373 | tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
374 | }
375 | return tags;
376 | }
377 |
378 |
379 | function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
380 | var type = file.getShortAt(entryOffset + 2, bigEnd),
381 | numValues = file.getLongAt(entryOffset + 4, bigEnd),
382 | valueOffset = file.getLongAt(entryOffset + 8, bigEnd) + tiffStart,
383 | offset,
384 | vals, val, n,
385 | numerator, denominator;
386 |
387 | switch (type) {
388 | case 1: // byte, 8-bit unsigned int
389 | case 7: // undefined, 8-bit byte, value depending on field
390 | if (numValues == 1) {
391 | return file.getByteAt(entryOffset + 8, bigEnd);
392 | } else {
393 | offset = numValues > 4 ? valueOffset : (entryOffset + 8);
394 | vals = [];
395 | for (n = 0; n < numValues; n++) {
396 | vals[n] = file.getByteAt(offset + n);
397 | }
398 | return vals;
399 | }
400 |
401 | case 2: // ascii, 8-bit byte
402 | offset = numValues > 4 ? valueOffset : (entryOffset + 8);
403 | return file.getStringAt(offset, numValues - 1);
404 |
405 | case 3: // short, 16 bit int
406 | if (numValues == 1) {
407 | return file.getShortAt(entryOffset + 8, bigEnd);
408 | } else {
409 | offset = numValues > 2 ? valueOffset : (entryOffset + 8);
410 | vals = [];
411 | for (n = 0; n < numValues; n++) {
412 | vals[n] = file.getShortAt(offset + 2 * n, bigEnd);
413 | }
414 | return vals;
415 | }
416 |
417 | case 4: // long, 32 bit int
418 | if (numValues == 1) {
419 | return file.getLongAt(entryOffset + 8, bigEnd);
420 | } else {
421 | vals = [];
422 | for (var n = 0; n < numValues; n++) {
423 | vals[n] = file.getLongAt(valueOffset + 4 * n, bigEnd);
424 | }
425 | return vals;
426 | }
427 |
428 | case 5: // rational = two long values, first is numerator, second is denominator
429 | if (numValues == 1) {
430 | numerator = file.getLongAt(valueOffset, bigEnd);
431 | denominator = file.getLongAt(valueOffset + 4, bigEnd);
432 | val = new Number(numerator / denominator);
433 | val.numerator = numerator;
434 | val.denominator = denominator;
435 | return val;
436 | } else {
437 | vals = [];
438 | for (n = 0; n < numValues; n++) {
439 | numerator = file.getLongAt(valueOffset + 8 * n, bigEnd);
440 | denominator = file.getLongAt(valueOffset + 4 + 8 * n, bigEnd);
441 | vals[n] = new Number(numerator / denominator);
442 | vals[n].numerator = numerator;
443 | vals[n].denominator = denominator;
444 | }
445 | return vals;
446 | }
447 |
448 | case 9: // slong, 32 bit signed int
449 | if (numValues == 1) {
450 | return file.getSLongAt(entryOffset + 8, bigEnd);
451 | } else {
452 | vals = [];
453 | for (n = 0; n < numValues; n++) {
454 | vals[n] = file.getSLongAt(valueOffset + 4 * n, bigEnd);
455 | }
456 | return vals;
457 | }
458 |
459 | case 10: // signed rational, two slongs, first is numerator, second is denominator
460 | if (numValues == 1) {
461 | return file.getSLongAt(valueOffset, bigEnd) / file.getSLongAt(valueOffset + 4, bigEnd);
462 | } else {
463 | vals = [];
464 | for (n = 0; n < numValues; n++) {
465 | vals[n] = file.getSLongAt(valueOffset + 8 * n, bigEnd) / file.getSLongAt(valueOffset + 4 + 8 * n, bigEnd);
466 | }
467 | return vals;
468 | }
469 | }
470 | }
471 |
472 |
473 | function readEXIFData(file, start) {
474 | if (file.getStringAt(start, 4) != "Exif") {
475 | if (debug)
476 | console.log("Not valid EXIF data! " + file.getStringAt(start, 4));
477 | return false;
478 | }
479 |
480 | var bigEnd,
481 | tags, tag,
482 | exifData, gpsData,
483 | tiffOffset = start + 6;
484 |
485 | // test for TIFF validity and endianness
486 | if (file.getShortAt(tiffOffset) == 0x4949) {
487 | bigEnd = false;
488 | } else if (file.getShortAt(tiffOffset) == 0x4D4D) {
489 | bigEnd = true;
490 | } else {
491 | if (debug)
492 | console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
493 | return false;
494 | }
495 |
496 | if (file.getShortAt(tiffOffset + 2, bigEnd) != 0x002A) {
497 | if (debug)
498 | console.log("Not valid TIFF data! (no 0x002A)");
499 | return false;
500 | }
501 |
502 | if (file.getLongAt(tiffOffset + 4, bigEnd) != 0x00000008) {
503 | if (debug)
504 | console.log("Not valid TIFF data! (First offset not 8)", file.getShortAt(tiffOffset + 4, bigEnd));
505 | return false;
506 | }
507 |
508 | tags = readTags(file, tiffOffset, tiffOffset + 8, TiffTags, bigEnd);
509 |
510 | if (tags.ExifIFDPointer) {
511 | exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
512 | for (tag in exifData) {
513 | switch (tag) {
514 | case "LightSource" :
515 | case "Flash" :
516 | case "MeteringMode" :
517 | case "ExposureProgram" :
518 | case "SensingMethod" :
519 | case "SceneCaptureType" :
520 | case "SceneType" :
521 | case "CustomRendered" :
522 | case "WhiteBalance" :
523 | case "GainControl" :
524 | case "Contrast" :
525 | case "Saturation" :
526 | case "Sharpness" :
527 | case "SubjectDistanceRange" :
528 | case "FileSource" :
529 | exifData[tag] = StringValues[tag][exifData[tag]];
530 | break;
531 |
532 | case "ExifVersion" :
533 | case "FlashpixVersion" :
534 | exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
535 | break;
536 |
537 | case "ComponentsConfiguration" :
538 | exifData[tag] =
539 | StringValues.Components[exifData[tag][0]]
540 | + StringValues.Components[exifData[tag][1]]
541 | + StringValues.Components[exifData[tag][2]]
542 | + StringValues.Components[exifData[tag][3]];
543 | break;
544 | }
545 | tags[tag] = exifData[tag];
546 | }
547 | }
548 |
549 | if (tags.GPSInfoIFDPointer) {
550 | gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
551 | for (tag in gpsData) {
552 | switch (tag) {
553 | case "GPSVersionID" :
554 | gpsData[tag] = gpsData[tag][0]
555 | + "." + gpsData[tag][1]
556 | + "." + gpsData[tag][2]
557 | + "." + gpsData[tag][3];
558 | break;
559 | }
560 | tags[tag] = gpsData[tag];
561 | }
562 | }
563 |
564 | return tags;
565 | }
566 |
567 |
568 | function getData(img, callback) {
569 | if (!img.complete)
570 | return false;
571 | if (!imageHasData(img)) {
572 | getImageData(img, callback);
573 | } else {
574 | if (callback) {
575 | callback.call(img);
576 | }
577 | }
578 | return true;
579 | }
580 |
581 | function getTag(img, tag) {
582 | if (!imageHasData(img))
583 | return;
584 | return img.exifdata[tag];
585 | }
586 |
587 | function getAllTags(img) {
588 | if (!imageHasData(img))
589 | return {};
590 | var a,
591 | data = img.exifdata,
592 | tags = {};
593 | for (a in data) {
594 | if (data.hasOwnProperty(a)) {
595 | tags[a] = data[a];
596 | }
597 | }
598 | return tags;
599 | }
600 |
601 | function pretty(img) {
602 | if (!imageHasData(img))
603 | return "";
604 | var a,
605 | data = img.exifdata,
606 | strPretty = "";
607 | for (a in data) {
608 | if (data.hasOwnProperty(a)) {
609 | if (typeof data[a] == "object") {
610 | if (data[a] instanceof Number) {
611 | strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
612 | } else {
613 | strPretty += a + " : [" + data[a].length + " values]\r\n";
614 | }
615 | } else {
616 | strPretty += a + " : " + data[a] + "\r\n";
617 | }
618 | }
619 | }
620 | return strPretty;
621 | }
622 |
623 | function readFromBinaryFile(file) {
624 | return findEXIFinJPEG(file);
625 | }
626 |
627 |
628 | return {
629 | readFromBinaryFile: readFromBinaryFile,
630 | pretty: pretty,
631 | getTag: getTag,
632 | getAllTags: getAllTags,
633 | getData: getData,
634 | Tags: ExifTags,
635 | TiffTags: TiffTags,
636 | GPSTags: GPSTags,
637 | StringValues: StringValues
638 | };
639 |
640 | })();
--------------------------------------------------------------------------------
/static/js/lib/jquery.exif.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Javascript EXIF Reader - jQuery plugin 0.1.3
4 | * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
5 | * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
6 | */
7 |
8 | (function($) {
9 |
10 |
11 | var BinaryFile = function(strData, iDataOffset, iDataLength) {
12 | var data = strData;
13 | var dataOffset = iDataOffset || 0;
14 | var dataLength = 0;
15 |
16 | this.getRawData = function() {
17 | return data;
18 | };
19 |
20 | if (typeof strData == "string") {
21 | dataLength = iDataLength || data.length;
22 |
23 | this.getByteAt = function(iOffset) {
24 | return data.charCodeAt(iOffset + dataOffset) & 0xFF;
25 | };
26 | } else if (typeof strData == "unknown") {
27 | dataLength = iDataLength || IEBinary_getLength(data);
28 |
29 | this.getByteAt = function(iOffset) {
30 | return IEBinary_getByteAt(data, iOffset + dataOffset);
31 | };
32 | }
33 |
34 | this.getLength = function() {
35 | return dataLength;
36 | };
37 |
38 | this.getSByteAt = function(iOffset) {
39 | var iByte = this.getByteAt(iOffset);
40 | if (iByte > 127)
41 | return iByte - 256;
42 | else
43 | return iByte;
44 | };
45 |
46 | this.getShortAt = function(iOffset, bBigEndian) {
47 | var iShort = bBigEndian ?
48 | (this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1)
49 | : (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset);
50 | if (iShort < 0) iShort += 65536;
51 | return iShort;
52 | };
53 | this.getSShortAt = function(iOffset, bBigEndian) {
54 | var iUShort = this.getShortAt(iOffset, bBigEndian);
55 | if (iUShort > 32767)
56 | return iUShort - 65536;
57 | else
58 | return iUShort;
59 | };
60 | this.getLongAt = function(iOffset, bBigEndian) {
61 | var iByte1 = this.getByteAt(iOffset),
62 | iByte2 = this.getByteAt(iOffset + 1),
63 | iByte3 = this.getByteAt(iOffset + 2),
64 | iByte4 = this.getByteAt(iOffset + 3);
65 |
66 | var iLong = bBigEndian ?
67 | (((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4
68 | : (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
69 | if (iLong < 0) iLong += 4294967296;
70 | return iLong;
71 | };
72 | this.getSLongAt = function(iOffset, bBigEndian) {
73 | var iULong = this.getLongAt(iOffset, bBigEndian);
74 | if (iULong > 2147483647)
75 | return iULong - 4294967296;
76 | else
77 | return iULong;
78 | };
79 | this.getStringAt = function(iOffset, iLength) {
80 | var aStr = [];
81 | for (var i=iOffset,j=0;i