Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

element-resize-detector

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

element-resize-detector - npm Package Compare versions

Comparing version 0.1.4 to 0.2.2

.travis.yml

480

build/element-resize-detector.js

@@ -1,4 +0,41 @@

!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.elementResizeDetectorMaker=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.elementResizeDetectorMaker = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
var detector = module.exports = {};
detector.isIE = function(version) {
function isAnyIeVersion() {
var agent = navigator.userAgent.toLowerCase();
return agent.indexOf("msie") !== -1 || agent.indexOf("trident") !== -1;
}
if(!isAnyIeVersion()) {
return false;
}
if(!version) {
return true;
}
//Shamelessly stolen from https://gist.github.com/padolsey/527683
var ieVersion = (function(){
var undef,
v = 3,
div = document.createElement("div"),
all = div.getElementsByTagName("i");
do {
div.innerHTML = "<!--[if gt IE " + (++v) + "]><i></i><![endif]-->";
}
while (all[0]);
return v > 4 ? v : undef;
}());
return version === ieVersion;
};
},{}],2:[function(require,module,exports){
"use strict";
var utils = module.exports = {};

@@ -22,3 +59,3 @@

},{}],2:[function(require,module,exports){
},{}],3:[function(require,module,exports){
//Heavily inspired by http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/

@@ -29,29 +66,100 @@

var forEach = require("./collection-utils").forEach;
var elementUtils = require("./element-utils");
var elementUtilsMaker = require("./element-utils");
var listenerHandlerMaker = require("./listener-handler");
var idGeneratorMaker = require("./id-generator");
var listenerHandlerMaker = require("./listener-handler");
var idHandlerMaker = require("./id-handler");
var reporterMaker = require("./reporter");
/**
* @typedef idHandler
* @type {object}
* @property {function} get Gets the resize detector id of the element.
* @property {function} set Generate and sets the resize detector id of the element.
*/
/**
* @typedef Options
* @type {object}
* @property {boolean} callOnAdd Determines if listeners should be called when they are getting added.
Default is true. If true, the listener is guaranteed to be called when it has been added.
If false, the listener will not be guarenteed to be called when it has been added (does not prevent it from being called).
* @property {idHandler} idHandler A custom id handler that is responsible for generating, setting and retrieving id's for elements.
If not provided, a default id handler will be used.
* @property {reporter} reporter A custom reporter that handles reporting logs, warnings and errors.
If not provided, a default id handler will be used.
If set to false, then nothing will be reported.
*/
/**
* Creates an element resize detector instance.
* @public
* @param {Options?} options Optional global options object that will decide how this instance will work.
*/
module.exports = function(options) {
options = options || {};
var allowMultipleListeners = options.allowMultipleListeners === undefined ? true : false;
var eventListenerHandler = listenerHandlerMaker();
var idGenerator = idGeneratorMaker();
//Options to be used as default for the listenTo function.
var globalOptions = {};
globalOptions.callOnAdd = !!getOption(options, "callOnAdd", true);
//idHandler is currently not an option to the listenTo function, so it should not be added to globalOptions.
var idHandler = options.idHandler;
if(!idHandler) {
var idGenerator = idGeneratorMaker();
var defaultIdHandler = idHandlerMaker(idGenerator);
idHandler = defaultIdHandler;
}
//reporter is currently not an option to the listenTo function, so it should not be added to globalOptions.
var reporter = options.reporter;
if(!reporter) {
//If options.reporter is false, then the reporter should be quiet.
var quiet = reporter === false;
reporter = reporterMaker(quiet);
}
var eventListenerHandler = listenerHandlerMaker(idHandler);
var elementUtils = elementUtilsMaker(idHandler);
/**
* Makes the given elements resize-detectable and starts listening to resize events on the elements. Calls the event callback for each event for each element.
* @public
* @param {Options?} options Optional options object. These options will override the global options. Some options may not be overriden, such as idHandler.
* @param {element[]|element} elements The given array of elements to detect resize events of. Single element is also valid.
* @param {function} listener The callback to be executed for each resize event for each element.
*/
function listenTo(elements, listener) {
function isListenedTo(element) {
return elementUtils.isDetectable(element) && eventListenerHandler.get(element).length;
function listenTo(options, elements, listener) {
function onResizeCallback(element) {
var listeners = eventListenerHandler.get(element);
forEach(listeners, function(listener) {
listener(element);
});
}
function addListener(element, listener) {
elementUtils.addListener(element, listener);
function onElementReadyToAddListener(callOnAdd, element, listener) {
eventListenerHandler.add(element, listener);
if(callOnAdd) {
listener(element);
}
}
//Options object may be omitted.
if(!listener) {
listener = elements;
elements = options;
options = {};
}
if(!elements) {
throw new Error("At least one element required.");
}
if(!listener) {
throw new Error("Listener required.");
}
if(elements.length === undefined) {

@@ -61,8 +169,27 @@ elements = [elements];

var callOnAdd = getOption(options, "callOnAdd", globalOptions.callOnAdd);
forEach(elements, function(element) {
//The element may change size directly after the call to listenTo, which would be unable to detect it because
//the async adding of the object. By checking the size before and after, the size change can still be detected
//and the listener can be called accordingly.
var preWidth = element.offsetWidth;
var preHeight = element.offsetHeight;
if(!elementUtils.isDetectable(element)) {
//The element is not prepared to be detectable, so do prepare it and add a listener to it.
var id = idGenerator.newId();
return elementUtils.makeDetectable(element, id, function(element) {
addListener(element, listener);
return elementUtils.makeDetectable(reporter, element, function(element) {
elementUtils.addListener(element, onResizeCallback);
onElementReadyToAddListener(callOnAdd, element, listener);
//Only here the uncaught resize may occur (since this code is async).
//Check if the size is the same as when adding the listener.
var postWidth = element.offsetWidth;
var postHeight = element.offsetHeight;
//If callOnAdd is true, then the listener will have been called either way, so no need to call the listener manually then.
if(!callOnAdd && (preWidth !== postWidth || preHeight !== postHeight)) {
//The element was changed while the object was being added. Call the listener.
listener(element);
}
});

@@ -72,10 +199,3 @@ }

//The element has been prepared to be detectable and is ready to be listened to.
if(isListenedTo(element) && !allowMultipleListeners) {
//Since there is a listener and we disallow multiple listeners no listener should be added.
return;
}
//Since multiple listeners is allowed, another listener is added to the element.
return addListener(element, listener);
onElementReadyToAddListener(callOnAdd, element, listener);
});

@@ -89,105 +209,171 @@ }

},{"./collection-utils":1,"./element-utils":3,"./id-generator":4,"./listener-handler":5}],3:[function(require,module,exports){
function getOption(options, name, defaultValue) {
var value = options[name];
if((value === undefined || value === null) && defaultValue !== undefined) {
return defaultValue;
}
return value;
}
},{"./collection-utils":2,"./element-utils":4,"./id-generator":5,"./id-handler":6,"./listener-handler":7,"./reporter":8}],4:[function(require,module,exports){
"use strict";
var forEach = require("./collection-utils").forEach;
var browserDetector = require("./browser-detector");
var utils = module.exports = {};
module.exports = function(idHandler) {
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
function isDetectable(element) {
if(browserDetector.isIE(8)) {
//IE 8 does not use the object method.
//Check only if the element has been given an id.
return !!idHandler.get(element);
}
/**
* Gets the elq id of the element.
* @public
* @param {element} The target element to get the id of.
* @returns {string} The id of the element.
*/
utils.getId = function(element) {
return element.getAttribute("elq-target-id");
};
return !!getObject(element);
}
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
utils.isDetectable = function(element) {
return getObject(element);
};
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
function addListener(element, listener) {
if(!isDetectable(element)) {
throw new Error("Element is not detectable.");
}
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
utils.addListener = function(element, listener) {
if(!utils.isDetectable(element)) {
throw new Error("Element is not detectable.");
function listenerProxy() {
listener(element);
}
if(browserDetector.isIE(8)) {
//IE 8 does not support object, but supports the resize event directly on elements.
element.attachEvent("onresize", listenerProxy);
} else {
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", listenerProxy);
}
}
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", function() {
listener(element);
});
};
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
function makeDetectable(reporter, element, callback) {
function injectObject(id, element, callback) {
var OBJECT_STYLE = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {*} id An unique id in the context of all detectable elements.
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
utils.makeDetectable = function(element, id, callback) {
function onObjectLoad() {
/*jshint validthis:true */
function onObjectLoad() {
/*jshint validthis:true */
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//Append the style to the object.
objectDocument.head.appendChild(style);
//Append the style to the object.
objectDocument.head.appendChild(style);
this.style.cssText = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
//TODO: Is this needed here?
//this.style.cssText = OBJECT_STYLE;
//Notify that the element is ready to be listened to.
callback(element);
}
//Notify that the element is ready to be listened to.
callback(element);
}
//Create an unique elq-target-id for the target element, so that event listeners can be identified to this element.
element.setAttribute("elq-target-id", id);
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
var style = getComputedStyle(element);
if(style.position === "static") {
element.style.position = "relative";
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
if(getComputedStyle(element).position === "static") {
element.style.position = "relative";
var removeRelativeStyles = function(reporter, element, style, property) {
function getNumericalValue(value) {
return value.replace(/[^-\d\.]/g, "");
}
var value = style[property];
if(value !== "auto" && getNumericalValue(value) !== "0") {
reporter.warn("An element that is positioned static has style." + property + "=" + value + " which is ignored due to the static positioning. The element will need to be positioned relateive, so the style." + property + " will be set to 0. Element: ", element);
element.style[property] = 0;
}
};
//Check so that there are no accidental styles that will make the element styled differently now that is is relative.
//If there are any, set them to 0 (this should be okay with the user since the style properties did nothing before [since the element was positioned static] anyway).
removeRelativeStyles(reporter, element, style, "top");
removeRelativeStyles(reporter, element, style, "right");
removeRelativeStyles(reporter, element, style, "bottom");
removeRelativeStyles(reporter, element, style, "left");
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.style.cssText = OBJECT_STYLE;
object.onload = onObjectLoad;
object.setAttribute("erd-object-id", id);
//Safari: This must occur before adding the object to the DOM.
//IE: Does not like that this happens before, even if it is also added after.
if(!browserDetector.isIE()) {
object.data = "about:blank";
}
element.appendChild(object);
//IE: This must occur after adding the object to the DOM.
if(browserDetector.isIE()) {
object.data = "about:blank";
}
}
//Create an unique erd-target-id for the target element, so that event listeners can be identified to this element.
var id = idHandler.set(element);
if(browserDetector.isIE(8)) {
//IE 8 does not support objects properly. Luckily they do support the resize event.
//So do not inject the object and notify that the element is already ready to be listened to.
//The event handler for the resize event is attached in the utils.addListener instead.
callback(element);
} else {
injectObject(id, element, callback);
}
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.data = "about:blank";
object.onload = onObjectLoad;
object.setAttribute("elq-object-id", id);
element.appendChild(object);
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("erd-object-id")) {
return child;
}
});
}
return {
isDetectable: isDetectable,
makeDetectable: makeDetectable,
addListener: addListener,
};
};
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("elq-object-id")) {
return child;
}
});
}
},{"./collection-utils":1}],4:[function(require,module,exports){
},{"./browser-detector":1,"./collection-utils":2}],5:[function(require,module,exports){
"use strict";

@@ -203,3 +389,3 @@

*/
function newId() {
function generate() {
return idCount++;

@@ -209,12 +395,49 @@ }

return {
newId: newId
generate: generate
};
};
},{}],5:[function(require,module,exports){
},{}],6:[function(require,module,exports){
"use strict";
var elementUtils = require("./element-utils");
module.exports = function(idGenerator) {
module.exports = function() {
/**
* Gets the resize detector id of the element.
* @public
* @param {element} The target element to get the id of.
* @returns {string|number} The id of the element.
*/
function getId(element) {
return element.getAttribute("erd-target-id");
}
/**
* Sets the resize detector id of the element.
* @public
* @param {element} The target element to set the id to.
* @param {string?} An optional id to set to the element. If not specified, an id will be generated. All id's must be unique.
* @returns {string|number} The id of the element.
*/
function setId(element, id) {
if(!id && id !== 0) {
//Number should be generated.
id = idGenerator.generate();
}
element.setAttribute("erd-target-id", id);
return id;
}
return {
get: getId,
set: setId
};
};
},{}],7:[function(require,module,exports){
"use strict";
module.exports = function(idHandler) {
var eventListeners = {};

@@ -229,3 +452,3 @@

function getListeners(element) {
return eventListeners[elementUtils.getId(element)];
return eventListeners[idHandler.get(element)];
}

@@ -240,3 +463,3 @@

function addListener(element, listener) {
var id = elementUtils.getId(element);
var id = idHandler.get(element);

@@ -256,4 +479,33 @@ if(!eventListeners[id]) {

},{"./element-utils":3}]},{},[2])(2)
},{}],8:[function(require,module,exports){
"use strict";
/* global console: false */
/**
* Reporter that handles the reporting of logs, warnings and errors.
* @public
* @param {boolean} quiet Tells if the reporter should be quiet or not.
*/
module.exports = function(quiet) {
var noop = function () {
//Does nothing.
};
var reporter = {
log: noop,
warn: noop,
error: noop
};
if(!quiet && window.console) {
reporter.log = console.log;
reporter.warn = console.warn;
reporter.error = console.error;
}
return reporter;
};
},{}]},{},[3])(3)
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9ncnVudC1icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvY29sbGVjdGlvbi11dGlscy5qcyIsInNyYy9lbGVtZW50LXJlc2l6ZS1kZXRlY3Rvci5qcyIsInNyYy9lbGVtZW50LXV0aWxzLmpzIiwic3JjL2lkLWdlbmVyYXRvci5qcyIsInNyYy9saXN0ZW5lci1oYW5kbGVyLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcInVzZSBzdHJpY3RcIjtcblxudmFyIHV0aWxzID0gbW9kdWxlLmV4cG9ydHMgPSB7fTtcblxuLyoqXG4gKiBMb29wcyB0aHJvdWdoIHRoZSBjb2xsZWN0aW9uIGFuZCBjYWxscyB0aGUgY2FsbGJhY2sgZm9yIGVhY2ggZWxlbWVudC4gaWYgdGhlIGNhbGxiYWNrIHJldHVybnMgdHJ1dGh5LCB0aGUgbG9vcCBpcyBicm9rZW4gYW5kIHJldHVybnMgdGhlIHNhbWUgdmFsdWUuXG4gKiBAcHVibGljXG4gKiBAcGFyYW0geyp9IGNvbGxlY3Rpb24gVGhlIGNvbGxlY3Rpb24gdG8gbG9vcCB0aHJvdWdoLiBOZWVkcyB0byBoYXZlIGEgbGVuZ3RoIHByb3BlcnR5IHNldCBhbmQgaGF2ZSBpbmRpY2VzIHNldCBmcm9tIDAgdG8gbGVuZ3RoIC0gMS5cbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrIFRoZSBjYWxsYmFjayB0byBiZSBjYWxsZWQgZm9yIGVhY2ggZWxlbWVudC4gVGhlIGVsZW1lbnQgd2lsbCBiZSBnaXZlbiBhcyBhIHBhcmFtZXRlciB0byB0aGUgY2FsbGJhY2suIElmIHRoaXMgY2FsbGJhY2sgcmV0dXJucyB0cnV0aHksIHRoZSBsb29wIGlzIGJyb2tlbiBhbmQgdGhlIHNhbWUgdmFsdWUgaXMgcmV0dXJuZWQuXG4gKiBAcmV0dXJucyB7Kn0gVGhlIHZhbHVlIHRoYXQgYSBjYWxsYmFjayBoYXMgcmV0dXJuZWQgKGlmIHRydXRoeSkuIE90aGVyd2lzZSBub3RoaW5nLlxuICovXG51dGlscy5mb3JFYWNoID0gZnVuY3Rpb24oY29sbGVjdGlvbiwgY2FsbGJhY2spIHtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgY29sbGVjdGlvbi5sZW5ndGg7IGkrKykge1xuICAgICAgICB2YXIgcmVzdWx0ID0gY2FsbGJhY2soY29sbGVjdGlvbltpXSk7XG4gICAgICAgIGlmKHJlc3VsdCkge1xuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfVxuICAgIH1cbn07XG4iLCIvL0hlYXZpbHkgaW5zcGlyZWQgYnkgaHR0cDovL3d3dy5iYWNrYWxsZXljb2Rlci5jb20vMjAxMy8wMy8xOC9jcm9zcy1icm93c2VyLWV2ZW50LWJhc2VkLWVsZW1lbnQtcmVzaXplLWRldGVjdGlvbi9cblxuXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBmb3JFYWNoID0gcmVxdWlyZShcIi4vY29sbGVjdGlvbi11dGlsc1wiKS5mb3JFYWNoO1xudmFyIGVsZW1lbnRVdGlscyA9IHJlcXVpcmUoXCIuL2VsZW1lbnQtdXRpbHNcIik7XG52YXIgaWRHZW5lcmF0b3JNYWtlciA9IHJlcXVpcmUoXCIuL2lkLWdlbmVyYXRvclwiKTtcbnZhciBsaXN0ZW5lckhhbmRsZXJNYWtlciA9IHJlcXVpcmUoXCIuL2xpc3RlbmVyLWhhbmRsZXJcIik7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24ob3B0aW9ucykge1xuICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICAgIHZhciBhbGxvd011bHRpcGxlTGlzdGVuZXJzID0gb3B0aW9ucy5hbGxvd011bHRpcGxlTGlzdGVuZXJzID09PSB1bmRlZmluZWQgPyB0cnVlIDogZmFsc2U7XG5cbiAgICB2YXIgZXZlbnRMaXN0ZW5lckhhbmRsZXIgPSBsaXN0ZW5lckhhbmRsZXJNYWtlcigpO1xuICAgIHZhciBpZEdlbmVyYXRvciA9IGlkR2VuZXJhdG9yTWFrZXIoKTtcblxuICAgIC8qKlxuICAgICAqIE1ha2VzIHRoZSBnaXZlbiBlbGVtZW50cyByZXNpemUtZGV0ZWN0YWJsZSBhbmQgc3RhcnRzIGxpc3RlbmluZyB0byByZXNpemUgZXZlbnRzIG9uIHRoZSBlbGVtZW50cy4gQ2FsbHMgdGhlIGV2ZW50IGNhbGxiYWNrIGZvciBlYWNoIGV2ZW50IGZvciBlYWNoIGVsZW1lbnQuXG4gICAgICogQHB1YmxpY1xuICAgICAqIEBwYXJhbSB7ZWxlbWVudFtdfGVsZW1lbnR9IGVsZW1lbnRzIFRoZSBnaXZlbiBhcnJheSBvZiBlbGVtZW50cyB0byBkZXRlY3QgcmVzaXplIGV2ZW50cyBvZi4gU2luZ2xlIGVsZW1lbnQgaXMgYWxzbyB2YWxpZC5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBsaXN0ZW5lciBUaGUgY2FsbGJhY2sgdG8gYmUgZXhlY3V0ZWQgZm9yIGVhY2ggcmVzaXplIGV2ZW50IGZvciBlYWNoIGVsZW1lbnQuXG4gICAgICovXG4gICAgZnVuY3Rpb24gbGlzdGVuVG8oZWxlbWVudHMsIGxpc3RlbmVyKSB7XG4gICAgICAgIGZ1bmN0aW9uIGlzTGlzdGVuZWRUbyhlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm4gZWxlbWVudFV0aWxzLmlzRGV0ZWN0YWJsZShlbGVtZW50KSAmJiBldmVudExpc3RlbmVySGFuZGxlci5nZXQoZWxlbWVudCkubGVuZ3RoO1xuICAgICAgICB9XG5cbiAgICAgICAgZnVuY3Rpb24gYWRkTGlzdGVuZXIoZWxlbWVudCwgbGlzdGVuZXIpIHtcbiAgICAgICAgICAgIGVsZW1lbnRVdGlscy5hZGRMaXN0ZW5lcihlbGVtZW50LCBsaXN0ZW5lcik7XG4gICAgICAgICAgICBldmVudExpc3RlbmVySGFuZGxlci5hZGQoZWxlbWVudCwgbGlzdGVuZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoZWxlbWVudHMubGVuZ3RoID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW1lbnRzID0gW2VsZW1lbnRzXTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvckVhY2goZWxlbWVudHMsIGZ1bmN0aW9uKGVsZW1lbnQpIHtcbiAgICAgICAgICAgIGlmKCFlbGVtZW50VXRpbHMuaXNEZXRlY3RhYmxlKGVsZW1lbnQpKSB7XG4gICAgICAgICAgICAgICAgLy9UaGUgZWxlbWVudCBpcyBub3QgcHJlcGFyZWQgdG8gYmUgZGV0ZWN0YWJsZSwgc28gZG8gcHJlcGFyZSBpdCBhbmQgYWRkIGEgbGlzdGVuZXIgdG8gaXQuXG4gICAgICAgICAgICAgICAgdmFyIGlkID0gaWRHZW5lcmF0b3IubmV3SWQoKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWxlbWVudFV0aWxzLm1ha2VEZXRlY3RhYmxlKGVsZW1lbnQsIGlkLCBmdW5jdGlvbihlbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIGFkZExpc3RlbmVyKGVsZW1lbnQsIGxpc3RlbmVyKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy9UaGUgZWxlbWVudCBoYXMgYmVlbiBwcmVwYXJlZCB0byBiZSBkZXRlY3RhYmxlIGFuZCBpcyByZWFkeSB0byBiZSBsaXN0ZW5lZCB0by5cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYoaXNMaXN0ZW5lZFRvKGVsZW1lbnQpICYmICFhbGxvd011bHRpcGxlTGlzdGVuZXJzKSB7XG4gICAgICAgICAgICAgICAgLy9TaW5jZSB0aGVyZSBpcyBhIGxpc3RlbmVyIGFuZCB3ZSBkaXNhbGxvdyBtdWx0aXBsZSBsaXN0ZW5lcnMgbm8gbGlzdGVuZXIgc2hvdWxkIGJlIGFkZGVkLlxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy9TaW5jZSBtdWx0aXBsZSBsaXN0ZW5lcnMgaXMgYWxsb3dlZCwgYW5vdGhlciBsaXN0ZW5lciBpcyBhZGRlZCB0byB0aGUgZWxlbWVudC5cbiAgICAgICAgICAgIHJldHVybiBhZGRMaXN0ZW5lcihlbGVtZW50LCBsaXN0ZW5lcik7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIGxpc3RlblRvOiBsaXN0ZW5Ub1xuICAgIH07XG59O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBmb3JFYWNoID0gcmVxdWlyZShcIi4vY29sbGVjdGlvbi11dGlsc1wiKS5mb3JFYWNoO1xuXG52YXIgdXRpbHMgPSBtb2R1bGUuZXhwb3J0cyA9IHt9O1xuXG4vKipcbiAqIEdldHMgdGhlIGVscSBpZCBvZiB0aGUgZWxlbWVudC5cbiAqIEBwdWJsaWNcbiAqIEBwYXJhbSB7ZWxlbWVudH0gVGhlIHRhcmdldCBlbGVtZW50IHRvIGdldCB0aGUgaWQgb2YuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgaWQgb2YgdGhlIGVsZW1lbnQuXG4gKi9cbnV0aWxzLmdldElkID0gZnVuY3Rpb24oZWxlbWVudCkge1xuICAgIHJldHVybiBlbGVtZW50LmdldEF0dHJpYnV0ZShcImVscS10YXJnZXQtaWRcIik7XG59O1xuXG4vKipcbiAqIFRlbGxzIGlmIHRoZSBlbGVtZW50IGhhcyBiZWVuIG1hZGUgZGV0ZWN0YWJsZSBhbmQgcmVhZHkgdG8gYmUgbGlzdGVuZWQgZm9yIHJlc2l6ZSBldmVudHMuXG4gKiBAcHVibGljXG4gKiBAcGFyYW0ge2VsZW1lbnR9IFRoZSBlbGVtZW50IHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgb3IgZmFsc2UgZGVwZW5kaW5nIG9uIGlmIHRoZSBlbGVtZW50IGlzIGRldGVjdGFibGUgb3Igbm90LlxuICovXG51dGlscy5pc0RldGVjdGFibGUgPSBmdW5jdGlvbihlbGVtZW50KSB7XG4gICAgcmV0dXJuIGdldE9iamVjdChlbGVtZW50KTtcbn07XG5cbi8qKlxuICogQWRkcyBhIHJlc2l6ZSBldmVudCBsaXN0ZW5lciB0byB0aGUgZWxlbWVudC5cbiAqIEBwdWJsaWNcbiAqIEBwYXJhbSB7ZWxlbWVudH0gZWxlbWVudCBUaGUgZWxlbWVudCB0aGF0IHNob3VsZCBoYXZlIHRoZSBsaXN0ZW5lciBhZGRlZC5cbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGxpc3RlbmVyIFRoZSBsaXN0ZW5lciBjYWxsYmFjayB0byBiZSBjYWxsZWQgZm9yIGVhY2ggcmVzaXplIGV2ZW50IG9mIHRoZSBlbGVtZW50LiBUaGUgZWxlbWVudCB3aWxsIGJlIGdpdmVuIGFzIGEgcGFyYW1ldGVyIHRvIHRoZSBsaXN0ZW5lciBjYWxsYmFjay5cbiAqL1xudXRpbHMuYWRkTGlzdGVuZXIgPSBmdW5jdGlvbihlbGVtZW50LCBsaXN0ZW5lcikge1xuICAgIGlmKCF1dGlscy5pc0RldGVjdGFibGUoZWxlbWVudCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRWxlbWVudCBpcyBub3QgZGV0ZWN0YWJsZS5cIik7XG4gICAgfVxuXG4gICAgdmFyIG9iamVjdCA9IGdldE9iamVjdChlbGVtZW50KTtcbiAgICBvYmplY3QuY29udGVudERvY3VtZW50LmRlZmF1bHRWaWV3LmFkZEV2ZW50TGlzdGVuZXIoXCJyZXNpemVcIiwgZnVuY3Rpb24oKSB7XG4gICAgICAgIGxpc3RlbmVyKGVsZW1lbnQpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBNYWtlcyBhbiBlbGVtZW50IGRldGVjdGFibGUgYW5kIHJlYWR5IHRvIGJlIGxpc3RlbmVkIGZvciByZXNpemUgZXZlbnRzLiBXaWxsIGNhbGwgdGhlIGNhbGxiYWNrIHdoZW4gdGhlIGVsZW1lbnQgaXMgcmVhZHkgdG8gYmUgbGlzdGVuZWQgZm9yIHJlc2l6ZSBjaGFuZ2VzLlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7ZWxlbWVudH0gZWxlbWVudCBUaGUgZWxlbWVudCB0byBtYWtlIGRldGVjdGFibGVcbiAqIEBwYXJhbSB7Kn0gaWQgQW4gdW5pcXVlIGlkIGluIHRoZSBjb250ZXh0IG9mIGFsbCBkZXRlY3RhYmxlIGVsZW1lbnRzLlxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgVGhlIGNhbGxiYWNrIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBlbGVtZW50IGlzIHJlYWR5IHRvIGJlIGxpc3RlbmVkIGZvciByZXNpemUgY2hhbmdlcy4gV2lsbCBiZSBjYWxsZWQgd2l0aCB0aGUgZWxlbWVudCBhcyBmaXJzdCBwYXJhbWV0ZXIuXG4gKi9cbnV0aWxzLm1ha2VEZXRlY3RhYmxlID0gZnVuY3Rpb24oZWxlbWVudCwgaWQsIGNhbGxiYWNrKSB7XG4gICAgZnVuY3Rpb24gb25PYmplY3RMb2FkKCkge1xuICAgICAgICAvKmpzaGludCB2YWxpZHRoaXM6dHJ1ZSAqL1xuXG4gICAgICAgIC8vQ3JlYXRlIHRoZSBzdHlsZSBlbGVtZW50IHRvIGJlIGFkZGVkIHRvIHRoZSBvYmplY3QuXG4gICAgICAgIHZhciBvYmplY3REb2N1bWVudCA9IHRoaXMuY29udGVudERvY3VtZW50O1xuICAgICAgICB2YXIgc3R5bGUgPSBvYmplY3REb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3R5bGVcIik7XG4gICAgICAgIHN0eWxlLmlubmVySFRNTCA9IFwiaHRtbCwgYm9keSB7IG1hcmdpbjogMDsgcGFkZGluZzogMCB9IGRpdiB7IC13ZWJraXQtdHJhbnNpdGlvbjogb3BhY2l0eSAwLjAxczsgLW1zLXRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IC1vLXRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IHRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IG9wYWNpdHk6IDA7IH1cIjtcblxuICAgICAgICAvL1RPRE86IFJlbW92ZSBhbnkgc3R5bGVzIHRoYXQgaGFzIGJlZW4gc2V0IG9uIHRoZSBvYmplY3QuIE9ubHkgdGhlIHN0eWxlIGFib3ZlIHNob3VsZCBiZSBzdHlsaW5nIHRoZSBvYmplY3QuXG5cbiAgICAgICAgLy9BcHBlbmQgdGhlIHN0eWxlIHRvIHRoZSBvYmplY3QuXG4gICAgICAgIG9iamVjdERvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoc3R5bGUpO1xuXG4gICAgICAgIHRoaXMuc3R5bGUuY3NzVGV4dCA9IFwiZGlzcGxheTogYmxvY2s7IHBvc2l0aW9uOiBhYnNvbHV0ZTsgdG9wOiAwOyBsZWZ0OiAwOyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBib3JkZXI6IG5vbmU7IHBhZGRpbmc6IDA7IG1hcmdpbjogMDsgb3BhY2l0eTogMDsgei1pbmRleDogLTEwMDA7IHBvaW50ZXItZXZlbnRzOiBub25lO1wiO1xuXG4gICAgICAgIC8vTm90aWZ5IHRoYXQgdGhlIGVsZW1lbnQgaXMgcmVhZHkgdG8gYmUgbGlzdGVuZWQgdG8uXG4gICAgICAgIGNhbGxiYWNrKGVsZW1lbnQpO1xuICAgIH1cblxuICAgIC8vQ3JlYXRlIGFuIHVuaXF1ZSBlbHEtdGFyZ2V0LWlkIGZvciB0aGUgdGFyZ2V0IGVsZW1lbnQsIHNvIHRoYXQgZXZlbnQgbGlzdGVuZXJzIGNhbiBiZSBpZGVudGlmaWVkIHRvIHRoaXMgZWxlbWVudC5cbiAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShcImVscS10YXJnZXQtaWRcIiwgaWQpO1xuXG4gICAgLy9UaGUgdGFyZ2V0IGVsZW1lbnQgbmVlZHMgdG8gYmUgcG9zaXRpb25lZCAoZXZlcnl0aGluZyBleGNlcHQgc3RhdGljKSBzbyB0aGUgYWJzb2x1dGUgcG9zaXRpb25lZCBvYmplY3Qgd2lsbCBiZSBwb3NpdGlvbmVkIHJlbGF0aXZlIHRvIHRoZSB0YXJnZXQgZWxlbWVudC5cbiAgICBpZihnZXRDb21wdXRlZFN0eWxlKGVsZW1lbnQpLnBvc2l0aW9uID09PSBcInN0YXRpY1wiKSB7XG4gICAgICAgIGVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSBcInJlbGF0aXZlXCI7XG4gICAgfVxuXG4gICAgLy9BZGQgYW4gb2JqZWN0IGVsZW1lbnQgYXMgYSBjaGlsZCB0byB0aGUgdGFyZ2V0IGVsZW1lbnQgdGhhdCB3aWxsIGJlIGxpc3RlbmVkIHRvIGZvciByZXNpemUgZXZlbnRzLlxuICAgIHZhciBvYmplY3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwib2JqZWN0XCIpO1xuICAgIG9iamVjdC50eXBlID0gXCJ0ZXh0L2h0bWxcIjtcbiAgICBvYmplY3QuZGF0YSA9IFwiYWJvdXQ6YmxhbmtcIjtcbiAgICBvYmplY3Qub25sb2FkID0gb25PYmplY3RMb2FkO1xuICAgIG9iamVjdC5zZXRBdHRyaWJ1dGUoXCJlbHEtb2JqZWN0LWlkXCIsIGlkKTtcbiAgICBlbGVtZW50LmFwcGVuZENoaWxkKG9iamVjdCk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdGhlIGNoaWxkIG9iamVjdCBvZiB0aGUgdGFyZ2V0IGVsZW1lbnQuXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtlbGVtZW50fSBlbGVtZW50IFRoZSB0YXJnZXQgZWxlbWVudC5cbiAqIEByZXR1cm5zIFRoZSBvYmplY3QgZWxlbWVudCBvZiB0aGUgdGFyZ2V0LlxuICovXG5mdW5jdGlvbiBnZXRPYmplY3QoZWxlbWVudCkge1xuICAgIHJldHVybiBmb3JFYWNoKGVsZW1lbnQuY2hpbGRyZW4sIGZ1bmN0aW9uKGNoaWxkKSB7XG4gICAgICAgIGlmKGNoaWxkLmhhc0F0dHJpYnV0ZShcImVscS1vYmplY3QtaWRcIikpIHtcbiAgICAgICAgICAgIHJldHVybiBjaGlsZDtcbiAgICAgICAgfVxuICAgIH0pO1xufVxuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGlkQ291bnQgPSAxO1xuXG4gICAgLyoqXG4gICAgICogR2VuZXJhdGVzIGEgbmV3IHVuaXF1ZSBpZCBpbiB0aGUgY29udGV4dC5cbiAgICAgKiBAcHVibGljXG4gICAgICogQHJldHVybnMge251bWJlcn0gQSB1bmlxdWUgaWQgaW4gdGhlIGNvbnRleHQuXG4gICAgICovXG4gICAgZnVuY3Rpb24gbmV3SWQoKSB7XG4gICAgICAgIHJldHVybiBpZENvdW50Kys7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgbmV3SWQ6IG5ld0lkXG4gICAgfTtcbn07XG4iLCJcInVzZSBzdHJpY3RcIjtcblxudmFyIGVsZW1lbnRVdGlscyA9IHJlcXVpcmUoXCIuL2VsZW1lbnQtdXRpbHNcIik7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGV2ZW50TGlzdGVuZXJzID0ge307XG5cbiAgICAvKipcbiAgICAgKiBHZXRzIGFsbCBsaXN0ZW5lcnMgZm9yIHRoZSBnaXZlbiBlbGVtZW50LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBAcGFyYW0ge2VsZW1lbnR9IGVsZW1lbnQgVGhlIGVsZW1lbnQgdG8gZ2V0IGFsbCBsaXN0ZW5lcnMgZm9yLlxuICAgICAqIEByZXR1cm5zIEFsbCBsaXN0ZW5lcnMgZm9yIHRoZSBnaXZlbiBlbGVtZW50LlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGdldExpc3RlbmVycyhlbGVtZW50KSB7XG4gICAgICAgIHJldHVybiBldmVudExpc3RlbmVyc1tlbGVtZW50VXRpbHMuZ2V0SWQoZWxlbWVudCldO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0b3JlcyB0aGUgZ2l2ZW4gbGlzdGVuZXIgZm9yIHRoZSBnaXZlbiBlbGVtZW50LiBXaWxsIG5vdCBhY3R1YWxseSBhZGQgdGhlIGxpc3RlbmVyIHRvIHRoZSBlbGVtZW50LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBAcGFyYW0ge2VsZW1lbnR9IGVsZW1lbnQgVGhlIGVsZW1lbnQgdGhhdCBzaG91bGQgaGF2ZSB0aGUgbGlzdGVuZXIgYWRkZWQuXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gbGlzdGVuZXIgVGhlIGNhbGxiYWNrIHRoYXQgdGhlIGVsZW1lbnQgaGFzIGFkZGVkLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGFkZExpc3RlbmVyKGVsZW1lbnQsIGxpc3RlbmVyKSB7XG4gICAgICAgIHZhciBpZCA9IGVsZW1lbnRVdGlscy5nZXRJZChlbGVtZW50KTtcblxuICAgICAgICBpZighZXZlbnRMaXN0ZW5lcnNbaWRdKSB7XG4gICAgICAgICAgICBldmVudExpc3RlbmVyc1tpZF0gPSBbXTtcbiAgICAgICAgfVxuXG4gICAgICAgIGV2ZW50TGlzdGVuZXJzW2lkXS5wdXNoKGxpc3RlbmVyKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBnZXQ6IGdldExpc3RlbmVycyxcbiAgICAgICAgYWRkOiBhZGRMaXN0ZW5lclxuICAgIH07XG59O1xuIl19
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9ncnVudC1icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvYnJvd3Nlci1kZXRlY3Rvci5qcyIsInNyYy9jb2xsZWN0aW9uLXV0aWxzLmpzIiwic3JjL2VsZW1lbnQtcmVzaXplLWRldGVjdG9yLmpzIiwic3JjL2VsZW1lbnQtdXRpbHMuanMiLCJzcmMvaWQtZ2VuZXJhdG9yLmpzIiwic3JjL2lkLWhhbmRsZXIuanMiLCJzcmMvbGlzdGVuZXItaGFuZGxlci5qcyIsInNyYy9yZXBvcnRlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBkZXRlY3RvciA9IG1vZHVsZS5leHBvcnRzID0ge307XG5cbmRldGVjdG9yLmlzSUUgPSBmdW5jdGlvbih2ZXJzaW9uKSB7XG4gICAgZnVuY3Rpb24gaXNBbnlJZVZlcnNpb24oKSB7XG4gICAgICAgIHZhciBhZ2VudCA9IG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgcmV0dXJuIGFnZW50LmluZGV4T2YoXCJtc2llXCIpICE9PSAtMSB8fCBhZ2VudC5pbmRleE9mKFwidHJpZGVudFwiKSAhPT0gLTE7XG4gICAgfVxuXG4gICAgaWYoIWlzQW55SWVWZXJzaW9uKCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGlmKCF2ZXJzaW9uKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vU2hhbWVsZXNzbHkgc3RvbGVuIGZyb20gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vcGFkb2xzZXkvNTI3NjgzXG4gICAgdmFyIGllVmVyc2lvbiA9IChmdW5jdGlvbigpe1xuICAgICAgICB2YXIgdW5kZWYsXG4gICAgICAgICAgICB2ID0gMyxcbiAgICAgICAgICAgIGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIiksXG4gICAgICAgICAgICBhbGwgPSBkaXYuZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJpXCIpO1xuXG4gICAgICAgIGRvIHtcbiAgICAgICAgICAgIGRpdi5pbm5lckhUTUwgPSBcIjwhLS1baWYgZ3QgSUUgXCIgKyAoKyt2KSArIFwiXT48aT48L2k+PCFbZW5kaWZdLS0+XCI7XG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKGFsbFswXSk7XG5cbiAgICAgICAgcmV0dXJuIHYgPiA0ID8gdiA6IHVuZGVmO1xuICAgIH0oKSk7XG5cbiAgICByZXR1cm4gdmVyc2lvbiA9PT0gaWVWZXJzaW9uO1xufTtcbiIsIlwidXNlIHN0cmljdFwiO1xuXG52YXIgdXRpbHMgPSBtb2R1bGUuZXhwb3J0cyA9IHt9O1xuXG4vKipcbiAqIExvb3BzIHRocm91Z2ggdGhlIGNvbGxlY3Rpb24gYW5kIGNhbGxzIHRoZSBjYWxsYmFjayBmb3IgZWFjaCBlbGVtZW50LiBpZiB0aGUgY2FsbGJhY2sgcmV0dXJucyB0cnV0aHksIHRoZSBsb29wIGlzIGJyb2tlbiBhbmQgcmV0dXJucyB0aGUgc2FtZSB2YWx1ZS5cbiAqIEBwdWJsaWNcbiAqIEBwYXJhbSB7Kn0gY29sbGVjdGlvbiBUaGUgY29sbGVjdGlvbiB0byBsb29wIHRocm91Z2guIE5lZWRzIHRvIGhhdmUgYSBsZW5ndGggcHJvcGVydHkgc2V0IGFuZCBoYXZlIGluZGljZXMgc2V0IGZyb20gMCB0byBsZW5ndGggLSAxLlxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2sgVGhlIGNhbGxiYWNrIHRvIGJlIGNhbGxlZCBmb3IgZWFjaCBlbGVtZW50LiBUaGUgZWxlbWVudCB3aWxsIGJlIGdpdmVuIGFzIGEgcGFyYW1ldGVyIHRvIHRoZSBjYWxsYmFjay4gSWYgdGhpcyBjYWxsYmFjayByZXR1cm5zIHRydXRoeSwgdGhlIGxvb3AgaXMgYnJva2VuIGFuZCB0aGUgc2FtZSB2YWx1ZSBpcyByZXR1cm5lZC5cbiAqIEByZXR1cm5zIHsqfSBUaGUgdmFsdWUgdGhhdCBhIGNhbGxiYWNrIGhhcyByZXR1cm5lZCAoaWYgdHJ1dGh5KS4gT3RoZXJ3aXNlIG5vdGhpbmcuXG4gKi9cbnV0aWxzLmZvckVhY2ggPSBmdW5jdGlvbihjb2xsZWN0aW9uLCBjYWxsYmFjaykge1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBjb2xsZWN0aW9uLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciByZXN1bHQgPSBjYWxsYmFjayhjb2xsZWN0aW9uW2ldKTtcbiAgICAgICAgaWYocmVzdWx0KSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9XG4gICAgfVxufTtcbiIsIi8vSGVhdmlseSBpbnNwaXJlZCBieSBodHRwOi8vd3d3LmJhY2thbGxleWNvZGVyLmNvbS8yMDEzLzAzLzE4L2Nyb3NzLWJyb3dzZXItZXZlbnQtYmFzZWQtZWxlbWVudC1yZXNpemUtZGV0ZWN0aW9uL1xuXG5cInVzZSBzdHJpY3RcIjtcblxudmFyIGZvckVhY2ggPSByZXF1aXJlKFwiLi9jb2xsZWN0aW9uLXV0aWxzXCIpLmZvckVhY2g7XG52YXIgZWxlbWVudFV0aWxzTWFrZXIgPSByZXF1aXJlKFwiLi9lbGVtZW50LXV0aWxzXCIpO1xudmFyIGxpc3RlbmVySGFuZGxlck1ha2VyID0gcmVxdWlyZShcIi4vbGlzdGVuZXItaGFuZGxlclwiKTtcbnZhciBpZEdlbmVyYXRvck1ha2VyID0gcmVxdWlyZShcIi4vaWQtZ2VuZXJhdG9yXCIpO1xudmFyIGlkSGFuZGxlck1ha2VyID0gcmVxdWlyZShcIi4vaWQtaGFuZGxlclwiKTtcbnZhciByZXBvcnRlck1ha2VyID0gcmVxdWlyZShcIi4vcmVwb3J0ZXJcIik7XG5cbi8qKlxuICogQHR5cGVkZWYgaWRIYW5kbGVyXG4gKiBAdHlwZSB7b2JqZWN0fVxuICogQHByb3BlcnR5IHtmdW5jdGlvbn0gZ2V0IEdldHMgdGhlIHJlc2l6ZSBkZXRlY3RvciBpZCBvZiB0aGUgZWxlbWVudC5cbiAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IHNldCBHZW5lcmF0ZSBhbmQgc2V0cyB0aGUgcmVzaXplIGRldGVjdG9yIGlkIG9mIHRoZSBlbGVtZW50LlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYgT3B0aW9uc1xuICogQHR5cGUge29iamVjdH1cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gY2FsbE9uQWRkICAgIERldGVybWluZXMgaWYgbGlzdGVuZXJzIHNob3VsZCBiZSBjYWxsZWQgd2hlbiB0aGV5IGFyZSBnZXR0aW5nIGFkZGVkLiBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlZmF1bHQgaXMgdHJ1ZS4gSWYgdHJ1ZSwgdGhlIGxpc3RlbmVyIGlzIGd1YXJhbnRlZWQgdG8gYmUgY2FsbGVkIHdoZW4gaXQgaGFzIGJlZW4gYWRkZWQuIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSWYgZmFsc2UsIHRoZSBsaXN0ZW5lciB3aWxsIG5vdCBiZSBndWFyZW50ZWVkIHRvIGJlIGNhbGxlZCB3aGVuIGl0IGhhcyBiZWVuIGFkZGVkIChkb2VzIG5vdCBwcmV2ZW50IGl0IGZyb20gYmVpbmcgY2FsbGVkKS5cbiAqIEBwcm9wZXJ0eSB7aWRIYW5kbGVyfSBpZEhhbmRsZXIgIEEgY3VzdG9tIGlkIGhhbmRsZXIgdGhhdCBpcyByZXNwb25zaWJsZSBmb3IgZ2VuZXJhdGluZywgc2V0dGluZyBhbmQgcmV0cmlldmluZyBpZCdzIGZvciBlbGVtZW50cy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElmIG5vdCBwcm92aWRlZCwgYSBkZWZhdWx0IGlkIGhhbmRsZXIgd2lsbCBiZSB1c2VkLlxuICogQHByb3BlcnR5IHtyZXBvcnRlcn0gcmVwb3J0ZXIgICAgQSBjdXN0b20gcmVwb3J0ZXIgdGhhdCBoYW5kbGVzIHJlcG9ydGluZyBsb2dzLCB3YXJuaW5ncyBhbmQgZXJyb3JzLiBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElmIG5vdCBwcm92aWRlZCwgYSBkZWZhdWx0IGlkIGhhbmRsZXIgd2lsbCBiZSB1c2VkLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSWYgc2V0IHRvIGZhbHNlLCB0aGVuIG5vdGhpbmcgd2lsbCBiZSByZXBvcnRlZC5cbiAqL1xuXG4vKipcbiAqIENyZWF0ZXMgYW4gZWxlbWVudCByZXNpemUgZGV0ZWN0b3IgaW5zdGFuY2UuXG4gKiBAcHVibGljXG4gKiBAcGFyYW0ge09wdGlvbnM/fSBvcHRpb25zIE9wdGlvbmFsIGdsb2JhbCBvcHRpb25zIG9iamVjdCB0aGF0IHdpbGwgZGVjaWRlIGhvdyB0aGlzIGluc3RhbmNlIHdpbGwgd29yay5cbiAqL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgICAvL09wdGlvbnMgdG8gYmUgdXNlZCBhcyBkZWZhdWx0IGZvciB0aGUgbGlzdGVuVG8gZnVuY3Rpb24uXG4gICAgdmFyIGdsb2JhbE9wdGlvbnMgPSB7fTtcbiAgICBnbG9iYWxPcHRpb25zLmNhbGxPbkFkZCA9ICEhZ2V0T3B0aW9uKG9wdGlvbnMsIFwiY2FsbE9uQWRkXCIsIHRydWUpO1xuXG4gICAgLy9pZEhhbmRsZXIgaXMgY3VycmVudGx5IG5vdCBhbiBvcHRpb24gdG8gdGhlIGxpc3RlblRvIGZ1bmN0aW9uLCBzbyBpdCBzaG91bGQgbm90IGJlIGFkZGVkIHRvIGdsb2JhbE9wdGlvbnMuXG4gICAgdmFyIGlkSGFuZGxlciA9IG9wdGlvbnMuaWRIYW5kbGVyO1xuXG4gICAgaWYoIWlkSGFuZGxlcikge1xuICAgICAgICB2YXIgaWRHZW5lcmF0b3IgPSBpZEdlbmVyYXRvck1ha2VyKCk7XG4gICAgICAgIHZhciBkZWZhdWx0SWRIYW5kbGVyID0gaWRIYW5kbGVyTWFrZXIoaWRHZW5lcmF0b3IpO1xuICAgICAgICBpZEhhbmRsZXIgPSBkZWZhdWx0SWRIYW5kbGVyO1xuICAgIH1cblxuICAgIC8vcmVwb3J0ZXIgaXMgY3VycmVudGx5IG5vdCBhbiBvcHRpb24gdG8gdGhlIGxpc3RlblRvIGZ1bmN0aW9uLCBzbyBpdCBzaG91bGQgbm90IGJlIGFkZGVkIHRvIGdsb2JhbE9wdGlvbnMuXG4gICAgdmFyIHJlcG9ydGVyID0gb3B0aW9ucy5yZXBvcnRlcjtcblxuICAgIGlmKCFyZXBvcnRlcikge1xuICAgICAgICAvL0lmIG9wdGlvbnMucmVwb3J0ZXIgaXMgZmFsc2UsIHRoZW4gdGhlIHJlcG9ydGVyIHNob3VsZCBiZSBxdWlldC5cbiAgICAgICAgdmFyIHF1aWV0ID0gcmVwb3J0ZXIgPT09IGZhbHNlO1xuICAgICAgICByZXBvcnRlciA9IHJlcG9ydGVyTWFrZXIocXVpZXQpO1xuICAgIH1cblxuICAgIHZhciBldmVudExpc3RlbmVySGFuZGxlciA9IGxpc3RlbmVySGFuZGxlck1ha2VyKGlkSGFuZGxlcik7XG4gICAgdmFyIGVsZW1lbnRVdGlscyA9IGVsZW1lbnRVdGlsc01ha2VyKGlkSGFuZGxlcik7XG5cbiAgICAvKipcbiAgICAgKiBNYWtlcyB0aGUgZ2l2ZW4gZWxlbWVudHMgcmVzaXplLWRldGVjdGFibGUgYW5kIHN0YXJ0cyBsaXN0ZW5pbmcgdG8gcmVzaXplIGV2ZW50cyBvbiB0aGUgZWxlbWVudHMuIENhbGxzIHRoZSBldmVudCBjYWxsYmFjayBmb3IgZWFjaCBldmVudCBmb3IgZWFjaCBlbGVtZW50LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBAcGFyYW0ge09wdGlvbnM/fSBvcHRpb25zIE9wdGlvbmFsIG9wdGlvbnMgb2JqZWN0LiBUaGVzZSBvcHRpb25zIHdpbGwgb3ZlcnJpZGUgdGhlIGdsb2JhbCBvcHRpb25zLiBTb21lIG9wdGlvbnMgbWF5IG5vdCBiZSBvdmVycmlkZW4sIHN1Y2ggYXMgaWRIYW5kbGVyLlxuICAgICAqIEBwYXJhbSB7ZWxlbWVudFtdfGVsZW1lbnR9IGVsZW1lbnRzIFRoZSBnaXZlbiBhcnJheSBvZiBlbGVtZW50cyB0byBkZXRlY3QgcmVzaXplIGV2ZW50cyBvZi4gU2luZ2xlIGVsZW1lbnQgaXMgYWxzbyB2YWxpZC5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBsaXN0ZW5lciBUaGUgY2FsbGJhY2sgdG8gYmUgZXhlY3V0ZWQgZm9yIGVhY2ggcmVzaXplIGV2ZW50IGZvciBlYWNoIGVsZW1lbnQuXG4gICAgICovXG4gICAgZnVuY3Rpb24gbGlzdGVuVG8ob3B0aW9ucywgZWxlbWVudHMsIGxpc3RlbmVyKSB7XG4gICAgICAgIGZ1bmN0aW9uIG9uUmVzaXplQ2FsbGJhY2soZWxlbWVudCkge1xuICAgICAgICAgICAgdmFyIGxpc3RlbmVycyA9IGV2ZW50TGlzdGVuZXJIYW5kbGVyLmdldChlbGVtZW50KTtcblxuICAgICAgICAgICAgZm9yRWFjaChsaXN0ZW5lcnMsIGZ1bmN0aW9uKGxpc3RlbmVyKSB7XG4gICAgICAgICAgICAgICAgbGlzdGVuZXIoZWxlbWVudCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZ1bmN0aW9uIG9uRWxlbWVudFJlYWR5VG9BZGRMaXN0ZW5lcihjYWxsT25BZGQsIGVsZW1lbnQsIGxpc3RlbmVyKSB7XG4gICAgICAgICAgICBldmVudExpc3RlbmVySGFuZGxlci5hZGQoZWxlbWVudCwgbGlzdGVuZXIpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZihjYWxsT25BZGQpIHtcbiAgICAgICAgICAgICAgICBsaXN0ZW5lcihlbGVtZW50KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vT3B0aW9ucyBvYmplY3QgbWF5IGJlIG9taXR0ZWQuXG4gICAgICAgIGlmKCFsaXN0ZW5lcikge1xuICAgICAgICAgICAgbGlzdGVuZXIgPSBlbGVtZW50cztcbiAgICAgICAgICAgIGVsZW1lbnRzID0gb3B0aW9ucztcbiAgICAgICAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCFlbGVtZW50cykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQXQgbGVhc3Qgb25lIGVsZW1lbnQgcmVxdWlyZWQuXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoIWxpc3RlbmVyKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJMaXN0ZW5lciByZXF1aXJlZC5cIik7XG4gICAgICAgIH1cblxuICAgICAgICBpZihlbGVtZW50cy5sZW5ndGggPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBbZWxlbWVudHNdO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGNhbGxPbkFkZCA9IGdldE9wdGlvbihvcHRpb25zLCBcImNhbGxPbkFkZFwiLCBnbG9iYWxPcHRpb25zLmNhbGxPbkFkZCk7XG5cbiAgICAgICAgZm9yRWFjaChlbGVtZW50cywgZnVuY3Rpb24oZWxlbWVudCkge1xuICAgICAgICAgICAgLy9UaGUgZWxlbWVudCBtYXkgY2hhbmdlIHNpemUgZGlyZWN0bHkgYWZ0ZXIgdGhlIGNhbGwgdG8gbGlzdGVuVG8sIHdoaWNoIHdvdWxkIGJlIHVuYWJsZSB0byBkZXRlY3QgaXQgYmVjYXVzZVxuICAgICAgICAgICAgLy90aGUgYXN5bmMgYWRkaW5nIG9mIHRoZSBvYmplY3QuIEJ5IGNoZWNraW5nIHRoZSBzaXplIGJlZm9yZSBhbmQgYWZ0ZXIsIHRoZSBzaXplIGNoYW5nZSBjYW4gc3RpbGwgYmUgZGV0ZWN0ZWRcbiAgICAgICAgICAgIC8vYW5kIHRoZSBsaXN0ZW5lciBjYW4gYmUgY2FsbGVkIGFjY29yZGluZ2x5LlxuICAgICAgICAgICAgdmFyIHByZVdpZHRoID0gZWxlbWVudC5vZmZzZXRXaWR0aDtcbiAgICAgICAgICAgIHZhciBwcmVIZWlnaHQgPSBlbGVtZW50Lm9mZnNldEhlaWdodDtcblxuICAgICAgICAgICAgaWYoIWVsZW1lbnRVdGlscy5pc0RldGVjdGFibGUoZWxlbWVudCkpIHtcbiAgICAgICAgICAgICAgICAvL1RoZSBlbGVtZW50IGlzIG5vdCBwcmVwYXJlZCB0byBiZSBkZXRlY3RhYmxlLCBzbyBkbyBwcmVwYXJlIGl0IGFuZCBhZGQgYSBsaXN0ZW5lciB0byBpdC5cbiAgICAgICAgICAgICAgICByZXR1cm4gZWxlbWVudFV0aWxzLm1ha2VEZXRlY3RhYmxlKHJlcG9ydGVyLCBlbGVtZW50LCBmdW5jdGlvbihlbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnRVdGlscy5hZGRMaXN0ZW5lcihlbGVtZW50LCBvblJlc2l6ZUNhbGxiYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgb25FbGVtZW50UmVhZHlUb0FkZExpc3RlbmVyKGNhbGxPbkFkZCwgZWxlbWVudCwgbGlzdGVuZXIpO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vT25seSBoZXJlIHRoZSB1bmNhdWdodCByZXNpemUgbWF5IG9jY3VyIChzaW5jZSB0aGlzIGNvZGUgaXMgYXN5bmMpLlxuICAgICAgICAgICAgICAgICAgICAvL0NoZWNrIGlmIHRoZSBzaXplIGlzIHRoZSBzYW1lIGFzIHdoZW4gYWRkaW5nIHRoZSBsaXN0ZW5lci5cbiAgICAgICAgICAgICAgICAgICAgdmFyIHBvc3RXaWR0aCA9IGVsZW1lbnQub2Zmc2V0V2lkdGg7XG4gICAgICAgICAgICAgICAgICAgIHZhciBwb3N0SGVpZ2h0ID0gZWxlbWVudC5vZmZzZXRIZWlnaHQ7XG5cbiAgICAgICAgICAgICAgICAgICAgLy9JZiBjYWxsT25BZGQgaXMgdHJ1ZSwgdGhlbiB0aGUgbGlzdGVuZXIgd2lsbCBoYXZlIGJlZW4gY2FsbGVkIGVpdGhlciB3YXksIHNvIG5vIG5lZWQgdG8gY2FsbCB0aGUgbGlzdGVuZXIgbWFudWFsbHkgdGhlbi5cbiAgICAgICAgICAgICAgICAgICAgaWYoIWNhbGxPbkFkZCAmJiAocHJlV2lkdGggIT09IHBvc3RXaWR0aCB8fCBwcmVIZWlnaHQgIT09IHBvc3RIZWlnaHQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvL1RoZSBlbGVtZW50IHdhcyBjaGFuZ2VkIHdoaWxlIHRoZSBvYmplY3Qgd2FzIGJlaW5nIGFkZGVkLiBDYWxsIHRoZSBsaXN0ZW5lci5cbiAgICAgICAgICAgICAgICAgICAgICAgIGxpc3RlbmVyKGVsZW1lbnQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vVGhlIGVsZW1lbnQgaGFzIGJlZW4gcHJlcGFyZWQgdG8gYmUgZGV0ZWN0YWJsZSBhbmQgaXMgcmVhZHkgdG8gYmUgbGlzdGVuZWQgdG8uXG4gICAgICAgICAgICBvbkVsZW1lbnRSZWFkeVRvQWRkTGlzdGVuZXIoY2FsbE9uQWRkLCBlbGVtZW50LCBsaXN0ZW5lcik7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIGxpc3RlblRvOiBsaXN0ZW5Ub1xuICAgIH07XG59O1xuXG5mdW5jdGlvbiBnZXRPcHRpb24ob3B0aW9ucywgbmFtZSwgZGVmYXVsdFZhbHVlKSB7XG4gICAgdmFyIHZhbHVlID0gb3B0aW9uc1tuYW1lXTtcblxuICAgIGlmKCh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsKSAmJiBkZWZhdWx0VmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICAgIH1cblxuICAgIHJldHVybiB2YWx1ZTtcbn1cbiIsIlwidXNlIHN0cmljdFwiO1xuXG52YXIgZm9yRWFjaCA9IHJlcXVpcmUoXCIuL2NvbGxlY3Rpb24tdXRpbHNcIikuZm9yRWFjaDtcbnZhciBicm93c2VyRGV0ZWN0b3IgPSByZXF1aXJlKFwiLi9icm93c2VyLWRldGVjdG9yXCIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKGlkSGFuZGxlcikge1xuICAgIC8qKlxuICAgICAqIFRlbGxzIGlmIHRoZSBlbGVtZW50IGhhcyBiZWVuIG1hZGUgZGV0ZWN0YWJsZSBhbmQgcmVhZHkgdG8gYmUgbGlzdGVuZWQgZm9yIHJlc2l6ZSBldmVudHMuXG4gICAgICogQHB1YmxpY1xuICAgICAqIEBwYXJhbSB7ZWxlbWVudH0gVGhlIGVsZW1lbnQgdG8gY2hlY2suXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgb3IgZmFsc2UgZGVwZW5kaW5nIG9uIGlmIHRoZSBlbGVtZW50IGlzIGRldGVjdGFibGUgb3Igbm90LlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGlzRGV0ZWN0YWJsZShlbGVtZW50KSB7XG4gICAgICAgIGlmKGJyb3dzZXJEZXRlY3Rvci5pc0lFKDgpKSB7XG4gICAgICAgICAgICAvL0lFIDggZG9lcyBub3QgdXNlIHRoZSBvYmplY3QgbWV0aG9kLlxuICAgICAgICAgICAgLy9DaGVjayBvbmx5IGlmIHRoZSBlbGVtZW50IGhhcyBiZWVuIGdpdmVuIGFuIGlkLlxuICAgICAgICAgICAgcmV0dXJuICEhaWRIYW5kbGVyLmdldChlbGVtZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAhIWdldE9iamVjdChlbGVtZW50KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgcmVzaXplIGV2ZW50IGxpc3RlbmVyIHRvIHRoZSBlbGVtZW50LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBAcGFyYW0ge2VsZW1lbnR9IGVsZW1lbnQgVGhlIGVsZW1lbnQgdGhhdCBzaG91bGQgaGF2ZSB0aGUgbGlzdGVuZXIgYWRkZWQuXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gbGlzdGVuZXIgVGhlIGxpc3RlbmVyIGNhbGxiYWNrIHRvIGJlIGNhbGxlZCBmb3IgZWFjaCByZXNpemUgZXZlbnQgb2YgdGhlIGVsZW1lbnQuIFRoZSBlbGVtZW50IHdpbGwgYmUgZ2l2ZW4gYXMgYSBwYXJhbWV0ZXIgdG8gdGhlIGxpc3RlbmVyIGNhbGxiYWNrLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGFkZExpc3RlbmVyKGVsZW1lbnQsIGxpc3RlbmVyKSB7XG4gICAgICAgIGlmKCFpc0RldGVjdGFibGUoZWxlbWVudCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkVsZW1lbnQgaXMgbm90IGRldGVjdGFibGUuXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgZnVuY3Rpb24gbGlzdGVuZXJQcm94eSgpIHtcbiAgICAgICAgICAgIGxpc3RlbmVyKGVsZW1lbnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYoYnJvd3NlckRldGVjdG9yLmlzSUUoOCkpIHtcbiAgICAgICAgICAgIC8vSUUgOCBkb2VzIG5vdCBzdXBwb3J0IG9iamVjdCwgYnV0IHN1cHBvcnRzIHRoZSByZXNpemUgZXZlbnQgZGlyZWN0bHkgb24gZWxlbWVudHMuXG4gICAgICAgICAgICBlbGVtZW50LmF0dGFjaEV2ZW50KFwib25yZXNpemVcIiwgbGlzdGVuZXJQcm94eSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgb2JqZWN0ID0gZ2V0T2JqZWN0KGVsZW1lbnQpO1xuICAgICAgICAgICAgb2JqZWN0LmNvbnRlbnREb2N1bWVudC5kZWZhdWx0Vmlldy5hZGRFdmVudExpc3RlbmVyKFwicmVzaXplXCIsIGxpc3RlbmVyUHJveHkpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTWFrZXMgYW4gZWxlbWVudCBkZXRlY3RhYmxlIGFuZCByZWFkeSB0byBiZSBsaXN0ZW5lZCBmb3IgcmVzaXplIGV2ZW50cy4gV2lsbCBjYWxsIHRoZSBjYWxsYmFjayB3aGVuIHRoZSBlbGVtZW50IGlzIHJlYWR5IHRvIGJlIGxpc3RlbmVkIGZvciByZXNpemUgY2hhbmdlcy5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7ZWxlbWVudH0gZWxlbWVudCBUaGUgZWxlbWVudCB0byBtYWtlIGRldGVjdGFibGVcbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBjYWxsYmFjayBUaGUgY2FsbGJhY2sgdG8gYmUgY2FsbGVkIHdoZW4gdGhlIGVsZW1lbnQgaXMgcmVhZHkgdG8gYmUgbGlzdGVuZWQgZm9yIHJlc2l6ZSBjaGFuZ2VzLiBXaWxsIGJlIGNhbGxlZCB3aXRoIHRoZSBlbGVtZW50IGFzIGZpcnN0IHBhcmFtZXRlci5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiBtYWtlRGV0ZWN0YWJsZShyZXBvcnRlciwgZWxlbWVudCwgY2FsbGJhY2spIHtcbiAgICAgICAgZnVuY3Rpb24gaW5qZWN0T2JqZWN0KGlkLCBlbGVtZW50LCBjYWxsYmFjaykge1xuICAgICAgICAgICAgdmFyIE9CSkVDVF9TVFlMRSA9IFwiZGlzcGxheTogYmxvY2s7IHBvc2l0aW9uOiBhYnNvbHV0ZTsgdG9wOiAwOyBsZWZ0OiAwOyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBib3JkZXI6IG5vbmU7IHBhZGRpbmc6IDA7IG1hcmdpbjogMDsgb3BhY2l0eTogMDsgei1pbmRleDogLTEwMDA7IHBvaW50ZXItZXZlbnRzOiBub25lO1wiO1xuXG4gICAgICAgICAgICBmdW5jdGlvbiBvbk9iamVjdExvYWQoKSB7XG4gICAgICAgICAgICAgICAgLypqc2hpbnQgdmFsaWR0aGlzOnRydWUgKi9cblxuICAgICAgICAgICAgICAgIC8vQ3JlYXRlIHRoZSBzdHlsZSBlbGVtZW50IHRvIGJlIGFkZGVkIHRvIHRoZSBvYmplY3QuXG4gICAgICAgICAgICAgICAgdmFyIG9iamVjdERvY3VtZW50ID0gdGhpcy5jb250ZW50RG9jdW1lbnQ7XG4gICAgICAgICAgICAgICAgdmFyIHN0eWxlID0gb2JqZWN0RG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInN0eWxlXCIpO1xuICAgICAgICAgICAgICAgIHN0eWxlLmlubmVySFRNTCA9IFwiaHRtbCwgYm9keSB7IG1hcmdpbjogMDsgcGFkZGluZzogMCB9IGRpdiB7IC13ZWJraXQtdHJhbnNpdGlvbjogb3BhY2l0eSAwLjAxczsgLW1zLXRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IC1vLXRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IHRyYW5zaXRpb246IG9wYWNpdHkgMC4wMXM7IG9wYWNpdHk6IDA7IH1cIjtcblxuICAgICAgICAgICAgICAgIC8vVE9ETzogUmVtb3ZlIGFueSBzdHlsZXMgdGhhdCBoYXMgYmVlbiBzZXQgb24gdGhlIG9iamVjdC4gT25seSB0aGUgc3R5bGUgYWJvdmUgc2hvdWxkIGJlIHN0eWxpbmcgdGhlIG9iamVjdC5cblxuICAgICAgICAgICAgICAgIC8vQXBwZW5kIHRoZSBzdHlsZSB0byB0aGUgb2JqZWN0LlxuICAgICAgICAgICAgICAgIG9iamVjdERvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoc3R5bGUpO1xuXG4gICAgICAgICAgICAgICAgLy9UT0RPOiBJcyB0aGlzIG5lZWRlZCBoZXJlP1xuICAgICAgICAgICAgICAgIC8vdGhpcy5zdHlsZS5jc3NUZXh0ID0gT0JKRUNUX1NUWUxFO1xuXG4gICAgICAgICAgICAgICAgLy9Ob3RpZnkgdGhhdCB0aGUgZWxlbWVudCBpcyByZWFkeSB0byBiZSBsaXN0ZW5lZCB0by5cbiAgICAgICAgICAgICAgICBjYWxsYmFjayhlbGVtZW50KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy9UaGUgdGFyZ2V0IGVsZW1lbnQgbmVlZHMgdG8gYmUgcG9zaXRpb25lZCAoZXZlcnl0aGluZyBleGNlcHQgc3RhdGljKSBzbyB0aGUgYWJzb2x1dGUgcG9zaXRpb25lZCBvYmplY3Qgd2lsbCBiZSBwb3NpdGlvbmVkIHJlbGF0aXZlIHRvIHRoZSB0YXJnZXQgZWxlbWVudC5cbiAgICAgICAgICAgIHZhciBzdHlsZSA9IGdldENvbXB1dGVkU3R5bGUoZWxlbWVudCk7XG4gICAgICAgICAgICBpZihzdHlsZS5wb3NpdGlvbiA9PT0gXCJzdGF0aWNcIikge1xuICAgICAgICAgICAgICAgIGVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSBcInJlbGF0aXZlXCI7XG5cbiAgICAgICAgICAgICAgICB2YXIgcmVtb3ZlUmVsYXRpdmVTdHlsZXMgPSBmdW5jdGlvbihyZXBvcnRlciwgZWxlbWVudCwgc3R5bGUsIHByb3BlcnR5KSB7XG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIGdldE51bWVyaWNhbFZhbHVlKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdmFsdWUucmVwbGFjZSgvW14tXFxkXFwuXS9nLCBcIlwiKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHZhciB2YWx1ZSA9IHN0eWxlW3Byb3BlcnR5XTtcblxuICAgICAgICAgICAgICAgICAgICBpZih2YWx1ZSAhPT0gXCJhdXRvXCIgJiYgZ2V0TnVtZXJpY2FsVmFsdWUodmFsdWUpICE9PSBcIjBcIikge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3J0ZXIud2FybihcIkFuIGVsZW1lbnQgdGhhdCBpcyBwb3NpdGlvbmVkIHN0YXRpYyBoYXMgc3R5bGUuXCIgKyBwcm9wZXJ0eSArIFwiPVwiICsgdmFsdWUgKyBcIiB3aGljaCBpcyBpZ25vcmVkIGR1ZSB0byB0aGUgc3RhdGljIHBvc2l0aW9uaW5nLiBUaGUgZWxlbWVudCB3aWxsIG5lZWQgdG8gYmUgcG9zaXRpb25lZCByZWxhdGVpdmUsIHNvIHRoZSBzdHlsZS5cIiArIHByb3BlcnR5ICsgXCIgd2lsbCBiZSBzZXQgdG8gMC4gRWxlbWVudDogXCIsIGVsZW1lbnQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbWVudC5zdHlsZVtwcm9wZXJ0eV0gPSAwO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIC8vQ2hlY2sgc28gdGhhdCB0aGVyZSBhcmUgbm8gYWNjaWRlbnRhbCBzdHlsZXMgdGhhdCB3aWxsIG1ha2UgdGhlIGVsZW1lbnQgc3R5bGVkIGRpZmZlcmVudGx5IG5vdyB0aGF0IGlzIGlzIHJlbGF0aXZlLlxuICAgICAgICAgICAgICAgIC8vSWYgdGhlcmUgYXJlIGFueSwgc2V0IHRoZW0gdG8gMCAodGhpcyBzaG91bGQgYmUgb2theSB3aXRoIHRoZSB1c2VyIHNpbmNlIHRoZSBzdHlsZSBwcm9wZXJ0aWVzIGRpZCBub3RoaW5nIGJlZm9yZSBbc2luY2UgdGhlIGVsZW1lbnQgd2FzIHBvc2l0aW9uZWQgc3RhdGljXSBhbnl3YXkpLlxuICAgICAgICAgICAgICAgIHJlbW92ZVJlbGF0aXZlU3R5bGVzKHJlcG9ydGVyLCBlbGVtZW50LCBzdHlsZSwgXCJ0b3BcIik7XG4gICAgICAgICAgICAgICAgcmVtb3ZlUmVsYXRpdmVTdHlsZXMocmVwb3J0ZXIsIGVsZW1lbnQsIHN0eWxlLCBcInJpZ2h0XCIpO1xuICAgICAgICAgICAgICAgIHJlbW92ZVJlbGF0aXZlU3R5bGVzKHJlcG9ydGVyLCBlbGVtZW50LCBzdHlsZSwgXCJib3R0b21cIik7XG4gICAgICAgICAgICAgICAgcmVtb3ZlUmVsYXRpdmVTdHlsZXMocmVwb3J0ZXIsIGVsZW1lbnQsIHN0eWxlLCBcImxlZnRcIik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vQWRkIGFuIG9iamVjdCBlbGVtZW50IGFzIGEgY2hpbGQgdG8gdGhlIHRhcmdldCBlbGVtZW50IHRoYXQgd2lsbCBiZSBsaXN0ZW5lZCB0byBmb3IgcmVzaXplIGV2ZW50cy5cbiAgICAgICAgICAgIHZhciBvYmplY3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwib2JqZWN0XCIpO1xuICAgICAgICAgICAgb2JqZWN0LnR5cGUgPSBcInRleHQvaHRtbFwiO1xuICAgICAgICAgICAgb2JqZWN0LnN0eWxlLmNzc1RleHQgPSBPQkpFQ1RfU1RZTEU7XG4gICAgICAgICAgICBvYmplY3Qub25sb2FkID0gb25PYmplY3RMb2FkO1xuICAgICAgICAgICAgb2JqZWN0LnNldEF0dHJpYnV0ZShcImVyZC1vYmplY3QtaWRcIiwgaWQpO1xuXG4gICAgICAgICAgICAvL1NhZmFyaTogVGhpcyBtdXN0IG9jY3VyIGJlZm9yZSBhZGRpbmcgdGhlIG9iamVjdCB0byB0aGUgRE9NLlxuICAgICAgICAgICAgLy9JRTogRG9lcyBub3QgbGlrZSB0aGF0IHRoaXMgaGFwcGVucyBiZWZvcmUsIGV2ZW4gaWYgaXQgaXMgYWxzbyBhZGRlZCBhZnRlci5cbiAgICAgICAgICAgIGlmKCFicm93c2VyRGV0ZWN0b3IuaXNJRSgpKSB7XG4gICAgICAgICAgICAgICAgb2JqZWN0LmRhdGEgPSBcImFib3V0OmJsYW5rXCI7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGVsZW1lbnQuYXBwZW5kQ2hpbGQob2JqZWN0KTtcblxuICAgICAgICAgICAgLy9JRTogVGhpcyBtdXN0IG9jY3VyIGFmdGVyIGFkZGluZyB0aGUgb2JqZWN0IHRvIHRoZSBET00uXG4gICAgICAgICAgICBpZihicm93c2VyRGV0ZWN0b3IuaXNJRSgpKSB7XG4gICAgICAgICAgICAgICAgb2JqZWN0LmRhdGEgPSBcImFib3V0OmJsYW5rXCI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvL0NyZWF0ZSBhbiB1bmlxdWUgZXJkLXRhcmdldC1pZCBmb3IgdGhlIHRhcmdldCBlbGVtZW50LCBzbyB0aGF0IGV2ZW50IGxpc3RlbmVycyBjYW4gYmUgaWRlbnRpZmllZCB0byB0aGlzIGVsZW1lbnQuXG4gICAgICAgIHZhciBpZCA9IGlkSGFuZGxlci5zZXQoZWxlbWVudCk7XG5cbiAgICAgICAgaWYoYnJvd3NlckRldGVjdG9yLmlzSUUoOCkpIHtcbiAgICAgICAgICAgIC8vSUUgOCBkb2VzIG5vdCBzdXBwb3J0IG9iamVjdHMgcHJvcGVybHkuIEx1Y2tpbHkgdGhleSBkbyBzdXBwb3J0IHRoZSByZXNpemUgZXZlbnQuXG4gICAgICAgICAgICAvL1NvIGRvIG5vdCBpbmplY3QgdGhlIG9iamVjdCBhbmQgbm90aWZ5IHRoYXQgdGhlIGVsZW1lbnQgaXMgYWxyZWFkeSByZWFkeSB0byBiZSBsaXN0ZW5lZCB0by5cbiAgICAgICAgICAgIC8vVGhlIGV2ZW50IGhhbmRsZXIgZm9yIHRoZSByZXNpemUgZXZlbnQgaXMgYXR0YWNoZWQgaW4gdGhlIHV0aWxzLmFkZExpc3RlbmVyIGluc3RlYWQuXG4gICAgICAgICAgICBjYWxsYmFjayhlbGVtZW50KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGluamVjdE9iamVjdChpZCwgZWxlbWVudCwgY2FsbGJhY2spO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY2hpbGQgb2JqZWN0IG9mIHRoZSB0YXJnZXQgZWxlbWVudC5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7ZWxlbWVudH0gZWxlbWVudCBUaGUgdGFyZ2V0IGVsZW1lbnQuXG4gICAgICogQHJldHVybnMgVGhlIG9iamVjdCBlbGVtZW50IG9mIHRoZSB0YXJnZXQuXG4gICAgICovXG4gICAgZnVuY3Rpb24gZ2V0T2JqZWN0KGVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIGZvckVhY2goZWxlbWVudC5jaGlsZHJlbiwgZnVuY3Rpb24oY2hpbGQpIHtcbiAgICAgICAgICAgIGlmKGNoaWxkLmhhc0F0dHJpYnV0ZShcImVyZC1vYmplY3QtaWRcIikpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY2hpbGQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIGlzRGV0ZWN0YWJsZTogaXNEZXRlY3RhYmxlLFxuICAgICAgICBtYWtlRGV0ZWN0YWJsZTogbWFrZURldGVjdGFibGUsXG4gICAgICAgIGFkZExpc3RlbmVyOiBhZGRMaXN0ZW5lcixcbiAgICB9O1xufTtcbiIsIlwidXNlIHN0cmljdFwiO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBpZENvdW50ID0gMTtcblxuICAgIC8qKlxuICAgICAqIEdlbmVyYXRlcyBhIG5ldyB1bmlxdWUgaWQgaW4gdGhlIGNvbnRleHQuXG4gICAgICogQHB1YmxpY1xuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IEEgdW5pcXVlIGlkIGluIHRoZSBjb250ZXh0LlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGdlbmVyYXRlKCkge1xuICAgICAgICByZXR1cm4gaWRDb3VudCsrO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIGdlbmVyYXRlOiBnZW5lcmF0ZVxuICAgIH07XG59O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oaWRHZW5lcmF0b3IpIHtcblxuICAgIC8qKlxuICAgICAqIEdldHMgdGhlIHJlc2l6ZSBkZXRlY3RvciBpZCBvZiB0aGUgZWxlbWVudC5cbiAgICAgKiBAcHVibGljXG4gICAgICogQHBhcmFtIHtlbGVtZW50fSBUaGUgdGFyZ2V0IGVsZW1lbnQgdG8gZ2V0IHRoZSBpZCBvZi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfG51bWJlcn0gVGhlIGlkIG9mIHRoZSBlbGVtZW50LlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGdldElkKGVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQuZ2V0QXR0cmlidXRlKFwiZXJkLXRhcmdldC1pZFwiKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSByZXNpemUgZGV0ZWN0b3IgaWQgb2YgdGhlIGVsZW1lbnQuXG4gICAgICogQHB1YmxpY1xuICAgICAqIEBwYXJhbSB7ZWxlbWVudH0gVGhlIHRhcmdldCBlbGVtZW50IHRvIHNldCB0aGUgaWQgdG8uXG4gICAgICogQHBhcmFtIHtzdHJpbmc/fSBBbiBvcHRpb25hbCBpZCB0byBzZXQgdG8gdGhlIGVsZW1lbnQuIElmIG5vdCBzcGVjaWZpZWQsIGFuIGlkIHdpbGwgYmUgZ2VuZXJhdGVkLiBBbGwgaWQncyBtdXN0IGJlIHVuaXF1ZS5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfG51bWJlcn0gVGhlIGlkIG9mIHRoZSBlbGVtZW50LlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHNldElkKGVsZW1lbnQsIGlkKSB7XG4gICAgICAgIGlmKCFpZCAmJiBpZCAhPT0gMCkge1xuICAgICAgICAgICAgLy9OdW1iZXIgc2hvdWxkIGJlIGdlbmVyYXRlZC5cbiAgICAgICAgICAgIGlkID0gaWRHZW5lcmF0b3IuZ2VuZXJhdGUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKFwiZXJkLXRhcmdldC1pZFwiLCBpZCk7XG5cbiAgICAgICAgcmV0dXJuIGlkO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAgIGdldDogZ2V0SWQsXG4gICAgICAgIHNldDogc2V0SWRcbiAgICB9O1xufTtcbiIsIlwidXNlIHN0cmljdFwiO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKGlkSGFuZGxlcikge1xuICAgIHZhciBldmVudExpc3RlbmVycyA9IHt9O1xuXG4gICAgLyoqXG4gICAgICogR2V0cyBhbGwgbGlzdGVuZXJzIGZvciB0aGUgZ2l2ZW4gZWxlbWVudC5cbiAgICAgKiBAcHVibGljXG4gICAgICogQHBhcmFtIHtlbGVtZW50fSBlbGVtZW50IFRoZSBlbGVtZW50IHRvIGdldCBhbGwgbGlzdGVuZXJzIGZvci5cbiAgICAgKiBAcmV0dXJucyBBbGwgbGlzdGVuZXJzIGZvciB0aGUgZ2l2ZW4gZWxlbWVudC5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiBnZXRMaXN0ZW5lcnMoZWxlbWVudCkge1xuICAgICAgICByZXR1cm4gZXZlbnRMaXN0ZW5lcnNbaWRIYW5kbGVyLmdldChlbGVtZW50KV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU3RvcmVzIHRoZSBnaXZlbiBsaXN0ZW5lciBmb3IgdGhlIGdpdmVuIGVsZW1lbnQuIFdpbGwgbm90IGFjdHVhbGx5IGFkZCB0aGUgbGlzdGVuZXIgdG8gdGhlIGVsZW1lbnQuXG4gICAgICogQHB1YmxpY1xuICAgICAqIEBwYXJhbSB7ZWxlbWVudH0gZWxlbWVudCBUaGUgZWxlbWVudCB0aGF0IHNob3VsZCBoYXZlIHRoZSBsaXN0ZW5lciBhZGRlZC5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBsaXN0ZW5lciBUaGUgY2FsbGJhY2sgdGhhdCB0aGUgZWxlbWVudCBoYXMgYWRkZWQuXG4gICAgICovXG4gICAgZnVuY3Rpb24gYWRkTGlzdGVuZXIoZWxlbWVudCwgbGlzdGVuZXIpIHtcbiAgICAgICAgdmFyIGlkID0gaWRIYW5kbGVyLmdldChlbGVtZW50KTtcblxuICAgICAgICBpZighZXZlbnRMaXN0ZW5lcnNbaWRdKSB7XG4gICAgICAgICAgICBldmVudExpc3RlbmVyc1tpZF0gPSBbXTtcbiAgICAgICAgfVxuXG4gICAgICAgIGV2ZW50TGlzdGVuZXJzW2lkXS5wdXNoKGxpc3RlbmVyKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBnZXQ6IGdldExpc3RlbmVycyxcbiAgICAgICAgYWRkOiBhZGRMaXN0ZW5lclxuICAgIH07XG59O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbi8qIGdsb2JhbCBjb25zb2xlOiBmYWxzZSAqL1xuXG4vKipcbiAqIFJlcG9ydGVyIHRoYXQgaGFuZGxlcyB0aGUgcmVwb3J0aW5nIG9mIGxvZ3MsIHdhcm5pbmdzIGFuZCBlcnJvcnMuXG4gKiBAcHVibGljXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHF1aWV0IFRlbGxzIGlmIHRoZSByZXBvcnRlciBzaG91bGQgYmUgcXVpZXQgb3Igbm90LlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKHF1aWV0KSB7XG4gICAgdmFyIG5vb3AgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vRG9lcyBub3RoaW5nLlxuICAgIH07XG5cbiAgICB2YXIgcmVwb3J0ZXIgPSB7XG4gICAgICAgIGxvZzogbm9vcCxcbiAgICAgICAgd2Fybjogbm9vcCxcbiAgICAgICAgZXJyb3I6IG5vb3BcbiAgICB9O1xuXG4gICAgaWYoIXF1aWV0ICYmIHdpbmRvdy5jb25zb2xlKSB7XG4gICAgICAgIHJlcG9ydGVyLmxvZyA9IGNvbnNvbGUubG9nO1xuICAgICAgICByZXBvcnRlci53YXJuID0gY29uc29sZS53YXJuO1xuICAgICAgICByZXBvcnRlci5lcnJvciA9IGNvbnNvbGUuZXJyb3I7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlcG9ydGVyO1xufTsiXX0=
/*!
* element-resize-detector 0.1.4 (2015-02-02, 12:04)
* element-resize-detector 0.2.1 (2015-03-23, 19:27)
* https://github.com/wnr/element-resize-detector

@@ -7,5 +7,42 @@ * Licensed under MIT

!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.elementResizeDetectorMaker=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.elementResizeDetectorMaker = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
var detector = module.exports = {};
detector.isIE = function(version) {
function isAnyIeVersion() {
var agent = navigator.userAgent.toLowerCase();
return agent.indexOf("msie") !== -1 || agent.indexOf("trident") !== -1;
}
if(!isAnyIeVersion()) {
return false;
}
if(!version) {
return true;
}
//Shamelessly stolen from https://gist.github.com/padolsey/527683
var ieVersion = (function(){
var undef,
v = 3,
div = document.createElement("div"),
all = div.getElementsByTagName("i");
do {
div.innerHTML = "<!--[if gt IE " + (++v) + "]><i></i><![endif]-->";
}
while (all[0]);
return v > 4 ? v : undef;
}());
return version === ieVersion;
};
},{}],2:[function(require,module,exports){
"use strict";
var utils = module.exports = {};

@@ -29,3 +66,3 @@

},{}],2:[function(require,module,exports){
},{}],3:[function(require,module,exports){
//Heavily inspired by http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/

@@ -36,29 +73,100 @@

var forEach = require("./collection-utils").forEach;
var elementUtils = require("./element-utils");
var elementUtilsMaker = require("./element-utils");
var listenerHandlerMaker = require("./listener-handler");
var idGeneratorMaker = require("./id-generator");
var listenerHandlerMaker = require("./listener-handler");
var idHandlerMaker = require("./id-handler");
var reporterMaker = require("./reporter");
/**
* @typedef idHandler
* @type {object}
* @property {function} get Gets the resize detector id of the element.
* @property {function} set Generate and sets the resize detector id of the element.
*/
/**
* @typedef Options
* @type {object}
* @property {boolean} callOnAdd Determines if listeners should be called when they are getting added.
Default is true. If true, the listener is guaranteed to be called when it has been added.
If false, the listener will not be guarenteed to be called when it has been added (does not prevent it from being called).
* @property {idHandler} idHandler A custom id handler that is responsible for generating, setting and retrieving id's for elements.
If not provided, a default id handler will be used.
* @property {reporter} reporter A custom reporter that handles reporting logs, warnings and errors.
If not provided, a default id handler will be used.
If set to false, then nothing will be reported.
*/
/**
* Creates an element resize detector instance.
* @public
* @param {Options?} options Optional global options object that will decide how this instance will work.
*/
module.exports = function(options) {
options = options || {};
var allowMultipleListeners = options.allowMultipleListeners === undefined ? true : false;
var eventListenerHandler = listenerHandlerMaker();
var idGenerator = idGeneratorMaker();
//Options to be used as default for the listenTo function.
var globalOptions = {};
globalOptions.callOnAdd = !!getOption(options, "callOnAdd", true);
//idHandler is currently not an option to the listenTo function, so it should not be added to globalOptions.
var idHandler = options.idHandler;
if(!idHandler) {
var idGenerator = idGeneratorMaker();
var defaultIdHandler = idHandlerMaker(idGenerator);
idHandler = defaultIdHandler;
}
//reporter is currently not an option to the listenTo function, so it should not be added to globalOptions.
var reporter = options.reporter;
if(!reporter) {
//If options.reporter is false, then the reporter should be quiet.
var quiet = reporter === false;
reporter = reporterMaker(quiet);
}
var eventListenerHandler = listenerHandlerMaker(idHandler);
var elementUtils = elementUtilsMaker(idHandler);
/**
* Makes the given elements resize-detectable and starts listening to resize events on the elements. Calls the event callback for each event for each element.
* @public
* @param {Options?} options Optional options object. These options will override the global options. Some options may not be overriden, such as idHandler.
* @param {element[]|element} elements The given array of elements to detect resize events of. Single element is also valid.
* @param {function} listener The callback to be executed for each resize event for each element.
*/
function listenTo(elements, listener) {
function isListenedTo(element) {
return elementUtils.isDetectable(element) && eventListenerHandler.get(element).length;
function listenTo(options, elements, listener) {
function onResizeCallback(element) {
var listeners = eventListenerHandler.get(element);
forEach(listeners, function(listener) {
listener(element);
});
}
function addListener(element, listener) {
elementUtils.addListener(element, listener);
function onElementReadyToAddListener(callOnAdd, element, listener) {
eventListenerHandler.add(element, listener);
if(callOnAdd) {
listener(element);
}
}
//Options object may be omitted.
if(!listener) {
listener = elements;
elements = options;
options = {};
}
if(!elements) {
throw new Error("At least one element required.");
}
if(!listener) {
throw new Error("Listener required.");
}
if(elements.length === undefined) {

@@ -68,8 +176,27 @@ elements = [elements];

var callOnAdd = getOption(options, "callOnAdd", globalOptions.callOnAdd);
forEach(elements, function(element) {
//The element may change size directly after the call to listenTo, which would be unable to detect it because
//the async adding of the object. By checking the size before and after, the size change can still be detected
//and the listener can be called accordingly.
var preWidth = element.offsetWidth;
var preHeight = element.offsetHeight;
if(!elementUtils.isDetectable(element)) {
//The element is not prepared to be detectable, so do prepare it and add a listener to it.
var id = idGenerator.newId();
return elementUtils.makeDetectable(element, id, function(element) {
addListener(element, listener);
return elementUtils.makeDetectable(reporter, element, function(element) {
elementUtils.addListener(element, onResizeCallback);
onElementReadyToAddListener(callOnAdd, element, listener);
//Only here the uncaught resize may occur (since this code is async).
//Check if the size is the same as when adding the listener.
var postWidth = element.offsetWidth;
var postHeight = element.offsetHeight;
//If callOnAdd is true, then the listener will have been called either way, so no need to call the listener manually then.
if(!callOnAdd && (preWidth !== postWidth || preHeight !== postHeight)) {
//The element was changed while the object was being added. Call the listener.
listener(element);
}
});

@@ -79,10 +206,3 @@ }

//The element has been prepared to be detectable and is ready to be listened to.
if(isListenedTo(element) && !allowMultipleListeners) {
//Since there is a listener and we disallow multiple listeners no listener should be added.
return;
}
//Since multiple listeners is allowed, another listener is added to the element.
return addListener(element, listener);
onElementReadyToAddListener(callOnAdd, element, listener);
});

@@ -96,105 +216,171 @@ }

},{"./collection-utils":1,"./element-utils":3,"./id-generator":4,"./listener-handler":5}],3:[function(require,module,exports){
function getOption(options, name, defaultValue) {
var value = options[name];
if((value === undefined || value === null) && defaultValue !== undefined) {
return defaultValue;
}
return value;
}
},{"./collection-utils":2,"./element-utils":4,"./id-generator":5,"./id-handler":6,"./listener-handler":7,"./reporter":8}],4:[function(require,module,exports){
"use strict";
var forEach = require("./collection-utils").forEach;
var browserDetector = require("./browser-detector");
var utils = module.exports = {};
module.exports = function(idHandler) {
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
function isDetectable(element) {
if(browserDetector.isIE(8)) {
//IE 8 does not use the object method.
//Check only if the element has been given an id.
return !!idHandler.get(element);
}
/**
* Gets the elq id of the element.
* @public
* @param {element} The target element to get the id of.
* @returns {string} The id of the element.
*/
utils.getId = function(element) {
return element.getAttribute("elq-target-id");
};
return !!getObject(element);
}
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
utils.isDetectable = function(element) {
return getObject(element);
};
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
function addListener(element, listener) {
if(!isDetectable(element)) {
throw new Error("Element is not detectable.");
}
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
utils.addListener = function(element, listener) {
if(!utils.isDetectable(element)) {
throw new Error("Element is not detectable.");
function listenerProxy() {
listener(element);
}
if(browserDetector.isIE(8)) {
//IE 8 does not support object, but supports the resize event directly on elements.
element.attachEvent("onresize", listenerProxy);
} else {
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", listenerProxy);
}
}
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", function() {
listener(element);
});
};
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
function makeDetectable(reporter, element, callback) {
function injectObject(id, element, callback) {
var OBJECT_STYLE = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {*} id An unique id in the context of all detectable elements.
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
utils.makeDetectable = function(element, id, callback) {
function onObjectLoad() {
/*jshint validthis:true */
function onObjectLoad() {
/*jshint validthis:true */
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//Append the style to the object.
objectDocument.head.appendChild(style);
//Append the style to the object.
objectDocument.head.appendChild(style);
this.style.cssText = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
//TODO: Is this needed here?
//this.style.cssText = OBJECT_STYLE;
//Notify that the element is ready to be listened to.
callback(element);
}
//Notify that the element is ready to be listened to.
callback(element);
}
//Create an unique elq-target-id for the target element, so that event listeners can be identified to this element.
element.setAttribute("elq-target-id", id);
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
var style = getComputedStyle(element);
if(style.position === "static") {
element.style.position = "relative";
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
if(getComputedStyle(element).position === "static") {
element.style.position = "relative";
var removeRelativeStyles = function(reporter, element, style, property) {
function getNumericalValue(value) {
return value.replace(/[^-\d\.]/g, "");
}
var value = style[property];
if(value !== "auto" && getNumericalValue(value) !== "0") {
reporter.warn("An element that is positioned static has style." + property + "=" + value + " which is ignored due to the static positioning. The element will need to be positioned relateive, so the style." + property + " will be set to 0. Element: ", element);
element.style[property] = 0;
}
};
//Check so that there are no accidental styles that will make the element styled differently now that is is relative.
//If there are any, set them to 0 (this should be okay with the user since the style properties did nothing before [since the element was positioned static] anyway).
removeRelativeStyles(reporter, element, style, "top");
removeRelativeStyles(reporter, element, style, "right");
removeRelativeStyles(reporter, element, style, "bottom");
removeRelativeStyles(reporter, element, style, "left");
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.style.cssText = OBJECT_STYLE;
object.onload = onObjectLoad;
object.setAttribute("erd-object-id", id);
//Safari: This must occur before adding the object to the DOM.
//IE: Does not like that this happens before, even if it is also added after.
if(!browserDetector.isIE()) {
object.data = "about:blank";
}
element.appendChild(object);
//IE: This must occur after adding the object to the DOM.
if(browserDetector.isIE()) {
object.data = "about:blank";
}
}
//Create an unique erd-target-id for the target element, so that event listeners can be identified to this element.
var id = idHandler.set(element);
if(browserDetector.isIE(8)) {
//IE 8 does not support objects properly. Luckily they do support the resize event.
//So do not inject the object and notify that the element is already ready to be listened to.
//The event handler for the resize event is attached in the utils.addListener instead.
callback(element);
} else {
injectObject(id, element, callback);
}
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.data = "about:blank";
object.onload = onObjectLoad;
object.setAttribute("elq-object-id", id);
element.appendChild(object);
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("erd-object-id")) {
return child;
}
});
}
return {
isDetectable: isDetectable,
makeDetectable: makeDetectable,
addListener: addListener,
};
};
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("elq-object-id")) {
return child;
}
});
}
},{"./collection-utils":1}],4:[function(require,module,exports){
},{"./browser-detector":1,"./collection-utils":2}],5:[function(require,module,exports){
"use strict";

@@ -210,3 +396,3 @@

*/
function newId() {
function generate() {
return idCount++;

@@ -216,12 +402,49 @@ }

return {
newId: newId
generate: generate
};
};
},{}],5:[function(require,module,exports){
},{}],6:[function(require,module,exports){
"use strict";
var elementUtils = require("./element-utils");
module.exports = function(idGenerator) {
module.exports = function() {
/**
* Gets the resize detector id of the element.
* @public
* @param {element} The target element to get the id of.
* @returns {string|number} The id of the element.
*/
function getId(element) {
return element.getAttribute("erd-target-id");
}
/**
* Sets the resize detector id of the element.
* @public
* @param {element} The target element to set the id to.
* @param {string?} An optional id to set to the element. If not specified, an id will be generated. All id's must be unique.
* @returns {string|number} The id of the element.
*/
function setId(element, id) {
if(!id && id !== 0) {
//Number should be generated.
id = idGenerator.generate();
}
element.setAttribute("erd-target-id", id);
return id;
}
return {
get: getId,
set: setId
};
};
},{}],7:[function(require,module,exports){
"use strict";
module.exports = function(idHandler) {
var eventListeners = {};

@@ -236,3 +459,3 @@

function getListeners(element) {
return eventListeners[elementUtils.getId(element)];
return eventListeners[idHandler.get(element)];
}

@@ -247,3 +470,3 @@

function addListener(element, listener) {
var id = elementUtils.getId(element);
var id = idHandler.get(element);

@@ -263,3 +486,32 @@ if(!eventListeners[id]) {

},{"./element-utils":3}]},{},[2])(2)
},{}],8:[function(require,module,exports){
"use strict";
/* global console: false */
/**
* Reporter that handles the reporting of logs, warnings and errors.
* @public
* @param {boolean} quiet Tells if the reporter should be quiet or not.
*/
module.exports = function(quiet) {
var noop = function () {
//Does nothing.
};
var reporter = {
log: noop,
warn: noop,
error: noop
};
if(!quiet && window.console) {
reporter.log = console.log;
reporter.warn = console.warn;
reporter.error = console.error;
}
return reporter;
};
},{}]},{},[3])(3)
});
/*!
* element-resize-detector 0.1.4 (2015-02-02, 12:04)
* element-resize-detector 0.2.1 (2015-03-23, 19:27)
* https://github.com/wnr/element-resize-detector

@@ -7,2 +7,2 @@ * Licensed under MIT

!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.elementResizeDetectorMaker=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b){"use strict";var c=b.exports={};c.forEach=function(a,b){for(var c=0;c<a.length;c++){var d=b(a[c]);if(d)return d}}},{}],2:[function(a,b){"use strict";var c=a("./collection-utils").forEach,d=a("./element-utils"),e=a("./id-generator"),f=a("./listener-handler");b.exports=function(a){function b(a,b){function e(a){return d.isDetectable(a)&&h.get(a).length}function f(a,b){d.addListener(a,b),h.add(a,b)}void 0===a.length&&(a=[a]),c(a,function(a){if(!d.isDetectable(a)){var c=i.newId();return d.makeDetectable(a,c,function(a){f(a,b)})}if(!e(a)||g)return f(a,b)})}a=a||{};var g=void 0===a.allowMultipleListeners?!0:!1,h=f(),i=e();return{listenTo:b}}},{"./collection-utils":1,"./element-utils":3,"./id-generator":4,"./listener-handler":5}],3:[function(a,b){"use strict";function c(a){return d(a.children,function(a){return a.hasAttribute("elq-object-id")?a:void 0})}var d=a("./collection-utils").forEach,e=b.exports={};e.getId=function(a){return a.getAttribute("elq-target-id")},e.isDetectable=function(a){return c(a)},e.addListener=function(a,b){if(!e.isDetectable(a))throw new Error("Element is not detectable.");var d=c(a);d.contentDocument.defaultView.addEventListener("resize",function(){b(a)})},e.makeDetectable=function(a,b,c){function d(){var b=this.contentDocument,d=b.createElement("style");d.innerHTML="html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }",b.head.appendChild(d),this.style.cssText="display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;",c(a)}a.setAttribute("elq-target-id",b),"static"===getComputedStyle(a).position&&(a.style.position="relative");var e=document.createElement("object");e.type="text/html",e.data="about:blank",e.onload=d,e.setAttribute("elq-object-id",b),a.appendChild(e)}},{"./collection-utils":1}],4:[function(a,b){"use strict";b.exports=function(){function a(){return b++}var b=1;return{newId:a}}},{}],5:[function(a,b){"use strict";var c=a("./element-utils");b.exports=function(){function a(a){return d[c.getId(a)]}function b(a,b){var e=c.getId(a);d[e]||(d[e]=[]),d[e].push(b)}var d={};return{get:a,add:b}}},{"./element-utils":3}]},{},[2])(2)});
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.elementResizeDetectorMaker=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b){"use strict";var c=b.exports={};c.isIE=function(a){function b(){var a=navigator.userAgent.toLowerCase();return-1!==a.indexOf("msie")||-1!==a.indexOf("trident")}if(!b())return!1;if(!a)return!0;var c=function(){var a,b=3,c=document.createElement("div"),d=c.getElementsByTagName("i");do c.innerHTML="<!--[if gt IE "+ ++b+"]><i></i><![endif]-->";while(d[0]);return b>4?b:a}();return a===c}},{}],2:[function(a,b){"use strict";var c=b.exports={};c.forEach=function(a,b){for(var c=0;c<a.length;c++){var d=b(a[c]);if(d)return d}}},{}],3:[function(a,b){"use strict";function c(a,b,c){var d=a[b];return void 0!==d&&null!==d||void 0===c?d:c}var d=a("./collection-utils").forEach,e=a("./element-utils"),f=a("./listener-handler"),g=a("./id-generator"),h=a("./id-handler"),i=a("./reporter");b.exports=function(a){function b(a,b,e){function f(a){var b=p.get(a);d(b,function(b){b(a)})}function g(a,b,c){p.add(b,c),a&&c(b)}if(e||(e=b,b=a,a={}),!b)throw new Error("At least one element required.");if(!e)throw new Error("Listener required.");void 0===b.length&&(b=[b]);var h=c(a,"callOnAdd",j.callOnAdd);d(b,function(a){var b=a.offsetWidth,c=a.offsetHeight;return q.isDetectable(a)?void g(h,a,e):q.makeDetectable(n,a,function(a){q.addListener(a,f),g(h,a,e);var d=a.offsetWidth,i=a.offsetHeight;h||b===d&&c===i||e(a)})})}a=a||{};var j={};j.callOnAdd=!!c(a,"callOnAdd",!0);var k=a.idHandler;if(!k){var l=g(),m=h(l);k=m}var n=a.reporter;if(!n){var o=n===!1;n=i(o)}var p=f(k),q=e(k);return{listenTo:b}}},{"./collection-utils":2,"./element-utils":4,"./id-generator":5,"./id-handler":6,"./listener-handler":7,"./reporter":8}],4:[function(a,b){"use strict";var c=a("./collection-utils").forEach,d=a("./browser-detector");b.exports=function(a){function b(b){return d.isIE(8)?!!a.get(b):!!g(b)}function e(a,c){function e(){c(a)}if(!b(a))throw new Error("Element is not detectable.");if(d.isIE(8))a.attachEvent("onresize",e);else{var f=g(a);f.contentDocument.defaultView.addEventListener("resize",e)}}function f(b,c,e){function f(a,c,e){function f(){var a=this.contentDocument,b=a.createElement("style");b.innerHTML="html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }",a.head.appendChild(b),e(c)}var g="display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;",h=getComputedStyle(c);if("static"===h.position){c.style.position="relative";var i=function(a,b,c,d){function e(a){return a.replace(/[^-\d\.]/g,"")}var f=c[d];"auto"!==f&&"0"!==e(f)&&(a.warn("An element that is positioned static has style."+d+"="+f+" which is ignored due to the static positioning. The element will need to be positioned relateive, so the style."+d+" will be set to 0. Element: ",b),b.style[d]=0)};i(b,c,h,"top"),i(b,c,h,"right"),i(b,c,h,"bottom"),i(b,c,h,"left")}var j=document.createElement("object");j.type="text/html",j.style.cssText=g,j.onload=f,j.setAttribute("erd-object-id",a),d.isIE()||(j.data="about:blank"),c.appendChild(j),d.isIE()&&(j.data="about:blank")}var g=a.set(c);d.isIE(8)?e(c):f(g,c,e)}function g(a){return c(a.children,function(a){return a.hasAttribute("erd-object-id")?a:void 0})}return{isDetectable:b,makeDetectable:f,addListener:e}}},{"./browser-detector":1,"./collection-utils":2}],5:[function(a,b){"use strict";b.exports=function(){function a(){return b++}var b=1;return{generate:a}}},{}],6:[function(a,b){"use strict";b.exports=function(a){function b(a){return a.getAttribute("erd-target-id")}function c(b,c){return c||0===c||(c=a.generate()),b.setAttribute("erd-target-id",c),c}return{get:b,set:c}}},{}],7:[function(a,b){"use strict";b.exports=function(a){function b(b){return d[a.get(b)]}function c(b,c){var e=a.get(b);d[e]||(d[e]=[]),d[e].push(c)}var d={};return{get:b,add:c}}},{}],8:[function(a,b){"use strict";b.exports=function(a){var b=function(){},c={log:b,warn:b,error:b};return!a&&window.console&&(c.log=console.log,c.warn=console.warn,c.error=console.error),c}},{}]},{},[3])(3)});

@@ -0,3 +1,46 @@

/* global process:false */
"use strict";
var _ = require("lodash");
var sauceConnectLauncher = require("sauce-connect-launcher");
function registerSauceBrowsers(config, sauceBrowsers, configFile) {
function capitalize(string) {
if(!string.charAt) {
return string;
}
return string.charAt(0).toUpperCase() + string.slice(1);
}
var karma = config.karma;
var tasks = [];
var formatName = function(result, part) {
return result + capitalize(part);
};
for(var key in sauceBrowsers) {
if(sauceBrowsers.hasOwnProperty(key)) {
var parts = key.toLowerCase().split("_");
var name = _.reduce(parts, formatName, "sauce");
var configObject = {
configFile: configFile,
options: {
browsers: sauceBrowsers[key]
}
};
karma[name] = configObject;
tasks.push("karma:" + name);
}
}
return tasks;
}
module.exports = function(grunt) {

@@ -62,5 +105,53 @@ require("load-grunt-tasks")(grunt);

}
}
},
karma: {
local: {
configFile: "karma.conf.js",
options: {
browsers: [
"Chrome",
"Safari",
"Firefox",
//"IE8 - Win7",
//"IE10 - Win7",
//"IE11 - Win8.1"
],
singleRun: true
}
}
},
"sauce_connect": {
options: {
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
verbose: true,
build: process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER,
testName: "element-resize-detector"
},
tunnel: {}
}
};
var sauceBrowsers = [
"SL_CHROME_LATEST_OSX", "SL_CHROME_LATEST_WINDOWS", "SL_CHROME_LATEST_LINUX",
"SL_FIREFOX_LATEST_OSX", "SL_FIREFOX_LATEST_WINDOWS", "SL_FIREFOX_LATEST_LINUX",
"SL_SAFARI_LATEST_OSX", "SL_SAFARI_LATEST_WINDOWS",
"SL_OPERA_LATEST_WINDOWS", "SL_OPERA_LATEST_LINUX",
"SL_IE_LATEST_WINDOWS", "SL_IE_10_WINDOWS", "SL_IE_9_WINDOWS", "SL_IE_8_WINDOWS",
"SL_IOS_LATEST_IPHONE", "SL_IOS_LATEST_IPAD",
"SL_IOS_7_IPHONE", "SL_IOS_7_IPAD"
];
function batchSauceBrowsers(browsers, batchSize) {
var number = 1;
var batchMap = {};
_.forEach(_.chunk(browsers, batchSize), function(chunk) {
batchMap["sauceBrowserChunk" + number++] = chunk;
});
return batchMap;
}
var NUM_PARALLEL_BROWSERS = 3;
var sauceBrowserTasks = registerSauceBrowsers(config, batchSauceBrowsers(sauceBrowsers, NUM_PARALLEL_BROWSERS), "karma.sauce.conf.js");
grunt.initConfig(config);

@@ -75,5 +166,61 @@

grunt.registerTask("test:style", ["jshint"]);
grunt.registerTask("test", ["test:style", "build:dev"]); //TODO
grunt.registerTask("test:sauce", ["build"].concat(sauceBrowserTasks));
grunt.registerTask("test", ["test:style", "build:dev", "karma:local"]);
grunt.registerTask("ci", ["test:style", "sauceConnectTunnel", "test:sauce"]);
grunt.registerTask("default", ["test"]);
var sauceConnectTunnel = {};
grunt.registerTask("sauceConnectTunnel", "Starts a sauce connect tunnel", function(keepAlive) {
if(!process.env.SAUCE_USERNAME) {
grunt.log.error("env SAUCE_USERNAME needs to be set.");
return false;
}
if(!process.env.SAUCE_ACCESS_KEY) {
grunt.log.error("env SAUCE_ACCESS_KEY needs to be set.");
return false;
}
var done = this.async();
sauceConnectLauncher({
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
logger: grunt.log.writeln,
verbose: true,
logfile: "sauce-connect.log"
}, function (err, sauceConnectProcess) {
function stop() {
grunt.log.writeln("Stopping...");
sauceConnectTunnel.process.close(function() {
grunt.log.writeln("Closed Sauce Connect process");
done();
});
}
if (err) {
grunt.log.error(err.message);
done(false);
}
sauceConnectTunnel.process = sauceConnectProcess;
grunt.log.success("Sauce Connect ready!");
if(keepAlive) {
grunt.log.writeln("The tunnel will be kept alive. Stop it by terminating this process with SIGINT (Ctrl-C).");
process.on("SIGINT", function() {
grunt.log.writeln();
stop();
});
} else {
grunt.log.writeln("Closing tunnel since the :keepAlive argument is not present...");
done();
}
});
});
};
{
"name": "element-resize-detector",
"version": "0.1.4",
"version": "0.2.2",
"description": "resize event emitter for elements.",

@@ -17,6 +17,20 @@ "homepage": "https://github.com/wnr/element-resize-detector",

"grunt-browserify": "^3.3.0",
"grunt-cli": "^0.1.13",
"grunt-contrib-jshint": "^0.11.0",
"grunt-contrib-uglify": "^0.7.0",
"jquery": "^2.1.3",
"load-grunt-tasks": "^3.0.0"
"grunt-karma": "^0.10.1",
"jasmine-core": "^2.2.0",
"jasmine-expect": "^2.0.0-beta1",
"jasmine-jquery": "^2.0.6",
"jquery": "^1.11.2",
"karma": "^0.12.31",
"karma-chrome-launcher": "^0.1.7",
"karma-firefox-launcher": "^0.1.4",
"karma-ievms": "0.0.4",
"karma-jasmine": "^0.3.5",
"karma-safari-launcher": "^0.1.1",
"karma-sauce-launcher": "^0.2.10",
"load-grunt-tasks": "^3.0.0",
"lodash": "^3.3.1",
"sauce-connect-launcher": "^0.10.1"
},

@@ -26,4 +40,5 @@ "scripts": {

"dist": "grunt dist",
"test": "grunt test"
"test": "grunt test",
"test-ci": "grunt ci"
}
}

@@ -28,7 +28,2 @@ # element-resize-detector

var erdDefault = elementResizeDetectorMaker();
//With custom options.
var erdCustom = elementResizeDetectorMaker({
allowMultipleListeners: true //Default is false, which only allows one listener function per element.
});
```

@@ -35,0 +30,0 @@

@@ -6,29 +6,100 @@ //Heavily inspired by http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/

var forEach = require("./collection-utils").forEach;
var elementUtils = require("./element-utils");
var elementUtilsMaker = require("./element-utils");
var listenerHandlerMaker = require("./listener-handler");
var idGeneratorMaker = require("./id-generator");
var listenerHandlerMaker = require("./listener-handler");
var idHandlerMaker = require("./id-handler");
var reporterMaker = require("./reporter");
/**
* @typedef idHandler
* @type {object}
* @property {function} get Gets the resize detector id of the element.
* @property {function} set Generate and sets the resize detector id of the element.
*/
/**
* @typedef Options
* @type {object}
* @property {boolean} callOnAdd Determines if listeners should be called when they are getting added.
Default is true. If true, the listener is guaranteed to be called when it has been added.
If false, the listener will not be guarenteed to be called when it has been added (does not prevent it from being called).
* @property {idHandler} idHandler A custom id handler that is responsible for generating, setting and retrieving id's for elements.
If not provided, a default id handler will be used.
* @property {reporter} reporter A custom reporter that handles reporting logs, warnings and errors.
If not provided, a default id handler will be used.
If set to false, then nothing will be reported.
*/
/**
* Creates an element resize detector instance.
* @public
* @param {Options?} options Optional global options object that will decide how this instance will work.
*/
module.exports = function(options) {
options = options || {};
var allowMultipleListeners = options.allowMultipleListeners === undefined ? true : false;
var eventListenerHandler = listenerHandlerMaker();
var idGenerator = idGeneratorMaker();
//Options to be used as default for the listenTo function.
var globalOptions = {};
globalOptions.callOnAdd = !!getOption(options, "callOnAdd", true);
//idHandler is currently not an option to the listenTo function, so it should not be added to globalOptions.
var idHandler = options.idHandler;
if(!idHandler) {
var idGenerator = idGeneratorMaker();
var defaultIdHandler = idHandlerMaker(idGenerator);
idHandler = defaultIdHandler;
}
//reporter is currently not an option to the listenTo function, so it should not be added to globalOptions.
var reporter = options.reporter;
if(!reporter) {
//If options.reporter is false, then the reporter should be quiet.
var quiet = reporter === false;
reporter = reporterMaker(quiet);
}
var eventListenerHandler = listenerHandlerMaker(idHandler);
var elementUtils = elementUtilsMaker(idHandler);
/**
* Makes the given elements resize-detectable and starts listening to resize events on the elements. Calls the event callback for each event for each element.
* @public
* @param {Options?} options Optional options object. These options will override the global options. Some options may not be overriden, such as idHandler.
* @param {element[]|element} elements The given array of elements to detect resize events of. Single element is also valid.
* @param {function} listener The callback to be executed for each resize event for each element.
*/
function listenTo(elements, listener) {
function isListenedTo(element) {
return elementUtils.isDetectable(element) && eventListenerHandler.get(element).length;
function listenTo(options, elements, listener) {
function onResizeCallback(element) {
var listeners = eventListenerHandler.get(element);
forEach(listeners, function(listener) {
listener(element);
});
}
function addListener(element, listener) {
elementUtils.addListener(element, listener);
function onElementReadyToAddListener(callOnAdd, element, listener) {
eventListenerHandler.add(element, listener);
if(callOnAdd) {
listener(element);
}
}
//Options object may be omitted.
if(!listener) {
listener = elements;
elements = options;
options = {};
}
if(!elements) {
throw new Error("At least one element required.");
}
if(!listener) {
throw new Error("Listener required.");
}
if(elements.length === undefined) {

@@ -38,8 +109,27 @@ elements = [elements];

var callOnAdd = getOption(options, "callOnAdd", globalOptions.callOnAdd);
forEach(elements, function(element) {
//The element may change size directly after the call to listenTo, which would be unable to detect it because
//the async adding of the object. By checking the size before and after, the size change can still be detected
//and the listener can be called accordingly.
var preWidth = element.offsetWidth;
var preHeight = element.offsetHeight;
if(!elementUtils.isDetectable(element)) {
//The element is not prepared to be detectable, so do prepare it and add a listener to it.
var id = idGenerator.newId();
return elementUtils.makeDetectable(element, id, function(element) {
addListener(element, listener);
return elementUtils.makeDetectable(reporter, element, function(element) {
elementUtils.addListener(element, onResizeCallback);
onElementReadyToAddListener(callOnAdd, element, listener);
//Only here the uncaught resize may occur (since this code is async).
//Check if the size is the same as when adding the listener.
var postWidth = element.offsetWidth;
var postHeight = element.offsetHeight;
//If callOnAdd is true, then the listener will have been called either way, so no need to call the listener manually then.
if(!callOnAdd && (preWidth !== postWidth || preHeight !== postHeight)) {
//The element was changed while the object was being added. Call the listener.
listener(element);
}
});

@@ -49,10 +139,3 @@ }

//The element has been prepared to be detectable and is ready to be listened to.
if(isListenedTo(element) && !allowMultipleListeners) {
//Since there is a listener and we disallow multiple listeners no listener should be added.
return;
}
//Since multiple listeners is allowed, another listener is added to the element.
return addListener(element, listener);
onElementReadyToAddListener(callOnAdd, element, listener);
});

@@ -65,1 +148,11 @@ }

};
function getOption(options, name, defaultValue) {
var value = options[name];
if((value === undefined || value === null) && defaultValue !== undefined) {
return defaultValue;
}
return value;
}
"use strict";
var forEach = require("./collection-utils").forEach;
var browserDetector = require("./browser-detector");
var utils = module.exports = {};
module.exports = function(idHandler) {
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
function isDetectable(element) {
if(browserDetector.isIE(8)) {
//IE 8 does not use the object method.
//Check only if the element has been given an id.
return !!idHandler.get(element);
}
/**
* Gets the elq id of the element.
* @public
* @param {element} The target element to get the id of.
* @returns {string} The id of the element.
*/
utils.getId = function(element) {
return element.getAttribute("elq-target-id");
};
return !!getObject(element);
}
/**
* Tells if the element has been made detectable and ready to be listened for resize events.
* @public
* @param {element} The element to check.
* @returns {boolean} True or false depending on if the element is detectable or not.
*/
utils.isDetectable = function(element) {
return getObject(element);
};
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
function addListener(element, listener) {
if(!isDetectable(element)) {
throw new Error("Element is not detectable.");
}
/**
* Adds a resize event listener to the element.
* @public
* @param {element} element The element that should have the listener added.
* @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
*/
utils.addListener = function(element, listener) {
if(!utils.isDetectable(element)) {
throw new Error("Element is not detectable.");
function listenerProxy() {
listener(element);
}
if(browserDetector.isIE(8)) {
//IE 8 does not support object, but supports the resize event directly on elements.
element.attachEvent("onresize", listenerProxy);
} else {
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", listenerProxy);
}
}
var object = getObject(element);
object.contentDocument.defaultView.addEventListener("resize", function() {
listener(element);
});
};
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
function makeDetectable(reporter, element, callback) {
function injectObject(id, element, callback) {
var OBJECT_STYLE = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
/**
* Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
* @private
* @param {element} element The element to make detectable
* @param {*} id An unique id in the context of all detectable elements.
* @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
*/
utils.makeDetectable = function(element, id, callback) {
function onObjectLoad() {
/*jshint validthis:true */
function onObjectLoad() {
/*jshint validthis:true */
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//Create the style element to be added to the object.
var objectDocument = this.contentDocument;
var style = objectDocument.createElement("style");
style.innerHTML = "html, body { margin: 0; padding: 0 } div { -webkit-transition: opacity 0.01s; -ms-transition: opacity 0.01s; -o-transition: opacity 0.01s; transition: opacity 0.01s; opacity: 0; }";
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//TODO: Remove any styles that has been set on the object. Only the style above should be styling the object.
//Append the style to the object.
objectDocument.head.appendChild(style);
//Append the style to the object.
objectDocument.head.appendChild(style);
this.style.cssText = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
//TODO: Is this needed here?
//this.style.cssText = OBJECT_STYLE;
//Notify that the element is ready to be listened to.
callback(element);
}
//Notify that the element is ready to be listened to.
callback(element);
}
//Create an unique elq-target-id for the target element, so that event listeners can be identified to this element.
element.setAttribute("elq-target-id", id);
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
var style = getComputedStyle(element);
if(style.position === "static") {
element.style.position = "relative";
//The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
if(getComputedStyle(element).position === "static") {
element.style.position = "relative";
var removeRelativeStyles = function(reporter, element, style, property) {
function getNumericalValue(value) {
return value.replace(/[^-\d\.]/g, "");
}
var value = style[property];
if(value !== "auto" && getNumericalValue(value) !== "0") {
reporter.warn("An element that is positioned static has style." + property + "=" + value + " which is ignored due to the static positioning. The element will need to be positioned relateive, so the style." + property + " will be set to 0. Element: ", element);
element.style[property] = 0;
}
};
//Check so that there are no accidental styles that will make the element styled differently now that is is relative.
//If there are any, set them to 0 (this should be okay with the user since the style properties did nothing before [since the element was positioned static] anyway).
removeRelativeStyles(reporter, element, style, "top");
removeRelativeStyles(reporter, element, style, "right");
removeRelativeStyles(reporter, element, style, "bottom");
removeRelativeStyles(reporter, element, style, "left");
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.style.cssText = OBJECT_STYLE;
object.onload = onObjectLoad;
object.setAttribute("erd-object-id", id);
//Safari: This must occur before adding the object to the DOM.
//IE: Does not like that this happens before, even if it is also added after.
if(!browserDetector.isIE()) {
object.data = "about:blank";
}
element.appendChild(object);
//IE: This must occur after adding the object to the DOM.
if(browserDetector.isIE()) {
object.data = "about:blank";
}
}
//Create an unique erd-target-id for the target element, so that event listeners can be identified to this element.
var id = idHandler.set(element);
if(browserDetector.isIE(8)) {
//IE 8 does not support objects properly. Luckily they do support the resize event.
//So do not inject the object and notify that the element is already ready to be listened to.
//The event handler for the resize event is attached in the utils.addListener instead.
callback(element);
} else {
injectObject(id, element, callback);
}
}
//Add an object element as a child to the target element that will be listened to for resize events.
var object = document.createElement("object");
object.type = "text/html";
object.data = "about:blank";
object.onload = onObjectLoad;
object.setAttribute("elq-object-id", id);
element.appendChild(object);
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("erd-object-id")) {
return child;
}
});
}
return {
isDetectable: isDetectable,
makeDetectable: makeDetectable,
addListener: addListener,
};
};
/**
* Returns the child object of the target element.
* @private
* @param {element} element The target element.
* @returns The object element of the target.
*/
function getObject(element) {
return forEach(element.children, function(child) {
if(child.hasAttribute("elq-object-id")) {
return child;
}
});
}

@@ -11,3 +11,3 @@ "use strict";

*/
function newId() {
function generate() {
return idCount++;

@@ -17,4 +17,4 @@ }

return {
newId: newId
generate: generate
};
};
"use strict";
var elementUtils = require("./element-utils");
module.exports = function() {
module.exports = function(idHandler) {
var eventListeners = {};

@@ -15,3 +13,3 @@

function getListeners(element) {
return eventListeners[elementUtils.getId(element)];
return eventListeners[idHandler.get(element)];
}

@@ -26,3 +24,3 @@

function addListener(element, listener) {
var id = elementUtils.getId(element);
var id = idHandler.get(element);

@@ -29,0 +27,0 @@ if(!eventListeners[id]) {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc