Skip to content

Commit

Permalink
Use optional npm dependencies to shim DOMParser and WebSocket in node
Browse files Browse the repository at this point in the history
  • Loading branch information
LeartS committed May 26, 2019
1 parent ea6126b commit c66c56f
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 60 deletions.
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,9 @@
"sinon-qunit": "~2.0.0",
"yarpm": "^0.2.1"
},
"dependencies": {}
"dependencies": {},
"optionalDependencies": {
"ws": "^7.0.0",
"xmldom": "^0.1.27"
}
}
1 change: 1 addition & 0 deletions src/bosh.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/* global window, setTimeout, clearTimeout, XMLHttpRequest, ActiveXObject */

import core from './core';
import { DOMParser } from './shims'

const Strophe = core.Strophe;
const $build = core.$build;
Expand Down
64 changes: 5 additions & 59 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import MD5 from './md5';
import SHA1 from './sha1';
import utils from './utils';
import * as shims from './shims';

/** Function: $build
* Create a Strophe.Builder.
Expand Down Expand Up @@ -330,26 +331,6 @@ const Strophe = {
*/
_xmlGenerator: null,

/** PrivateFunction: _makeGenerator
* _Private_ function that creates a dummy XML DOM document to serve as
* an element and text node generator.
*/
_makeGenerator: function () {
let doc;
// IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.
// Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be
// less than 10 in the case of IE9 and below.
if (document.implementation.createDocument === undefined ||
document.implementation.createDocument && document.documentMode && document.documentMode < 10) {
doc = this._getIEXmlDom();
doc.appendChild(doc.createElement('strophe'));
} else {
doc = document.implementation
.createDocument('jabber:client', 'strophe', null);
}
return doc;
},

/** Function: xmlGenerator
* Get the DOM document to generate elements.
*
Expand All @@ -358,45 +339,11 @@ const Strophe = {
*/
xmlGenerator: function () {
if (!Strophe._xmlGenerator) {
Strophe._xmlGenerator = Strophe._makeGenerator();
Strophe._xmlGenerator = shims.getDummyXMLDOMDocument()
}
return Strophe._xmlGenerator;
},

/** PrivateFunction: _getIEXmlDom
* Gets IE xml doc object
*
* Returns:
* A Microsoft XML DOM Object
* See Also:
* http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
*/
_getIEXmlDom : function() {
let doc = null;
const docStrings = [
"Msxml2.DOMDocument.6.0",
"Msxml2.DOMDocument.5.0",
"Msxml2.DOMDocument.4.0",
"MSXML2.DOMDocument.3.0",
"MSXML2.DOMDocument",
"MSXML.DOMDocument",
"Microsoft.XMLDOM"
];

for (let d=0; d<docStrings.length; d++) {
if (doc === null) {
try {
doc = new ActiveXObject(docStrings[d]);
} catch (e) {
doc = null;
}
} else {
break;
}
}
return doc;
},

/** Function: xmlElement
* Create an XML DOM element.
*
Expand Down Expand Up @@ -512,8 +459,8 @@ const Strophe = {
xmlHtmlNode: function (html) {
let node;
//ensure text is escaped
if (DOMParser) {
const parser = new DOMParser();
if (shims.DOMParser) {
const parser = new shims.DOMParser();
node = parser.parseFromString(html, "text/xml");
} else {
node = new ActiveXObject("Microsoft.XMLDOM");
Expand Down Expand Up @@ -1160,7 +1107,7 @@ Strophe.Builder.prototype = {
* The Strophe.Builder object.
*/
h: function (html) {
const fragment = document.createElement('body');
const fragment = Strophe.xmlGenerator().createElement('body');
// force the browser to try and fix any invalid HTML tags
fragment.innerHTML = html;
// copy cleaned html into an xml dom
Expand Down Expand Up @@ -3505,7 +3452,6 @@ Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism("OAUTHBEARER", tru
Strophe.SASLOAuthBearer.prototype.test = function(connection) {
return connection.pass !== null;
};

Strophe.SASLOAuthBearer.prototype.onChallenge = function(connection) {
let auth_str = 'n,';
if (connection.authcid !== null) {
Expand Down
120 changes: 120 additions & 0 deletions src/shims.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* This module provides uniform
* Shims APIs and globals that are not present in all JS environments,
* the most common example for Strophe being browser APIs like WebSocket
* and DOM that don't exist under nodejs.
*
* Usually these will be supplied in nodejs by conditionally requiring a
* NPM module that provides a compatible implementation.
*/

/**
* WHATWG WebSockets API
* https://www.w3.org/TR/websockets/
*
* Interface to use the web socket protocol
*
* Used implementations:
* - supported browsers: built-in in WebSocket global
* https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Browser_compatibility
* - nodejs: use standard-compliant 'ws' module
* https://www.npmjs.com/package/ws
*/
function getWebSocketImplementation () {
let WebSocketImplementation = WebSocket
if (typeof WebSocketImplementation === 'undefined') {
try {
WebSocketImplementation = require('ws');
} catch (err) {
throw new Error('You must install the "ws" package to use Strophe in nodejs.');
}
}
return WebSocketImplementation
}
export const WebSocket = getWebSocketImplementation()

/**
* DOMParser
* https://w3c.github.io/DOM-Parsing/#the-domparser-interface
*
* Interface to parse XML strings into Document objects
*
* Used implementations:
* - supported browsers: built-in in DOMParser global
* https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility
* - nodejs: use 'xmldom' module
* https://www.npmjs.com/package/xmldom
*/
function getDOMParserImplementation () {
let DOMParserImplementation = DOMParser
if (typeof DOMParserImplementation === 'undefined') {
try {
DOMParserImplementation = require('xmldom').DOMParser;
} catch (err) {
throw new Error('You must install the "xmldom" package to use Strophe in nodejs.');
}
}
return DOMParserImplementation
}
export const DOMParser = getDOMParserImplementation()

/**
* Gets IE xml doc object. Used by getDummyXMLDocument shim.
*
* Returns:
* A Microsoft XML DOM Object
* See Also:
* http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
*/
function _getIEXmlDom () {
const docStrings = [
"Msxml2.DOMDocument.6.0",
"Msxml2.DOMDocument.5.0",
"Msxml2.DOMDocument.4.0",
"MSXML2.DOMDocument.3.0",
"MSXML2.DOMDocument",
"MSXML.DOMDocument",
"Microsoft.XMLDOM"
];
for (let d = 0; d < docStrings.length; d++) {
try {
const doc = new ActiveXObject(docStrings[d]);
return doc
} catch (e) {
// Try next one
}
}
}

/**
* Creates a dummy XML DOM document to serve as an element and text node generator.
*
* Used implementations:
* - IE < 10: avoid using createDocument() due to a memory leak, use ie-specific
* workaround
* - other supported browsers: use document's createDocument
* - nodejs: use 'xmldom'
*/
export function getDummyXMLDOMDocument () {
// nodejs
if (typeof document === 'undefined') {
try {
const DOMImplementation = require('xmldom').DOMImplementation;
return new DOMImplementation().createDocument('jabber:client', 'strophe', null);
} catch (err) {
throw new Error('You must install the "xmldom" package to use Strophe in nodejs.');
}
}
// IE < 10
if (
document.implementation.createDocument === undefined ||
document.implementation.createDocument && document.documentMode && document.documentMode < 10
) {
const doc = _getIEXmlDom();
doc.appendChild(doc.createElement('strophe'));
return doc
}
// All other supported browsers
return document.implementation.createDocument('jabber:client', 'strophe', null)
}

1 change: 1 addition & 0 deletions src/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/* global window, clearTimeout, WebSocket, DOMParser */

import core from './core';
import { WebSocket, DOMParser } from './shims';

const Strophe = core.Strophe;
const $build = core.$build;
Expand Down

0 comments on commit c66c56f

Please sign in to comment.