You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2019/07/30 08:53:39 UTC
[openmeetings] branch master updated: [OPENMEETINGS-2093]
kurento-utils and fabricjs are updated
This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/master by this push:
new 5667d5a [OPENMEETINGS-2093] kurento-utils and fabricjs are updated
5667d5a is described below
commit 5667d5adfacebbb4d0944c390e49243ecbf59a3b
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Tue Jul 30 15:53:24 2019 +0700
[OPENMEETINGS-2093] kurento-utils and fabricjs are updated
---
.../apache/openmeetings/web/room/kurento-utils.js | 1623 +++++++-------------
.../openmeetings/web/room/raw-video-manager.js | 14 +-
.../org/apache/openmeetings/web/room/raw-video.js | 4 +-
.../org/apache/openmeetings/web/room/wb/fabric.js | 1471 ++++++++++--------
.../openmeetings/web/room/wb/raw-tool-base.js | 2 +-
.../openmeetings/web/room/wb/raw-wb-board.js | 4 +-
6 files changed, 1389 insertions(+), 1729 deletions(-)
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/kurento-utils.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/kurento-utils.js
index 3c1fc36..4d64cd9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/kurento-utils.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/kurento-utils.js
@@ -2,7 +2,7 @@
var freeice = require('freeice');
var inherits = require('inherits');
var UAParser = require('ua-parser-js');
-window.UUID = require('uuid');
+window.uuidv4 = require('uuid/v4');
var hark = require('hark');
var EventEmitter = require('events').EventEmitter;
var recursive = require('merge').recursive.bind(undefined, true);
@@ -51,14 +51,22 @@ var dumpSDP = function (description) {
};
function bufferizeCandidates(pc, onerror) {
var candidatesQueue = [];
- pc.addEventListener('signalingstatechange', function () {
- if (this.signalingState === 'stable') {
+ function setSignalingstatechangeAccordingWwebBrowser(functionToExecute, pc) {
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ pc.onsignalingstatechange = functionToExecute;
+ } else {
+ pc.addEventListener('signalingstatechange', functionToExecute);
+ }
+ }
+ var signalingstatechangeFunction = function () {
+ if (pc.signalingState === 'stable') {
while (candidatesQueue.length) {
var entry = candidatesQueue.shift();
pc.addIceCandidate(entry.candidate, entry.callback, entry.callback);
}
}
- });
+ };
+ setSignalingstatechangeAccordingWwebBrowser(signalingstatechangeFunction, pc);
return function (candidate, callback) {
callback = callback || onerror;
switch (pc.signalingState) {
@@ -111,6 +119,21 @@ function getSimulcastInfo(videoStream) {
lines.push('');
return lines.join('\n');
}
+function sleep(milliseconds) {
+ var start = new Date().getTime();
+ for (var i = 0; i < 10000000; i++) {
+ if (new Date().getTime() - start > milliseconds) {
+ break;
+ }
+ }
+}
+function setIceCandidateAccordingWebBrowser(functionToExecute, pc) {
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ pc.onicecandidate = functionToExecute;
+ } else {
+ pc.addEventListener('icecandidate', functionToExecute);
+ }
+}
function WebRtcPeer(mode, options, callback) {
if (!(this instanceof WebRtcPeer)) {
return new WebRtcPeer(mode, options, callback);
@@ -134,7 +157,7 @@ function WebRtcPeer(mode, options, callback) {
var dataChannelConfig = options.dataChannelConfig;
var useDataChannels = options.dataChannels || false;
var dataChannel;
- var guid = UUID.v4();
+ var guid = uuidv4();
var configuration = recursive({ iceServers: freeice() }, options.configuration);
var onicecandidate = options.onicecandidate;
if (onicecandidate)
@@ -207,24 +230,24 @@ function WebRtcPeer(mode, options, callback) {
}
}
if (!pc.getLocalStreams && pc.getSenders) {
- pc.getLocalStreams = function() {
+ pc.getLocalStreams = function () {
var stream = new MediaStream();
- pc.getSenders().forEach(function(sender) {
+ pc.getSenders().forEach(function (sender) {
stream.addTrack(sender.track);
});
return [stream];
};
}
if (!pc.getRemoteStreams && pc.getReceivers) {
- pc.getRemoteStreams = function() {
+ pc.getRemoteStreams = function () {
var stream = new MediaStream();
- pc.getReceivers().forEach(function(sender) {
+ pc.getReceivers().forEach(function (sender) {
stream.addTrack(sender.track);
});
return [stream];
};
}
- pc.addEventListener('icecandidate', function (event) {
+ var iceCandidateFunction = function (event) {
var candidate = event.candidate;
if (EventEmitter.listenerCount(self, 'icecandidate') || EventEmitter.listenerCount(self, 'candidategatheringdone')) {
if (candidate) {
@@ -234,10 +257,16 @@ function WebRtcPeer(mode, options, callback) {
} else {
cand = candidate;
}
- self.emit('icecandidate', cand);
+ if (typeof AdapterJS === 'undefined') {
+ self.emit('icecandidate', cand);
+ }
candidategatheringdone = false;
} else if (!candidategatheringdone) {
- self.emit('candidategatheringdone');
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ EventEmitter.prototype.emit('candidategatheringdone', cand);
+ } else {
+ self.emit('candidategatheringdone');
+ }
candidategatheringdone = true;
}
} else if (!candidategatheringdone) {
@@ -245,7 +274,8 @@ function WebRtcPeer(mode, options, callback) {
if (!candidate)
candidategatheringdone = true;
}
- });
+ };
+ setIceCandidateAccordingWebBrowser(iceCandidateFunction, pc);
pc.onaddstream = options.onaddstream;
pc.onnegotiationneeded = options.onnegotiationneeded;
this.on('newListener', function (event, listener) {
@@ -282,21 +312,46 @@ function WebRtcPeer(mode, options, callback) {
offerToReceiveAudio: mode !== 'sendonly' && offerAudio,
offerToReceiveVideo: mode !== 'sendonly' && offerVideo
};
+ if (mode === 'recvonly' && pc.addTransceiver && browserDependantConstraints.offerToReceiveAudio) {
+ pc.addTransceiver('audio');
+ }
+ if (mode === 'recvonly' && pc.addTransceiver && browserDependantConstraints.offerToReceiveVideo) {
+ pc.addTransceiver('video');
+ }
var constraints = browserDependantConstraints;
logger.debug('constraints: ' + JSON.stringify(constraints));
- pc.createOffer(constraints).then(function (offer) {
- logger.debug('Created SDP offer');
- offer = mangleSdpToAddSimulcast(offer);
- return pc.setLocalDescription(offer);
- }).then(function () {
- var localDescription = pc.localDescription;
- logger.debug('Local description set', localDescription.sdp);
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription);
- logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
- }
- callback(null, localDescription.sdp, self.processAnswer.bind(self));
- }).catch(callback);
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ var setLocalDescriptionOnSuccess = function () {
+ sleep(1000);
+ var localDescription = pc.localDescription;
+ logger.debug('Local description set', localDescription.sdp);
+ if (multistream && usePlanB) {
+ localDescription = interop.toUnifiedPlan(localDescription);
+ logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
+ }
+ callback(null, localDescription.sdp, self.processAnswer.bind(self));
+ };
+ var createOfferOnSuccess = function (offer) {
+ logger.debug('Created SDP offer');
+ logger.debug('Local description set', pc.localDescription);
+ pc.setLocalDescription(offer, setLocalDescriptionOnSuccess, callback);
+ };
+ pc.createOffer(createOfferOnSuccess, callback, constraints);
+ } else {
+ pc.createOffer(constraints).then(function (offer) {
+ logger.debug('Created SDP offer');
+ offer = mangleSdpToAddSimulcast(offer);
+ return pc.setLocalDescription(offer);
+ }).then(function () {
+ var localDescription = pc.localDescription;
+ logger.debug('Local description set', localDescription.sdp);
+ if (multistream && usePlanB) {
+ localDescription = interop.toUnifiedPlan(localDescription);
+ logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
+ }
+ callback(null, localDescription.sdp, self.processAnswer.bind(self));
+ }).catch(callback);
+ }
};
this.getLocalSessionDescriptor = function () {
return pc.localDescription;
@@ -310,12 +365,19 @@ function WebRtcPeer(mode, options, callback) {
var stream = pc.getRemoteStreams()[0];
remoteVideo.srcObject = stream;
logger.debug('Remote stream:', stream);
- remoteVideo.load();
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ remoteVideo = attachMediaStream(remoteVideo, stream);
+ } else {
+ remoteVideo.load();
+ }
}
}
this.showLocalVideo = function () {
localVideo.srcObject = videoStream;
localVideo.muted = true;
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ localVideo = attachMediaStream(localVideo, videoStream);
+ }
};
this.send = function (data) {
if (dataChannel && dataChannel.readyState === 'open') {
@@ -399,12 +461,12 @@ function WebRtcPeer(mode, options, callback) {
self.showLocalVideo();
}
if (videoStream) {
- videoStream.getTracks().forEach(function(track) {
+ videoStream.getTracks().forEach(function (track) {
pc.addTrack(track, videoStream);
});
}
if (audioStream) {
- audioStream.getTracks().forEach(function(track) {
+ audioStream.getTracks().forEach(function (track) {
pc.addTrack(track, audioStream);
});
}
@@ -419,10 +481,17 @@ function WebRtcPeer(mode, options, callback) {
if (constraints === undefined) {
constraints = MEDIA_CONSTRAINTS;
}
- navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
- videoStream = stream;
- start();
- }).catch(callback);
+ if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
+ navigator.getUserMedia(constraints, function (stream) {
+ videoStream = stream;
+ start();
+ }, callback);
+ } else {
+ navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
+ videoStream = stream;
+ start();
+ }).catch(callback);
+ }
}
if (sendSource === 'webcam') {
getMedia(mediaConstraints);
@@ -442,13 +511,17 @@ function WebRtcPeer(mode, options, callback) {
if (localVideo) {
localVideo.pause();
localVideo.srcObject = null;
- localVideo.load();
+ if (typeof AdapterJS === 'undefined') {
+ localVideo.load();
+ }
localVideo.muted = false;
}
if (remoteVideo) {
remoteVideo.pause();
remoteVideo.srcObject = null;
- remoteVideo.load();
+ if (typeof AdapterJS === 'undefined') {
+ remoteVideo.load();
+ }
}
self.removeAllListeners();
if (window.cancelChooseDesktopMedia !== undefined) {
@@ -527,7 +600,9 @@ WebRtcPeer.prototype.dispose = function () {
} catch (err) {
logger.warn('Exception disposing webrtc peer ' + err);
}
- this.emit('_dispose');
+ if (typeof AdapterJS === 'undefined') {
+ this.emit('_dispose');
+ }
};
function WebRtcPeerRecvonly(options, callback) {
if (!(this instanceof WebRtcPeerRecvonly)) {
@@ -559,141 +634,13 @@ exports.WebRtcPeerSendonly = WebRtcPeerSendonly;
exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
exports.hark = harkUtils;
exports.browser = browser;
-},{"events":7,"freeice":4,"hark":8,"inherits":9,"kurento-browser-extensions":10,"merge":11,"sdp-translator":18,"ua-parser-js":21,"uuid":23}],2:[function(require,module,exports){
+},{"events":4,"freeice":5,"hark":8,"inherits":9,"kurento-browser-extensions":10,"merge":11,"sdp-translator":18,"ua-parser-js":21,"uuid/v4":24}],2:[function(require,module,exports){
if (window.addEventListener)
module.exports = require('./index');
},{"./index":3}],3:[function(require,module,exports){
var WebRtcPeer = require('./WebRtcPeer');
exports.WebRtcPeer = WebRtcPeer;
},{"./WebRtcPeer":1}],4:[function(require,module,exports){
-/* jshint node: true */
-'use strict';
-
-var normalice = require('normalice');
-
-/**
- # freeice
-
- The `freeice` module is a simple way of getting random STUN or TURN server
- for your WebRTC application. The list of servers (just STUN at this stage)
- were sourced from this [gist](https://gist.github.com/zziuni/3741933).
-
- ## Example Use
-
- The following demonstrates how you can use `freeice` with
- [rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):
-
- <<< examples/quickconnect.js
-
- As the `freeice` module generates ice servers in a list compliant with the
- WebRTC spec you will be able to use it with raw `RTCPeerConnection`
- constructors and other WebRTC libraries.
-
- ## Hey, don't use my STUN/TURN server!
-
- If for some reason your free STUN or TURN server ends up in the
- list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or
- [turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))
- that is used in this module, you can feel
- free to open an issue on this repository and those servers will be removed
- within 24 hours (or sooner). This is the quickest and probably the most
- polite way to have something removed (and provides us some visibility
- if someone opens a pull request requesting that a server is added).
-
- ## Please add my server!
-
- If you have a server that you wish to add to the list, that's awesome! I'm
- sure I speak on behalf of a whole pile of WebRTC developers who say thanks.
- To get it into the list, feel free to either open a pull request or if you
- find that process a bit daunting then just create an issue requesting
- the addition of the server (make sure you provide all the details, and if
- you have a Terms of Service then including that in the PR/issue would be
- awesome).
-
- ## I know of a free server, can I add it?
-
- Sure, if you do your homework and make sure it is ok to use (I'm currently
- in the process of reviewing the terms of those STUN servers included from
- the original list). If it's ok to go, then please see the previous entry
- for how to add it.
-
- ## Current List of Servers
-
- * current as at the time of last `README.md` file generation
-
- ### STUN
-
- <<< stun.json
-
- ### TURN
-
- <<< turn.json
-
-**/
-
-var freeice = function(opts) {
- // if a list of servers has been provided, then use it instead of defaults
- var servers = {
- stun: (opts || {}).stun || require('./stun.json'),
- turn: (opts || {}).turn || require('./turn.json')
- };
-
- var stunCount = (opts || {}).stunCount || 2;
- var turnCount = (opts || {}).turnCount || 0;
- var selected;
-
- function getServers(type, count) {
- var out = [];
- var input = [].concat(servers[type]);
- var idx;
-
- while (input.length && out.length < count) {
- idx = (Math.random() * input.length) | 0;
- out = out.concat(input.splice(idx, 1));
- }
-
- return out.map(function(url) {
- //If it's a not a string, don't try to "normalice" it otherwise using type:url will screw it up
- if ((typeof url !== 'string') && (! (url instanceof String))) {
- return url;
- } else {
- return normalice(type + ':' + url);
- }
- });
- }
-
- // add stun servers
- selected = [].concat(getServers('stun', stunCount));
-
- if (turnCount) {
- selected = selected.concat(getServers('turn', turnCount));
- }
-
- return selected;
-};
-
-module.exports = freeice;
-},{"./stun.json":5,"./turn.json":6,"normalice":12}],5:[function(require,module,exports){
-module.exports=[
- "stun.l.google.com:19302",
- "stun1.l.google.com:19302",
- "stun2.l.google.com:19302",
- "stun3.l.google.com:19302",
- "stun4.l.google.com:19302",
- "stun.ekiga.net",
- "stun.ideasip.com",
- "stun.schlund.de",
- "stun.stunprotocol.org:3478",
- "stun.voiparound.com",
- "stun.voipbuster.com",
- "stun.voipstunt.com",
- "stun.voxgratia.org"
-]
-
-},{}],6:[function(require,module,exports){
-module.exports=[]
-
-},{}],7:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@@ -715,16 +662,8 @@ module.exports=[]
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
-var objectCreate = Object.create || objectCreatePolyfill
-var objectKeys = Object.keys || objectKeysPolyfill
-var bind = Function.prototype.bind || functionBindPolyfill
-
function EventEmitter() {
- if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- }
-
+ this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
@@ -737,486 +676,401 @@ EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
-var defaultMaxListeners = 10;
-
-var hasDefineProperty;
-try {
- var o = {};
- if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
- hasDefineProperty = o.x === 0;
-} catch (err) { hasDefineProperty = false }
-if (hasDefineProperty) {
- Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
- enumerable: true,
- get: function() {
- return defaultMaxListeners;
- },
- set: function(arg) {
- // check whether the input is a positive number (whose value is zero or
- // greater and not a NaN).
- if (typeof arg !== 'number' || arg < 0 || arg !== arg)
- throw new TypeError('"defaultMaxListeners" must be a positive number');
- defaultMaxListeners = arg;
- }
- });
-} else {
- EventEmitter.defaultMaxListeners = defaultMaxListeners;
-}
+EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
-EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
- if (typeof n !== 'number' || n < 0 || isNaN(n))
- throw new TypeError('"n" argument must be a positive number');
+EventEmitter.prototype.setMaxListeners = function(n) {
+ if (!isNumber(n) || n < 0 || isNaN(n))
+ throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
-function $getMaxListeners(that) {
- if (that._maxListeners === undefined)
- return EventEmitter.defaultMaxListeners;
- return that._maxListeners;
-}
-
-EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
- return $getMaxListeners(this);
-};
+EventEmitter.prototype.emit = function(type) {
+ var er, handler, len, args, i, listeners;
-// These standalone emit* functions are used to optimize calling of event
-// handlers for fast cases because emit() itself often has a variable number of
-// arguments and can be deoptimized because of that. These functions always have
-// the same number of arguments and thus do not get deoptimized, so the code
-// inside them can execute faster.
-function emitNone(handler, isFn, self) {
- if (isFn)
- handler.call(self);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self);
- }
-}
-function emitOne(handler, isFn, self, arg1) {
- if (isFn)
- handler.call(self, arg1);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1);
- }
-}
-function emitTwo(handler, isFn, self, arg1, arg2) {
- if (isFn)
- handler.call(self, arg1, arg2);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2);
- }
-}
-function emitThree(handler, isFn, self, arg1, arg2, arg3) {
- if (isFn)
- handler.call(self, arg1, arg2, arg3);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2, arg3);
- }
-}
-
-function emitMany(handler, isFn, self, args) {
- if (isFn)
- handler.apply(self, args);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].apply(self, args);
- }
-}
-
-EventEmitter.prototype.emit = function emit(type) {
- var er, handler, len, args, i, events;
- var doError = (type === 'error');
-
- events = this._events;
- if (events)
- doError = (doError && events.error == null);
- else if (!doError)
- return false;
+ if (!this._events)
+ this._events = {};
// If there is no 'error' event listener then throw.
- if (doError) {
- if (arguments.length > 1)
+ if (type === 'error') {
+ if (!this._events.error ||
+ (isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
- if (er instanceof Error) {
- throw er; // Unhandled 'error' event
- } else {
- // At least give some kind of context to the user
- var err = new Error('Unhandled "error" event. (' + er + ')');
- err.context = er;
- throw err;
+ if (er instanceof Error) {
+ throw er; // Unhandled 'error' event
+ } else {
+ // At least give some kind of context to the user
+ var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
+ err.context = er;
+ throw err;
+ }
}
- return false;
}
- handler = events[type];
+ handler = this._events[type];
- if (!handler)
+ if (isUndefined(handler))
return false;
- var isFn = typeof handler === 'function';
- len = arguments.length;
- switch (len) {
+ if (isFunction(handler)) {
+ switch (arguments.length) {
// fast cases
- case 1:
- emitNone(handler, isFn, this);
- break;
- case 2:
- emitOne(handler, isFn, this, arguments[1]);
- break;
- case 3:
- emitTwo(handler, isFn, this, arguments[1], arguments[2]);
- break;
- case 4:
- emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
- break;
+ case 1:
+ handler.call(this);
+ break;
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
// slower
- default:
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- emitMany(handler, isFn, this, args);
+ default:
+ args = Array.prototype.slice.call(arguments, 1);
+ handler.apply(this, args);
+ }
+ } else if (isObject(handler)) {
+ args = Array.prototype.slice.call(arguments, 1);
+ listeners = handler.slice();
+ len = listeners.length;
+ for (i = 0; i < len; i++)
+ listeners[i].apply(this, args);
}
return true;
};
-function _addListener(target, type, listener, prepend) {
+EventEmitter.prototype.addListener = function(type, listener) {
var m;
- var events;
- var existing;
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
- events = target._events;
- if (!events) {
- events = target._events = objectCreate(null);
- target._eventsCount = 0;
- } else {
- // To avoid recursion in the case that type === "newListener"! Before
- // adding it to the listeners, first emit "newListener".
- if (events.newListener) {
- target.emit('newListener', type,
- listener.listener ? listener.listener : listener);
-
- // Re-assign `events` because a newListener handler could have caused the
- // this._events to be assigned to a new object
- events = target._events;
- }
- existing = events[type];
- }
+ if (!this._events)
+ this._events = {};
+
+ // To avoid recursion in the case that type === "newListener"! Before
+ // adding it to the listeners, first emit "newListener".
+ if (this._events.newListener)
+ this.emit('newListener', type,
+ isFunction(listener.listener) ?
+ listener.listener : listener);
- if (!existing) {
+ if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
- existing = events[type] = listener;
- ++target._eventsCount;
- } else {
- if (typeof existing === 'function') {
- // Adding the second element, need to change to array.
- existing = events[type] =
- prepend ? [listener, existing] : [existing, listener];
+ this._events[type] = listener;
+ else if (isObject(this._events[type]))
+ // If we've already got an array, just append.
+ this._events[type].push(listener);
+ else
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+
+ // Check for listener leak
+ if (isObject(this._events[type]) && !this._events[type].warned) {
+ if (!isUndefined(this._maxListeners)) {
+ m = this._maxListeners;
} else {
- // If we've already got an array, just append.
- if (prepend) {
- existing.unshift(listener);
- } else {
- existing.push(listener);
- }
+ m = EventEmitter.defaultMaxListeners;
}
- // Check for listener leak
- if (!existing.warned) {
- m = $getMaxListeners(target);
- if (m && m > 0 && existing.length > m) {
- existing.warned = true;
- var w = new Error('Possible EventEmitter memory leak detected. ' +
- existing.length + ' "' + String(type) + '" listeners ' +
- 'added. Use emitter.setMaxListeners() to ' +
- 'increase limit.');
- w.name = 'MaxListenersExceededWarning';
- w.emitter = target;
- w.type = type;
- w.count = existing.length;
- if (typeof console === 'object' && console.warn) {
- console.warn('%s: %s', w.name, w.message);
- }
+ if (m && m > 0 && this._events[type].length > m) {
+ this._events[type].warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ this._events[type].length);
+ if (typeof console.trace === 'function') {
+ // not supported in IE 10
+ console.trace();
}
}
}
- return target;
-}
-
-EventEmitter.prototype.addListener = function addListener(type, listener) {
- return _addListener(this, type, listener, false);
+ return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
-EventEmitter.prototype.prependListener =
- function prependListener(type, listener) {
- return _addListener(this, type, listener, true);
- };
+EventEmitter.prototype.once = function(type, listener) {
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
-function onceWrapper() {
- if (!this.fired) {
- this.target.removeListener(this.type, this.wrapFn);
- this.fired = true;
- switch (arguments.length) {
- case 0:
- return this.listener.call(this.target);
- case 1:
- return this.listener.call(this.target, arguments[0]);
- case 2:
- return this.listener.call(this.target, arguments[0], arguments[1]);
- case 3:
- return this.listener.call(this.target, arguments[0], arguments[1],
- arguments[2]);
- default:
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i)
- args[i] = arguments[i];
- this.listener.apply(this.target, args);
+ var fired = false;
+
+ function g() {
+ this.removeListener(type, g);
+
+ if (!fired) {
+ fired = true;
+ listener.apply(this, arguments);
}
}
-}
-function _onceWrap(target, type, listener) {
- var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
- var wrapped = bind.call(onceWrapper, state);
- wrapped.listener = listener;
- state.wrapFn = wrapped;
- return wrapped;
-}
+ g.listener = listener;
+ this.on(type, g);
-EventEmitter.prototype.once = function once(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.on(type, _onceWrap(this, type, listener));
return this;
};
-EventEmitter.prototype.prependOnceListener =
- function prependOnceListener(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.prependListener(type, _onceWrap(this, type, listener));
+// emits a 'removeListener' event iff the listener was removed
+EventEmitter.prototype.removeListener = function(type, listener) {
+ var list, position, length, i;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events || !this._events[type])
+ return this;
+
+ list = this._events[type];
+ length = list.length;
+ position = -1;
+
+ if (list === listener ||
+ (isFunction(list.listener) && list.listener === listener)) {
+ delete this._events[type];
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+
+ } else if (isObject(list)) {
+ for (i = length; i-- > 0;) {
+ if (list[i] === listener ||
+ (list[i].listener && list[i].listener === listener)) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position < 0)
return this;
- };
-// Emits a 'removeListener' event if and only if the listener was removed.
-EventEmitter.prototype.removeListener =
- function removeListener(type, listener) {
- var list, events, position, i, originalListener;
+ if (list.length === 1) {
+ list.length = 0;
+ delete this._events[type];
+ } else {
+ list.splice(position, 1);
+ }
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+ }
- events = this._events;
- if (!events)
- return this;
+ return this;
+};
- list = events[type];
- if (!list)
- return this;
+EventEmitter.prototype.removeAllListeners = function(type) {
+ var key, listeners;
- if (list === listener || list.listener === listener) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else {
- delete events[type];
- if (events.removeListener)
- this.emit('removeListener', type, list.listener || listener);
- }
- } else if (typeof list !== 'function') {
- position = -1;
+ if (!this._events)
+ return this;
- for (i = list.length - 1; i >= 0; i--) {
- if (list[i] === listener || list[i].listener === listener) {
- originalListener = list[i].listener;
- position = i;
- break;
- }
- }
+ // not listening for removeListener, no need to emit
+ if (!this._events.removeListener) {
+ if (arguments.length === 0)
+ this._events = {};
+ else if (this._events[type])
+ delete this._events[type];
+ return this;
+ }
+
+ // emit removeListener for all listeners on all events
+ if (arguments.length === 0) {
+ for (key in this._events) {
+ if (key === 'removeListener') continue;
+ this.removeAllListeners(key);
+ }
+ this.removeAllListeners('removeListener');
+ this._events = {};
+ return this;
+ }
- if (position < 0)
- return this;
+ listeners = this._events[type];
- if (position === 0)
- list.shift();
- else
- spliceOne(list, position);
+ if (isFunction(listeners)) {
+ this.removeListener(type, listeners);
+ } else if (listeners) {
+ // LIFO order
+ while (listeners.length)
+ this.removeListener(type, listeners[listeners.length - 1]);
+ }
+ delete this._events[type];
- if (list.length === 1)
- events[type] = list[0];
+ return this;
+};
- if (events.removeListener)
- this.emit('removeListener', type, originalListener || listener);
- }
+EventEmitter.prototype.listeners = function(type) {
+ var ret;
+ if (!this._events || !this._events[type])
+ ret = [];
+ else if (isFunction(this._events[type]))
+ ret = [this._events[type]];
+ else
+ ret = this._events[type].slice();
+ return ret;
+};
- return this;
- };
+EventEmitter.prototype.listenerCount = function(type) {
+ if (this._events) {
+ var evlistener = this._events[type];
-EventEmitter.prototype.removeAllListeners =
- function removeAllListeners(type) {
- var listeners, events, i;
+ if (isFunction(evlistener))
+ return 1;
+ else if (evlistener)
+ return evlistener.length;
+ }
+ return 0;
+};
- events = this._events;
- if (!events)
- return this;
+EventEmitter.listenerCount = function(emitter, type) {
+ return emitter.listenerCount(type);
+};
- // not listening for removeListener, no need to emit
- if (!events.removeListener) {
- if (arguments.length === 0) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- } else if (events[type]) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else
- delete events[type];
- }
- return this;
- }
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+
+},{}],5:[function(require,module,exports){
+/* jshint node: true */
+'use strict';
+
+var normalice = require('normalice');
+
+/**
+ # freeice
+
+ The `freeice` module is a simple way of getting random STUN or TURN server
+ for your WebRTC application. The list of servers (just STUN at this stage)
+ were sourced from this [gist](https://gist.github.com/zziuni/3741933).
+
+ ## Example Use
+
+ The following demonstrates how you can use `freeice` with
+ [rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):
+
+ <<< examples/quickconnect.js
+
+ As the `freeice` module generates ice servers in a list compliant with the
+ WebRTC spec you will be able to use it with raw `RTCPeerConnection`
+ constructors and other WebRTC libraries.
+
+ ## Hey, don't use my STUN/TURN server!
+
+ If for some reason your free STUN or TURN server ends up in the
+ list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or
+ [turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))
+ that is used in this module, you can feel
+ free to open an issue on this repository and those servers will be removed
+ within 24 hours (or sooner). This is the quickest and probably the most
+ polite way to have something removed (and provides us some visibility
+ if someone opens a pull request requesting that a server is added).
- // emit removeListener for all listeners on all events
- if (arguments.length === 0) {
- var keys = objectKeys(events);
- var key;
- for (i = 0; i < keys.length; ++i) {
- key = keys[i];
- if (key === 'removeListener') continue;
- this.removeAllListeners(key);
- }
- this.removeAllListeners('removeListener');
- this._events = objectCreate(null);
- this._eventsCount = 0;
- return this;
- }
+ ## Please add my server!
- listeners = events[type];
+ If you have a server that you wish to add to the list, that's awesome! I'm
+ sure I speak on behalf of a whole pile of WebRTC developers who say thanks.
+ To get it into the list, feel free to either open a pull request or if you
+ find that process a bit daunting then just create an issue requesting
+ the addition of the server (make sure you provide all the details, and if
+ you have a Terms of Service then including that in the PR/issue would be
+ awesome).
- if (typeof listeners === 'function') {
- this.removeListener(type, listeners);
- } else if (listeners) {
- // LIFO order
- for (i = listeners.length - 1; i >= 0; i--) {
- this.removeListener(type, listeners[i]);
- }
- }
+ ## I know of a free server, can I add it?
- return this;
- };
+ Sure, if you do your homework and make sure it is ok to use (I'm currently
+ in the process of reviewing the terms of those STUN servers included from
+ the original list). If it's ok to go, then please see the previous entry
+ for how to add it.
-function _listeners(target, type, unwrap) {
- var events = target._events;
+ ## Current List of Servers
- if (!events)
- return [];
+ * current as at the time of last `README.md` file generation
- var evlistener = events[type];
- if (!evlistener)
- return [];
+ ### STUN
- if (typeof evlistener === 'function')
- return unwrap ? [evlistener.listener || evlistener] : [evlistener];
+ <<< stun.json
- return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
-}
+ ### TURN
-EventEmitter.prototype.listeners = function listeners(type) {
- return _listeners(this, type, true);
-};
+ <<< turn.json
-EventEmitter.prototype.rawListeners = function rawListeners(type) {
- return _listeners(this, type, false);
-};
+**/
-EventEmitter.listenerCount = function(emitter, type) {
- if (typeof emitter.listenerCount === 'function') {
- return emitter.listenerCount(type);
- } else {
- return listenerCount.call(emitter, type);
- }
-};
+var freeice = function(opts) {
+ // if a list of servers has been provided, then use it instead of defaults
+ var servers = {
+ stun: (opts || {}).stun || require('./stun.json'),
+ turn: (opts || {}).turn || require('./turn.json')
+ };
-EventEmitter.prototype.listenerCount = listenerCount;
-function listenerCount(type) {
- var events = this._events;
+ var stunCount = (opts || {}).stunCount || 2;
+ var turnCount = (opts || {}).turnCount || 0;
+ var selected;
- if (events) {
- var evlistener = events[type];
+ function getServers(type, count) {
+ var out = [];
+ var input = [].concat(servers[type]);
+ var idx;
- if (typeof evlistener === 'function') {
- return 1;
- } else if (evlistener) {
- return evlistener.length;
+ while (input.length && out.length < count) {
+ idx = (Math.random() * input.length) | 0;
+ out = out.concat(input.splice(idx, 1));
}
- }
- return 0;
-}
+ return out.map(function(url) {
+ //If it's a not a string, don't try to "normalice" it otherwise using type:url will screw it up
+ if ((typeof url !== 'string') && (! (url instanceof String))) {
+ return url;
+ } else {
+ return normalice(type + ':' + url);
+ }
+ });
+ }
-EventEmitter.prototype.eventNames = function eventNames() {
- return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
-};
+ // add stun servers
+ selected = [].concat(getServers('stun', stunCount));
-// About 1.5x faster than the two-arg version of Array#splice().
-function spliceOne(list, index) {
- for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
- list[i] = list[k];
- list.pop();
-}
+ if (turnCount) {
+ selected = selected.concat(getServers('turn', turnCount));
+ }
-function arrayClone(arr, n) {
- var copy = new Array(n);
- for (var i = 0; i < n; ++i)
- copy[i] = arr[i];
- return copy;
-}
+ return selected;
+};
-function unwrapListeners(arr) {
- var ret = new Array(arr.length);
- for (var i = 0; i < ret.length; ++i) {
- ret[i] = arr[i].listener || arr[i];
- }
- return ret;
-}
+module.exports = freeice;
+},{"./stun.json":6,"./turn.json":7,"normalice":12}],6:[function(require,module,exports){
+module.exports=[
+ "stun.l.google.com:19302",
+ "stun1.l.google.com:19302",
+ "stun2.l.google.com:19302",
+ "stun3.l.google.com:19302",
+ "stun4.l.google.com:19302",
+ "stun.ekiga.net",
+ "stun.ideasip.com",
+ "stun.schlund.de",
+ "stun.stunprotocol.org:3478",
+ "stun.voiparound.com",
+ "stun.voipbuster.com",
+ "stun.voipstunt.com",
+ "stun.voxgratia.org"
+]
-function objectCreatePolyfill(proto) {
- var F = function() {};
- F.prototype = proto;
- return new F;
-}
-function objectKeysPolyfill(obj) {
- var keys = [];
- for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
- keys.push(k);
- }
- return k;
-}
-function functionBindPolyfill(context) {
- var fn = this;
- return function () {
- return fn.apply(context, arguments);
- };
-}
+},{}],7:[function(require,module,exports){
+module.exports=[]
},{}],8:[function(require,module,exports){
var WildEmitter = require('wildemitter');
@@ -1235,13 +1089,15 @@ function getMaxVolume (analyser, fftBins) {
}
-var audioContextType = window.AudioContext || window.webkitAudioContext;
+var audioContextType;
+if (typeof window !== 'undefined') {
+ audioContextType = window.AudioContext || window.webkitAudioContext;
+}
// use a single audio context due to hardware limits
var audioContext = null;
module.exports = function(stream, options) {
var harker = new WildEmitter();
-
// make it not break in non-supported browsers
if (!audioContextType) return harker;
@@ -1254,16 +1110,15 @@ module.exports = function(stream, options) {
history = options.history || 10,
running = true;
- //Setup Audio Context
- if (!audioContext) {
- audioContext = new audioContextType();
- }
+ // Ensure that just a single AudioContext is internally created
+ audioContext = options.audioContext || audioContext || new audioContextType();
+
var sourceNode, fftBins, analyser;
analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.smoothingTimeConstant = smoothing;
- fftBins = new Float32Array(analyser.fftSize);
+ fftBins = new Float32Array(analyser.frequencyBinCount);
if (stream.jquery) stream = stream[0];
if (stream instanceof HTMLAudioElement || stream instanceof HTMLVideoElement) {
@@ -1282,6 +1137,19 @@ module.exports = function(stream, options) {
harker.speaking = false;
+ harker.suspend = function() {
+ return audioContext.suspend();
+ }
+ harker.resume = function() {
+ return audioContext.resume();
+ }
+ Object.defineProperty(harker, 'state', { get: function() {
+ return audioContext.state;
+ }});
+ audioContext.onstatechange = function() {
+ harker.emit('state_change', audioContext.state);
+ }
+
harker.setThreshold = function(t) {
threshold = t;
};
@@ -1289,7 +1157,7 @@ module.exports = function(stream, options) {
harker.setInterval = function(i) {
interval = i;
};
-
+
harker.stop = function() {
running = false;
harker.emit('volume_change', -100, threshold);
@@ -1297,6 +1165,8 @@ module.exports = function(stream, options) {
harker.speaking = false;
harker.emit('stopped_speaking');
}
+ analyser.disconnect();
+ sourceNode.disconnect();
};
harker.speakingHistory = [];
for (var i = 0; i < history; i++) {
@@ -1307,12 +1177,12 @@ module.exports = function(stream, options) {
// and emit events if changed
var looper = function() {
setTimeout(function() {
-
+
//check if stop has been called
if(!running) {
return;
}
-
+
var currentVolume = getMaxVolume(analyser, fftBins);
harker.emit('volume_change', currentVolume, threshold);
@@ -1344,32 +1214,35 @@ module.exports = function(stream, options) {
};
looper();
-
return harker;
}
-},{"wildemitter":24}],9:[function(require,module,exports){
+},{"wildemitter":25}],9:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
- ctor.super_ = superCtor
- ctor.prototype = Object.create(superCtor.prototype, {
- constructor: {
- value: ctor,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
+ if (superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ })
+ }
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
- ctor.super_ = superCtor
- var TempCtor = function () {}
- TempCtor.prototype = superCtor.prototype
- ctor.prototype = new TempCtor()
- ctor.prototype.constructor = ctor
+ if (superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
}
}
@@ -3156,12 +3029,12 @@ exports.parse = function(sdp) {
},{"sdp-transform":14}],21:[function(require,module,exports){
/*!
- * UAParser.js v0.7.19
+ * UAParser.js v0.7.20
* Lightweight JavaScript-based User-Agent string parser
* https://github.com/faisalman/ua-parser-js
*
- * Copyright © 2012-2016 Faisal Salman <fy...@gmail.com>
- * Dual licensed under GPLv2 or MIT
+ * Copyright © 2012-2019 Faisal Salman <f...@faisalman.com>
+ * Licensed under MIT License
*/
(function (window, undefined) {
@@ -3173,7 +3046,7 @@ exports.parse = function(sdp) {
/////////////
- var LIBVERSION = '0.7.19',
+ var LIBVERSION = '0.7.20',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
@@ -3202,15 +3075,15 @@ exports.parse = function(sdp) {
var util = {
extend : function (regexes, extensions) {
- var margedRegexes = {};
+ var mergedRegexes = {};
for (var i in regexes) {
if (extensions[i] && extensions[i].length % 2 === 0) {
- margedRegexes[i] = extensions[i].concat(regexes[i]);
+ mergedRegexes[i] = extensions[i].concat(regexes[i]);
} else {
- margedRegexes[i] = regexes[i];
+ mergedRegexes[i] = regexes[i];
}
}
- return margedRegexes;
+ return mergedRegexes;
},
has : function (str1, str2) {
if (typeof str1 === "string") {
@@ -3240,14 +3113,7 @@ exports.parse = function(sdp) {
rgx : function (ua, arrays) {
- //var result = {},
- var i = 0, j, k, p, q, matches, match;//, args = arguments;
-
- /*// construct object barebones
- for (p = 0; p < args[1].length; p++) {
- q = args[1][p];
- result[typeof q === OBJ_TYPE ? q[0] : q] = undefined;
- }*/
+ var i = 0, j, k, p, q, matches, match;
// loop through all regexes maps
while (i < arrays.length && !matches) {
@@ -3295,8 +3161,6 @@ exports.parse = function(sdp) {
}
i += 2;
}
- // console.log(this);
- //return this;
},
str : function (str, map) {
@@ -3411,14 +3275,17 @@ exports.parse = function(sdp) {
// Webkit/KHTML based
/(rekonq)\/([\w\.]*)/i, // Rekonq
- /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark)\/([\w\.-]+)/i
- // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser
+ /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon)\/([\w\.-]+)/i
+ // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
], [NAME, VERSION], [
+ /(konqueror)\/([\w\.]+)/i // Konqueror
+ ], [[NAME, 'Konqueror'], VERSION], [
+
/(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11
], [[NAME, 'IE'], VERSION], [
- /(edge|edgios|edga)\/((\d+)?[\w\.]+)/i // Microsoft Edge
+ /(edge|edgios|edga|edg)\/((\d+)?[\w\.]+)/i // Microsoft Edge
], [[NAME, 'Edge'], VERSION], [
/(yabrowser)\/([\w\.]+)/i // Yandex
@@ -3439,6 +3306,9 @@ exports.parse = function(sdp) {
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
], [[NAME, /_/g, ' '], VERSION], [
+ /(windowswechat qbcore)\/([\w\.]+)/i // WeChat Desktop for Windows Built-in Browser
+ ], [[NAME, 'WeChat(Win) Desktop'], VERSION], [
+
/(micromessenger)\/([\w\.]+)/i // WeChat
], [[NAME, 'WeChat'], VERSION], [
@@ -3488,6 +3358,9 @@ exports.parse = function(sdp) {
/android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)*/i // Android Browser
], [VERSION, [NAME, 'Android Browser']], [
+ /(sailfishbrowser)\/([\w\.]+)/i // Sailfish Browser
+ ], [[NAME, 'Sailfish Browser'], VERSION], [
+
/(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i
// Chrome/OmniWeb/Arora/Tizen/Nokia
], [NAME, VERSION], [
@@ -3516,7 +3389,6 @@ exports.parse = function(sdp) {
/webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [
- /(konqueror)\/([\w\.]+)/i, // Konqueror
/(webkit|khtml)\/([\w\.]+)/i
], [NAME, VERSION], [
@@ -3539,117 +3411,6 @@ exports.parse = function(sdp) {
/(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser
/(mosaic)[\/\s]([\w\.]+)/i // Mosaic
], [NAME, VERSION]
-
- /* /////////////////////
- // Media players BEGIN
- ////////////////////////
-
- , [
-
- /(apple(?:coremedia|))\/((\d+)[\w\._]+)/i, // Generic Apple CoreMedia
- /(coremedia) v((\d+)[\w\._]+)/i
- ], [NAME, VERSION], [
-
- /(aqualung|lyssna|bsplayer)\/((\d+)?[\w\.-]+)/i // Aqualung/Lyssna/BSPlayer
- ], [NAME, VERSION], [
-
- /(ares|ossproxy)\s((\d+)[\w\.-]+)/i // Ares/OSSProxy
- ], [NAME, VERSION], [
-
- /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/((\d+)[\w\.-]+)/i,
- // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC
- // NSPlayer/PSP-InternetRadioPlayer/Videos
- /(clementine|music player daemon)\s((\d+)[\w\.-]+)/i, // Clementine/MPD
- /(lg player|nexplayer)\s((\d+)[\d\.]+)/i,
- /player\/(nexplayer|lg player)\s((\d+)[\w\.-]+)/i // NexPlayer/LG Player
- ], [NAME, VERSION], [
- /(nexplayer)\s((\d+)[\w\.-]+)/i // Nexplayer
- ], [NAME, VERSION], [
-
- /(flrp)\/((\d+)[\w\.-]+)/i // Flip Player
- ], [[NAME, 'Flip Player'], VERSION], [
-
- /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i
- // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit
- ], [NAME], [
-
- /(gstreamer) souphttpsrc (?:\([^\)]+\)){0,1} libsoup\/((\d+)[\w\.-]+)/i
- // Gstreamer
- ], [NAME, VERSION], [
-
- /(htc streaming player)\s[\w_]+\s\/\s((\d+)[\d\.]+)/i, // HTC Streaming Player
- /(java|python-urllib|python-requests|wget|libcurl)\/((\d+)[\w\.-_]+)/i,
- // Java/urllib/requests/wget/cURL
- /(lavf)((\d+)[\d\.]+)/i // Lavf (FFMPEG)
- ], [NAME, VERSION], [
-
- /(htc_one_s)\/((\d+)[\d\.]+)/i // HTC One S
- ], [[NAME, /_/g, ' '], VERSION], [
-
- /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+){0,1})/i
- // MPlayer SVN
- ], [NAME, VERSION], [
-
- /(mplayer)(?:\s|\/|[unkow-]+)((\d+)[\w\.-]+)/i // MPlayer
- ], [NAME, VERSION], [
-
- /(mplayer)/i, // MPlayer (no other info)
- /(yourmuze)/i, // YourMuze
- /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime
- ], [NAME], [
-
- /(nero (?:home|scout))\/((\d+)[\w\.-]+)/i // Nero Home/Nero Scout
- ], [NAME, VERSION], [
-
- /(nokia\d+)\/((\d+)[\w\.-]+)/i // Nokia
- ], [NAME, VERSION], [
-
- /\s(songbird)\/((\d+)[\w\.-]+)/i // Songbird/Philips-Songbird
- ], [NAME, VERSION], [
-
- /(winamp)3 version ((\d+)[\w\.-]+)/i, // Winamp
- /(winamp)\s((\d+)[\w\.-]+)/i,
- /(winamp)mpeg\/((\d+)[\w\.-]+)/i
- ], [NAME, VERSION], [
-
- /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)
- // inlight radio
- ], [NAME], [
-
- /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/((\d+)[\w\.-]+)/i
- // QuickTime/RealMedia/RadioApp/RadioClientApplication/
- // SoundTap/Totem/Stagefright/Streamium
- ], [NAME, VERSION], [
-
- /(smp)((\d+)[\d\.]+)/i // SMP
- ], [NAME, VERSION], [
-
- /(vlc) media player - version ((\d+)[\w\.]+)/i, // VLC Videolan
- /(vlc)\/((\d+)[\w\.-]+)/i,
- /(xbmc|gvfs|xine|xmms|irapp)\/((\d+)[\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp
- /(foobar2000)\/((\d+)[\d\.]+)/i, // Foobar2000
- /(itunes)\/((\d+)[\d\.]+)/i // iTunes
- ], [NAME, VERSION], [
-
- /(wmplayer)\/((\d+)[\w\.-]+)/i, // Windows Media Player
- /(windows-media-player)\/((\d+)[\w\.-]+)/i
- ], [[NAME, /-/g, ' '], VERSION], [
-
- /windows\/((\d+)[\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i
- // Windows Media Server
- ], [VERSION, [NAME, 'Windows']], [
-
- /(com\.riseupradioalarm)\/((\d+)[\d\.]*)/i // RiseUP Radio Alarm
- ], [NAME, VERSION], [
-
- /(rad.io)\s((\d+)[\d\.]+)/i, // Rad.io
- /(radio.(?:de|at|fr))\s((\d+)[\d\.]+)/i
- ], [[NAME, 'rad.io'], VERSION]
-
- //////////////////////
- // Media players END
- ////////////////////*/
-
],
cpu : [[
@@ -3680,7 +3441,7 @@ exports.parse = function(sdp) {
device : [[
- /\((ipad|playbook);[\w\s\);-]+(rim|apple)/i // iPad/PlayBook
+ /\((ipad|playbook);[\w\s\),;-]+(rim|apple)/i // iPad/PlayBook
], [MODEL, VENDOR, [TYPE, TABLET]], [
/applecoremedia\/[\w\.]+ \((ipad)/ // iPad
@@ -3718,13 +3479,13 @@ exports.parse = function(sdp) {
/\(bb10;\s(\w+)/i // BlackBerry 10
], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [
// Asus Tablets
- /android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7|padfone)/i
+ /android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7|padfone|p00c)/i
], [MODEL, [VENDOR, 'Asus'], [TYPE, TABLET]], [
/(sony)\s(tablet\s[ps])\sbuild\//i, // Sony
/(sony)?(?:sgp.+)\sbuild\//i
], [[VENDOR, 'Sony'], [MODEL, 'Xperia Tablet'], [TYPE, TABLET]], [
- /android.+\s([c-g]\d{4}|so[-l]\w+)\sbuild\//i
+ /android.+\s([c-g]\d{4}|so[-l]\w+)(?=\sbuild\/|\).+chrome\/(?![1-6]{0,1}\d\.))/i
], [MODEL, [VENDOR, 'Sony'], [TYPE, MOBILE]], [
/\s(ouya)\s/i, // Ouya
@@ -3740,13 +3501,10 @@ exports.parse = function(sdp) {
/(sprint\s(\w+))/i // Sprint Phones
], [[VENDOR, mapper.str, maps.device.sprint.vendor], [MODEL, mapper.str, maps.device.sprint.model], [TYPE, MOBILE]], [
- /(lenovo)\s?(S(?:5000|6000)+(?:[-][\w+]))/i // Lenovo tablets
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
-
- /(htc)[;_\s-]+([\w\s]+(?=\))|\w+)*/i, // HTC
+ /(htc)[;_\s-]+([\w\s]+(?=\)|\sbuild)|\w+)/i, // HTC
/(zte)-(\w*)/i, // ZTE
- /(alcatel|geeksphone|lenovo|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i
- // Alcatel/GeeksPhone/Lenovo/Nexian/Panasonic/Sony
+ /(alcatel|geeksphone|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i
+ // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
/(nexus\s9)/i // HTC Nexus 9
@@ -3799,7 +3557,7 @@ exports.parse = function(sdp) {
/(nokia)[\s_-]?([\w-]*)/i
], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [
- /android\s3\.[\s\w;-]{10}(a\d{3})/i // Acer
+ /android[x\d\.\s;]+\s([ab][1-7]\-?[0178a]\d\d?)/i // Acer
], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
/android.+([vl]k\-?\d{3})\s+build/i // LG Tablet
@@ -3813,8 +3571,12 @@ exports.parse = function(sdp) {
/android.+lg(\-?[\d\w]+)\s+build/i
], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [
+ /(lenovo)\s?(s(?:5000|6000)(?:[\w-]+)|tab(?:[\s\w]+))/i // Lenovo tablets
+ ], [VENDOR, MODEL, [TYPE, TABLET]], [
/android.+(ideatab[a-z0-9\-\s]+)/i // Lenovo
], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
+ /(lenovo)[_\s-]?([\w-]+)/i
+ ], [VENDOR, MODEL, [TYPE, MOBILE]], [
/linux;.+((jolla));/i // Jolla
], [VENDOR, MODEL, [TYPE, MOBILE]], [
@@ -3834,19 +3596,20 @@ exports.parse = function(sdp) {
/android.+;\s(pixel c)[\s)]/i // Google Pixel C
], [MODEL, [VENDOR, 'Google'], [TYPE, TABLET]], [
- /android.+;\s(pixel( [23])?( xl)?)\s/i // Google Pixel
+ /android.+;\s(pixel( [23])?( xl)?)[\s)]/i // Google Pixel
], [MODEL, [VENDOR, 'Google'], [TYPE, MOBILE]], [
/android.+;\s(\w+)\s+build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
/android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i, // Xiaomi Hongmi
- /android.+(mi[\s\-_]*(?:one|one[\s_]plus|note lte)?[\s_]*(?:\d?\w?)[\s_]*(?:plus)?)\s+build/i, // Xiaomi Mi
+ /android.+(mi[\s\-_]*(?:a\d|one|one[\s_]plus|note lte)?[\s_]*(?:\d?\w?)[\s_]*(?:plus)?)\s+build/i,
+ // Xiaomi Mi
/android.+(redmi[\s\-_]*(?:note)?(?:[\s_]*[\w\s]+))\s+build/i // Redmi Phones
], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [
/android.+(mi[\s\-_]*(?:pad)(?:[\s_]*[\w\s]+))\s+build/i // Mi Pad tablets
],[[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, TABLET]], [
- /android.+;\s(m[1-5]\snote)\sbuild/i // Meizu Tablet
- ], [MODEL, [VENDOR, 'Meizu'], [TYPE, TABLET]], [
- /(mz)-([\w-]{2,})/i // Meizu Phone
+ /android.+;\s(m[1-5]\snote)\sbuild/i // Meizu
+ ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
+ /(mz)-([\w-]{2,})/i
], [[VENDOR, 'Meizu'], MODEL, [TYPE, MOBILE]], [
/android.+a000(1)\s+build/i, // OnePlus
@@ -3924,60 +3687,11 @@ exports.parse = function(sdp) {
/\s(mobile)(?:[;\/]|\ssafari)/i // Unidentifiable Mobile
], [[TYPE, util.lowerize], VENDOR, MODEL], [
+ /[\s\/\(](smart-?tv)[;\)]/i // SmartTV
+ ], [[TYPE, SMARTTV]], [
+
/(android[\w\.\s\-]{0,9});.+build/i // Generic Android Device
], [MODEL, [VENDOR, 'Generic']]
-
-
- /*//////////////////////////
- // TODO: move to string map
- ////////////////////////////
-
- /(C6603)/i // Sony Xperia Z C6603
- ], [[MODEL, 'Xperia Z C6603'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [
- /(C6903)/i // Sony Xperia Z 1
- ], [[MODEL, 'Xperia Z 1'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [
-
- /(SM-G900[F|H])/i // Samsung Galaxy S5
- ], [[MODEL, 'Galaxy S5'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
- /(SM-G7102)/i // Samsung Galaxy Grand 2
- ], [[MODEL, 'Galaxy Grand 2'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
- /(SM-G530H)/i // Samsung Galaxy Grand Prime
- ], [[MODEL, 'Galaxy Grand Prime'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
- /(SM-G313HZ)/i // Samsung Galaxy V
- ], [[MODEL, 'Galaxy V'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
- /(SM-T805)/i // Samsung Galaxy Tab S 10.5
- ], [[MODEL, 'Galaxy Tab S 10.5'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [
- /(SM-G800F)/i // Samsung Galaxy S5 Mini
- ], [[MODEL, 'Galaxy S5 Mini'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
- /(SM-T311)/i // Samsung Galaxy Tab 3 8.0
- ], [[MODEL, 'Galaxy Tab 3 8.0'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [
-
- /(T3C)/i // Advan Vandroid T3C
- ], [MODEL, [VENDOR, 'Advan'], [TYPE, TABLET]], [
- /(ADVAN T1J\+)/i // Advan Vandroid T1J+
- ], [[MODEL, 'Vandroid T1J+'], [VENDOR, 'Advan'], [TYPE, TABLET]], [
- /(ADVAN S4A)/i // Advan Vandroid S4A
- ], [[MODEL, 'Vandroid S4A'], [VENDOR, 'Advan'], [TYPE, MOBILE]], [
-
- /(V972M)/i // ZTE V972M
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
-
- /(i-mobile)\s(IQ\s[\d\.]+)/i // i-mobile IQ
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /(IQ6.3)/i // i-mobile IQ IQ 6.3
- ], [[MODEL, 'IQ 6.3'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [
- /(i-mobile)\s(i-style\s[\d\.]+)/i // i-mobile i-STYLE
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /(i-STYLE2.1)/i // i-mobile i-STYLE 2.1
- ], [[MODEL, 'i-STYLE 2.1'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [
-
- /(mobiistar touch LAI 512)/i // mobiistar touch LAI 512
- ], [[MODEL, 'Touch LAI 512'], [VENDOR, 'mobiistar'], [TYPE, MOBILE]], [
-
- /////////////
- // END TODO
- ///////////*/
-
],
engine : [[
@@ -3985,8 +3699,12 @@ exports.parse = function(sdp) {
/windows.+\sedge\/([\w\.]+)/i // EdgeHTML
], [VERSION, [NAME, 'EdgeHTML']], [
+ /webkit\/537\.36.+chrome\/(?!27)/i // Blink
+ ], [[NAME, 'Blink']], [
+
/(presto)\/([\w\.]+)/i, // Presto
- /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m
+ /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i,
+ // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
/(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i, // KHTML/Tasman/Links
/(icab)[\/\s]([23]\.[\d\.]+)/i // iCab
], [NAME, VERSION], [
@@ -4012,9 +3730,8 @@ exports.parse = function(sdp) {
], [[NAME, 'BlackBerry'], VERSION], [
/(blackberry)\w*\/?([\w\.]*)/i, // Blackberry
/(tizen)[\/\s]([\w\.]+)/i, // Tizen
- /(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]*)/i,
- // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki
- /linux;.+(sailfish);/i // Sailfish OS
+ /(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|sailfish|contiki)[\/\s-]?([\w\.]*)/i
+ // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki/Sailfish OS
], [NAME, VERSION], [
/(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]*)/i // Symbian
], [[NAME, 'Symbian'], VERSION], [
@@ -4072,22 +3789,6 @@ exports.parse = function(sdp) {
/////////////////
// Constructor
////////////////
- /*
- var Browser = function (name, version) {
- this[NAME] = name;
- this[VERSION] = version;
- };
- var CPU = function (arch) {
- this[ARCHITECTURE] = arch;
- };
- var Device = function (vendor, model, type) {
- this[VENDOR] = vendor;
- this[MODEL] = model;
- this[TYPE] = type;
- };
- var Engine = Browser;
- var OS = Browser;
- */
var UAParser = function (uastring, extensions) {
if (typeof uastring === 'object') {
@@ -4101,11 +3802,6 @@ exports.parse = function(sdp) {
var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
var rgxmap = extensions ? util.extend(regexes, extensions) : regexes;
- //var browser = new Browser();
- //var cpu = new CPU();
- //var device = new Device();
- //var engine = new Engine();
- //var os = new OS();
this.getBrowser = function () {
var browser = { name: undefined, version: undefined };
@@ -4148,11 +3844,6 @@ exports.parse = function(sdp) {
};
this.setUA = function (uastring) {
ua = uastring;
- //browser = new Browser();
- //cpu = new CPU();
- //device = new Device();
- //engine = new Engine();
- //os = new OS();
return this;
};
return this;
@@ -4186,7 +3877,6 @@ exports.parse = function(sdp) {
NAME : NAME,
VERSION : VERSION
};
- //UAParser.Utils = util;
///////////
// Export
@@ -4199,39 +3889,10 @@ exports.parse = function(sdp) {
if (typeof module !== UNDEF_TYPE && module.exports) {
exports = module.exports = UAParser;
}
- // TODO: test!!!!!!!!
- /*
- if (require && require.main === module && process) {
- // cli
- var jsonize = function (arr) {
- var res = [];
- for (var i in arr) {
- res.push(new UAParser(arr[i]).getResult());
- }
- process.stdout.write(JSON.stringify(res, null, 2) + '\n');
- };
- if (process.stdin.isTTY) {
- // via args
- jsonize(process.argv.slice(2));
- } else {
- // via pipe
- var str = '';
- process.stdin.on('readable', function() {
- var read = process.stdin.read();
- if (read !== null) {
- str += read;
- }
- });
- process.stdin.on('end', function () {
- jsonize(str.replace(/\n$/, '').split('\n'));
- });
- }
- }
- */
exports.UAParser = UAParser;
} else {
// requirejs env (optional)
- if (typeof(define) === FUNC_TYPE && define.amd) {
+ if (typeof(define) === 'function' && define.amd) {
define(function () {
return UAParser;
});
@@ -4265,202 +3926,81 @@ exports.parse = function(sdp) {
})(typeof window === 'object' ? window : this);
},{}],22:[function(require,module,exports){
-(function (global){
-
-var rng;
-
-var crypto = global.crypto || global.msCrypto; // for IE 11
-if (crypto && crypto.getRandomValues) {
- // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
- // Moderately fast, high quality
- var _rnds8 = new Uint8Array(16);
- rng = function whatwgRNG() {
- crypto.getRandomValues(_rnds8);
- return _rnds8;
- };
+/**
+ * Convert array of 16 byte values to UUID string format of the form:
+ * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+ */
+var byteToHex = [];
+for (var i = 0; i < 256; ++i) {
+ byteToHex[i] = (i + 0x100).toString(16).substr(1);
+}
+
+function bytesToUuid(buf, offset) {
+ var i = offset || 0;
+ var bth = byteToHex;
+ // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
+ return ([bth[buf[i++]], bth[buf[i++]],
+ bth[buf[i++]], bth[buf[i++]], '-',
+ bth[buf[i++]], bth[buf[i++]], '-',
+ bth[buf[i++]], bth[buf[i++]], '-',
+ bth[buf[i++]], bth[buf[i++]], '-',
+ bth[buf[i++]], bth[buf[i++]],
+ bth[buf[i++]], bth[buf[i++]],
+ bth[buf[i++]], bth[buf[i++]]]).join('');
}
-if (!rng) {
+module.exports = bytesToUuid;
+
+},{}],23:[function(require,module,exports){
+// Unique ID creation requires a high quality random # generator. In the
+// browser this is a little complicated due to unknown quality of Math.random()
+// and inconsistent support for the `crypto` API. We do the best we can via
+// feature-detection
+
+// getRandomValues needs to be invoked in a context where "this" is a Crypto
+// implementation. Also, find the complete implementation of crypto on IE11.
+var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
+ (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
+
+if (getRandomValues) {
+ // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
+ var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
+
+ module.exports = function whatwgRNG() {
+ getRandomValues(rnds8);
+ return rnds8;
+ };
+} else {
// Math.random()-based (RNG)
//
// If all else fails, use Math.random(). It's fast, but is of unspecified
// quality.
- var _rnds = new Array(16);
- rng = function() {
+ var rnds = new Array(16);
+
+ module.exports = function mathRNG() {
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
- _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
+ rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
}
- return _rnds;
+ return rnds;
};
}
-module.exports = rng;
-
-
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}],23:[function(require,module,exports){
-// uuid.js
-//
-// Copyright (c) 2010-2012 Robert Kieffer
-// MIT License - http://opensource.org/licenses/mit-license.php
-
-// Unique ID creation requires a high quality random # generator. We feature
-// detect to determine the best RNG source, normalizing to a function that
-// returns 128-bits of randomness, since that's what's usually required
-var _rng = require('./rng');
-
-// Maps for number <-> hex string conversion
-var _byteToHex = [];
-var _hexToByte = {};
-for (var i = 0; i < 256; i++) {
- _byteToHex[i] = (i + 0x100).toString(16).substr(1);
- _hexToByte[_byteToHex[i]] = i;
-}
-
-// **`parse()` - Parse a UUID into it's component bytes**
-function parse(s, buf, offset) {
- var i = (buf && offset) || 0, ii = 0;
-
- buf = buf || [];
- s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
- if (ii < 16) { // Don't overflow!
- buf[i + ii++] = _hexToByte[oct];
- }
- });
-
- // Zero out remaining bytes if string was short
- while (ii < 16) {
- buf[i + ii++] = 0;
- }
-
- return buf;
-}
-
-// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
-function unparse(buf, offset) {
- var i = offset || 0, bth = _byteToHex;
- return bth[buf[i++]] + bth[buf[i++]] +
- bth[buf[i++]] + bth[buf[i++]] + '-' +
- bth[buf[i++]] + bth[buf[i++]] + '-' +
- bth[buf[i++]] + bth[buf[i++]] + '-' +
- bth[buf[i++]] + bth[buf[i++]] + '-' +
- bth[buf[i++]] + bth[buf[i++]] +
- bth[buf[i++]] + bth[buf[i++]] +
- bth[buf[i++]] + bth[buf[i++]];
-}
-
-// **`v1()` - Generate time-based UUID**
-//
-// Inspired by https://github.com/LiosK/UUID.js
-// and http://docs.python.org/library/uuid.html
-
-// random #'s we need to init node and clockseq
-var _seedBytes = _rng();
-
-// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
-var _nodeId = [
- _seedBytes[0] | 0x01,
- _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
-];
-
-// Per 4.2.2, randomize (14 bit) clockseq
-var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
-
-// Previous uuid creation time
-var _lastMSecs = 0, _lastNSecs = 0;
-
-// See https://github.com/broofa/node-uuid for API details
-function v1(options, buf, offset) {
- var i = buf && offset || 0;
- var b = buf || [];
-
- options = options || {};
-
- var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
-
- // UUID timestamps are 100 nano-second units since the Gregorian epoch,
- // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
- // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
- // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
- var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
-
- // Per 4.2.1.2, use count of uuid's generated during the current clock
- // cycle to simulate higher resolution clock
- var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
-
- // Time since last uuid creation (in msecs)
- var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
-
- // Per 4.2.1.2, Bump clockseq on clock regression
- if (dt < 0 && options.clockseq === undefined) {
- clockseq = clockseq + 1 & 0x3fff;
- }
-
- // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
- // time interval
- if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
- nsecs = 0;
- }
-
- // Per 4.2.1.2 Throw error if too many uuids are requested
- if (nsecs >= 10000) {
- throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
- }
-
- _lastMSecs = msecs;
- _lastNSecs = nsecs;
- _clockseq = clockseq;
-
- // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
- msecs += 12219292800000;
-
- // `time_low`
- var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
- b[i++] = tl >>> 24 & 0xff;
- b[i++] = tl >>> 16 & 0xff;
- b[i++] = tl >>> 8 & 0xff;
- b[i++] = tl & 0xff;
-
- // `time_mid`
- var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
- b[i++] = tmh >>> 8 & 0xff;
- b[i++] = tmh & 0xff;
-
- // `time_high_and_version`
- b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
- b[i++] = tmh >>> 16 & 0xff;
-
- // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
- b[i++] = clockseq >>> 8 | 0x80;
-
- // `clock_seq_low`
- b[i++] = clockseq & 0xff;
-
- // `node`
- var node = options.node || _nodeId;
- for (var n = 0; n < 6; n++) {
- b[i + n] = node[n];
- }
-
- return buf ? buf : unparse(b);
-}
-
-// **`v4()` - Generate random UUID**
+},{}],24:[function(require,module,exports){
+var rng = require('./lib/rng');
+var bytesToUuid = require('./lib/bytesToUuid');
-// See https://github.com/broofa/node-uuid for API details
function v4(options, buf, offset) {
- // Deprecated - 'format' argument, as supported in v1.2
var i = buf && offset || 0;
if (typeof(options) == 'string') {
- buf = options == 'binary' ? new Array(16) : null;
+ buf = options === 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
- var rnds = options.random || (options.rng || _rng)();
+ var rnds = options.random || (options.rng || rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
@@ -4468,24 +4008,17 @@ function v4(options, buf, offset) {
// Copy bytes to buffer, if provided
if (buf) {
- for (var ii = 0; ii < 16; ii++) {
+ for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
- return buf || unparse(rnds);
+ return buf || bytesToUuid(rnds);
}
-// Export public API
-var uuid = v4;
-uuid.v1 = v1;
-uuid.v4 = v4;
-uuid.parse = parse;
-uuid.unparse = unparse;
-
-module.exports = uuid;
+module.exports = v4;
-},{"./rng":22}],24:[function(require,module,exports){
+},{"./lib/bytesToUuid":22,"./lib/rng":23}],25:[function(require,module,exports){
/*
WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
on @visionmedia's Emitter from UI Kit.
@@ -4577,9 +4110,11 @@ WildEmitter.mixin = function (constructor) {
// remove specific handler
i = callbacks.indexOf(fn);
- callbacks.splice(i, 1);
- if (callbacks.length === 0) {
- delete this.callbacks[event];
+ if (i !== -1) {
+ callbacks.splice(i, 1);
+ if (callbacks.length === 0) {
+ delete this.callbacks[event];
+ }
}
return this;
};
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
index 5255291..72cf1d7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
@@ -130,7 +130,9 @@ var VideoManager = (function() {
if (!inited) {
return;
}
+ const streamMap = {};
c.streams.forEach(function(sd) {
+ streamMap[sd.uid] = sd.uid;
sd.self = c.self;
if (VideoUtil.isSharing(sd) || VideoUtil.isRecording(sd)) {
return;
@@ -153,13 +155,13 @@ var VideoManager = (function() {
w.data().setRights(c.rights);
}
}
- if (c.streams.length === 0) {
- // check for non inited video window
- const v = $('#' + VideoUtil.getVid(c.uid));
- if (v.length === 1) {
- _closeV(v);
+ $('[data-client-uid="' + c.cuid + '"]').each(function() {
+ const sd = $(this).data().stream();
+ if (!streamMap[sd.uid]) {
+ //not-inited/invalid video window
+ _closeV($(this));
}
- }
+ });
}
function _closeV(v) {
if (v.dialog('instance') !== undefined) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
index 951133e..ae657b2 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
@@ -113,7 +113,7 @@ var Video = (function() {
}
_handleVolume(lastVolume);
}
- callback(msg, cnts, _stream);
+ video && callback(msg, cnts, _stream);
})
.catch(function(err) {
VideoManager.sendMessage({
@@ -257,7 +257,7 @@ var Video = (function() {
let contSel;
if (opts.interview) {
const area = $('.pod-area');
- const contId = UUID.v4();
+ const contId = uuidv4();
contSel = '#' + contId;
area.append($('<div class="pod"></div>').attr('id', contId));
WbArea.updateAreaClass();
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
index 363d401..509f38c 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
@@ -2,7 +2,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '2.7.0' };
+var fabric = fabric || { version: '3.3.0' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -12,25 +12,30 @@ else if (typeof define === 'function' && define.amd) {
}
/* _AMD_END_ */
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
- if (document instanceof HTMLDocument)
+ if (document instanceof (typeof HTMLDocument !== 'undefined' ? HTMLDocument : Document)) {
fabric.document = document;
- else
- fabric.document = document.implementation.createHTMLDocument("");
+ }
+ else {
+ fabric.document = document.implementation.createHTMLDocument('');
+ }
fabric.window = window;
}
else {
// assume we're running under node.js when document/window are not present
- fabric.document = require('jsdom')
- .jsdom(
- decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),
- { features: {
+ var jsdom = require('jsdom');
+ var virtualWindow = new jsdom.JSDOM(
+ decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),
+ {
+ features: {
FetchExternalResources: ['img']
- }
- });
+ },
+ resources: 'usable'
+ }).window;
+ fabric.document = virtualWindow.document;
fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
- fabric.window = fabric.document.defaultView;
- DOMParser = require('xmldom').DOMParser;
+ fabric.window = virtualWindow;
+ DOMParser = fabric.window.DOMParser;
}
/**
@@ -69,10 +74,9 @@ fabric.SHARED_ATTRIBUTES = [
* Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
*/
fabric.DPI = 96;
-fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
+fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)';
fabric.fontPaths = { };
fabric.iMatrix = [1, 0, 0, 1, 0, 0];
-fabric.canvasModule = 'canvas';
/**
* Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
@@ -113,6 +117,15 @@ fabric.charWidthsCache = { };
fabric.textureSize = 2048;
/**
+ * When 'true', style information is not retained when copy/pasting text, making
+ * pasted text use destination style.
+ * Defaults to 'false'.
+ * @type Boolean
+ * @default
+ */
+fabric.disableStyleCopyPaste = false;
+
+/**
* Enable webgl for filtering picture is available
* A filtering backend will be initialized, this will both take memory and
* time since a default 2048x2048 canvas will be created for the gl context
@@ -168,6 +181,15 @@ fabric.boundsOfCurveCache = { };
*/
fabric.cachesBoundsOfCurve = true;
+/**
+ * Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on
+ * Chrome + old hardware. if your users are experiencing empty images after filtering you may try to force this to true
+ * this has to be set before instantiating the filtering backend ( before filtering the first image )
+ * @type Boolean
+ * @default false
+ */
+fabric.forceGLPutImageData = false;
+
fabric.initFilterBackend = function() {
if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) {
console.log('max texture size: ' + fabric.maxTextureSize);
@@ -247,7 +269,7 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
*/
function stopObserving(eventName, handler) {
if (!this.__eventListeners) {
- return;
+ return this;
}
// remove all key/value pairs (event name -> event handler)
@@ -280,12 +302,12 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
*/
function fire(eventName, options) {
if (!this.__eventListeners) {
- return;
+ return this;
}
var listenersForEvent = this.__eventListeners[eventName];
if (!listenersForEvent) {
- return;
+ return this;
}
for (var i = 0, len = listenersForEvent.length; i < len; i++) {
@@ -1005,16 +1027,19 @@ fabric.CommonMethods = {
enlivenObjects: function(objects, callback, namespace, reviver) {
objects = objects || [];
+ var enlivenedObjects = [],
+ numLoadedObjects = 0,
+ numTotalObjects = objects.length;
+
function onLoaded() {
if (++numLoadedObjects === numTotalObjects) {
- callback && callback(enlivenedObjects);
+ callback && callback(enlivenedObjects.filter(function(obj) {
+ // filter out undefined objects (objects that gave error)
+ return obj;
+ }));
}
}
- var enlivenedObjects = [],
- numLoadedObjects = 0,
- numTotalObjects = objects.length;
-
if (!numTotalObjects) {
callback && callback(enlivenedObjects);
return;
@@ -1446,6 +1471,19 @@ fabric.CommonMethods = {
findScaleToCover: function(source, destination) {
return Math.max(destination.width / source.width, destination.height / source.height);
+ },
+
+ /**
+ * given an array of 6 number returns something like `"matrix(...numbers)"`
+ * @memberOf fabric.util
+ * @param {Array} trasnform an array with 6 numbers
+ * @return {String} transform matrix for svg
+ * @return {Object.y} Limited dimensions by Y
+ */
+ matrixToSVG: function(transform) {
+ return 'matrix(' + transform.map(function(value) {
+ return fabric.util.toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
+ }).join(' ') + ')';
}
};
})(typeof exports !== 'undefined' ? exports : this);
@@ -2105,170 +2143,8 @@ fabric.CommonMethods = {
(function () {
-
- var unknown = 'unknown';
-
- /* EVENT HANDLING */
-
- function areHostMethods(object) {
- var methodNames = Array.prototype.slice.call(arguments, 1),
- t, i, len = methodNames.length;
- for (i = 0; i < len; i++) {
- t = typeof object[methodNames[i]];
- if (!(/^(?:function|object|unknown)$/).test(t)) {
- return false;
- }
- }
- return true;
- }
-
- /** @ignore */
- var getElement,
- setElement,
- getUniqueId = (function () {
- var uid = 0;
- return function (element) {
- return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
- };
- })();
-
- (function () {
- var elements = { };
- /** @ignore */
- getElement = function (uid) {
- return elements[uid];
- };
- /** @ignore */
- setElement = function (uid, element) {
- elements[uid] = element;
- };
- })();
-
- function createListener(uid, handler) {
- return {
- handler: handler,
- wrappedHandler: createWrappedHandler(uid, handler)
- };
- }
-
- function createWrappedHandler(uid, handler) {
- return function (e) {
- handler.call(getElement(uid), e || fabric.window.event);
- };
- }
-
- function createDispatcher(uid, eventName) {
- return function (e) {
- if (handlers[uid] && handlers[uid][eventName]) {
- var handlersForEvent = handlers[uid][eventName];
- for (var i = 0, len = handlersForEvent.length; i < len; i++) {
- handlersForEvent[i].call(this, e || fabric.window.event);
- }
- }
- };
- }
-
- var shouldUseAddListenerRemoveListener = (
- areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') &&
- areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')),
-
- shouldUseAttachEventDetachEvent = (
- areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') &&
- areHostMethods(fabric.window, 'attachEvent', 'detachEvent')),
-
- // IE branch
- listeners = { },
-
- // DOM L0 branch
- handlers = { },
-
- addListener, removeListener;
-
- if (shouldUseAddListenerRemoveListener) {
- /** @ignore */
- addListener = function (element, eventName, handler, options) {
- // since ie10 or ie9 can use addEventListener but they do not support options, i need to check
- element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler, options) {
- element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
- };
- }
-
- else if (shouldUseAttachEventDetachEvent) {
- /** @ignore */
- addListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- setElement(uid, element);
- if (!listeners[uid]) {
- listeners[uid] = { };
- }
- if (!listeners[uid][eventName]) {
- listeners[uid][eventName] = [];
-
- }
- var listener = createListener(uid, handler);
- listeners[uid][eventName].push(listener);
- element.attachEvent('on' + eventName, listener.wrappedHandler);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element), listener;
- if (listeners[uid] && listeners[uid][eventName]) {
- for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
- listener = listeners[uid][eventName][i];
- if (listener && listener.handler === handler) {
- element.detachEvent('on' + eventName, listener.wrappedHandler);
- listeners[uid][eventName][i] = null;
- }
- }
- }
- };
- }
- else {
- /** @ignore */
- addListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- if (!handlers[uid]) {
- handlers[uid] = { };
- }
- if (!handlers[uid][eventName]) {
- handlers[uid][eventName] = [];
- var existingHandler = element['on' + eventName];
- if (existingHandler) {
- handlers[uid][eventName].push(existingHandler);
- }
- element['on' + eventName] = createDispatcher(uid, eventName);
- }
- handlers[uid][eventName].push(handler);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- if (handlers[uid] && handlers[uid][eventName]) {
- var handlersForEvent = handlers[uid][eventName];
- for (var i = 0, len = handlersForEvent.length; i < len; i++) {
- if (handlersForEvent[i] === handler) {
- handlersForEvent.splice(i, 1);
- }
- }
- }
- };
- }
-
+ // since ie10 or ie9 can use addEventListener but they do not support options, i need to check
+ var couldUseAttachEvent = !!fabric.document.createElement('div').attachEvent;
/**
* Adds an event listener to an element
* @function
@@ -2277,7 +2153,9 @@ fabric.CommonMethods = {
* @param {String} eventName
* @param {Function} handler
*/
- fabric.util.addListener = addListener;
+ fabric.util.addListener = function(element, eventName, handler, options) {
+ element && element.addEventListener(eventName, handler, couldUseAttachEvent ? false : options);
+ };
/**
* Removes an event listener from an element
@@ -2287,60 +2165,27 @@ fabric.CommonMethods = {
* @param {String} eventName
* @param {Function} handler
*/
- fabric.util.removeListener = removeListener;
-
- /**
- * Cross-browser wrapper for getting event's coordinates
- * @memberOf fabric.util
- * @param {Event} event Event object
- */
- function getPointer(event) {
- event || (event = fabric.window.event);
-
- var element = event.target ||
- (typeof event.srcElement !== unknown ? event.srcElement : null),
-
- scroll = fabric.util.getScrollLeftTop(element);
- return {
- x: pointerX(event) + scroll.left,
- y: pointerY(event) + scroll.top
- };
- }
-
- var pointerX = function(event) {
- return event.clientX;
- },
-
- pointerY = function(event) {
- return event.clientY;
- };
-
- function _getPointer(event, pageProp, clientProp) {
- var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
- var pointer, eventTouchProp = event[touchProp];
-
- if (eventTouchProp && eventTouchProp[0]) {
- pointer = eventTouchProp[0][clientProp];
- }
+ fabric.util.removeListener = function(element, eventName, handler, options) {
+ element && element.removeEventListener(eventName, handler, couldUseAttachEvent ? false : options);
+ };
- if (typeof pointer === 'undefined') {
- pointer = event[clientProp];
+ function getTouchInfo(event) {
+ var touchProp = event.changedTouches;
+ if (touchProp && touchProp[0]) {
+ return touchProp[0];
}
-
- return pointer;
+ return event;
}
- if (fabric.isTouchSupported) {
- pointerX = function(event) {
- return _getPointer(event, 'pageX', 'clientX');
- };
- pointerY = function(event) {
- return _getPointer(event, 'pageY', 'clientY');
+ fabric.util.getPointer = function(event) {
+ var element = event.target,
+ scroll = fabric.util.getScrollLeftTop(element),
+ _evt = getTouchInfo(event);
+ return {
+ x: _evt.clientX + scroll.left,
+ y: _evt.clientY + scroll.top
};
- }
-
- fabric.util.getPointer = getPointer;
-
+ };
})();
@@ -2841,6 +2686,7 @@ if (typeof console !== 'undefined') {
* @param {Number} [options.byValue=100] Value to modify the property by
* @param {Function} [options.easing] Easing function
* @param {Number} [options.duration=500] Duration of change (in ms)
+ * @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.
*/
function animate(options) {
@@ -2861,6 +2707,8 @@ if (typeof console !== 'undefined') {
options.onStart && options.onStart();
(function tick(ticktime) {
+ // TODO: move abort call after calculation
+ // and pass (current,valuePerc, timePerc) as arguments
if (abort()) {
onComplete(endValue, 1, 1);
return;
@@ -2938,6 +2786,7 @@ if (typeof console !== 'undefined') {
* @param {Function} [options.onChange] Callback; invoked on every value change
* @param {Function} [options.onComplete] Callback; invoked when value change is completed
* @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.
+ * @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.
*/
function animateColor(fromColor, toColor, duration, options) {
var startColor = new fabric.Color(fromColor).getSource(),
@@ -4411,8 +4260,8 @@ if (typeof console !== 'undefined') {
loadSVGFromString: function(string, callback, reviver, options) {
string = string.trim();
var doc;
- if (typeof DOMParser !== 'undefined') {
- var parser = new DOMParser();
+ if (typeof fabric.window.DOMParser !== 'undefined') {
+ var parser = new fabric.window.DOMParser();
if (parser && parser.parseFromString) {
doc = parser.parseFromString(string, 'text/xml');
}
@@ -5892,12 +5741,13 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* @param {Object} object Object to create a gradient for
* @return {String} SVG representation of an gradient (linear/radial)
*/
- toSVG: function(object) {
- var coords = clone(this.coords, true), i, len,
+ toSVG: function(object, options) {
+ var coords = clone(this.coords, true), i, len, options = options || {},
markup, commonAttributes, colorStops = clone(this.colorStops, true),
needsSwap = coords.r1 > coords.r2,
transform = this.gradientTransform ? this.gradientTransform.concat() : fabric.iMatrix.concat(),
- offsetX = object.width / 2 - this.offsetX, offsetY = object.height / 2 - this.offsetY;
+ offsetX = object.width / 2 - this.offsetX, offsetY = object.height / 2 - this.offsetY,
+ withViewport = !!options.additionalTransform;
// colorStops must be sorted ascending
colorStops.sort(function(a, b) {
return a.offset - b.offset;
@@ -5913,7 +5763,8 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
commonAttributes = 'id="SVGID_' + this.id +
'" gradientUnits="userSpaceOnUse"';
- commonAttributes += ' gradientTransform="matrix(' + transform.join(' ') + ')" ';
+ commonAttributes += ' gradientTransform="' + (withViewport ?
+ options.additionalTransform + ' ' : '') + fabric.util.matrixToSVG(transform) + '" ';
if (this.type === 'linear') {
markup = [
@@ -6428,6 +6279,15 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
includeDefaultValues: true,
/**
+ * When `false`, the shadow will scale with the object.
+ * When `true`, the shadow's offsetX, offsetY, and blur will not be affected by the object's scale.
+ * default to false
+ * @type Boolean
+ * @default
+ */
+ nonScaling: false,
+
+ /**
* Constructor
* @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetY properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px")
* @return {fabric.Shadow} thisArg
@@ -6526,12 +6386,13 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
blur: this.blur,
offsetX: this.offsetX,
offsetY: this.offsetY,
- affectStroke: this.affectStroke
+ affectStroke: this.affectStroke,
+ nonScaling: this.nonScaling
};
}
var obj = { }, proto = fabric.Shadow.prototype;
- ['color', 'blur', 'offsetX', 'offsetY', 'affectStroke'].forEach(function(prop) {
+ ['color', 'blur', 'offsetX', 'offsetY', 'affectStroke', 'nonScaling'].forEach(function(prop) {
if (this[prop] !== proto[prop]) {
obj[prop] = this[prop];
}
@@ -7551,27 +7412,41 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* @param {string} property 'background' or 'overlay'
*/
_renderBackgroundOrOverlay: function(ctx, property) {
- var object = this[property + 'Color'], v;
- if (object) {
- ctx.fillStyle = object.toLive
- ? object.toLive(ctx, this)
- : object;
-
- ctx.fillRect(
- object.offsetX || 0,
- object.offsetY || 0,
- this.width,
- this.height);
+ var fill = this[property + 'Color'], object = this[property + 'Image'],
+ v = this.viewportTransform, needsVpt = this[property + 'Vpt'];
+ if (!fill && !object) {
+ return;
+ }
+ if (fill) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(this.width, 0);
+ ctx.lineTo(this.width, this.height);
+ ctx.lineTo(0, this.height);
+ ctx.closePath();
+ ctx.fillStyle = fill.toLive
+ ? fill.toLive(ctx, this)
+ : fill;
+ if (needsVpt) {
+ ctx.transform(
+ v[0], v[1], v[2], v[3],
+ v[4] + (fill.offsetX || 0),
+ v[5] + (fill.offsetY || 0)
+ );
+ }
+ var m = fill.gradientTransform || fill.patternTransform;
+ m && ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ ctx.fill();
+ ctx.restore();
}
- object = this[property + 'Image'];
if (object) {
- if (this[property + 'Vpt']) {
- v = this.viewportTransform;
- ctx.save();
+ ctx.save();
+ if (needsVpt) {
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
}
object.render(ctx);
- this[property + 'Vpt'] && ctx.restore();
+ ctx.restore();
}
},
@@ -7732,7 +7607,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
objects: this._toObjects(methodName, propertiesToInclude),
};
if (clipPath) {
- data.clipPath = this._toObjectMethod(clipPath, methodName, propertiesToInclude);
+ data.clipPath = this._toObject(this.clipPath, methodName, propertiesToInclude);
}
extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
@@ -7853,13 +7728,13 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
if (this.clipPath) {
markup.push('<g clip-path="url(#' + this.clipPath.clipPathId + ')" >\n');
}
- this._setSVGBgOverlayColor(markup, 'backgroundColor');
+ this._setSVGBgOverlayColor(markup, 'background');
this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
this._setSVGObjects(markup, reviver);
if (this.clipPath) {
markup.push('</g>\n');
}
- this._setSVGBgOverlayColor(markup, 'overlayColor');
+ this._setSVGBgOverlayColor(markup, 'overlay');
this._setSVGBgOverlayImage(markup, 'overlayImage', reviver);
markup.push('</svg>');
@@ -7943,10 +7818,18 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
*/
createSVGRefElementsMarkup: function() {
var _this = this,
- markup = ['backgroundColor', 'overlayColor'].map(function(prop) {
- var fill = _this[prop];
+ markup = ['background', 'overlay'].map(function(prop) {
+ var fill = _this[prop + 'Color'];
if (fill && fill.toLive) {
- return fill.toSVG(_this, false);
+ var shouldTransform = _this[prop + 'Vpt'], vpt = _this.viewportTransform,
+ object = {
+ width: _this.width / (shouldTransform ? vpt[0] : 1),
+ height: _this.height / (shouldTransform ? vpt[3] : 1)
+ };
+ return fill.toSVG(
+ object,
+ { additionalTransform: shouldTransform ? fabric.util.matrixToSVG(vpt) : '' }
+ );
}
});
return markup.join('');
@@ -8043,16 +7926,18 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* @private
*/
_setSVGBgOverlayColor: function(markup, property) {
- var filler = this[property], vpt = this.viewportTransform, finalWidth = this.width / vpt[0],
- finalHeight = this.height / vpt[3];
+ var filler = this[property + 'Color'], vpt = this.viewportTransform, finalWidth = this.width,
+ finalHeight = this.height;
if (!filler) {
return;
}
if (filler.toLive) {
- var repeat = filler.repeat;
+ var repeat = filler.repeat, iVpt = fabric.util.invertTransform(vpt), shouldInvert = this[property + 'Vpt'],
+ additionalTransform = shouldInvert ? fabric.util.matrixToSVG(iVpt) : '';
markup.push(
- '<rect transform="translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
- ' x="', filler.offsetX - finalWidth / 2, '" y="', filler.offsetY - finalHeight / 2, '" ',
+ '<rect transform="' + additionalTransform + ' translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
+ ' x="', filler.offsetX - finalWidth / 2,
+ '" y="', filler.offsetY - finalHeight / 2, '" ',
'width="',
(repeat === 'repeat-y' || repeat === 'no-repeat'
? filler.source.width
@@ -8068,7 +7953,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
else {
markup.push(
'<rect x="0" y="0" width="100%" height="100%" ',
- 'fill="', this[property], '"',
+ 'fill="', filler, '"',
'></rect>\n'
);
}
@@ -8521,6 +8406,11 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
ctx.shadowOffsetY = this.shadow.offsetY * zoom;
},
+ needsFullRender: function() {
+ var color = new fabric.Color(this.color);
+ return color.getAlpha() < 1 || !!this.shadow;
+ },
+
/**
* Removes brush shadow styles
* @private
@@ -8535,7 +8425,6 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
(function() {
-
/**
* PencilBrush class
* @class fabric.PencilBrush
@@ -8544,6 +8433,13 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
/**
+ * Discard points that are less than `decimate` pixel distant from each other
+ * @type Number
+ * @default 0.4
+ */
+ decimate: 0.4,
+
+ /**
* Constructor
* @param {fabric.Canvas} canvas
* @return {fabric.PencilBrush} Instance of a pencil brush
@@ -8567,7 +8463,10 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* Inovoked on mouse down
* @param {Object} pointer
*/
- onMouseDown: function(pointer) {
+ onMouseDown: function(pointer, options) {
+ if (!this.canvas._isMainEvent(options.e)) {
+ return;
+ }
this._prepareForDrawing(pointer);
// capture coordinates immediately
// this allows to draw dots (when movement never occurs)
@@ -8579,9 +8478,12 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* Inovoked on mouse move
* @param {Object} pointer
*/
- onMouseMove: function(pointer) {
+ onMouseMove: function(pointer, options) {
+ if (!this.canvas._isMainEvent(options.e)) {
+ return;
+ }
if (this._captureDrawingPath(pointer) && this._points.length > 1) {
- if (this.needsFullRender) {
+ if (this.needsFullRender()) {
// redraw curve
// clear top canvas
this.canvas.clearContext(this.canvas.contextTop);
@@ -8605,9 +8507,13 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
/**
* Invoked on mouse up
*/
- onMouseUp: function() {
+ onMouseUp: function(options) {
+ if (!this.canvas._isMainEvent(options.e)) {
+ return true;
+ }
this.oldEnd = undefined;
this._finalizeAndAddPath();
+ return false;
},
/**
@@ -8640,10 +8546,8 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @private
*/
_reset: function() {
- this._points.length = 0;
+ this._points = [];
this._setBrushStyles();
- var color = new fabric.Color(this.color);
- this.needsFullRender = (color.getAlpha() < 1);
this._setShadow();
},
@@ -8704,7 +8608,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
var path = [], i, width = this.width / 1000,
p1 = new fabric.Point(points[0].x, points[0].y),
p2 = new fabric.Point(points[1].x, points[1].y),
- len = points.length, multSignX = 1, multSignY = 1, manyPoints = len > 2;
+ len = points.length, multSignX = 1, multSignY = 0, manyPoints = len > 2;
if (manyPoints) {
multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1;
@@ -8747,10 +8651,6 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
strokeLineJoin: this.strokeLineJoin,
strokeDashArray: this.strokeDashArray,
});
- var position = new fabric.Point(path.left + path.width / 2, path.top + path.height / 2);
- position = path.translateToGivenOrigin(position, 'center', 'center', path.originX, path.originY);
- path.top = position.y;
- path.left = position.x;
if (this.shadow) {
this.shadow.affectStroke = true;
path.setShadow(this.shadow);
@@ -8760,6 +8660,29 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
},
/**
+ * Decimate poins array with the decimate value
+ */
+ decimatePoints: function(points, distance) {
+ if (points.length <= 2) {
+ return points;
+ }
+ var zoom = this.canvas.getZoom(), adjustedDistance = Math.pow(distance / zoom, 2),
+ i, l = points.length - 1, lastPoint = points[0], newPoints = [lastPoint],
+ cDistance;
+ for (i = 1; i < l; i++) {
+ cDistance = Math.pow(lastPoint.x - points[i].x, 2) + Math.pow(lastPoint.y - points[i].y, 2);
+ if (cDistance >= adjustedDistance) {
+ lastPoint = points[i];
+ newPoints.push(lastPoint);
+ }
+ }
+ if (newPoints.length === 1) {
+ newPoints.push(new fabric.Point(newPoints[0].x, newPoints[0].y));
+ }
+ return newPoints;
+ },
+
+ /**
* On mouseup after drawing the path on contextTop canvas
* we use the points captured to create an new fabric path object
* and add it to the fabric canvas.
@@ -8767,7 +8690,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
_finalizeAndAddPath: function() {
var ctx = this.canvas.contextTop;
ctx.closePath();
-
+ if (this.decimate) {
+ this._points = this.decimatePoints(this._points, this.decimate);
+ }
var pathData = this.convertPointsToSVGPath(this._points).join('');
if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
@@ -8824,13 +8749,16 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
var point = this.addPoint(pointer),
ctx = this.canvas.contextTop;
this._saveAndTransform(ctx);
+ this.dot(ctx, point);
+ ctx.restore();
+ },
+
+ dot: function(ctx, point) {
ctx.fillStyle = point.fill;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
-
- ctx.restore();
},
/**
@@ -8849,15 +8777,10 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
*/
_render: function() {
var ctx = this.canvas.contextTop, i, len,
- points = this.points, point;
+ points = this.points;
this._saveAndTransform(ctx);
for (i = 0, len = points.length; i < len; i++) {
- point = points[i];
- ctx.fillStyle = point.fill;
- ctx.beginPath();
- ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
- ctx.closePath();
- ctx.fill();
+ this.dot(ctx, points[i]);
}
ctx.restore();
},
@@ -8867,7 +8790,14 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* @param {Object} pointer
*/
onMouseMove: function(pointer) {
- this.drawDot(pointer);
+ if (this.needsFullRender()) {
+ this.canvas.clearContext(this.canvas.contextTop);
+ this.addPoint(pointer);
+ this._render();
+ }
+ else {
+ this.drawDot(pointer);
+ }
},
/**
@@ -10115,6 +10045,18 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
changeX = target.scaleX !== scaleX,
changeY = target.scaleY !== scaleY;
+ transform.newScaleX = scaleX;
+ transform.newScaleY = scaleY;
+ if (by === 'x' && target instanceof fabric.Textbox) {
+ var w = target.width * (localMouse.x / _dim.x);
+ if (w >= target.getMinWidth()) {
+ scaled = w !== target.width;
+ target.set('width', w);
+ return scaled;
+ }
+ return false;
+ }
+
if (lockScalingFlip && scaleX <= 0 && scaleX < target.scaleX) {
forbidScalingX = true;
localMouse.x = 0;
@@ -10138,8 +10080,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
else if (by === 'y' && !target.get('lockUniScaling')) {
forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = changeY));
}
- transform.newScaleX = scaleX;
- transform.newScaleY = scaleY;
forbidScalingX || forbidScalingY || this._flipObject(transform, by);
return scaled;
},
@@ -10606,7 +10546,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
height: height + 'px',
left: 0,
top: 0,
- 'touch-action': this.allowTouchScrolling ? 'manipulation' : 'none'
+ 'touch-action': this.allowTouchScrolling ? 'manipulation' : 'none',
+ '-ms-touch-action': this.allowTouchScrolling ? 'manipulation' : 'none'
});
element.width = width;
element.height = height;
@@ -10788,9 +10729,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @chainable
*/
discardActiveObject: function (e) {
- var currentActives = this.getActiveObjects();
+ var currentActives = this.getActiveObjects(), activeObject = this.getActiveObject();
if (currentActives.length) {
- this.fire('before:selection:cleared', { target: currentActives[0], e: e });
+ this.fire('before:selection:cleared', { target: activeObject, e: e });
}
this._discardActiveObject(e);
this._fireSelectionEvents(currentActives, e);
@@ -10920,11 +10861,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
fabric.Canvas[prop] = fabric.StaticCanvas[prop];
}
}
-
- if (fabric.isTouchSupported) {
- /** @ignore */
- fabric.Canvas.prototype._setCursorFromEvent = function() { };
- }
})();
@@ -10946,7 +10882,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
addEventOptions = { passive: false };
function checkClick(e, value) {
- return 'which' in e ? e.which === value : e.button === value - 1;
+ return e.button && (e.button === value - 1);
}
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
@@ -10967,6 +10903,13 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
],
/**
+ * Contains the id of the touch event that owns the fabric transform
+ * @type Number
+ * @private
+ */
+ mainTouchId: null,
+
+ /**
* Adds mouse listeners to canvas
* @private
*/
@@ -10979,27 +10922,38 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.addOrRemove(addListener, 'add');
},
+ /**
+ * return an event prefix pointer or mouse.
+ * @private
+ */
+ _getEventPrefix: function () {
+ return this.enablePointerEvents ? 'pointer' : 'mouse';
+ },
+
addOrRemove: function(functor, eventjsFunctor) {
+ var canvasElement = this.upperCanvasEl,
+ eventTypePrefix = this._getEventPrefix();
functor(fabric.window, 'resize', this._onResize);
- functor(this.upperCanvasEl, 'mousedown', this._onMouseDown);
- functor(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
- functor(this.upperCanvasEl, 'mouseout', this._onMouseOut);
- functor(this.upperCanvasEl, 'mouseenter', this._onMouseEnter);
- functor(this.upperCanvasEl, 'wheel', this._onMouseWheel);
- functor(this.upperCanvasEl, 'contextmenu', this._onContextMenu);
- functor(this.upperCanvasEl, 'dblclick', this._onDoubleClick);
- functor(this.upperCanvasEl, 'touchstart', this._onMouseDown, addEventOptions);
- functor(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
- functor(this.upperCanvasEl, 'dragover', this._onDragOver);
- functor(this.upperCanvasEl, 'dragenter', this._onDragEnter);
- functor(this.upperCanvasEl, 'dragleave', this._onDragLeave);
- functor(this.upperCanvasEl, 'drop', this._onDrop);
+ functor(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
+ functor(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
+ functor(canvasElement, eventTypePrefix + 'out', this._onMouseOut);
+ functor(canvasElement, eventTypePrefix + 'enter', this._onMouseEnter);
+ functor(canvasElement, 'wheel', this._onMouseWheel);
+ functor(canvasElement, 'contextmenu', this._onContextMenu);
+ functor(canvasElement, 'dblclick', this._onDoubleClick);
+ functor(canvasElement, 'dragover', this._onDragOver);
+ functor(canvasElement, 'dragenter', this._onDragEnter);
+ functor(canvasElement, 'dragleave', this._onDragLeave);
+ functor(canvasElement, 'drop', this._onDrop);
+ if (!this.enablePointerEvents) {
+ functor(canvasElement, 'touchstart', this._onTouchStart, addEventOptions);
+ }
if (typeof eventjs !== 'undefined' && eventjsFunctor in eventjs) {
- eventjs[eventjsFunctor](this.upperCanvasEl, 'gesture', this._onGesture);
- eventjs[eventjsFunctor](this.upperCanvasEl, 'drag', this._onDrag);
- eventjs[eventjsFunctor](this.upperCanvasEl, 'orientation', this._onOrientationChange);
- eventjs[eventjsFunctor](this.upperCanvasEl, 'shake', this._onShake);
- eventjs[eventjsFunctor](this.upperCanvasEl, 'longpress', this._onLongPress);
+ eventjs[eventjsFunctor](canvasElement, 'gesture', this._onGesture);
+ eventjs[eventjsFunctor](canvasElement, 'drag', this._onDrag);
+ eventjs[eventjsFunctor](canvasElement, 'orientation', this._onOrientationChange);
+ eventjs[eventjsFunctor](canvasElement, 'shake', this._onShake);
+ eventjs[eventjsFunctor](canvasElement, 'longpress', this._onLongPress);
}
},
@@ -11009,9 +10963,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
removeListeners: function() {
this.addOrRemove(removeListener, 'remove');
// if you dispose on a mouseDown, before mouse up, you need to clean document to...
- removeListener(fabric.document, 'mouseup', this._onMouseUp);
- removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
- removeListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
+ var eventTypePrefix = this._getEventPrefix();
+ removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
+ removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
+ removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
},
@@ -11024,8 +10979,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return;
}
this._onMouseDown = this._onMouseDown.bind(this);
+ this._onTouchStart = this._onTouchStart.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
+ this._onTouchEnd = this._onTouchEnd.bind(this);
this._onResize = this._onResize.bind(this);
this._onGesture = this._onGesture.bind(this);
this._onDrag = this._onDrag.bind(this);
@@ -11093,7 +11050,13 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {Event} e Event object fired on mouseenter
*/
_onMouseEnter: function(e) {
- if (!this.findTarget(e)) {
+ // This find target and consequent 'mouse:over' is used to
+ // clear old instances on hovered target.
+ // calling findTarget has the side effect of killing target.__corner.
+ // as a short term fix we are not firing this if we are currently transforming.
+ // as a long term fix we need to separate the action of finding a target with the
+ // side effects we added to it.
+ if (!this.currentTransform && !this.findTarget(e)) {
this.fire('mouse:over', { target: null, e: e });
this._hoveredTarget = null;
}
@@ -11160,26 +11123,104 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
},
/**
+ * Return a the id of an event.
+ * returns either the pointerId or the identifier or 0 for the mouse event
+ * @private
+ * @param {Event} evt Event object
+ */
+ getPointerId: function(evt) {
+ var changedTouches = evt.changedTouches;
+
+ if (changedTouches) {
+ return changedTouches[0] && changedTouches[0].identifier;
+ }
+
+ if (this.enablePointerEvents) {
+ return evt.pointerId;
+ }
+
+ return -1;
+ },
+
+ /**
+ * Determines if an event has the id of the event that is considered main
+ * @private
+ * @param {evt} event Event object
+ */
+ _isMainEvent: function(evt) {
+ if (evt.isPrimary === true) {
+ return true;
+ }
+ if (evt.isPrimary === false) {
+ return false;
+ }
+ if (evt.type === 'touchend' && evt.touches.length === 0) {
+ return true;
+ }
+ if (evt.changedTouches) {
+ return evt.changedTouches[0].identifier === this.mainTouchId;
+ }
+ return true;
+ },
+
+ /**
* @private
* @param {Event} e Event object fired on mousedown
*/
- _onMouseDown: function (e) {
+ _onTouchStart: function(e) {
+ e.preventDefault();
+ if (this.mainTouchId === null) {
+ this.mainTouchId = this.getPointerId(e);
+ }
this.__onMouseDown(e);
this._resetTransformEventData();
- addListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
+ var canvasElement = this.upperCanvasEl,
+ eventTypePrefix = this._getEventPrefix();
+ addListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
+ // Unbind mousedown to prevent double triggers from touch devices
+ removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
+ },
- removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
- removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
+ /**
+ * @private
+ * @param {Event} e Event object fired on mousedown
+ */
+ _onMouseDown: function (e) {
+ this.__onMouseDown(e);
+ this._resetTransformEventData();
+ var canvasElement = this.upperCanvasEl,
+ eventTypePrefix = this._getEventPrefix();
+ removeListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
+ addListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
+ addListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
+ },
- if (e.type === 'touchstart') {
- // Unbind mousedown to prevent double triggers from touch devices
- removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
+ /**
+ * @private
+ * @param {Event} e Event object fired on mousedown
+ */
+ _onTouchEnd: function(e) {
+ if (e.touches.length > 0) {
+ // if there are still touches stop here
+ return;
}
- else {
- addListener(fabric.document, 'mouseup', this._onMouseUp);
- addListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
+ this.__onMouseUp(e);
+ this._resetTransformEventData();
+ this.mainTouchId = null;
+ var eventTypePrefix = this._getEventPrefix();
+ removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
+ removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
+ var _this = this;
+ if (this._willAddMouseDown) {
+ clearTimeout(this._willAddMouseDown);
}
+ this._willAddMouseDown = setTimeout(function() {
+ // Wait 400ms before rebinding mousedown to prevent double triggers
+ // from touch devices
+ addListener(_this.upperCanvasEl, eventTypePrefix + 'down', _this._onMouseDown);
+ _this._willAddMouseDown = 0;
+ }, 400);
},
/**
@@ -11189,22 +11230,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_onMouseUp: function (e) {
this.__onMouseUp(e);
this._resetTransformEventData();
- removeListener(fabric.document, 'mouseup', this._onMouseUp);
- removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
-
- removeListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
- removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
-
- addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
- addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
-
- if (e.type === 'touchend') {
- // Wait 400ms before rebinding mousedown to prevent double triggers
- // from touch devices
- var _this = this;
- setTimeout(function() {
- addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
- }, 400);
+ var canvasElement = this.upperCanvasEl,
+ eventTypePrefix = this._getEventPrefix();
+ if (this._isMainEvent(e)) {
+ removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
+ removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
+ addListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
}
},
@@ -11284,6 +11315,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return;
}
+ if (!this._isMainEvent(e)) {
+ return;
+ }
if (transform) {
this._finalizeCurrentTransform(e);
shouldRender = transform.actionPerformed;
@@ -11450,7 +11484,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
fabric.util.clipContext(this, this.contextTop);
}
var pointer = this.getPointer(e);
- this.freeDrawingBrush.onMouseDown(pointer);
+ this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer });
this._handleEvent(e, 'down');
},
@@ -11461,7 +11495,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_onMouseMoveInDrawingMode: function(e) {
if (this._isCurrentlyDrawing) {
var pointer = this.getPointer(e);
- this.freeDrawingBrush.onMouseMove(pointer);
+ this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer });
}
this.setCursor(this.freeDrawingCursor);
this._handleEvent(e, 'move');
@@ -11472,11 +11506,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {Event} e Event object fired on mouseup
*/
_onMouseUpInDrawingMode: function(e) {
- this._isCurrentlyDrawing = false;
if (this.clipTo) {
this.contextTop.restore();
}
- this.freeDrawingBrush.onMouseUp();
+ var pointer = this.getPointer(e);
+ this._isCurrentlyDrawing = this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer });
this._handleEvent(e, 'up');
},
@@ -11512,6 +11546,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
return;
}
+ if (!this._isMainEvent(e)) {
+ return;
+ }
+
// ignore if some object is being transformed at this moment
if (this._currentTransform) {
return;
@@ -11595,7 +11633,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* Method that defines the actions when mouse is hovering the canvas.
- * The currentTransform parameter will definde whether the user is rotating/scaling/translating
+ * The currentTransform parameter will define whether the user is rotating/scaling/translating
* an image or neither of them (only hovering). A group selection is also possible and would cancel
* all any other type of action.
* In case of an image transformation only the top canvas will be rendered.
@@ -11611,7 +11649,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this._onMouseMoveInDrawingMode(e);
return;
}
- if (typeof e.touches !== 'undefined' && e.touches.length > 1) {
+
+ if (!this._isMainEvent(e)) {
return;
}
@@ -11645,7 +11684,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fireOverOutEvents: function(target, e) {
- this.fireSynteticInOutEvents(target, e, {
+ this.fireSyntheticInOutEvents(target, e, {
targetName: '_hoveredTarget',
canvasEvtOut: 'mouse:out',
evtOut: 'mouseout',
@@ -11661,7 +11700,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fireEnterLeaveEvents: function(target, e) {
- this.fireSynteticInOutEvents(target, e, {
+ this.fireSyntheticInOutEvents(target, e, {
targetName: '_draggedoverTarget',
evtOut: 'dragleave',
evtIn: 'dragenter',
@@ -11669,7 +11708,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
},
/**
- * Manage the syntetic in/out events for the fabric objects on the canvas
+ * Manage the synthetic in/out events for the fabric objects on the canvas
* @param {Fabric.Object} target the target where the target from the supported events
* @param {Event} e Event object fired
* @param {Object} config configuration for the function to work
@@ -11680,7 +11719,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {String} config.evtIn name of the event to fire for in
* @private
*/
- fireSynteticInOutEvents: function(target, e, config) {
+ fireSyntheticInOutEvents: function(target, e, config) {
var inOpt, outOpt, oldTarget = this[config.targetName], outFires, inFires,
targetChanged = oldTarget !== target, canvasEvtIn = config.canvasEvtIn, canvasEvtOut = config.canvasEvtOut;
if (targetChanged) {
@@ -11801,9 +11840,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
* @param {Event} e Event object
- * @param {Object} transform current tranform
+ * @param {Object} transform current transform
* @param {Number} x mouse position x from origin
- * @param {Number} y mouse poistion y from origin
+ * @param {Number} y mouse position y from origin
* @return {Boolean} true if the scaling occurred
*/
_onScale: function(e, transform, x, y) {
@@ -11843,7 +11882,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.setCursor(this.defaultCursor);
return false;
}
-
var hoverCursor = target.hoverCursor || this.hoverCursor,
activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ?
this._activeObject : null,
@@ -12048,7 +12086,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
for (var i = this._objects.length; i--; ) {
currentObject = this._objects[i];
- if (!currentObject || !currentObject.selectable || !currentObject.visible || currentObject.onSelect({ e: e })) {
+ if (!currentObject || !currentObject.selectable || !currentObject.visible) {
continue;
}
@@ -12058,7 +12096,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
(allowIntersect && currentObject.containsPoint(selectionX2Y2))
) {
group.push(currentObject);
-
// only add one object if it's a click
if (isClick) {
break;
@@ -12066,6 +12103,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
}
}
+ if (group.length > 1) {
+ group = group.filter(function(object) {
+ return !object.onSelect({ e: e });
+ });
+ }
+
return group;
},
@@ -12134,7 +12177,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* Create a new HTMLCanvas element painted with the current canvas content.
* No need to resize the actual one or repaint it.
* Will transfer object ownership to a new canvas, paint it, and set everything back.
- * This is an intermediary step used to get to a dataUrl but also it is usefull to
+ * This is an intermediary step used to get to a dataUrl but also it is useful to
* create quick image copies of a canvas without passing for the dataUrl string
* @param {Number} [multiplier] a zoom factor.
* @param {Object} [cropping] Cropping informations
@@ -12156,25 +12199,27 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
translateX = (vp[4] - (cropping.left || 0)) * multiplier,
translateY = (vp[5] - (cropping.top || 0)) * multiplier,
originalInteractive = this.interactive,
- originalContext = this.contextContainer,
newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
- canvasEl = fabric.util.createCanvasElement();
+ originalRetina = this.enableRetinaScaling,
+ canvasEl = fabric.util.createCanvasElement(),
+ originalContextTop = this.contextTop;
canvasEl.width = scaledWidth;
canvasEl.height = scaledHeight;
+ this.contextTop = null;
+ this.enableRetinaScaling = false;
this.interactive = false;
this.viewportTransform = newVp;
this.width = scaledWidth;
this.height = scaledHeight;
this.calcViewportBoundaries();
- this.contextContainer = canvasEl.getContext('2d');
- // will be renderAllExport();
- this.renderAll();
+ this.renderCanvas(canvasEl.getContext('2d'), this._objects);
this.viewportTransform = vp;
this.width = originalWidth;
this.height = originalHeight;
this.calcViewportBoundaries();
- this.contextContainer = originalContext;
this.interactive = originalInteractive;
+ this.enableRetinaScaling = originalRetina;
+ this.contextTop = originalContextTop;
return canvasEl;
},
});
@@ -12233,31 +12278,25 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
: fabric.util.object.clone(json);
var _this = this,
+ clipPath = serialized.clipPath,
renderOnAddRemove = this.renderOnAddRemove;
+
this.renderOnAddRemove = false;
+ delete serialized.clipPath;
+
this._enlivenObjects(serialized.objects, function (enlivenedObjects) {
_this.clear();
_this._setBgOverlay(serialized, function () {
- enlivenedObjects.forEach(function(obj, index) {
- // we splice the array just in case some custom classes restored from JSON
- // will add more object to canvas at canvas init.
- _this.insertAt(obj, index);
- });
- _this.renderOnAddRemove = renderOnAddRemove;
- // remove parts i cannot set as options
- delete serialized.objects;
- delete serialized.backgroundImage;
- delete serialized.overlayImage;
- delete serialized.background;
- delete serialized.overlay;
- // this._initOptions does too many things to just
- // call it. Normally loading an Object from JSON
- // create the Object instance. Here the Canvas is
- // already an instance and we are just loading things over it
- _this._setOptions(serialized);
- _this.renderAll();
- callback && callback();
+ if (clipPath) {
+ _this._enlivenObjects([clipPath], function (enlivenedCanvasClip) {
+ _this.clipPath = enlivenedCanvasClip[0];
+ _this.__setupCanvas.call(_this, serialized, enlivenedObjects, renderOnAddRemove, callback);
+ });
+ }
+ else {
+ _this.__setupCanvas.call(_this, serialized, enlivenedObjects, renderOnAddRemove, callback);
+ }
});
}, reviver);
return this;
@@ -12266,6 +12305,36 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* @private
* @param {Object} serialized Object with background and overlay information
+ * @param {Array} restored canvas objects
+ * @param {Function} cached renderOnAddRemove callback
+ * @param {Function} callback Invoked after all background and overlay images/patterns loaded
+ */
+ __setupCanvas: function(serialized, enlivenedObjects, renderOnAddRemove, callback) {
+ var _this = this;
+ enlivenedObjects.forEach(function(obj, index) {
+ // we splice the array just in case some custom classes restored from JSON
+ // will add more object to canvas at canvas init.
+ _this.insertAt(obj, index);
+ });
+ this.renderOnAddRemove = renderOnAddRemove;
+ // remove parts i cannot set as options
+ delete serialized.objects;
+ delete serialized.backgroundImage;
+ delete serialized.overlayImage;
+ delete serialized.background;
+ delete serialized.overlay;
+ // this._initOptions does too many things to just
+ // call it. Normally loading an Object from JSON
+ // create the Object instance. Here the Canvas is
+ // already an instance and we are just loading things over it
+ this._setOptions(serialized);
+ this.renderAll();
+ callback && callback();
+ },
+
+ /**
+ * @private
+ * @param {Object} serialized Object with background and overlay information
* @param {Function} callback Invoked after all background and overlay images/patterns loaded
*/
_setBgOverlay: function(serialized, callback) {
@@ -12781,6 +12850,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Transform matrix (similar to SVG's transform matrix)
+ * This property has been depreacted. Since caching and and qrDecompose this
+ * property can be handled with the standard top,left,scaleX,scaleY,angle and skewX.
+ * A documentation example on how to parse and merge a transformMatrix will be provided before
+ * completely removing it in fabric 4.0
+ * If you are starting a project now, DO NOT use it.
+ * @deprecated since 3.2.0
* @type Array
*/
transformMatrix: null,
@@ -12931,7 +13006,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* When `true`, object is not exported in OBJECT/JSON
- * since 1.6.3
+ * @since 1.6.3
* @type Boolean
* @default
*/
@@ -12939,8 +13014,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* When `true`, object is cached on an additional canvas.
+ * When `false`, object is not cached unless necessary ( clipPath )
* default to true
- * since 1.7.0
+ * @since 1.7.0
* @type Boolean
* @default true
*/
@@ -13142,20 +13218,20 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_getCacheCanvasDimensions: function() {
var objectScale = this.getTotalObjectScaling(),
- dim = this._getNonTransformedDimensions(),
- zoomX = objectScale.scaleX,
- zoomY = objectScale.scaleY,
- width = dim.x * zoomX,
- height = dim.y * zoomY;
+ // caculate dimensions without skewing
+ dim = this._getTransformedDimensions(0, 0),
+ neededX = dim.x * objectScale.scaleX / this.scaleX,
+ neededY = dim.y * objectScale.scaleY / this.scaleY;
return {
- // for sure this ALIASING_LIMIT is slightly crating problem
- // in situation in wich the cache canvas gets an upper limit
- width: width + ALIASING_LIMIT,
- height: height + ALIASING_LIMIT,
- zoomX: zoomX,
- zoomY: zoomY,
- x: dim.x,
- y: dim.y
+ // for sure this ALIASING_LIMIT is slightly creating problem
+ // in situation in which the cache canvas gets an upper limit
+ // also objectScale contains already scaleX and scaleY
+ width: neededX + ALIASING_LIMIT,
+ height: neededY + ALIASING_LIMIT,
+ zoomX: objectScale.scaleX,
+ zoomY: objectScale.scaleY,
+ x: neededX,
+ y: neededY
};
},
@@ -13204,8 +13280,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._cacheContext.setTransform(1, 0, 0, 1, 0, 0);
this._cacheContext.clearRect(0, 0, canvas.width, canvas.height);
}
- drawingWidth = dims.x * zoomX / 2;
- drawingHeight = dims.y * zoomY / 2;
+ drawingWidth = dims.x / 2;
+ drawingHeight = dims.y / 2;
this.cacheTranslationX = Math.round(canvas.width / 2 - drawingWidth) + drawingWidth;
this.cacheTranslationY = Math.round(canvas.height / 2 - drawingHeight) + drawingHeight;
this.cacheWidth = width;
@@ -13322,6 +13398,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var prototype = fabric.util.getKlass(object.type).prototype,
stateProperties = prototype.stateProperties;
stateProperties.forEach(function(prop) {
+ if (prop === 'left' || prop === 'top') {
+ return;
+ }
if (object[prop] === prototype[prop]) {
delete object[prop];
}
@@ -13525,15 +13604,44 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
+ * return true if the object will draw a stroke
+ * Does not consider text styles. This is just a shortcut used at rendering time
+ * We want it to be an aproximation and be fast.
+ * wrote to avoid extra caching, it has to return true when stroke happens,
+ * can guess when it will not happen at 100% chance, does not matter if it misses
+ * some use case where the stroke is invisible.
+ * @since 3.0.0
+ * @returns Boolean
+ */
+ hasStroke: function() {
+ return this.stroke && this.stroke !== 'transparent' && this.strokeWidth !== 0;
+ },
+
+ /**
+ * return true if the object will draw a fill
+ * Does not consider text styles. This is just a shortcut used at rendering time
+ * We want it to be an aproximation and be fast.
+ * wrote to avoid extra caching, it has to return true when fill happens,
+ * can guess when it will not happen at 100% chance, does not matter if it misses
+ * some use case where the fill is invisible.
+ * @since 3.0.0
+ * @returns Boolean
+ */
+ hasFill: function() {
+ return this.fill && this.fill !== 'transparent';
+ },
+
+ /**
* When set to `true`, force the object to have its own cache, even if it is inside a group
* it may be needed when your object behave in a particular way on the cache and always needs
* its own isolated canvas to render correctly.
* Created to be overridden
* since 1.7.12
- * @returns false
+ * @returns Boolean
*/
needsItsOwnCache: function() {
- if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') {
+ if (this.paintFirst === 'stroke' &&
+ this.hasFill() && this.hasStroke() && typeof this.shadow === 'object') {
return true;
}
if (this.clipPath) {
@@ -13548,11 +13656,14 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* needsItsOwnCache should be used when the object drawing method requires
* a cache step. None of the fabric classes requires it.
* Generally you do not cache objects in groups because the group outside is cached.
+ * Read as: cache if is needed, or if the feature is enabled but we are not already caching.
* @return {Boolean}
*/
shouldCache: function() {
- this.ownCaching = this.objectCaching &&
- (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
+ this.ownCaching = this.needsItsOwnCache() || (
+ this.objectCaching &&
+ (!this.group || !this.group.isOnACache())
+ );
return this.ownCaching;
},
@@ -13794,10 +13905,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return;
}
- var shadow = this.shadow, canvas = this.canvas,
+ var shadow = this.shadow, canvas = this.canvas, scaling,
multX = (canvas && canvas.viewportTransform[0]) || 1,
- multY = (canvas && canvas.viewportTransform[3]) || 1,
- scaling = this.getObjectScaling();
+ multY = (canvas && canvas.viewportTransform[3]) || 1;
+ if (shadow.nonScaling) {
+ scaling = { scaleX: 1, scaleY: 1 };
+ }
+ else {
+ scaling = this.getObjectScaling();
+ }
if (canvas && canvas._isRetinaScaling()) {
multX *= fabric.devicePixelRatio;
multY *= fabric.devicePixelRatio;
@@ -13860,6 +13976,17 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* @private
+ * function that actually render something on the context.
+ * empty here to allow Obects to work on tests to benchmark fabric functionalites
+ * not related to rendering
+ * @param {CanvasRenderingContext2D} ctx Context to render on
+ */
+ _render: function(/* ctx */) {
+
+ },
+
+ /**
+ * @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderFill: function(ctx) {
@@ -14029,9 +14156,14 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (shadow) {
shadowBlur = shadow.blur;
- scaling = this.getObjectScaling();
- shadowOffset.x = 2 * Math.round((abs(shadow.offsetX) + shadowBlur) * abs(scaling.scaleX));
- shadowOffset.y = 2 * Math.round((abs(shadow.offsetY) + shadowBlur) * abs(scaling.scaleY));
+ if (shadow.nonScaling) {
+ scaling = { scaleX: 1, scaleY: 1 };
+ }
+ else {
+ scaling = this.getObjectScaling();
+ }
+ shadowOffset.x = 2 * Math.round(abs(shadow.offsetX) + shadowBlur) * (abs(scaling.scaleX));
+ shadowOffset.y = 2 * Math.round(abs(shadow.offsetY) + shadowBlur) * (abs(scaling.scaleY));
}
el.width = boundingRect.width + shadowOffset.x;
el.height = boundingRect.height + shadowOffset.y;
@@ -14697,7 +14829,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* each property is an object with x, y, instance of Fabric.Point.
* The coordinates depends from this properties: width, height, scaleX, scaleY
* skewX, skewY, angle, strokeWidth, top, left.
- * Those coordinates are usefull to understand where an object is. They get updated
+ * Those coordinates are useful to understand where an object is. They get updated
* with oCoords but they do not need to be updated when zoom or panning change.
* The coordinates get updated with @method setCoords.
* You can calculate them without updating with @method calcCoords(true);
@@ -14851,7 +14983,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Fabric.Point} pointTL Top Left point
* @param {Fabric.Point} pointBR Top Right point
* @param {Boolean} calculate use coordinates of current position instead of .oCoords
- * @return {Boolean} true if the objects containe the point
+ * @return {Boolean} true if the object contains the point
*/
_containsCenterOfCanvas: function(pointTL, pointBR, calculate) {
// worst case scenario the object is so big that contains the screen
@@ -14956,7 +15088,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
- * the box is intented as aligned to axis of canvas.
+ * the box is intended as aligned to axis of canvas.
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of .oCoords / .aCoords
* @return {Object} Object with left, top, width, height properties
@@ -14967,7 +15099,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
- * Returns width of an object bounding box counting transformations
+ * Returns width of an object's bounding box counting transformations
* before 2.0 it was named getWidth();
* @return {Number} width value
*/
@@ -15044,7 +15176,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
- * Calculate and returns the .coords of an object.
+ * Calculates and returns the .coords of an object.
* @return {Object} Object with tl, tr, br, bl ....
* @chainable
*/
@@ -15119,7 +15251,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* Sets corner position coordinates based on current angle, width and height.
* See {@link https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords|When-to-call-setCoords}
* @param {Boolean} [ignoreZoom] set oCoords with or without the viewport transform.
- * @param {Boolean} [skipAbsolute] skip calculation of aCoords, usefull in setViewportTransform
+ * @param {Boolean} [skipAbsolute] skip calculation of aCoords, useful in setViewportTransform
* @return {fabric.Object} thisArg
* @chainable
*/
@@ -15167,10 +15299,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
- * calculate trasform Matrix that represent current transformation from
- * object properties.
- * @param {Boolean} [skipGroup] return transformMatrix for object and not go upward with parents
- * @return {Array} matrix Transform Matrix for the object
+ * calculate transform matrix that represents the current transformations from the
+ * object's properties.
+ * @param {Boolean} [skipGroup] return transform matrix for object not counting parent transformations
+ * @return {Array} transform matrix for the object
*/
calcTransformMatrix: function(skipGroup) {
if (skipGroup) {
@@ -15238,7 +15370,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/*
- * Calculate object bounding boxdimensions from its properties scale, skew.
+ * Calculate object bounding box dimensions from its properties scale, skew.
+ * @param {Number} skewX, a value to override current skewX
+ * @param {Number} skewY, a value to override current skewY
* @private
* @return {Object} .x width dimension
* @return {Object} .y height dimension
@@ -15262,7 +15396,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
dimY = dimensions.y;
}
if (noSkew) {
- return this._finalizeDiemensions(dimX * this.scaleX, dimY * this.scaleY);
+ return this._finalizeDimensions(dimX * this.scaleX, dimY * this.scaleY);
}
else {
dimX /= 2;
@@ -15291,25 +15425,25 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
points[i] = fabric.util.transformPoint(points[i], transformMatrix);
}
bbox = fabric.util.makeBoundingBoxFromPoints(points);
- return this._finalizeDiemensions(bbox.width, bbox.height);
+ return this._finalizeDimensions(bbox.width, bbox.height);
},
/*
- * Calculate object bounding boxdimensions from its properties scale, skew.
+ * Calculate object bounding box dimensions from its properties scale, skew.
* @param Number width width of the bbox
* @param Number height height of the bbox
* @private
* @return {Object} .x finalized width dimension
* @return {Object} .y finalized height dimension
*/
- _finalizeDiemensions: function(width, height) {
+ _finalizeDimensions: function(width, height) {
return this.strokeUniform ?
{ x: width + this.strokeWidth, y: height + this.strokeWidth }
:
{ x: width, y: height };
},
/*
- * Calculate object dimensions for controls. include padding and canvas zoom
+ * Calculate object dimensions for controls, including padding and canvas zoom.
* private
*/
_calculateCurrentDimensions: function() {
@@ -15542,10 +15676,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
getSvgTransform: function(full, additionalTransform) {
var transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(),
- svgTransform = transform.map(function(value) {
- return toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
- }).join(' ');
- return 'transform="matrix(' + svgTransform + ')' +
+ svgTransform = 'transform="' + fabric.util.matrixToSVG(transform);
+ return svgTransform +
(additionalTransform || '') + this.getSvgTransformMatrix() + '" ';
},
@@ -15554,7 +15686,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransformMatrix: function() {
- return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
+ return this.transformMatrix ? ' ' + fabric.util.matrixToSVG(this.transformMatrix) : '';
},
_setSVGBg: function(textBgRects) {
@@ -15581,7 +15713,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- return this._createBaseSVGMarkup(this._toSVG(), { reviver: reviver });
+ return this._createBaseSVGMarkup(this._toSVG(reviver), { reviver: reviver });
},
/**
@@ -15590,7 +15722,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} svg representation of an instance
*/
toClipPathSVG: function(reviver) {
- return '\t' + this._createBaseClipPathSVGMarkup(this._toSVG(), { reviver: reviver });
+ return '\t' + this._createBaseClipPathSVGMarkup(this._toSVG(reviver), { reviver: reviver });
},
/**
@@ -15615,13 +15747,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_createBaseSVGMarkup: function(objectMarkup, options) {
options = options || {};
- var noStyle = options.noStyle, withShadow = options.withShadow,
+ var noStyle = options.noStyle,
reviver = options.reviver,
styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ',
- shadowInfo = withShadow ? 'style="' + this.getSvgFilter() + '" ' : '',
+ shadowInfo = options.withShadow ? 'style="' + this.getSvgFilter() + '" ' : '',
clipPath = this.clipPath,
vectorEffect = this.strokeUniform ? 'vector-effect="non-scaling-stroke" ' : '',
- absoluteClipPath = this.clipPath && this.clipPath.absolutePositioned,
+ absoluteClipPath = clipPath && clipPath.absolutePositioned,
+ stroke = this.stroke, fill = this.fill, shadow = this.shadow,
commonPieces, markup = [], clipPathMarkup,
// insert commons in the markup, style and svgCommons
index = objectMarkup.indexOf('COMMON_PARTS'),
@@ -15629,7 +15762,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (clipPath) {
clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
clipPathMarkup = '<clipPath id="' + clipPath.clipPathId + '" >\n' +
- this.clipPath.toClipPathSVG(reviver) +
+ clipPath.toClipPathSVG(reviver) +
'</clipPath>\n';
}
if (absoluteClipPath) {
@@ -15650,14 +15783,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
additionalTransform ? 'transform="' + additionalTransform + '" ' : '',
].join('');
objectMarkup[index] = commonPieces;
- if (this.fill && this.fill.toLive) {
- markup.push(this.fill.toSVG(this, false));
+ if (fill && fill.toLive) {
+ markup.push(fill.toSVG(this));
}
- if (this.stroke && this.stroke.toLive) {
- markup.push(this.stroke.toSVG(this, false));
+ if (stroke && stroke.toLive) {
+ markup.push(stroke.toSVG(this));
}
- if (this.shadow) {
- markup.push(this.shadow.toSVG(this));
+ if (shadow) {
+ markup.push(shadow.toSVG(this));
}
if (clipPath) {
markup.push(clipPathMarkup);
@@ -17459,6 +17592,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
+ parsedAttributes.height = parsedAttributes.height || 0;
+ parsedAttributes.width = parsedAttributes.width || 0;
var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
rect.visible = rect.visible && rect.width > 0 && rect.height > 0;
callback(rect);
@@ -17541,15 +17676,28 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
options = options || {};
this.points = points || [];
this.callSuper('initialize', options);
- var calcDim = this._calcDimensions();
+ this._setPositionDimensions(options);
+ },
+
+ _setPositionDimensions: function(options) {
+ var calcDim = this._calcDimensions(options), correctLeftTop;
+ this.width = calcDim.width;
+ this.height = calcDim.height;
+ if (!options.fromSVG) {
+ correctLeftTop = this.translateToGivenOrigin(
+ { x: calcDim.left - this.strokeWidth / 2, y: calcDim.top - this.strokeWidth / 2 },
+ 'left',
+ 'top',
+ this.originX,
+ this.originY
+ );
+ }
if (typeof options.left === 'undefined') {
- this.left = calcDim.left;
+ this.left = options.fromSVG ? calcDim.left : correctLeftTop.x;
}
if (typeof options.top === 'undefined') {
- this.top = calcDim.top;
+ this.top = options.fromSVG ? calcDim.top : correctLeftTop.y;
}
- this.width = calcDim.width;
- this.height = calcDim.height;
this.pathOffset = {
x: calcDim.left + this.width / 2,
y: calcDim.top + this.height / 2
@@ -17695,17 +17843,22 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {Function} callback callback function invoked after parsing
* @param {Object} [options] Options object
*/
- fabric.Polyline.fromElement = function(element, callback, options) {
- if (!element) {
- return callback(null);
- }
- options || (options = { });
-
- var points = fabric.parsePointsAttribute(element.getAttribute('points')),
- parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
+ fabric.Polyline.fromElementGenerator = function(_class) {
+ return function(element, callback, options) {
+ if (!element) {
+ return callback(null);
+ }
+ options || (options = { });
- callback(new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options)));
+ var points = fabric.parsePointsAttribute(element.getAttribute('points')),
+ parsedAttributes = fabric.parseAttributes(element, fabric[_class].ATTRIBUTE_NAMES);
+ parsedAttributes.fromSVG = true;
+ callback(new fabric[_class](points, extend(parsedAttributes, options)));
+ };
};
+
+ fabric.Polyline.fromElement = fabric.Polyline.fromElementGenerator('Polyline');
+
/* _FROM_SVG_END_ */
/**
@@ -17726,8 +17879,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'use strict';
- var fabric = global.fabric || (global.fabric = { }),
- extend = fabric.util.object.extend;
+ var fabric = global.fabric || (global.fabric = { });
if (fabric.Polygon) {
fabric.warn('fabric.Polygon is already defined');
@@ -17788,18 +17940,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {Function} callback callback function invoked after parsing
* @param {Object} [options] Options object
*/
- fabric.Polygon.fromElement = function(element, callback, options) {
- if (!element) {
- return callback(null);
- }
-
- options || (options = { });
-
- var points = fabric.parsePointsAttribute(element.getAttribute('points')),
- parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
-
- callback(new fabric.Polygon(points, extend(parsedAttributes, options)));
- };
+ fabric.Polygon.fromElement = fabric.Polyline.fromElementGenerator('Polygon');
/* _FROM_SVG_END_ */
/**
@@ -17904,31 +18045,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this.path = this._parsePath();
}
- this._setPositionDimensions(options);
- },
-
- /**
- * @private
- * @param {Object} options Options object
- */
- _setPositionDimensions: function(options) {
- var calcDim = this._parseDimensions();
-
- this.width = calcDim.width;
- this.height = calcDim.height;
-
- if (typeof options.left === 'undefined') {
- this.left = calcDim.left;
- }
-
- if (typeof options.top === 'undefined') {
- this.top = calcDim.top;
- }
-
- this.pathOffset = this.pathOffset || {
- x: calcDim.left + this.width / 2,
- y: calcDim.top + this.height / 2
- };
+ fabric.Polyline.prototype._setPositionDimensions.call(this, options);
},
/**
@@ -18261,12 +18378,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
- var o = extend(this.callSuper('toObject', propertiesToInclude), {
+ return extend(this.callSuper('toObject', propertiesToInclude), {
path: this.path.map(function(item) { return item.slice(); }),
- top: this.top,
- left: this.left,
});
- return o;
},
/**
@@ -18389,7 +18503,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* @private
*/
- _parseDimensions: function() {
+ _calcDimensions: function() {
var aX = [],
aY = [],
@@ -18698,16 +18812,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
maxX = max(aX) || 0,
maxY = max(aY) || 0,
deltaX = maxX - minX,
- deltaY = maxY - minY,
+ deltaY = maxY - minY;
- o = {
- left: minX,
- top: minY,
- width: deltaX,
- height: deltaY
- };
-
- return o;
+ return {
+ left: minX,
+ top: minY,
+ width: deltaX,
+ height: deltaY
+ };
}
});
@@ -18752,6 +18864,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
fabric.Path.fromElement = function(element, callback, options) {
var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
+ parsedAttributes.fromSVG = true;
callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
};
/* _FROM_SVG_END_ */
@@ -19041,15 +19154,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* Decide if the object should cache or not. Create its own cache level
- * objectCaching is a global flag, wins over everything
* needsItsOwnCache should be used when the object drawing method requires
* a cache step. None of the fabric classes requires it.
- * Generally you do not cache objects in groups because the group outside is cached.
+ * Generally you do not cache objects in groups because the group is already cached.
* @return {Boolean}
*/
shouldCache: function() {
- var ownCache = this.objectCaching && (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
- this.ownCaching = ownCache;
+ var ownCache = fabric.Object.prototype.shouldCache.call(this);
if (ownCache) {
for (var i = 0, len = this._objects.length; i < len; i++) {
if (this._objects[i].willDrawShadow()) {
@@ -19281,16 +19392,29 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {Function} [reviver] Method for further parsing of svg representation.
* @return {String} svg representation of an instance
*/
- toSVG: function(reviver) {
- var svgString = [];
+ _toSVG: function(reviver) {
+ var svgString = ['<g ', 'COMMON_PARTS', ' >\n'];
for (var i = 0, len = this._objects.length; i < len; i++) {
- svgString.push('\t', this._objects[i].toSVG(reviver));
+ svgString.push('\t\t', this._objects[i].toSVG(reviver));
}
+ svgString.push('</g>\n');
+ return svgString;
+ },
- return this._createBaseSVGMarkup(
- svgString,
- { reviver: reviver, noStyle: true, withShadow: true });
+ /**
+ * Returns styles-string for svg-export, specific version for group
+ * @return {String}
+ */
+ getSvgStyles: function() {
+ var opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ?
+ 'opacity: ' + this.opacity + ';' : '',
+ visibility = this.visible ? '' : ' visibility: hidden;';
+ return [
+ opacity,
+ this.getSvgFilter(),
+ visibility
+ ].join('');
},
/**
@@ -19997,7 +20121,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* Decide if the object should cache or not. Create its own cache level
- * objectCaching is a global flag, wins over everything
* needsItsOwnCache should be used when the object drawing method requires
* a cache step. None of the fabric classes requires it.
* Generally you do not cache objects in groups because the group outside is cached.
@@ -20008,8 +20131,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Boolean}
*/
shouldCache: function() {
- this.ownCaching = this.objectCaching && this.needsItsOwnCache();
- return this.ownCaching;
+ return this.needsItsOwnCache();
},
_renderFill: function(ctx) {
@@ -20421,8 +20543,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* putImageData is faster than drawImage for that specific operation.
*/
chooseFastestCopyGLTo2DMethod: function(width, height) {
- var canMeasurePerf = typeof window.performance !== 'undefined';
- var canUseImageData;
+ var canMeasurePerf = typeof window.performance !== 'undefined', canUseImageData;
try {
new ImageData(1, 1);
canUseImageData = true;
@@ -20442,6 +20563,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var targetCanvas = fabric.util.createCanvasElement();
// eslint-disable-next-line no-undef
var imageBuffer = new ArrayBuffer(width * height * 4);
+ if (fabric.forceGLPutImageData) {
+ this.imageBuffer = imageBuffer;
+ this.copyGLTo2D = copyGLTo2DPutImageData;
+ return;
+ }
var testContext = {
imageBuffer: imageBuffer,
destinationWidth: width,
@@ -24752,6 +24878,16 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
},
/**
+ * Detect if a line has a linebreak and so we need to account for it when moving
+ * and counting style.
+ * It return always for text and Itext.
+ * @return Number
+ */
+ missingNewlineOffset: function() {
+ return 1;
+ },
+
+ /**
* Returns string representation of an instance
* @return {String} String representation of text object
*/
@@ -24958,7 +25094,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* possibly overridden to accommodate different measure logic or
* to hook some external lib for character measurement
* @private
- * @param {String} char to be measured
+ * @param {String} _char, char to be measured
* @param {Object} charStyle style of char to be measured
* @param {String} [previousChar] previous char
* @param {Object} [prevCharStyle] style of previous char
@@ -25004,12 +25140,12 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
/**
* Computes height of character at given position
- * @param {Number} line the line number
- * @param {Number} char the character number
+ * @param {Number} line the line index number
+ * @param {Number} _char the character index number
* @return {Number} fontSize of the character
*/
- getHeightOfChar: function(line, char) {
- return this.getValueOfPropertyAt(line, char, 'fontSize');
+ getHeightOfChar: function(line, _char) {
+ return this.getValueOfPropertyAt(line, _char, 'fontSize');
},
/**
@@ -25541,11 +25677,12 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
var style = styleObject || this, family = this.fontFamily,
fontIsGeneric = fabric.Text.genericFonts.indexOf(family.toLowerCase()) > -1;
var fontFamily = family === undefined ||
- family.indexOf('\'') > -1 ||
+ family.indexOf('\'') > -1 || family.indexOf(',') > -1 ||
family.indexOf('"') > -1 || fontIsGeneric
? style.fontFamily : '"' + style.fontFamily + '"';
return [
// node-canvas needs "weight style", while browsers need "style weight"
+ // verify if this can be fixed in JSDOM
(fabric.isLikelyNode ? style.fontWeight : style.fontStyle),
(fabric.isLikelyNode ? style.fontStyle : style.fontWeight),
forMeasuring ? this.CACHE_FONT_SIZE + 'px' : style.fontSize + 'px',
@@ -25799,7 +25936,9 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
/**
* Returns true if object has a style property or has it ina specified line
- * @param {Number} lineIndex
+ * This function is used to detect if a text will use a particular property or not.
+ * @param {String} property to check for
+ * @param {Number} lineIndex to check the style on
* @return {Boolean}
*/
styleHas: function(property, lineIndex) {
@@ -25809,7 +25948,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
return false;
}
- var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
+ var obj = typeof lineIndex === 'undefined' ? this.styles : { 0: this.styles[lineIndex] };
// eslint-disable-next-line
for (var p1 in obj) {
// eslint-disable-next-line
@@ -25920,7 +26059,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
var loc = this.get2DCursorLocation(index);
if (!this._getLineStyle(loc.lineIndex)) {
- this._setLineStyle(loc.lineIndex, {});
+ this._setLineStyle(loc.lineIndex);
}
if (!this._getStyleDeclaration(loc.lineIndex, loc.charIndex)) {
@@ -25939,8 +26078,8 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
if (typeof selectionStart === 'undefined') {
selectionStart = this.selectionStart;
}
- var lines = skipWrapping ? this._unwrappedTextLines : this._textLines;
- var len = lines.length;
+ var lines = skipWrapping ? this._unwrappedTextLines : this._textLines,
+ len = lines.length;
for (var i = 0; i < len; i++) {
if (selectionStart <= lines[i].length) {
return {
@@ -25948,7 +26087,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
charIndex: selectionStart
};
}
- selectionStart -= lines[i].length + 1;
+ selectionStart -= lines[i].length + this.missingNewlineOffset(i);
}
return {
lineIndex: i - 1,
@@ -26068,19 +26207,20 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
/**
* @param {Number} lineIndex
+ * @return {Boolean} if the line exists or not
* @private
*/
_getLineStyle: function(lineIndex) {
- return this.styles[lineIndex];
+ return !!this.styles[lineIndex];
},
/**
+ * Set the line style to an empty object so that is initialized
* @param {Number} lineIndex
- * @param {Object} style
* @private
*/
- _setLineStyle: function(lineIndex, style) {
- this.styles[lineIndex] = style;
+ _setLineStyle: function(lineIndex) {
+ this.styles[lineIndex] = {};
},
/**
@@ -26357,36 +26497,29 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* Prepare and clean the contextTop
*/
clearContextTop: function(skipRestore) {
- if (!this.isEditing) {
+ if (!this.isEditing || !this.canvas || !this.canvas.contextTop) {
return;
}
- if (this.canvas && this.canvas.contextTop) {
- var ctx = this.canvas.contextTop, v = this.canvas.viewportTransform;
- ctx.save();
- ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
- this.transform(ctx);
- this.transformMatrix && ctx.transform.apply(ctx, this.transformMatrix);
- this._clearTextArea(ctx);
- skipRestore || ctx.restore();
- }
+ var ctx = this.canvas.contextTop, v = this.canvas.viewportTransform;
+ ctx.save();
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+ this.transform(ctx);
+ this.transformMatrix && ctx.transform.apply(ctx, this.transformMatrix);
+ this._clearTextArea(ctx);
+ skipRestore || ctx.restore();
},
/**
* Renders cursor or selection (depending on what exists)
+ * it does on the contextTop. If contextTop is not available, do nothing.
*/
renderCursorOrSelection: function() {
- if (!this.isEditing || !this.canvas) {
+ if (!this.isEditing || !this.canvas || !this.canvas.contextTop) {
return;
}
- var boundaries = this._getCursorBoundaries(), ctx;
- if (this.canvas && this.canvas.contextTop) {
- ctx = this.canvas.contextTop;
- this.clearContextTop(true);
- }
- else {
- ctx = this.canvas.contextContainer;
- ctx.save();
- }
+ var boundaries = this._getCursorBoundaries(),
+ ctx = this.canvas.contextTop;
+ this.clearContextTop(true);
if (this.selectionStart === this.selectionEnd) {
this.renderCursor(boundaries, ctx);
}
@@ -26803,7 +26936,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
/**
* Find new selection index representing start of current word according to current selection index
- * @param {Number} startFrom Surrent selection index
+ * @param {Number} startFrom Current selection index
* @return {Number} New selection index
*/
findWordBoundaryLeft: function(startFrom) {
@@ -26839,7 +26972,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
index++;
}
}
- while (/\S/.test(this._text[index]) && index < this.text.length) {
+ while (/\S/.test(this._text[index]) && index < this._text.length) {
offset++;
index++;
}
@@ -26871,7 +27004,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
findLineBoundaryRight: function(startFrom) {
var offset = 0, index = startFrom;
- while (!/\n/.test(this._text[index]) && index < this.text.length) {
+ while (!/\n/.test(this._text[index]) && index < this._text.length) {
offset++;
index++;
}
@@ -26886,13 +27019,13 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* @return {Number} Index of the beginning or end of a word
*/
searchWordBoundary: function(selectionStart, direction) {
- var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
- _char = this.text.charAt(index),
+ var index = this._reSpace.test(this._text[selectionStart]) ? selectionStart - 1 : selectionStart,
+ _char = this._text[index],
reNonWord = /[ \n\.,;!\?\-]/;
- while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
+ while (!reNonWord.test(_char) && index > 0 && index < this._text.length) {
index += direction;
- _char = this.text.charAt(index);
+ _char = this._text[index];
}
if (reNonWord.test(_char) && _char !== '\n') {
index += direction === 1 ? 0 : 1;
@@ -27034,7 +27167,6 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
}
this.borderColor = this.editingBorderColor;
-
this.hasControls = this.selectable = false;
this.lockMovementX = this.lockMovementY = true;
},
@@ -27124,9 +27256,9 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
if (!this.canvas) {
return { x: 1, y: 1 };
}
- var desiredPostion = this.inCompositionMode ? this.compositionStart : this.selectionStart,
- boundaries = this._getCursorBoundaries(desiredPostion),
- cursorLocation = this.get2DCursorLocation(desiredPostion),
+ var desiredPosition = this.inCompositionMode ? this.compositionStart : this.selectionStart,
+ boundaries = this._getCursorBoundaries(desiredPosition),
+ cursorLocation = this.get2DCursorLocation(desiredPosition),
lineIndex = cursorLocation.lineIndex,
charIndex = cursorLocation.charIndex,
charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize') * this.lineHeight,
@@ -27178,6 +27310,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
lockMovementX: this.lockMovementX,
lockMovementY: this.lockMovementY,
hoverCursor: this.hoverCursor,
+ selectable: this.selectable,
defaultCursor: this.canvas && this.canvas.defaultCursor,
moveCursor: this.canvas && this.canvas.moveCursor
};
@@ -27194,6 +27327,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
this.hoverCursor = this._savedProps.hoverCursor;
this.hasControls = this._savedProps.hasControls;
this.borderColor = this._savedProps.borderColor;
+ this.selectable = this._savedProps.selectable;
this.lockMovementX = this._savedProps.lockMovementX;
this.lockMovementY = this._savedProps.lockMovementY;
@@ -27212,7 +27346,6 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
var isTextChanged = (this._textBeforeEdit !== this.text);
this.selected = false;
this.isEditing = false;
- this.selectable = true;
this.selectionEnd = this.selectionStart;
@@ -27391,7 +27524,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* @param {Number} lineIndex Index of a line
* @param {Number} charIndex Index of a char
* @param {Number} quantity number Style object to insert, if given
- * @param {Array} copiedStyle array of style objecs
+ * @param {Array} copiedStyle array of style objects
*/
insertCharStyleObject: function(lineIndex, charIndex, quantity, copiedStyle) {
if (!this.styles) {
@@ -27443,23 +27576,23 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
*/
insertNewStyleBlock: function(insertedText, start, copiedStyle) {
var cursorLoc = this.get2DCursorLocation(start, true),
- addedLines = [0], linesLenght = 0;
+ addedLines = [0], linesLength = 0;
for (var i = 0; i < insertedText.length; i++) {
if (insertedText[i] === '\n') {
- linesLenght++;
- addedLines[linesLenght] = 0;
+ linesLength++;
+ addedLines[linesLength] = 0;
}
else {
- addedLines[linesLenght]++;
+ addedLines[linesLength]++;
}
}
if (addedLines[0] > 0) {
this.insertCharStyleObject(cursorLoc.lineIndex, cursorLoc.charIndex, addedLines[0], copiedStyle);
copiedStyle = copiedStyle && copiedStyle.slice(addedLines[0] + 1);
}
- linesLenght && this.insertNewlineStyleObject(
- cursorLoc.lineIndex, cursorLoc.charIndex + addedLines[0], linesLenght);
- for (var i = 1; i < linesLenght; i++) {
+ linesLength && this.insertNewlineStyleObject(
+ cursorLoc.lineIndex, cursorLoc.charIndex + addedLines[0], linesLength);
+ for (var i = 1; i < linesLength; i++) {
if (addedLines[i] > 0) {
this.insertCharStyleObject(cursorLoc.lineIndex + i, 0, addedLines[i], copiedStyle);
}
@@ -27475,7 +27608,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
},
/**
- * Set the selectionStart and selectionEnd according to the ne postion of cursor
+ * Set the selectionStart and selectionEnd according to the new position of cursor
* mimic the key - mouse navigation when shift is pressed.
*/
setSelectionStartEndWithShift: function(start, end, newSelection) {
@@ -27736,7 +27869,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
height += this.getHeightOfLine(i) * this.scaleY;
lineIndex = i;
if (i > 0) {
- charIndex += this._textLines[i - 1].length + 1;
+ charIndex += this._textLines[i - 1].length + this.missingNewlineOffset(i - 1);
}
}
else {
@@ -27977,7 +28110,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
}
if (insertedText.length) {
- if (fromPaste && insertedText.join('') === fabric.copiedText) {
+ if (fromPaste && insertedText.join('') === fabric.copiedText && !fabric.disableStyleCopyPaste) {
this.insertNewStyleBlock(insertedText, this.selectionStart, fabric.copiedTextStyle);
}
else {
@@ -28025,7 +28158,12 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
fabric.copiedText = this.getSelectedText();
- fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd, true);
+ if (!fabric.disableStyleCopyPaste) {
+ fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd, true);
+ }
+ else {
+ fabric.copiedTextStyle = null;
+ }
this._copyDone = true;
},
@@ -28082,7 +28220,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
indexOnOtherLine = this._getIndexOnLine(lineIndex + 1, widthBeforeCursor),
textAfterCursor = this._textLines[lineIndex].slice(charIndex);
- return textAfterCursor.length + indexOnOtherLine + 2;
+ return textAfterCursor.length + indexOnOtherLine + 1 + this.missingNewlineOffset(lineIndex);
},
/**
@@ -28117,9 +28255,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var charIndex = cursorLocation.charIndex,
widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
indexOnOtherLine = this._getIndexOnLine(lineIndex - 1, widthBeforeCursor),
- textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex);
+ textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex),
+ missingNewlineOffset = this.missingNewlineOffset(lineIndex - 1);
// return a negative offset
- return -this._textLines[lineIndex - 1].length + indexOnOtherLine - textBeforeCursor.length;
+ return -this._textLines[lineIndex - 1].length
+ + indexOnOtherLine - textBeforeCursor.length + (1 - missingNewlineOffset);
},
/**
@@ -28446,12 +28586,22 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Function} [reviver] Method for further parsing of svg representation.
* @return {String} svg representation of an instance
*/
- toSVG: function(reviver) {
+ _toSVG: function() {
var offsets = this._getSVGLeftTopOffsets(),
- textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft),
- internalMarkup = this._wrapSVGTextAndBg(textAndBg);
+ textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft);
+ return this._wrapSVGTextAndBg(textAndBg);
+ },
+
+ /**
+ * Returns svg representation of an instance
+ * @param {Function} [reviver] Method for further parsing of svg representation.
+ * @return {String} svg representation of an instance
+ */
+ toSVG: function(reviver) {
return this._createBaseSVGMarkup(
- internalMarkup, { reviver: reviver, noStyle: true, withShadow: true });
+ this._toSVG(),
+ { reviver: reviver, noStyle: true, withShadow: true }
+ );
},
/**
@@ -28796,7 +28946,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
charCount++;
realLineCount++;
}
- else if (!this.graphemeSplit && this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) && i > 0) {
+ else if (!this.splitByGrapheme && this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) && i > 0) {
// this case deals with space's that are removed from end of lines when wrapping
realLineCharCount++;
charCount++;
@@ -28812,7 +28962,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
},
/**
- * Returns true if object has a style property or has it ina specified line
+ * Returns true if object has a style property or has it on a specified line
* @param {Number} lineIndex
* @return {Boolean}
*/
@@ -28898,39 +29048,31 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var map = this._styleMap[lineIndex];
lineIndex = map.line;
charIndex = map.offset + charIndex;
-
delete this.styles[lineIndex][charIndex];
},
/**
- * probably broken need a fix
+ * probably broken need a fix
+ * Returns the real style line that correspond to the wrapped lineIndex line
+ * Used just to verify if the line does exist or not.
* @param {Number} lineIndex
+ * @returns {Boolean} if the line exists or not
* @private
*/
_getLineStyle: function(lineIndex) {
var map = this._styleMap[lineIndex];
- return this.styles[map.line];
+ return !!this.styles[map.line];
},
/**
- * probably broken need a fix
+ * Set the line style to an empty object so that is initialized
* @param {Number} lineIndex
* @param {Object} style
* @private
*/
- _setLineStyle: function(lineIndex, style) {
- var map = this._styleMap[lineIndex];
- this.styles[map.line] = style;
- },
-
- /**
- * probably broken need a fix
- * @param {Number} lineIndex
- * @private
- */
- _deleteLineStyle: function(lineIndex) {
+ _setLineStyle: function(lineIndex) {
var map = this._styleMap[lineIndex];
- delete this.styles[map.line];
+ this.styles[map.line] = {};
},
/**
@@ -28998,11 +29140,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
lineJustStarted = true,
additionalSpace = splitByGrapheme ? 0 : this._getWidthOfCharSpacing(),
reservedSpace = reservedSpace || 0;
-
+ // fix a difference between split and graphemeSplit
+ if (words.length === 0) {
+ words.push([]);
+ }
desiredWidth -= reservedSpace;
for (var i = 0; i < words.length; i++) {
- // i would avoid resplitting the graphemes
- word = fabric.util.string.graphemeSplit(words[i]);
+ // if using splitByGrapheme words are already in graphemes.
+ word = splitByGrapheme ? words[i] : fabric.util.string.graphemeSplit(words[i]);
wordWidth = this._measureWord(word, lineIndex, offset);
offset += word.length;
@@ -29060,6 +29205,18 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
},
/**
+ * Detect if a line has a linebreak and so we need to account for it when moving
+ * and counting style.
+ * @return Number
+ */
+ missingNewlineOffset: function(lineIndex) {
+ if (this.splitByGrapheme) {
+ return this.isEndOfWrapping(lineIndex) ? 1 : 0;
+ }
+ return 1;
+ },
+
+ /**
* Gets lines of text to render in the Textbox. This function calculates
* text wrapping on the fly every time it is called.
* @param {String} text text to split
@@ -29070,7 +29227,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var newText = fabric.Text.prototype._splitTextIntoLines.call(this, text),
graphemeLines = this._wrapText(newText.lines, this.width),
lines = new Array(graphemeLines.length);
-
for (var i = 0; i < graphemeLines.length; i++) {
lines[i] = graphemeLines[i].join('');
}
@@ -29083,6 +29239,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return Math.max(this.minWidth, this.dynamicMinWidth);
},
+ _removeExtraneousStyles: function() {
+ var linesToKeep = {};
+ for (var prop in this._styleMap) {
+ if (this._textLines[prop]) {
+ linesToKeep[this._styleMap[prop].line] = 1;
+ }
+ }
+ for (var prop in this.styles) {
+ if (!linesToKeep[prop]) {
+ delete this.styles[prop];
+ }
+ }
+ },
+
/**
* Returns object representation of an instance
* @method toObject
@@ -29105,50 +29275,3 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return fabric.Object._fromObject('Textbox', object, callback, 'text');
};
})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
- /**
- * Override _setObjectScale and add Textbox specific resizing behavior. Resizing
- * a Textbox doesn't scale text, it only changes width and makes text wrap automatically.
- */
- var setObjectScaleOverridden = fabric.Canvas.prototype._setObjectScale;
-
- fabric.Canvas.prototype._setObjectScale = function(localMouse, transform,
- lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
-
- var t = transform.target, scaled,
- scaleX = localMouse.x * t.scaleX / _dim.x,
- scaleY = localMouse.y * t.scaleY / _dim.y;
- if (by === 'x' && t instanceof fabric.Textbox) {
- var tw = t._getTransformedDimensions().x;
- var w = t.width * (localMouse.x / tw);
- transform.newScaleX = scaleX;
- transform.newScaleY = scaleY;
- if (w >= t.getMinWidth()) {
- scaled = w !== t.width;
- t.set('width', w);
- return scaled;
- }
- }
- else {
- return setObjectScaleOverridden.call(fabric.Canvas.prototype, localMouse, transform,
- lockScalingX, lockScalingY, by, lockScalingFlip, _dim);
- }
- };
-
- fabric.util.object.extend(fabric.Textbox.prototype, /** @lends fabric.IText.prototype */ {
- /**
- * @private
- */
- _removeExtraneousStyles: function() {
- for (var prop in this._styleMap) {
- if (!this._textLines[prop]) {
- delete this.styles[this._styleMap[prop].line];
- }
- }
- },
-
- });
-})();
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-tool-base.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-tool-base.js
index 256155a..16c0c0e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-tool-base.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-tool-base.js
@@ -2,7 +2,7 @@
var Base = function() {
const base = {};
base.objectCreated = function(o, canvas) {
- o.uid = UUID.v4();
+ o.uid = uuidv4();
o.slide = canvas.slide;
canvas.trigger("wb:object:created", o);
return o.uid;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
index 8fa4a63..c35e618 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
@@ -563,7 +563,7 @@ var Wb = function() {
switch(o.type) {
case 'textbox':
case 'i-text':
- o.uid = UUID.v4();
+ o.uid = uuidv4();
o.slide = this.slide;
objCreatedHandler(o);
break;
@@ -618,7 +618,7 @@ var Wb = function() {
}
}
function pathCreatedHandler(o) {
- o.path.uid = UUID.v4();
+ o.path.uid = uuidv4();
o.path.slide = this.slide;
objCreatedHandler(o.path);
};