1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Information about the browser and run-time environment.
 26  * @class
 27  */
 28 AjxEnv = function() {
 29 };
 30 
 31 //
 32 // Constants
 33 //
 34 
 35 /** User locale. */
 36 AjxEnv.DEFAULT_LOCALE = window.navigator.userLanguage || window.navigator.language || window.navigator.systemLanguage;
 37 
 38 //
 39 // Data
 40 //
 41 
 42 AjxEnv._inited = false;
 43 
 44 // NOTE: These are here so that the documentation tool can pick them up
 45 
 46 /** Gecko date. */
 47 AjxEnv.geckoDate;
 48 /** Mozilla version. */
 49 AjxEnv.mozVersion;
 50 /** WebKit version. */
 51 AjxEnv.webKitVersion;
 52 /** Trident version. */
 53 AjxEnv.tridentVersion;
 54 /** Macintosh. */
 55 AjxEnv.isMac;
 56 /** Windows. */
 57 AjxEnv.isWindows;
 58 /** Windows 64-bit. */
 59 AjxEnv.isWindows64;
 60 /** Linux. */
 61 AjxEnv.isLinux;
 62 /** Netscape Navigator compatible. */
 63 AjxEnv.isNav;
 64 /** Netscape Navigator version 4. */
 65 AjxEnv.isNav4;
 66 /** "legacy" Internet Explorer -- i.e. version 10 and earlier */
 67 AjxEnv.isIE;
 68 /** "modern" Internet Explorer -- i.e. version 11 and Edge */
 69 AjxEnv.isModernIE;
 70 
 71 
 72 AjxEnv.trueNs;
 73 
 74 
 75 /** Netscape Navigator version 6. */
 76 AjxEnv.isNav6;
 77 /** Netscape Navigator version 6 (or higher). */
 78 AjxEnv.isNav6up;
 79 /** Netscape Navigator version 7. */
 80 AjxEnv.isNav7;
 81 /** Internet Explorer version 3. */
 82 AjxEnv.isIE3;
 83 /** Internet Explorer version 4. */
 84 AjxEnv.isIE4;
 85 /** Internet Explorer version 4 (or higher). */
 86 AjxEnv.isIE4up;
 87 /** Internet Explorer version 5. */
 88 AjxEnv.isIE5;
 89 /** Internet Explorer version 5.5. */
 90 AjxEnv.isIE5_5;
 91 /** Internet Explorer version 5 (or higher). */
 92 AjxEnv.isIE5up;
 93 /** Internet Explorer version 5.5 (or higher). */
 94 AjxEnv.isIE5_5up;
 95 /** Internet Explorer version 6. */
 96 AjxEnv.isIE6;
 97 /** Internet Explorer version 6 (or higher). */
 98 AjxEnv.isIE6up;
 99 /** Internet Explorer version 7. */
100 AjxEnv.isIE7;
101 /** Internet Explorer version 7 (or higher). */
102 AjxEnv.isIE7up;
103 /** Internet Explorer version 8. */
104 AjxEnv.isIE8;
105 /** Internet Explorer version 8 (or higher). */
106 AjxEnv.isIE8up;
107 /** Internet Explorer version 9. */
108 AjxEnv.isIE9;
109 /** Internet Explorer version 9 (or higher). */
110 AjxEnv.isIE9up;
111 /** Internet Explorer version 10. */
112 AjxEnv.isIE10;
113 
114 AjxEnv.isNormalResolution;
115 AjxEnv.ieScaleFactor;
116 
117 
118 /** Mozilla Firefox. */
119 AjxEnv.isFirefox;
120 /** Mozilla Firefox version 1 (or higher). */
121 AjxEnv.isFirefox1up;
122 /** Mozilla Firefox version 1.5 (or higher). */
123 AjxEnv.isFirefox1_5up;
124 /** Mozilla Firefox version 3 (or higher). */
125 AjxEnv.isFirefox3up;
126 /** Mozilla Firefox version 3.6 (or higher). */
127 AjxEnv.isFirefox3_6up;
128 /** Mozilla Firefox version 4 (or higher). */
129 AjxEnv.isFirefox4up;
130 /** Mozilla. */
131 AjxEnv.isMozilla;
132 /** Mozilla version 1.4 (or higher). */
133 AjxEnv.isMozilla1_4up;
134 /** Safari. */
135 AjxEnv.isSafari;
136 /** Safari version 2. */
137 AjxEnv.isSafari2;
138 /** Safari version 3. */
139 AjxEnv.isSafari3;
140 /** Safari version 3 (or higher). */
141 AjxEnv.isSafari3up;
142 /** Safari version 4. */
143 AjxEnv.isSafari4;
144 /** Safari version 4 (or higher). */
145 AjxEnv.isSafari4up;
146 /** Safari version 5 (or higher). */
147 AjxEnv.isSafari5up;
148 /** Safari version 5.1 (or higher). */
149 AjxEnv.isSafari5_1up;
150 /** Camino. */
151 AjxEnv.isCamino;
152 /** Chrome. */
153 AjxEnv.isChrome;
154 AjxEnv.isChrome2up;
155 AjxEnv.isChrome7;
156 AjxEnv.isChrome10up;
157 /** Gecko-based. */
158 AjxEnv.isGeckoBased;
159 /** WebKit-based. */
160 AjxEnv.isWebKitBased;
161 /** Trident, i.e. the MSIE rendering engine */
162 AjxEnv.isTrident;
163 /** Opera. */
164 AjxEnv.isOpera;
165 
166 
167 AjxEnv.useTransparentPNGs;
168 
169 
170 /** Zimbra Desktop. */
171 AjxEnv.isDesktop;
172 /** Zimbra Desktop version 2 (or higher). */
173 AjxEnv.isDesktop2up;
174 /** Screen size is less then 800x600. */
175 AjxEnv.is800x600orLower;
176 /** Screen size is less then 1024x768. */
177 AjxEnv.is1024x768orLower;
178 
179 /** HTML5 Support **/
180 AjxEnv.supportsHTML5File;
181 
182 AjxEnv.supported = Modernizr;
183 
184 // Test for HTML's New Template Tag support
185 AjxEnv.supported.addTest('template', function() {
186 	return 'content' in document.createElement('template');
187 });
188 
189 /** Supports indirect global eval() **/
190 AjxEnv.indirectEvalIsGlobal;
191 (function(){
192 	// Feature detection to see if eval referenced by alias runs in global scope
193 	// See davidflanagan.com/2010/12/global-eval-in.html 
194 	AjxEnv.indirectEvalIsGlobal=false;
195 	var evl=window.eval;
196 	try{
197 		evl('__indirectEval=true');
198 		if('__indirectEval' in window){
199 			AjxEnv.indirectEvalIsGlobal=true;
200 			delete __indirectEval;
201 		}
202 	}catch(e){}
203 })();
204 
205 //
206 // Public functions
207 //
208 
209 AjxEnv.reset =
210 function() {
211 	AjxEnv.geckoDate = 0;
212 	AjxEnv.mozVersion = -1;
213 	AjxEnv.webKitVersion = -1;
214 	AjxEnv.isMac = false;
215 	AjxEnv.isWindows = false;
216 	AjxEnv.isWindows64 = false;
217 	AjxEnv.isLinux = false;
218 	AjxEnv.isNav  = false;
219 	AjxEnv.isIE = false;
220 	AjxEnv.isNav4 = false;
221 	AjxEnv.trueNs = true;
222 	AjxEnv.isNav6 = false;
223 	AjxEnv.isNav6up = false;
224 	AjxEnv.isNav7 = false;
225 	AjxEnv.isIE3 = false;
226 	AjxEnv.isIE4 = false;
227 	AjxEnv.isIE4up = false;
228 	AjxEnv.isIE5 = false;
229 	AjxEnv.isIE5_5 = false;
230 	AjxEnv.isIE5up = false;
231 	AjxEnv.isIE5_5up = false;
232 	AjxEnv.isIE6  = false;
233 	AjxEnv.isIE6up = false;
234 	AjxEnv.isIE7  = false;
235 	AjxEnv.isIE7up = false;
236 	AjxEnv.isIE8  = false;
237 	AjxEnv.isIE8up = false;
238 	AjxEnv.isIE9   = false;
239 	AjxEnv.isIE9up = false;
240 	AjxEnv.isIE10  = false;
241 	AjxEnv.isModernIE  = false;
242 	AjxEnv.isMSEdge = false;
243 	AjxEnv.isNormalResolution = false;
244 	AjxEnv.ieScaleFactor = 1;
245 	AjxEnv.isFirefox = false;
246 	AjxEnv.isFirefox1up = false;
247 	AjxEnv.isFirefox1_5up = false;
248 	AjxEnv.isFirefox3up = false;
249 	AjxEnv.isFirefox3_6up = false;
250 	AjxEnv.isFirefox4up = false;
251 	AjxEnv.isMozilla = false;
252 	AjxEnv.isMozilla1_4up = false;
253 	AjxEnv.isSafari = false;
254 	AjxEnv.isSafari2 = false;
255 	AjxEnv.isSafari3 = false;
256     AjxEnv.isSafari4 = false;
257 	AjxEnv.isSafari3up = false;
258 	AjxEnv.isSafari4up = false;
259     AjxEnv.isSafari5up = false;
260     AjxEnv.isSafari5_1up = false;
261 	AjxEnv.isSafari6up = false;
262 	AjxEnv.isCamino = false;
263 	AjxEnv.isChrome = false;
264     AjxEnv.isChrome2up = false;
265     AjxEnv.isChrome7 = false;
266     AjxEnv.isChrome10up = false;
267 	AjxEnv.isChrome19up = false;
268 	AjxEnv.isGeckoBased = false;
269 	AjxEnv.isWebKitBased = false;
270 	AjxEnv.isTrident = false;
271 	AjxEnv.isOpera = false;
272 	AjxEnv.useTransparentPNGs = false;
273 	AjxEnv.isDesktop = false;
274 	AjxEnv.isDesktop2up = false;
275 
276     //HTML5
277     AjxEnv.supportsHTML5File = false;
278 	AjxEnv.supportsPlaceholder = false;
279     AjxEnv.supportsCSS3RemUnits = false;
280 
281 	// screen resolution - ADD MORE RESOLUTION CHECKS AS NEEDED HERE:
282 	AjxEnv.is800x600orLower = screen && (screen.width <= 800 && screen.height <= 600);
283     AjxEnv.is1024x768orLower = screen && (screen.width <= 1024 && screen.height <= 768);
284 };
285 
286 AjxEnv.parseUA = 
287 function() {
288 	AjxEnv.reset();
289 
290 	var agt = navigator.userAgent.toLowerCase();
291 	var agtArr = agt.split(" ");
292 	var isSpoofer = false;
293 	var isWebTv = false;
294 	var isHotJava = false;
295 	var beginsWithMozilla = false;
296 	var isCompatible = false;
297 	var isTrident = false;
298 
299 	if (agtArr != null) {
300 		var browserVersion;
301 		var index = -1;
302 
303 		if ((index = agtArr[0].search(/^\s*mozilla\//))!= -1) {
304 			beginsWithMozilla = true;
305 			AjxEnv.browserVersion = parseFloat(agtArr[0].substring(index + 8));
306 			AjxEnv.isNav = true;
307 		}
308 
309 		var token;
310 		for (var i = 0; i < agtArr.length; ++i) {
311 			token = agtArr[i];
312 			if (token.indexOf('compatible') != -1 ) {
313 				isCompatible = true;
314 				AjxEnv.isNav = false;
315 			} else if ((token.indexOf('opera')) != -1) {
316 				AjxEnv.isOpera = true;
317 				AjxEnv.isNav = false;
318 				browserVersion = parseFloat(agtArr[i+1]);
319 			} else if ((token.indexOf('spoofer')) != -1) {
320 				isSpoofer = true;
321 				AjxEnv.isNav = false;
322 			} else if ((token.indexOf('webtv')) != -1) {
323 				isWebTv = true;
324 				AjxEnv.isNav = false;
325 			} else if ((token.indexOf('hotjava')) != -1) {
326 				isHotJava = true;
327 				AjxEnv.isNav = false;
328 			} else if ((index = token.indexOf('msie')) != -1) {
329 				AjxEnv.isIE = true;
330 				browserVersion = parseFloat(agtArr[i+1]);
331 			} else if ((index = token.indexOf('trident/')) != -1) {
332 				AjxEnv.isTrident = true;
333 				AjxEnv.tridentVersion = parseFloat(token.substr(index + 8));
334 			} else if ((index = token.indexOf('edge/')) != -1) {
335 				AjxEnv.isMSEdge = true;
336 				browserVersion = parseFloat(token.substr(index + 5));
337 				AjxEnv.isSafari = false;
338 				AjxEnv.isChrome = false;
339 				AjxEnv.isWebKitBased = false;
340 			} else if ((index = token.indexOf('gecko/')) != -1) {
341 				AjxEnv.isGeckoBased = true;
342 				AjxEnv.geckoDate = parseFloat(token.substr(index + 6));
343 			} else if ((index = token.indexOf('applewebkit/')) != -1) {
344 				AjxEnv.isWebKitBased = true;
345 				AjxEnv.webKitVersion = parseFloat(token.substr(index + 12));
346 			} else if ((index = token.indexOf('rv:')) != -1) {
347 				AjxEnv.mozVersion = parseFloat(token.substr(index + 3));
348 				browserVersion = AjxEnv.mozVersion;
349 			} else if ((index = token.indexOf('firefox/')) != -1) {
350 				AjxEnv.isFirefox = true;
351 				browserVersion = parseFloat(token.substr(index + 8));
352 			} else if ((index = token.indexOf('prism')) != -1) {
353 				AjxEnv.isPrism = true;
354 			} else if ((index = token.indexOf('camino/')) != -1) {
355 				AjxEnv.isCamino = true;
356 				browserVersion = parseFloat(token.substr(index + 7));
357 			} else if ((index = token.indexOf('netscape6/')) != -1) {
358 				AjxEnv.trueNs = true;
359 				browserVersion = parseFloat(token.substr(index + 10));
360 			} else if ((index = token.indexOf('netscape/')) != -1) {
361 				AjxEnv.trueNs = true;
362 				browserVersion = parseFloat(token.substr(index + 9));
363 			} else if ((index = token.indexOf('safari/')) != -1) {
364 				AjxEnv.isSafari = true;
365 			} else if ((index = token.indexOf('chrome/')) != -1) {
366 				AjxEnv.isChrome = true;
367 				browserVersion = parseFloat(token.substr(index + 7));
368 			} else if (index = token.indexOf('version/') != -1) {
369 				// this is how safari sets browser version
370 				browserVersion = parseFloat(token.substr(index + 7));
371 			} else if (token.indexOf('windows') != -1) {
372 				AjxEnv.isWindows = true;
373 			} else if (token.indexOf('win64') != -1) {
374 				AjxEnv.isWindows64 = true;
375 			} else if ((token.indexOf('macintosh') != -1) ||
376 					   (token.indexOf('mac_') != -1)) {
377 				AjxEnv.isMac = true;
378 			} else if (token.indexOf('linux') != -1) {
379 				AjxEnv.isLinux = true;
380 			} else if ((index = token.indexOf('zdesktop/')) != -1) {
381 				AjxEnv.isDesktop = true;
382 				browserVersion = parseFloat(token.substr(index + 9));
383 			}
384 		}
385 		AjxEnv.browserVersion = browserVersion;
386 
387 		// Note: Opera and WebTV spoof Navigator. We do strict client detection.
388 		AjxEnv.isNav 			= (beginsWithMozilla && !isSpoofer && !isCompatible && !AjxEnv.isOpera && !isWebTv && !isHotJava && !AjxEnv.isSafari);
389 		AjxEnv.isIE				= (AjxEnv.isIE && !AjxEnv.isOpera);
390 		AjxEnv.isNav4			= (AjxEnv.isNav && (browserVersion == 4) && (!AjxEnv.isIE));
391 		AjxEnv.isNav6			= (AjxEnv.isNav && AjxEnv.trueNs && (browserVersion >= 6.0 && browserVersion < 7.0));
392 		AjxEnv.isNav6up 		= (AjxEnv.isNav && AjxEnv.trueNs && (browserVersion >= 6.0));
393 		AjxEnv.isNav7 			= (AjxEnv.isNav && AjxEnv.trueNs && (browserVersion >= 7.0 && browserVersion < 8.0));
394 		AjxEnv.isIE3 			= (AjxEnv.isIE && browserVersion <  4.0);
395 		AjxEnv.isIE4			= (AjxEnv.isIE && browserVersion >= 4.0 && browserVersion < 5.0);
396 		AjxEnv.isIE4up			= (AjxEnv.isIE && browserVersion >= 4.0);
397 		AjxEnv.isIE5			= (AjxEnv.isIE && browserVersion >= 5.0 && browserVersion < 6.0);
398 		AjxEnv.isIE5_5			= (AjxEnv.isIE && browserVersion == 5.5);
399 		AjxEnv.isIE5up			= (AjxEnv.isIE && browserVersion >= 5.0);
400 		AjxEnv.isIE5_5up		= (AjxEnv.isIE && browserVersion >= 5.5);
401 		AjxEnv.isIE6			= (AjxEnv.isIE && browserVersion >= 6.0 && browserVersion < 7.0);
402 		AjxEnv.isIE6up			= (AjxEnv.isIE && browserVersion >= 6.0);
403 		AjxEnv.isIE7			= (AjxEnv.isIE && browserVersion >= 7.0 && browserVersion < 8.0);
404 		AjxEnv.isIE7up			= (AjxEnv.isIE && browserVersion >= 7.0);
405 		AjxEnv.isIE8			= (AjxEnv.isIE && browserVersion >= 8.0 && browserVersion < 9.0);
406 		AjxEnv.isIE8up			= (AjxEnv.isIE && browserVersion >= 8.0);
407 		AjxEnv.isIE9			= (AjxEnv.isIE && browserVersion >= 9.0 && browserVersion < 10.0);
408 		AjxEnv.isIE9up			= (AjxEnv.isIE && browserVersion >= 9.0);
409 		AjxEnv.isIE10			= (AjxEnv.isIE && browserVersion >= 10.0 && browserVersion < 11.0);
410 		// IE11
411 		AjxEnv.isModernIE	   = (!AjxEnv.isIE && AjxEnv.mozVersion >= 11.0 && AjxEnv.tridentVersion >= 7.0);
412 		// IE12
413 		AjxEnv.isModernIE	   = AjxEnv.isModernIE || (!AjxEnv.isIE && AjxEnv.isMSEdge && browserVersion >= 12.0);
414 		if (AjxEnv.isModernIE) {
415 			AjxEnv.isSafari = false;
416 			AjxEnv.isChrome = false;
417 			AjxEnv.isIE12 = (browserVersion >= 12.0);
418 		}
419 
420 
421 		AjxEnv.isMozilla		= ((AjxEnv.isNav && AjxEnv.mozVersion && AjxEnv.isGeckoBased && (AjxEnv.geckoDate != 0)));
422 		AjxEnv.isMozilla1_4up	= (AjxEnv.isMozilla && (AjxEnv.mozVersion >= 1.4));
423 		AjxEnv.isFirefox 		= ((AjxEnv.isMozilla && AjxEnv.isFirefox));
424 		AjxEnv.isFirefox1up		= (AjxEnv.isFirefox && browserVersion >= 1.0);
425 		AjxEnv.isFirefox1_5up	= (AjxEnv.isFirefox && browserVersion >= 1.5);
426 		AjxEnv.isFirefox2_0up	= (AjxEnv.isFirefox && browserVersion >= 2.0);
427 		AjxEnv.isFirefox3up		= (AjxEnv.isFirefox && browserVersion >= 3.0);
428 		AjxEnv.isFirefox3_5up	= (AjxEnv.isFirefox && browserVersion >= 3.5);
429 		AjxEnv.isFirefox3_6up	= (AjxEnv.isFirefox && browserVersion >= 3.6);
430 		AjxEnv.isFirefox4up		= (AjxEnv.isFirefox && browserVersion >= 4.0);
431 		AjxEnv.isSafari2		= (AjxEnv.isSafari && browserVersion >= 2.0 && browserVersion < 3.0);
432 		AjxEnv.isSafari3		= (AjxEnv.isSafari && browserVersion >= 3.0 && browserVersion < 4.0) || AjxEnv.isChrome;
433         AjxEnv.isSafari4        = (AjxEnv.isSafari && browserVersion >= 4.0);
434 		AjxEnv.isSafari3up		= (AjxEnv.isSafari && browserVersion >= 3.0) || AjxEnv.isChrome;
435 		AjxEnv.isSafari4up		= (AjxEnv.isSafari && browserVersion >= 4.0) || AjxEnv.isChrome;
436         AjxEnv.isSafari5up	    = (AjxEnv.isSafari && browserVersion >= 5.0) || AjxEnv.isChrome;
437         AjxEnv.isSafari5_1up	= (AjxEnv.isSafari && browserVersion >= 5.1) || AjxEnv.isChrome;
438 		AjxEnv.isSafari6up      = AjxEnv.isSafari && browserVersion >= 6.0;
439 		AjxEnv.isDesktop2up		= (AjxEnv.isDesktop && browserVersion >= 2.0);
440         AjxEnv.isChrome2up		= (AjxEnv.isChrome && browserVersion >= 2.0);
441         AjxEnv.isChrome7		= (AjxEnv.isChrome && browserVersion >= 7.0);
442         AjxEnv.isChrome10up		= (AjxEnv.isChrome && browserVersion >= 10.0);
443 		AjxEnv.isChrome19up		= (AjxEnv.isChrome && browserVersion >= 19.0);
444 
445 		AjxEnv.browser = "[unknown]";
446 		if (AjxEnv.isOpera) 				{	AjxEnv.browser = "OPERA";	}
447 		else if (AjxEnv.isChrome)			{	AjxEnv.browser = "GC" + browserVersion;	}
448 		else if (AjxEnv.isSafari)			{	AjxEnv.browser = "SAF" + browserVersion; }
449 		else if (AjxEnv.isCamino)			{	AjxEnv.browser = "CAM";		}
450 		else if (isWebTv)					{	AjxEnv.browser = "WEBTV";	}
451 		else if (isHotJava)					{	AjxEnv.browser = "HOTJAVA";	}
452 		else if (AjxEnv.isFirefox)			{	AjxEnv.browser = "FF" + browserVersion; }
453 		else if (AjxEnv.isPrism)			{	AjxEnv.browser = "PRISM";	}
454 		else if (AjxEnv.isNav7)				{	AjxEnv.browser = "NAV7";	}
455 		else if (AjxEnv.isNav6)				{	AjxEnv.browser = "NAV6";	}
456 		else if (AjxEnv.isNav4)				{	AjxEnv.browser = "NAV4";	}
457 		else if (AjxEnv.isIE)				{	AjxEnv.browser = "IE" + browserVersion; }
458 		else if (AjxEnv.isModernIE)			{	AjxEnv.browser = "IE" + browserVersion; }
459 		else if (AjxEnv.isDesktop)			{	AjxEnv.browser = "ZD" + browserVersion; }
460 
461 		AjxEnv.platform = "[unknown]";
462 		if (AjxEnv.isWindows)				{	AjxEnv.platform = "Win";	}
463 		else if (AjxEnv.isMac)				{	AjxEnv.platform = "Mac";	}
464 		else if (AjxEnv.isLinux)			{	AjxEnv.platform = "Linux";	}
465 	}
466 
467 	// setup some global setting we can check for high resolution
468 	if (AjxEnv.isIE) {
469 		AjxEnv.isNormalResolution = true;
470 		AjxEnv.ieScaleFactor = screen.deviceXDPI / screen.logicalXDPI;
471 		if (AjxEnv.ieScaleFactor > 1) {
472 			AjxEnv.isNormalResolution = false;
473 		}
474 	}
475 
476 	AjxEnv._inited = !AjxEnv.isIE;
477 
478 	// test for safari nightly
479 	if (AjxEnv.isSafari) {
480 		var webkit = AjxEnv.getWebkitVersion();
481 		AjxEnv.isSafariNightly = (webkit && webkit['is_nightly']);
482 		// if not safari v3 or the nightly, assume we're dealing with v2  :/
483 		AjxEnv.isSafari2 = !AjxEnv.isSafari3 && !AjxEnv.isSafariNightly;
484 	}
485 
486     //HTML5
487     AjxEnv.supportsHTML5File = !!( window.FileReader || AjxEnv.isChrome || AjxEnv.isSafari6up );
488     AjxEnv.supportsPlaceholder 	= 'placeholder' in document.createElement('INPUT');
489 
490     try {
491         // IE8 doesn't support REM units
492         var div = document.createElement('div');
493         div.style.fontSize = '1rem';
494         AjxEnv.supportsCSS3RemUnits = (div.style.fontSize == '1rem');
495     } catch (e) {
496         AjxEnv.supportsCSS3RemUnits = false;
497     }
498 };
499 
500 // code provided by webkit authors to determine if nightly browser
501 AjxEnv.getWebkitVersion =
502 function() {
503 
504 	var webkit_version;
505 	var regex = new RegExp("\\(.*\\) AppleWebKit/(.*) \\((.*)");
506 	var matches = regex.exec(navigator.userAgent);
507 	if (matches) {
508 		var version = matches[1];
509 		var bits = version.split(".");
510 		var is_nightly = (version[version.length - 1] == "+");
511 		var minor = is_nightly ? "+" : parseInt(bits[1]);
512 		// If minor is Not a Number (NaN) return an empty string
513 		if (isNaN(minor)) minor = "";
514 
515 		webkit_version = {major:parseInt(bits[0]), minor:minor, is_nightly:is_nightly};
516 	}
517 	return webkit_version || {};
518 };
519 
520 
521 AjxEnv.parseUA();
522 
523 AjxEnv.isOfflineSupported = (AjxEnv.isFirefox || AjxEnv.isChrome) && AjxEnv.supported.localstorage && AjxEnv.supported.applicationcache && AjxEnv.supported.indexeddb && AjxEnv.supported.template;
524 
525 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
526 /*
527 if ( !Function.prototype.bind ) {
528   Function.prototype.bind = function( obj ) {
529     var slice = [].slice,
530         args = slice.call(arguments, 1),
531         self = this,
532         nop = function () {},
533         bound = function () {
534           return self.apply( this instanceof nop ? this : ( obj || {} ),
535                               args.concat( slice.call(arguments) ) );   
536         };
537     nop.prototype = self.prototype;
538     bound.prototype = new nop();
539     return bound;
540   };
541 }
542 */
543 
544 // An alternative, simpler implementation. Not sure whether it does everything that the above version does,
545 // but it should work fine as a basic closure-style callback.
546 if (!Function.prototype.bind) {
547 	Function.prototype.bind = function(thisObj) {
548 		var that = this;
549 		var args;
550                 
551 		if (arguments.length > 1) {
552 			// optimization: create the extra array object only if needed. 
553 			args = Array.prototype.slice.call(arguments, 1);
554 		}
555                 
556 		return function () {
557 			var allArgs = args;
558 
559 			// optimization: concat array only if needed
560 			if (arguments.length > 0) {
561 				allArgs = (args && args.length) ? args.concat(Array.prototype.slice.call(arguments)) : arguments;
562 			}
563 
564 			// for some reason, IE does not like the undefined allArgs hence the below condition.
565 			return allArgs ? that.apply(thisObj, allArgs) : that.apply(thisObj);
566 		};
567 	};
568 }
569 
570 /**
571  * This should be a temporary hack as we transition from AjxCallback to bind(). Rather
572  * than change hundreds of call sites with 'callback.run()' to see if the callback is
573  * an AjxCallback or a closure, add a run() method to Function which just invokes the
574  * closure.
575  */
576 Function.prototype.run = function() {
577 	return this.apply(this, arguments);
578 };
579 
580 /**
581  * Polyfill for Object.getPrototypeOf -- currently only required by IE8.
582  *
583  * Written by John Resig: http://ejohn.org/blog/objectgetprototypeof/
584  */
585 if (typeof Object.getPrototypeOf !== "function") {
586 	if (typeof "test".__proto__ === "object") {
587 		Object.getPrototypeOf = function(object) {
588 			return object.__proto__;
589 		};
590 	} else {
591 		Object.getPrototypeOf = function(object) {
592 			// May break if the constructor has been tampered with
593 			return object.constructor.prototype;
594 		};
595 	}
596 }
597 
598 /**
599  * Polyfill for Object.keys -- currently only required by IE8.
600  *
601  * From Mozilla:
602  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
603  */
604 if (typeof Object.keys !== "function") {
605 	Object.keys = (function() {
606 		'use strict';
607 		var hasOwnProperty = Object.prototype.hasOwnProperty,
608 			hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
609 			dontEnums = [
610 				'toString',
611 				'toLocaleString',
612 				'valueOf',
613 				'hasOwnProperty',
614 				'isPrototypeOf',
615 				'propertyIsEnumerable',
616 				'constructor'
617 			],
618 			dontEnumsLength = dontEnums.length;
619 
620 		return function(obj) {
621 			if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
622 				throw new TypeError('Object.keys called on non-object');
623 			}
624 
625 			var result = [], prop, i;
626 
627 			for (prop in obj) {
628 				if (hasOwnProperty.call(obj, prop)) {
629 					result.push(prop);
630 				}
631 			}
632 
633 			if (hasDontEnumBug) {
634 				for (i = 0; i < dontEnumsLength; i++) {
635 					if (hasOwnProperty.call(obj, dontEnums[i])) {
636 						result.push(dontEnums[i]);
637 					}
638 				}
639 			}
640 			return result;
641 		};
642 	}());
643 }
644 
645 /**
646  * Polyfill for String.startsWith -- currently required by Safari, Opera, and IE.
647  */
648 if (typeof String.prototype.startsWith != 'function') {
649 	String.prototype.startsWith = function(searchString, position) {
650 		position = position || 0;
651 		return this.indexOf(searchString, position) === position;
652 	};
653 }
654 
655 /**
656  * Polyfill for Array.prototype.indexOf -- currently only required by IE8.
657  *
658  * From Mozilla:
659  * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
660  */
661 if (!Array.prototype.indexOf) {
662 	Array.prototype.indexOf = function(searchElement, fromIndex) {
663 
664 		var k;
665 
666 		// 1. Let O be the result of calling ToObject passing
667 		//    the this value as the argument.
668 		if (this == null) {
669 			throw new TypeError('"this" is null or not defined');
670 		}
671 
672 		var O = Object(this);
673 
674 		// 2. Let lenValue be the result of calling the Get
675 		//    internal method of O with the argument "length".
676 		// 3. Let len be ToUint32(lenValue).
677 		var len = O.length >>> 0;
678 
679 		// 4. If len is 0, return -1.
680 		if (len === 0) {
681 			return -1;
682 		}
683 
684 		// 5. If argument fromIndex was passed let n be
685 		//    ToInteger(fromIndex); else let n be 0.
686 		var n = +fromIndex || 0;
687 
688 		if (Math.abs(n) === Infinity) {
689 			n = 0;
690 		}
691 
692 		// 6. If n >= len, return -1.
693 		if (n >= len) {
694 			return -1;
695 		}
696 
697 		// 7. If n >= 0, then Let k be n.
698 		// 8. Else, n<0, Let k be len - abs(n).
699 		//    If k is less than 0, then let k be 0.
700 		k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
701 
702 		// 9. Repeat, while k < len
703 		while (k < len) {
704 			// a. Let Pk be ToString(k).
705 			//   This is implicit for LHS operands of the in operator
706 			// b. Let kPresent be the result of calling the
707 			//    HasProperty internal method of O with argument Pk.
708 			//   This step can be combined with c
709 			// c. If kPresent is true, then
710 			//    i.  Let elementK be the result of calling the Get
711 			//        internal method of O with the argument ToString(k).
712 			//   ii.  Let same be the result of applying the
713 			//        Strict Equality Comparison Algorithm to
714 			//        searchElement and elementK.
715 			//  iii.  If same is true, return k.
716 			if (k in O && O[k] === searchElement) {
717 				return k;
718 			}
719 			k++;
720 		}
721 		return -1;
722 	};
723 }
724 
725 /**
726  * Polyfill for Array.prototype.some -- currently only required by IE8.
727  *
728  * From Mozilla:
729  * https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some
730  */
731 if (!Array.prototype.some) {
732 	// Production steps of ECMA-262, Edition 5, 15.4.4.17
733 	// Reference: http://es5.github.io/#x15.4.4.17
734 	Array.prototype.some = function(fun/*, thisArg*/) {
735 		'use strict';
736 
737 		if (this == null) {
738 			throw new TypeError('Array.prototype.some called on null or undefined');
739 		}
740 
741 		if (typeof fun !== 'function') {
742 			throw new TypeError();
743 		}
744 
745 		var t = Object(this);
746 		var len = t.length >>> 0;
747 
748 		var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
749 		for (var i = 0; i < len; i++) {
750 			if (i in t && fun.call(thisArg, t[i], i, t)) {
751 				return true;
752 			}
753 		}
754 
755 		return false;
756 	};
757 }
758