-
Notifications
You must be signed in to change notification settings - Fork 324
/
Copy pathdpdb.js
179 lines (155 loc) · 5.62 KB
/
dpdb.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
/*
* Copyright 2015 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Offline cache of the DPDB, to be used until we load the online one (and
// as a fallback in case we can't load the online one).
var DPDB_CACHE = require('./dpdb-cache.js');
var Util = require('../util.js');
// Online DPDB URL.
var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json';
/**
* Calculates device parameters based on the DPDB (Device Parameter Database).
* Initially, uses the cached DPDB values.
*
* If fetchOnline == true, then this object tries to fetch the online version
* of the DPDB and updates the device info if a better match is found.
* Calls the onDeviceParamsUpdated callback when there is an update to the
* device information.
*/
function Dpdb(fetchOnline, onDeviceParamsUpdated) {
// Start with the offline DPDB cache while we are loading the real one.
this.dpdb = DPDB_CACHE;
// Calculate device params based on the offline version of the DPDB.
this.recalculateDeviceParams_();
// XHR to fetch online DPDB file, if requested.
if (fetchOnline) {
// Set the callback.
this.onDeviceParamsUpdated = onDeviceParamsUpdated;
var xhr = new XMLHttpRequest();
var obj = this;
xhr.open('GET', ONLINE_DPDB_URL, true);
xhr.addEventListener('load', function() {
obj.loading = false;
if (xhr.status >= 200 && xhr.status <= 299) {
// Success.
obj.dpdb = JSON.parse(xhr.response);
obj.recalculateDeviceParams_();
} else {
// Error loading the DPDB.
console.error('Error loading online DPDB!');
}
});
xhr.send();
}
}
// Returns the current device parameters.
Dpdb.prototype.getDeviceParams = function() {
return this.deviceParams;
};
// Recalculates this device's parameters based on the DPDB.
Dpdb.prototype.recalculateDeviceParams_ = function() {
var newDeviceParams = this.calcDeviceParams_();
if (newDeviceParams) {
this.deviceParams = newDeviceParams;
// Invoke callback, if it is set.
if (this.onDeviceParamsUpdated) {
this.onDeviceParamsUpdated(this.deviceParams);
}
} else {
console.error('Failed to recalculate device parameters.');
}
};
// Returns a DeviceParams object that represents the best guess as to this
// device's parameters. Can return null if the device does not match any
// known devices.
Dpdb.prototype.calcDeviceParams_ = function() {
var db = this.dpdb; // shorthand
if (!db) {
console.error('DPDB not available.');
return null;
}
if (db.format != 1) {
console.error('DPDB has unexpected format version.');
return null;
}
if (!db.devices || !db.devices.length) {
console.error('DPDB does not have a devices section.');
return null;
}
// Get the actual user agent and screen dimensions in pixels.
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
var width = Util.getScreenWidth();
var height = Util.getScreenHeight();
if (!db.devices) {
console.error('DPDB has no devices section.');
return null;
}
for (var i = 0; i < db.devices.length; i++) {
var device = db.devices[i];
if (!device.rules) {
console.warn('Device[' + i + '] has no rules section.');
continue;
}
if (device.type != 'ios' && device.type != 'android') {
console.warn('Device[' + i + '] has invalid type.');
continue;
}
// See if this device is of the appropriate type.
if (Util.isIOS() != (device.type == 'ios')) continue;
// See if this device matches any of the rules:
var matched = false;
for (var j = 0; j < device.rules.length; j++) {
var rule = device.rules[j];
if (this.matchRule_(rule, userAgent, width, height)) {
matched = true;
break;
}
}
if (!matched) continue;
// device.dpi might be an array of [ xdpi, ydpi] or just a scalar.
var xdpi = device.dpi[0] || device.dpi;
var ydpi = device.dpi[1] || device.dpi;
return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
}
console.warn('No DPDB device match.');
return null;
};
Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) {
// We can only match 'ua' and 'res' rules, not other types like 'mdmh'
// (which are meant for native platforms).
if (!rule.ua && !rule.res) return false;
// If our user agent string doesn't contain the indicated user agent string,
// the match fails.
if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
// If the rule specifies screen dimensions that don't correspond to ours,
// the match fails.
if (rule.res) {
if (!rule.res[0] || !rule.res[1]) return false;
var resX = rule.res[0];
var resY = rule.res[1];
// Compare min and max so as to make the order not matter, i.e., it should
// be true that 640x480 == 480x640.
if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) ||
(Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) {
return false;
}
}
return true;
}
function DeviceParams(params) {
this.xdpi = params.xdpi;
this.ydpi = params.ydpi;
this.bevelMm = params.bevelMm;
}
module.exports = Dpdb;