Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
476e1d79
Commit
476e1d79
authored
Jan 29, 2018
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
IE11 fixes
parent
f724f490
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
819 additions
and
750 deletions
+819
-750
CHANGES.md
CHANGES.md
+1
-0
dist/converse.js
dist/converse.js
+814
-741
src/converse-profile.js
src/converse-profile.js
+4
-9
No files found.
CHANGES.md
View file @
476e1d79
...
...
@@ -4,6 +4,7 @@
### Bugfixes
-
Various fixes for IE11.
-
Could not register on Ejabberd 18.
`"Missing attribute 'id' in tag qualified by namespace 'jabber:client'"`
-
#993
`moment.format`
is not a function error when sending a message.
-
#994 TypeError when using the
`user.login`
API.
...
...
dist/converse.js
View file @
476e1d79
...
...
@@ -2735,7 +2735,7 @@ if ( typeof define === "function" && define.amd ) {
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
* @license Licensed under MIT license
* See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
* @version
4.1.1
* @version
v4.2.2+97478eb6
*/
(function (global, factory) {
...
...
@@ -2753,7 +2753,9 @@ function isFunction(x) {
return typeof x === 'function';
}
var _isArray = undefined;
var _isArray = void 0;
if (Array.isArray) {
_isArray = Array.isArray;
} else {
...
...
@@ -2765,8 +2767,8 @@ if (Array.isArray) {
var isArray = _isArray;
var len = 0;
var vertxNext =
undefined
;
var customSchedulerFn =
undefined
;
var vertxNext =
void 0
;
var customSchedulerFn =
void 0
;
var asap = function asap(callback, arg) {
queue[len] = callback;
...
...
@@ -2795,7 +2797,7 @@ function setAsap(asapFn) {
var browserWindow = typeof window !== 'undefined' ? window : undefined;
var browserGlobal = browserWindow || {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' &&
({})
.toString.call(process) === '[object process]';
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' &&
{}
.toString.call(process) === '[object process]';
// test for web worker but not in IE10
var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
...
...
@@ -2875,7 +2877,7 @@ function attemptVertx() {
}
}
var scheduleFlush =
undefined
;
var scheduleFlush =
void 0
;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
...
...
@@ -2890,8 +2892,6 @@ if (isNode) {
}
function then(onFulfillment, onRejection) {
var _arguments = arguments;
var parent = this;
var child = new this.constructor(noop);
...
...
@@ -2902,13 +2902,12 @@ function then(onFulfillment, onRejection) {
var _state = parent._state;
if (_state) {
(function () {
var callback = _arguments[_state - 1];
var callback = arguments[_state - 1];
asap(function () {
return invokeCallback(_state, child, callback, parent._result);
});
})();
} else {
subscribe(parent, child, onFulfillment, onRejection);
}
...
...
@@ -3100,6 +3099,7 @@ function subscribe(parent, child, onFulfillment, onRejection) {
var _subscribers = parent._subscribers;
var length = _subscribers.length;
parent._onerror = null;
_subscribers[length] = child;
...
...
@@ -3119,8 +3119,8 @@ function publish(promise) {
return;
}
var child =
undefined
,
callback =
undefined
,
var child =
void 0
,
callback =
void 0
,
detail = promise._result;
for (var i = 0; i < subscribers.length; i += 3) {
...
...
@@ -3154,10 +3154,10 @@ function tryCatch(callback, detail) {
function invokeCallback(settled, promise, callback, detail) {
var hasCallback = isFunction(callback),
value =
undefined
,
error =
undefined
,
succeeded =
undefined
,
failed =
undefined
;
value =
void 0
,
error =
void 0
,
succeeded =
void 0
,
failed =
void 0
;
if (hasCallback) {
value = tryCatch(callback, detail);
...
...
@@ -3216,7 +3216,16 @@ function makePromise(promise) {
promise._subscribers = [];
}
function Enumerator$1(Constructor, input) {
function validationError() {
return new Error('Array Methods must be provided an Array');
}
function validationError() {
return new Error('Array Methods must be provided an Array');
}
var Enumerator = function () {
function Enumerator(Constructor, input) {
this._instanceConstructor = Constructor;
this.promise = new Constructor(noop);
...
...
@@ -3242,22 +3251,19 @@ function Enumerator$1(Constructor, input) {
} else {
reject(this.promise, validationError());
}
}
function validationError() {
return new Error('Array Methods must be provided an Array');
}
}
Enumerator$1.prototype._enumerate = function
(input) {
Enumerator.prototype._enumerate = function _enumerate
(input) {
for (var i = 0; this._state === PENDING && i < input.length; i++) {
this._eachEntry(input[i], i);
}
};
};
Enumerator$1.prototype._eachEntry = function
(entry, i) {
Enumerator.prototype._eachEntry = function _eachEntry
(entry, i) {
var c = this._instanceConstructor;
var resolve$$1 = c.resolve;
if (resolve$$1 === resolve$1) {
var _then = getThen(entry);
...
...
@@ -3266,7 +3272,7 @@ Enumerator$1.prototype._eachEntry = function (entry, i) {
} else if (typeof _then !== 'function') {
this._remaining--;
this._result[i] = entry;
} else if (c === Promise$3
) {
} else if (c === Promise$2
) {
var promise = new c(noop);
handleMaybeThenable(promise, entry, _then);
this._willSettleAt(promise, i);
...
...
@@ -3278,11 +3284,12 @@ Enumerator$1.prototype._eachEntry = function (entry, i) {
} else {
this._willSettleAt(resolve$$1(entry), i);
}
};
};
Enumerator$1.prototype._settledAt = function
(state, i, value) {
Enumerator.prototype._settledAt = function _settledAt
(state, i, value) {
var promise = this.promise;
if (promise._state === PENDING) {
this._remaining--;
...
...
@@ -3296,9 +3303,9 @@ Enumerator$1.prototype._settledAt = function (state, i, value) {
if (this._remaining === 0) {
fulfill(promise, this._result);
}
};
};
Enumerator$1.prototype._willSettleAt = function
(promise, i) {
Enumerator.prototype._willSettleAt = function _willSettleAt
(promise, i) {
var enumerator = this;
subscribe(promise, undefined, function (value) {
...
...
@@ -3306,7 +3313,10 @@ Enumerator$1.prototype._willSettleAt = function (promise, i) {
}, function (reason) {
return enumerator._settledAt(REJECTED, i, reason);
});
};
};
return Enumerator;
}();
/**
`Promise.all` accepts an array of promises, and returns a new promise which
...
...
@@ -3355,8 +3365,8 @@ Enumerator$1.prototype._willSettleAt = function (promise, i) {
fulfilled, or rejected if any of them become rejected.
@static
*/
function all
$1
(entries) {
return new Enumerator
$1
(this, entries).promise;
function all(entries) {
return new Enumerator(this, entries).promise;
}
/**
...
...
@@ -3424,7 +3434,7 @@ function all$1(entries) {
@return {Promise} a promise which settles in the same way as the first passed
promise to settle.
*/
function race
$1
(entries) {
function race(entries) {
/*jshint validthis:true */
var Constructor = this;
...
...
@@ -3591,37 +3601,27 @@ function needsNew() {
```
@class Promise
@param {
f
unction} resolver
@param {
F
unction} resolver
Useful for tooling.
@constructor
*/
function Promise$3(resolver) {
var Promise$2 = function () {
function Promise(resolver) {
this[PROMISE_ID] = nextId();
this._result = this._state = undefined;
this._subscribers = [];
if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise$3 ? initializePromise(this, resolver) : needsNew();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
}
}
Promise$3.all = all$1;
Promise$3.race = race$1;
Promise$3.resolve = resolve$1;
Promise$3.reject = reject$1;
Promise$3._setScheduler = setScheduler;
Promise$3._setAsap = setAsap;
Promise$3._asap = asap;
Promise$3.prototype = {
constructor: Promise$3,
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
...
...
@@ -3629,14 +3629,11 @@ Promise$3.prototype = {
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
...
...
@@ -3646,7 +3643,6 @@ Promise$3.prototype = {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
...
...
@@ -3659,7 +3655,6 @@ Promise$3.prototype = {
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
...
...
@@ -3671,15 +3666,12 @@ Promise$3.prototype = {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
...
...
@@ -3687,9 +3679,7 @@ Promise$3.prototype = {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
...
...
@@ -3699,15 +3689,11 @@ Promise$3.prototype = {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
let result;
try {
result = findResult();
// success
...
...
@@ -3715,9 +3701,7 @@ Promise$3.prototype = {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
...
...
@@ -3727,9 +3711,7 @@ Promise$3.prototype = {
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
...
...
@@ -3737,15 +3719,11 @@ Promise$3.prototype = {
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
let author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
...
...
@@ -3754,19 +3732,12 @@ Promise$3.prototype = {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
...
...
@@ -3791,9 +3762,7 @@ Promise$3.prototype = {
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
...
...
@@ -3803,50 +3772,112 @@ Promise$3.prototype = {
// something went wrong
});
```
@method then
@param {Function} onFulfilled
@param {Function} onRejected
Useful for tooling.
@return {Promise}
*/
then: then,
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn't find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
Useful for tooling.
@return {Promise}
*/
'catch': function _catch(onRejection) {
Promise.prototype.catch = function _catch(onRejection) {
return this.then(null, onRejection);
};
/**
`finally` will be invoked regardless of the promise's fate just as native
try/catch/finally behaves
Synchronous example:
```js
findAuthor() {
if (Math.random() > 0.5) {
throw new Error();
}
};
return new Author();
}
try {
return findAuthor(); // succeed or fail
} catch(error) {
return findOtherAuther();
} finally {
// always runs
// doesn't affect the return value
}
```
Asynchronous example:
```js
findAuthor().catch(function(reason){
return findOtherAuther();
}).finally(function(){
// author was either found, or not
});
```
@method finally
@param {Function} callback
@return {Promise}
*/
Promise.prototype.finally = function _finally(callback) {
var promise = this;
var constructor = promise.constructor;
return promise.then(function (value) {
return constructor.resolve(callback()).then(function () {
return value;
});
}, function (reason) {
return constructor.resolve(callback()).then(function () {
throw reason;
});
});
};
return Promise;
}();
Promise$2.prototype.then = then;
Promise$2.all = all;
Promise$2.race = race;
Promise$2.resolve = resolve$1;
Promise$2.reject = reject$1;
Promise$2._setScheduler = setScheduler;
Promise$2._setAsap = setAsap;
Promise$2._asap = asap;
/*global self*/
function polyfill
$1
() {
var local =
undefined
;
function polyfill() {
var local =
void 0
;
if (typeof global !== 'undefined') {
local = global;
...
...
@@ -3875,19 +3906,21 @@ function polyfill$1() {
}
}
local.Promise = Promise$
3
;
local.Promise = Promise$
2
;
}
// Strange compat..
Promise$
3.polyfill = polyfill$1
;
Promise$
3.Promise = Promise$3
;
Promise$
2.polyfill = polyfill
;
Promise$
2.Promise = Promise$2
;
Promise$
3
.polyfill();
Promise$
2
.polyfill();
return Promise$
3
;
return Promise$
2
;
})));
//# sourceMappingURL=es6-promise.auto.map
;
/**
...
...
@@ -32786,11 +32819,6 @@ Strophe.Connection.prototype = {
* are ready and keep poll requests going.
*/
_onIdle: function () {
// if (typeof previous_date === 'undefined') {
// previous_date = new Date();
// }
// console.log(Math.abs(new Date() - previous_date));
// previous_date = new Date();
var i, thand, since, newList;
// add timed handlers scheduled for addition
...
...
@@ -33910,7 +33938,7 @@ Strophe.Bosh.prototype = {
(reqStatus >= 400 && reqStatus < 600) ||
reqStatus >= 12000) {
// request failed
Strophe.
warn
("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened");
Strophe.
error
("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened");
this._hitError(reqStatus);
this._callProtocolErrorHandlers(req);
if (reqStatus >= 400 && reqStatus < 500) {
...
...
@@ -38248,21 +38276,13 @@ return Backbone.BrowserStorage;
}, console);
if (level === Strophe.LogLevel.ERROR) {
if (_converse.debug) {
logger.trace("".concat(prefix, " ").concat(moment().format(), " ERROR: ").concat(message), style);
} else {
logger.error("".concat(prefix, " ERROR: ").concat(message), style);
}
} else if (level === Strophe.LogLevel.WARN) {
if (_converse.debug) {
logger.warn("".concat(prefix, " ").concat(moment().format(), " WARNING: ").concat(message), style);
}
} else if (level === Strophe.LogLevel.FATAL) {
if (_converse.debug) {
logger.trace("".concat(prefix, " ").concat(moment().format(), " FATAL: ").concat(message), style);
} else {
logger.error("".concat(prefix, " FATAL: ").concat(message), style);
}
} else if (_converse.debug) {
if (level === Strophe.LogLevel.DEBUG) {
logger.debug("".concat(prefix, " ").concat(moment().format(), " DEBUG: ").concat(message), style);
...
...
@@ -38467,7 +38487,7 @@ return Backbone.BrowserStorage;
// ----------------------
this.generateResource = function () {
return "/converse.js-".concat(Math.floor(Math.random() * 139749
825
).toString());
return "/converse.js-".concat(Math.floor(Math.random() * 139749
528
).toString());
};
this.sendCSI = function (stat) {
...
...
@@ -41370,7 +41390,6 @@ return Backbone.BrowserStorage;
}(this.emojione = this.emojione || {}));
if(typeof module === "object") module.exports = this.emojione;
define("emojione", (function (global) {
return function () {
var ret, fn;
...
...
@@ -41380,45 +41399,44 @@ define("emojione", (function (global) {
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
*
默认配置
*
default settings
*
* @author
老雷
<leizongmin@gmail.com>
* @author
Zongmin Lei
<leizongmin@gmail.com>
*/
var FilterCSS = require(
'cssfilter'
).FilterCSS;
var getDefaultCSSWhiteList = require(
'cssfilter'
).getDefaultWhiteList;
var _ = require(
'./util'
);
var FilterCSS = require(
"cssfilter"
).FilterCSS;
var getDefaultCSSWhiteList = require(
"cssfilter"
).getDefaultWhiteList;
var _ = require(
"./util"
);
// 默认白名单
function getDefaultWhiteList () {
function getDefaultWhiteList() {
return {
a:
['target', 'href', 'title'
],
abbr:
['title'
],
a:
["target", "href", "title"
],
abbr:
["title"
],
address: [],
area:
['shape', 'coords', 'href', 'alt'
],
area:
["shape", "coords", "href", "alt"
],
article: [],
aside: [],
audio:
['autoplay', 'controls', 'loop', 'preload', 'src'
],
audio:
["autoplay", "controls", "loop", "preload", "src"
],
b: [],
bdi:
['dir'
],
bdo:
['dir'
],
bdi:
["dir"
],
bdo:
["dir"
],
big: [],
blockquote: [
'cite'
],
blockquote: [
"cite"
],
br: [],
caption: [],
center: [],
cite: [],
code: [],
col:
['align', 'valign', 'span', 'width'
],
colgroup: [
'align', 'valign', 'span', 'width'
],
col:
["align", "valign", "span", "width"
],
colgroup: [
"align", "valign", "span", "width"
],
dd: [],
del:
['datetime'
],
details: [
'open'
],
del:
["datetime"
],
details: [
"open"
],
div: [],
dl: [],
dt: [],
em: [],
font:
['color', 'size', 'face'
],
font:
["color", "size", "face"
],
footer: [],
h1: [],
h2: [],
...
...
@@ -41429,8 +41447,8 @@ function getDefaultWhiteList () {
header: [],
hr: [],
i: [],
img:
['src', 'alt', 'title', 'width', 'height'
],
ins:
['datetime'
],
img:
["src", "alt", "title", "width", "height"
],
ins:
["datetime"
],
li: [],
mark: [],
nav: [],
...
...
@@ -41438,88 +41456,87 @@ function getDefaultWhiteList () {
p: [],
pre: [],
s: [],
section:[],
section:
[],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
table:
['width', 'border', 'align', 'valign'
],
tbody:
['align', 'valign'
],
td:
['width', 'rowspan', 'colspan', 'align', 'valign'
],
tfoot:
['align', 'valign'
],
th:
['width', 'rowspan', 'colspan', 'align', 'valign'
],
thead:
['align', 'valign'
],
tr:
['rowspan', 'align', 'valign'
],
table:
["width", "border", "align", "valign"
],
tbody:
["align", "valign"
],
td:
["width", "rowspan", "colspan", "align", "valign"
],
tfoot:
["align", "valign"
],
th:
["width", "rowspan", "colspan", "align", "valign"
],
thead:
["align", "valign"
],
tr:
["rowspan", "align", "valign"
],
tt: [],
u: [],
ul: [],
video:
['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width'
]
video:
["autoplay", "controls", "loop", "preload", "src", "height", "width"
]
};
}
// 默认CSS Filter
var defaultCSSFilter = new FilterCSS();
/**
*
匹配到标签时的处理方法
*
default onTag function
*
* @param {String} tag
* @param {String} html
* @param {Object} options
* @return {String}
*/
function onTag
(tag, html, options) {
function onTag(tag, html, options) {
// do nothing
}
/**
*
匹配到不在白名单上的标签时的处理方法
*
default onIgnoreTag function
*
* @param {String} tag
* @param {String} html
* @param {Object} options
* @return {String}
*/
function onIgnoreTag
(tag, html, options) {
function onIgnoreTag(tag, html, options) {
// do nothing
}
/**
*
匹配到标签属性时的处理方法
*
default onTagAttr function
*
* @param {String} tag
* @param {String} name
* @param {String} value
* @return {String}
*/
function onTagAttr
(tag, name, value) {
function onTagAttr(tag, name, value) {
// do nothing
}
/**
*
匹配到不在白名单上的标签属性时的处理方法
*
default onIgnoreTagAttr function
*
* @param {String} tag
* @param {String} name
* @param {String} value
* @return {String}
*/
function onIgnoreTagAttr
(tag, name, value) {
function onIgnoreTagAttr(tag, name, value) {
// do nothing
}
/**
*
HTML转义
*
default escapeHtml function
*
* @param {String} html
*/
function escapeHtml
(html) {
return html.replace(REGEXP_LT,
'<').replace(REGEXP_GT, '>'
);
function escapeHtml(html) {
return html.replace(REGEXP_LT,
"<").replace(REGEXP_GT, ">"
);
}
/**
*
安全的标签属性值
*
default safeAttrValue function
*
* @param {String} tag
* @param {String} name
...
...
@@ -41527,46 +41544,46 @@ function escapeHtml (html) {
* @param {Object} cssFilter
* @return {String}
*/
function safeAttrValue
(tag, name, value, cssFilter) {
//
转换为友好的属性值,再做判断
function safeAttrValue(tag, name, value, cssFilter) {
//
unescape attribute value firstly
value = friendlyAttrValue(value);
if (name ===
'href' || name === 'src'
) {
//
过滤 href 和 src 属性
//
仅允许 http:// | https:// | mailto: | / | # 开头的地址
if (name ===
"href" || name === "src"
) {
//
filter `href` and `src` attribute
//
only allow the value that starts with `http://` | `https://` | `mailto:` | `/` | `#`
value = _.trim(value);
if (value === '#') return '#';
if (!(value.substr(0, 7) === 'http://' ||
value.substr(0, 8) === 'https://' ||
value.substr(0, 7) === 'mailto:' ||
value[0] === '#' ||
value[0] === '/')) {
return '';
if (value === "#") return "#";
if (
!(
value.substr(0, 7) === "http://" ||
value.substr(0, 8) === "https://" ||
value.substr(0, 7) === "mailto:" ||
value.substr(0, 4) === "tel:" ||
value[0] === "#" ||
value[0] === "/"
)
) {
return "";
}
} else if (name ===
'background'
) {
//
过滤 background 属性 (这个xss漏洞较老了,可能已经不适用)
//
javascript:
} else if (name ===
"background"
) {
//
filter `background` attribute (maybe no use)
//
`javascript:`
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
return
''
;
return
""
;
}
} else if (name === 'style') {
// /*注释*/
/*REGEXP_DEFAULT_ON_TAG_ATTR_3.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_3.test(value)) {
return '';
}*/
// expression()
} else if (name === "style") {
// `expression()`
REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) {
return
''
;
return
""
;
}
//
url()
//
`url()`
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
return
''
;
return
""
;
}
}
if (cssFilter !== false) {
...
...
@@ -41575,161 +41592,166 @@ function safeAttrValue (tag, name, value, cssFilter) {
}
}
//
输出时需要转义<>"
//
escape `<>"` before returns
value = escapeAttrValue(value);
return value;
}
//
正则表达式
//
RegExp list
var REGEXP_LT = /</g;
var REGEXP_GT = />/g;
var REGEXP_QUOTE = /"/g;
var REGEXP_QUOTE_2 = /"/g;
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/
img
;
var REGEXP_ATTR_VALUE_COLON = /:?/
img
;
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/
img
;
var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//
mg
;
var REGEXP_DEFAULT_ON_TAG_ATTR_4 = /((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/
ig
;
var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/
ig
;
var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//
ig
;
var REGEXP_DEFAULT_ON_TAG_ATTR_7 = /e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/
ig
;
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/
ig
;
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/
gim
;
var REGEXP_ATTR_VALUE_COLON = /:?/
gim
;
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/
gim
;
var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//
gm
;
var REGEXP_DEFAULT_ON_TAG_ATTR_4 = /((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/
gi
;
var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/
gi
;
var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//
gi
;
var REGEXP_DEFAULT_ON_TAG_ATTR_7 = /e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/
gi
;
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/
gi
;
/**
*
对双引号进行转义
*
escape doube quote
*
* @param {String} str
* @return {String} str
*/
function escapeQuote
(str) {
return str.replace(REGEXP_QUOTE,
'"'
);
function escapeQuote(str) {
return str.replace(REGEXP_QUOTE,
"""
);
}
/**
*
对双引号进行转义
*
unescape double quote
*
* @param {String} str
* @return {String} str
*/
function unescapeQuote
(str) {
function unescapeQuote(str) {
return str.replace(REGEXP_QUOTE_2, '"');
}
/**
*
对html实体编码进行转义
*
escape html entities
*
* @param {String} str
* @return {String}
*/
function escapeHtmlEntities
(str) {
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode
(str, code) {
return
(code[0] === 'x' || code[0] === 'X')
function escapeHtmlEntities(str) {
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode(str, code) {
return
code[0] === "x" || code[0] === "X"
? String.fromCharCode(parseInt(code.substr(1), 16))
: String.fromCharCode(parseInt(code, 10));
});
}
/**
*
对html5新增的危险实体编码进行转义
*
escape html5 new danger entities
*
* @param {String} str
* @return {String}
*/
function escapeDangerHtml5Entities (str) {
return str.replace(REGEXP_ATTR_VALUE_COLON, ':')
.replace(REGEXP_ATTR_VALUE_NEWLINE, ' ');
function escapeDangerHtml5Entities(str) {
return str
.replace(REGEXP_ATTR_VALUE_COLON, ":")
.replace(REGEXP_ATTR_VALUE_NEWLINE, " ");
}
/**
*
清除不可见字符
*
clear nonprintable characters
*
* @param {String} str
* @return {String}
*/
function clearNonPrintableCharacter
(str) {
var str2 =
''
;
function clearNonPrintableCharacter(str) {
var str2 =
""
;
for (var i = 0, len = str.length; i < len; i++) {
str2 += str.charCodeAt(i) < 32 ?
' '
: str.charAt(i);
str2 += str.charCodeAt(i) < 32 ?
" "
: str.charAt(i);
}
return _.trim(str2);
}
/**
*
将标签的属性值转换成一般字符,便于分析
*
get friendly attribute value
*
* @param {String} str
* @return {String}
*/
function friendlyAttrValue
(str) {
str = unescapeQuote(str);
// 双引号
str = escapeHtmlEntities(str);
// 转换HTML实体编码
str = escapeDangerHtml5Entities(str);
// 转换危险的HTML5新增实体编码
str = clearNonPrintableCharacter(str);
// 清除不可见字符
function friendlyAttrValue(str) {
str = unescapeQuote(str);
str = escapeHtmlEntities(str);
str = escapeDangerHtml5Entities(str);
str = clearNonPrintableCharacter(str);
return str;
}
/**
*
转义用于输出的标签属性值
*
unescape attribute value
*
* @param {String} str
* @return {String}
*/
function escapeAttrValue
(str) {
function escapeAttrValue(str) {
str = escapeQuote(str);
str = escapeHtml(str);
return str;
}
/**
*
去掉不在白名单中的标签onIgnoreTag处理方法
*
`onIgnoreTag` function for removing all the tags that are not in whitelist
*/
function onIgnoreTagStripAll
() {
return
''
;
function onIgnoreTagStripAll() {
return
""
;
}
/**
* 删除标签体
* remove tag body
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
*
* @param {array} tags
要删除的标签列表
* @param {function} next
对不在列表中的标签的处理函数,可选
* @param {array} tags
* @param {function} next
*/
function StripTagBody
(tags, next) {
if (typeof
(next) !== 'function'
) {
next = function
() {};
function StripTagBody(tags, next) {
if (typeof
next !== "function"
) {
next = function() {};
}
var isRemoveAllTag = !Array.isArray(tags);
function isRemoveTag
(tag) {
function isRemoveTag(tag) {
if (isRemoveAllTag) return true;
return
(_.indexOf(tags, tag) !== -1)
;
return
_.indexOf(tags, tag) !== -1
;
}
var removeList = [];
// 要删除的位置范围列表
var posStart = false;
// 当前标签开始位置
var removeList = [];
var posStart = false;
return {
onIgnoreTag: function
(tag, html, options) {
onIgnoreTag: function(tag, html, options) {
if (isRemoveTag(tag)) {
if (options.isClosing) {
var ret =
'[/removed]'
;
var ret =
"[/removed]"
;
var end = options.position + ret.length;
removeList.push([posStart !== false ? posStart : options.position, end]);
removeList.push([
posStart !== false ? posStart : options.position,
end
]);
posStart = false;
return ret;
} else {
if (!posStart) {
posStart = options.position;
}
return
'[removed]'
;
return
"[removed]"
;
}
} else {
return next(tag, html, options);
}
},
remove: function
(html) {
var rethtml =
''
;
remove: function(html) {
var rethtml =
""
;
var lastPos = 0;
_.forEach(removeList, function
(pos) {
_.forEach(removeList, function(pos) {
rethtml += html.slice(lastPos, pos[0]);
lastPos = pos[1];
});
...
...
@@ -41740,25 +41762,25 @@ function StripTagBody (tags, next) {
}
/**
*
去除备注标签
*
remove html comments
*
* @param {String} html
* @return {String}
*/
function stripCommentTag
(html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP,
''
);
function stripCommentTag(html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP,
""
);
}
var STRIP_COMMENT_TAG_REGEXP = /<!--[\s\S]*?-->/g;
/**
*
去除不可见字符
*
remove invisible characters
*
* @param {String} html
* @return {String}
*/
function stripBlankChar
(html) {
var chars = html.split(
''
);
chars = chars.filter(function
(char) {
function stripBlankChar(html) {
var chars = html.split(
""
);
chars = chars.filter(function(char) {
var c = char.charCodeAt(0);
if (c === 127) return false;
if (c <= 31) {
...
...
@@ -41767,10 +41789,9 @@ function stripBlankChar (html) {
}
return true;
});
return chars.join(
''
);
return chars.join(
""
);
}
exports.whiteList = getDefaultWhiteList();
exports.getDefaultWhiteList = getDefaultWhiteList;
exports.onTag = onTag;
...
...
@@ -41795,131 +41816,126 @@ exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList;
},{"./util":4,"cssfilter":8}],2:[function(require,module,exports){
/**
*
模块入口
*
xss
*
* @author
老雷
<leizongmin@gmail.com>
* @author
Zongmin Lei
<leizongmin@gmail.com>
*/
var DEFAULT = require('./default');
var parser = require('./parser');
var FilterXSS = require('./xss');
var DEFAULT = require("./default");
var parser = require("./parser");
var FilterXSS = require("./xss");
/**
*
XSS过滤
*
filter xss function
*
* @param {String} html
要过滤的HTML代码
* @param {Object} options
选项:whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml
* @param {String} html
* @param {Object} options
{ whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
* @return {String}
*/
function filterXSS
(html, options) {
function filterXSS(html, options) {
var xss = new FilterXSS(options);
return xss.process(html);
}
// 输出
exports = module.exports = filterXSS;
exports.FilterXSS = FilterXSS;
for (var i in DEFAULT) exports[i] = DEFAULT[i];
for (var i in parser) exports[i] = parser[i];
// 在浏览器端使用
if (typeof window !== 'undefined') {
// using `xss` on the browser, output `filterXSS` to the globals
if (typeof window !== "undefined") {
window.filterXSS = module.exports;
}
},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){
/**
*
简单
HTML Parser
*
Simple
HTML Parser
*
* @author
老雷
<leizongmin@gmail.com>
* @author
Zongmin Lei
<leizongmin@gmail.com>
*/
var _ = require(
'./util'
);
var _ = require(
"./util"
);
/**
*
获取标签的名称
*
get tag name
*
* @param {String} html
如:
'<a hef="#">'
* @param {String} html
e.g.
'<a hef="#">'
* @return {String}
*/
function getTagName
(html) {
var i =
html.indexOf(' '
);
function getTagName(html) {
var i =
_.spaceIndex(html
);
if (i === -1) {
var tagName = html.slice(1, -1);
} else {
var tagName = html.slice(1, i + 1);
}
tagName = _.trim(tagName).toLowerCase();
if (tagName.slice(0, 1) ===
'/'
) tagName = tagName.slice(1);
if (tagName.slice(-1) ===
'/'
) tagName = tagName.slice(0, -1);
if (tagName.slice(0, 1) ===
"/"
) tagName = tagName.slice(1);
if (tagName.slice(-1) ===
"/"
) tagName = tagName.slice(0, -1);
return tagName;
}
/**
*
是否为闭合标签
*
is close tag?
*
* @param {String} html 如:'<a hef="#">'
* @return {Boolean}
*/
function isClosing
(html) {
return
(html.slice(0, 2) === '</')
;
function isClosing(html) {
return
html.slice(0, 2) === "</"
;
}
/**
*
分析HTML代码,调用相应的函数处理,返回处理后的HTML
*
parse input html and returns processed html
*
* @param {String} html
* @param {Function} onTag 处理标签的函数
* 参数格式: function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml 对HTML进行转义的函数
* @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml
* @return {String}
*/
function parseTag
(html, onTag, escapeHtml) {
'user strict'
;
function parseTag(html, onTag, escapeHtml) {
"user strict"
;
var rethtml =
''; // 待返回的HTML
var lastPos = 0;
// 上一个标签结束位置
var tagStart = false;
// 当前标签开始位置
var quoteStart = false;
// 引号开始位置
var currentPos = 0;
// 当前位置
var len = html.length;
// HTML长度
var current
Html = ''; // 当前标签的HTML代码
var current
TagName = ''; // 当前标签的名称
var rethtml =
"";
var lastPos = 0;
var tagStart = false;
var quoteStart = false;
var currentPos = 0;
var len = html.length;
var current
TagName = "";
var current
Html = "";
// 逐个分析字符
for (currentPos = 0; currentPos < len; currentPos++) {
var c = html.charAt(currentPos);
if (tagStart === false) {
if (c ===
'<'
) {
if (c ===
"<"
) {
tagStart = currentPos;
continue;
}
} else {
if (quoteStart === false) {
if (c ===
'<'
) {
if (c ===
"<"
) {
rethtml += escapeHtml(html.slice(lastPos, currentPos));
tagStart = currentPos;
lastPos = currentPos;
continue;
}
if (c ===
'>'
) {
if (c ===
">"
) {
rethtml += escapeHtml(html.slice(lastPos, tagStart));
currentHtml = html.slice(tagStart, currentPos + 1);
currentTagName = getTagName(currentHtml);
rethtml += onTag(tagStart,
rethtml += onTag(
tagStart,
rethtml.length,
currentTagName,
currentHtml,
isClosing(currentHtml));
isClosing(currentHtml)
);
lastPos = currentPos + 1;
tagStart = false;
continue;
}
// HTML标签内的引号仅当前一个字符是等于号时才有效
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === '=') {
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === "=") {
quoteStart = c;
continue;
}
...
...
@@ -41938,45 +41954,46 @@ function parseTag (html, onTag, escapeHtml) {
return rethtml;
}
// 不符合属性名称规则的正则表达式
var REGEXP_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img;
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/gim;
/**
*
分析标签HTML代码,调用相应的函数处理,返回HTML
*
parse input attributes and returns processed attributes
*
* @param {String} html 如标签'<a href="#" target="_blank">' 则为 'href="#" target="_blank"'
* @param {Function} onAttr 处理属性值的函数
* 函数格式: function (name, value)
* @param {String} html e.g. `href="#" target="_blank"`
* @param {Function} onAttr e.g. `function (name, value)`
* @return {String}
*/
function parseAttr
(html, onAttr) {
'user strict'
;
function parseAttr(html, onAttr) {
"user strict"
;
var lastPos = 0;
// 当前位置
var retAttrs = [];
// 待返回的属性列表
var tmpName = false;
// 临时属性名称
var len = html.length;
// HTML代码长度
var lastPos = 0;
var retAttrs = [];
var tmpName = false;
var len = html.length;
function addAttr
(name, value) {
function addAttr(name, value) {
name = _.trim(name);
name = name.replace(REGEXP_
ATTR_NAME, ''
).toLowerCase();
name = name.replace(REGEXP_
ILLEGAL_ATTR_NAME, ""
).toLowerCase();
if (name.length < 1) return;
var ret = onAttr(name, value ||
''
);
var ret = onAttr(name, value ||
""
);
if (ret) retAttrs.push(ret);
}
;
}
// 逐个分析字符
for (var i = 0; i < len; i++) {
var c = html.charAt(i);
var v, j;
if (tmpName === false && c ===
'='
) {
if (tmpName === false && c ===
"="
) {
tmpName = html.slice(lastPos, i);
lastPos = i + 1;
continue;
}
if (tmpName !== false) {
// HTML标签内的引号仅当前一个字符是等于号时才有效
if (i === lastPos && (c === '"' || c === "'") && html.charAt(i - 1) === '=') {
if (
i === lastPos &&
(c === '"' || c === "'") &&
html.charAt(i - 1) === "="
) {
j = html.indexOf(c, i + 1);
if (j === -1) {
break;
...
...
@@ -41990,7 +42007,8 @@ function parseAttr (html, onAttr) {
}
}
}
if (c === ' ') {
if (/\s|\n|\t/.test(c)) {
html = html.replace(/\s|\n|\t/g, " ");
if (tmpName === false) {
j = findNextEqual(html, i);
if (j === -1) {
...
...
@@ -42027,51 +42045,52 @@ function parseAttr (html, onAttr) {
}
}
return _.trim(retAttrs.join(
' '
));
return _.trim(retAttrs.join(
" "
));
}
function findNextEqual
(str, i) {
function findNextEqual(str, i) {
for (; i < str.length; i++) {
var c = str[i];
if (c ===
' '
) continue;
if (c ===
'='
) return i;
if (c ===
" "
) continue;
if (c ===
"="
) return i;
return -1;
}
}
function findBeforeEqual
(str, i) {
function findBeforeEqual(str, i) {
for (; i > 0; i--) {
var c = str[i];
if (c ===
' '
) continue;
if (c ===
'='
) return i;
if (c ===
" "
) continue;
if (c ===
"="
) return i;
return -1;
}
}
function isQuoteWrapString (text) {
if ((text[0] === '"' && text[text.length - 1] === '"') ||
(text[0] === '\'' && text[text.length - 1] === '\'')) {
function isQuoteWrapString(text) {
if (
(text[0] === '"' && text[text.length - 1] === '"') ||
(text[0] === "'" && text[text.length - 1] === "'")
) {
return true;
} else {
return false;
}
}
;
}
function stripQuoteWrap
(text) {
function stripQuoteWrap(text) {
if (isQuoteWrapString(text)) {
return text.substr(1, text.length - 2);
} else {
return text;
}
};
}
exports.parseTag = parseTag;
exports.parseAttr = parseAttr;
},{"./util":4}],4:[function(require,module,exports){
module.exports = {
indexOf: function
(arr, item) {
indexOf: function(arr, item) {
var i, j;
if (Array.prototype.indexOf) {
return arr.indexOf(item);
...
...
@@ -42083,7 +42102,7 @@ module.exports = {
}
return -1;
},
forEach: function
(arr, fn, scope) {
forEach: function(arr, fn, scope) {
var i, j;
if (Array.prototype.forEach) {
return arr.forEach(fn, scope);
...
...
@@ -42092,57 +42111,61 @@ module.exports = {
fn.call(scope, arr[i], i, arr);
}
},
trim: function
(str) {
trim: function(str) {
if (String.prototype.trim) {
return str.trim();
}
return str.replace(/(^\s*)|(\s*$)/g, '');
return str.replace(/(^\s*)|(\s*$)/g, "");
},
spaceIndex: function(str) {
var reg = /\s|\n|\t/;
var match = reg.exec(str);
return match ? match.index : -1;
}
};
},{}],5:[function(require,module,exports){
/**
*
过滤XSS
*
filter xss
*
* @author
老雷
<leizongmin@gmail.com>
* @author
Zongmin Lei
<leizongmin@gmail.com>
*/
var FilterCSS = require(
'cssfilter'
).FilterCSS;
var DEFAULT = require(
'./default'
);
var parser = require(
'./parser'
);
var FilterCSS = require(
"cssfilter"
).FilterCSS;
var DEFAULT = require(
"./default"
);
var parser = require(
"./parser"
);
var parseTag = parser.parseTag;
var parseAttr = parser.parseAttr;
var _ = require('./util');
var _ = require("./util");
/**
*
返回值是否为空
*
returns `true` if the input value is `undefined` or `null`
*
* @param {Object} obj
* @return {Boolean}
*/
function isNull
(obj) {
return
(obj === undefined || obj === null)
;
function isNull(obj) {
return
obj === undefined || obj === null
;
}
/**
*
取标签内的属性列表字符串
*
get attributes for a tag
*
* @param {String} html
* @return {Object}
* - {String} html
* - {Boolean} closing
*/
function getAttrs
(html) {
var i =
html.indexOf(' '
);
function getAttrs(html) {
var i =
_.spaceIndex(html
);
if (i === -1) {
return {
html:
''
,
closing:
(html[html.length - 2] === '/')
html:
""
,
closing:
html[html.length - 2] === "/"
};
}
html = _.trim(html.slice(i + 1, -1));
var isClosing =
(html[html.length - 1] === '/')
;
var isClosing =
html[html.length - 1] === "/"
;
if (isClosing) html = _.trim(html.slice(0, -1));
return {
html: html,
...
...
@@ -42151,12 +42174,12 @@ function getAttrs (html) {
}
/**
*
浅拷贝对象
*
shallow copy
*
* @param {Object} obj
* @return {Object}
*/
function shallowCopyObject
(obj) {
function shallowCopyObject(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
...
...
@@ -42165,20 +42188,22 @@ function shallowCopyObject (obj) {
}
/**
*
XSS过滤对象
*
FilterXSS class
*
* @param {Object} options
*
选项:
whiteList, onTag, onTagAttr, onIgnoreTag,
*
whiteList, onTag, onTagAttr, onIgnoreTag,
* onIgnoreTagAttr, safeAttrValue, escapeHtml
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
* css{whiteList, onAttr, onIgnoreAttr}
css=false表示禁用cssfilter
* css{whiteList, onAttr, onIgnoreAttr}
`css=false` means don't use `cssfilter`
*/
function FilterXSS
(options) {
function FilterXSS(options) {
options = shallowCopyObject(options || {});
if (options.stripIgnoreTag) {
if (options.onIgnoreTag) {
console.error('Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time');
console.error(
'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
);
}
options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
}
...
...
@@ -42201,16 +42226,16 @@ function FilterXSS (options) {
}
/**
*
开始处理
*
start process and returns result
*
* @param {String} html
* @return {String}
*/
FilterXSS.prototype.process = function
(html) {
//
兼容各种奇葩输入
html = html ||
''
;
FilterXSS.prototype.process = function(html) {
//
compatible with the input
html = html ||
""
;
html = html.toString();
if (!html) return
''
;
if (!html) return
""
;
var me = this;
var options = me.options;
...
...
@@ -42223,55 +42248,55 @@ FilterXSS.prototype.process = function (html) {
var escapeHtml = options.escapeHtml;
var cssFilter = me.cssFilter;
//
是否清除不可见字符
//
remove invisible characters
if (options.stripBlankChar) {
html = DEFAULT.stripBlankChar(html);
}
//
是否禁止备注标签
//
remove html comments
if (!options.allowCommentTag) {
html = DEFAULT.stripCommentTag(html);
}
//
如果开启了
stripIgnoreTagBody
//
if enable
stripIgnoreTagBody
var stripIgnoreTagBody = false;
if (options.stripIgnoreTagBody) {
var stripIgnoreTagBody = DEFAULT.StripTagBody(options.stripIgnoreTagBody, onIgnoreTag);
var stripIgnoreTagBody = DEFAULT.StripTagBody(
options.stripIgnoreTagBody,
onIgnoreTag
);
onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
}
var retHtml = parseTag(html, function (sourcePosition, position, tag, html, isClosing) {
var retHtml = parseTag(
html,
function(sourcePosition, position, tag, html, isClosing) {
var info = {
sourcePosition: sourcePosition,
position: position,
isClosing: isClosing,
isWhite: (tag in whiteList
)
isWhite: whiteList.hasOwnProperty(tag
)
};
// 调用onTag处理
// call `onTag()`
var ret = onTag(tag, html, info);
if (!isNull(ret)) return ret;
// 默认标签处理方法
if (info.isWhite) {
// 白名单标签,解析标签属性
// 如果是闭合标签,则不需要解析属性
if (info.isClosing) {
return '</' + tag + '>'
;
return "</" + tag + ">"
;
}
var attrs = getAttrs(html);
var whiteAttrList = whiteList[tag];
var attrsHtml = parseAttr(attrs.html, function (name, value) {
// 调用onTagAttr处理
var isWhiteAttr = (_.indexOf(whiteAttrList, name) !== -1);
var attrsHtml = parseAttr(attrs.html, function(name, value) {
// call `onTagAttr()`
var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
var ret = onTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
// 默认的属性处理方法
if (isWhiteAttr) {
// 白名单属性,调用safeAttrValue过滤属性值
// call `safeAttrValue()`
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
return name + '="' + value + '"';
...
...
@@ -42279,30 +42304,30 @@ FilterXSS.prototype.process = function (html) {
return name;
}
} else {
// 非白名单属性,调用onIgnoreTagAttr处理
// call `onIgnoreTagAttr()`
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
return;
}
});
// 构造新的标签代码
var html = '<'
+ tag;
if (attrsHtml) html += ' '
+ attrsHtml;
if (attrs.closing) html += ' /'
;
html += '>'
;
// build new tag html
var html = "<"
+ tag;
if (attrsHtml) html += " "
+ attrsHtml;
if (attrs.closing) html += " /"
;
html += ">"
;
return html;
} else {
// 非白名单标签,调用onIgnoreTag处理
// call `onIgnoreTag()`
var ret = onIgnoreTag(tag, html, info);
if (!isNull(ret)) return ret;
return escapeHtml(html);
}
},
escapeHtml
);
}, escapeHtml);
// 如果开启了stripIgnoreTagBody,需要对结果再进行处理
// if enable stripIgnoreTagBody
if (stripIgnoreTagBody) {
retHtml = stripIgnoreTagBody.remove(retHtml);
}
...
...
@@ -42310,7 +42335,6 @@ FilterXSS.prototype.process = function (html) {
return retHtml;
};
module.exports = FilterXSS;
},{"./default":1,"./parser":3,"./util":4,"cssfilter":8}],6:[function(require,module,exports){
...
...
@@ -42354,14 +42378,16 @@ function shallowCopyObject (obj) {
*
* @param {Object} options
* - {Object} whiteList
* - {Object} onAttr
* - {Object} onIgnoreAttr
* - {Function} onAttr
* - {Function} onIgnoreAttr
* - {Function} safeAttrValue
*/
function FilterCSS (options) {
options = shallowCopyObject(options || {});
options.whiteList = options.whiteList || DEFAULT.whiteList;
options.onAttr = options.onAttr || DEFAULT.onAttr;
options.onIgnoreAttr = options.onIgnoreAttr || DEFAULT.onIgnoreAttr;
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
this.options = options;
}
...
...
@@ -42376,6 +42402,7 @@ FilterCSS.prototype.process = function (css) {
var whiteList = options.whiteList;
var onAttr = options.onAttr;
var onIgnoreAttr = options.onIgnoreAttr;
var safeAttrValue = options.safeAttrValue;
var retCSS = parseStyle(css, function (sourcePosition, position, name, value, source) {
...
...
@@ -42386,6 +42413,10 @@ FilterCSS.prototype.process = function (css) {
else if (check instanceof RegExp) isWhite = check.test(value);
if (isWhite !== true) isWhite = false;
// 如果过滤后 value 为空则直接忽略
value = safeAttrValue(name, value);
if (!value) return;
var opts = {
position: position,
sourcePosition: sourcePosition,
...
...
@@ -42797,11 +42828,26 @@ function onIgnoreAttr (name, value, options) {
// do nothing
}
var REGEXP_URL_JAVASCRIPT = /javascript\s*\:/img;
/**
* 过滤属性值
*
* @param {String} name
* @param {String} value
* @return {String}
*/
function safeAttrValue(name, value) {
if (REGEXP_URL_JAVASCRIPT.test(value)) return '';
return value;
}
exports.whiteList = getDefaultWhiteList();
exports.getDefaultWhiteList = getDefaultWhiteList;
exports.onAttr = onAttr;
exports.onIgnoreAttr = onIgnoreAttr;
exports.safeAttrValue = safeAttrValue;
},{}],8:[function(require,module,exports){
/**
...
...
@@ -44043,7 +44089,7 @@ return __p
'xmlns': Strophe.NS.FORWARD
}).c('delay', {
'xmns': Strophe.NS.DELAY,
'stamp': moment.format()
'stamp': moment
()
.format()
}).up().cnode(messageStanza.tree()));
}
},
...
...
@@ -44317,14 +44363,7 @@ return __p
return;
}
var that = this;
u.fadeIn(this.el, function () {
that.afterShown();
if (focus) {
that.focus();
}
});
u.fadeIn(this.el, _.bind(this.afterShown, this, focus));
},
show: function show(focus) {
if (_.isUndefined(this.debouncedShow)) {
...
...
@@ -47189,7 +47228,7 @@ return __p
},
render: function render() {
// Replace the default dropdown with something nicer
var
select = this.el.querySelector('select#select-xmpp-status
');
var
options = this.el.querySelectorAll('#select-xmpp-status option
');
var chat_status = this.model.get('status') || 'offline';
this.el.innerHTML = tpl_choose_status();
this.el.querySelector('#fancy-xmpp-status-select').innerHTML = tpl_chat_status({
...
...
@@ -47199,14 +47238,14 @@ return __p
'desc_change_status': __('Click to change your chat status')
}); // iterate through all the <option> elements and add option values
var options_list = _.map(
select.querySelectorAll('option')
, function (el) {
var options_list = _.map(
options
, function (el) {
return tpl_status_option({
'value': el.value,
'text': el.text
});
});
var options_target = this.el.querySelector("
#target dd ul
");
var options_target = this.el.querySelector("
.xmpp-status-menu
");
options_target.classList.add('collapsed');
options_target.innerHTML = options_list.join('');
return this;
...
...
@@ -47368,13 +47407,17 @@ return __p
clearSession: function clearSession() {
this.__super__.clearSession.apply(this, arguments);
var controlbox = this.chatboxes.get('controlbox');
var chatboxes = _.get(this, 'chatboxes', null);
if (!_.isNil(chatboxes)) {
var controlbox = chatboxes.get('controlbox');
if (controlbox && controlbox.collection && controlbox.collection.browserStorage) {
controlbox.save({
'connected': false
});
}
}
},
ChatBoxes: {
chatBoxMayBeShown: function chatBoxMayBeShown(chatbox) {
...
...
@@ -47866,6 +47909,10 @@ return __p
renderTab: function renderTab() {
var controlbox = _converse.chatboxes.get('controlbox');
if (_.isNil(controlbox)) {
return;
}
var chats = fp.filter(_.partial(u.isOfType, CHATBOX_TYPE), _converse.chatboxes.models);
this.tab_el.innerHTML = tpl_contacts_tab({
'label_contacts': LABEL_CONTACTS,
...
...
@@ -50235,7 +50282,7 @@ Strophe.addConnectionPlugin('disco',
this.items.sort();
var list_el = this.el.querySelector(this.listSelector);
var div = document.createElement('div');
list_el.
replaceWith(div
);
list_el.
parentNode.replaceChild(div, list_el
);
this.items.each(function (item) {
var view = _this.get(item.get(_this.subviewIndex));
...
...
@@ -50245,7 +50292,7 @@ Strophe.addConnectionPlugin('disco',
list_el.insertAdjacentElement('beforeend', view.el);
});
div.
replaceWith(list_el
);
div.
parentNode.replaceChild(list_el, div
);
}
});
return Backbone.OrderedListView;
...
...
@@ -51733,7 +51780,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
description: this.model.get('description') || ''
}));
},
afterShown: function afterShown() {
afterShown: function afterShown(
focus
) {
/* Override from converse-chatview, specifically to avoid
* the 'active' chat state from being sent out prematurely.
*
...
...
@@ -51746,6 +51793,11 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
}
this.occupantsview.setOccupantsHeight();
this.scrollDown();
if (focus) {
this.focus();
}
},
show: function show(focus) {
if (u.isVisible(this.el)) {
...
...
@@ -51759,11 +51811,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
u.showElement(this.el);
this.afterShown();
if (focus) {
this.focus();
}
this.afterShown(focus);
},
afterConnected: function afterConnected() {
if (this.model.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
...
...
@@ -53947,6 +53995,23 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
input_el.classList.remove('hidden');
this.removeSpinner();
},
roomStanzaItemToHTMLElement: function roomStanzaItemToHTMLElement(room) {
if (!u.isValidJID(room.getAttribute('jid'), '@')) {
// Some XMPP servers return the MUC service in
// the list of rooms (see #1003).
return null;
}
var name = Strophe.unescapeNode(room.getAttribute('name') || room.getAttribute('jid'));
var div = document.createElement('div');
div.innerHTML = tpl_room_item({
'name': name,
'jid': room.getAttribute('jid'),
'open_title': __('Click to open this room'),
'info_title': __('Show more information on this room')
});
return div.firstChild;
},
onRoomsFound: function onRoomsFound(iq) {
/* Handle the IQ stanza returned from the server, containing
* all its public rooms.
...
...
@@ -53960,19 +54025,13 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
available_chatrooms.innerHTML = tpl_rooms_results({
'feedback_text': __('Rooms found')
});
var div = document.createElement('div');
var fragment = document.createDocumentFragment();
for (var i = 0; i < this.rooms.length; i++) {
var name = Strophe.unescapeNode(this.rooms[i].getAttribute('name') || this.rooms[i].getAttribute('jid'));
div.innerHTML = tpl_room_item({
'name': name,
'jid': this.rooms[i].getAttribute('jid'),
'open_title': __('Click to open this room'),
'info_title': __('Show more information on this room')
var children = _.reject(_.map(this.rooms, this.roomStanzaItemToHTMLElement), _.isNil);
_.each(children, function (child) {
return fragment.appendChild(child);
});
fragment.appendChild(div.firstChild);
}
available_chatrooms.appendChild(fragment);
var input_el = this.el.querySelector('input#show-rooms');
...
...
@@ -55569,6 +55628,11 @@ Strophe.RSM.prototype = {
var messages = [];
var message_handler = _converse.connection.addHandler(function (message) {
if (options.groupchat && message.getAttribute('from') !== options['with']) {
// eslint-disable-line dot-notation
return true;
}
var result = message.querySelector('result');
if (!_.isNull(result) && result.getAttribute('queryid') === queryid) {
...
...
@@ -64208,7 +64272,7 @@ return __p
}
form.querySelector('input[type=submit]').classList.add('hidden');
this.fetchRegistrationForm(domain);
this.fetchRegistrationForm(domain
.trim()
);
},
fetchRegistrationForm: function fetchRegistrationForm(domain_name) {
/* This is called with a domain name based on which, it fetches a
...
...
@@ -64465,7 +64529,8 @@ return __p
var inputs = sizzle(':input:not([type=button]):not([type=submit])', form),
iq = $iq({
type: "set"
'type': 'set',
'id': _converse.connection.getUniqueId()
}).c("query", {
xmlns: Strophe.NS.REGISTER
});
...
...
@@ -65916,7 +65981,11 @@ return __p
height = "";
}
this.el.querySelector('.box-flyout').style.height = height;
var flyout_el = this.el.querySelector('.box-flyout');
if (!_.isNull(flyout_el)) {
flyout_el.style.height = height;
}
},
setChatBoxWidth: function setChatBoxWidth(width) {
var _converse = this.__super__._converse;
...
...
@@ -65928,7 +65997,11 @@ return __p
}
this.el.style.width = width;
this.el.querySelector('.box-flyout').style.width = width;
var flyout_el = this.el.querySelector('.box-flyout');
if (!_.isNull(flyout_el)) {
flyout_el.style.width = width;
}
},
adjustToViewport: function adjustToViewport() {
/* Event handler called when viewport gets resized. We remove
src/converse-profile.js
View file @
476e1d79
...
...
@@ -52,7 +52,7 @@
render
()
{
// Replace the default dropdown with something nicer
const
select
=
this
.
el
.
querySelector
(
'
select#select-xmpp-status
'
)
const
options
=
this
.
el
.
querySelectorAll
(
'
#select-xmpp-status option
'
);
const
chat_status
=
this
.
model
.
get
(
'
status
'
)
||
'
offline
'
;
this
.
el
.
innerHTML
=
tpl_choose_status
();
...
...
@@ -67,15 +67,10 @@
// iterate through all the <option> elements and add option values
const
options_list
=
_
.
map
(
select
.
querySelectorAll
(
'
option
'
),
function
(
el
)
{
return
tpl_status_option
({
'
value
'
:
el
.
value
,
'
text
'
:
el
.
text
});
}
options
,
(
el
)
=>
tpl_status_option
({
'
value
'
:
el
.
value
,
'
text
'
:
el
.
text
})
);
const
options_target
=
this
.
el
.
querySelector
(
"
#target dd ul
"
);
const
options_target
=
this
.
el
.
querySelector
(
"
.xmpp-status-menu
"
);
options_target
.
classList
.
add
(
'
collapsed
'
);
options_target
.
innerHTML
=
options_list
.
join
(
''
);
return
this
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment