Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / examples / image-organizer / SWFUpload / plugins / swfupload.speed.js
1 /*!
2  * Ext JS Library 3.0.3
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /*\r
8         Speed Plug-in\r
9         \r
10         Features:\r
11                 *Adds several properties to the 'file' object indicated upload speed, time left, upload time, etc.\r
12                         - currentSpeed -- String indicating the upload speed, bytes per second\r
13                         - averageSpeed -- Overall average upload speed, bytes per second\r
14                         - movingAverageSpeed -- Speed over averaged over the last several measurements, bytes per second\r
15                         - timeRemaining -- Estimated remaining upload time in seconds\r
16                         - timeElapsed -- Number of seconds passed for this upload\r
17                         - percentUploaded -- Percentage of the file uploaded (0 to 100)\r
18                         - sizeUploaded -- Formatted size uploaded so far, bytes\r
19                 \r
20                 *Adds setting 'moving_average_history_size' for defining the window size used to calculate the moving average speed.\r
21                 \r
22                 *Adds several Formatting functions for formatting that values provided on the file object.\r
23                         - SWFUpload.speed.formatBPS(bps) -- outputs string formatted in the best units (Gbps, Mbps, Kbps, bps)\r
24                         - SWFUpload.speed.formatTime(seconds) -- outputs string formatted in the best units (x Hr y M z S)\r
25                         - SWFUpload.speed.formatSize(bytes) -- outputs string formatted in the best units (w GB x MB y KB z B )\r
26                         - SWFUpload.speed.formatPercent(percent) -- outputs string formatted with a percent sign (x.xx %)\r
27                         - SWFUpload.speed.formatUnits(baseNumber, divisionArray, unitLabelArray, fractionalBoolean)\r
28                                 - Formats a number using the division array to determine how to apply the labels in the Label Array\r
29                                 - factionalBoolean indicates whether the number should be returned as a single fractional number with a unit (speed)\r
30                                     or as several numbers labeled with units (time)\r
31         */\r
32 \r
33 var SWFUpload;\r
34 if (typeof(SWFUpload) === "function") {\r
35         SWFUpload.speed = {};\r
36         \r
37         SWFUpload.prototype.initSettings = (function (oldInitSettings) {\r
38                 return function () {\r
39                         if (typeof(oldInitSettings) === "function") {\r
40                                 oldInitSettings.call(this);\r
41                         }\r
42                         \r
43                         this.ensureDefault = function (settingName, defaultValue) {\r
44                                 this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];\r
45                         };\r
46 \r
47                         // List used to keep the speed stats for the files we are tracking\r
48                         this.fileSpeedStats = {};\r
49                         this.speedSettings = {};\r
50 \r
51                         this.ensureDefault("moving_average_history_size", "10");\r
52                         \r
53                         this.speedSettings.user_file_queued_handler = this.settings.file_queued_handler;\r
54                         this.speedSettings.user_file_queue_error_handler = this.settings.file_queue_error_handler;\r
55                         this.speedSettings.user_upload_start_handler = this.settings.upload_start_handler;\r
56                         this.speedSettings.user_upload_error_handler = this.settings.upload_error_handler;\r
57                         this.speedSettings.user_upload_progress_handler = this.settings.upload_progress_handler;\r
58                         this.speedSettings.user_upload_success_handler = this.settings.upload_success_handler;\r
59                         this.speedSettings.user_upload_complete_handler = this.settings.upload_complete_handler;\r
60                         \r
61                         this.settings.file_queued_handler = SWFUpload.speed.fileQueuedHandler;\r
62                         this.settings.file_queue_error_handler = SWFUpload.speed.fileQueueErrorHandler;\r
63                         this.settings.upload_start_handler = SWFUpload.speed.uploadStartHandler;\r
64                         this.settings.upload_error_handler = SWFUpload.speed.uploadErrorHandler;\r
65                         this.settings.upload_progress_handler = SWFUpload.speed.uploadProgressHandler;\r
66                         this.settings.upload_success_handler = SWFUpload.speed.uploadSuccessHandler;\r
67                         this.settings.upload_complete_handler = SWFUpload.speed.uploadCompleteHandler;\r
68                         \r
69                         delete this.ensureDefault;\r
70                 };\r
71         })(SWFUpload.prototype.initSettings);\r
72 \r
73         \r
74         SWFUpload.speed.fileQueuedHandler = function (file) {\r
75                 if (typeof this.speedSettings.user_file_queued_handler === "function") {\r
76                         file = SWFUpload.speed.extendFile(file);\r
77                         \r
78                         return this.speedSettings.user_file_queued_handler.call(this, file);\r
79                 }\r
80         };\r
81         \r
82         SWFUpload.speed.fileQueueErrorHandler = function (file, errorCode, message) {\r
83                 if (typeof this.speedSettings.user_file_queue_error_handler === "function") {\r
84                         file = SWFUpload.speed.extendFile(file);\r
85                         \r
86                         return this.speedSettings.user_file_queue_error_handler.call(this, file, errorCode, message);\r
87                 }\r
88         };\r
89 \r
90         SWFUpload.speed.uploadStartHandler = function (file) {\r
91                 if (typeof this.speedSettings.user_upload_start_handler === "function") {\r
92                         file = SWFUpload.speed.extendFile(file, this.fileSpeedStats);\r
93                         return this.speedSettings.user_upload_start_handler.call(this, file);\r
94                 }\r
95         };\r
96         \r
97         SWFUpload.speed.uploadErrorHandler = function (file, errorCode, message) {\r
98                 file = SWFUpload.speed.extendFile(file, this.fileSpeedStats);\r
99                 SWFUpload.speed.removeTracking(file, this.fileSpeedStats);\r
100 \r
101                 if (typeof this.speedSettings.user_upload_error_handler === "function") {\r
102                         return this.speedSettings.user_upload_error_handler.call(this, file, errorCode, message);\r
103                 }\r
104         };\r
105         SWFUpload.speed.uploadProgressHandler = function (file, bytesComplete, bytesTotal) {\r
106                 this.updateTracking(file, bytesComplete);\r
107                 file = SWFUpload.speed.extendFile(file, this.fileSpeedStats);\r
108 \r
109                 if (typeof this.speedSettings.user_upload_progress_handler === "function") {\r
110                         return this.speedSettings.user_upload_progress_handler.call(this, file, bytesComplete, bytesTotal);\r
111                 }\r
112         };\r
113         \r
114         SWFUpload.speed.uploadSuccessHandler = function (file, serverData) {\r
115                 if (typeof this.speedSettings.user_upload_success_handler === "function") {\r
116                         file = SWFUpload.speed.extendFile(file, this.fileSpeedStats);\r
117                         return this.speedSettings.user_upload_success_handler.call(this, file, serverData);\r
118                 }\r
119         };\r
120         SWFUpload.speed.uploadCompleteHandler = function (file) {\r
121                 file = SWFUpload.speed.extendFile(file, this.fileSpeedStats);\r
122                 SWFUpload.speed.removeTracking(file, this.fileSpeedStats);\r
123 \r
124                 if (typeof this.speedSettings.user_upload_complete_handler === "function") {\r
125                         return this.speedSettings.user_upload_complete_handler.call(this, file);\r
126                 }\r
127         };\r
128         \r
129         // Private: extends the file object with the speed plugin values\r
130         SWFUpload.speed.extendFile = function (file, trackingList) {\r
131                 var tracking;\r
132                 \r
133                 if (trackingList) {\r
134                         tracking = trackingList[file.id];\r
135                 }\r
136                 \r
137                 if (tracking) {\r
138                         file.currentSpeed = tracking.currentSpeed;\r
139                         file.averageSpeed = tracking.averageSpeed;\r
140                         file.movingAverageSpeed = tracking.movingAverageSpeed;\r
141                         file.timeRemaining = tracking.timeRemaining;\r
142                         file.timeElapsed = tracking.timeElapsed;\r
143                         file.percentUploaded = tracking.percentUploaded;\r
144                         file.sizeUploaded = tracking.bytesUploaded;\r
145 \r
146                 } else {\r
147                         file.currentSpeed = 0;\r
148                         file.averageSpeed = 0;\r
149                         file.movingAverageSpeed = 0;\r
150                         file.timeRemaining = 0;\r
151                         file.timeElapsed = 0;\r
152                         file.percentUploaded = 0;\r
153                         file.sizeUploaded = 0;\r
154                 }\r
155                 \r
156                 return file;\r
157         };\r
158         \r
159         // Private: Updates the speed tracking object, or creates it if necessary\r
160         SWFUpload.prototype.updateTracking = function (file, bytesUploaded) {\r
161                 var tracking = this.fileSpeedStats[file.id];\r
162                 if (!tracking) {\r
163                         this.fileSpeedStats[file.id] = tracking = {};\r
164                 }\r
165                 \r
166                 // Sanity check inputs\r
167                 bytesUploaded = bytesUploaded || tracking.bytesUploaded || 0;\r
168                 if (bytesUploaded < 0) {\r
169                         bytesUploaded = 0;\r
170                 }\r
171                 if (bytesUploaded > file.size) {\r
172                         bytesUploaded = file.size;\r
173                 }\r
174                 \r
175                 var tickTime = (new Date()).getTime();\r
176                 if (!tracking.startTime) {\r
177                         tracking.startTime = (new Date()).getTime();\r
178                         tracking.lastTime = tracking.startTime;\r
179                         tracking.currentSpeed = 0;\r
180                         tracking.averageSpeed = 0;\r
181                         tracking.movingAverageSpeed = 0;\r
182                         tracking.movingAverageHistory = [];\r
183                         tracking.timeRemaining = 0;\r
184                         tracking.timeElapsed = 0;\r
185                         tracking.percentUploaded = bytesUploaded / file.size;\r
186                         tracking.bytesUploaded = bytesUploaded;\r
187                 } else if (tracking.startTime > tickTime) {\r
188                         this.debug("When backwards in time");\r
189                 } else {\r
190                         // Get time and deltas\r
191                         var now = (new Date()).getTime();\r
192                         var lastTime = tracking.lastTime;\r
193                         var deltaTime = now - lastTime;\r
194                         var deltaBytes = bytesUploaded - tracking.bytesUploaded;\r
195                         \r
196                         if (deltaBytes === 0 || deltaTime === 0) {\r
197                                 return tracking;\r
198                         }\r
199                         \r
200                         // Update tracking object\r
201                         tracking.lastTime = now;\r
202                         tracking.bytesUploaded = bytesUploaded;\r
203                         \r
204                         // Calculate speeds\r
205                         tracking.currentSpeed = (deltaBytes * 8 ) / (deltaTime / 1000);\r
206                         tracking.averageSpeed = (tracking.bytesUploaded * 8) / ((now - tracking.startTime) / 1000);\r
207 \r
208                         // Calculate moving average\r
209                         tracking.movingAverageHistory.push(tracking.currentSpeed);\r
210                         if (tracking.movingAverageHistory.length > this.settings.moving_average_history_size) {\r
211                                 tracking.movingAverageHistory.shift();\r
212                         }\r
213                         \r
214                         tracking.movingAverageSpeed = SWFUpload.speed.calculateMovingAverage(tracking.movingAverageHistory);\r
215                         \r
216                         // Update times\r
217                         tracking.timeRemaining = (file.size - tracking.bytesUploaded) * 8 / tracking.movingAverageSpeed;\r
218                         tracking.timeElapsed = (now - tracking.startTime) / 1000;\r
219                         \r
220                         // Update percent\r
221                         tracking.percentUploaded = (tracking.bytesUploaded / file.size * 100);\r
222                 }\r
223                 \r
224                 return tracking;\r
225         };\r
226         SWFUpload.speed.removeTracking = function (file, trackingList) {\r
227                 try {\r
228                         trackingList[file.id] = null;\r
229                         delete trackingList[file.id];\r
230                 } catch (ex) {\r
231                 }\r
232         };\r
233         \r
234         SWFUpload.speed.formatUnits = function (baseNumber, unitDivisors, unitLabels, singleFractional) {\r
235                 var i, unit, unitDivisor, unitLabel;\r
236 \r
237                 if (baseNumber === 0) {\r
238                         return "0 " + unitLabels[unitLabels.length - 1];\r
239                 }\r
240                 \r
241                 if (singleFractional) {\r
242                         unit = baseNumber;\r
243                         unitLabel = unitLabels.length >= unitDivisors.length ? unitLabels[unitDivisors.length - 1] : "";\r
244                         for (i = 0; i < unitDivisors.length; i++) {\r
245                                 if (baseNumber >= unitDivisors[i]) {\r
246                                         unit = (baseNumber / unitDivisors[i]).toFixed(2);\r
247                                         unitLabel = unitLabels.length >= i ? " " + unitLabels[i] : "";\r
248                                         break;\r
249                                 }\r
250                         }\r
251                         \r
252                         return unit + unitLabel;\r
253                 } else {\r
254                         var formattedStrings = [];\r
255                         var remainder = baseNumber;\r
256                         \r
257                         for (i = 0; i < unitDivisors.length; i++) {\r
258                                 unitDivisor = unitDivisors[i];\r
259                                 unitLabel = unitLabels.length > i ? " " + unitLabels[i] : "";\r
260                                 \r
261                                 unit = remainder / unitDivisor;\r
262                                 if (i < unitDivisors.length -1) {\r
263                                         unit = Math.floor(unit);\r
264                                 } else {\r
265                                         unit = unit.toFixed(2);\r
266                                 }\r
267                                 if (unit > 0) {\r
268                                         remainder = remainder % unitDivisor;\r
269                                         \r
270                                         formattedStrings.push(unit + unitLabel);\r
271                                 }\r
272                         }\r
273                         \r
274                         return formattedStrings.join(" ");\r
275                 }\r
276         };\r
277         \r
278         SWFUpload.speed.formatBPS = function (baseNumber) {\r
279                 var bpsUnits = [1073741824, 1048576, 1024, 1], bpsUnitLabels = ["Gbps", "Mbps", "Kbps", "bps"];\r
280                 return SWFUpload.speed.formatUnits(baseNumber, bpsUnits, bpsUnitLabels, true);\r
281         \r
282         };\r
283         SWFUpload.speed.formatTime = function (baseNumber) {\r
284                 var timeUnits = [86400, 3600, 60, 1], timeUnitLabels = ["d", "h", "m", "s"];\r
285                 return SWFUpload.speed.formatUnits(baseNumber, timeUnits, timeUnitLabels, false);\r
286         \r
287         };\r
288         SWFUpload.speed.formatBytes = function (baseNumber) {\r
289                 var sizeUnits = [1073741824, 1048576, 1024, 1], sizeUnitLabels = ["GB", "MB", "KB", "bytes"];\r
290                 return SWFUpload.speed.formatUnits(baseNumber, sizeUnits, sizeUnitLabels, true);\r
291         \r
292         };\r
293         SWFUpload.speed.formatPercent = function (baseNumber) {\r
294                 return baseNumber.toFixed(2) + " %";\r
295         };\r
296         \r
297         SWFUpload.speed.calculateMovingAverage = function (history) {\r
298                 var vals = [], size, sum = 0.0, mean = 0.0, varianceTemp = 0.0, variance = 0.0, standardDev = 0.0;\r
299                 var i;\r
300                 var mSum = 0, mCount = 0;\r
301                 \r
302                 size = history.length;\r
303                 \r
304                 // Check for sufficient data\r
305                 if (size >= 8) {\r
306                         // Clone the array and Calculate sum of the values \r
307                         for (i = 0; i < size; i++) {\r
308                                 vals[i] = history[i];\r
309                                 sum += vals[i];\r
310                         }\r
311 \r
312                         mean = sum / size;\r
313 \r
314                         // Calculate variance for the set\r
315                         for (i = 0; i < size; i++) {\r
316                                 varianceTemp += Math.pow((vals[i] - mean), 2);\r
317                         }\r
318 \r
319                         variance = varianceTemp / size;\r
320                         standardDev = Math.sqrt(variance);\r
321                         \r
322                         //Standardize the Data\r
323                         for (i = 0; i < size; i++) {\r
324                                 vals[i] = (vals[i] - mean) / standardDev;\r
325                         }\r
326 \r
327                         // Calculate the average excluding outliers\r
328                         var deviationRange = 2.0;\r
329                         for (i = 0; i < size; i++) {\r
330                                 \r
331                                 if (vals[i] <= deviationRange && vals[i] >= -deviationRange) {\r
332                                         mCount++;\r
333                                         mSum += history[i];\r
334                                 }\r
335                         }\r
336                         \r
337                 } else {\r
338                         // Calculate the average (not enough data points to remove outliers)\r
339                         mCount = size;\r
340                         for (i = 0; i < size; i++) {\r
341                                 mSum += history[i];\r
342                         }\r
343                 }\r
344 \r
345                 return mSum / mCount;\r
346         };\r
347         \r
348 }