Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / image_loader / image_loader.js
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8  * Loads and resizes an image.
9  * @constructor
10  */
11 function ImageLoader() {
12   /**
13    * Persistent cache object.
14    * @type {Cache}
15    * @private
16    */
17   this.cache_ = new Cache();
18
19   /**
20    * Manages pending requests and runs them in order of priorities.
21    * @type {Worker}
22    * @private
23    */
24   this.worker_ = new Worker();
25
26   // Grant permissions to all volumes, initialize the cache and then start the
27   // worker.
28   chrome.fileBrowserPrivate.getVolumeMetadataList(function(volumeMetadataList) {
29     var initPromises = volumeMetadataList.map(function(volumeMetadata) {
30       var requestPromise = new Promise(function(callback) {
31         chrome.fileBrowserPrivate.requestFileSystem(
32             volumeMetadata.volumeId,
33             callback);
34       });
35       return requestPromise;
36     });
37     initPromises.push(new Promise(this.cache_.initialize.bind(this.cache_)));
38
39     // After all initializatino promises are done, start the worker.
40     Promise.all(initPromises).then(this.worker_.start.bind(this.worker_));
41
42     // Listen for mount events, and grant permissions to volumes being mounted.
43     chrome.fileBrowserPrivate.onMountCompleted.addListener(
44         function(event) {
45           if (event.eventType == 'mount' && event.status == 'success') {
46             chrome.fileBrowserPrivate.requestFileSystem(
47                 event.volumeMetadata.volumeId, function() {});
48           }
49         });
50   }.bind(this));
51
52   // Listen for incoming requests.
53   chrome.extension.onMessageExternal.addListener(function(request,
54                                                           sender,
55                                                           sendResponse) {
56     if (ImageLoader.ALLOWED_CLIENTS.indexOf(sender.id) !== -1) {
57       // Sending a response may fail if the receiver already went offline.
58       // This is not an error, but a normal and quite common situation.
59       var failSafeSendResponse = function(response) {
60         try {
61           sendResponse(response);
62         }
63         catch (e) {
64           // Ignore the error.
65         }
66       };
67       return this.onMessage_(sender.id, request, failSafeSendResponse);
68     }
69   }.bind(this));
70 }
71
72 /**
73  * List of extensions allowed to perform image requests.
74  *
75  * @const
76  * @type {Array.<string>}
77  */
78 ImageLoader.ALLOWED_CLIENTS =
79     ['hhaomjibdihmijegdhdafkllkbggdgoj'];  // File Manager's extension id.
80
81 /**
82  * Handles a request. Depending on type of the request, starts or stops
83  * an image task.
84  *
85  * @param {string} senderId Sender's extension id.
86  * @param {Object} request Request message as a hash array.
87  * @param {function} callback Callback to be called to return response.
88  * @return {boolean} True if the message channel should stay alive until the
89  *     callback is called.
90  * @private
91  */
92 ImageLoader.prototype.onMessage_ = function(senderId, request, callback) {
93   var requestId = senderId + ':' + request.taskId;
94   if (request.cancel) {
95     // Cancel a task.
96     this.worker_.remove(requestId);
97     return false;  // No callback calls.
98   } else {
99     // Create a request task and add it to the worker (queue).
100     var requestTask = new Request(requestId, this.cache_, request, callback);
101     this.worker_.add(requestTask);
102     return true;  // Request will call the callback.
103   }
104 };
105
106 /**
107  * Returns the singleton instance.
108  * @return {ImageLoader} ImageLoader object.
109  */
110 ImageLoader.getInstance = function() {
111   if (!ImageLoader.instance_)
112     ImageLoader.instance_ = new ImageLoader();
113   return ImageLoader.instance_;
114 };
115
116 /**
117  * Checks if the options contain any image processing.
118  *
119  * @param {number} width Source width.
120  * @param {number} height Source height.
121  * @param {Object} options Resizing options as a hash array.
122  * @return {boolean} True if yes, false if not.
123  */
124 ImageLoader.shouldProcess = function(width, height, options) {
125   var targetDimensions = ImageLoader.resizeDimensions(width, height, options);
126
127   // Dimensions has to be adjusted.
128   if (targetDimensions.width != width || targetDimensions.height != height)
129     return true;
130
131   // Orientation has to be adjusted.
132   if (options.orientation)
133     return true;
134
135   // No changes required.
136   return false;
137 };
138
139 /**
140  * Calculates dimensions taking into account resize options, such as:
141  * - scale: for scaling,
142  * - maxWidth, maxHeight: for maximum dimensions,
143  * - width, height: for exact requested size.
144  * Returns the target size as hash array with width, height properties.
145  *
146  * @param {number} width Source width.
147  * @param {number} height Source height.
148  * @param {Object} options Resizing options as a hash array.
149  * @return {Object} Dimensions, eg. {width: 100, height: 50}.
150  */
151 ImageLoader.resizeDimensions = function(width, height, options) {
152   var sourceWidth = width;
153   var sourceHeight = height;
154
155   // Flip dimensions for odd orientation values: 1 (90deg) and 3 (270deg).
156   if (options.orientation && options.orientation % 2) {
157     sourceWidth = height;
158     sourceHeight = width;
159   }
160
161   var targetWidth = sourceWidth;
162   var targetHeight = sourceHeight;
163
164   if ('scale' in options) {
165     targetWidth = sourceWidth * options.scale;
166     targetHeight = sourceHeight * options.scale;
167   }
168
169   if (options.maxWidth &&
170       targetWidth > options.maxWidth) {
171       var scale = options.maxWidth / targetWidth;
172       targetWidth *= scale;
173       targetHeight *= scale;
174   }
175
176   if (options.maxHeight &&
177       targetHeight > options.maxHeight) {
178       var scale = options.maxHeight / targetHeight;
179       targetWidth *= scale;
180       targetHeight *= scale;
181   }
182
183   if (options.width)
184     targetWidth = options.width;
185
186   if (options.height)
187     targetHeight = options.height;
188
189   targetWidth = Math.round(targetWidth);
190   targetHeight = Math.round(targetHeight);
191
192   return {width: targetWidth, height: targetHeight};
193 };
194
195 /**
196  * Performs resizing of the source image into the target canvas.
197  *
198  * @param {HTMLCanvasElement|Image} source Source image or canvas.
199  * @param {HTMLCanvasElement} target Target canvas.
200  * @param {Object} options Resizing options as a hash array.
201  */
202 ImageLoader.resize = function(source, target, options) {
203   var targetDimensions = ImageLoader.resizeDimensions(
204       source.width, source.height, options);
205
206   target.width = targetDimensions.width;
207   target.height = targetDimensions.height;
208
209   // Default orientation is 0deg.
210   var orientation = options.orientation || 0;
211
212   // For odd orientation values: 1 (90deg) and 3 (270deg) flip dimensions.
213   var drawImageWidth;
214   var drawImageHeight;
215   if (orientation % 2) {
216     drawImageWidth = target.height;
217     drawImageHeight = target.width;
218   } else {
219     drawImageWidth = target.width;
220     drawImageHeight = target.height;
221   }
222
223   var targetContext = target.getContext('2d');
224   targetContext.save();
225   targetContext.translate(target.width / 2, target.height / 2);
226   targetContext.rotate(orientation * Math.PI / 2);
227   targetContext.drawImage(
228       source,
229       0, 0,
230       source.width, source.height,
231       -drawImageWidth / 2, -drawImageHeight / 2,
232       drawImageWidth, drawImageHeight);
233   targetContext.restore();
234 };