├── .gitignore
├── .npmignore
├── .travis.yml
├── Gruntfile.js
├── LICENSE
├── README.md
├── README.ru.md
├── bower.json
├── crossdomain.xml
├── custom-tasks
└── Gruntfile-ok.js
├── dist
├── FileAPI.flash.camera.swf
├── FileAPI.flash.image.swf
├── FileAPI.flash.swf
├── FileAPI.html5.js
├── FileAPI.html5.min.js
├── FileAPI.js
├── FileAPI.min.js
└── jquery.fileapi.min.js
├── examples
├── caman.html
├── demo.html
├── thumbnails.html
├── toolkit.css
├── userpic.html
├── watermark.html
└── webcam.html
├── flash
├── .gitignore
├── camera
│ ├── .settings
│ │ └── org.eclipse.core.resources.prefs
│ ├── html-template
│ │ ├── history
│ │ │ ├── history.css
│ │ │ ├── history.js
│ │ │ └── historyFrame.html
│ │ ├── index.template.html
│ │ ├── playerProductInstall.swf
│ │ └── swfobject.js
│ └── src
│ │ └── FileAPI_flash_camera.as
├── core
│ ├── .settings
│ │ └── org.eclipse.core.resources.prefs
│ ├── html-template
│ │ ├── history
│ │ │ ├── history.css
│ │ │ ├── history.js
│ │ │ └── historyFrame.html
│ │ ├── index.template.html
│ │ ├── playerProductInstall.swf
│ │ └── swfobject.js
│ ├── lib
│ │ ├── EnginesLibrary.swc
│ │ └── blooddy_crypto.swc
│ └── src
│ │ ├── FileAPI_flash.as
│ │ ├── net
│ │ └── inspirit
│ │ │ ├── MultipartURLLoader.as
│ │ │ └── events
│ │ │ └── MultipartURLLoaderEvent.as
│ │ └── ru
│ │ └── mail
│ │ ├── commands
│ │ ├── AbstractUploadFileCommand.as
│ │ ├── DecodeBytesToBitmapCommand.as
│ │ ├── LoadFileCommand.as
│ │ ├── ResizeFileCommand.as
│ │ ├── UploadCommand.as
│ │ ├── UploadFileCommand.as
│ │ ├── UploadImageCommand.as
│ │ ├── graphicloader
│ │ │ ├── IGraphicLoader.as
│ │ │ ├── SimpleGraphicLoader.as
│ │ │ └── events
│ │ │ │ └── GraphicLoaderCompleteEvent.as
│ │ └── textloader
│ │ │ ├── ITextLoader.as
│ │ │ ├── SimpleTextLoader.as
│ │ │ └── events
│ │ │ ├── LoaderProgressEvent.as
│ │ │ └── TextLoaderCompleteEvent.as
│ │ ├── communication
│ │ ├── JSCallbackPresenter.as
│ │ └── JSCaller.as
│ │ ├── controller
│ │ ├── AppController.as
│ │ └── CameraController.as
│ │ ├── data
│ │ ├── AbstractImageFactory.as
│ │ ├── AttachmentsModel.as
│ │ ├── IImageFactory.as
│ │ ├── ImageFactory.as
│ │ ├── builder
│ │ │ ├── AbstractDataBuilder.as
│ │ │ └── FilesDataBuilder.as
│ │ └── vo
│ │ │ ├── BaseFileVO.as
│ │ │ ├── ErrorVO.as
│ │ │ ├── FakeFileVO.as
│ │ │ ├── FileStatesEnum.as
│ │ │ ├── FileVO.as
│ │ │ ├── IFileVO.as
│ │ │ ├── ImageTransformVO.as
│ │ │ ├── OverlayVO.as
│ │ │ └── PhotoFileVO.as
│ │ ├── engines
│ │ ├── chain
│ │ │ ├── AbstractModelJSEngine.as
│ │ │ ├── EnginesFactory.as
│ │ │ ├── IJsCallerHolder.as
│ │ │ ├── IModelHolder.as
│ │ │ ├── manage
│ │ │ │ └── SelectFilesEngine.as
│ │ │ └── presentation
│ │ │ │ └── MouseListenerEngine.as
│ │ └── commands
│ │ │ ├── MouseListenerEngineCommand.as
│ │ │ └── SelectFilesCommand.as
│ │ ├── events
│ │ ├── CompleteEvent.as
│ │ ├── DecodeBytesToBitmapCompleteEvent.as
│ │ ├── ImageTransformCompleteEvent.as
│ │ └── UploadCompleteEvent.as
│ │ └── utils
│ │ ├── BMPDecoder.as
│ │ ├── ExifReader2.as
│ │ └── LoggerJS.as
└── image
│ ├── .settings
│ └── org.eclipse.core.resources.prefs
│ ├── html-template
│ ├── history
│ │ ├── history.css
│ │ ├── history.js
│ │ └── historyFrame.html
│ ├── index.template.html
│ ├── playerProductInstall.swf
│ └── swfobject.js
│ ├── lib
│ └── blooddy_crypto.swc
│ └── src
│ ├── Base64.as
│ └── FileAPI_flash_image.as
├── index.html
├── lib
├── FileAPI.Camera.js
├── FileAPI.Flash.Camera.js
├── FileAPI.Flash.js
├── FileAPI.Form.js
├── FileAPI.Image.js
├── FileAPI.XHR.js
├── FileAPI.core.js
├── canvas-to-blob.js
└── load-image-ios.js
├── node
├── file-api.js
└── server.js
├── package.json
├── plugins
├── FileAPI.exif.js
├── FileAPI.id3.js
├── caman.full.min.js
└── caman.min.js
├── server
├── FileAPI.class.php
└── ctrl.php
├── statics
├── body.png
├── body__top.png
├── content.png
├── docs.json
├── logo.png
├── main.css
├── watermark.png
└── xtpl.min.js
├── support.html
└── tests
├── files
├── 1px.gif
├── big.jpg
├── dino.png
├── hello.txt
├── image.jpg
├── lebowski.json
└── samples
│ ├── chrome-dino-180deg-50x50.png
│ ├── chrome-dino-50x50.jpeg
│ ├── chrome-dino-90deg-100x100.png
│ ├── chrome-dino-custom.png
│ ├── chrome-image-auto-100x100.jpeg
│ ├── firefox-dino-180deg-50x50.png
│ ├── firefox-dino-50x50.jpeg
│ ├── firefox-dino-90deg-100x100.png
│ ├── firefox-dino-custom.png
│ ├── firefox-image-auto-100x100.jpeg
│ ├── firefox-vintage.png
│ ├── phantomjs-dino-180deg-50x50.png
│ ├── phantomjs-dino-50x50.jpeg
│ ├── phantomjs-dino-90deg-100x100.png
│ ├── phantomjs-dino-custom.png
│ ├── phantomjs-image-auto-100x100.jpeg
│ ├── phantomjs-vintage.png
│ └── safari-dino-90deg-100x100.png
├── flash.html
├── grunt-task
├── phantomjs
│ ├── bridge.js
│ ├── grunt-lib-phantomjs-main.js
│ └── grunt-lib-phantomjs.js
└── qunit.js
├── index.html
├── json.html
├── pure-resize-tests.html
├── qunit
├── qunit.css
└── qunit.js
└── tests.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/FileAPI.html5ok.js
3 | dist/FileAPI.html5ok.min.js
4 | dist/FileAPI.ok.js
5 | dist/FileAPI.ok.min.js
6 | .idea
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 4.5
4 | before_script:
5 | - npm install -g grunt-cli
6 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (grunt) {
4 | // Project configuration.
5 | grunt.initConfig({
6 | pkg: grunt.file.readJSON('package.json'),
7 |
8 | jshint: {
9 | all: [
10 | 'Gruntfile.js'
11 | , 'lib/**/*.js'
12 | , 'plugins/jquery.fileapi.js'
13 | , 'node/**/*.js'
14 | ],
15 |
16 | options: {
17 | curly: true // + "Expected '{' and instead saw 'XXXX'."
18 | , immed: true
19 | , latedef: true
20 | , newcap: true // "Tolerate uncapitalized constructors"
21 | , noarg: true
22 | , sub: true
23 | , undef: true
24 | , unused: true
25 | , boss: true
26 | , eqnull: true
27 |
28 | , node: true
29 | , expr: true // - "Expected an assignment or function call and instead saw an expression."
30 | , supernew: true // - "Missing '()' invoking a constructor."
31 | , laxcomma: true
32 | , laxbreak: true
33 | , smarttabs: true
34 | }
35 | },
36 |
37 | version: {
38 | src: 'lib/FileAPI.core.js'
39 | },
40 |
41 | connect: {
42 | server: {
43 | options: {
44 | port: 9001,
45 | base: '.'
46 | }
47 | },
48 | standalone: {
49 | options: {
50 | hostname: '*',
51 | keepalive: true,
52 | port: 9001,
53 | base: '.'
54 | }
55 | }
56 | },
57 |
58 | curl: {
59 | jpg: {
60 | src: 'https://dl.dropboxusercontent.com/u/49592745/BigJPG.jpg',
61 | dest: 'tests/files/big.jpg'
62 | }
63 | },
64 |
65 | qunit: {
66 | all: {
67 | options: {
68 | timeout: 5 * 60 * 1000, // 5min
69 | files: {
70 | '1px_gif': ['tests/files/1px.gif']
71 | , 'big.jpg': ['tests/files/big.jpg']
72 | , 'hello.txt': ['tests/files/hello.txt']
73 | , 'image.jpg': ['tests/files/image.jpg']
74 | , 'dino.png': ['tests/files/dino.png']
75 | , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json']
76 | },
77 | urls: ['http://127.0.0.1:<%=connect.server.options.port%>/tests/index.html']
78 | }
79 | }
80 | },
81 |
82 | concat: {
83 | options: {
84 | banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %>\n' +
85 | ' * <%= pkg.description %>\n' +
86 | ' */\n\n',
87 |
88 | footer: 'if( typeof define === "function" && define.amd ){ define("<%= pkg.jam.name %>", [], function (){ return FileAPI; }); }'
89 | },
90 |
91 | all: {
92 | src: [
93 | 'lib/canvas-to-blob.js'
94 | , 'lib/FileAPI.core.js'
95 | , 'lib/FileAPI.Image.js'
96 | , 'lib/load-image-ios.js'
97 | , 'lib/FileAPI.Form.js'
98 | , 'lib/FileAPI.XHR.js'
99 | , 'lib/FileAPI.Camera.js'
100 | , 'lib/FileAPI.Flash.js'
101 | , 'lib/FileAPI.Flash.Camera.js'
102 | ],
103 | dest: 'dist/<%= pkg.exportName %>.js'
104 | },
105 |
106 | html5: {
107 | src: [
108 | 'lib/canvas-to-blob.js'
109 | , 'lib/FileAPI.core.js'
110 | , 'lib/FileAPI.Image.js'
111 | , 'lib/load-image-ios.js'
112 | , 'lib/FileAPI.Form.js'
113 | , 'lib/FileAPI.XHR.js'
114 | , 'lib/FileAPI.Camera.js'
115 | , 'lib/FileAPI.Flash.Camera.js'
116 | ],
117 | dest: 'dist/<%= pkg.exportName %>.html5.js'
118 | }
119 | },
120 |
121 | uglify: {
122 | options: { banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' },
123 | dist: {
124 | files: {
125 | 'dist/<%= pkg.exportName %>.min.js': ['<%= concat.all.dest %>']
126 | , 'dist/<%= pkg.exportName %>.html5.min.js': ['<%= concat.html5.dest %>']
127 | }
128 | }
129 | },
130 |
131 | mxmlc: {
132 | core: {
133 | options: {
134 | rawConfig: '-target-player=10.1 -static-link-runtime-shared-libraries=true -compiler.debug=false' +
135 | ' -library-path+=flash/core/lib/blooddy_crypto.swc -library-path+=flash/core/lib/EnginesLibrary.swc'
136 | },
137 | files: {
138 | 'dist/<%= pkg.exportName %>.flash.swf': ['flash/core/src/FileAPI_flash.as']
139 | }
140 | },
141 | image: {
142 | options: {
143 | rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' +
144 | ' -library-path+=flash/image/lib/blooddy_crypto.swc'
145 | },
146 | files: {
147 | 'dist/<%= pkg.exportName %>.flash.image.swf': ['flash/image/src/FileAPI_flash_image.as']
148 | }
149 | },
150 | camera: {
151 | options: {
152 | rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false'
153 | },
154 | files: {
155 | 'dist/<%= pkg.exportName %>.flash.camera.swf': ['flash/camera/src/FileAPI_flash_camera.as']
156 | }
157 | }
158 | },
159 |
160 | watch: {
161 | scripts: {
162 | files: 'lib/**/*.js',
163 | tasks: ['concat'],
164 | options: { interrupt: true }
165 | }
166 | }
167 | });
168 |
169 | // These plugins provide necessary tasks.
170 | grunt.loadNpmTasks('grunt-version');
171 | grunt.loadNpmTasks('grunt-contrib-jshint');
172 | grunt.loadNpmTasks('grunt-contrib-concat');
173 | grunt.loadNpmTasks('grunt-contrib-uglify');
174 | grunt.loadNpmTasks('grunt-contrib-watch');
175 | grunt.loadNpmTasks('grunt-contrib-connect');
176 | grunt.loadNpmTasks('grunt-contrib-compress');
177 | grunt.loadNpmTasks('grunt-mxmlc');
178 | grunt.loadNpmTasks('grunt-curl');
179 |
180 | // Load custom QUnit task, based on grunt-contrib-qunit, but support "files" option.
181 | grunt.loadTasks('./tests/grunt-task/');
182 | grunt.loadTasks('./custom-tasks/');
183 |
184 | // "npm build" runs these tasks
185 | grunt.registerTask('prepare-test-files', function (){
186 | // big.jpg added to git
187 | /*if (!grunt.file.exists('tests/files/big.jpg')) {
188 | grunt.task.run('curl');
189 | }*/
190 | });
191 |
192 | grunt.registerTask('express', 'Start a custom web server.', function() {
193 | var done = this.async();
194 |
195 | require('./node/server.js').createServer(8000, function () {
196 | done();
197 | });
198 | });
199 |
200 | grunt.registerTask('server', ['connect:server', 'express']);
201 | grunt.registerTask('dev', ['concat', 'server', 'watch']);
202 | grunt.registerTask('tests', ['jshint', 'concat', 'server', 'prepare-test-files', 'qunit']);
203 | grunt.registerTask('build', ['version', 'concat', 'uglify']);
204 | grunt.registerTask('build-all', ['build', 'mxmlc']);
205 | grunt.registerTask('default', ['tests', 'build']);
206 | };
207 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2012 FileAPI AUTHORS:
2 | Konstantin Lebedev, Demidov Vladimir.
3 |
4 | /*
5 | * Redistribution and use in source and binary forms, with or
6 | * without modification, are permitted provided that the following
7 | * conditions are met:
8 | *
9 | * 1. Redistributions of source code must retain the above
10 | * copyright notice, this list of conditions and the
11 | * following disclaimer.
12 | *
13 | * 2. Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials
16 | * provided with the distribution.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY MAILRU.RU GROUP ``AS IS'' AND
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 | * MAILRU.RU GROUP OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 | * SUCH DAMAGE.
31 | */
32 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fileapi",
3 | "main": [
4 | "./dist/FileAPI.flash.camera.swf",
5 | "./dist/FileAPI.flash.image.swf",
6 | "./dist/FileAPI.flash.swf",
7 | "./dist/FileAPI.html5.js",
8 | "./dist/FileAPI.js",
9 | "./dist/jquery.fileapi.min.js"
10 | ],
11 | "dependencies": {
12 | "jquery":">=1.8.2"
13 | },
14 | "ignore": [
15 | "custom-tasks/",
16 | "flash/",
17 | "lib/",
18 | "plugins/",
19 | "server/",
20 | "statics/",
21 | "tests/",
22 | "package.json",
23 | "bower.json",
24 | "README.md",
25 | ".*"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/custom-tasks/Gruntfile-ok.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (grunt) {
4 | // Project configuration.
5 | grunt.config.set('concat.ok', {
6 | src: [
7 | 'lib/FileAPI.header.js'
8 | , 'lib/canvas-to-blob.js'
9 | , 'lib/FileAPI.core.js'
10 | , 'lib/FileAPI.Image.js'
11 | , 'lib/load-image-ios.js'
12 | , 'lib/FileAPI.Form.js'
13 | , 'lib/FileAPI.XHR.js'
14 | , 'lib/FileAPI.Flash.js'
15 | , 'plugins/FileAPI.exif.js'
16 | ],
17 | dest: 'dist/<%= pkg.exportName %>.ok.js'
18 | });
19 | grunt.config.set('concat.html5ok', {
20 | src: [
21 | 'lib/FileAPI.header.js'
22 | , 'lib/canvas-to-blob.js'
23 | , 'lib/FileAPI.core.js'
24 | , 'lib/FileAPI.Image.js'
25 | , 'lib/load-image-ios.js'
26 | , 'lib/FileAPI.Form.js'
27 | , 'lib/FileAPI.XHR.js'
28 | , 'plugins/FileAPI.exif.js'
29 | ],
30 | dest: 'dist/<%= pkg.exportName %>.html5ok.js'
31 | });
32 |
33 | grunt.config.set('uglify.distok', {
34 | files: {
35 | 'dist/<%= pkg.exportName %>.ok.min.js': ['<%= concat.ok.dest %>'], 'dist/<%= pkg.exportName %>.html5ok.min.js': ['<%= concat.html5ok.dest %>']
36 | }
37 | });
38 |
39 | grunt.config.set('compress.main', {
40 | options: {
41 | archive: '<%= pkg.name %>-<%= pkg.version.replace(/\\./g,"-") %>.zip'
42 | },
43 | files: [
44 | {cwd: 'dist/', expand: true, src: ['*'], dest: '<%= pkg.version.replace(/\\./g,"-") %>/'}
45 | ]
46 | });
47 |
48 |
49 | grunt.registerTask('build-zip', ['build', 'compress']);
50 |
51 | };
52 |
--------------------------------------------------------------------------------
/dist/FileAPI.flash.camera.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/dist/FileAPI.flash.camera.swf
--------------------------------------------------------------------------------
/dist/FileAPI.flash.image.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/dist/FileAPI.flash.image.swf
--------------------------------------------------------------------------------
/dist/FileAPI.flash.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/dist/FileAPI.flash.swf
--------------------------------------------------------------------------------
/examples/thumbnails.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | FileAPI :: Thumbnails :: example
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
75 |
76 |
77 |
78 |
89 |
90 |
91 |
Thumbnails
92 |
93 |
94 |
95 | Select photo (min 480x320)
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/examples/toolkit.css:
--------------------------------------------------------------------------------
1 | .body {
2 | font-size: 14px;
3 | }
4 |
5 | .btn, .body {
6 | font-family: "Raleway","Helvetica Neue",Helvetica,Arial,sans-serif;
7 | }
8 |
9 |
10 | .btn {
11 | padding: 10px 50px;
12 | color: #fff;
13 | cursor: pointer;
14 | display: inline-block;
15 | text-decoration: none;
16 | font-size: 24px;
17 | border-radius: 4px;
18 | background-color: #80BD95;
19 | box-shadow: 0 3px 0 0 #72A884;
20 | text-shadow: 0 -2px 0 rgba(0,0,0,.2);
21 | }
22 |
23 |
24 | .loader {
25 | width: 30px;
26 | height: 30px;
27 | margin: 50px 0 50px -15px;
28 | border: 8px solid #000;
29 | border-right-color: transparent;
30 | display: inline-block;
31 | border-radius: 50%;
32 | box-shadow: 0 0 25px 2px #eee;
33 | border-right: 0 none;
34 | -webkit-animation: spin 1s linear infinite;
35 | -moz-animation: spin 1s linear infinite;
36 | -ms-animation: spin 1s linear infinite;
37 | -o-animation: spin 1s linear infinite;
38 | animation: spin 1s linear infinite;
39 | }
40 |
41 | .js-fileapi-wrapper {
42 | display: inline-block;
43 | *zoom: 1;
44 | *display: inline;
45 | }
46 | .js-fileapi-wrapper input {
47 | width: 0;
48 | height: 0;
49 | opacity: 0;
50 | overflow: 0;
51 | position: absolute;
52 | }
53 |
54 | @-webkit-keyframes spin {
55 | from { -webkit-transform: rotate(0deg); opacity: 0.4; }
56 | 50% { -webkit-transform: rotate(180deg); opacity: 1; }
57 | to { -webkit-transform: rotate(360deg); opacity: 0.4; }
58 | }
59 |
60 | @-moz-keyframes spin {
61 | from { -moz-transform: rotate(0deg); opacity: 0.4; }
62 | 50% { -moz-transform: rotate(180deg); opacity: 1; }
63 | to { -moz-transform: rotate(360deg); opacity: 0.4; }
64 | }
65 |
66 | @-ms-keyframes spin {
67 | from { -ms-transform: rotate(0deg); opacity: 0.4; }
68 | 50% { -ms-transform: rotate(180deg); opacity: 1; }
69 | to { -ms-transform: rotate(360deg); opacity: 0.4; }
70 | }
71 |
72 | @keyframes spin {
73 | from { transform: rotate(0deg); opacity: 0.2; }
74 | 50% { transform: rotate(180deg); opacity: 1; }
75 | to { transform: rotate(360deg); opacity: 0.2; }
76 | }
77 |
--------------------------------------------------------------------------------
/examples/webcam.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | FileAPI :: WebCam :: example
22 |
23 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
47 |
48 |
59 |
60 |
61 |
WebCam
62 |
63 |
64 |
65 |
shot
66 |
start
67 |
stop
68 |
69 |
Shots
70 |
71 |
72 |
Server
73 |
74 |
75 |
76 |
77 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/flash/.gitignore:
--------------------------------------------------------------------------------
1 | #.gitignore
2 |
3 | core/bin-debug
4 | core/bin-release
5 |
6 | core/.actionScriptProperties
7 | core/.project
8 |
9 | camera/bin-debug
10 | camera/bin-release
11 |
12 | camera/.actionScriptProperties
13 | camera/.project
14 |
15 | image/bin-debug
16 | image/bin-release
17 |
18 | image/.actionScriptProperties
19 | image/.project
20 |
--------------------------------------------------------------------------------
/flash/camera/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | #Thu May 02 20:14:08 MSD 2013
2 | eclipse.preferences.version=1
3 | encoding/=utf-8
4 |
--------------------------------------------------------------------------------
/flash/camera/html-template/history/history.css:
--------------------------------------------------------------------------------
1 | /* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */
2 |
3 | #ie_historyFrame { width: 0px; height: 0px; display:none }
4 | #firefox_anchorDiv { width: 0px; height: 0px; display:none }
5 | #safari_formDiv { width: 0px; height: 0px; display:none }
6 | #safari_rememberDiv { width: 0px; height: 0px; display:none }
7 |
--------------------------------------------------------------------------------
/flash/camera/html-template/history/historyFrame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
27 | Hidden frame for Browser History support.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/flash/camera/html-template/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 | ${title}
15 |
16 |
17 |
23 |
30 |
31 |
32 |
36 |
37 |
38 |
61 |
62 |
63 |
67 |
68 |
69 | To view this page ensure that Adobe Flash Player version
70 | ${version_major}.${version_minor}.${version_revision} or greater is installed.
71 |
72 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Either scripts and active content are not permitted to run or Adobe Flash Player version
96 | ${version_major}.${version_minor}.${version_revision} or greater is not installed.
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/flash/camera/html-template/playerProductInstall.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/camera/html-template/playerProductInstall.swf
--------------------------------------------------------------------------------
/flash/camera/src/FileAPI_flash_camera.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import flash.display.BitmapData;
4 | import flash.display.Sprite;
5 | import flash.display.StageAlign;
6 | import flash.display.StageScaleMode;
7 | import flash.events.Event;
8 | import flash.events.StatusEvent;
9 | import flash.media.Camera;
10 | import flash.media.Video;
11 | import flash.utils.setTimeout;
12 |
13 | public class FileAPI_flash_camera extends Sprite
14 | {
15 | private var video:Video;
16 | private var camera:Camera;
17 |
18 | public function FileAPI_flash_camera()
19 | {
20 | if (stage)
21 | init();
22 | else
23 | addEventListener(Event.ADDED_TO_STAGE, init);
24 |
25 | }
26 |
27 | public function init(e:Event = null):void
28 | {
29 | if(e)
30 | removeEventListener(Event.ADDED_TO_STAGE, init);
31 |
32 | stage.scaleMode = StageScaleMode.SHOW_ALL;
33 | stage.align = StageAlign.TOP_LEFT;
34 |
35 | // init camera
36 | camera = Camera.getCamera();
37 | if (camera != null) {
38 | camera.addEventListener(StatusEvent.STATUS, onCameraStatus);
39 | // we need to show settings dialog, so we attach camera to a video
40 | video = new Video();
41 | video.attachCamera(camera);
42 | if (!camera.muted) {
43 | onCameraStatus(new StatusEvent(StatusEvent.STATUS, false, false, 'Camera.Unmuted'));
44 | }
45 | else {
46 | setTimeout(function ():void {
47 | if (securityPanelIsClosed()) {
48 | onCameraStatus(new StatusEvent(StatusEvent.STATUS, false, false, 'Camera.Muted'));
49 | }
50 | }, 1000);
51 | }
52 |
53 | } else {
54 | // callback with error
55 | }
56 | }
57 |
58 | public function toggleCamera(on:Boolean):void {
59 | trace('toggleCamera',on);
60 | if (on) {
61 | if (video != null) {
62 | // turn current video off
63 | toggleCamera(false);
64 | }
65 | trace('stage width',stage.stageWidth,'stage w',stage.width,'camera width',camera.width);
66 | var w:Number = stage.stageWidth;
67 | var h:Number = stage.stageHeight; //stage.stageWidth * camera.height / camera.width;
68 | camera.setMode(w, h, camera.fps);
69 | video = new Video(w, h);
70 | video.attachCamera(camera);
71 |
72 | video.addEventListener(Event.ADDED_TO_STAGE, function(event:Event):void {
73 | trace('video addedToStage');
74 | // now the camera is visible
75 | dispatchEvent(new Event('Camera.On'));
76 | });
77 | addChildAt(video,0);
78 | } else {
79 | if(video)
80 | removeChild(video);
81 | video = null;
82 | }
83 | }
84 |
85 | public function shot():BitmapData {
86 | if (video) {
87 | trace('click!', video.width, stage.stageWidth);
88 | try{
89 | var bm:BitmapData = new BitmapData(video.width,video.height);
90 | bm.draw(video);
91 | return bm;
92 | } catch(error:Error) {
93 | trace('error',error.toString() );
94 | return null;
95 | }
96 | }
97 | return null;
98 | }
99 |
100 | private function onCameraStatus(event:StatusEvent):void {
101 | trace('status event:',event.toString() );
102 | video = null; // turn off video
103 | // redispatch
104 | dispatchEvent(event.clone());
105 | }
106 |
107 | /**
108 | * This code checks one time if the security panel is closed.
109 | * When you open the security panel, you should run this test
110 | * repeatedly with a timer (every 500ms seems to work well).
111 | * If the security panel is closed, you can then clean up your timers
112 | */
113 | private function securityPanelIsClosed():Boolean
114 | {
115 | // Why not just wait for an event from the SettingsPanel to know that it's closed? Because there isn't one.
116 | // See http://bugs.adobe.com/jira/browse/FP-41
117 | var closed:Boolean = true;
118 | var hack:BitmapData = new BitmapData(1,1);
119 | try
120 | {
121 | // Trying to capture the stage triggers a Security error when the settings dialog box is open.
122 | hack.draw(stage);
123 | }
124 | catch (error:Error)
125 | {
126 | closed = false;
127 | }
128 | hack.dispose();
129 | hack = null;
130 | return (closed);
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/flash/core/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | #Thu Aug 02 18:18:41 MSD 2012
2 | eclipse.preferences.version=1
3 | encoding/=utf-8
4 |
--------------------------------------------------------------------------------
/flash/core/html-template/history/history.css:
--------------------------------------------------------------------------------
1 | /* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */
2 |
3 | #ie_historyFrame { width: 0px; height: 0px; display:none }
4 | #firefox_anchorDiv { width: 0px; height: 0px; display:none }
5 | #safari_formDiv { width: 0px; height: 0px; display:none }
6 | #safari_rememberDiv { width: 0px; height: 0px; display:none }
7 |
--------------------------------------------------------------------------------
/flash/core/html-template/history/historyFrame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
27 | Hidden frame for Browser History support.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/flash/core/html-template/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 | ${title}
15 |
16 |
17 |
23 |
30 |
31 |
32 |
36 |
37 |
38 |
61 |
62 |
63 |
67 |
68 |
69 | To view this page ensure that Adobe Flash Player version
70 | ${version_major}.${version_minor}.${version_revision} or greater is installed.
71 |
72 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Either scripts and active content are not permitted to run or Adobe Flash Player version
96 | ${version_major}.${version_minor}.${version_revision} or greater is not installed.
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/flash/core/html-template/playerProductInstall.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/core/html-template/playerProductInstall.swf
--------------------------------------------------------------------------------
/flash/core/lib/EnginesLibrary.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/core/lib/EnginesLibrary.swc
--------------------------------------------------------------------------------
/flash/core/lib/blooddy_crypto.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/core/lib/blooddy_crypto.swc
--------------------------------------------------------------------------------
/flash/core/src/FileAPI_flash.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import flash.display.Sprite;
4 | import flash.display.StageAlign;
5 | import flash.display.StageQuality;
6 | import flash.display.StageScaleMode;
7 | import flash.events.Event;
8 | import flash.events.UncaughtErrorEvent;
9 |
10 | import ru.mail.controller.AppController;
11 |
12 | /**
13 | *
14 | * @author v.demidov https://github.com/im-saxo
15 | *
16 | */
17 | public class FileAPI_flash extends Sprite
18 | {
19 | private var _controller:AppController;
20 | private var _graphicContext:Sprite = new Sprite();
21 |
22 | public function FileAPI_flash()
23 | {
24 | if (stage) {
25 | init();
26 | }
27 | else {
28 | addEventListener(Event.ADDED_TO_STAGE, init);
29 | }
30 | }
31 |
32 | /**
33 | * entry point
34 | * @param event
35 | *
36 | */
37 | protected function init(event:Event = null):void
38 | {
39 | removeEventListener(Event.ADDED_TO_STAGE, init);
40 |
41 | // config stage
42 | stage.align = StageAlign.TOP_LEFT;
43 | stage.scaleMode = StageScaleMode.NO_SCALE;
44 | stage.quality = StageQuality.BEST;
45 |
46 | // add graphic context
47 | addChild(_graphicContext);
48 |
49 | // initiate controller
50 | _controller = new AppController(_graphicContext, loaderInfo.parameters);
51 | // add some global listeners
52 | stage.addEventListener(Event.RESIZE, _controller.onStageResize);
53 | loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, _controller.onUncaughtError);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/flash/core/src/net/inspirit/events/MultipartURLLoaderEvent.as:
--------------------------------------------------------------------------------
1 | package net.inspirit.events
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * MultipartURLLoader Event for async data prepare tracking
7 | * @author Eugene Zatepyakin
8 | */
9 | public class MultipartURLLoaderEvent extends Event
10 | {
11 | public static const DATA_PREPARE_PROGRESS:String = 'dataPrepareProgress';
12 | public static const DATA_PREPARE_COMPLETE:String = 'dataPrepareComplete';
13 |
14 | public var bytesWritten:uint = 0;
15 | public var bytesTotal:uint = 0;
16 |
17 | public function MultipartURLLoaderEvent(type:String, w:uint = 0, t:uint = 0)
18 | {
19 | super(type);
20 |
21 | bytesTotal = t;
22 | bytesWritten = w;
23 | }
24 |
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/AbstractUploadFileCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands
2 | {
3 | import flash.events.EventDispatcher;
4 | import flash.net.URLRequestHeader;
5 | import flash.net.URLVariables;
6 |
7 | import ru.mail.data.vo.ErrorVO;
8 | import ru.mail.events.UploadCompleteEvent;
9 |
10 | /**
11 | * Base class for UploadFileCommand and UploadImageCommand
12 | * @author v.demidov
13 | *
14 | */
15 | public class AbstractUploadFileCommand extends EventDispatcher
16 | {
17 | protected var _url:String;
18 | protected var _uploadDataFieldName:String = "Filedata";
19 | protected var _uploadPostData:URLVariables;
20 | protected var _contentType:String;
21 | protected var _requestHeaders:Array;
22 |
23 | public function AbstractUploadFileCommand(url:String, headers:Object, uploadPostData:Object, uploadDataFieldName:String = "Filedata")
24 | {
25 | super();
26 |
27 | _url = url;
28 | if (uploadDataFieldName && uploadDataFieldName != "")
29 | _uploadDataFieldName = uploadDataFieldName;
30 | parsePostData(uploadPostData, headers);
31 | }
32 |
33 | public function execute():void {/* do nothing*/}
34 |
35 | public function dispose():void {}
36 |
37 | /**
38 | * it cancels upload and disposes the object
39 | */
40 | public function cancel():void {}
41 |
42 | /**
43 | * prepare post data, content-type and other request headers for upload
44 | * @param uploadPostData
45 | * @param headers
46 | *
47 | */
48 | protected function parsePostData(uploadPostData:Object, headers:Object):void
49 | {
50 | // create URLVariables
51 | _uploadPostData = new URLVariables();
52 | var prop:String;
53 | if (uploadPostData != null)
54 | {
55 | for (prop in uploadPostData)
56 | {
57 | _uploadPostData[prop] = uploadPostData[prop];
58 | }
59 | }
60 |
61 | if (headers != null)
62 | {
63 | _requestHeaders = new Array();
64 |
65 | for (prop in headers)
66 | {
67 | if (prop == "Content-Type")
68 | {
69 | // handle Content-Type apart from other headers
70 | _contentType = headers[prop];
71 | }
72 | else {
73 | _requestHeaders.push( new URLRequestHeader(prop, headers[prop]) );
74 | }
75 | }
76 | }
77 | }
78 |
79 | protected function complete(isSuccess:Boolean, result:String, error:ErrorVO = null):void
80 | {
81 | dispatchEvent( new UploadCompleteEvent(isSuccess, result, error) );
82 |
83 | try{
84 | dispose();
85 | } catch (e:Error) {};
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/DecodeBytesToBitmapCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands
2 | {
3 |
4 | import flash.display.Bitmap;
5 | import flash.display.BitmapData;
6 | import flash.display.Loader;
7 | import flash.display.LoaderInfo;
8 | import flash.events.Event;
9 | import flash.events.EventDispatcher;
10 | import flash.events.IOErrorEvent;
11 | import flash.events.SecurityErrorEvent;
12 | import flash.events.TimerEvent;
13 | import flash.utils.ByteArray;
14 | import flash.utils.Timer;
15 |
16 | import ru.mail.data.vo.ErrorVO;
17 | import ru.mail.events.DecodeBytesToBitmapCompleteEvent;
18 | import ru.mail.utils.BMPDecoder;
19 |
20 |
21 | /**
22 | * The command decodes bytes to image with the Loader object. png, jpg, bmp and unanimated gif only.
23 | *
24 | * Animated gif - currently there is no check for it, so it will be decoded, but the image might be incorrect
25 | *
26 | * @author ivanova
27 | */
28 | public class DecodeBytesToBitmapCommand extends EventDispatcher
29 | {
30 | private var bmpDecoder:BMPDecoder = new BMPDecoder();
31 | private var _bytes:ByteArray ;
32 | private var _loader:Loader = new Loader() ;
33 | private var _terminateTimer:Timer = new Timer( 20 * 1000 );
34 | private var _isTerminated:Boolean = false;
35 |
36 | /**
37 | * ctor
38 | * @param bytes: the bytes collection to decode
39 | */
40 | public function DecodeBytesToBitmapCommand( bytes:ByteArray )
41 | {
42 | if ( null == bytes )
43 | throw new Error( "DecodeBytesToBitmapCommand bytes is null" ) ;
44 |
45 | _bytes = bytes ;
46 |
47 | _loader.contentLoaderInfo.addEventListener( Event.COMPLETE, _onDecodeImageComplete ) ;
48 | _loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, _onDecodeImageError ) ;
49 | _loader.contentLoaderInfo.addEventListener( SecurityErrorEvent.SECURITY_ERROR, _onDecodeImageError ) ;
50 |
51 | _terminateTimer.addEventListener( TimerEvent.TIMER, _onTerminateTimer);
52 | }
53 |
54 | public function dispose():void
55 | {
56 | _loader.contentLoaderInfo.removeEventListener( Event.COMPLETE, _onDecodeImageComplete ) ;
57 | _loader.contentLoaderInfo.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, _onDecodeImageError ) ;
58 | _loader.contentLoaderInfo.removeEventListener( IOErrorEvent.IO_ERROR, _onDecodeImageError ) ;
59 |
60 | _bytes = null ;
61 | try
62 | {
63 | if ( ( _loader.contentLoaderInfo.content as Bitmap ) != null )
64 | ( _loader.contentLoaderInfo.content as Bitmap ).bitmapData.dispose() ;
65 | }
66 | catch ( e:Error ) { }
67 |
68 | bmpDecoder = null;
69 | _loader = null ;
70 | _terminateTimer.removeEventListener( TimerEvent.TIMER, _onTerminateTimer);
71 | _terminateTimer = null;
72 | }
73 |
74 | public function execute():void
75 | {
76 | try
77 | {
78 | _terminateTimer.start();
79 | _loader.loadBytes( _bytes ) ;
80 | }
81 | catch ( e:Error )
82 | {
83 | complete( false, null, new ErrorVO( e.toString() ) ); ;
84 | }
85 | }
86 |
87 | private function _onDecodeImageComplete( e:Event ):void
88 | {
89 |
90 | try
91 | {
92 | var image:Bitmap = ( e.target as LoaderInfo ).content as Bitmap ;
93 | var isSuccess:Boolean = ( image != null ) ;
94 | complete( isSuccess, image ) ;
95 | }
96 | catch ( e:Error ) { complete( false, null, new ErrorVO( e.toString() ) ); }
97 | }
98 |
99 | private function _onDecodeImageError( e:Event ):void
100 | {
101 |
102 | try {
103 | var bd:BitmapData = bmpDecoder.decode(_bytes);
104 | var image:Bitmap = new Bitmap(bd);
105 | var isSuccess:Boolean = ( image != null && ( !bmpDecoder.decodeError || bmpDecoder.decodeError == "" ) ) ;
106 | complete( isSuccess, image, bmpDecoder.decodeError? new ErrorVO(bmpDecoder.decodeError) : null ) ;
107 | } catch(e:Error) {
108 | complete( false, null, new ErrorVO( e.toString() ) );
109 | }
110 | }
111 |
112 | private function _onTerminateTimer( e:TimerEvent ):void
113 | {
114 | complete( false, null, new ErrorVO("DecodeBytesToImageCommand timeout") );
115 | }
116 |
117 | private function complete( isSuccess:Boolean, image:Bitmap, error:ErrorVO = null ):void
118 | {
119 | if ( !_isTerminated )
120 | {
121 | _isTerminated = true;
122 | _terminateTimer.stop();
123 | dispatchEvent( new DecodeBytesToBitmapCompleteEvent( isSuccess, image, error ) ) ;
124 | }
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/LoadFileCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands
2 | {
3 | import flash.events.Event;
4 | import flash.events.EventDispatcher;
5 | import flash.events.IOErrorEvent;
6 | import flash.events.ProgressEvent;
7 | import flash.net.FileReference;
8 | import flash.utils.ByteArray;
9 |
10 | import ru.mail.data.vo.ErrorVO;
11 | import ru.mail.data.vo.FileStatesEnum;
12 | import ru.mail.data.vo.FileVO;
13 | import ru.mail.events.ImageTransformCompleteEvent;
14 |
15 | /**
16 | *
17 | * Load bytes from FileReference
18 | *
19 | * @author v.demidov
20 | *
21 | */
22 | public class LoadFileCommand extends EventDispatcher
23 | {
24 | private var fileRef:FileReference;
25 | private var file:FileVO;
26 | protected var isCancelled:Boolean = false;
27 |
28 | public function LoadFileCommand(file:FileVO)
29 | {
30 | super();
31 |
32 | if ( null == file )
33 | throw new Error( "LoadFileCommand fileVO is null" ) ;
34 |
35 | this.file = file;
36 | fileRef = file.fileRef; // shortcut
37 |
38 | fileRef.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
39 | fileRef.addEventListener(ProgressEvent.PROGRESS, onProgress);
40 | fileRef.addEventListener(Event.COMPLETE, onLoadComplete);
41 | }
42 |
43 | public function execute():void
44 | {
45 | try
46 | {
47 | // change file status to prevent simultanious uploading and loading
48 | file.status = FileStatesEnum.LOADING;
49 | // load
50 | fileRef.load() ;
51 | }
52 | catch ( e:Error ){
53 | complete( false, null, new ErrorVO(e.toString()) );
54 | }
55 | }
56 |
57 | /**
58 | * it cancels load and disposes the object
59 | */
60 | public function cancel():void
61 | {
62 | try{
63 | isCancelled = true;
64 |
65 | fileRef.cancel();
66 |
67 | dispose();
68 | }catch(e:Error){
69 | trace ("LoadFileCommand cancel() error: "+e.toString());
70 | }
71 |
72 | file.status = FileStatesEnum.SELECTED;
73 | }
74 |
75 | public function dispose():void
76 | {
77 | fileRef.removeEventListener( Event.COMPLETE, onLoadComplete);
78 | fileRef.removeEventListener( IOErrorEvent.IO_ERROR, onLoadError );
79 | fileRef.removeEventListener(ProgressEvent.PROGRESS, onProgress);
80 | }
81 |
82 | private function onLoadError(event:IOErrorEvent):void
83 | {
84 | complete( false, null, new ErrorVO(event.toString(), IOErrorEvent.IO_ERROR) );
85 | }
86 |
87 | private function onProgress(event:ProgressEvent):void
88 | {
89 | dispatchEvent(event.clone());
90 | }
91 |
92 | /**
93 | * step 1 complete,
94 | * step 2: create bitmap
95 | * @param event
96 | *
97 | */
98 | private function onLoadComplete(event:Event):void
99 | {
100 | if (isCancelled) {
101 | return;
102 | }
103 | else if (file.fileData != null)
104 | {
105 | complete( true, file.fileData );
106 | }
107 | else {
108 | complete( false, null, new ErrorVO("Error #1009: Loaded data is null.") );
109 | }
110 | }
111 |
112 | private function complete(isSuccess:Boolean, data:ByteArray, error:ErrorVO = null):void
113 | {
114 | dispatchEvent( new ImageTransformCompleteEvent(isSuccess, data, error) );
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/UploadFileCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands
2 | {
3 | import flash.events.DataEvent;
4 | import flash.events.Event;
5 | import flash.events.HTTPStatusEvent;
6 | import flash.events.IOErrorEvent;
7 | import flash.events.ProgressEvent;
8 | import flash.events.SecurityErrorEvent;
9 | import flash.events.TextEvent;
10 | import flash.net.FileReference;
11 | import flash.net.URLRequest;
12 | import flash.net.URLRequestMethod;
13 |
14 | import ru.mail.data.vo.ErrorVO;
15 | import ru.mail.utils.LoggerJS;
16 |
17 | /**
18 | * Upload using fileReference.upload()
19 | * @author v.demidov
20 | *
21 | */
22 | public class UploadFileCommand extends AbstractUploadFileCommand
23 | {
24 | private var fileRef:FileReference;
25 | private var status:String = null; // httpStatus. in case of upload error we get httpStatus event followed by ioError event. add status to error event using this temp variable
26 |
27 | public function UploadFileCommand(fileRef:FileReference, url:String, headers:Object, uploadPostData:Object, uploadDataFieldName:String)
28 | {
29 | super(url, headers, uploadPostData, uploadDataFieldName);
30 |
31 | this.fileRef = fileRef;
32 | }
33 |
34 | override public function dispose():void
35 | {
36 | fileRef.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
37 | fileRef.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
38 | fileRef.removeEventListener(IOErrorEvent.IO_ERROR, onError);
39 | fileRef.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
40 | fileRef.removeEventListener(ProgressEvent.PROGRESS, onProgress);
41 | }
42 |
43 | override public function execute():void
44 | {
45 | if ( _url == null ) {
46 | complete(false, null, new ErrorVO("UploadFileCommand: upload url is null") );
47 | return;
48 | }
49 |
50 | // add listeners
51 | fileRef.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
52 | fileRef.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
53 | fileRef.addEventListener(IOErrorEvent.IO_ERROR, onError);
54 | fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
55 | fileRef.addEventListener(ProgressEvent.PROGRESS, onProgress);
56 |
57 | // create request
58 | var request:URLRequest = new URLRequest(_url);
59 |
60 | // data
61 | request.method = URLRequestMethod.POST;
62 | request.data = _uploadPostData;
63 |
64 | LoggerJS.log("upload file with FileReference, url = " + request.url);
65 | try
66 | {
67 | fileRef.upload(request, _uploadDataFieldName) ;
68 | }
69 | catch ( e:Error ){
70 | trace ("UploadFileCommand execute err", e);
71 | complete( false, null, new ErrorVO( e.toString() ) );
72 | }
73 | }
74 |
75 | /**
76 | * Cancels upload and disposes the object
77 | *
78 | * Known issue: do not call cancel when the progress is 100% but uploadCompleteData hasn't received
79 | * in this case the file fill not be deleted from server
80 | */
81 | override public function cancel():void
82 | {
83 | try{
84 | trace ("UploadFileCommand:cancel");
85 | fileRef.cancel();
86 |
87 | dispose();
88 | }catch(e:Error){
89 | trace ("UploadFileCommand cancel() error: "+e.message);
90 | }
91 | }
92 |
93 | /**
94 | * Get the responce from server
95 | */
96 | private function onUploadCompleteData(event:DataEvent):void
97 | {
98 | trace ("onUploadCompleteData", event);
99 | complete(true, event.data);
100 | }
101 |
102 | private function onHTTPStatus(event:HTTPStatusEvent):void
103 | {
104 | trace ("onHTTPStatus", event);
105 | LoggerJS.log("fileReference.upload HTTPStatusEvent: " +event.status);
106 | status = event.status.toString();
107 | dispatchEvent(new TextEvent("httpStatus", false, false, event.status.toString() ) );
108 | }
109 |
110 | private function onError(event:Event):void
111 | {
112 | // choose error type
113 | var errorType:String = null;
114 | if (event is IOErrorEvent) {
115 | errorType = "IOError";
116 | } else if (event is SecurityErrorEvent) {
117 | errorType = "SecurityError";
118 | }
119 |
120 | LoggerJS.log("fileReference.upload onError: " +event.toString());
121 |
122 | trace ("onError", event);
123 | complete(false, null, new ErrorVO(event.toString(), errorType, status) );
124 | }
125 |
126 | private function onProgress(event:ProgressEvent):void
127 | {
128 | // because of bug (loaded:123123, total:0), use fileRef.size as total
129 | dispatchEvent(new ProgressEvent( ProgressEvent.PROGRESS, false, false, event.bytesLoaded, fileRef.size) );
130 | }
131 |
132 | }
133 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/UploadImageCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands
2 | {
3 | import flash.events.Event;
4 | import flash.events.HTTPStatusEvent;
5 | import flash.events.IOErrorEvent;
6 | import flash.events.ProgressEvent;
7 | import flash.events.SecurityErrorEvent;
8 | import flash.events.TextEvent;
9 |
10 | import net.inspirit.MultipartURLLoader;
11 | import net.inspirit.events.MultipartURLLoaderEvent;
12 |
13 | import ru.mail.data.vo.ErrorVO;
14 | import ru.mail.utils.LoggerJS;
15 |
16 | /**
17 | * Upload using MultipartURLLoader
18 | *
19 | * @author v.demidov
20 | *
21 | */
22 | public class UploadImageCommand extends AbstractUploadFileCommand
23 | {
24 | private var _files:Object;
25 | private var _totalSize:int = 0;
26 | private var _loader:MultipartURLLoader;
27 | private var status:String = null; // httpStatus. in case of upload error we get httpStatus event followed by ioError event. add status to error event using this temp variable
28 |
29 | public function UploadImageCommand(files:Object, url:String, headers:Object, uploadPostData:Object)
30 | {
31 | super(url, headers, uploadPostData);
32 |
33 | _files = files;
34 | }
35 |
36 | override public function dispose():void
37 | {
38 | _loader.removeEventListener(Event.COMPLETE, onUploadComplete);
39 | _loader.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
40 | _loader.loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
41 | _loader.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
42 | _loader.loader.removeEventListener(ProgressEvent.PROGRESS, onProgress);
43 |
44 | _loader.dispose();
45 | _loader = null;
46 | _files = null;
47 | }
48 |
49 | override public function execute():void
50 | {
51 | if ( _url == null ) {
52 | complete(false, null, new ErrorVO("UploadImageCommand: upload url is null") );
53 | return;
54 | }
55 |
56 | _loader = new MultipartURLLoader();
57 | // add listeners
58 | _loader.addEventListener(Event.COMPLETE, onUploadComplete);
59 | _loader.loader.addEventListener(ProgressEvent.PROGRESS, onProgress);
60 | _loader.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
61 | _loader.loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
62 | _loader.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
63 |
64 | // post data
65 | for (var s:String in _uploadPostData) {
66 | _loader.addVariable(s, _uploadPostData[s]);
67 | }
68 | // headers
69 | if (_requestHeaders) {
70 | _loader.requestHeaders = _requestHeaders;
71 | }
72 |
73 | // files data
74 | addFiles();
75 |
76 | try
77 | {
78 | LoggerJS.log("Load file with multipartURLLoader. url = "+_url );
79 | _loader.addEventListener(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE, function(event:MultipartURLLoaderEvent):void {
80 | event.currentTarget.removeEventListener(event.type, arguments.callee);
81 | _loader.startLoad();
82 | });
83 | _loader.load(_url, true);
84 | }
85 | catch ( e:Error ){
86 | trace ("UploadImageCommand execute err", e);
87 | complete( false, null, new ErrorVO( e.toString() ) );
88 | }
89 | }
90 |
91 | /**
92 | * add files
93 | */
94 | private function addFiles():void
95 | {
96 | trace ("addFiles");
97 | for (var s:String in _files )
98 | {
99 | for (var filename:String in _files[s]) {
100 | trace ("s="+s+ ", filename = " + filename);
101 | if ( _files[s][filename] ) {
102 | _totalSize += _files[s][filename].length;
103 | _loader.addFile(_files[s][filename], filename, s);
104 | }
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * Cancels upload and disposes the object
111 | */
112 | override public function cancel():void
113 | {
114 | try{
115 | trace ("UploadImageCommand:cancel");
116 | _loader.close();
117 |
118 | dispose();
119 | }catch(e:Error){
120 | trace ("UploadImageCommand cancel() error: "+e.message);
121 | }
122 | }
123 |
124 | /**
125 | * Get the responce from server
126 | */
127 | private function onUploadComplete(event:Event):void
128 | {
129 | trace ("onUploadCompleteData", event);
130 | complete(true, _loader.loader.data);
131 | }
132 |
133 | private function onHTTPStatus(event:HTTPStatusEvent):void
134 | {
135 | trace ("onHTTPStatus", event);
136 | LoggerJS.log("urlloader.upload onHTTPStatus: " +event.status);
137 | status = event.status.toString();
138 | dispatchEvent(new TextEvent("httpStatus", false, false, event.status.toString() ) );
139 | }
140 |
141 | private function onError(event:Event):void
142 | {
143 | // choose error type
144 | var errorType:String = null;
145 | if (event is IOErrorEvent) {
146 | errorType = "IOError";
147 | } else if (event is SecurityErrorEvent) {
148 | errorType = "SecurityError";
149 | }
150 |
151 | LoggerJS.log("fileReference.upload onError: " +event.toString());
152 |
153 | trace ("onError", event);
154 | complete(false, null, new ErrorVO(event.toString(), errorType, status) );
155 | }
156 |
157 | private function onProgress(event:ProgressEvent):void
158 | {
159 | // because of bug (loaded:123123, total:0), use _totalSize as total
160 | dispatchEvent(new ProgressEvent( ProgressEvent.PROGRESS, false, false, event.bytesLoaded, _totalSize) );
161 | }
162 |
163 | }
164 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/graphicloader/IGraphicLoader.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.graphicloader
2 | {
3 | import flash.net.URLRequest;
4 |
5 | /**
6 | */
7 | public interface IGraphicLoader
8 | {
9 | function loadGraphic( request:URLRequest ):void;
10 |
11 | function cancel():void;
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/graphicloader/SimpleGraphicLoader.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.graphicloader
2 | {
3 | import flash.display.Loader;
4 | import flash.events.Event;
5 | import flash.events.EventDispatcher;
6 | import flash.events.IEventDispatcher;
7 | import flash.events.IOErrorEvent;
8 | import flash.events.ProgressEvent;
9 | import flash.events.SecurityErrorEvent;
10 | import flash.events.TimerEvent;
11 | import flash.net.URLRequest;
12 | import flash.utils.Timer;
13 |
14 | import ru.mail.commands.graphicloader.events.GraphicLoaderCompleteEvent;
15 | import ru.mail.commands.textloader.events.LoaderProgressEvent;
16 | import ru.mail.data.vo.ErrorVO;
17 | import ru.mail.utils.LoggerJS;
18 |
19 |
20 | public class SimpleGraphicLoader extends EventDispatcher implements IGraphicLoader
21 | {
22 | private var _timeoutTimer:Timer = new Timer( _TIMEOUT, 1 ); // to keep off endless waiting
23 | private var _loader:Loader = new Loader();
24 |
25 | private var _TIMEOUT:uint = 60 * 1000; // timeout in millisecond: minutes * secs per min * millisecs per sec
26 | private const _SUCCESS:Boolean = true;
27 |
28 | public function SimpleGraphicLoader( timeOutInSeconds:uint = 60 )
29 | {
30 | _addURLLoaderListeners( _loader.contentLoaderInfo ) ;
31 |
32 | _TIMEOUT = timeOutInSeconds * 1000;
33 | _timeoutTimer = new Timer( _TIMEOUT, 1 ) ;
34 | _timeoutTimer.addEventListener( TimerEvent.TIMER
35 | , function( e:TimerEvent ):void{ _complete( _getContent() != null, new ErrorVO('SimpleGraphicLoader loadGraphic timeout') ); } ) ;
36 | }
37 |
38 | public function cancel():void
39 | {
40 | try
41 | {
42 | _loader.close();
43 | }catch ( e:Error ) { }
44 | }
45 |
46 | /**
47 | * method load data by request
48 | */
49 | public function loadGraphic( request:URLRequest ):void
50 | {
51 | //trace( "SimpleGraphicLoader.loadGraphic() ", request.url ) ;
52 | LoggerJS.log('SimpleGraphicLoader loadGraphic '+request.url);
53 | _timeoutTimer.start() ;
54 | try
55 | {
56 | import flash.system.LoaderContext ;
57 | _loader.load( request, new LoaderContext( true ) ) ;
58 | }
59 | catch( e:Error )
60 | {
61 | LoggerJS.log('SimpleGraphicLoader Error '+e.toString());
62 | _complete( !_SUCCESS, new ErrorVO( e.toString() ) );
63 | }
64 | }
65 |
66 | private function _complete( isSuccess:Boolean, error:ErrorVO = null ):void
67 | { //trace( "SimpleGraphicLoader._complete() ", isSuccess ) ;
68 | LoggerJS.log('SimpleGraphicLoader _complete, isSuccess = '+isSuccess+', error = '+(error?error.error:""));
69 | _timeoutTimer.stop() ;
70 |
71 | dispatchEvent( new GraphicLoaderCompleteEvent( isSuccess, _getContent(), error ) ) ;
72 | }
73 |
74 | private function _getContent():*
75 | {
76 | var content:* = null
77 | try
78 | {
79 | content = _loader.content;
80 | }
81 | catch ( e:Error ) {
82 | LoggerJS.log("SimpleGraphicLoader._getContent() Error "+ e.message);
83 | }
84 |
85 | return content;
86 | }
87 |
88 |
89 | private function _addURLLoaderListeners( dispatcher:IEventDispatcher ):void
90 | {
91 | dispatcher.addEventListener( Event.INIT, function( e:Event ):void{ _complete( _SUCCESS ); } );
92 | dispatcher.addEventListener( SecurityErrorEvent.SECURITY_ERROR, function( e:Event ):void{ _complete( !_SUCCESS, new ErrorVO(e.toString()) ); } );
93 | dispatcher.addEventListener( IOErrorEvent.IO_ERROR, function( e:Event ):void{ _complete( !_SUCCESS, new ErrorVO(e.toString()) ); } );
94 | dispatcher.addEventListener( ProgressEvent.PROGRESS
95 | , function( e:ProgressEvent ):void {
96 | dispatchEvent( new LoaderProgressEvent( e.bytesLoaded, e.bytesTotal ) )
97 | } ) ;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/graphicloader/events/GraphicLoaderCompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.graphicloader.events
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.data.vo.ErrorVO;
6 |
7 | ;
8 |
9 | public class GraphicLoaderCompleteEvent extends Event
10 | {
11 | public static const TYPE:String = "GraphicLoaderCompleteEvent" ;
12 |
13 | public function GraphicLoaderCompleteEvent( isSuccess:Boolean, content:*, error:ErrorVO = null )
14 | {
15 | super( TYPE ) ;
16 | _isSuccess = isSuccess ;
17 | _content = content ;
18 | _error = error;
19 | }
20 |
21 | public function get isSuccess():Boolean
22 | {
23 | return _isSuccess ;
24 | }
25 |
26 | public function get content():*
27 | {
28 | return _content ;
29 | }
30 |
31 | public function get error():ErrorVO
32 | {
33 | return _error;
34 | }
35 |
36 | private var _isSuccess:Boolean ;
37 | private var _content:* ;
38 | private var _error:ErrorVO;
39 | }
40 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/textloader/ITextLoader.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.textloader
2 | {
3 | import flash.net.URLRequest;
4 |
5 | /**
6 | * Simple util loader
7 | */
8 | public interface ITextLoader
9 | {
10 | function loadText( request:URLRequest ):void;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/textloader/SimpleTextLoader.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.textloader
2 | {
3 | import flash.events.Event;
4 | import flash.events.EventDispatcher;
5 | import flash.events.IEventDispatcher;
6 | import flash.events.IOErrorEvent;
7 | import flash.events.ProgressEvent;
8 | import flash.events.SecurityErrorEvent;
9 | import flash.events.TimerEvent;
10 | import flash.net.URLLoader;
11 | import flash.net.URLRequest;
12 | import flash.utils.Timer;
13 |
14 | import ru.mail.commands.textloader.events.*;
15 | import ru.mail.data.vo.ErrorVO;
16 |
17 | public class SimpleTextLoader extends EventDispatcher implements ITextLoader
18 | {
19 | private var _timeoutTimer:Timer = new Timer( _TIMEOUT, 1 ) ; // to keep off endless waiting
20 | private var _loader:URLLoader= new URLLoader() ;
21 |
22 | private var _TIMEOUT:uint = 1 * 60 * 1000 ; // timeout in millisecond: minutes * secs per min * millisecs per sec
23 | private const _SUCCESS:Boolean = true ;
24 |
25 | public function SimpleTextLoader( timeoutDelayInSecs:uint = 60 )
26 | {
27 | _addURLLoaderListeners( _loader ) ;
28 |
29 | _timeoutTimer.delay = timeoutDelayInSecs * 1000;
30 | _timeoutTimer.addEventListener( TimerEvent.TIMER
31 | , function( e:TimerEvent ):void{ _complete( !_SUCCESS ); } ) ;
32 | }
33 |
34 | /**
35 | * method load data by request
36 | */
37 | public function loadText( request:URLRequest ):void
38 | {
39 | _timeoutTimer.start() ;
40 | try
41 | {
42 | _loader.load( request ) ;
43 | }
44 | catch( e:Error )
45 | {
46 | _complete( !_SUCCESS, new ErrorVO( e.toString() ) );
47 | }
48 | }
49 |
50 | private function _complete( isSuccess:Boolean, error:ErrorVO = null ):void
51 | {
52 | _timeoutTimer.stop() ;
53 | dispatchEvent( new TextLoaderCompleteEvent( isSuccess, _loader.data, error ) ) ;
54 | }
55 |
56 | private function _addURLLoaderListeners( dispatcher:IEventDispatcher ):void
57 | {
58 | dispatcher.addEventListener( Event.COMPLETE, function( e:Event ):void{ _complete( _SUCCESS ); } );
59 | dispatcher.addEventListener( SecurityErrorEvent.SECURITY_ERROR, function( e:Event ):void{ _complete( !_SUCCESS, new ErrorVO(e.toString()) ); } );
60 | dispatcher.addEventListener( IOErrorEvent.IO_ERROR, function( e:Event ):void{ _complete( !_SUCCESS, new ErrorVO(e.toString()) ); } );
61 | dispatcher.addEventListener( ProgressEvent.PROGRESS
62 | , function( e:ProgressEvent ):void
63 | { dispatchEvent( new LoaderProgressEvent( e.bytesLoaded, e.bytesTotal ) ) } ) ;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/textloader/events/LoaderProgressEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.textloader.events
2 | {
3 | import flash.events.Event;
4 |
5 | public class LoaderProgressEvent extends Event
6 | {
7 | public static const TYPE:String = "LoaderProgressEvent" ;
8 |
9 | public function LoaderProgressEvent( loaded:uint, total:uint )
10 | {
11 | super( TYPE ) ;
12 | _loaded = loaded ;
13 | _total = total ;
14 | }
15 |
16 | public function get total():uint
17 | {
18 | return _total ;
19 | }
20 |
21 | public function get loaded():uint
22 | {
23 | return _loaded ;
24 | }
25 |
26 | private var _loaded:uint ;
27 | private var _total:uint ;
28 | }
29 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/commands/textloader/events/TextLoaderCompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.commands.textloader.events
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.data.vo.ErrorVO;
6 |
7 | public class TextLoaderCompleteEvent extends Event
8 | {
9 | public static const TYPE:String = "TextLoaderCompleteEvent" ;
10 |
11 | public function TextLoaderCompleteEvent( isSuccess:Boolean, content:String, error:ErrorVO = null )
12 | {
13 | super( TYPE ) ;
14 | _isSuccess = isSuccess ;
15 | _content = content ;
16 | _error = error;
17 | }
18 |
19 | public function get isSuccess():Boolean
20 | {
21 | return _isSuccess ;
22 | }
23 |
24 | public function get content():String
25 | {
26 | return _content ;
27 | }
28 |
29 | public function get error():ErrorVO
30 | {
31 | return _error;
32 | }
33 |
34 | private var _isSuccess:Boolean ;
35 | private var _content:String ;
36 | private var _error:ErrorVO;
37 | }
38 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/communication/JSCallbackPresenter.as:
--------------------------------------------------------------------------------
1 | package ru.mail.communication
2 | {
3 | import flash.external.ExternalInterface;
4 | import flash.system.Security;
5 |
6 | import ru.mail.controller.AppController;
7 | import ru.mail.utils.LoggerJS;
8 |
9 | /**
10 | * Configure js callback functions, redirect all of them to app controller.
11 | * Controller will decide what to do with them
12 | * @author v.demidov
13 | *
14 | */
15 | public class JSCallbackPresenter
16 | {
17 | private var appController:AppController;
18 |
19 | public function JSCallbackPresenter(appController:AppController)
20 | {
21 | this.appController = appController;
22 |
23 | try
24 | {
25 | Security.allowDomain("*");
26 |
27 | ExternalInterface.addCallback("cmd", parseCmd);
28 | }
29 | catch (e:Error) {
30 | LoggerJS.log('add js cmd callback error: '+e.toString() );
31 | trace ("{JSCallbackPresenter} - unable to set callback, error:", e.message);
32 | }
33 | }
34 |
35 | /**
36 | * cmd('commandType', { ... })
37 | *
38 | * @param command - string to determine what to do
39 | * @param data - details - an object or a string, depending on command
40 | * @return
41 | *
42 | */
43 | protected function parseCmd(command:String, data:Object):Object
44 | {
45 | LoggerJS.log('parseCmd, command: '+command);
46 | switch (command)
47 | {
48 | case "accept":
49 | appController.setTypeFilter(data.toString());
50 | break;
51 | case "upload":
52 | /* cmd("upload", {url:, data:URLVariables, headers:Object
53 | ,files: {'filename[original]': {
54 | id
55 | , name
56 | , matrix:{
57 | sx , ...
58 | ,dh
59 | ,deg
60 | , type:'image/png'
61 | , quality: 1 // качество jpeg
62 | ,overlay: [ // массив изображений, которые нужно разместить:
63 | { x: 0, y: 0, opacity: .5, src: '...' }
64 | , { x: 0, y: 0, w: 120, h: 30, opacity: 1, src: '...' }
65 | ]
66 | } }
67 | , 'filename[XL]': { id, name, matrix:null } }
68 | , callback:jsHandler}) */
69 | // headers: { 'Content-Type': 'application/x-mru-upload' , 'Content-Disposition': '...' , ...}
70 | appController.uploadFile(data.url
71 | , data.data, data.headers
72 | , data.files
73 | , data.callback);
74 | break;
75 | case "abort":
76 | // cmd('abort', { id: '...' })
77 | appController.cancelFile(data.id);
78 | break;
79 | case "hitTest":
80 | // cmd("hitTest")
81 | // return whether mouse is over the flash
82 | return appController.hitTest();
83 | break;
84 | case "multiple":
85 | appController.setMultipleSelect(data.toString() == "true");
86 | break;
87 | case "clear":
88 | // remove all files
89 | appController.clear();
90 | break;
91 | case "clearError":
92 | // clear shared object error data
93 | appController.clearError();
94 | break;
95 | case "getFileInfo":
96 | // cmd('getFileInfo', { id: '...', callback: '...' });
97 | appController.getFileInfo(data.id, data.callback)
98 | break;
99 | case "imageTransform":
100 | // cmd('imageTransform', {
101 | // id: '...',
102 | // matrix: {
103 | // sx: Number, // s* — original image region
104 | // sy: Number,
105 | // sw: Number, // if 0, then w - sx
106 | // sh: Number, // if 0, then h - sy
107 | // dw: Number, // if 0, then sw
108 | // dh: Number, // if 0, then sh
109 | // deg: Number
110 | // resize: String, // min, max OR preview
111 | // },
112 | // callback: '...'
113 | //});
114 | appController.imageTransform(data.id, data.matrix, data.callback);
115 | break;
116 | // camera:
117 | case "camera.on":
118 | appController.cameraController.cameraOn(data.callback);
119 | break;
120 | case "camera.off":
121 | appController.cameraController.cameraOff();
122 | break;
123 | case "shot":
124 | return appController.cameraController.shot();
125 | break;
126 | default:
127 | LoggerJS.log("cannot parse command: "+command);
128 | break;
129 | }
130 |
131 | // by default function doesn't have to return anything
132 | // If it have to, return it inside switch case
133 | return false;
134 | }
135 |
136 | }
137 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/controller/CameraController.as:
--------------------------------------------------------------------------------
1 | package ru.mail.controller
2 | {
3 | import flash.display.BitmapData;
4 | import flash.display.Sprite;
5 | import flash.events.Event;
6 | import flash.events.StatusEvent;
7 | import flash.net.URLRequest;
8 |
9 | import ru.mail.commands.graphicloader.SimpleGraphicLoader;
10 | import ru.mail.commands.graphicloader.events.GraphicLoaderCompleteEvent;
11 | import ru.mail.communication.JSCaller;
12 | import ru.mail.data.AttachmentsModel;
13 | import ru.mail.data.vo.PhotoFileVO;
14 | import ru.mail.utils.LoggerJS;
15 |
16 | public class CameraController
17 | {
18 | private var _jsCaller:JSCaller;
19 | private var _model:AttachmentsModel;
20 | private var _view:Sprite;
21 |
22 | private var _cameraSwf:*;
23 |
24 | public function CameraController(view:Sprite)
25 | {
26 | LoggerJS.log('Camera Controller - init');
27 |
28 | _view = view;
29 | _jsCaller = JSCaller.jsCaller;
30 | _model = AttachmentsModel.model;
31 |
32 | loadCamera();
33 | }
34 |
35 | public function cameraOn(callback:String):void
36 | {
37 | LoggerJS.log('camera.on called');
38 | try {
39 | _cameraSwf.addEventListener('Camera.On', function(event:Event):void {
40 | _jsCaller.callJS(callback, {error:null}, null, true);
41 | });
42 | _cameraSwf.toggleCamera(true);
43 | } catch (e:Error) {
44 | _jsCaller.callJS(callback, {error:e.toString()}, null, true);
45 | }
46 | }
47 |
48 | public function cameraOff():void
49 | {
50 | LoggerJS.log('camera.off called');
51 | _cameraSwf.toggleCamera(false);
52 | }
53 |
54 | public function shot():Object
55 | {
56 | LoggerJS.log('smile please!');
57 | var bm:BitmapData = _cameraSwf.shot();
58 | var result:Object = {};
59 | if (bm == null) {
60 | LoggerJS.log('shot image error');
61 | result.error = 'create shot fail';
62 | }
63 | else {
64 | LoggerJS.log('shot image w:'+bm.width+', h:'+bm.height);
65 | var fileVO:PhotoFileVO = _model.filesBuilder.createPhotoFileVO(bm);
66 | result.id = fileVO.fileID;
67 | result.type = fileVO.fileType;
68 | result.size = fileVO.fileSize;
69 | result.width = fileVO.imageData.width;
70 | result.height = fileVO.imageData.height;
71 | }
72 |
73 | return result;
74 | }
75 |
76 | // ============= init ================
77 |
78 | private function loadCamera():void
79 | {
80 | var loader:SimpleGraphicLoader = new SimpleGraphicLoader(10);
81 | loader.addEventListener(GraphicLoaderCompleteEvent.TYPE, function(evt:GraphicLoaderCompleteEvent):void {
82 | if (evt.isSuccess) {
83 | LoggerJS.log('load camera swf complete');
84 | try {
85 | _cameraSwf = evt.content;
86 | _cameraSwf.addEventListener(StatusEvent.STATUS, onCameraStatus);
87 | _view.addChild(_cameraSwf);
88 | } catch (e:Error) {
89 | LoggerJS.log('attach camera fail: '+e.toString());
90 | initComplete(false, evt.error.getError());
91 | }
92 |
93 | }
94 | else {
95 | LoggerJS.log('load camera swf complete, _isSuccess='+evt.isSuccess+', error='+evt.error.getError());
96 | // What a pity
97 | initComplete(false, evt.error.getError());
98 | }
99 | });
100 |
101 | loader.loadGraphic(new URLRequest(_model.useCamera || 'FileAPI.flash.camera.swf'));
102 | }
103 |
104 | private function onCameraStatus(event:StatusEvent):void
105 | {
106 | LoggerJS.log('onCameraStatus '+event.code);
107 | if (event.code == 'Camera.Unmuted') {
108 | // report ok
109 | initComplete(true);
110 | } else if (event.code == 'Camera.Muted') {
111 | // report user denied
112 | initComplete(false, 'user denied access to camera');
113 | } else {
114 | // report this strange thing
115 | initComplete(false, 'unknown camera status: '+event.code);
116 | }
117 | }
118 |
119 | private function initComplete(success:Boolean, error:String = null):void
120 | {
121 | _jsCaller.notifyCameraStatus(success? null : error);
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/AbstractImageFactory.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data
2 | {
3 | import ru.mail.data.vo.IFileVO;
4 |
5 | /**
6 | * Produce factories for file
7 | *
8 | * @author v.demidov
9 | *
10 | */
11 | public class AbstractImageFactory
12 | {
13 | private var file:IFileVO;
14 |
15 | public function AbstractImageFactory(target:IFileVO)
16 | {
17 | if (!target) {
18 | throw new Error("{ImageFactory} - init: target is null");
19 | }
20 |
21 | file = target;
22 | }
23 |
24 | public function getImageFactory():IImageFactory
25 | {
26 | return new ImageFactory(file);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/AttachmentsModel.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data
2 | {
3 | import flash.events.EventDispatcher;
4 | import flash.net.FileFilter;
5 | import flash.net.SharedObject;
6 |
7 | import ru.mail.data.builder.FilesDataBuilder;
8 |
9 | public class AttachmentsModel extends EventDispatcher
10 | {
11 |
12 | //=================================================
13 | // singleton
14 | //=================================================
15 |
16 | protected static var _instance:AttachmentsModel = null;
17 |
18 | /**
19 | * reference to Singleton
20 | * @return
21 | *
22 | */
23 | public static function get model():AttachmentsModel
24 | {
25 | if (!_instance)
26 | {
27 | _instance = new AttachmentsModel();
28 | }
29 |
30 | return _instance
31 | }
32 |
33 | //=================================================
34 | // consts
35 | //=================================================
36 |
37 | protected const DEFAULT_FILTER:FileFilter = new FileFilter( "All types", "*.*");
38 |
39 | //=================================================
40 | // vars
41 | //=================================================
42 |
43 | // TODO: remove unused vars
44 |
45 | public var fileFilters:Array = [DEFAULT_FILTER];
46 |
47 | protected var _filesBuilder:FilesDataBuilder = new FilesDataBuilder()
48 |
49 | public function get filesBuilder():FilesDataBuilder
50 | {
51 | return _filesBuilder;
52 | }
53 |
54 | public var useCamera:String = null;
55 |
56 | /**
57 | * if true, user can select multiple files
58 | */
59 | public var useMultipleSelect:Boolean = true;
60 |
61 | public var storeKey:String = "";
62 | protected var _hasError:Boolean = false;
63 | /**
64 | * read from sharedObject, whether application experienced error #2038 - problems with authorisation
65 | * while uploading through proxy.
66 | * @return
67 | *
68 | */
69 | public function get hasError():Boolean
70 | {
71 | return _hasError;
72 | }
73 |
74 | public function set hasError(value:Boolean):void
75 | {
76 | _hasError = value;
77 | writeError(value);
78 | }
79 |
80 | public function clearError():void {
81 | _clearError();
82 | _hasError = false;
83 | }
84 |
85 | private var _timeout:int = 0;
86 | /**
87 | * timeout for removing file
88 | * When calling cmd.abort(), do not delete file immediately, but wait for some time.
89 | * If file is called for upload or something, cancel timeout and do not remove file
90 | * @return
91 | *
92 | */
93 | public function get timeout():int
94 | {
95 | return _timeout;
96 | }
97 |
98 | public function set timeout(value:int):void
99 | {
100 | _timeout = int(value) || 0;
101 | }
102 |
103 |
104 | /**
105 | * @private Constructor
106 | */
107 | public function AttachmentsModel()
108 | {
109 | super();
110 |
111 | // lock
112 | if (_instance)
113 | {
114 | throw new Error("AttachmentsModel is singleton class, use get model method instead");
115 | }
116 |
117 | _hasError = readError();
118 | }
119 |
120 | public function updateHasError():void {
121 | _hasError = readError();
122 | }
123 |
124 | /**
125 | * whether error #2038 occured or not
126 | * @return
127 | */
128 | private function readError():Boolean {
129 | try
130 | {
131 | var so:SharedObject = SharedObject.getLocal("flashfileapi", '/');
132 | var error:String = so.data[storeKey+"savedError"];
133 |
134 | trace ("attachmetsModel read sharedobject, error = " + error);
135 |
136 | return (error == "1");
137 | }
138 | catch (e:Error) {
139 | trace ("read shared object error: "+e);
140 | }
141 |
142 | return false;
143 | }
144 |
145 | private function writeError(isError:Boolean):void {
146 | try
147 | {
148 | var so:SharedObject = SharedObject.getLocal("flashfileapi", '/');
149 | so.data[storeKey+"savedError"] = isError? "1" : "0";
150 | so.flush();
151 | }
152 | catch (e:Error) {
153 | trace ("write shared object error: "+e);
154 | }
155 | }
156 |
157 | /**
158 | * clear shared object data
159 | */
160 | private function _clearError():void {
161 | var so:SharedObject = SharedObject.getLocal("flashfileapi", '/');
162 | so.clear();
163 | }
164 |
165 |
166 | }
167 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/IImageFactory.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data
2 | {
3 | import ru.mail.data.vo.ImageTransformVO;
4 |
5 | /**
6 | * produces transformed images from target's source
7 | * @author v.demidov
8 | *
9 | */
10 | public interface IImageFactory
11 | {
12 | /**
13 | * Create transformed image using imageTransform params.
14 | * if imageTransform is null, return original image.
15 | * If original image has not been loaded, first load it.
16 | * The result image is returned async via completeEvent
17 | * @param imageTransform
18 | *
19 | */
20 | function createImage(imageTransform:ImageTransformVO):void;
21 | /**
22 | * try to read file's exif. return object with "Orientation" value
23 | * @return
24 | *
25 | */
26 | function readExif():Object;
27 | }
28 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/builder/AbstractDataBuilder.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.builder
2 | {
3 | import flash.events.EventDispatcher;
4 | import flash.utils.clearTimeout;
5 |
6 | import ru.mail.data.vo.BaseFileVO;
7 |
8 | public class AbstractDataBuilder extends EventDispatcher
9 | {
10 | protected var _type:String = "AbstractDataBuilder"
11 |
12 | public function get type():String
13 | {
14 | return _type;
15 | }
16 |
17 | protected var _items:Vector. = new Vector.();
18 | /**
19 | * Return just a copy of items vector
20 | * @return
21 | *
22 | */
23 | public function get items():Vector.
24 | {
25 | return _items.slice();
26 | }
27 |
28 | public function AbstractDataBuilder(type:String)
29 | {
30 | super();
31 |
32 | _type = type;
33 | }
34 |
35 | /**
36 | * Check id and add file to the collection.
37 | * @param file
38 | * @return file that have been added
39 | *
40 | */
41 | public function addFile(file:BaseFileVO):BaseFileVO
42 | {
43 | _items.push(file);
44 | validateID(file)
45 |
46 | return file;
47 | }
48 |
49 | /**
50 | * Search among all items and return file that matches given fileID
51 | * @param fileID
52 | * @return
53 | *
54 | */
55 | public function getFileByID(fileID:String):BaseFileVO
56 | {
57 | var result:BaseFileVO = null;
58 |
59 | for (var i:uint = 0; i < _items.length; i++)
60 | {
61 | if (_items[i].fileID == fileID) {
62 | result = _items[i];
63 | if (result.timeout) {
64 | clearTimeout(result.timeout); // cancel file remove
65 | }
66 | break;
67 | }
68 | }
69 |
70 | return result;
71 | }
72 |
73 | /**
74 | * Return file at given index.
75 | * @param index
76 | * @return
77 | *
78 | */
79 | public function getFileAt(index:int):BaseFileVO
80 | {
81 | return _items[index];
82 | }
83 |
84 | /**
85 | * Remove given file
86 | * @param file
87 | * @return removed file
88 | *
89 | */
90 | public function removeFile(file:BaseFileVO):BaseFileVO
91 | {
92 | var index:int = _items.indexOf(file);
93 |
94 | return removeFileAt(index);
95 | }
96 |
97 | /**
98 | *
99 | * @param fileID - id of the file to be removed
100 | * @return removed file
101 | *
102 | */
103 | public function removeFileByID(fileID:String):BaseFileVO
104 | {
105 | var file:BaseFileVO = getFileByID(fileID);
106 |
107 | if (file != null)
108 | {
109 | removeFile(file);
110 | }
111 |
112 | return file;
113 | }
114 |
115 | /**
116 | * If index is valid, remove file from this index
117 | * @param index
118 | * @return removed file
119 | *
120 | */
121 | public function removeFileAt(index:int):BaseFileVO
122 | {
123 | var result:BaseFileVO = null;
124 |
125 | if (index > -1 && index < _items.length)
126 | {
127 | result = _items.splice(index, 1)[0];
128 | result.loadCommand = null;
129 | result.uploadCommand = null;
130 | if(result.timeout) {
131 | clearTimeout(result.timeout);
132 | }
133 | }
134 |
135 | return result;
136 | }
137 |
138 | /**
139 | *
140 | *
141 | */
142 | public function removeAllFiles():void
143 | {
144 | for (var i:uint = 0; i < _items.length; i++) { // for garbage collector
145 | if (_items[i].timeout) {
146 | clearTimeout(_items[i].timeout);
147 | }
148 | }
149 | _items.length = 0;
150 | }
151 |
152 | /**
153 | * Return files array length
154 | * @return
155 | *
156 | */
157 | public function getFilesCount():int
158 | {
159 | return _items.length;
160 | }
161 |
162 | /**
163 | * returns random id string
164 | * @return
165 | *
166 | */
167 | public function generateID():String
168 | {
169 | return new String().concat( new Date().valueOf(), Math.floor( Math.random() * 1000 ) );
170 | }
171 |
172 | /**
173 | * Validate that file's id is not null and differs from other file ids.
174 | *
175 | * If id is not valid, generate new one and assign it to the file.
176 | * @param file
177 | * @return true if id had been changed
178 | *
179 | */
180 | protected function validateID(file:BaseFileVO):Boolean
181 | {
182 | var isIdChanged:Boolean = false;
183 |
184 | // check null
185 | if (file.fileID == null || file.fileID == ""){
186 | isIdChanged = true;
187 | file.fileID = generateID();
188 | }
189 |
190 | // compare with other files
191 | for (var i:uint = 0; i < _items.length; i++)
192 | {
193 | if (_items[i] == file)
194 | continue;
195 |
196 | if (_items[i].fileID == file.fileID) {
197 | // generate new id
198 | isIdChanged = true;
199 | file.fileID = generateID();
200 | }
201 | }
202 |
203 | return isIdChanged;
204 | }
205 | }
206 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/builder/FilesDataBuilder.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.builder
2 | {
3 | import flash.display.BitmapData;
4 | import flash.net.FileReference;
5 | import flash.utils.ByteArray;
6 |
7 | import ru.mail.data.AbstractImageFactory;
8 | import ru.mail.data.vo.FakeFileVO;
9 | import ru.mail.data.vo.FileVO;
10 | import ru.mail.data.vo.PhotoFileVO;
11 |
12 | /**
13 | * Create and store files
14 | *
15 | * @author v.demidov
16 | *
17 | */
18 | public class FilesDataBuilder extends AbstractDataBuilder
19 | {
20 | public static const TYPE:String = "FilesDataBuilder";
21 |
22 | public function FilesDataBuilder()
23 | {
24 | super(TYPE);
25 | }
26 |
27 | /**
28 | * Create fake file with empty data
29 | * @param fileID
30 | * @return
31 | *
32 | */
33 | public function createFakeFileVO(fileID:String = ''):FakeFileVO
34 | {
35 | // create
36 | var fileVO:FakeFileVO = new FakeFileVO();
37 | // set props
38 | fileVO.fileData = new ByteArray();
39 | fileVO.imageData = new BitmapData(1,1);
40 | fileVO.fileID = fileID;
41 |
42 | fileVO.abstractImageFactory = new AbstractImageFactory(fileVO);
43 |
44 | return fileVO;
45 | }
46 |
47 | public function createFileVO(fileReference:FileReference, fileID:String = '', addToCollection:Boolean = true):FileVO
48 | {
49 | if (fileReference == null) {
50 | throw new Error("FilesDataBuilder - createFileVO: fileReference is null");
51 | }
52 | // create
53 | var fileVO:FileVO = new FileVO();
54 | // set props
55 | fileVO.fileRef = fileReference;
56 | fileVO.fileID = fileID;
57 | //fileVO.imageFactory = new ImageFactory(fileVO, true);
58 | fileVO.abstractImageFactory = new AbstractImageFactory(fileVO);
59 | // add
60 | if(addToCollection)
61 | {
62 | addFile(fileVO);
63 | }
64 |
65 | return fileVO;
66 | }
67 |
68 | /*public function createRestoredFileVO(url:String, fileID:String = '', addToCollection:Boolean = true):RestoredFileVO
69 | {
70 | if (!url || url == "") {
71 | throw new Error("FilesDataBuilder - createRestoredFileVO: empty url");
72 | }
73 | // create
74 | var fileVO:RestoredFileVO = new RestoredFileVO();
75 | // set props
76 | fileVO.url = url;
77 | fileVO.fileID = fileID;
78 | // add
79 | if(addToCollection)
80 | {
81 | addFile(fileVO);
82 | }
83 |
84 | return fileVO;
85 | }*/
86 |
87 | public function createPhotoFileVO(image:BitmapData, fileID:String = '', addToCollection:Boolean = true):PhotoFileVO
88 | {
89 | if (image == null) {
90 | throw new Error("FilesDataBuilder - createPhotoFileVO: image BitmapData is null");
91 | }
92 |
93 | // create
94 | var fileVO:PhotoFileVO = new PhotoFileVO();
95 | // set props
96 | fileVO.imageData = image;
97 | fileVO.fileID = fileID;
98 | //fileVO.imageFactory = new ImageFactory(fileVO, true);
99 | fileVO.abstractImageFactory = new AbstractImageFactory(fileVO);
100 | // add
101 | if(addToCollection)
102 | {
103 | addFile(fileVO);
104 | }
105 |
106 | return fileVO;
107 | }
108 |
109 | }
110 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/BaseFileVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import ru.mail.data.AbstractImageFactory;
4 | import ru.mail.data.IImageFactory;
5 |
6 | /**
7 | * This class contains almost all information about the file. The difference is only the source of data -
8 | * it can be fileReference or loaded from url or image from web camera.
9 | * load from url isn't implemented now.
10 | *
11 | * @author v.demidov
12 | */
13 | public class BaseFileVO
14 | {
15 | protected var _fileID:String = "";
16 |
17 | public function get fileID():String
18 | {
19 | return _fileID;
20 | }
21 |
22 | public function set fileID(value:String):void
23 | {
24 | _fileID = value;
25 | }
26 |
27 | // TODO: refactor
28 | private var _status:String = "selected";
29 |
30 | public function get status():String {
31 | return _status;
32 | }
33 | public function set status(value:String):void {
34 | _status = value;
35 | }
36 |
37 | public var loadCommand:Object;
38 | public var uploadCommand:Object;
39 |
40 | public var timeout:uint = 0; // timeout that can remove file
41 |
42 | private var _abstractImageFactory:AbstractImageFactory;
43 |
44 | public function set abstractImageFactory(value:AbstractImageFactory):void
45 | {
46 | _abstractImageFactory = value;
47 | }
48 | /**
49 | * factory for producing images
50 | *
51 | * @return
52 | *
53 | */
54 | public function get imageFactory():IImageFactory
55 | {
56 | return _abstractImageFactory.getImageFactory();
57 | }
58 |
59 | public function BaseFileVO()
60 | {
61 |
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/ErrorVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | /**
4 | *
5 | * Try to get error type, message and ID from given string.
6 | *
7 | * Input string can be Error.toString() or ErrorEvent.toString() or anything else
8 | *
9 | * @author v.demidov
10 | *
11 | */
12 | public class ErrorVO
13 | {
14 | public var error:String = "";
15 | public var errorType:String = "error";
16 | public var errorID:String = "";
17 | public var errorMessage:String = "";
18 | public var httpStatus:String = '';
19 |
20 | public function ErrorVO(error:String, errorType:String = null, httpStatus:String = null)
21 | {
22 | super();
23 | parseError(error);
24 | if (errorType) {
25 | this.errorType = errorType;
26 | }
27 | if (httpStatus) {
28 | this.httpStatus = httpStatus;
29 | }
30 | }
31 |
32 | public function parseError(str:String):void {
33 | if (!str) {
34 | return;
35 | }
36 | error = str;
37 |
38 | var idIndex:int = str.indexOf("#");
39 | var msgIndex:int = str.indexOf(":", idIndex);
40 | var msgEndIndex:int = str.lastIndexOf('"');
41 | // #1234:
42 | errorID = str.substring(idIndex+1, msgIndex);
43 | //: Error details
44 | errorMessage = str.substring(msgIndex+1, msgEndIndex == -1? str.length : msgEndIndex);
45 | }
46 |
47 | public function getError():String {
48 | return errorType + " " + errorID + ": " + errorMessage;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/FakeFileVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import flash.display.BitmapData;
4 | import flash.utils.ByteArray;
5 |
6 | /**
7 | * Restored file (from url or any else bytearray). If it is image, it cannot be scaled or rotated.
8 | * @author v.demidov
9 | *
10 | */
11 | public class FakeFileVO extends BaseFileVO implements IFileVO
12 | {
13 | private var _fileData:ByteArray;
14 |
15 | public function get fileData():ByteArray
16 | {
17 | return _fileData;
18 | }
19 |
20 | public function set fileData(value:ByteArray):void
21 | {
22 | _fileData = value;
23 | }
24 |
25 | public function get fileSize():Number {
26 | return _fileData? _fileData.length : 0;
27 | }
28 |
29 | public function get fileName():String {
30 | return fileID;
31 | }
32 |
33 | public function get fileNameModified():String
34 | {
35 | return fileID;
36 | }
37 |
38 | public function get fileType():String {
39 | return "";
40 | }
41 |
42 | private var _imageData:BitmapData;
43 | /**
44 | * original image bitmapData;
45 | * @return
46 | *
47 | */
48 | public function get imageData():BitmapData
49 | {
50 | return _imageData;
51 | }
52 |
53 | public function set imageData(bd:BitmapData):void
54 | {
55 | _imageData = bd;
56 | }
57 |
58 | public function FakeFileVO()
59 | {
60 | super();
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/FileStatesEnum.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | public class FileStatesEnum
4 | {
5 | public static const SELECTED:String = "selected";
6 | public static const LOADING:String = "loading";
7 | public static const LOADED:String = "loaded";
8 | public static const RESIZING:String = "resizing";
9 | public static const UPLOADING:String = "uploading";
10 | public static const UPLOADED:String = "uploaded";
11 | public static const READY:String = "ready";
12 |
13 | public function FileStatesEnum()
14 | {
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/FileVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import flash.display.BitmapData;
4 | import flash.net.FileReference;
5 | import flash.utils.ByteArray;
6 |
7 | /**
8 | * Value object contains file reference, unique ID etc
9 | *
10 | * @author v.demidov
11 | *
12 | */
13 | public class FileVO extends BaseFileVO implements IFileVO
14 | {
15 | public var fileRef:FileReference;
16 |
17 | public function get fileData():ByteArray
18 | {
19 | return fileRef.data;
20 | }
21 |
22 | public function get fileSize():Number {
23 | var fileSize:Number = 0;
24 | try
25 | {
26 | fileSize = fileRef.size;
27 | }
28 | catch ( e:Error ) { }
29 |
30 | return fileSize;
31 | }
32 |
33 | public function get fileName():String
34 | {
35 | return fileRef.name;
36 | }
37 |
38 | public function get fileType():String
39 | {
40 | var fileNameParts:Array = fileName.split( "." );
41 | if ( fileNameParts.length < 2 )
42 | return "";
43 |
44 | return ( fileNameParts[ fileNameParts.length - 1 ] ).toLowerCase();
45 | }
46 |
47 | private var _imageData:BitmapData;
48 | /**
49 | * original image bitmapData;
50 | * @return
51 | *
52 | */
53 | public function get imageData():BitmapData
54 | {
55 | return _imageData;
56 | }
57 |
58 | public function set imageData(bd:BitmapData):void
59 | {
60 | _imageData = bd;
61 | }
62 |
63 | /**
64 | * for modified images we change filetype, because we encode bmp and gif with png encoder.
65 | * for jpg - do not modify, because we use jpg encoder for them
66 | * @return
67 | */
68 | public function get fileNameModified():String
69 | {
70 | if (fileType == 'jpg' || fileType == 'jpeg') {
71 | return fileName;
72 | }
73 | else {
74 | var fileNameParts:Array = fileName.split( "." );
75 | if ( fileNameParts.length < 2 )
76 | return fileName;
77 |
78 | return fileNameParts[0] + '.png';
79 | }
80 | }
81 |
82 | public function FileVO()
83 | {
84 | super();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/IFileVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import flash.display.BitmapData;
4 | import flash.utils.ByteArray;
5 |
6 | import ru.mail.data.IImageFactory;
7 |
8 | public interface IFileVO
9 | {
10 | function get fileID():String;
11 | function get fileData():ByteArray;
12 | function get fileSize():Number;
13 | function get fileName():String;
14 | function get fileNameModified():String;
15 | /**
16 | * it gets fileRef name, cuts the dot and returns the lowerCase file extenation.
17 | * it returns empty string if type is null
18 | */
19 | function get fileType():String;
20 |
21 | function get status():String;
22 | function set status(value:String):void;
23 |
24 | function get imageData():BitmapData;
25 | function set imageData(bd:BitmapData):void;
26 |
27 | function get imageFactory():IImageFactory;
28 | }
29 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/ImageTransformVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | /**
4 | * Value object with transformation matrix
5 | *
6 | * @author v.demidov
7 | *
8 | */
9 | public class ImageTransformVO
10 | {
11 | public static const TYPE_PNG:String = 'image/png';
12 | public static const TYPE_JPEG:String = 'image/jpeg';
13 |
14 | public var sx:Number = 0;
15 | public var sy:Number = 0;
16 | public var sw:Number = 0;
17 | public var sh:Number = 0;
18 | public var dw:Number = 0;
19 | public var dh:Number = 0;
20 | public var deg:Number = 0;
21 | public var type:String = 'image/png'; // encoded image type. If type value is unknown, png is used
22 | public var quality:Number = 1; // encode quality (jpeg only)
23 | public var overlay:Array = []; // array of OverlayVO instances
24 | public var multiPassResize: Boolean = true;
25 |
26 | public function ImageTransformVO(sx:Number = 0, sy:Number = 0, sw:Number = 0, sh:Number = 0, dw:Number = 0, dh:Number = 0, deg:Number = 0
27 | , type:String = null, quality:Number = 1, overlay:Array = null, multiPassResize:Boolean = true)
28 | {
29 | super();
30 |
31 | if ( !isNaN(sx) )
32 | this.sx = sx;
33 | if ( !isNaN(sy) )
34 | this.sy = sy;
35 | if ( !isNaN(sw) )
36 | this.sw = sw;
37 | if ( !isNaN(sh) )
38 | this.sh = sh;
39 | if ( !isNaN(dw) )
40 | this.dw = dw;
41 | if ( !isNaN(dh) )
42 | this.dh = dh;
43 | if ( !isNaN(deg) )
44 | this.deg = deg;
45 | if ( type )
46 | this.type = type;
47 | if ( !isNaN(quality) )
48 | this.quality = quality;
49 |
50 | this.multiPassResize = multiPassResize;
51 |
52 | if ( overlay )
53 | setOverlay( overlay );
54 | }
55 |
56 | /**
57 | * @private fill overlay array with value objects
58 | * @param overlay
59 | *
60 | */
61 | private function setOverlay(overlay:Array):void
62 | {
63 | var item:Object;
64 | var overlayVO:OverlayVO;
65 | for (var i:uint = 0; i < overlay.length; i++) {
66 | item = overlay[i];
67 | if ( item && item.src ) {
68 | overlayVO = new OverlayVO(item);
69 | this.overlay.push(overlayVO);
70 | }
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/OverlayVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import flash.display.BitmapData;
4 |
5 | /**
6 | * Value object with params for overlay
7 | *
8 | * @author demidov
9 | *
10 | */
11 | public class OverlayVO
12 | {
13 | public var x:Number;
14 | public var y:Number;
15 | public var w:Number;
16 | public var h:Number;
17 | /**
18 | * 0 1 2
19 | * 3 4 5
20 | * 6 7 8
21 | */
22 | public var rel:uint;
23 | public var opacity:Number;
24 | public var src:String;
25 |
26 | private var _imageData:BitmapData;
27 | public function get imageData():BitmapData
28 | {
29 | return _imageData;
30 | }
31 | public function set imageData(value:BitmapData):void
32 | {
33 | _imageData = value;
34 | // update default w, h
35 | if (w == 0) {
36 | w = _imageData.width;
37 | }
38 | if (h == 0) {
39 | h = _imageData.height;
40 | }
41 | }
42 |
43 |
44 | public function OverlayVO(item:Object)
45 | {
46 | this.x = item.x|0;
47 | this.y = item.y|0;
48 | this.w = item.w|0;
49 | this.h = item.h|0;
50 | this.rel = uint(item.rel)|0;
51 | this.opacity = item.opacity||1;
52 | this.src = item.src;
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/data/vo/PhotoFileVO.as:
--------------------------------------------------------------------------------
1 | package ru.mail.data.vo
2 | {
3 | import flash.display.BitmapData;
4 | import flash.geom.Rectangle;
5 | import flash.utils.ByteArray;
6 |
7 | public class PhotoFileVO extends BaseFileVO implements IFileVO
8 | {
9 | private var _image:BitmapData = null;
10 | private var _filedata:ByteArray = null;
11 |
12 | public function PhotoFileVO()
13 | {
14 | super();
15 | }
16 |
17 | public function get fileData():ByteArray
18 | {
19 | return _filedata;
20 | }
21 |
22 | public function get fileSize():Number
23 | {
24 | return _filedata? _filedata.length : 0;
25 | }
26 |
27 | public function get fileName():String
28 | {
29 | return _fileID+'.png';
30 | }
31 |
32 | /**
33 | * use fileName, because modified filename makes sense only for fileRef files.
34 | * @return
35 | *
36 | */
37 | public function get fileNameModified():String
38 | {
39 | return fileName;
40 | }
41 |
42 | public function get fileType():String
43 | {
44 | return 'png';
45 | }
46 |
47 | public function get imageData():BitmapData
48 | {
49 | return _image;
50 | }
51 |
52 | public function set imageData(bd:BitmapData):void
53 | {
54 | _image = bd;
55 | if (_image) {
56 | // get bytearray now to avoid this operation every time we need it.
57 | _filedata = _image.getPixels(new Rectangle(0, 0, _image.width, _image.height));
58 | }
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/AbstractModelJSEngine.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain
2 | {
3 | import ru.mail.communication.JSCaller;
4 | import ru.mail.data.AttachmentsModel;
5 |
6 | public class AbstractModelJSEngine extends AbstractEngine implements IJsCallerHolder, IModelHolder
7 | {
8 | protected var _jsCaller:JSCaller;
9 | /**
10 | * reference to JS communicator
11 | * @return
12 | *
13 | */
14 | public function get jsCaller():JSCaller
15 | {
16 | return _jsCaller;
17 | }
18 | public function set jsCaller(value:JSCaller):void
19 | {
20 | _jsCaller = value;
21 | }
22 |
23 | protected var _model:AttachmentsModel;
24 | /**
25 | * reference to the application model
26 | * @return
27 | *
28 | */
29 | public function get model():AttachmentsModel
30 | {
31 | return _model;
32 | }
33 | public function set model(value:AttachmentsModel):void
34 | {
35 | _model = value;
36 | }
37 |
38 | public function AbstractModelJSEngine(engineType:String)
39 | {
40 | super(engineType);
41 |
42 | // default
43 | model = AttachmentsModel.model;
44 | jsCaller = JSCaller.jsCaller;
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/EnginesFactory.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain
2 | {
3 | import ru.mail.engines.chain.manage.SelectFilesEngine;
4 | import ru.mail.engines.chain.presentation.MouseListenerEngine;
5 |
6 | /**
7 | * concreate engines factory for attachments uploader
8 | */
9 | public class EnginesFactory extends AbstractEnginesFactory
10 | {
11 | protected static var _instance:EnginesFactory = null;
12 |
13 | /**
14 | * should be overrided in derived classes
15 | * @return
16 | *
17 | */
18 | public static function getEnginesFactory():EnginesFactory {
19 | if ( !_instance ) {
20 | _instance = new EnginesFactory();
21 | }
22 |
23 | return _instance;
24 | }
25 |
26 | public function EnginesFactory() {
27 | super();
28 |
29 | if ( _instance ) {
30 | throw new Error("EnginesFactory is singleton class, use get engineFactory method instead");
31 | }
32 | }
33 |
34 | override protected function registerEngines():void {
35 | _registeredEngines.push(new SelectFilesEngine());
36 | _registeredEngines.push(new MouseListenerEngine());
37 | // _registeredEngines.push(new UploadEngine());
38 |
39 | super.registerEngines();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/IJsCallerHolder.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain
2 | {
3 | import ru.mail.communication.JSCaller;
4 |
5 | /**
6 | * For Engines that have jsCaller property
7 | * @author v.demidov
8 | *
9 | */
10 | public interface IJsCallerHolder
11 | {
12 | function get jsCaller():JSCaller;
13 | function set jsCaller(value:JSCaller):void
14 | }
15 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/IModelHolder.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain
2 | {
3 | import ru.mail.data.AttachmentsModel;
4 |
5 | /**
6 | * For Engines that have link to the Attachments model
7 | * @author v.demidov
8 | *
9 | */
10 | public interface IModelHolder
11 | {
12 | function get model():AttachmentsModel;
13 | function set model(value:AttachmentsModel):void;
14 | }
15 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/manage/SelectFilesEngine.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain.manage
2 | {
3 | import flash.errors.IllegalOperationError;
4 | import flash.events.Event;
5 | import flash.events.EventDispatcher;
6 | import flash.net.FileReference;
7 | import flash.net.FileReferenceList;
8 |
9 | import ru.mail.data.vo.ErrorVO;
10 | import ru.mail.data.vo.FileVO;
11 | import ru.mail.engines.chain.AbstractModelJSEngine;
12 | import ru.mail.engines.commands.AbstractEngineCommand;
13 | import ru.mail.engines.commands.SelectFilesCommand;
14 | import ru.mail.engines.events.CommandCompleteEvent;
15 | import ru.mail.engines.exceptions.WrongEngineCommandType;
16 |
17 | /**
18 | * Select files, add files to model, send filesInfo
19 | *
20 | * @author v.demidov
21 | *
22 | */
23 | public class SelectFilesEngine extends AbstractModelJSEngine
24 | {
25 | public static const TYPE:String = "SelectFilesEngine";
26 |
27 | /**
28 | * It can be whether FileReferenceList or FileReference. It depends on model.useMultipleSelect value
29 | */
30 | protected var _fileRefObj:EventDispatcher = null;
31 |
32 | protected var _isActive:Boolean = false;
33 |
34 | public function SelectFilesEngine()
35 | {
36 | super(TYPE);
37 | }
38 |
39 | override public function handle(e:AbstractEngineCommand):void
40 | {
41 | var command:SelectFilesCommand = e as SelectFilesCommand;
42 |
43 | if (command == null){
44 | throw new WrongEngineCommandType("{SelectFilesEngine} - handle: command type is: " + command.type + " engine to handle is: " + command.engineToHandleCommandType);
45 | }
46 |
47 | super.handle(command);
48 | selectFiles(command.filesFilters);
49 | }
50 |
51 | /**
52 | * browse for files on disk
53 | * @param filesFilter
54 | *
55 | */
56 | private function selectFiles(filesFilters:Array):void
57 | {
58 | // mail-8225 do not allow multiple sessions
59 | if (_isActive)
60 | return;
61 |
62 | var useMultiple:Boolean = _model.useMultipleSelect;
63 |
64 | if (useMultiple)
65 | _fileRefObj = new FileReferenceList();
66 | else
67 | _fileRefObj = new FileReference();
68 |
69 | _fileRefObj.addEventListener( Event.SELECT, onFilesSelected) ;
70 | _fileRefObj.addEventListener( Event.CANCEL, onFilesSelectionCanceled) ;
71 |
72 | try {
73 | if (useMultiple)
74 | (_fileRefObj as FileReferenceList).browse( filesFilters ) ;
75 | else
76 | (_fileRefObj as FileReference).browse( filesFilters ) ;
77 |
78 | jsCaller.notifyJSFilesEvents("browse");
79 | _isActive = true;
80 | }
81 | catch(error:Error) {
82 | trace ("selectFiles error", error);
83 |
84 | // call js
85 | jsCaller.notifyJSErrors( new ErrorVO(error.toString(), 'browseError') );
86 |
87 | if ( error is IllegalOperationError) {
88 | complete(false, _fileRefObj);
89 | }
90 | else if ( error is ArgumentError ) {
91 | complete(false, _fileRefObj);
92 | }
93 | else if ( error is Error ) {
94 | complete(false, _fileRefObj);
95 | }
96 | }
97 | }
98 |
99 | private function onFilesSelected(event:Event):void {
100 | complete( true, event.target as EventDispatcher);
101 | }
102 |
103 | private function onFilesSelectionCanceled(event:Event):void {
104 | complete( true, event.target as EventDispatcher, true);
105 | }
106 |
107 | /**
108 | * add files to model, notify js
109 | *
110 | * @param fileList
111 | *
112 | */
113 | private function processFiles(fileList:Array):void
114 | {
115 | var fileVO:FileVO;
116 | var filesListForJS:Vector. = new Vector.();
117 |
118 | for each (var fileRef:FileReference in fileList)
119 | {
120 | fileVO = _model.filesBuilder.createFileVO(fileRef);
121 |
122 | filesListForJS.push(fileVO);
123 | }
124 |
125 | // notifyJS - collect all files in a vector and then send them all at once
126 | jsCaller.notifyJSFilesEvents("select", filesListForJS);
127 | }
128 |
129 | /**
130 | *
131 | * @param isSuccess
132 | * @param fileRefrenceList
133 | * @param isCanceled
134 | *
135 | */
136 | private function complete( isSuccess:Boolean, fileRefrenceObj:EventDispatcher, isCanceled:Boolean = false ):void {
137 | //remove listeners
138 | fileRefrenceObj.removeEventListener( Event.SELECT, onFilesSelected) ;
139 | fileRefrenceObj.removeEventListener( Event.CANCEL, onFilesSelectionCanceled) ;
140 |
141 | _isActive = false;
142 |
143 | var fileList:Array;
144 |
145 | if (isCanceled) {
146 | // notify about cancel
147 | _jsCaller.notifyJSFilesEvents("cancel");
148 | }
149 | else {
150 | if (fileRefrenceObj is FileReferenceList) {
151 | fileList = (fileRefrenceObj as FileReferenceList).fileList;
152 | }
153 | else {
154 | fileList = [fileRefrenceObj];
155 | }
156 |
157 | processFiles(fileList);
158 | }
159 |
160 | var event:CommandCompleteEvent = new CommandCompleteEvent(isSuccess);
161 | dispatchEvent(event);
162 | }
163 |
164 | }
165 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/chain/presentation/MouseListenerEngine.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.chain.presentation
2 | {
3 | import flash.display.InteractiveObject;
4 | import flash.events.MouseEvent;
5 |
6 | import ru.mail.communication.JSCaller;
7 | import ru.mail.data.AttachmentsModel;
8 | import ru.mail.engines.chain.AbstractEngine;
9 | import ru.mail.engines.commands.AbstractEngineCommand;
10 | import ru.mail.engines.commands.MouseListenerEngineCommand;
11 | import ru.mail.engines.commands.SelectFilesCommand;
12 |
13 | public class MouseListenerEngine extends AbstractEngine
14 | {
15 | public static const TYPE:String = "MouseListenerEngine";
16 |
17 | private var rollOutFlag:Boolean = false;
18 |
19 | protected var _view:InteractiveObject;
20 |
21 | /**
22 | * we need link to the target object to subscribe to mouseEvents
23 | * @param value
24 | *
25 | */
26 | public function get view():InteractiveObject
27 | {
28 | return _view;
29 | }
30 |
31 | public function set view(value:InteractiveObject):void
32 | {
33 | if (_view != null)
34 | {
35 | // remove event listeners from old target
36 | unsubscribeTarget();
37 | }
38 |
39 | // set new value
40 | _view = value;
41 |
42 | try {
43 | // add listeners to new target
44 | subscribeTarget();
45 | } catch (e:Error) {
46 | trace ("{MouseListenerEngine} - set target: cannot subscribe target", e.message);
47 | }
48 | }
49 |
50 | public function MouseListenerEngine()
51 | {
52 | super(TYPE);
53 | }
54 |
55 | override public function handle(command:AbstractEngineCommand):void
56 | {
57 | super.handle(command);
58 |
59 | if (command is MouseListenerEngineCommand)
60 | {
61 | rollOutFlag = !(command as MouseListenerEngineCommand).isDispatchRollout;
62 | }
63 | }
64 |
65 | protected function subscribeTarget():void
66 | {
67 | _view.addEventListener(MouseEvent.ROLL_OVER, onMouseEvent);
68 | _view.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
69 | _view.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
70 | _view.addEventListener(MouseEvent.ROLL_OUT, onMouseEvent);
71 | }
72 |
73 | protected function unsubscribeTarget():void
74 | {
75 | _view.removeEventListener(MouseEvent.ROLL_OVER, onMouseEvent);
76 | _view.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
77 | _view.removeEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
78 | _view.removeEventListener(MouseEvent.ROLL_OUT, onMouseEvent);
79 | }
80 |
81 | protected function onMouseEvent(event:MouseEvent):void
82 | {
83 | // баг: открывается окно выбора файлов (browse() ) и от этого бросается эвент rollout,
84 | // и от этого жс сворачивает флешку и больше ее не слушает
85 | if (event.type == MouseEvent.ROLL_OUT && rollOutFlag)
86 | {
87 | // and
88 | return;
89 | // ha-ha!
90 | }
91 | // call js
92 | JSCaller.jsCaller.notifyJSMouseEvents(event.type);
93 |
94 | if (event.type == MouseEvent.MOUSE_UP)
95 | {
96 | rollOutFlag = true;
97 |
98 | // get file filter from model, and dispatch select files event
99 | var fileFilters:Array = AttachmentsModel.model.fileFilters;
100 | dispatchEvent(new SelectFilesCommand(fileFilters));
101 | }
102 |
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/commands/MouseListenerEngineCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.commands
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.engines.chain.presentation.MouseListenerEngine;
6 |
7 | public class MouseListenerEngineCommand extends AbstractEngineCommand
8 | {
9 | public static const TYPE:String = "MouseListenerEngineCommand";
10 |
11 | private var _dispatchRollout:Boolean;
12 | /**
13 | * true - dispatch js event mouseleave on rollout
14 | * false - do not dispatch
15 | * @return
16 | *
17 | */
18 | public function get isDispatchRollout():Boolean
19 | {
20 | return _dispatchRollout;
21 | }
22 |
23 |
24 | public function MouseListenerEngineCommand(dispatchRollout:Boolean)
25 | {
26 | super(TYPE, MouseListenerEngine.TYPE);
27 |
28 | _dispatchRollout = dispatchRollout;
29 | }
30 |
31 | override public function clone():Event
32 | {
33 | return new MouseListenerEngineCommand(_dispatchRollout);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/engines/commands/SelectFilesCommand.as:
--------------------------------------------------------------------------------
1 | package ru.mail.engines.commands
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.engines.chain.manage.SelectFilesEngine;
6 |
7 | /**
8 | * command to activate SelectFilesCommand engine
9 | *
10 | * store filesFilter to use in fileReference.browse
11 | *
12 | * @author s.osipov
13 | *
14 | */
15 | public class SelectFilesCommand extends AbstractEngineCommand
16 | {
17 | public static const TYPE:String = "SelectFilesCommand";
18 |
19 | private var _filesFilters:Array = null;
20 | /**
21 | * file filter to use in fileReferenceList
22 | * @return
23 | *
24 | */
25 | public function get filesFilters():Array
26 | {
27 | return _filesFilters;
28 | }
29 |
30 | public function SelectFilesCommand(filters:Array)
31 | {
32 | super(TYPE, SelectFilesEngine.TYPE);
33 |
34 | _filesFilters = filters;
35 | }
36 |
37 | override public function clone():Event
38 | {
39 | return new SelectFilesCommand(_filesFilters);
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/events/CompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.events
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.data.vo.ErrorVO;
6 |
7 | /**
8 | * the event that is triggered by Command on its executing completion.
9 | * it provides access to execution result flag, that indicates whether command executed successfully
10 | * @author ivanova
11 | */
12 | public class CompleteEvent extends Event
13 | {
14 | public static const TYPE:String = "CompleteEvent" ;
15 |
16 | private var _isSuccess:Boolean;
17 | public function get isSuccess():Boolean
18 | {
19 | return _isSuccess ;
20 | }
21 |
22 | private var _error:ErrorVO
23 | public function get error():ErrorVO
24 | {
25 | return _error;
26 | }
27 |
28 | /**
29 | * ctor
30 | * @param isSuccess: indicates whether command executed successfully.
31 | */
32 | public function CompleteEvent( isSuccess:Boolean, error:ErrorVO = null, type:String = null )
33 | {
34 | super( type? type : TYPE ) ;
35 | _isSuccess = isSuccess ;
36 | _error = error;
37 | }
38 |
39 | override public function clone():Event
40 | {
41 | return new CompleteEvent(_isSuccess, error);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/events/DecodeBytesToBitmapCompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.events
2 | {
3 | import flash.display.Bitmap;
4 | import flash.events.Event;
5 |
6 | import ru.mail.data.vo.ErrorVO;
7 |
8 | public class DecodeBytesToBitmapCompleteEvent extends CompleteEvent
9 | {
10 | public static const TYPE:String = "DecodeBytesToBitmapCompletedEvent";
11 |
12 | private var _decodedBitmap:Bitmap;
13 | /**
14 | * transformed image data
15 | * @return
16 | */
17 | public function get decodedBitmap():Bitmap
18 | {
19 | return _decodedBitmap;
20 | }
21 |
22 |
23 | public function DecodeBytesToBitmapCompleteEvent(isSuccess:Boolean, decodedBitmap:Bitmap, error:ErrorVO = null)
24 | {
25 | super(isSuccess, error, TYPE);
26 |
27 | _decodedBitmap = decodedBitmap;
28 | }
29 |
30 | override public function clone():Event
31 | {
32 | return new DecodeBytesToBitmapCompleteEvent(isSuccess, decodedBitmap, error);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/events/ImageTransformCompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.events
2 | {
3 | import flash.events.Event;
4 | import flash.utils.ByteArray;
5 |
6 | import ru.mail.data.vo.ErrorVO;
7 |
8 | public class ImageTransformCompleteEvent extends CompleteEvent
9 | {
10 | public static const TYPE:String = "ImageTransformCompleteEvent";
11 |
12 | private var _data:ByteArray;
13 | /**
14 | * transformed image data
15 | * @return
16 | */
17 | public function get data():ByteArray
18 | {
19 | return _data;
20 | }
21 |
22 | public function ImageTransformCompleteEvent(isSuccess:Boolean, data:ByteArray, error:ErrorVO = null)
23 | {
24 | super(isSuccess, error, TYPE);
25 |
26 | _data = data;
27 | }
28 |
29 | override public function clone():Event
30 | {
31 | return new ImageTransformCompleteEvent(isSuccess, data, error);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/events/UploadCompleteEvent.as:
--------------------------------------------------------------------------------
1 | package ru.mail.events
2 | {
3 | import flash.events.Event;
4 |
5 | import ru.mail.data.vo.ErrorVO;
6 |
7 | public class UploadCompleteEvent extends CompleteEvent
8 | {
9 | public static const TYPE:String = "UploadCompleteEvent";
10 |
11 | private var _result:String;
12 | /**
13 | * Upload server responce
14 | */
15 | public function get result():String
16 | {
17 | return _result;
18 | }
19 |
20 | public function UploadCompleteEvent(isSuccess:Boolean, result:String, error:ErrorVO=null)
21 | {
22 | super(isSuccess, error, TYPE);
23 |
24 | _result = result;
25 | }
26 |
27 | override public function clone():Event
28 | {
29 | return new UploadCompleteEvent(isSuccess, result, error);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/flash/core/src/ru/mail/utils/ExifReader2.as:
--------------------------------------------------------------------------------
1 | package ru.mail.utils
2 | {
3 |
4 | import flash.utils.ByteArray;
5 |
6 | /**
7 | * Get data from exif.
8 | * Only orientation tag
9 | * If need something else, add tags.
10 | *
11 | */
12 | public class ExifReader2
13 | {
14 | private var m_data:ByteArray = new ByteArray();
15 | private var m_exif:Object = new Object;
16 | private var m_exifKeys:Array = new Array();
17 |
18 | private var m_intel:Boolean=true;
19 | private var m_loc:uint=0;
20 |
21 | private var DATASIZES:Object = new Object;
22 | private var TAGS:Object = new Object;
23 |
24 | public function getKeys():Array{
25 | return m_exifKeys;
26 | }
27 | public function hasKey(key:String):Boolean{
28 | return m_exif[key] != undefined;
29 | }
30 | public function getValue(key:String):Object{
31 | if(m_exif[key] == undefined) return null;
32 | return m_exif[key];
33 | }
34 |
35 | public function ExifReader2(){
36 | DATASIZES[1] = 1;
37 | DATASIZES[2] = 1;
38 | DATASIZES[3] = 2;
39 | DATASIZES[4] = 4;
40 | DATASIZES[5] = 8;
41 | DATASIZES[6] = 1;
42 | DATASIZES[7] = 1;
43 | DATASIZES[8] = 2;
44 | DATASIZES[9] = 4;
45 | DATASIZES[10] = 8;
46 | DATASIZES[11] = 4;
47 | DATASIZES[12] = 8;
48 |
49 | TAGS[0x0112] = 'Orientation';
50 |
51 | //... add more if you like.
52 | //See http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
53 | }
54 |
55 |
56 | public function processData( data:ByteArray ):Boolean {
57 |
58 | m_data = data ;
59 | m_data.position = 0 ;
60 |
61 | var iter:int=0;
62 |
63 | //confirm JPG type
64 | if(!(m_data.readUnsignedByte()==0xff && m_data.readUnsignedByte()==0xd8))
65 | return false ;
66 |
67 | //Locate APP1 MARKER
68 | var ff:uint=0;
69 | var marker:uint=0;
70 | for(iter=0;iter<5;++iter){ //cap iterations
71 | ff = m_data.readUnsignedByte();
72 | marker = m_data.readUnsignedByte();
73 | var size:uint = (m_data.readUnsignedByte()<<8) + m_data.readUnsignedByte();
74 | if(marker == 0x00e1) break;
75 | else{
76 | for(var x:int=0;x100) numEntries=100; //cap entries
112 |
113 | m_loc+=2;
114 | for(iter=0; iter=utf-8
4 |
--------------------------------------------------------------------------------
/flash/image/html-template/history/history.css:
--------------------------------------------------------------------------------
1 | /* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */
2 |
3 | #ie_historyFrame { width: 0px; height: 0px; display:none }
4 | #firefox_anchorDiv { width: 0px; height: 0px; display:none }
5 | #safari_formDiv { width: 0px; height: 0px; display:none }
6 | #safari_rememberDiv { width: 0px; height: 0px; display:none }
7 |
--------------------------------------------------------------------------------
/flash/image/html-template/history/historyFrame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
27 | Hidden frame for Browser History support.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/flash/image/html-template/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 | ${title}
15 |
16 |
17 |
23 |
30 |
31 |
32 |
36 |
37 |
38 |
61 |
62 |
63 |
67 |
68 |
69 | To view this page ensure that Adobe Flash Player version
70 | ${version_major}.${version_minor}.${version_revision} or greater is installed.
71 |
72 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Either scripts and active content are not permitted to run or Adobe Flash Player version
96 | ${version_major}.${version_minor}.${version_revision} or greater is not installed.
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/flash/image/html-template/playerProductInstall.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/image/html-template/playerProductInstall.swf
--------------------------------------------------------------------------------
/flash/image/lib/blooddy_crypto.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/flash/image/lib/blooddy_crypto.swc
--------------------------------------------------------------------------------
/flash/image/src/Base64.as:
--------------------------------------------------------------------------------
1 | package{
2 | import flash.utils.ByteArray;
3 | public class Base64 {
4 | private static const decodeChars:Array =
5 | [-1, -1, -1, -1, -1, -1, -1, -1,
6 | -1, -1, -1, -1, -1, -1, -1, -1,
7 | -1, -1, -1, -1, -1, -1, -1, -1,
8 | -1, -1, -1, -1, -1, -1, -1, -1,
9 | -1, -1, -1, -1, -1, -1, -1, -1,
10 | -1, -1, -1, 62, -1, -1, -1, 63,
11 | 52, 53, 54, 55, 56, 57, 58, 59,
12 | 60, 61, -1, -1, -1, -1, -1, -1,
13 | -1, 0, 1, 2, 3, 4, 5, 6,
14 | 7, 8, 9, 10, 11, 12, 13, 14,
15 | 15, 16, 17, 18, 19, 20, 21, 22,
16 | 23, 24, 25, -1, -1, -1, -1, -1,
17 | -1, 26, 27, 28, 29, 30, 31, 32,
18 | 33, 34, 35, 36, 37, 38, 39, 40,
19 | 41, 42, 43, 44, 45, 46, 47, 48,
20 | 49, 50, 51, -1, -1, -1, -1, -1];
21 | public static function decode(str:String):ByteArray {
22 | var c1:int;
23 | var c2:int;
24 | var c3:int;
25 | var c4:int;
26 | var i:int;
27 | var len:int;
28 | var out:ByteArray;
29 | len = str.length;
30 | i = 0;
31 | out = new ByteArray();
32 | while (i < len) {
33 | // c1
34 | do {
35 | c1 = decodeChars[str.charCodeAt(i++) & 0xff];
36 | } while (i < len && c1 == -1);
37 | if (c1 == -1) {
38 | break;
39 | }
40 | // c2
41 | do {
42 | c2 = decodeChars[str.charCodeAt(i++) & 0xff];
43 | } while (i < len && c2 == -1);
44 | if (c2 == -1) {
45 | break;
46 | }
47 | out.writeByte((c1 << 2) | ((c2 & 0x30) >> 4));
48 | // c3
49 | do {
50 | c3 = str.charCodeAt(i++) & 0xff;
51 | if (c3 == 61) {
52 | return out;
53 | }
54 | c3 = decodeChars[c3];
55 | } while (i < len && c3 == -1);
56 | if (c3 == -1) {
57 | break;
58 | }
59 | out.writeByte(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2));
60 | // c4
61 | do {
62 | c4 = str.charCodeAt(i++) & 0xff;
63 | if (c4 == 61) {
64 | return out;
65 | }
66 | c4 = decodeChars[c4];
67 | } while (i < len && c4 == -1);
68 | if (c4 == -1) {
69 | break;
70 | }
71 | out.writeByte(((c3 & 0x03) << 6) | c4);
72 | }
73 | return out;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/lib/FileAPI.Flash.Camera.js:
--------------------------------------------------------------------------------
1 | /**
2 | * FileAPI fallback to Flash
3 | *
4 | * @flash-developer "Vladimir Demidov"
5 | */
6 |
7 | /*global window, FileAPI */
8 | (function (window, jQuery, api) {
9 | "use strict";
10 |
11 | var _each = api.each,
12 | _cameraQueue = [];
13 |
14 | if (api.support.flash && (api.media && (!api.support.media || !api.html5 || api.insecureChrome))) {
15 | (function () {
16 | function _wrap(fn) {
17 | var id = fn.wid = api.uid();
18 | api.Flash._fn[id] = fn;
19 | return 'FileAPI.Flash._fn.' + id;
20 | }
21 |
22 |
23 | function _unwrap(fn) {
24 | try {
25 | api.Flash._fn[fn.wid] = null;
26 | delete api.Flash._fn[fn.wid];
27 | } catch (e) {
28 | }
29 | }
30 |
31 | var flash = api.Flash;
32 | api.extend(api.Flash, {
33 |
34 | patchCamera: function () {
35 | api.Camera.fallback = function (el, options, callback) {
36 | var camId = api.uid();
37 | api.log('FlashAPI.Camera.publish: ' + camId);
38 | flash.publish(el, camId, api.extend(options, {
39 | camera: true,
40 | onEvent: _wrap(function _(evt) {
41 | if (evt.type === 'camera') {
42 | _unwrap(_);
43 |
44 | if (evt.error) {
45 | api.log('FlashAPI.Camera.publish.error: ' + evt.error);
46 | callback(evt.error);
47 | } else {
48 | api.log('FlashAPI.Camera.publish.success: ' + camId);
49 | callback(null);
50 | }
51 | }
52 | })
53 | }));
54 | };
55 | // Run
56 | _each(_cameraQueue, function (args) {
57 | api.Camera.fallback.apply(api.Camera, args);
58 | });
59 | _cameraQueue = [];
60 |
61 |
62 | // FileAPI.Camera:proto
63 | api.extend(api.Camera.prototype, {
64 | _id: function () {
65 | return this.video.id;
66 | },
67 |
68 | start: function (callback) {
69 | var _this = this;
70 | flash.cmd(this._id(), 'camera.on', {
71 | callback: _wrap(function _(evt) {
72 | _unwrap(_);
73 |
74 | if (evt.error) {
75 | api.log('FlashAPI.camera.on.error: ' + evt.error);
76 | callback(evt.error, _this);
77 | } else {
78 | api.log('FlashAPI.camera.on.success: ' + _this._id());
79 | _this._active = true;
80 | callback(null, _this);
81 | }
82 | })
83 | });
84 | },
85 |
86 | stop: function () {
87 | this._active = false;
88 | flash.cmd(this._id(), 'camera.off');
89 | },
90 |
91 | shot: function () {
92 | api.log('FlashAPI.Camera.shot:', this._id());
93 |
94 | var shot = api.Flash.cmd(this._id(), 'shot', {});
95 | shot.type = 'image/png';
96 | shot.flashId = this._id();
97 | shot.isShot = true;
98 |
99 | return new api.Camera.Shot(shot);
100 | }
101 | });
102 | }
103 | });
104 |
105 | api.Camera.fallback = function () {
106 | _cameraQueue.push(arguments);
107 | };
108 |
109 | }());
110 | }
111 | }(window, window.jQuery, FileAPI));
112 |
--------------------------------------------------------------------------------
/lib/FileAPI.Form.js:
--------------------------------------------------------------------------------
1 | /*global window, FileAPI */
2 |
3 | (function (api, window){
4 | "use strict";
5 |
6 | var
7 | document = window.document
8 | , FormData = window.FormData
9 | , Form = function (){ this.items = []; }
10 | , encodeURIComponent = window.encodeURIComponent
11 | ;
12 |
13 |
14 | Form.prototype = {
15 |
16 | append: function (name, blob, file, type){
17 | this.items.push({
18 | name: name
19 | , blob: blob && blob.blob || (blob == void 0 ? '' : blob)
20 | , file: blob && (file || blob.name)
21 | , type: blob && (type || blob.type)
22 | });
23 | },
24 |
25 | each: function (fn){
26 | var i = 0, n = this.items.length;
27 | for( ; i < n; i++ ){
28 | fn.call(this, this.items[i]);
29 | }
30 | },
31 |
32 | toData: function (fn, options){
33 | // allow chunked transfer if we have only one file to send
34 | // flag is used below and in XHR._send
35 | options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1;
36 |
37 | if( !api.support.html5 ){
38 | api.log('FileAPI.Form.toHtmlData');
39 | this.toHtmlData(fn);
40 | }
41 | else if( !api.formData || this.multipart || !FormData ){
42 | api.log('FileAPI.Form.toMultipartData');
43 | this.toMultipartData(fn);
44 | }
45 | else if( options._chunked ){
46 | api.log('FileAPI.Form.toPlainData');
47 | this.toPlainData(fn);
48 | }
49 | else {
50 | api.log('FileAPI.Form.toFormData');
51 | this.toFormData(fn);
52 | }
53 | },
54 |
55 | _to: function (data, complete, next, arg){
56 | var queue = api.queue(function (){
57 | complete(data);
58 | });
59 |
60 | this.each(function (file){
61 | try{
62 | next(file, data, queue, arg);
63 | }
64 | catch( err ){
65 | api.log('FileAPI.Form._to: ' + err.message);
66 | complete(err);
67 | }
68 | });
69 |
70 | queue.check();
71 | },
72 |
73 |
74 | toHtmlData: function (fn){
75 | this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){
76 | var blob = file.blob, hidden;
77 |
78 | if( file.file ){
79 | api.reset(blob, true);
80 | // set new name
81 | blob.name = file.name;
82 | blob.disabled = false;
83 | data.appendChild(blob);
84 | }
85 | else {
86 | hidden = document.createElement('input');
87 | hidden.name = file.name;
88 | hidden.type = 'hidden';
89 | hidden.value = blob;
90 | data.appendChild(hidden);
91 | }
92 | });
93 | },
94 |
95 | toPlainData: function (fn){
96 | this._to({}, fn, function (file, data, queue){
97 | if( file.file ){
98 | data.type = file.file;
99 | }
100 |
101 | if( file.blob.toBlob ){
102 | // canvas
103 | queue.inc();
104 | _convertFile(file, function (file, blob){
105 | data.name = file.name;
106 | data.file = blob;
107 | data.size = blob.length;
108 | data.type = file.type;
109 | queue.next();
110 | });
111 | }
112 | else if( file.file ){
113 | // file
114 | data.name = file.blob.name;
115 | data.file = file.blob;
116 | data.size = file.blob.size;
117 | data.type = file.type;
118 | }
119 | else {
120 | // additional data
121 | if( !data.params ){
122 | data.params = [];
123 | }
124 | data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob));
125 | }
126 |
127 | data.start = -1;
128 | data.end = data.file && data.file.FileAPIReadPosition || -1;
129 | data.retry = 0;
130 | });
131 | },
132 |
133 | toFormData: function (fn){
134 | this._to(new FormData, fn, function (file, data, queue){
135 | if( file.blob && file.blob.toBlob ){
136 | queue.inc();
137 | _convertFile(file, function (file, blob){
138 | data.append(file.name, blob, file.file);
139 | queue.next();
140 | });
141 | }
142 | else if( file.file ){
143 | data.append(file.name, file.blob, file.file);
144 | }
145 | else {
146 | data.append(file.name, file.blob);
147 | }
148 |
149 | if( file.file ){
150 | data.append('_'+file.name, file.file);
151 | }
152 | });
153 | },
154 |
155 |
156 | toMultipartData: function (fn){
157 | this._to([], fn, function (file, data, queue, boundary){
158 | queue.inc();
159 | _convertFile(file, function (file, blob){
160 | data.push(
161 | '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '')
162 | + (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '')
163 | + '\r\n'
164 | + '\r\n'+ (file.file ? blob : encodeURIComponent(blob))
165 | + '\r\n')
166 | );
167 | queue.next();
168 | }, true);
169 | }, api.expando);
170 | }
171 | };
172 |
173 |
174 | function _convertFile(file, fn, useBinaryString){
175 | var blob = file.blob, filename = file.file;
176 |
177 | if( filename ){
178 | if( !blob.toDataURL ){
179 | // The Blob is not an image.
180 | api.readAsBinaryString(blob, function (evt){
181 | if( evt.type == 'load' ){
182 | fn(file, evt.result);
183 | }
184 | });
185 | return;
186 | }
187 |
188 | var
189 | mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' }
190 | , type = mime[file.type] ? file.type : 'image/png'
191 | , ext = mime[type] || '.png'
192 | , quality = blob.quality || 1
193 | ;
194 |
195 | if( !filename.match(new RegExp(ext+'$', 'i')) ){
196 | // Does not change the current extension, but add a new one.
197 | filename += ext.replace('?', '');
198 | }
199 |
200 | file.file = filename;
201 | file.type = type;
202 |
203 | if( !useBinaryString && blob.toBlob ){
204 | blob.toBlob(function (blob){
205 | fn(file, blob);
206 | }, type, quality);
207 | }
208 | else {
209 | fn(file, api.toBinaryString(blob.toDataURL(type, quality)));
210 | }
211 | }
212 | else {
213 | fn(file, blob);
214 | }
215 | }
216 |
217 |
218 | // @export
219 | api.Form = Form;
220 | })(FileAPI, window);
221 |
--------------------------------------------------------------------------------
/lib/canvas-to-blob.js:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaScript Canvas to Blob 2.0.5
3 | * https://github.com/blueimp/JavaScript-Canvas-to-Blob
4 | *
5 | * Copyright 2012, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | *
11 | * Based on stackoverflow user Stoive's code snippet:
12 | * http://stackoverflow.com/q/4998908
13 | */
14 |
15 | /*jslint nomen: true, regexp: true */
16 | /*global window, atob, Blob, ArrayBuffer, Uint8Array */
17 |
18 | (function (window) {
19 | 'use strict';
20 | var CanvasPrototype = window.HTMLCanvasElement &&
21 | window.HTMLCanvasElement.prototype,
22 | hasBlobConstructor = window.Blob && (function () {
23 | try {
24 | return Boolean(new Blob());
25 | } catch (e) {
26 | return false;
27 | }
28 | }()),
29 | hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
30 | (function () {
31 | try {
32 | return new Blob([new Uint8Array(100)]).size === 100;
33 | } catch (e) {
34 | return false;
35 | }
36 | }()),
37 | BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
38 | window.MozBlobBuilder || window.MSBlobBuilder,
39 | dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
40 | window.ArrayBuffer && window.Uint8Array && function (dataURI) {
41 | var byteString,
42 | arrayBuffer,
43 | intArray,
44 | i,
45 | mimeString,
46 | bb;
47 | if (dataURI.split(',')[0].indexOf('base64') >= 0) {
48 | // Convert base64 to raw binary data held in a string:
49 | byteString = atob(dataURI.split(',')[1]);
50 | } else {
51 | // Convert base64/URLEncoded data component to raw binary data:
52 | byteString = decodeURIComponent(dataURI.split(',')[1]);
53 | }
54 | // Write the bytes of the string to an ArrayBuffer:
55 | arrayBuffer = new ArrayBuffer(byteString.length);
56 | intArray = new Uint8Array(arrayBuffer);
57 | for (i = 0; i < byteString.length; i += 1) {
58 | intArray[i] = byteString.charCodeAt(i);
59 | }
60 | // Separate out the mime component:
61 | mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
62 | // Write the ArrayBuffer (or ArrayBufferView) to a blob:
63 | if (hasBlobConstructor) {
64 | return new Blob(
65 | [hasArrayBufferViewSupport ? intArray : arrayBuffer],
66 | {type: mimeString}
67 | );
68 | }
69 | bb = new BlobBuilder();
70 | bb.append(arrayBuffer);
71 | return bb.getBlob(mimeString);
72 | };
73 | if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
74 | if (CanvasPrototype.mozGetAsFile) {
75 | CanvasPrototype.toBlob = function (callback, type, quality) {
76 | if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
77 | callback(dataURLtoBlob(this.toDataURL(type, quality)));
78 | } else {
79 | callback(this.mozGetAsFile('blob', type));
80 | }
81 | };
82 | } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
83 | CanvasPrototype.toBlob = function (callback, type, quality) {
84 | callback(dataURLtoBlob(this.toDataURL(type, quality)));
85 | };
86 | }
87 | }
88 | window.dataURLtoBlob = dataURLtoBlob;
89 | })(window);
90 |
--------------------------------------------------------------------------------
/lib/load-image-ios.js:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaScript Load Image iOS scaling fixes 1.0.3
3 | * https://github.com/blueimp/JavaScript-Load-Image
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * iOS image scaling fixes based on
9 | * https://github.com/stomita/ios-imagefile-megapixel
10 | *
11 | * Licensed under the MIT license:
12 | * http://www.opensource.org/licenses/MIT
13 | */
14 |
15 | /*jslint nomen: true, bitwise: true */
16 | /*global FileAPI, window, document */
17 |
18 | (function (factory) {
19 | 'use strict';
20 | factory(FileAPI);
21 | }(function (loadImage) {
22 | 'use strict';
23 |
24 | // Only apply fixes on the iOS platform:
25 | if (!window.navigator || !window.navigator.platform ||
26 | !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
27 | return;
28 | }
29 |
30 | var originalRenderMethod = loadImage.renderImageToCanvas;
31 |
32 | // Detects subsampling in JPEG images:
33 | loadImage.detectSubsampling = function (img) {
34 | var canvas,
35 | context;
36 | if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
37 | canvas = document.createElement('canvas');
38 | canvas.width = canvas.height = 1;
39 | context = canvas.getContext('2d');
40 | context.drawImage(img, -img.width + 1, 0);
41 | // subsampled image becomes half smaller in rendering size.
42 | // check alpha channel value to confirm image is covering edge pixel or not.
43 | // if alpha value is 0 image is not covering, hence subsampled.
44 | return context.getImageData(0, 0, 1, 1).data[3] === 0;
45 | }
46 | return false;
47 | };
48 |
49 | // Detects vertical squash in JPEG images:
50 | loadImage.detectVerticalSquash = function (img, subsampled) {
51 | var naturalHeight = img.naturalHeight || img.height,
52 | canvas = document.createElement('canvas'),
53 | context = canvas.getContext('2d'),
54 | data,
55 | sy,
56 | ey,
57 | py,
58 | alpha;
59 | if (subsampled) {
60 | naturalHeight /= 2;
61 | }
62 | canvas.width = 1;
63 | canvas.height = naturalHeight;
64 | context.drawImage(img, 0, 0);
65 | data = context.getImageData(0, 0, 1, naturalHeight).data;
66 | // search image edge pixel position in case it is squashed vertically:
67 | sy = 0;
68 | ey = naturalHeight;
69 | py = naturalHeight;
70 | while (py > sy) {
71 | alpha = data[(py - 1) * 4 + 3];
72 | if (alpha === 0) {
73 | ey = py;
74 | } else {
75 | sy = py;
76 | }
77 | py = (ey + sy) >> 1;
78 | }
79 | return (py / naturalHeight) || 1;
80 | };
81 |
82 | // Renders image to canvas while working around iOS image scaling bugs:
83 | // https://github.com/blueimp/JavaScript-Load-Image/issues/13
84 | loadImage.renderImageToCanvas = function (
85 | canvas,
86 | img,
87 | sourceX,
88 | sourceY,
89 | sourceWidth,
90 | sourceHeight,
91 | destX,
92 | destY,
93 | destWidth,
94 | destHeight
95 | ) {
96 | if (img._type === 'image/jpeg') {
97 | var context = canvas.getContext('2d'),
98 | tmpCanvas = document.createElement('canvas'),
99 | tileSize = 1024,
100 | tmpContext = tmpCanvas.getContext('2d'),
101 | subsampled,
102 | vertSquashRatio,
103 | tileX,
104 | tileY;
105 | tmpCanvas.width = tileSize;
106 | tmpCanvas.height = tileSize;
107 | context.save();
108 | subsampled = loadImage.detectSubsampling(img);
109 | if (subsampled) {
110 | sourceX /= 2;
111 | sourceY /= 2;
112 | sourceWidth /= 2;
113 | sourceHeight /= 2;
114 | }
115 | vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
116 | if (subsampled || vertSquashRatio !== 1) {
117 | sourceY *= vertSquashRatio;
118 | destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
119 | destHeight = Math.ceil(
120 | tileSize * destHeight / sourceHeight / vertSquashRatio
121 | );
122 | destY = 0;
123 | tileY = 0;
124 | while (tileY < sourceHeight) {
125 | destX = 0;
126 | tileX = 0;
127 | while (tileX < sourceWidth) {
128 | tmpContext.clearRect(0, 0, tileSize, tileSize);
129 | tmpContext.drawImage(
130 | img,
131 | sourceX,
132 | sourceY,
133 | sourceWidth,
134 | sourceHeight,
135 | -tileX,
136 | -tileY,
137 | sourceWidth,
138 | sourceHeight
139 | );
140 | context.drawImage(
141 | tmpCanvas,
142 | 0,
143 | 0,
144 | tileSize,
145 | tileSize,
146 | destX,
147 | destY,
148 | destWidth,
149 | destHeight
150 | );
151 | tileX += tileSize;
152 | destX += destWidth;
153 | }
154 | tileY += tileSize;
155 | destY += destHeight;
156 | }
157 | context.restore();
158 | return canvas;
159 | }
160 | }
161 | return originalRenderMethod(
162 | canvas,
163 | img,
164 | sourceX,
165 | sourceY,
166 | sourceWidth,
167 | sourceHeight,
168 | destX,
169 | destY,
170 | destWidth,
171 | destHeight
172 | );
173 | };
174 |
175 | }));
176 |
--------------------------------------------------------------------------------
/node/file-api.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var qs = require('qs');
3 | var imageSize = require('image-size');
4 |
5 | function convertToBase64(buffer, mimetype) {
6 | return 'data:' + mimetype + ';base64,' + buffer.toString('base64');
7 | }
8 |
9 | function fileApi() {
10 | return function (req, res, next) {
11 | var queryString = '';
12 |
13 | req.files = {};
14 | req.images = {};
15 |
16 | req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
17 | var buffersArray = [];
18 |
19 | file.on('data', function (data) {
20 | buffersArray.push(data);
21 | });
22 |
23 | file.on('end', function () {
24 | var bufferResult = Buffer.concat(buffersArray);
25 | var fileObj = {
26 | name: filename,
27 | type: mimetype,
28 | mime: mimetype,
29 | size: bufferResult.length,
30 | dataURL: convertToBase64(bufferResult, mimetype)
31 | };
32 |
33 | req.files[fieldname] = fileObj;
34 |
35 | if (mimetype.indexOf('image/') === 0) {
36 | fs.writeFileSync(filename, bufferResult);
37 |
38 | var size = imageSize(filename);
39 |
40 | fileObj.width = size.width;
41 | fileObj.height = size.height;
42 |
43 | req.images[fieldname] = fileObj;
44 |
45 | fs.unlinkSync(filename);
46 | }
47 | });
48 | });
49 |
50 | req.busboy.on('field', function (key, value) {
51 | queryString += encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
52 | });
53 |
54 | req.busboy.on('finish', function () {
55 | req.body = qs.parse(queryString);
56 |
57 | next();
58 | });
59 | };
60 | }
61 |
62 | module.exports = fileApi;
63 |
--------------------------------------------------------------------------------
/node/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var busboy = require('connect-busboy');
3 | var fileApi = require('./file-api');
4 | var app = express();
5 |
6 | app.use(express.static('.', {index: 'index.html'}));
7 |
8 | app.use(function (req, res, next) {
9 | // Enable CORS for non static files
10 | var origin = req.get('Origin');
11 |
12 | if (origin) {
13 | res.set({
14 | 'Access-Control-Allow-Origin': origin,
15 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
16 | 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, X-Foo, X-Rnd',
17 | 'Access-Control-Allow-Credentials': 'true'
18 | });
19 | }
20 | next();
21 | });
22 |
23 | var uploadPath = '/upload';
24 |
25 | app.options(uploadPath, function (req, res) {
26 | res.end();
27 | });
28 |
29 | app.post(
30 | uploadPath,
31 | busboy({immediate: true}), // parse post data
32 | fileApi(), // prepare req.body, req.files and req.images
33 | function (req, res) {
34 | var jsonp = req.query.callback || null;
35 |
36 | res[jsonp ? 'jsonp' : 'json']({
37 | status: 200,
38 | statusText: 'OK',
39 | images: req.images,
40 | data: {
41 | HEADERS: req.headers,
42 | _REQUEST: req.body,
43 | _FILES: req.files
44 | }
45 | });
46 | }
47 | );
48 |
49 | // Export
50 | module.exports.createServer = function (port, callback) {
51 | var server = app.listen(port, function () {
52 | var host = server.address().address;
53 | var port = server.address().port;
54 |
55 | console.log('Test server listening at http://%s:%s', host, port);
56 |
57 | callback(server);
58 | });
59 | };
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fileapi",
3 | "exportName": "FileAPI",
4 | "version": "2.2.0",
5 | "devDependencies": {
6 | "connect-busboy": "~0.0.2",
7 | "eventemitter2": "~0.4.13",
8 | "express": "~4.12.3",
9 | "flex-sdk": "^4.6.0-0",
10 | "grunt": "^0.4.5",
11 | "grunt-cli": "^1.3.2",
12 | "grunt-contrib-compress": "~0.9.1",
13 | "grunt-contrib-concat": "~0.4.0",
14 | "grunt-contrib-connect": "~0.8.0",
15 | "grunt-contrib-jshint": "~0.10.0",
16 | "grunt-contrib-uglify": "~0.5.0",
17 | "grunt-contrib-watch": "~0.6.1",
18 | "grunt-curl": "~2.0.2",
19 | "grunt-mxmlc": "~0.5.2",
20 | "grunt-version": "~0.3.0",
21 | "image-size": "~0.3.5",
22 | "phantomjs": "~1.9.7-9",
23 | "qs": "~2.4.1",
24 | "semver": "~5.0.0",
25 | "temporary": "~0.0.8"
26 | },
27 | "peerDependencies": {},
28 | "description": "FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.",
29 | "main": "dist/FileAPI.js",
30 | "files": [
31 | "dist",
32 | "plugins",
33 | "*.js"
34 | ],
35 | "jam": {
36 | "name": "FileAPI"
37 | },
38 | "scripts": {
39 | "test": "grunt tests --verbose",
40 | "build": "grunt build"
41 | },
42 | "repository": {
43 | "type": "git",
44 | "url": "git://github.com/mailru/FileAPI.git"
45 | },
46 | "keywords": [
47 | "FileAPI",
48 | "upload",
49 | "file",
50 | "html5",
51 | "chunked"
52 | ],
53 | "author": "Konstantin Lebedev ",
54 | "contributors": [
55 | "Vladimir Demidov ",
56 | "Ilya Lebedev ",
57 | "Mikhail Bezoyan "
58 | ],
59 | "license": "BSD",
60 | "dependencies": {}
61 | }
62 |
--------------------------------------------------------------------------------
/server/FileAPI.class.php:
--------------------------------------------------------------------------------
1 | $mixedValue ){
10 | self::rRestructuringFilesArray($arrayForFill[$currentKey],
11 | $nameKey,
12 | $mixedValue,
13 | $fileDescriptionParam);
14 | }
15 | } else {
16 | $arrayForFill[$currentKey][$fileDescriptionParam] = $currentMixedValue;
17 | }
18 | }
19 |
20 |
21 | private static function determineMimeType(&$file){
22 | if( function_exists('mime_content_type') ){
23 | if( isset($file['tmp_name']) && is_string($file['tmp_name']) ){
24 | if( $file['type'] == 'application/octet-stream' ){
25 | $mime = mime_content_type($file['tmp_name']);
26 | if( !empty($mime) ){
27 | $file['type'] = $mime;
28 | }
29 | }
30 | }
31 | else if( is_array($file) ){
32 | foreach( $file as &$entry ){
33 | self::determineMimeType($entry);
34 | }
35 | }
36 | }
37 | }
38 |
39 |
40 | /**
41 | * Enable CORS -- http://enable-cors.org/
42 | * @param array [$options]
43 | */
44 | public static function enableCORS($options = null){
45 | if( is_null($options) ){
46 | $options = array();
47 | }
48 |
49 | if( !isset($options['origin']) ){
50 | $options['origin'] = $_SERVER['HTTP_ORIGIN'];
51 | }
52 |
53 | if( !isset($options['methods']) ){
54 | $options['methods'] = 'POST, GET';
55 | }
56 |
57 | if( !isset($options['headers']) ){
58 | $options['headers'] = array();
59 | }
60 |
61 | header('Access-Control-Allow-Origin: ' . $options['origin']);
62 | header('Access-Control-Allow-Methods: ' . $options['methods']);
63 | header('Access-Control-Allow-Headers: ' . implode(', ', array_merge($options['headers'], array('X-Requested-With', 'Content-Range', 'Content-Disposition'))));
64 |
65 | if( !isset($options['cookie']) || $options['cookie'] ){
66 | header('Access-Control-Allow-Credentials: true');
67 | }
68 | }
69 |
70 |
71 | /**
72 | * Request header
73 | * @return array
74 | */
75 | public static function getRequestHeaders(){
76 | $headers = array();
77 |
78 | foreach( $_SERVER as $key => $value ){
79 | if( substr($key, 0, 5) == 'HTTP_' ){
80 | $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
81 | $headers[$header] = $value;
82 | }
83 | }
84 |
85 | return $headers;
86 | }
87 |
88 |
89 | /**
90 | * Retrieve File List
91 | * @return array
92 | */
93 | public static function getFiles(){
94 | $files = array();
95 |
96 | // http://www.php.net/manual/ru/reserved.variables.files.php#106558
97 | foreach( $_FILES as $firstNameKey => $arFileDescriptions ){
98 | foreach( $arFileDescriptions as $fileDescriptionParam => $mixedValue ){
99 | self::rRestructuringFilesArray($files, $firstNameKey, $_FILES[$firstNameKey][$fileDescriptionParam], $fileDescriptionParam);
100 | }
101 | }
102 |
103 | self::determineMimeType($files);
104 |
105 | return $files;
106 | }
107 |
108 |
109 | /**
110 | * Make server response
111 | * @param array $res
112 | * @param string [$jsonp]
113 | */
114 | public static function makeResponse(array $res, $jsonp = null){
115 | $body = $res['body'];
116 | $json = is_array($body) ? json_encode($body) : $body;
117 |
118 | $httpStatus = isset($res['status']) ? $res['status'] : self::OK;
119 | $httpStatusText = addslashes(isset($res['statusText']) ? $res['statusText'] : 'OK');
120 | $httpHeaders = isset($res['headers']) ? $res['headers'] : array();
121 |
122 | if( empty($jsonp) ){
123 | header("HTTP/1.1 $httpStatus $httpStatusText");
124 | $httpHeaders['Content-Type'] = 'application/json';
125 | foreach( $httpHeaders as $header => $value ){
126 | header("$header: $value");
127 | }
128 | echo $json;
129 | }
130 | else {
131 | $json = addslashes($json);
132 |
133 | echo <<
135 | (function (ctx, jsonp){
136 | 'use strict';
137 | var status = $httpStatus, statusText = "$httpStatusText", response = "$json";
138 | try {
139 | ctx[jsonp](status, statusText, response);
140 | } catch (e){
141 | var data = "{\"id\":\"$jsonp\",\"status\":"+status+",\"statusText\":\""+statusText+"\",\"response\":\""+response.replace(/\"/g, '\\\\\"')+"\"}";
142 | try {
143 | ctx.postMessage(data, document.referrer);
144 | } catch (e){}
145 | }
146 | })(window.parent, '$jsonp');
147 |
148 | END;
149 | }
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/server/ctrl.php:
--------------------------------------------------------------------------------
1 | $images
40 | , 'data' => array('_REQUEST' => $_REQUEST, '_FILES' => $files)
41 | );
42 |
43 |
44 | // Server response: "HTTP/1.1 200 OK"
45 | FileAPI::makeResponse(array(
46 | 'status' => FileAPI::OK
47 | , 'statusText' => 'OK'
48 | , 'body' => $json
49 | ), $jsonp);
50 | exit;
51 | }
52 |
53 |
54 |
55 |
56 | function fetchImages($files, &$images, $name = 'file'){
57 | if( isset($files['tmp_name']) ){
58 | $filename = $files['tmp_name'];
59 | list($mime) = explode(';', @mime_content_type($filename));
60 |
61 | if( strpos($mime, 'image') !== false ){
62 | $size = getimagesize($filename);
63 | $base64 = base64_encode(file_get_contents($filename));
64 |
65 | $images[$name] = array(
66 | 'width' => $size[0]
67 | , 'height' => $size[1]
68 | , 'mime' => $mime
69 | , 'size' => filesize($filename)
70 | , 'dataURL' => 'data:'. $mime .';base64,'. $base64
71 | );
72 | }
73 | }
74 | else {
75 | foreach( $files as $name => $file ){
76 | fetchImages($file, $images, $name);
77 | }
78 | }
79 | }
80 | ?>
81 |
--------------------------------------------------------------------------------
/statics/body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/statics/body.png
--------------------------------------------------------------------------------
/statics/body__top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/statics/body__top.png
--------------------------------------------------------------------------------
/statics/content.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/statics/content.png
--------------------------------------------------------------------------------
/statics/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/statics/logo.png
--------------------------------------------------------------------------------
/statics/watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/statics/watermark.png
--------------------------------------------------------------------------------
/support.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | FileAPI :: Support
14 |
15 |
22 |
23 |
24 |
25 | Single:
26 |
27 |
28 |
29 | Multiple:
30 |
31 |
32 |
33 |
34 |
35 |
36 |
49 |
50 |
51 |
52 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/tests/files/1px.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/1px.gif
--------------------------------------------------------------------------------
/tests/files/big.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/big.jpg
--------------------------------------------------------------------------------
/tests/files/dino.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/dino.png
--------------------------------------------------------------------------------
/tests/files/hello.txt:
--------------------------------------------------------------------------------
1 | Hello FileAPI!
2 |
--------------------------------------------------------------------------------
/tests/files/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/image.jpg
--------------------------------------------------------------------------------
/tests/files/lebowski.json:
--------------------------------------------------------------------------------
1 | {
2 | "adverts":[
3 | {
4 | "title":"Walter",
5 | "text":"You see what happens, Larry?",
6 | "url":"http://www.imdb.com/title/tt0118715/quotes"
7 | },
8 | {
9 | "title":"Walter",
10 | "text":"I don't roll on Shabbos!",
11 | "url":"http://www.imdb.com/title/tt0118715/quotes"
12 | },
13 | {
14 | "title":"Blond Thug",
15 | "text":"Where's the money, Lebowski?",
16 | "url":"http://www.imdb.com/title/tt0118715/quotes"
17 | },
18 | {
19 | "title":"Nihilist",
20 | "text":"We believe in nothing, Lebowski.",
21 | "url":"http://www.imdb.com/title/tt0118715/quotes"
22 | },
23 | {
24 | "title":"Walter",
25 | "text":"Is this your homework, Larry?",
26 | "url":"http://www.imdb.com/title/tt0118715/quotes"
27 | },
28 | {
29 | "title":"Nihilist",
30 | "text":"Ve vant ze money, Lebowski",
31 | "url":"http://www.imdb.com/title/tt0118715/quotes"
32 | }
33 | ],
34 |
35 | "sections":[
36 | {
37 | "id":1234,
38 | "title":"The Dude",
39 | "rip":0
40 | },
41 | {
42 | "id":2345,
43 | "title":"Walter Sobchak",
44 | "rip":0
45 | },
46 | {
47 | "id":3456,
48 | "title":"Donny",
49 | "rip":1
50 | },
51 | {
52 | "id":4567,
53 | "title":"Maude Lebowski",
54 | "rip":0
55 | },
56 | {
57 | "id":5678,
58 | "title":"The Big Lebowski",
59 | "rip":0
60 | },
61 | {
62 | "id":6789,
63 | "title":"Brandt",
64 | "rip":0
65 | },
66 | {
67 | "id":7890,
68 | "title":"Jesus Quintana",
69 | "rip":0
70 | }
71 | ],
72 |
73 | "total":654329,
74 | "online":[
75 | { "name":"true" },
76 | { "name":"false" },
77 | { "name":"short" },
78 | { "name":"long" },
79 | { "name":"apha" },
80 | { "name":"omega" },
81 | { "name":"drag" },
82 | { "name":"drop" },
83 | { "name":"make" },
84 | { "name":"clean" },
85 | { "name":"east" },
86 | { "name":"west" },
87 | { "name":"up" },
88 | { "name":"down" },
89 | { "name":"sun" },
90 | { "name":"rain" },
91 | { "name":"secondary" },
92 | { "name":"main" }
93 | ],
94 |
95 | "news":[
96 | {
97 | "time":"03:45",
98 | "id":987,
99 | "title":"The Stranger",
100 | "text":"See, they call Los Angeles the \"City Of Angels\"; but I didn't find it to be that, exactly. But I'll allow it as there are s ome nice folks there. 'Course I ain't never been to London, and I ain't never seen France. And I ain't never seen no queen in her damned undies, so the feller says. But I'll tell you what - after seeing Los Angeles, and this here story I'm about to unfold, well, I guess I seen somethin' every bit as stupefyin' as you'd seen in any of them other places. And in English , too. So I can die with a smile on my face, without feelin' like the good Lord gypped me. Now this here story I'm about to unfold took place in the early '90s - just about the time of our conflict with Sad'm and the I-raqis. I only mention it be cause sometimes there's a man..."
101 | },
102 | {
103 | "time":"03:48",
104 | "id":876,
105 | "title":"The Stranger",
106 | "text":"...I won't say a hero, 'cause, what's a hero? Sometimes, there's a man. And I'm talkin' about the Dude here - the Dude from Los Angeles. Sometimes, there's a man, well, he's the man for his time and place. He fits right in there. And that's the Dude. The Dude, from Los Angeles. And even if he's a lazy man - and the Dude was most certainly that. Quite possibly the laziest in all of Los Angeles County, which would place him high in the runnin' for laziest worldwide. Sometimes there's a man , sometimes, there's a man. Well, I lost my train of thought here. But... aw, hell. I've done introduced it enough."
107 | },
108 | {
109 | "time":"03:50",
110 | "id":765,
111 | "title":"Walter Sobchak",
112 | "text":"Donny was a good bowler, and a good man. He was one of us. He was a man who loved the outdoors... and bowling, and as a surfer he explored the beaches of Southern California, from La Jolla to Leo Carrillo and... up to... Pismo. He died, like so many young men of his generation, he died before his time. In your wisdom, Lord, you took him, as you took so many bright flowering young men at Khe Sanh, at Langdok, at Hill 364. These young men gave their lives. And so would Donny. Donny, who loved bowling. And so, Theodore Donald Karabotsos, in accordance with what we think your dying wishes might well have been, we commit your final mortal remains to the bosom of the Pacific Ocean, which you loved so well. Good night, sweet prince."
113 | },
114 | {
115 | "time":"03:52",
116 | "id":654,
117 | "title":"The Dude",
118 | "text":"God damn you Walter! You fuckin' asshole! Everything's a fuckin' travesty with you, man! And what was all that shit about Vietnam? What the FUCK, has anything got to do with Vietnam? What the fuck are you talking about?"
119 | },
120 | {
121 | "time":"03:57",
122 | "id":543,
123 | "title":"Jesus Quintana",
124 | "text":"What's this day of rest shit? What's this bullshit? I don't fuckin' care! It don't matter to Jesus. But you're not foolin'me, man. You might fool the fucks in the league office, but you don't fool Jesus. This bush league psyche-out stuff. Laughable, man - ha ha! I would have fucked you in the ass Saturday. I fuck you in the ass next Wednesday instead. Wooo! You gotadate Wednesday, baby!"
125 | },
126 | {
127 | "time":"03:59",
128 | "id":432,
129 | "title":"Jesus Quintana",
130 | "text":"Let me tell you something, pendejo. You pull any of your crazy shit with us, you flash a piece out on the lanes, I'll take it away from you, stick it up your ass and pull the fucking trigger 'til it goes \"click.\""
131 | },
132 | {
133 | "time":"04:01",
134 | "id":321,
135 | "title":"The Dude",
136 | "text":"Let me explain something to you. Um, I am not \"Mr. Lebowski\". You're Mr. Lebowski. I'm the Dude. So that's what you call me. You know, that or, uh, His Dudeness, or uh, Duder, or El Duderino if you're not into the whole brevity thing."
137 | }
138 | ]
139 | }
140 |
--------------------------------------------------------------------------------
/tests/files/samples/chrome-dino-180deg-50x50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/chrome-dino-180deg-50x50.png
--------------------------------------------------------------------------------
/tests/files/samples/chrome-dino-50x50.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/chrome-dino-50x50.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/chrome-dino-90deg-100x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/chrome-dino-90deg-100x100.png
--------------------------------------------------------------------------------
/tests/files/samples/chrome-dino-custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/chrome-dino-custom.png
--------------------------------------------------------------------------------
/tests/files/samples/chrome-image-auto-100x100.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/chrome-image-auto-100x100.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/firefox-dino-180deg-50x50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-dino-180deg-50x50.png
--------------------------------------------------------------------------------
/tests/files/samples/firefox-dino-50x50.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-dino-50x50.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/firefox-dino-90deg-100x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-dino-90deg-100x100.png
--------------------------------------------------------------------------------
/tests/files/samples/firefox-dino-custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-dino-custom.png
--------------------------------------------------------------------------------
/tests/files/samples/firefox-image-auto-100x100.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-image-auto-100x100.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/firefox-vintage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/firefox-vintage.png
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-dino-180deg-50x50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-dino-180deg-50x50.png
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-dino-50x50.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-dino-50x50.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-dino-90deg-100x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-dino-90deg-100x100.png
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-dino-custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-dino-custom.png
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-image-auto-100x100.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-image-auto-100x100.jpeg
--------------------------------------------------------------------------------
/tests/files/samples/phantomjs-vintage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/phantomjs-vintage.png
--------------------------------------------------------------------------------
/tests/files/samples/safari-dino-90deg-100x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mailru/FileAPI/5b50e8ed012e089eb578e586d860a6fd035e16d8/tests/files/samples/safari-dino-90deg-100x100.png
--------------------------------------------------------------------------------
/tests/flash.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | FileAPI :: Tests
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
112 |
113 |