diff --git a/.tests/Launch-Scripts/createShortcut.test.js b/.tests/Launch-Scripts/createShortcut.test.js
index c6f819334f..546998daa3 100644
--- a/.tests/Launch-Scripts/createShortcut.test.js
+++ b/.tests/Launch-Scripts/createShortcut.test.js
@@ -86,4 +86,4 @@ describe('createShortcut', () => {
'Shortcuts not supported on your system'
)
})
-})
\ No newline at end of file
+})
diff --git a/.tests/Launch-Scripts/runSetup.test.js b/.tests/Launch-Scripts/runSetup.test.js
index 2fc7785de8..54c25d76d8 100644
--- a/.tests/Launch-Scripts/runSetup.test.js
+++ b/.tests/Launch-Scripts/runSetup.test.js
@@ -1,5 +1,6 @@
const nock = require('nock')
const simpleGit = require('simple-git')
+const process = require('process')
const env = require('../../Environment').newEnvironment()
const externalScriptsURLs = env.EXTERNAL_SCRIPTS
const {
@@ -10,7 +11,8 @@ const {
const {
setUpstreamAndOrigin,
runSetup,
- installExternalScripts
+ installExternalScripts,
+ errorResp
} = require('../../Launch-Scripts/runSetup')
// ****** VERY IMPORTANT NOTE *******
@@ -49,7 +51,8 @@ simpleGit.mockReturnValue({
jest.mock('process', () => {
return {
- cwd: jest.fn(() => './')
+ cwd: jest.fn(() => './'),
+ exit: jest.fn(() => 'there was an error')
}
})
@@ -76,6 +79,13 @@ afterEach(() => {
nock.restore()
})
+describe('errorResp', () => {
+ it('should exit if error caught', () => {
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
+ errorResp('there was an error')
+ expect(mockExit).toHaveBeenCalledWith()
+ })
+})
describe('setUpstreamAndOrigin', () => {
it('should return success msg if github is setup correctly', async () => {
const resp = await setUpstreamAndOrigin('./')
diff --git a/.tests/Launch-Scripts/tfSetup.test.js b/.tests/Launch-Scripts/tfSetup.test.js
new file mode 100644
index 0000000000..23c470ed48
--- /dev/null
+++ b/.tests/Launch-Scripts/tfSetup.test.js
@@ -0,0 +1,38 @@
+const tfSetup = require('../../Launch-Scripts/tfSetup')
+const os = require('os')
+
+jest.mock('os', () => {
+ return {
+ platform: jest.fn(() => 'win32')
+ }
+})
+
+jest.mock('fs', () => {
+ return {
+ copyFile: jest.fn(() => 'true'),
+ existsSync: jest.fn(() => true),
+ readdirSync: jest.fn(() => {
+ return {
+ filter: jest.fn((dirent) => {
+ return {
+ map: jest.fn((dirent) => 'dirname')
+ }
+ })
+ }
+ })
+ }
+})
+
+afterEach(() => {
+ jest.clearAllMocks()
+})
+
+describe('tfSetup', () => {
+ it('should copy tensorflow.dll for windows sytems', () => {
+ expect(tfSetup()).toEqual('tensorflow dll copied')
+ })
+ it('should do nothing if not windows system', () => {
+ jest.spyOn(os, 'platform').mockReturnValue('linux')
+ expect(tfSetup()).toEqual('non-windows system')
+ })
+})
\ No newline at end of file
diff --git a/Launch-Scripts/runSetup.js b/Launch-Scripts/runSetup.js
index bd63235a49..f63924a3a6 100644
--- a/Launch-Scripts/runSetup.js
+++ b/Launch-Scripts/runSetup.js
@@ -7,11 +7,10 @@ const externalScriptsDir = path.join(process.cwd(), 'Platform', 'WebServer', 'ex
const env = require('../Environment').newEnvironment()
const externalScriptsURLs = env.EXTERNAL_SCRIPTS
const projectPluginMap = require('../Plugins/project-plugin-map.json')
-const simpleGit = require('simple-git')
const errorResp = (e) => {
console.error(e)
- process.exit(1)
+ process.exit()
}
// ** export setup piece by piece for more robust tests **
@@ -35,12 +34,13 @@ const installExternalScripts = () => {
}
})
}
- console.log('External scripts installed')
return 'External scripts installed'
}
const setUpstreamAndOrigin = async (dir, repo='Superalgos') => {
+ console.log('Setting up github upstream and origin...')
// initialize simpleGit
+ const simpleGit = require('simple-git')
const options = {
binary: 'git',
maxConcurrentProcesses: 6,
@@ -124,8 +124,23 @@ const setUpstreamAndOrigin = async (dir, repo='Superalgos') => {
return 'Set upstream and origin for github'
}
-const runSetup = () => {
+const runSetup = (tfjs=false) => {
// Install Node_Modules to Main Superalgos Directory
+
+ // install tensorflow if user ran tensorflow setup file
+ if (tfjs !== false) {
+ console.log('Including tensorflow.js in your setup...')
+ nodeModulesDirs = [
+ path.join(process.cwd(),
+ "Projects",
+ "TensorFlow",
+ "TS",
+ "Bot-Modules",
+ "Learning-Bot",
+ "Low-Frequency-Learning")
+ ]
+ }
+
let dir = process.cwd()
let command = 'echo Results of install at ' + dir + ' & npm ci'
let nodeInstPromise = new Promise(resolve => {
@@ -155,7 +170,7 @@ const runSetup = () => {
// Donload external scripts
console.log('')
- console.log('Downloading external scripts …')
+ console.log('Setting up your environment …')
console.log('')
installExternalScripts()
@@ -165,19 +180,18 @@ const runSetup = () => {
// Ensure upstream and origin are set for this repo and submodules
let gitUser
- let usesSSH = false
setUpstreamAndOrigin().then(async () => {
Object.values(projectPluginMap).forEach(plugin => {
setUpstreamAndOrigin(plugin.dir, plugin.repo)
})
}).catch(errorResp)
})
- console.log('Setup complete')
return 'Setup complete'
}
module.exports = {
runSetup,
setUpstreamAndOrigin,
- installExternalScripts
+ installExternalScripts,
+ errorResp
}
diff --git a/Launch-Scripts/tfSetup.js b/Launch-Scripts/tfSetup.js
new file mode 100644
index 0000000000..a6596ac464
--- /dev/null
+++ b/Launch-Scripts/tfSetup.js
@@ -0,0 +1,27 @@
+const fs = require('fs')
+const os = require('os')
+const path = require('path')
+
+const tfSetup = () => {
+ const srcFolder = './node_modules/@tensorflow/tfjs-node/deps/lib/'
+ const destFolder = './node_modules/@tensorflow/tfjs-node/lib/'
+
+ if (os.platform() === 'win32' && fs.existsSync(srcFolder) && fs.existsSync(destFolder)) {
+ const dest = fs.readdirSync(destFolder, { withFileTypes: true })
+ .filter(dirent => dirent.isDirectory())
+ .map(dirent => dirent.name)
+
+
+ fs.copyFile(path.join(srcFolder, "tensorflow.dll"), path.join(destFolder, dest[0], "tensorflow.dll"), (err) => {
+ if (err) {
+ console.log(err)
+ return err
+ }
+ console.log('tensorflow dll copied')
+ })
+ return 'tensorflow dll copied'
+ }
+ return 'non-windows system'
+}
+
+module.exports = tfSetup
diff --git a/Platform/WebServer/externalScripts/jquery-3.6.0.js b/Platform/WebServer/externalScripts/jquery-3.6.0.js
index baf219e008..fc6c299b73 100644
--- a/Platform/WebServer/externalScripts/jquery-3.6.0.js
+++ b/Platform/WebServer/externalScripts/jquery-3.6.0.js
@@ -4912,4 +4912,5970 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
div = fragment.appendChild( document.createElement( "div" ) ),
input = document.createElement( "input" );
- // Support: Android 4.0 - 4.3 o
\ No newline at end of file
+ // Support: Android 4.0 - 4.3 only
+ // Check state lost if the name is set (#11217)
+ // Support: Windows Web Apps (WWA)
+ // `name` and `type` must use .setAttribute for WWA (#14901)
+ input.setAttribute( "type", "radio" );
+ input.setAttribute( "checked", "checked" );
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+
+ // Support: Android <=4.1 only
+ // Older WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE <=11 only
+ // Make sure textarea (and checkbox) defaultValue is properly cloned
+ div.innerHTML = "";
+ support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+
+ // Support: IE <=9 only
+ // IE <=9 replaces ";
+ support.option = !!div.lastChild;
+} )();
+
+
+// We have to close these tags to support XHTML (#13200)
+var wrapMap = {
+
+ // XHTML parsers do not magically insert elements in the
+ // same way that tag soup parsers do. So we cannot shorten
+ // this by omitting
or other required elements.
+ thead: [ 1, "" ],
+ col: [ 2, "" ],
+ tr: [ 2, "" ],
+ td: [ 3, "" ],
+
+ _default: [ 0, "", "" ]
+};
+
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// Support: IE <=9 only
+if ( !support.option ) {
+ wrapMap.optgroup = wrapMap.option = [ 1, "" ];
+}
+
+
+function getAll( context, tag ) {
+
+ // Support: IE <=9 - 11 only
+ // Use typeof to avoid zero-argument method invocation on host objects (#15151)
+ var ret;
+
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ ret = context.getElementsByTagName( tag || "*" );
+
+ } else if ( typeof context.querySelectorAll !== "undefined" ) {
+ ret = context.querySelectorAll( tag || "*" );
+
+ } else {
+ ret = [];
+ }
+
+ if ( tag === undefined || tag && nodeName( context, tag ) ) {
+ return jQuery.merge( [ context ], ret );
+ }
+
+ return ret;
+}
+
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+ var i = 0,
+ l = elems.length;
+
+ for ( ; i < l; i++ ) {
+ dataPriv.set(
+ elems[ i ],
+ "globalEval",
+ !refElements || dataPriv.get( refElements[ i ], "globalEval" )
+ );
+ }
+}
+
+
+var rhtml = /<|?\w+;/;
+
+function buildFragment( elems, context, scripts, selection, ignored ) {
+ var elem, tmp, tag, wrap, attached, j,
+ fragment = context.createDocumentFragment(),
+ nodes = [],
+ i = 0,
+ l = elems.length;
+
+ for ( ; i < l; i++ ) {
+ elem = elems[ i ];
+
+ if ( elem || elem === 0 ) {
+
+ // Add nodes directly
+ if ( toType( elem ) === "object" ) {
+
+ // Support: Android <=4.0 only, PhantomJS 1 only
+ // push.apply(_, arraylike) throws on ancient WebKit
+ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+ // Convert non-html into a text node
+ } else if ( !rhtml.test( elem ) ) {
+ nodes.push( context.createTextNode( elem ) );
+
+ // Convert html into DOM nodes
+ } else {
+ tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
+
+ // Deserialize a standard representation
+ tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+ tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
+
+ // Descend through wrappers to the right content
+ j = wrap[ 0 ];
+ while ( j-- ) {
+ tmp = tmp.lastChild;
+ }
+
+ // Support: Android <=4.0 only, PhantomJS 1 only
+ // push.apply(_, arraylike) throws on ancient WebKit
+ jQuery.merge( nodes, tmp.childNodes );
+
+ // Remember the top-level container
+ tmp = fragment.firstChild;
+
+ // Ensure the created nodes are orphaned (#12392)
+ tmp.textContent = "";
+ }
+ }
+ }
+
+ // Remove wrapper from fragment
+ fragment.textContent = "";
+
+ i = 0;
+ while ( ( elem = nodes[ i++ ] ) ) {
+
+ // Skip elements already in the context collection (trac-4087)
+ if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
+ if ( ignored ) {
+ ignored.push( elem );
+ }
+ continue;
+ }
+
+ attached = isAttached( elem );
+
+ // Append to fragment
+ tmp = getAll( fragment.appendChild( elem ), "script" );
+
+ // Preserve script evaluation history
+ if ( attached ) {
+ setGlobalEval( tmp );
+ }
+
+ // Capture executables
+ if ( scripts ) {
+ j = 0;
+ while ( ( elem = tmp[ j++ ] ) ) {
+ if ( rscriptType.test( elem.type || "" ) ) {
+ scripts.push( elem );
+ }
+ }
+ }
+ }
+
+ return fragment;
+}
+
+
+var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
+
+function returnTrue() {
+ return true;
+}
+
+function returnFalse() {
+ return false;
+}
+
+// Support: IE <=9 - 11+
+// focus() and blur() are asynchronous, except when they are no-op.
+// So expect focus to be synchronous when the element is already active,
+// and blur to be synchronous when the element is not already active.
+// (focus and blur are always synchronous in other supported browsers,
+// this just defines when we can count on it).
+function expectSync( elem, type ) {
+ return ( elem === safeActiveElement() ) === ( type === "focus" );
+}
+
+// Support: IE <=9 only
+// Accessing document.activeElement can throw unexpectedly
+// https://bugs.jquery.com/ticket/13393
+function safeActiveElement() {
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
+}
+
+function on( elem, types, selector, data, fn, one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ on( elem, type, selector, data, types[ type ], one );
+ }
+ return elem;
+ }
+
+ if ( data == null && fn == null ) {
+
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return elem;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return elem.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ } );
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var handleObjIn, eventHandle, tmp,
+ events, t, handleObj,
+ special, handlers, type, namespaces, origType,
+ elemData = dataPriv.get( elem );
+
+ // Only attach events to objects that accept data
+ if ( !acceptData( elem ) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Ensure that invalid selectors throw exceptions at attach time
+ // Evaluate against documentElement in case elem is a non-element node (e.g., document)
+ if ( selector ) {
+ jQuery.find.matchesSelector( documentElement, selector );
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ if ( !( events = elemData.events ) ) {
+ events = elemData.events = Object.create( null );
+ }
+ if ( !( eventHandle = elemData.handle ) ) {
+ eventHandle = elemData.handle = function( e ) {
+
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
+ jQuery.event.dispatch.apply( elem, arguments ) : undefined;
+ };
+ }
+
+ // Handle multiple events separated by a space
+ types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[ t ] ) || [];
+ type = origType = tmp[ 1 ];
+ namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+
+ // There *must* be a type, no attaching namespace-only handlers
+ if ( !type ) {
+ continue;
+ }
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend( {
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join( "." )
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !( handlers = events[ type ] ) ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener if the special events handler returns false
+ if ( !special.setup ||
+ special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ },
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var j, origCount, tmp,
+ events, t, handleObj,
+ special, handlers, type, namespaces, origType,
+ elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
+
+ if ( !elemData || !( events = elemData.events ) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[ t ] ) || [];
+ type = origType = tmp[ 1 ];
+ namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[ 2 ] &&
+ new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector ||
+ selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown ||
+ special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove data and the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ dataPriv.remove( elem, "handle events" );
+ }
+ },
+
+ dispatch: function( nativeEvent ) {
+
+ var i, j, ret, matched, handleObj, handlerQueue,
+ args = new Array( arguments.length ),
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( nativeEvent ),
+
+ handlers = (
+ dataPriv.get( this, "events" ) || Object.create( null )
+ )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[ 0 ] = event;
+
+ for ( i = 1; i < arguments.length; i++ ) {
+ args[ i ] = arguments[ i ];
+ }
+
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( ( handleObj = matched.handlers[ j++ ] ) &&
+ !event.isImmediatePropagationStopped() ) {
+
+ // If the event is namespaced, then each handler is only invoked if it is
+ // specially universal or its namespaces are a superset of the event's.
+ if ( !event.rnamespace || handleObj.namespace === false ||
+ event.rnamespace.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
+ handleObj.handler ).apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( ( event.result = ret ) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ handlers: function( event, handlers ) {
+ var i, handleObj, sel, matchedHandlers, matchedSelectors,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Find delegate handlers
+ if ( delegateCount &&
+
+ // Support: IE <=9
+ // Black-hole SVG