This repository has been archived by the owner on Nov 3, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
open.js
245 lines (212 loc) · 8.58 KB
/
open.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
window.addEventListener('localized', function() {
var activity; // The activity object we're handling
var activityData; // The data sent by the initiating app
var blob; // The blob we'll be displaying and maybe saving
var frame; // The MediaFrame that displays the image
var saved = false; // Did we save the file?
var storage; // The DeviceStorage object used for saving
var title; // What we call the image in the titlebar and banner
// Register a handler to receive the Activity object
navigator.mozSetMessageHandler('activity', handleOpenActivity);
function $(id) { return document.getElementById(id); }
// If the image is bigger than this, decoding it will take too much
// memory, and we don't want to cause an OOM, so we won't display it.
//
// XXX: see bug 847060: we ought to be able to handle images bigger
// than 5 megapixels. But I'm getting OOMs on 8mp images, so I'm
// keeping this small.
//
var MAX_IMAGE_SIZE = 5 * 1024 * 1024;
// If we can't figure out the image size in megapixels, then we have to base
// our decision whether or not to display it on the file size. Note that
// this is a very, very imperfect test. imagesize.js has code to determine
// the image size for jpeg, png and gif images, so we only have to use this
// file size test for other image formats.
var MAX_FILE_SIZE = .5 * 1024 * 1024;
function handleOpenActivity(request) {
activity = request;
activityData = activity.source.data;
// Set up the UI, if it is not already set up
if (!frame) {
// Hook up the buttons
$('back').addEventListener('click', done);
$('save').addEventListener('click', save);
// And register event handlers for gestures
frame = new MediaFrame($('frame'), false);
if (CONFIG_REQUIRED_EXIF_PREVIEW_WIDTH) {
frame.setMinimumPreviewSize(CONFIG_REQUIRED_EXIF_PREVIEW_WIDTH,
CONFIG_REQUIRED_EXIF_PREVIEW_HEIGHT);
}
var gestureDetector = new GestureDetector(frame.container);
gestureDetector.startDetecting();
frame.container.addEventListener('dbltap', handleDoubleTap);
frame.container.addEventListener('transform', handleTransform);
frame.container.addEventListener('pan', handlePan);
frame.container.addEventListener('swipe', handleSwipe);
window.addEventListener('resize', frame.resize.bind(frame));
// Report errors if we're passed an invalid image
frame.onerror = function invalid() {
displayError('imageinvalid');
};
}
// Display the filename in the header, if there was one
title = baseName(activityData.filename || '');
$('filename').textContent = title;
// Start off with the Save button hidden.
// We'll enable it below in the open() function if needed.
$('menu').hidden = true;
blob = activityData.blob;
open(blob);
}
// Display the specified blob, unless it is too big to display
function open(blob) {
// If the app that initiated this activity wants us to do allow the
// user to save this blob as a file, and if device storage is available
// and if there is enough free space, then display a save button.
if (activityData.allowSave && activityData.filename && checkFilename()) {
getStorageIfAvailable('pictures', blob.size, function(ds) {
storage = ds;
$('menu').hidden = false;
});
}
// Figure out how big (in pixels) the image is.
// For JPEG images, this also gets us the preview image if there is one.
getImageSize(blob, success, error);
// Called if we get an image size
function success(metadata) {
var pixels = metadata.width * metadata.height;
// If the image is too large, display an error
if (pixels > MAX_IMAGE_SIZE) {
displayError('imagetoobig');
return;
}
// If there was no EXIF preview, or if the image is not very big,
// display the full-size image.
if (!metadata.preview || pixels < 512 * 1024) {
frame.displayImage(blob,
metadata.width,
metadata.height,
null,
metadata.rotation,
metadata.mirrored);
}
else {
// If we found an EXIF preview, and can determine its size, then
// we can display it instead of the big image and save a lot of
// memory.
parseJPEGMetadata(blob.slice(metadata.preview.start,
metadata.preview.end,
'image/jpeg'),
function success(previewmetadata) {
// If we parsed the preview image, add its
// dimensions to the metdata.preview
// object, and then let the MediaFrame
// object display the preview instead of
// the full-size image.
metadata.preview.width = previewmetadata.width;
metadata.preview.height = previewmetadata.height;
frame.displayImage(blob,
metadata.width,
metadata.height,
metadata.preview,
metadata.rotation,
metadata.mirrored);
},
function error() {
// If we couldn't parse the preview image,
// just display full-size.
frame.displayImage(blob,
metadata.width,
metadata.height);
});
}
}
// Called when metadata parsing fails.
function error(msg) {
//
// This wasn't a JPEG, PNG, or GIF image.
//
// If the file size isn't too large, try to display it anyway,
// and then display an error message if the frame.onerror
// function gets called.
//
if (blob.size < MAX_FILE_SIZE) {
frame.displayImage(blob);
}
else {
displayError('imagetoobig');
}
}
}
function checkFilename() {
var dotIdx = activityData.filename.lastIndexOf('.');
if (dotIdx > -1) {
var ext = activityData.filename.substr(dotIdx + 1);
return MimeMapper.guessTypeFromExtension(ext) === blob.type;
} else {
return false;
}
}
function displayError(msgid) {
alert(navigator.mozL10n.get(msgid));
done();
}
function done() {
activity.postResult({ saved: saved });
activity = null;
}
function handleDoubleTap(e) {
var scale;
if (frame.fit.scale > frame.fit.baseScale)
scale = frame.fit.baseScale / frame.fit.scale;
else
scale = 2;
frame.zoom(scale, e.detail.clientX, e.detail.clientY, 200);
}
function handleTransform(e) {
frame.zoom(e.detail.relative.scale,
e.detail.midpoint.clientX,
e.detail.midpoint.clientY);
}
function handlePan(e) {
frame.pan(e.detail.relative.dx, e.detail.relative.dy);
}
function handleSwipe(e) {
var direction = e.detail.direction;
var velocity = e.detail.vy;
if (direction === 'down' && velocity > 2)
done();
}
function save() {
// Hide the menu that holds the save button: we can only save once
$('menu').hidden = true;
// XXX work around bug 870619
$('filename').textContent = $('filename').textContent;
getUnusedFilename(storage, activityData.filename, function(filename) {
var savereq = storage.addNamed(blob, filename);
savereq.onsuccess = function() {
// Remember that it has been saved so we can pass this back
// to the invoking app
saved = filename;
// And tell the user
showBanner(navigator.mozL10n.get('saved', { filename: title }));
};
savereq.onerror = function(e) {
// XXX we don't report this to the user because it is hard to
// localize.
console.error('Error saving', filename, e);
};
});
}
function showBanner(msg) {
$('message').textContent = msg;
$('banner').hidden = false;
setTimeout(function() {
$('banner').hidden = true;
}, 3000);
}
// Strip directories and just return the base filename
function baseName(filename) {
return filename.substring(filename.lastIndexOf('/') + 1);
}
});