Merge "Integrating mobile device detection class into MW core"
[lhc/web/wiklou.git] / includes / DeviceDetection.php
1 <?php
2 /**
3 * Device detection class
4 *
5 * Copyright © 2011 Patrick Reilly
6 * http://www.mediawiki.org/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * http://www.gnu.org/copyleft/gpl.html
22 *
23 * @file
24 */
25
26 /**
27 * Provides abstraction for a device.
28 * A device can select which format a request should receive and
29 * may be extended to provide access to particular device functionality.
30 * @since 1.20
31 */
32 class DeviceDetection {
33
34 /**
35 * @return array
36 */
37 public function getAvailableFormats() {
38 $formats = array (
39 'html' => array (
40 'view_format' => 'html',
41 'search_bar' => 'default',
42 'footmenu' => 'default',
43 'with_layout' => 'application',
44 'css_file_name' => 'default',
45 'supports_javascript' => false,
46 'supports_jquery' => false,
47 'disable_zoom' => true,
48 'parser' => 'html',
49 'disable_links' => true,
50 ),
51 'capable' => array (
52 'view_format' => 'html',
53 'search_bar' => 'default',
54 'footmenu' => 'default',
55 'with_layout' => 'application',
56 'css_file_name' => 'default',
57 'supports_javascript' => true,
58 'supports_jquery' => true,
59 'disable_zoom' => true,
60 'parser' => 'html',
61 'disable_links' => true,
62 ),
63 'webkit' => array (
64 'view_format' => 'html',
65 'search_bar' => 'webkit',
66 'footmenu' => 'default',
67 'with_layout' => 'application',
68 'css_file_name' => 'webkit',
69 'supports_javascript' => true,
70 'supports_jquery' => true,
71 'disable_zoom' => false,
72 'parser' => 'html',
73 'disable_links' => true,
74 ),
75 'ie' => array (
76 'view_format' => 'html',
77 'search_bar' => 'default',
78 'footmenu' => 'default',
79 'with_layout' => 'application',
80 'css_file_name' => 'default',
81 'supports_javascript' => true,
82 'supports_jquery' => true,
83 'disable_zoom' => false,
84 'parser' => 'html',
85 'disable_links' => true,
86 ),
87 'android' => array (
88 'view_format' => 'html',
89 'search_bar' => 'default',
90 'footmenu' => 'default',
91 'with_layout' => 'application',
92 'css_file_name' => 'android',
93 'supports_javascript' => true,
94 'supports_jquery' => true,
95 'disable_zoom' => false,
96 'parser' => 'html',
97 'disable_links' => true,
98 ),
99 'iphone' => array (
100 'view_format' => 'html',
101 'search_bar' => 'webkit',
102 'footmenu' => 'default',
103 'with_layout' => 'application',
104 'css_file_name' => 'iphone',
105 'supports_javascript' => true,
106 'supports_jquery' => true,
107 'disable_zoom' => false,
108 'parser' => 'html',
109 'disable_links' => true,
110 ),
111 'iphone2' => array (
112 'view_format' => 'html',
113 'search_bar' => 'default',
114 'footmenu' => 'default',
115 'with_layout' => 'application',
116 'css_file_name' => 'iphone2',
117 'supports_javascript' => true,
118 'supports_jquery' => true,
119 'disable_zoom' => true,
120 'parser' => 'html',
121 'disable_links' => true,
122 ),
123 'native_iphone' => array (
124 'view_format' => 'html',
125 'search_bar' => false,
126 'footmenu' => 'default',
127 'with_layout' => 'application',
128 'css_file_name' => 'default',
129 'supports_javascript' => true,
130 'supports_jquery' => true,
131 'disable_zoom' => false,
132 'parser' => 'html',
133 'disable_links' => false,
134 ),
135 'palm_pre' => array (
136 'view_format' => 'html',
137 'search_bar' => 'default',
138 'footmenu' => 'default',
139 'with_layout' => 'application',
140 'css_file_name' => 'palm_pre',
141 'supports_javascript' => true,
142 'supports_jquery' => false,
143 'disable_zoom' => true,
144 'parser' => 'html',
145 'disable_links' => true,
146 ),
147 'kindle' => array (
148 'view_format' => 'html',
149 'search_bar' => 'kindle',
150 'footmenu' => 'default',
151 'with_layout' => 'application',
152 'css_file_name' => 'kindle',
153 'supports_javascript' => false,
154 'supports_jquery' => false,
155 'disable_zoom' => true,
156 'parser' => 'html',
157 'disable_links' => true,
158 ),
159 'kindle2' => array (
160 'view_format' => 'html',
161 'search_bar' => 'kindle',
162 'footmenu' => 'default',
163 'with_layout' => 'application',
164 'css_file_name' => 'kindle',
165 'supports_javascript' => false,
166 'supports_jquery' => false,
167 'disable_zoom' => true,
168 'parser' => 'html',
169 'disable_links' => true,
170 ),
171 'blackberry' => array (
172 'view_format' => 'html',
173 'search_bar' => 'default',
174 'footmenu' => 'default',
175 'with_layout' => 'application',
176 'css_file_name' => 'blackberry',
177 'supports_javascript' => true,
178 'supports_jquery' => false,
179 'disable_zoom' => true,
180 'parser' => 'html',
181 'disable_links' => true,
182 ),
183 'blackberry-lt5' => array (
184 'view_format' => 'html',
185 'search_bar' => 'default',
186 'footmenu' => 'default',
187 'with_layout' => 'application',
188 'css_file_name' => 'blackberry',
189 'supports_javascript' => false,
190 'supports_jquery' => false,
191 'disable_zoom' => true,
192 'parser' => 'html',
193 'disable_links' => true,
194 ),
195 'netfront' => array (
196 'view_format' => 'html',
197 'search_bar' => 'simple',
198 'footmenu' => 'simple',
199 'with_layout' => 'application',
200 'css_file_name' => 'simple',
201 'supports_javascript' => false,
202 'supports_jquery' => false,
203 'disable_zoom' => true,
204 'parser' => 'html',
205 'disable_links' => true,
206 ),
207 'wap2' => array (
208 'view_format' => 'html',
209 'search_bar' => 'simple',
210 'footmenu' => 'simple',
211 'with_layout' => 'application',
212 'css_file_name' => 'simple',
213 'supports_javascript' => false,
214 'supports_jquery' => false,
215 'disable_zoom' => true,
216 'parser' => 'html',
217 'disable_links' => true,
218 ),
219 'psp' => array (
220 'view_format' => 'html',
221 'search_bar' => 'simple',
222 'footmenu' => 'simple',
223 'with_layout' => 'application',
224 'css_file_name' => 'psp',
225 'supports_javascript' => false,
226 'supports_jquery' => false,
227 'disable_zoom' => true,
228 'parser' => 'html',
229 'disable_links' => true,
230 ),
231 'ps3' => array (
232 'view_format' => 'html',
233 'search_bar' => 'simple',
234 'footmenu' => 'simple',
235 'with_layout' => 'application',
236 'css_file_name' => 'simple',
237 'supports_javascript' => false,
238 'supports_jquery' => false,
239 'disable_zoom' => true,
240 'parser' => 'html',
241 'disable_links' => true,
242 ),
243 'wii' => array (
244 'view_format' => 'html',
245 'search_bar' => 'wii',
246 'footmenu' => 'default',
247 'with_layout' => 'application',
248 'css_file_name' => 'wii',
249 'supports_javascript' => true,
250 'supports_jquery' => true,
251 'disable_zoom' => true,
252 'parser' => 'html',
253 'disable_links' => true,
254 ),
255 'operamini' => array (
256 'view_format' => 'html',
257 'search_bar' => 'simple',
258 'footmenu' => 'simple',
259 'with_layout' => 'application',
260 'css_file_name' => 'operamini',
261 'supports_javascript' => false,
262 'supports_jquery' => false,
263 'disable_zoom' => true,
264 'parser' => 'html',
265 'disable_links' => true,
266 ),
267 'operamobile' => array (
268 'view_format' => 'html',
269 'search_bar' => 'simple',
270 'footmenu' => 'simple',
271 'with_layout' => 'application',
272 'css_file_name' => 'operamobile',
273 'supports_javascript' => true,
274 'supports_jquery' => true,
275 'disable_zoom' => true,
276 'parser' => 'html',
277 'disable_links' => true,
278 ),
279 'nokia' => array (
280 'view_format' => 'html',
281 'search_bar' => 'webkit',
282 'footmenu' => 'default',
283 'with_layout' => 'application',
284 'css_file_name' => 'nokia',
285 'supports_javascript' => true,
286 'supports_jquery' => false,
287 'disable_zoom' => true,
288 'parser' => 'html',
289 'disable_links' => true,
290 ),
291 'wml' => array (
292 'view_format' => 'wml',
293 'search_bar' => 'wml',
294 'supports_javascript' => false,
295 'supports_jquery' => false,
296 'parser' => 'wml',
297 ),
298 );
299 return $formats;
300 }
301
302 /**
303 * @param $userAgent
304 * @param string $acceptHeader
305 * @return array
306 */
307 public function detectDevice( $userAgent, $acceptHeader = '' ) {
308 $formatName = $this->detectFormatName( $userAgent, $acceptHeader );
309 return $this->getDevice( $formatName );
310 }
311
312 /**
313 * @param $formatName
314 * @return array
315 */
316 public function getDevice( $formatName ) {
317 $format = $this->getAvailableFormats();
318 return ( isset( $format[$formatName] ) ) ? $format[$formatName] : array();
319 }
320
321 /**
322 * @param $userAgent string
323 * @param $acceptHeader string
324 * @return string
325 */
326 public function detectFormatName( $userAgent, $acceptHeader = '' ) {
327 $formatName = '';
328
329 if ( preg_match( '/Android/', $userAgent ) ) {
330 $formatName = 'android';
331 if ( strpos( $userAgent, 'Opera Mini' ) !== false ) {
332 $formatName = 'operamini';
333 }
334 } else if ( preg_match( '/MSIE 9.0/', $userAgent ) ||
335 preg_match( '/MSIE 8.0/', $userAgent ) ) {
336 $formatName = 'ie';
337 } else if( preg_match( '/MSIE/', $userAgent ) ) {
338 $formatName = 'html';
339 } else if ( strpos( $userAgent, 'Opera Mobi' ) !== false ) {
340 $formatName = 'operamobile';
341 } elseif ( preg_match( '/iPad.* Safari/', $userAgent ) ) {
342 $formatName = 'iphone';
343 } elseif ( preg_match( '/iPhone.* Safari/', $userAgent ) ) {
344 if ( strpos( $userAgent, 'iPhone OS 2' ) !== false ) {
345 $formatName = 'iphone2';
346 } else {
347 $formatName = 'iphone';
348 }
349 } elseif ( preg_match( '/iPhone/', $userAgent ) ) {
350 if ( strpos( $userAgent, 'Opera' ) !== false ) {
351 $formatName = 'operamini';
352 } else {
353 $formatName = 'native_iphone';
354 }
355 } elseif ( preg_match( '/WebKit/', $userAgent ) ) {
356 if ( preg_match( '/Series60/', $userAgent ) ) {
357 $formatName = 'nokia';
358 } elseif ( preg_match( '/webOS/', $userAgent ) ) {
359 $formatName = 'palm_pre';
360 } else {
361 $formatName = 'webkit';
362 }
363 } elseif ( preg_match( '/Opera/', $userAgent ) ) {
364 if ( strpos( $userAgent, 'Nintendo Wii' ) !== false ) {
365 $formatName = 'wii';
366 } elseif ( strpos( $userAgent, 'Opera Mini' ) !== false ) {
367 $formatName = 'operamini';
368 } elseif ( strpos( $userAgent, 'Opera Mobi' ) !== false ) {
369 $formatName = 'iphone';
370 } else {
371 $formatName = 'webkit';
372 }
373 } elseif ( preg_match( '/Kindle\/1.0/', $userAgent ) ) {
374 $formatName = 'kindle';
375 } elseif ( preg_match( '/Kindle\/2.0/', $userAgent ) ) {
376 $formatName = 'kindle2';
377 } elseif ( preg_match( '/Firefox/', $userAgent ) ) {
378 $formatName = 'capable';
379 } elseif ( preg_match( '/NetFront/', $userAgent ) ) {
380 $formatName = 'netfront';
381 } elseif ( preg_match( '/SEMC-Browser/', $userAgent ) ) {
382 $formatName = 'wap2';
383 } elseif ( preg_match( '/Series60/', $userAgent ) ) {
384 $formatName = 'wap2';
385 } elseif ( preg_match( '/PlayStation Portable/', $userAgent ) ) {
386 $formatName = 'psp';
387 } elseif ( preg_match( '/PLAYSTATION 3/', $userAgent ) ) {
388 $formatName = 'ps3';
389 } elseif ( preg_match( '/SAMSUNG/', $userAgent ) ) {
390 $formatName = 'capable';
391 } elseif ( preg_match( '/BlackBerry/', $userAgent ) ) {
392 if( preg_match( '/BlackBerry[^\/]*\/[1-4]\./', $userAgent ) ) {
393 $formatName = 'blackberry-lt5';
394 } else {
395 $formatName = 'blackberry';
396 }
397 }
398
399 if ( $formatName === '' ) {
400 if ( strpos( $acceptHeader, 'application/vnd.wap.xhtml+xml' ) !== false ) {
401 // Should be wap2
402 $formatName = 'html';
403 } elseif ( strpos( $acceptHeader, 'vnd.wap.wml' ) !== false ) {
404 $formatName = 'wml';
405 } else {
406 $formatName = 'html';
407 }
408 }
409 return $formatName;
410 }
411
412 /**
413 * @return array: List of all device-specific stylesheets
414 */
415 public function getCssFiles() {
416 $devices = $this->getAvailableFormats();
417 $files = array();
418 foreach ( $devices as $dev ) {
419 if ( isset( $dev['css_file_name'] ) ) {
420 $files[] = $dev['css_file_name'];
421 }
422 }
423 return array_unique( $files );
424 }
425 }