1 /**
  2  * @fileOverview BTM Browser/Rendering engine sniffer.
  3  * @author <a href="http://www.bobs-bits.com">Stephen Reay</a>
  4  * @author Browser detection based a <a href="http://www.quirksmode.org/js/detect.html">concept</a> by <a href="mailto:xyz@quirksmode.org">Peter-Paul Koch</a>
  5  */
  6 
  7 /**
  8  * @namespace Functionality and Properties for identifying/targetting Browser features, functionality and quirks
  9  */
 10 BTM.Browser = {};
 11 
 12 (function () {
 13 	var browserData = [
 14 		{
 15 			nameString: navigator.userAgent,
 16 			namesearch: "Chrome",
 17 			versionString: navigator.userAgent,
 18 			versionSearch: /Chrome\/([^\r\n\s]*)/,
 19 			identity: "Chrome"
 20 		},
 21 		{ 	nameString: navigator.userAgent,
 22 			nameSearch: "OmniWeb",
 23 			versionString: navigator.userAgent,
 24 			versionSearch: /OmniWeb\/v?([^\r\n\s]*)/,
 25 			identity: "OmniWeb"
 26 		},
 27 		{
 28 			nameString: navigator.vendor,
 29 			nameSearch: "Apple",
 30 			versionString: navigator.userAgent,
 31 			versionSearch: /(Safari|Version)\/([^\r\n\s]*)/,
 32 			versionPosition: 2,
 33 			versionMatrix: {
 34 				'85.5' : '1',
 35 				'85.5' : '1',
 36 				'85.7' : '1.0.2',
 37 				'85.8' : '1.0.3',
 38 				'85.8.1' : '1.0.3',
 39 				'100' : '1.1',
 40 				'100.1' : '1.1.1',
 41 				'125.7' : '1.2.2',
 42 				'125.8' : '1.2.2',
 43 				'125.9' : '1.2.3',
 44 				'125.11' : '1.2.4',
 45 				'125.12' : '1.2.4',
 46 				'312' : '1.3',
 47 				'312.3' : '1.3.1',
 48 				'312.3.1' : '1.3.1',
 49 				'312.5' : '1.3.2',
 50 				'312.6' : '1.3.2',
 51 				'412' : '2',
 52 				'412.2' : '2',
 53 				'412.2.2' : '2',
 54 				'412.5' : '2.0.1',
 55 				'416.12' : '2.0.2',
 56 				'416.13' : '2.0.2',
 57 				'417.8' : '2.0.3',
 58 				'417.9.2' : '2.0.3',
 59 				'417.9.3' : '2.0.3',
 60 				'419.3' : '2.0.4'
 61 			},
 62 			identity: "Safari"
 63 		},
 64 		{
 65 			nameProp: window.opera,
 66 			versionString: navigator.userAgent,
 67 			versionSearch: /Opera[\/ ]([^\r\n\s]*)/,
 68 			identity: "Opera"
 69 		},
 70 		{
 71 			nameString: navigator.vendor,
 72 			nameSearch: "iCab",
 73 			versionString: navigator.userAgent,
 74 			versionSearch: /iCab[\/ ]([^\r\n\s]*)/,
 75 			identity: "iCab"
 76 		},
 77 		{
 78 			nameString: navigator.vendor,
 79 			nameSearch: "KDE",
 80 			versionString: navigator.userAgent,
 81 			versionSearch: /Konqueror\/([^\r\n\s]*?);/,
 82 			identity: "Konqueror"
 83 		},
 84 		{
 85 			nameString: navigator.userAgent,
 86 			nameSearch: "Firefox",
 87 			versionString: navigator.userAgent,
 88 			versionSearch: /Firefox\/([^\r\n\s]*)/,
 89 			identity: "Firefox"
 90 		},
 91 		{
 92 			nameString: navigator.userAgent,
 93 			nameSearch: "Camino",
 94 			versionString: navigator.userAgent,
 95 			versionSearch: /Camino\/([^\r\n\s]*)/,
 96 			identity: "Camino"
 97 		},
 98 		{		// for newer Netscapes (6+)
 99 			nameString: navigator.userAgent,
100 			nameSearch: "Netscape",
101 			versionString: navigator.userAgent,
102 			versionSearch: /Netscape6?\/([^\r\n\s]*)|Navigator\/([^\r\n\s]*)/,
103 			identity: "Netscape"
104 		},
105 		{
106 			nameString: navigator.userAgent,
107 			nameSearch: "MSIE",
108 			versionString: navigator.userAgent,
109 			versionSearch: new RegExp('MSIE ([^\r\n\s]*);'),
110 			identity: "Explorer"
111 		},
112 		{
113 			nameString: navigator.userAgent,
114 			nameSearch: "Gecko",
115 			versionString: navigator.userAgent,
116 			versionSearch: /rv:([^\r\n\s]*)/,
117 			identity: "Mozilla"
118 		},
119 		{ 		// for older Netscapes (4-)
120 			nameString: navigator.userAgent,
121 			nameSearch: "Mozilla",
122 			versionString: navigator.userAgent,
123 			versionSearch: /Mozilla\/([^\r\n\s\-]*)/,
124 			identity: "Netscape"
125 		}
126 
127 	];
128 	BTM.log("Defined Browser checks: " + browserData.length);
129 
130 	var osData = [
131 		{
132 			nameString: navigator.platform,
133 			nameSearch: "Win",
134 			identity: "Windows"
135 		},
136 		{
137 			nameString: navigator.platform,
138 			nameSearch: "Mac",
139 			identity: "Mac"
140 		},
141 		{
142 			nameString: navigator.platform,
143 			nameSearch: "Linux",
144 			identity: "Linux"
145 		}
146 	
147 	];
148 	BTM.log("Defined Operating System checks: " + osData.length);
149 
150 	var engineData = [
151 		{
152 			nameString: navigator.userAgent,
153 			nameSearch: /AppleWebKit/,
154 			versionString: navigator.userAgent,
155 			versionSearch: /AppleWebKit\/([\d\.]*\+?)/,
156 			identity: "WebKit"
157 		},
158 		{
159 			nameString: navigator.userAgent,
160 			nameSearch: /Opera/,
161 			versionString: navigator.userAgent,
162 			versionSearch: /Opera[\/ ]([\d\.]*)/,
163 			identity: "Presto"
164 		},
165 		{
166 			nameString: navigator.userAgent,
167 			nameSearch: /iCab/,
168 			versionString: navigator.userAgent,
169 			versionSearch: /iCab[\/ ]([0-3][\d\.]*)/,
170 			identity: "iCab"
171 		},
172 		{
173 			nameString: navigator.userAgent,
174 			nameSearch: /KHTML/,
175 			versionString: navigator.userAgent,
176 			versionSearch: /KHTML\/([\d\.]*)/,
177 			identity: "KHTML"
178 		},
179 		{
180 			nameString: navigator.userAgent,
181 			nameSearch: /MSIE/,
182 			versionString: navigator.userAgent,
183 			versionSearch: /MSIE (.*?)\;/,
184 			identity: "Trident"
185 		},
186 		{
187 			nameString: navigator.userAgent,
188 			nameSearch: /Gecko/,
189 			versionString: navigator.userAgent,
190 			versionSearch: /rv:(.*?)\)/,
191 			identity: "Gecko"
192 		}
193 	];
194 	BTM.log("Defined Rendering Engine checks: " + engineData.length);
195 
196 
197 	var combos = [
198 		['name'],
199 		['name', '_', 'version'],
200 		['engine'],
201 		['engine', '_', 'engineVersion'],
202 		['platform']
203 /*
204 		['platform','_','name'],
205 		['platform','_','name','_','version'],
206 		['platform','_','engine'],
207 		['platform','_','engine','_','engineVersion']
208 */
209 	];
210 	BTM.log("Defined Browser/OS/Engine class combinations:" + combos.length);
211 
212 	var targets = {
213 		'width': [
214 			640,
215 			800,
216 			1024,
217 			1280,
218 			1680,
219 			1920,
220 			2560
221 		],
222 		'height' : [
223 			480,
224 			600,
225 			720,
226 			768,
227 			800,
228 			1024,
229 			1050,
230 			1080,
231 			1200,
232 			1400,
233 			1440,
234 			1536,
235 			1600
236 		]
237 	};
238 	
239 /* 	BTM.Browser.findDimensions = findDimensions; */
240 	function findDimensions(axis) {
241 		//Window size code from http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
242 		var dimensions = {};
243 		BTM.log("Finding Window dimension values");
244 		if (Object.isNumber(window.innerWidth)) {
245 			//Non-IE
246 			dimensions.width = window.innerWidth;
247 			dimensions.height = window.innerHeight;
248 		}
249 		else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
250 			//IE 6+ in 'standards compliant mode'
251 			dimensions.width = document.documentElement.clientWidth;
252 			dimensions.height = document.documentElement.clientHeight;
253 		}
254 		else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
255 			//IE 4 compatible
256 			dimensions.width = document.body.clientWidth;
257 			dimensions.height = document.body.clientHeight;
258 		}
259 		
260 		Object.update(BTM.Browser, dimensions);
261 		
262 		BTM.log("Window dimensions detected: " + dimensions.width + 'x' + dimensions.height);
263 		
264 		if (Object.isString(axis)) {
265 			return dimensions[axis];
266 		}
267 		else {
268 			return dimensions;
269 		}	
270 	}
271 
272 	function findName(data) {
273 		for (var i = 0; i < data.length; i++) {
274 			if (data[i].nameSearch && data[i].nameString) {
275 				var test = data[i].nameString.match(data[i].nameSearch);
276 				if (test) {
277 					return data[i].identity;
278 				}
279 			}
280 			else if (data[i].prop) {
281 				return data[i].identity;
282 			}
283 		}
284 	}
285 
286 	function findVersion(data) {
287 		for (var i = 0; i < data.length; i++) {
288 			var match = data[i].versionString.match(data[i].versionSearch);
289 			if (match) {
290 				var index = data[i].versionPosition || 1;
291 				if (data[i].versionMatrix && data[i].versionMatrix[match[index]]) {
292 					return data[i].versionMatrix[match[index]];
293 				}
294 				else {
295 					return match[index];
296 				}
297 			}
298 		}
299 	}
300 
301 	function makeCombo(data) {
302 		var str = '';
303 		for (var i = 0; i < data.length; i++) {
304 			if (BTM.Browser[data[i]]) {
305 				str += BTM.Browser[data[i]];
306 			}
307 			else {
308 				str += data[i];
309 			}
310 		}
311 		
312 		return str.replaceMulti(['\\.', '\\#', '\\!', '\\~'], '-').toLowerCase();
313 	}
314 
315 	function addBrowserClasses(data) {
316 		BTM.log('Adding Browser Targetting classes', data);
317 		for (var i = 0; i < data.length; i++) {
318 			var className = makeCombo(data[i]);
319 			BTM.DOM.addClass(document.body, className);
320 		}
321 	}
322 
323 
324 	function addResClasses(data) {
325 		BTM.log('Adding Resolution Targetting classes', data);
326 		for (var i = 0; i < data.length; i++) {
327 			var className = makeCombo(data[i]);
328 			BTM.DOM.addClass(document.body, className);
329 		}
330 	}
331 	
332 	function findMode() {
333 		BTM.log("Finding Browser mode", document.compatMode);
334 		if (document.compatMode === 'BackCompat') {
335 			return 'quirks';
336 		}
337 		else if (document.compatMode === 'CSS1Compat') {
338 			return 'strict';
339 		}
340 	}
341 	
342 	/**
343 	 * Convenience function to check a Browsers name/engine/version information
344 	 * @param {String} name the name of the Browser to check. Checks engine name first, then browser name
345 	 * @param {Number|String} [version] the version to check for. Checks against browser/engine version depending on what the name parameter matches. Checks against the version/versionString depending if it's a Number or String, respectively.
346 	 * @param {String} [mode='eq'] the mode to check the version. Accepted values are 'eq', 'lt', 'lte', 'gt' and 'gte' which are tested with the operators ===, <, <=, > and >= respectively
347 	 * @returns {Boolean} true or false, if supplied parameters match the current Browser environment
348 	 */
349 	BTM.Browser.is = function is(name, version, mode) {
350 		
351 		mode = mode || 'eq';
352 		version = version || false;
353 
354 		//Matching an engine name
355 		if (BTM.Browser.engine === name) {
356 			var ver = BTM.Browser.engineVersion;
357 			var verStr = BTM.Browser.engineVersionString;
358 		}
359 		// Matching a browser name
360 		else if (BTM.Browser.name === name) {
361 			var ver = BTM.Browser.version;
362 			var verStr = BTM.Browser.versionString;
363 		}
364 		// Can't match browser or engine name
365 		else {
366 			return false;
367 		}
368 		
369 		// Just checking a name
370 		if (version === false) {
371 			return true;
372 		}
373 		
374 		// Matching a version string
375 		if (Object.isString(version)) {
376 			return (verStr === version);
377 		}
378 		//Matching a version number
379 		else {
380 			switch(mode) {
381 				// Less than mode
382 				case 'lt':
383 					return ver < version;
384 					break;
385 				
386 				// Less than or equal mode
387 				case 'lte':
388 					return ver <= version;
389 					break;
390 
391 				// Greater than mode
392 				case 'gt':
393 					return ver > version;
394 					break;
395 				
396 				// Greater than or equal mode
397 				case 'gte':
398 					return ver >= version;
399 					break;
400 
401 				// Equals mode
402 				case 'eq':
403 				default:
404 					return (ver === version);
405 					break;
406 			}
407 		}
408 	};
409 
410 	
411 	BTM.log("Finding Browser/Engine/Platform information");
412 	
413 	/**
414 	 * The Browser name
415 	 * @type {String}
416 	 */
417 	BTM.Browser.name = findName(browserData);
418 	
419 	/**
420 	 * The Browser version string
421 	 * @type {String}
422 	 */
423 	BTM.Browser.versionString = findVersion(browserData);
424 	
425 	/**
426 	 * The Browser version
427 	 * @type {Number}
428 	 */
429 	BTM.Browser.version = parseFloat(BTM.Browser.versionString);
430 	
431 	/**
432 	 * The Browser Engine
433 	 * @type {String}
434 	 */
435 	BTM.Browser.engine = findName(engineData);
436 	
437 	/**
438 	 * The Browser Engine version string
439 	 * @type {String}
440 	 */
441 	BTM.Browser.engineVersionString = findVersion(engineData);
442 	
443 	/**
444 	 * The Browser Engine version
445 	 * @type {Number}
446 	 */
447 	BTM.Browser.engineVersion = parseFloat(BTM.Browser.engineVersionString);
448 	
449 	/**
450 	 * The Browser Platform (OS)
451 	 * @type {String}
452 	 */
453 	BTM.Browser.platform = findName(osData);
454 
455 	/**
456 	 * The Browser mode - 'quirks' or 'strict'
457 	 * @type {String}
458 	 */
459 	BTM.Browser.mode = findMode();
460 	
461 	BTM.log("Browser/Egnine/Platform information found", BTM.Browser);
462 	
463 	BTM.observe(document, 'DOMContentLoaded', addBrowserClasses.curry(combos), 0);
464 	BTM.observe(document, 'DOMContentLoaded', findDimensions, 0);
465 
466 	BTM.observe(window, 'resize', findDimensions, 0);
467 })();
468