Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • konstantin/akplanning
  • matedealer/akplanning
  • kif/akplanning
  • mirco/akplanning
  • lordofthevoid/akplanning
  • voidptr/akplanning
  • xayomer/akplanning-fork
  • mollux/akplanning
  • neumantm/akplanning
  • mmarx/akplanning
  • nerf/akplanning
  • felix_bonn/akplanning
  • sebastian.uschmann/akplanning
13 results
Show changes
Showing
with 1747 additions and 207 deletions
(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.dragula = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
'use strict';
var cache = {};
var start = '(?:^|\\s)';
var end = '(?:\\s|$)';
function lookupClass (className) {
var cached = cache[className];
if (cached) {
cached.lastIndex = 0;
} else {
cache[className] = cached = new RegExp(start + className + end, 'g');
}
return cached;
}
function addClass (el, className) {
var current = el.className;
if (!current.length) {
el.className = className;
} else if (!lookupClass(className).test(current)) {
el.className += ' ' + className;
}
}
function rmClass (el, className) {
el.className = el.className.replace(lookupClass(className), ' ').trim();
}
module.exports = {
add: addClass,
rm: rmClass
};
},{}],2:[function(require,module,exports){
(function (global){
'use strict';
var emitter = require('contra/emitter');
var crossvent = require('crossvent');
var classes = require('./classes');
var doc = document;
var documentElement = doc.documentElement;
function dragula (initialContainers, options) {
var len = arguments.length;
if (len === 1 && Array.isArray(initialContainers) === false) {
options = initialContainers;
initialContainers = [];
}
var _mirror; // mirror image
var _source; // source container
var _item; // item being dragged
var _offsetX; // reference x
var _offsetY; // reference y
var _moveX; // reference move x
var _moveY; // reference move y
var _initialSibling; // reference sibling when grabbed
var _currentSibling; // reference sibling now
var _copy; // item used for copying
var _renderTimer; // timer for setTimeout renderMirrorImage
var _lastDropTarget = null; // last container item was over
var _grabbed; // holds mousedown context until first mousemove
var o = options || {};
if (o.moves === void 0) { o.moves = always; }
if (o.accepts === void 0) { o.accepts = always; }
if (o.invalid === void 0) { o.invalid = invalidTarget; }
if (o.containers === void 0) { o.containers = initialContainers || []; }
if (o.isContainer === void 0) { o.isContainer = never; }
if (o.copy === void 0) { o.copy = false; }
if (o.copySortSource === void 0) { o.copySortSource = false; }
if (o.revertOnSpill === void 0) { o.revertOnSpill = false; }
if (o.removeOnSpill === void 0) { o.removeOnSpill = false; }
if (o.direction === void 0) { o.direction = 'vertical'; }
if (o.ignoreInputTextSelection === void 0) { o.ignoreInputTextSelection = true; }
if (o.mirrorContainer === void 0) { o.mirrorContainer = doc.body; }
var drake = emitter({
containers: o.containers,
start: manualStart,
end: end,
cancel: cancel,
remove: remove,
destroy: destroy,
canMove: canMove,
dragging: false
});
if (o.removeOnSpill === true) {
drake.on('over', spillOver).on('out', spillOut);
}
events();
return drake;
function isContainer (el) {
return drake.containers.indexOf(el) !== -1 || o.isContainer(el);
}
function events (remove) {
var op = remove ? 'remove' : 'add';
touchy(documentElement, op, 'mousedown', grab);
touchy(documentElement, op, 'mouseup', release);
}
function eventualMovements (remove) {
var op = remove ? 'remove' : 'add';
touchy(documentElement, op, 'mousemove', startBecauseMouseMoved);
}
function movements (remove) {
var op = remove ? 'remove' : 'add';
crossvent[op](documentElement, 'selectstart', preventGrabbed); // IE8
crossvent[op](documentElement, 'click', preventGrabbed);
}
function destroy () {
events(true);
release({});
}
function preventGrabbed (e) {
if (_grabbed) {
e.preventDefault();
}
}
function grab (e) {
_moveX = e.clientX;
_moveY = e.clientY;
var ignore = whichMouseButton(e) !== 1 || e.metaKey || e.ctrlKey;
if (ignore) {
return; // we only care about honest-to-god left clicks and touch events
}
var item = e.target;
var context = canStart(item);
if (!context) {
return;
}
_grabbed = context;
eventualMovements();
if (e.type === 'mousedown') {
if (isInput(item)) { // see also: https://github.com/bevacqua/dragula/issues/208
item.focus(); // fixes https://github.com/bevacqua/dragula/issues/176
} else {
e.preventDefault(); // fixes https://github.com/bevacqua/dragula/issues/155
}
}
}
function startBecauseMouseMoved (e) {
if (!_grabbed) {
return;
}
if (whichMouseButton(e) === 0) {
release({});
return; // when text is selected on an input and then dragged, mouseup doesn't fire. this is our only hope
}
// truthy check fixes #239, equality fixes #207, fixes #501
if ((e.clientX !== void 0 && Math.abs(e.clientX - _moveX) <= (o.slideFactorX || 0)) &&
(e.clientY !== void 0 && Math.abs(e.clientY - _moveY) <= (o.slideFactorY || 0))) {
return;
}
if (o.ignoreInputTextSelection) {
var clientX = getCoord('clientX', e) || 0;
var clientY = getCoord('clientY', e) || 0;
var elementBehindCursor = doc.elementFromPoint(clientX, clientY);
if (isInput(elementBehindCursor)) {
return;
}
}
var grabbed = _grabbed; // call to end() unsets _grabbed
eventualMovements(true);
movements();
end();
start(grabbed);
var offset = getOffset(_item);
_offsetX = getCoord('pageX', e) - offset.left;
_offsetY = getCoord('pageY', e) - offset.top;
classes.add(_copy || _item, 'gu-transit');
renderMirrorImage();
drag(e);
}
function canStart (item) {
if (drake.dragging && _mirror) {
return;
}
if (isContainer(item)) {
return; // don't drag container itself
}
var handle = item;
while (getParent(item) && isContainer(getParent(item)) === false) {
if (o.invalid(item, handle)) {
return;
}
item = getParent(item); // drag target should be a top element
if (!item) {
return;
}
}
var source = getParent(item);
if (!source) {
return;
}
if (o.invalid(item, handle)) {
return;
}
var movable = o.moves(item, source, handle, nextEl(item));
if (!movable) {
return;
}
return {
item: item,
source: source
};
}
function canMove (item) {
return !!canStart(item);
}
function manualStart (item) {
var context = canStart(item);
if (context) {
start(context);
}
}
function start (context) {
if (isCopy(context.item, context.source)) {
_copy = context.item.cloneNode(true);
drake.emit('cloned', _copy, context.item, 'copy');
}
_source = context.source;
_item = context.item;
_initialSibling = _currentSibling = nextEl(context.item);
drake.dragging = true;
drake.emit('drag', _item, _source);
}
function invalidTarget () {
return false;
}
function end () {
if (!drake.dragging) {
return;
}
var item = _copy || _item;
drop(item, getParent(item));
}
function ungrab () {
_grabbed = false;
eventualMovements(true);
movements(true);
}
function release (e) {
ungrab();
if (!drake.dragging) {
return;
}
var item = _copy || _item;
var clientX = getCoord('clientX', e) || 0;
var clientY = getCoord('clientY', e) || 0;
var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
if (dropTarget && ((_copy && o.copySortSource) || (!_copy || dropTarget !== _source))) {
drop(item, dropTarget);
} else if (o.removeOnSpill) {
remove();
} else {
cancel();
}
}
function drop (item, target) {
var parent = getParent(item);
if (_copy && o.copySortSource && target === _source) {
parent.removeChild(_item);
}
if (isInitialPlacement(target)) {
drake.emit('cancel', item, _source, _source);
} else {
drake.emit('drop', item, target, _source, _currentSibling);
}
cleanup();
}
function remove () {
if (!drake.dragging) {
return;
}
var item = _copy || _item;
var parent = getParent(item);
if (parent) {
parent.removeChild(item);
}
drake.emit(_copy ? 'cancel' : 'remove', item, parent, _source);
cleanup();
}
function cancel (revert) {
if (!drake.dragging) {
return;
}
var reverts = arguments.length > 0 ? revert : o.revertOnSpill;
var item = _copy || _item;
var parent = getParent(item);
var initial = isInitialPlacement(parent);
if (initial === false && reverts) {
if (_copy) {
if (parent) {
parent.removeChild(_copy);
}
} else {
_source.insertBefore(item, _initialSibling);
}
}
if (initial || reverts) {
drake.emit('cancel', item, _source, _source);
} else {
drake.emit('drop', item, parent, _source, _currentSibling);
}
cleanup();
}
function cleanup () {
var item = _copy || _item;
ungrab();
removeMirrorImage();
if (item) {
classes.rm(item, 'gu-transit');
}
if (_renderTimer) {
clearTimeout(_renderTimer);
}
drake.dragging = false;
if (_lastDropTarget) {
drake.emit('out', item, _lastDropTarget, _source);
}
drake.emit('dragend', item);
_source = _item = _copy = _initialSibling = _currentSibling = _renderTimer = _lastDropTarget = null;
}
function isInitialPlacement (target, s) {
var sibling;
if (s !== void 0) {
sibling = s;
} else if (_mirror) {
sibling = _currentSibling;
} else {
sibling = nextEl(_copy || _item);
}
return target === _source && sibling === _initialSibling;
}
function findDropTarget (elementBehindCursor, clientX, clientY) {
var target = elementBehindCursor;
while (target && !accepted()) {
target = getParent(target);
}
return target;
function accepted () {
var droppable = isContainer(target);
if (droppable === false) {
return false;
}
var immediate = getImmediateChild(target, elementBehindCursor);
var reference = getReference(target, immediate, clientX, clientY);
var initial = isInitialPlacement(target, reference);
if (initial) {
return true; // should always be able to drop it right back where it was
}
return o.accepts(_item, target, _source, reference);
}
}
function drag (e) {
if (!_mirror) {
return;
}
e.preventDefault();
var clientX = getCoord('clientX', e) || 0;
var clientY = getCoord('clientY', e) || 0;
var x = clientX - _offsetX;
var y = clientY - _offsetY;
_mirror.style.left = x + 'px';
_mirror.style.top = y + 'px';
var item = _copy || _item;
var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
var changed = dropTarget !== null && dropTarget !== _lastDropTarget;
if (changed || dropTarget === null) {
out();
_lastDropTarget = dropTarget;
over();
}
var parent = getParent(item);
if (dropTarget === _source && _copy && !o.copySortSource) {
if (parent) {
parent.removeChild(item);
}
return;
}
var reference;
var immediate = getImmediateChild(dropTarget, elementBehindCursor);
if (immediate !== null) {
reference = getReference(dropTarget, immediate, clientX, clientY);
} else if (o.revertOnSpill === true && !_copy) {
reference = _initialSibling;
dropTarget = _source;
} else {
if (_copy && parent) {
parent.removeChild(item);
}
return;
}
if (
(reference === null && changed) ||
reference !== item &&
reference !== nextEl(item)
) {
_currentSibling = reference;
dropTarget.insertBefore(item, reference);
drake.emit('shadow', item, dropTarget, _source);
}
function moved (type) { drake.emit(type, item, _lastDropTarget, _source); }
function over () { if (changed) { moved('over'); } }
function out () { if (_lastDropTarget) { moved('out'); } }
}
function spillOver (el) {
classes.rm(el, 'gu-hide');
}
function spillOut (el) {
if (drake.dragging) { classes.add(el, 'gu-hide'); }
}
function renderMirrorImage () {
if (_mirror) {
return;
}
var rect = _item.getBoundingClientRect();
_mirror = _item.cloneNode(true);
_mirror.style.width = getRectWidth(rect) + 'px';
_mirror.style.height = getRectHeight(rect) + 'px';
classes.rm(_mirror, 'gu-transit');
classes.add(_mirror, 'gu-mirror');
o.mirrorContainer.appendChild(_mirror);
touchy(documentElement, 'add', 'mousemove', drag);
classes.add(o.mirrorContainer, 'gu-unselectable');
drake.emit('cloned', _mirror, _item, 'mirror');
}
function removeMirrorImage () {
if (_mirror) {
classes.rm(o.mirrorContainer, 'gu-unselectable');
touchy(documentElement, 'remove', 'mousemove', drag);
getParent(_mirror).removeChild(_mirror);
_mirror = null;
}
}
function getImmediateChild (dropTarget, target) {
var immediate = target;
while (immediate !== dropTarget && getParent(immediate) !== dropTarget) {
immediate = getParent(immediate);
}
if (immediate === documentElement) {
return null;
}
return immediate;
}
function getReference (dropTarget, target, x, y) {
var horizontal = o.direction === 'horizontal';
var reference = target !== dropTarget ? inside() : outside();
return reference;
function outside () { // slower, but able to figure out any position
var len = dropTarget.children.length;
var i;
var el;
var rect;
for (i = 0; i < len; i++) {
el = dropTarget.children[i];
rect = el.getBoundingClientRect();
if (horizontal && (rect.left + rect.width / 2) > x) { return el; }
if (!horizontal && (rect.top + rect.height / 2) > y) { return el; }
}
return null;
}
function inside () { // faster, but only available if dropped inside a child element
var rect = target.getBoundingClientRect();
if (horizontal) {
return resolve(x > rect.left + getRectWidth(rect) / 2);
}
return resolve(y > rect.top + getRectHeight(rect) / 2);
}
function resolve (after) {
return after ? nextEl(target) : target;
}
}
function isCopy (item, container) {
return typeof o.copy === 'boolean' ? o.copy : o.copy(item, container);
}
}
function touchy (el, op, type, fn) {
var touch = {
mouseup: 'touchend',
mousedown: 'touchstart',
mousemove: 'touchmove'
};
var pointers = {
mouseup: 'pointerup',
mousedown: 'pointerdown',
mousemove: 'pointermove'
};
var microsoft = {
mouseup: 'MSPointerUp',
mousedown: 'MSPointerDown',
mousemove: 'MSPointerMove'
};
if (global.navigator.pointerEnabled) {
crossvent[op](el, pointers[type], fn);
} else if (global.navigator.msPointerEnabled) {
crossvent[op](el, microsoft[type], fn);
} else {
crossvent[op](el, touch[type], fn);
crossvent[op](el, type, fn);
}
}
function whichMouseButton (e) {
if (e.touches !== void 0) { return e.touches.length; }
if (e.which !== void 0 && e.which !== 0) { return e.which; } // see https://github.com/bevacqua/dragula/issues/261
if (e.buttons !== void 0) { return e.buttons; }
var button = e.button;
if (button !== void 0) { // see https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575
return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0);
}
}
function getOffset (el) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + getScroll('scrollLeft', 'pageXOffset'),
top: rect.top + getScroll('scrollTop', 'pageYOffset')
};
}
function getScroll (scrollProp, offsetProp) {
if (typeof global[offsetProp] !== 'undefined') {
return global[offsetProp];
}
if (documentElement.clientHeight) {
return documentElement[scrollProp];
}
return doc.body[scrollProp];
}
function getElementBehindPoint (point, x, y) {
point = point || {};
var state = point.className || '';
var el;
point.className += ' gu-hide';
el = doc.elementFromPoint(x, y);
point.className = state;
return el;
}
function never () { return false; }
function always () { return true; }
function getRectWidth (rect) { return rect.width || (rect.right - rect.left); }
function getRectHeight (rect) { return rect.height || (rect.bottom - rect.top); }
function getParent (el) { return el.parentNode === doc ? null : el.parentNode; }
function isInput (el) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable(el); }
function isEditable (el) {
if (!el) { return false; } // no parents were editable
if (el.contentEditable === 'false') { return false; } // stop the lookup
if (el.contentEditable === 'true') { return true; } // found a contentEditable element in the chain
return isEditable(getParent(el)); // contentEditable is set to 'inherit'
}
function nextEl (el) {
return el.nextElementSibling || manually();
function manually () {
var sibling = el;
do {
sibling = sibling.nextSibling;
} while (sibling && sibling.nodeType !== 1);
return sibling;
}
}
function getEventHost (e) {
// on touchend event, we have to use `e.changedTouches`
// see http://stackoverflow.com/questions/7192563/touchend-event-properties
// see https://github.com/bevacqua/dragula/issues/34
if (e.targetTouches && e.targetTouches.length) {
return e.targetTouches[0];
}
if (e.changedTouches && e.changedTouches.length) {
return e.changedTouches[0];
}
return e;
}
function getCoord (coord, e) {
var host = getEventHost(e);
var missMap = {
pageX: 'clientX', // IE8
pageY: 'clientY' // IE8
};
if (coord in missMap && !(coord in host) && missMap[coord] in host) {
coord = missMap[coord];
}
return host[coord];
}
module.exports = dragula;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./classes":1,"contra/emitter":5,"crossvent":6}],3:[function(require,module,exports){
module.exports = function atoa (a, n) { return Array.prototype.slice.call(a, n); }
},{}],4:[function(require,module,exports){
'use strict';
var ticky = require('ticky');
module.exports = function debounce (fn, args, ctx) {
if (!fn) { return; }
ticky(function run () {
fn.apply(ctx || null, args || []);
});
};
},{"ticky":10}],5:[function(require,module,exports){
'use strict';
var atoa = require('atoa');
var debounce = require('./debounce');
module.exports = function emitter (thing, options) {
var opts = options || {};
var evt = {};
if (thing === undefined) { thing = {}; }
thing.on = function (type, fn) {
if (!evt[type]) {
evt[type] = [fn];
} else {
evt[type].push(fn);
}
return thing;
};
thing.once = function (type, fn) {
fn._once = true; // thing.off(fn) still works!
thing.on(type, fn);
return thing;
};
thing.off = function (type, fn) {
var c = arguments.length;
if (c === 1) {
delete evt[type];
} else if (c === 0) {
evt = {};
} else {
var et = evt[type];
if (!et) { return thing; }
et.splice(et.indexOf(fn), 1);
}
return thing;
};
thing.emit = function () {
var args = atoa(arguments);
return thing.emitterSnapshot(args.shift()).apply(this, args);
};
thing.emitterSnapshot = function (type) {
var et = (evt[type] || []).slice(0);
return function () {
var args = atoa(arguments);
var ctx = this || thing;
if (type === 'error' && opts.throws !== false && !et.length) { throw args.length === 1 ? args[0] : args; }
et.forEach(function emitter (listen) {
if (opts.async) { debounce(listen, args, ctx); } else { listen.apply(ctx, args); }
if (listen._once) { thing.off(type, listen); }
});
return thing;
};
};
return thing;
};
},{"./debounce":4,"atoa":3}],6:[function(require,module,exports){
(function (global){
'use strict';
var customEvent = require('custom-event');
var eventmap = require('./eventmap');
var doc = global.document;
var addEvent = addEventEasy;
var removeEvent = removeEventEasy;
var hardCache = [];
if (!global.addEventListener) {
addEvent = addEventHard;
removeEvent = removeEventHard;
}
module.exports = {
add: addEvent,
remove: removeEvent,
fabricate: fabricateEvent
};
function addEventEasy (el, type, fn, capturing) {
return el.addEventListener(type, fn, capturing);
}
function addEventHard (el, type, fn) {
return el.attachEvent('on' + type, wrap(el, type, fn));
}
function removeEventEasy (el, type, fn, capturing) {
return el.removeEventListener(type, fn, capturing);
}
function removeEventHard (el, type, fn) {
var listener = unwrap(el, type, fn);
if (listener) {
return el.detachEvent('on' + type, listener);
}
}
function fabricateEvent (el, type, model) {
var e = eventmap.indexOf(type) === -1 ? makeCustomEvent() : makeClassicEvent();
if (el.dispatchEvent) {
el.dispatchEvent(e);
} else {
el.fireEvent('on' + type, e);
}
function makeClassicEvent () {
var e;
if (doc.createEvent) {
e = doc.createEvent('Event');
e.initEvent(type, true, true);
} else if (doc.createEventObject) {
e = doc.createEventObject();
}
return e;
}
function makeCustomEvent () {
return new customEvent(type, { detail: model });
}
}
function wrapperFactory (el, type, fn) {
return function wrapper (originalEvent) {
var e = originalEvent || global.event;
e.target = e.target || e.srcElement;
e.preventDefault = e.preventDefault || function preventDefault () { e.returnValue = false; };
e.stopPropagation = e.stopPropagation || function stopPropagation () { e.cancelBubble = true; };
e.which = e.which || e.keyCode;
fn.call(el, e);
};
}
function wrap (el, type, fn) {
var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn);
hardCache.push({
wrapper: wrapper,
element: el,
type: type,
fn: fn
});
return wrapper;
}
function unwrap (el, type, fn) {
var i = find(el, type, fn);
if (i) {
var wrapper = hardCache[i].wrapper;
hardCache.splice(i, 1); // free up a tad of memory
return wrapper;
}
}
function find (el, type, fn) {
var i, item;
for (i = 0; i < hardCache.length; i++) {
item = hardCache[i];
if (item.element === el && item.type === type && item.fn === fn) {
return i;
}
}
}
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./eventmap":7,"custom-event":8}],7:[function(require,module,exports){
(function (global){
'use strict';
var eventmap = [];
var eventname = '';
var ron = /^on/;
for (eventname in global) {
if (ron.test(eventname)) {
eventmap.push(eventname.slice(2));
}
}
module.exports = eventmap;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],8:[function(require,module,exports){
(function (global){
var NativeCustomEvent = global.CustomEvent;
function useNative () {
try {
var p = new NativeCustomEvent('cat', { detail: { foo: 'bar' } });
return 'cat' === p.type && 'bar' === p.detail.foo;
} catch (e) {
}
return false;
}
/**
* Cross-browser `CustomEvent` constructor.
*
* https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent.CustomEvent
*
* @public
*/
module.exports = useNative() ? NativeCustomEvent :
// IE >= 9
'undefined' !== typeof document && 'function' === typeof document.createEvent ? function CustomEvent (type, params) {
var e = document.createEvent('CustomEvent');
if (params) {
e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
} else {
e.initCustomEvent(type, false, false, void 0);
}
return e;
} :
// IE <= 8
function CustomEvent (type, params) {
var e = document.createEventObject();
e.type = type;
if (params) {
e.bubbles = Boolean(params.bubbles);
e.cancelable = Boolean(params.cancelable);
e.detail = params.detail;
} else {
e.bubbles = false;
e.cancelable = false;
e.detail = void 0;
}
return e;
}
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],9:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],10:[function(require,module,exports){
(function (setImmediate){
var si = typeof setImmediate === 'function', tick;
if (si) {
tick = function (fn) { setImmediate(fn); };
} else {
tick = function (fn) { setTimeout(fn, 0); };
}
module.exports = tick;
}).call(this,require("timers").setImmediate)
},{"timers":11}],11:[function(require,module,exports){
(function (setImmediate,clearImmediate){
var nextTick = require('process/browser.js').nextTick;
var apply = Function.prototype.apply;
var slice = Array.prototype.slice;
var immediateIds = {};
var nextImmediateId = 0;
// DOM APIs, for completeness
exports.setTimeout = function() {
return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
};
exports.setInterval = function() {
return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
};
exports.clearTimeout =
exports.clearInterval = function(timeout) { timeout.close(); };
function Timeout(id, clearFn) {
this._id = id;
this._clearFn = clearFn;
}
Timeout.prototype.unref = Timeout.prototype.ref = function() {};
Timeout.prototype.close = function() {
this._clearFn.call(window, this._id);
};
// Does not start the time, just sets up the members needed.
exports.enroll = function(item, msecs) {
clearTimeout(item._idleTimeoutId);
item._idleTimeout = msecs;
};
exports.unenroll = function(item) {
clearTimeout(item._idleTimeoutId);
item._idleTimeout = -1;
};
exports._unrefActive = exports.active = function(item) {
clearTimeout(item._idleTimeoutId);
var msecs = item._idleTimeout;
if (msecs >= 0) {
item._idleTimeoutId = setTimeout(function onTimeout() {
if (item._onTimeout)
item._onTimeout();
}, msecs);
}
};
// That's not how node.js implements it but the exposed api is the same.
exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
var id = nextImmediateId++;
var args = arguments.length < 2 ? false : slice.call(arguments, 1);
immediateIds[id] = true;
nextTick(function onNextTick() {
if (immediateIds[id]) {
// fn.call() is faster so we optimize for the common use-case
// @see http://jsperf.com/call-apply-segu
if (args) {
fn.apply(null, args);
} else {
fn.call(null);
}
// Prevent ids from leaking
exports.clearImmediate(id);
}
});
return id;
};
exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
delete immediateIds[id];
};
}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
},{"process/browser.js":9,"timers":11}]},{},[2])(2)
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjbGFzc2VzLmpzIiwiZHJhZ3VsYS5qcyIsIm5vZGVfbW9kdWxlcy9hdG9hL2F0b2EuanMiLCJub2RlX21vZHVsZXMvY29udHJhL2RlYm91bmNlLmpzIiwibm9kZV9tb2R1bGVzL2NvbnRyYS9lbWl0dGVyLmpzIiwibm9kZV9tb2R1bGVzL2Nyb3NzdmVudC9zcmMvY3Jvc3N2ZW50LmpzIiwibm9kZV9tb2R1bGVzL2Nyb3NzdmVudC9zcmMvZXZlbnRtYXAuanMiLCJub2RlX21vZHVsZXMvY3VzdG9tLWV2ZW50L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3Byb2Nlc3MvYnJvd3Nlci5qcyIsIm5vZGVfbW9kdWxlcy90aWNreS90aWNreS1icm93c2VyLmpzIiwibm9kZV9tb2R1bGVzL3RpbWVycy1icm93c2VyaWZ5L21haW4uanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ25tQkE7QUFDQTs7QUNEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUN0REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ3JHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDaERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ3hMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiByKGUsbix0KXtmdW5jdGlvbiBvKGksZil7aWYoIW5baV0pe2lmKCFlW2ldKXt2YXIgYz1cImZ1bmN0aW9uXCI9PXR5cGVvZiByZXF1aXJlJiZyZXF1aXJlO2lmKCFmJiZjKXJldHVybiBjKGksITApO2lmKHUpcmV0dXJuIHUoaSwhMCk7dmFyIGE9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitpK1wiJ1wiKTt0aHJvdyBhLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsYX12YXIgcD1uW2ldPXtleHBvcnRzOnt9fTtlW2ldWzBdLmNhbGwocC5leHBvcnRzLGZ1bmN0aW9uKHIpe3ZhciBuPWVbaV1bMV1bcl07cmV0dXJuIG8obnx8cil9LHAscC5leHBvcnRzLHIsZSxuLHQpfXJldHVybiBuW2ldLmV4cG9ydHN9Zm9yKHZhciB1PVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmUsaT0wO2k8dC5sZW5ndGg7aSsrKW8odFtpXSk7cmV0dXJuIG99cmV0dXJuIHJ9KSgpIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY2FjaGUgPSB7fTtcbnZhciBzdGFydCA9ICcoPzpefFxcXFxzKSc7XG52YXIgZW5kID0gJyg/OlxcXFxzfCQpJztcblxuZnVuY3Rpb24gbG9va3VwQ2xhc3MgKGNsYXNzTmFtZSkge1xuICB2YXIgY2FjaGVkID0gY2FjaGVbY2xhc3NOYW1lXTtcbiAgaWYgKGNhY2hlZCkge1xuICAgIGNhY2hlZC5sYXN0SW5kZXggPSAwO1xuICB9IGVsc2Uge1xuICAgIGNhY2hlW2NsYXNzTmFtZV0gPSBjYWNoZWQgPSBuZXcgUmVnRXhwKHN0YXJ0ICsgY2xhc3NOYW1lICsgZW5kLCAnZycpO1xuICB9XG4gIHJldHVybiBjYWNoZWQ7XG59XG5cbmZ1bmN0aW9uIGFkZENsYXNzIChlbCwgY2xhc3NOYW1lKSB7XG4gIHZhciBjdXJyZW50ID0gZWwuY2xhc3NOYW1lO1xuICBpZiAoIWN1cnJlbnQubGVuZ3RoKSB7XG4gICAgZWwuY2xhc3NOYW1lID0gY2xhc3NOYW1lO1xuICB9IGVsc2UgaWYgKCFsb29rdXBDbGFzcyhjbGFzc05hbWUpLnRlc3QoY3VycmVudCkpIHtcbiAgICBlbC5jbGFzc05hbWUgKz0gJyAnICsgY2xhc3NOYW1lO1xuICB9XG59XG5cbmZ1bmN0aW9uIHJtQ2xhc3MgKGVsLCBjbGFzc05hbWUpIHtcbiAgZWwuY2xhc3NOYW1lID0gZWwuY2xhc3NOYW1lLnJlcGxhY2UobG9va3VwQ2xhc3MoY2xhc3NOYW1lKSwgJyAnKS50cmltKCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhZGQ6IGFkZENsYXNzLFxuICBybTogcm1DbGFzc1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGVtaXR0ZXIgPSByZXF1aXJlKCdjb250cmEvZW1pdHRlcicpO1xudmFyIGNyb3NzdmVudCA9IHJlcXVpcmUoJ2Nyb3NzdmVudCcpO1xudmFyIGNsYXNzZXMgPSByZXF1aXJlKCcuL2NsYXNzZXMnKTtcbnZhciBkb2MgPSBkb2N1bWVudDtcbnZhciBkb2N1bWVudEVsZW1lbnQgPSBkb2MuZG9jdW1lbnRFbGVtZW50O1xuXG5mdW5jdGlvbiBkcmFndWxhIChpbml0aWFsQ29udGFpbmVycywgb3B0aW9ucykge1xuICB2YXIgbGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgaWYgKGxlbiA9PT0gMSAmJiBBcnJheS5pc0FycmF5KGluaXRpYWxDb250YWluZXJzKSA9PT0gZmFsc2UpIHtcbiAgICBvcHRpb25zID0gaW5pdGlhbENvbnRhaW5lcnM7XG4gICAgaW5pdGlhbENvbnRhaW5lcnMgPSBbXTtcbiAgfVxuICB2YXIgX21pcnJvcjsgLy8gbWlycm9yIGltYWdlXG4gIHZhciBfc291cmNlOyAvLyBzb3VyY2UgY29udGFpbmVyXG4gIHZhciBfaXRlbTsgLy8gaXRlbSBiZWluZyBkcmFnZ2VkXG4gIHZhciBfb2Zmc2V0WDsgLy8gcmVmZXJlbmNlIHhcbiAgdmFyIF9vZmZzZXRZOyAvLyByZWZlcmVuY2UgeVxuICB2YXIgX21vdmVYOyAvLyByZWZlcmVuY2UgbW92ZSB4XG4gIHZhciBfbW92ZVk7IC8vIHJlZmVyZW5jZSBtb3ZlIHlcbiAgdmFyIF9pbml0aWFsU2libGluZzsgLy8gcmVmZXJlbmNlIHNpYmxpbmcgd2hlbiBncmFiYmVkXG4gIHZhciBfY3VycmVudFNpYmxpbmc7IC8vIHJlZmVyZW5jZSBzaWJsaW5nIG5vd1xuICB2YXIgX2NvcHk7IC8vIGl0ZW0gdXNlZCBmb3IgY29weWluZ1xuICB2YXIgX3JlbmRlclRpbWVyOyAvLyB0aW1lciBmb3Igc2V0VGltZW91dCByZW5kZXJNaXJyb3JJbWFnZVxuICB2YXIgX2xhc3REcm9wVGFyZ2V0ID0gbnVsbDsgLy8gbGFzdCBjb250YWluZXIgaXRlbSB3YXMgb3ZlclxuICB2YXIgX2dyYWJiZWQ7IC8vIGhvbGRzIG1vdXNlZG93biBjb250ZXh0IHVudGlsIGZpcnN0IG1vdXNlbW92ZVxuXG4gIHZhciBvID0gb3B0aW9ucyB8fCB7fTtcbiAgaWYgKG8ubW92ZXMgPT09IHZvaWQgMCkgeyBvLm1vdmVzID0gYWx3YXlzOyB9XG4gIGlmIChvLmFjY2VwdHMgPT09IHZvaWQgMCkgeyBvLmFjY2VwdHMgPSBhbHdheXM7IH1cbiAgaWYgKG8uaW52YWxpZCA9PT0gdm9pZCAwKSB7IG8uaW52YWxpZCA9IGludmFsaWRUYXJnZXQ7IH1cbiAgaWYgKG8uY29udGFpbmVycyA9PT0gdm9pZCAwKSB7IG8uY29udGFpbmVycyA9IGluaXRpYWxDb250YWluZXJzIHx8IFtdOyB9XG4gIGlmIChvLmlzQ29udGFpbmVyID09PSB2b2lkIDApIHsgby5pc0NvbnRhaW5lciA9IG5ldmVyOyB9XG4gIGlmIChvLmNvcHkgPT09IHZvaWQgMCkgeyBvLmNvcHkgPSBmYWxzZTsgfVxuICBpZiAoby5jb3B5U29ydFNvdXJjZSA9PT0gdm9pZCAwKSB7IG8uY29weVNvcnRTb3VyY2UgPSBmYWxzZTsgfVxuICBpZiAoby5yZXZlcnRPblNwaWxsID09PSB2b2lkIDApIHsgby5yZXZlcnRPblNwaWxsID0gZmFsc2U7IH1cbiAgaWYgKG8ucmVtb3ZlT25TcGlsbCA9PT0gdm9pZCAwKSB7IG8ucmVtb3ZlT25TcGlsbCA9IGZhbHNlOyB9XG4gIGlmIChvLmRpcmVjdGlvbiA9PT0gdm9pZCAwKSB7IG8uZGlyZWN0aW9uID0gJ3ZlcnRpY2FsJzsgfVxuICBpZiAoby5pZ25vcmVJbnB1dFRleHRTZWxlY3Rpb24gPT09IHZvaWQgMCkgeyBvLmlnbm9yZUlucHV0VGV4dFNlbGVjdGlvbiA9IHRydWU7IH1cbiAgaWYgKG8ubWlycm9yQ29udGFpbmVyID09PSB2b2lkIDApIHsgby5taXJyb3JDb250YWluZXIgPSBkb2MuYm9keTsgfVxuXG4gIHZhciBkcmFrZSA9IGVtaXR0ZXIoe1xuICAgIGNvbnRhaW5lcnM6IG8uY29udGFpbmVycyxcbiAgICBzdGFydDogbWFudWFsU3RhcnQsXG4gICAgZW5kOiBlbmQsXG4gICAgY2FuY2VsOiBjYW5jZWwsXG4gICAgcmVtb3ZlOiByZW1vdmUsXG4gICAgZGVzdHJveTogZGVzdHJveSxcbiAgICBjYW5Nb3ZlOiBjYW5Nb3ZlLFxuICAgIGRyYWdnaW5nOiBmYWxzZVxuICB9KTtcblxuICBpZiAoby5yZW1vdmVPblNwaWxsID09PSB0cnVlKSB7XG4gICAgZHJha2Uub24oJ292ZXInLCBzcGlsbE92ZXIpLm9uKCdvdXQnLCBzcGlsbE91dCk7XG4gIH1cblxuICBldmVudHMoKTtcblxuICByZXR1cm4gZHJha2U7XG5cbiAgZnVuY3Rpb24gaXNDb250YWluZXIgKGVsKSB7XG4gICAgcmV0dXJuIGRyYWtlLmNvbnRhaW5lcnMuaW5kZXhPZihlbCkgIT09IC0xIHx8IG8uaXNDb250YWluZXIoZWwpO1xuICB9XG5cbiAgZnVuY3Rpb24gZXZlbnRzIChyZW1vdmUpIHtcbiAgICB2YXIgb3AgPSByZW1vdmUgPyAncmVtb3ZlJyA6ICdhZGQnO1xuICAgIHRvdWNoeShkb2N1bWVudEVsZW1lbnQsIG9wLCAnbW91c2Vkb3duJywgZ3JhYik7XG4gICAgdG91Y2h5KGRvY3VtZW50RWxlbWVudCwgb3AsICdtb3VzZXVwJywgcmVsZWFzZSk7XG4gIH1cblxuICBmdW5jdGlvbiBldmVudHVhbE1vdmVtZW50cyAocmVtb3ZlKSB7XG4gICAgdmFyIG9wID0gcmVtb3ZlID8gJ3JlbW92ZScgOiAnYWRkJztcbiAgICB0b3VjaHkoZG9jdW1lbnRFbGVtZW50LCBvcCwgJ21vdXNlbW92ZScsIHN0YXJ0QmVjYXVzZU1vdXNlTW92ZWQpO1xuICB9XG5cbiAgZnVuY3Rpb24gbW92ZW1lbnRzIChyZW1vdmUpIHtcbiAgICB2YXIgb3AgPSByZW1vdmUgPyAncmVtb3ZlJyA6ICdhZGQnO1xuICAgIGNyb3NzdmVudFtvcF0oZG9jdW1lbnRFbGVtZW50LCAnc2VsZWN0c3RhcnQnLCBwcmV2ZW50R3JhYmJlZCk7IC8vIElFOFxuICAgIGNyb3NzdmVudFtvcF0oZG9jdW1lbnRFbGVtZW50LCAnY2xpY2snLCBwcmV2ZW50R3JhYmJlZCk7XG4gIH1cblxuICBmdW5jdGlvbiBkZXN0cm95ICgpIHtcbiAgICBldmVudHModHJ1ZSk7XG4gICAgcmVsZWFzZSh7fSk7XG4gIH1cblxuICBmdW5jdGlvbiBwcmV2ZW50R3JhYmJlZCAoZSkge1xuICAgIGlmIChfZ3JhYmJlZCkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGdyYWIgKGUpIHtcbiAgICBfbW92ZVggPSBlLmNsaWVudFg7XG4gICAgX21vdmVZID0gZS5jbGllbnRZO1xuXG4gICAgdmFyIGlnbm9yZSA9IHdoaWNoTW91c2VCdXR0b24oZSkgIT09IDEgfHwgZS5tZXRhS2V5IHx8IGUuY3RybEtleTtcbiAgICBpZiAoaWdub3JlKSB7XG4gICAgICByZXR1cm47IC8vIHdlIG9ubHkgY2FyZSBhYm91dCBob25lc3QtdG8tZ29kIGxlZnQgY2xpY2tzIGFuZCB0b3VjaCBldmVudHNcbiAgICB9XG4gICAgdmFyIGl0ZW0gPSBlLnRhcmdldDtcbiAgICB2YXIgY29udGV4dCA9IGNhblN0YXJ0KGl0ZW0pO1xuICAgIGlmICghY29udGV4dCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBfZ3JhYmJlZCA9IGNvbnRleHQ7XG4gICAgZXZlbnR1YWxNb3ZlbWVudHMoKTtcbiAgICBpZiAoZS50eXBlID09PSAnbW91c2Vkb3duJykge1xuICAgICAgaWYgKGlzSW5wdXQoaXRlbSkpIHsgLy8gc2VlIGFsc286IGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8yMDhcbiAgICAgICAgaXRlbS5mb2N1cygpOyAvLyBmaXhlcyBodHRwczovL2dpdGh1Yi5jb20vYmV2YWNxdWEvZHJhZ3VsYS9pc3N1ZXMvMTc2XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7IC8vIGZpeGVzIGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8xNTVcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzdGFydEJlY2F1c2VNb3VzZU1vdmVkIChlKSB7XG4gICAgaWYgKCFfZ3JhYmJlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAod2hpY2hNb3VzZUJ1dHRvbihlKSA9PT0gMCkge1xuICAgICAgcmVsZWFzZSh7fSk7XG4gICAgICByZXR1cm47IC8vIHdoZW4gdGV4dCBpcyBzZWxlY3RlZCBvbiBhbiBpbnB1dCBhbmQgdGhlbiBkcmFnZ2VkLCBtb3VzZXVwIGRvZXNuJ3QgZmlyZS4gdGhpcyBpcyBvdXIgb25seSBob3BlXG4gICAgfVxuXG4gICAgLy8gdHJ1dGh5IGNoZWNrIGZpeGVzICMyMzksIGVxdWFsaXR5IGZpeGVzICMyMDcsIGZpeGVzICM1MDFcbiAgICBpZiAoKGUuY2xpZW50WCAhPT0gdm9pZCAwICYmIE1hdGguYWJzKGUuY2xpZW50WCAtIF9tb3ZlWCkgPD0gKG8uc2xpZGVGYWN0b3JYIHx8IDApKSAmJlxuICAgICAgKGUuY2xpZW50WSAhPT0gdm9pZCAwICYmIE1hdGguYWJzKGUuY2xpZW50WSAtIF9tb3ZlWSkgPD0gKG8uc2xpZGVGYWN0b3JZIHx8IDApKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChvLmlnbm9yZUlucHV0VGV4dFNlbGVjdGlvbikge1xuICAgICAgdmFyIGNsaWVudFggPSBnZXRDb29yZCgnY2xpZW50WCcsIGUpIHx8IDA7XG4gICAgICB2YXIgY2xpZW50WSA9IGdldENvb3JkKCdjbGllbnRZJywgZSkgfHwgMDtcbiAgICAgIHZhciBlbGVtZW50QmVoaW5kQ3Vyc29yID0gZG9jLmVsZW1lbnRGcm9tUG9pbnQoY2xpZW50WCwgY2xpZW50WSk7XG4gICAgICBpZiAoaXNJbnB1dChlbGVtZW50QmVoaW5kQ3Vyc29yKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIGdyYWJiZWQgPSBfZ3JhYmJlZDsgLy8gY2FsbCB0byBlbmQoKSB1bnNldHMgX2dyYWJiZWRcbiAgICBldmVudHVhbE1vdmVtZW50cyh0cnVlKTtcbiAgICBtb3ZlbWVudHMoKTtcbiAgICBlbmQoKTtcbiAgICBzdGFydChncmFiYmVkKTtcblxuICAgIHZhciBvZmZzZXQgPSBnZXRPZmZzZXQoX2l0ZW0pO1xuICAgIF9vZmZzZXRYID0gZ2V0Q29vcmQoJ3BhZ2VYJywgZSkgLSBvZmZzZXQubGVmdDtcbiAgICBfb2Zmc2V0WSA9IGdldENvb3JkKCdwYWdlWScsIGUpIC0gb2Zmc2V0LnRvcDtcblxuICAgIGNsYXNzZXMuYWRkKF9jb3B5IHx8IF9pdGVtLCAnZ3UtdHJhbnNpdCcpO1xuICAgIHJlbmRlck1pcnJvckltYWdlKCk7XG4gICAgZHJhZyhlKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhblN0YXJ0IChpdGVtKSB7XG4gICAgaWYgKGRyYWtlLmRyYWdnaW5nICYmIF9taXJyb3IpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGlzQ29udGFpbmVyKGl0ZW0pKSB7XG4gICAgICByZXR1cm47IC8vIGRvbid0IGRyYWcgY29udGFpbmVyIGl0c2VsZlxuICAgIH1cbiAgICB2YXIgaGFuZGxlID0gaXRlbTtcbiAgICB3aGlsZSAoZ2V0UGFyZW50KGl0ZW0pICYmIGlzQ29udGFpbmVyKGdldFBhcmVudChpdGVtKSkgPT09IGZhbHNlKSB7XG4gICAgICBpZiAoby5pbnZhbGlkKGl0ZW0sIGhhbmRsZSkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaXRlbSA9IGdldFBhcmVudChpdGVtKTsgLy8gZHJhZyB0YXJnZXQgc2hvdWxkIGJlIGEgdG9wIGVsZW1lbnRcbiAgICAgIGlmICghaXRlbSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIHZhciBzb3VyY2UgPSBnZXRQYXJlbnQoaXRlbSk7XG4gICAgaWYgKCFzb3VyY2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKG8uaW52YWxpZChpdGVtLCBoYW5kbGUpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIG1vdmFibGUgPSBvLm1vdmVzKGl0ZW0sIHNvdXJjZSwgaGFuZGxlLCBuZXh0RWwoaXRlbSkpO1xuICAgIGlmICghbW92YWJsZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBpdGVtOiBpdGVtLFxuICAgICAgc291cmNlOiBzb3VyY2VcbiAgICB9O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuTW92ZSAoaXRlbSkge1xuICAgIHJldHVybiAhIWNhblN0YXJ0KGl0ZW0pO1xuICB9XG5cbiAgZnVuY3Rpb24gbWFudWFsU3RhcnQgKGl0ZW0pIHtcbiAgICB2YXIgY29udGV4dCA9IGNhblN0YXJ0KGl0ZW0pO1xuICAgIGlmIChjb250ZXh0KSB7XG4gICAgICBzdGFydChjb250ZXh0KTtcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzdGFydCAoY29udGV4dCkge1xuICAgIGlmIChpc0NvcHkoY29udGV4dC5pdGVtLCBjb250ZXh0LnNvdXJjZSkpIHtcbiAgICAgIF9jb3B5ID0gY29udGV4dC5pdGVtLmNsb25lTm9kZSh0cnVlKTtcbiAgICAgIGRyYWtlLmVtaXQoJ2Nsb25lZCcsIF9jb3B5LCBjb250ZXh0Lml0ZW0sICdjb3B5Jyk7XG4gICAgfVxuXG4gICAgX3NvdXJjZSA9IGNvbnRleHQuc291cmNlO1xuICAgIF9pdGVtID0gY29udGV4dC5pdGVtO1xuICAgIF9pbml0aWFsU2libGluZyA9IF9jdXJyZW50U2libGluZyA9IG5leHRFbChjb250ZXh0Lml0ZW0pO1xuXG4gICAgZHJha2UuZHJhZ2dpbmcgPSB0cnVlO1xuICAgIGRyYWtlLmVtaXQoJ2RyYWcnLCBfaXRlbSwgX3NvdXJjZSk7XG4gIH1cblxuICBmdW5jdGlvbiBpbnZhbGlkVGFyZ2V0ICgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBmdW5jdGlvbiBlbmQgKCkge1xuICAgIGlmICghZHJha2UuZHJhZ2dpbmcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIGl0ZW0gPSBfY29weSB8fCBfaXRlbTtcbiAgICBkcm9wKGl0ZW0sIGdldFBhcmVudChpdGVtKSk7XG4gIH1cblxuICBmdW5jdGlvbiB1bmdyYWIgKCkge1xuICAgIF9ncmFiYmVkID0gZmFsc2U7XG4gICAgZXZlbnR1YWxNb3ZlbWVudHModHJ1ZSk7XG4gICAgbW92ZW1lbnRzKHRydWUpO1xuICB9XG5cbiAgZnVuY3Rpb24gcmVsZWFzZSAoZSkge1xuICAgIHVuZ3JhYigpO1xuXG4gICAgaWYgKCFkcmFrZS5kcmFnZ2luZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB2YXIgaXRlbSA9IF9jb3B5IHx8IF9pdGVtO1xuICAgIHZhciBjbGllbnRYID0gZ2V0Q29vcmQoJ2NsaWVudFgnLCBlKSB8fCAwO1xuICAgIHZhciBjbGllbnRZID0gZ2V0Q29vcmQoJ2NsaWVudFknLCBlKSB8fCAwO1xuICAgIHZhciBlbGVtZW50QmVoaW5kQ3Vyc29yID0gZ2V0RWxlbWVudEJlaGluZFBvaW50KF9taXJyb3IsIGNsaWVudFgsIGNsaWVudFkpO1xuICAgIHZhciBkcm9wVGFyZ2V0ID0gZmluZERyb3BUYXJnZXQoZWxlbWVudEJlaGluZEN1cnNvciwgY2xpZW50WCwgY2xpZW50WSk7XG4gICAgaWYgKGRyb3BUYXJnZXQgJiYgKChfY29weSAmJiBvLmNvcHlTb3J0U291cmNlKSB8fCAoIV9jb3B5IHx8IGRyb3BUYXJnZXQgIT09IF9zb3VyY2UpKSkge1xuICAgICAgZHJvcChpdGVtLCBkcm9wVGFyZ2V0KTtcbiAgICB9IGVsc2UgaWYgKG8ucmVtb3ZlT25TcGlsbCkge1xuICAgICAgcmVtb3ZlKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNhbmNlbCgpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGRyb3AgKGl0ZW0sIHRhcmdldCkge1xuICAgIHZhciBwYXJlbnQgPSBnZXRQYXJlbnQoaXRlbSk7XG4gICAgaWYgKF9jb3B5ICYmIG8uY29weVNvcnRTb3VyY2UgJiYgdGFyZ2V0ID09PSBfc291cmNlKSB7XG4gICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQoX2l0ZW0pO1xuICAgIH1cbiAgICBpZiAoaXNJbml0aWFsUGxhY2VtZW50KHRhcmdldCkpIHtcbiAgICAgIGRyYWtlLmVtaXQoJ2NhbmNlbCcsIGl0ZW0sIF9zb3VyY2UsIF9zb3VyY2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICBkcmFrZS5lbWl0KCdkcm9wJywgaXRlbSwgdGFyZ2V0LCBfc291cmNlLCBfY3VycmVudFNpYmxpbmcpO1xuICAgIH1cbiAgICBjbGVhbnVwKCk7XG4gIH1cblxuICBmdW5jdGlvbiByZW1vdmUgKCkge1xuICAgIGlmICghZHJha2UuZHJhZ2dpbmcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIGl0ZW0gPSBfY29weSB8fCBfaXRlbTtcbiAgICB2YXIgcGFyZW50ID0gZ2V0UGFyZW50KGl0ZW0pO1xuICAgIGlmIChwYXJlbnQpIHtcbiAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChpdGVtKTtcbiAgICB9XG4gICAgZHJha2UuZW1pdChfY29weSA/ICdjYW5jZWwnIDogJ3JlbW92ZScsIGl0ZW0sIHBhcmVudCwgX3NvdXJjZSk7XG4gICAgY2xlYW51cCgpO1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsIChyZXZlcnQpIHtcbiAgICBpZiAoIWRyYWtlLmRyYWdnaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciByZXZlcnRzID0gYXJndW1lbnRzLmxlbmd0aCA+IDAgPyByZXZlcnQgOiBvLnJldmVydE9uU3BpbGw7XG4gICAgdmFyIGl0ZW0gPSBfY29weSB8fCBfaXRlbTtcbiAgICB2YXIgcGFyZW50ID0gZ2V0UGFyZW50KGl0ZW0pO1xuICAgIHZhciBpbml0aWFsID0gaXNJbml0aWFsUGxhY2VtZW50KHBhcmVudCk7XG4gICAgaWYgKGluaXRpYWwgPT09IGZhbHNlICYmIHJldmVydHMpIHtcbiAgICAgIGlmIChfY29weSkge1xuICAgICAgICBpZiAocGFyZW50KSB7XG4gICAgICAgICAgcGFyZW50LnJlbW92ZUNoaWxkKF9jb3B5KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgX3NvdXJjZS5pbnNlcnRCZWZvcmUoaXRlbSwgX2luaXRpYWxTaWJsaW5nKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGluaXRpYWwgfHwgcmV2ZXJ0cykge1xuICAgICAgZHJha2UuZW1pdCgnY2FuY2VsJywgaXRlbSwgX3NvdXJjZSwgX3NvdXJjZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRyYWtlLmVtaXQoJ2Ryb3AnLCBpdGVtLCBwYXJlbnQsIF9zb3VyY2UsIF9jdXJyZW50U2libGluZyk7XG4gICAgfVxuICAgIGNsZWFudXAoKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNsZWFudXAgKCkge1xuICAgIHZhciBpdGVtID0gX2NvcHkgfHwgX2l0ZW07XG4gICAgdW5ncmFiKCk7XG4gICAgcmVtb3ZlTWlycm9ySW1hZ2UoKTtcbiAgICBpZiAoaXRlbSkge1xuICAgICAgY2xhc3Nlcy5ybShpdGVtLCAnZ3UtdHJhbnNpdCcpO1xuICAgIH1cbiAgICBpZiAoX3JlbmRlclRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQoX3JlbmRlclRpbWVyKTtcbiAgICB9XG4gICAgZHJha2UuZHJhZ2dpbmcgPSBmYWxzZTtcbiAgICBpZiAoX2xhc3REcm9wVGFyZ2V0KSB7XG4gICAgICBkcmFrZS5lbWl0KCdvdXQnLCBpdGVtLCBfbGFzdERyb3BUYXJnZXQsIF9zb3VyY2UpO1xuICAgIH1cbiAgICBkcmFrZS5lbWl0KCdkcmFnZW5kJywgaXRlbSk7XG4gICAgX3NvdXJjZSA9IF9pdGVtID0gX2NvcHkgPSBfaW5pdGlhbFNpYmxpbmcgPSBfY3VycmVudFNpYmxpbmcgPSBfcmVuZGVyVGltZXIgPSBfbGFzdERyb3BUYXJnZXQgPSBudWxsO1xuICB9XG5cbiAgZnVuY3Rpb24gaXNJbml0aWFsUGxhY2VtZW50ICh0YXJnZXQsIHMpIHtcbiAgICB2YXIgc2libGluZztcbiAgICBpZiAocyAhPT0gdm9pZCAwKSB7XG4gICAgICBzaWJsaW5nID0gcztcbiAgICB9IGVsc2UgaWYgKF9taXJyb3IpIHtcbiAgICAgIHNpYmxpbmcgPSBfY3VycmVudFNpYmxpbmc7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNpYmxpbmcgPSBuZXh0RWwoX2NvcHkgfHwgX2l0ZW0pO1xuICAgIH1cbiAgICByZXR1cm4gdGFyZ2V0ID09PSBfc291cmNlICYmIHNpYmxpbmcgPT09IF9pbml0aWFsU2libGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGZpbmREcm9wVGFyZ2V0IChlbGVtZW50QmVoaW5kQ3Vyc29yLCBjbGllbnRYLCBjbGllbnRZKSB7XG4gICAgdmFyIHRhcmdldCA9IGVsZW1lbnRCZWhpbmRDdXJzb3I7XG4gICAgd2hpbGUgKHRhcmdldCAmJiAhYWNjZXB0ZWQoKSkge1xuICAgICAgdGFyZ2V0ID0gZ2V0UGFyZW50KHRhcmdldCk7XG4gICAgfVxuICAgIHJldHVybiB0YXJnZXQ7XG5cbiAgICBmdW5jdGlvbiBhY2NlcHRlZCAoKSB7XG4gICAgICB2YXIgZHJvcHBhYmxlID0gaXNDb250YWluZXIodGFyZ2V0KTtcbiAgICAgIGlmIChkcm9wcGFibGUgPT09IGZhbHNlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgdmFyIGltbWVkaWF0ZSA9IGdldEltbWVkaWF0ZUNoaWxkKHRhcmdldCwgZWxlbWVudEJlaGluZEN1cnNvcik7XG4gICAgICB2YXIgcmVmZXJlbmNlID0gZ2V0UmVmZXJlbmNlKHRhcmdldCwgaW1tZWRpYXRlLCBjbGllbnRYLCBjbGllbnRZKTtcbiAgICAgIHZhciBpbml0aWFsID0gaXNJbml0aWFsUGxhY2VtZW50KHRhcmdldCwgcmVmZXJlbmNlKTtcbiAgICAgIGlmIChpbml0aWFsKSB7XG4gICAgICAgIHJldHVybiB0cnVlOyAvLyBzaG91bGQgYWx3YXlzIGJlIGFibGUgdG8gZHJvcCBpdCByaWdodCBiYWNrIHdoZXJlIGl0IHdhc1xuICAgICAgfVxuICAgICAgcmV0dXJuIG8uYWNjZXB0cyhfaXRlbSwgdGFyZ2V0LCBfc291cmNlLCByZWZlcmVuY2UpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGRyYWcgKGUpIHtcbiAgICBpZiAoIV9taXJyb3IpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuXG4gICAgdmFyIGNsaWVudFggPSBnZXRDb29yZCgnY2xpZW50WCcsIGUpIHx8IDA7XG4gICAgdmFyIGNsaWVudFkgPSBnZXRDb29yZCgnY2xpZW50WScsIGUpIHx8IDA7XG4gICAgdmFyIHggPSBjbGllbnRYIC0gX29mZnNldFg7XG4gICAgdmFyIHkgPSBjbGllbnRZIC0gX29mZnNldFk7XG5cbiAgICBfbWlycm9yLnN0eWxlLmxlZnQgPSB4ICsgJ3B4JztcbiAgICBfbWlycm9yLnN0eWxlLnRvcCA9IHkgKyAncHgnO1xuXG4gICAgdmFyIGl0ZW0gPSBfY29weSB8fCBfaXRlbTtcbiAgICB2YXIgZWxlbWVudEJlaGluZEN1cnNvciA9IGdldEVsZW1lbnRCZWhpbmRQb2ludChfbWlycm9yLCBjbGllbnRYLCBjbGllbnRZKTtcbiAgICB2YXIgZHJvcFRhcmdldCA9IGZpbmREcm9wVGFyZ2V0KGVsZW1lbnRCZWhpbmRDdXJzb3IsIGNsaWVudFgsIGNsaWVudFkpO1xuICAgIHZhciBjaGFuZ2VkID0gZHJvcFRhcmdldCAhPT0gbnVsbCAmJiBkcm9wVGFyZ2V0ICE9PSBfbGFzdERyb3BUYXJnZXQ7XG4gICAgaWYgKGNoYW5nZWQgfHwgZHJvcFRhcmdldCA9PT0gbnVsbCkge1xuICAgICAgb3V0KCk7XG4gICAgICBfbGFzdERyb3BUYXJnZXQgPSBkcm9wVGFyZ2V0O1xuICAgICAgb3ZlcigpO1xuICAgIH1cbiAgICB2YXIgcGFyZW50ID0gZ2V0UGFyZW50KGl0ZW0pO1xuICAgIGlmIChkcm9wVGFyZ2V0ID09PSBfc291cmNlICYmIF9jb3B5ICYmICFvLmNvcHlTb3J0U291cmNlKSB7XG4gICAgICBpZiAocGFyZW50KSB7XG4gICAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChpdGVtKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHJlZmVyZW5jZTtcbiAgICB2YXIgaW1tZWRpYXRlID0gZ2V0SW1tZWRpYXRlQ2hpbGQoZHJvcFRhcmdldCwgZWxlbWVudEJlaGluZEN1cnNvcik7XG4gICAgaWYgKGltbWVkaWF0ZSAhPT0gbnVsbCkge1xuICAgICAgcmVmZXJlbmNlID0gZ2V0UmVmZXJlbmNlKGRyb3BUYXJnZXQsIGltbWVkaWF0ZSwgY2xpZW50WCwgY2xpZW50WSk7XG4gICAgfSBlbHNlIGlmIChvLnJldmVydE9uU3BpbGwgPT09IHRydWUgJiYgIV9jb3B5KSB7XG4gICAgICByZWZlcmVuY2UgPSBfaW5pdGlhbFNpYmxpbmc7XG4gICAgICBkcm9wVGFyZ2V0ID0gX3NvdXJjZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKF9jb3B5ICYmIHBhcmVudCkge1xuICAgICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQoaXRlbSk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChcbiAgICAgIChyZWZlcmVuY2UgPT09IG51bGwgJiYgY2hhbmdlZCkgfHxcbiAgICAgIHJlZmVyZW5jZSAhPT0gaXRlbSAmJlxuICAgICAgcmVmZXJlbmNlICE9PSBuZXh0RWwoaXRlbSlcbiAgICApIHtcbiAgICAgIF9jdXJyZW50U2libGluZyA9IHJlZmVyZW5jZTtcbiAgICAgIGRyb3BUYXJnZXQuaW5zZXJ0QmVmb3JlKGl0ZW0sIHJlZmVyZW5jZSk7XG4gICAgICBkcmFrZS5lbWl0KCdzaGFkb3cnLCBpdGVtLCBkcm9wVGFyZ2V0LCBfc291cmNlKTtcbiAgICB9XG4gICAgZnVuY3Rpb24gbW92ZWQgKHR5cGUpIHsgZHJha2UuZW1pdCh0eXBlLCBpdGVtLCBfbGFzdERyb3BUYXJnZXQsIF9zb3VyY2UpOyB9XG4gICAgZnVuY3Rpb24gb3ZlciAoKSB7IGlmIChjaGFuZ2VkKSB7IG1vdmVkKCdvdmVyJyk7IH0gfVxuICAgIGZ1bmN0aW9uIG91dCAoKSB7IGlmIChfbGFzdERyb3BUYXJnZXQpIHsgbW92ZWQoJ291dCcpOyB9IH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHNwaWxsT3ZlciAoZWwpIHtcbiAgICBjbGFzc2VzLnJtKGVsLCAnZ3UtaGlkZScpO1xuICB9XG5cbiAgZnVuY3Rpb24gc3BpbGxPdXQgKGVsKSB7XG4gICAgaWYgKGRyYWtlLmRyYWdnaW5nKSB7IGNsYXNzZXMuYWRkKGVsLCAnZ3UtaGlkZScpOyB9XG4gIH1cblxuICBmdW5jdGlvbiByZW5kZXJNaXJyb3JJbWFnZSAoKSB7XG4gICAgaWYgKF9taXJyb3IpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHJlY3QgPSBfaXRlbS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBfbWlycm9yID0gX2l0ZW0uY2xvbmVOb2RlKHRydWUpO1xuICAgIF9taXJyb3Iuc3R5bGUud2lkdGggPSBnZXRSZWN0V2lkdGgocmVjdCkgKyAncHgnO1xuICAgIF9taXJyb3Iuc3R5bGUuaGVpZ2h0ID0gZ2V0UmVjdEhlaWdodChyZWN0KSArICdweCc7XG4gICAgY2xhc3Nlcy5ybShfbWlycm9yLCAnZ3UtdHJhbnNpdCcpO1xuICAgIGNsYXNzZXMuYWRkKF9taXJyb3IsICdndS1taXJyb3InKTtcbiAgICBvLm1pcnJvckNvbnRhaW5lci5hcHBlbmRDaGlsZChfbWlycm9yKTtcbiAgICB0b3VjaHkoZG9jdW1lbnRFbGVtZW50LCAnYWRkJywgJ21vdXNlbW92ZScsIGRyYWcpO1xuICAgIGNsYXNzZXMuYWRkKG8ubWlycm9yQ29udGFpbmVyLCAnZ3UtdW5zZWxlY3RhYmxlJyk7XG4gICAgZHJha2UuZW1pdCgnY2xvbmVkJywgX21pcnJvciwgX2l0ZW0sICdtaXJyb3InKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZU1pcnJvckltYWdlICgpIHtcbiAgICBpZiAoX21pcnJvcikge1xuICAgICAgY2xhc3Nlcy5ybShvLm1pcnJvckNvbnRhaW5lciwgJ2d1LXVuc2VsZWN0YWJsZScpO1xuICAgICAgdG91Y2h5KGRvY3VtZW50RWxlbWVudCwgJ3JlbW92ZScsICdtb3VzZW1vdmUnLCBkcmFnKTtcbiAgICAgIGdldFBhcmVudChfbWlycm9yKS5yZW1vdmVDaGlsZChfbWlycm9yKTtcbiAgICAgIF9taXJyb3IgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGdldEltbWVkaWF0ZUNoaWxkIChkcm9wVGFyZ2V0LCB0YXJnZXQpIHtcbiAgICB2YXIgaW1tZWRpYXRlID0gdGFyZ2V0O1xuICAgIHdoaWxlIChpbW1lZGlhdGUgIT09IGRyb3BUYXJnZXQgJiYgZ2V0UGFyZW50KGltbWVkaWF0ZSkgIT09IGRyb3BUYXJnZXQpIHtcbiAgICAgIGltbWVkaWF0ZSA9IGdldFBhcmVudChpbW1lZGlhdGUpO1xuICAgIH1cbiAgICBpZiAoaW1tZWRpYXRlID09PSBkb2N1bWVudEVsZW1lbnQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gaW1tZWRpYXRlO1xuICB9XG5cbiAgZnVuY3Rpb24gZ2V0UmVmZXJlbmNlIChkcm9wVGFyZ2V0LCB0YXJnZXQsIHgsIHkpIHtcbiAgICB2YXIgaG9yaXpvbnRhbCA9IG8uZGlyZWN0aW9uID09PSAnaG9yaXpvbnRhbCc7XG4gICAgdmFyIHJlZmVyZW5jZSA9IHRhcmdldCAhPT0gZHJvcFRhcmdldCA/IGluc2lkZSgpIDogb3V0c2lkZSgpO1xuICAgIHJldHVybiByZWZlcmVuY2U7XG5cbiAgICBmdW5jdGlvbiBvdXRzaWRlICgpIHsgLy8gc2xvd2VyLCBidXQgYWJsZSB0byBmaWd1cmUgb3V0IGFueSBwb3NpdGlvblxuICAgICAgdmFyIGxlbiA9IGRyb3BUYXJnZXQuY2hpbGRyZW4ubGVuZ3RoO1xuICAgICAgdmFyIGk7XG4gICAgICB2YXIgZWw7XG4gICAgICB2YXIgcmVjdDtcbiAgICAgIGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgICBlbCA9IGRyb3BUYXJnZXQuY2hpbGRyZW5baV07XG4gICAgICAgIHJlY3QgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGhvcml6b250YWwgJiYgKHJlY3QubGVmdCArIHJlY3Qud2lkdGggLyAyKSA+IHgpIHsgcmV0dXJuIGVsOyB9XG4gICAgICAgIGlmICghaG9yaXpvbnRhbCAmJiAocmVjdC50b3AgKyByZWN0LmhlaWdodCAvIDIpID4geSkgeyByZXR1cm4gZWw7IH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc2lkZSAoKSB7IC8vIGZhc3RlciwgYnV0IG9ubHkgYXZhaWxhYmxlIGlmIGRyb3BwZWQgaW5zaWRlIGEgY2hpbGQgZWxlbWVudFxuICAgICAgdmFyIHJlY3QgPSB0YXJnZXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICBpZiAoaG9yaXpvbnRhbCkge1xuICAgICAgICByZXR1cm4gcmVzb2x2ZSh4ID4gcmVjdC5sZWZ0ICsgZ2V0UmVjdFdpZHRoKHJlY3QpIC8gMik7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzb2x2ZSh5ID4gcmVjdC50b3AgKyBnZXRSZWN0SGVpZ2h0KHJlY3QpIC8gMik7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcmVzb2x2ZSAoYWZ0ZXIpIHtcbiAgICAgIHJldHVybiBhZnRlciA/IG5leHRFbCh0YXJnZXQpIDogdGFyZ2V0O1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGlzQ29weSAoaXRlbSwgY29udGFpbmVyKSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvLmNvcHkgPT09ICdib29sZWFuJyA/IG8uY29weSA6IG8uY29weShpdGVtLCBjb250YWluZXIpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHRvdWNoeSAoZWwsIG9wLCB0eXBlLCBmbikge1xuICB2YXIgdG91Y2ggPSB7XG4gICAgbW91c2V1cDogJ3RvdWNoZW5kJyxcbiAgICBtb3VzZWRvd246ICd0b3VjaHN0YXJ0JyxcbiAgICBtb3VzZW1vdmU6ICd0b3VjaG1vdmUnXG4gIH07XG4gIHZhciBwb2ludGVycyA9IHtcbiAgICBtb3VzZXVwOiAncG9pbnRlcnVwJyxcbiAgICBtb3VzZWRvd246ICdwb2ludGVyZG93bicsXG4gICAgbW91c2Vtb3ZlOiAncG9pbnRlcm1vdmUnXG4gIH07XG4gIHZhciBtaWNyb3NvZnQgPSB7XG4gICAgbW91c2V1cDogJ01TUG9pbnRlclVwJyxcbiAgICBtb3VzZWRvd246ICdNU1BvaW50ZXJEb3duJyxcbiAgICBtb3VzZW1vdmU6ICdNU1BvaW50ZXJNb3ZlJ1xuICB9O1xuICBpZiAoZ2xvYmFsLm5hdmlnYXRvci5wb2ludGVyRW5hYmxlZCkge1xuICAgIGNyb3NzdmVudFtvcF0oZWwsIHBvaW50ZXJzW3R5cGVdLCBmbik7XG4gIH0gZWxzZSBpZiAoZ2xvYmFsLm5hdmlnYXRvci5tc1BvaW50ZXJFbmFibGVkKSB7XG4gICAgY3Jvc3N2ZW50W29wXShlbCwgbWljcm9zb2Z0W3R5cGVdLCBmbik7XG4gIH0gZWxzZSB7XG4gICAgY3Jvc3N2ZW50W29wXShlbCwgdG91Y2hbdHlwZV0sIGZuKTtcbiAgICBjcm9zc3ZlbnRbb3BdKGVsLCB0eXBlLCBmbik7XG4gIH1cbn1cblxuZnVuY3Rpb24gd2hpY2hNb3VzZUJ1dHRvbiAoZSkge1xuICBpZiAoZS50b3VjaGVzICE9PSB2b2lkIDApIHsgcmV0dXJuIGUudG91Y2hlcy5sZW5ndGg7IH1cbiAgaWYgKGUud2hpY2ggIT09IHZvaWQgMCAmJiBlLndoaWNoICE9PSAwKSB7IHJldHVybiBlLndoaWNoOyB9IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vYmV2YWNxdWEvZHJhZ3VsYS9pc3N1ZXMvMjYxXG4gIGlmIChlLmJ1dHRvbnMgIT09IHZvaWQgMCkgeyByZXR1cm4gZS5idXR0b25zOyB9XG4gIHZhciBidXR0b24gPSBlLmJ1dHRvbjtcbiAgaWYgKGJ1dHRvbiAhPT0gdm9pZCAwKSB7IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vanF1ZXJ5L2pxdWVyeS9ibG9iLzk5ZThmZjFiYWE3YWUzNDFlOTRiYjg5YzNlODQ1NzBjN2MzYWQ5ZWEvc3JjL2V2ZW50LmpzI0w1NzMtTDU3NVxuICAgIHJldHVybiBidXR0b24gJiAxID8gMSA6IGJ1dHRvbiAmIDIgPyAzIDogKGJ1dHRvbiAmIDQgPyAyIDogMCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0T2Zmc2V0IChlbCkge1xuICB2YXIgcmVjdCA9IGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICByZXR1cm4ge1xuICAgIGxlZnQ6IHJlY3QubGVmdCArIGdldFNjcm9sbCgnc2Nyb2xsTGVmdCcsICdwYWdlWE9mZnNldCcpLFxuICAgIHRvcDogcmVjdC50b3AgKyBnZXRTY3JvbGwoJ3Njcm9sbFRvcCcsICdwYWdlWU9mZnNldCcpXG4gIH07XG59XG5cbmZ1bmN0aW9uIGdldFNjcm9sbCAoc2Nyb2xsUHJvcCwgb2Zmc2V0UHJvcCkge1xuICBpZiAodHlwZW9mIGdsb2JhbFtvZmZzZXRQcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICByZXR1cm4gZ2xvYmFsW29mZnNldFByb3BdO1xuICB9XG4gIGlmIChkb2N1bWVudEVsZW1lbnQuY2xpZW50SGVpZ2h0KSB7XG4gICAgcmV0dXJuIGRvY3VtZW50RWxlbWVudFtzY3JvbGxQcm9wXTtcbiAgfVxuICByZXR1cm4gZG9jLmJvZHlbc2Nyb2xsUHJvcF07XG59XG5cbmZ1bmN0aW9uIGdldEVsZW1lbnRCZWhpbmRQb2ludCAocG9pbnQsIHgsIHkpIHtcbiAgcG9pbnQgPSBwb2ludCB8fCB7fTtcbiAgdmFyIHN0YXRlID0gcG9pbnQuY2xhc3NOYW1lIHx8ICcnO1xuICB2YXIgZWw7XG4gIHBvaW50LmNsYXNzTmFtZSArPSAnIGd1LWhpZGUnO1xuICBlbCA9IGRvYy5lbGVtZW50RnJvbVBvaW50KHgsIHkpO1xuICBwb2ludC5jbGFzc05hbWUgPSBzdGF0ZTtcbiAgcmV0dXJuIGVsO1xufVxuXG5mdW5jdGlvbiBuZXZlciAoKSB7IHJldHVybiBmYWxzZTsgfVxuZnVuY3Rpb24gYWx3YXlzICgpIHsgcmV0dXJuIHRydWU7IH1cbmZ1bmN0aW9uIGdldFJlY3RXaWR0aCAocmVjdCkgeyByZXR1cm4gcmVjdC53aWR0aCB8fCAocmVjdC5yaWdodCAtIHJlY3QubGVmdCk7IH1cbmZ1bmN0aW9uIGdldFJlY3RIZWlnaHQgKHJlY3QpIHsgcmV0dXJuIHJlY3QuaGVpZ2h0IHx8IChyZWN0LmJvdHRvbSAtIHJlY3QudG9wKTsgfVxuZnVuY3Rpb24gZ2V0UGFyZW50IChlbCkgeyByZXR1cm4gZWwucGFyZW50Tm9kZSA9PT0gZG9jID8gbnVsbCA6IGVsLnBhcmVudE5vZGU7IH1cbmZ1bmN0aW9uIGlzSW5wdXQgKGVsKSB7IHJldHVybiBlbC50YWdOYW1lID09PSAnSU5QVVQnIHx8IGVsLnRhZ05hbWUgPT09ICdURVhUQVJFQScgfHwgZWwudGFnTmFtZSA9PT0gJ1NFTEVDVCcgfHwgaXNFZGl0YWJsZShlbCk7IH1cbmZ1bmN0aW9uIGlzRWRpdGFibGUgKGVsKSB7XG4gIGlmICghZWwpIHsgcmV0dXJuIGZhbHNlOyB9IC8vIG5vIHBhcmVudHMgd2VyZSBlZGl0YWJsZVxuICBpZiAoZWwuY29udGVudEVkaXRhYmxlID09PSAnZmFsc2UnKSB7IHJldHVybiBmYWxzZTsgfSAvLyBzdG9wIHRoZSBsb29rdXBcbiAgaWYgKGVsLmNvbnRlbnRFZGl0YWJsZSA9PT0gJ3RydWUnKSB7IHJldHVybiB0cnVlOyB9IC8vIGZvdW5kIGEgY29udGVudEVkaXRhYmxlIGVsZW1lbnQgaW4gdGhlIGNoYWluXG4gIHJldHVybiBpc0VkaXRhYmxlKGdldFBhcmVudChlbCkpOyAvLyBjb250ZW50RWRpdGFibGUgaXMgc2V0IHRvICdpbmhlcml0J1xufVxuXG5mdW5jdGlvbiBuZXh0RWwgKGVsKSB7XG4gIHJldHVybiBlbC5uZXh0RWxlbWVudFNpYmxpbmcgfHwgbWFudWFsbHkoKTtcbiAgZnVuY3Rpb24gbWFudWFsbHkgKCkge1xuICAgIHZhciBzaWJsaW5nID0gZWw7XG4gICAgZG8ge1xuICAgICAgc2libGluZyA9IHNpYmxpbmcubmV4dFNpYmxpbmc7XG4gICAgfSB3aGlsZSAoc2libGluZyAmJiBzaWJsaW5nLm5vZGVUeXBlICE9PSAxKTtcbiAgICByZXR1cm4gc2libGluZztcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRFdmVudEhvc3QgKGUpIHtcbiAgLy8gb24gdG91Y2hlbmQgZXZlbnQsIHdlIGhhdmUgdG8gdXNlIGBlLmNoYW5nZWRUb3VjaGVzYFxuICAvLyBzZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83MTkyNTYzL3RvdWNoZW5kLWV2ZW50LXByb3BlcnRpZXNcbiAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9iZXZhY3F1YS9kcmFndWxhL2lzc3Vlcy8zNFxuICBpZiAoZS50YXJnZXRUb3VjaGVzICYmIGUudGFyZ2V0VG91Y2hlcy5sZW5ndGgpIHtcbiAgICByZXR1cm4gZS50YXJnZXRUb3VjaGVzWzBdO1xuICB9XG4gIGlmIChlLmNoYW5nZWRUb3VjaGVzICYmIGUuY2hhbmdlZFRvdWNoZXMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGUuY2hhbmdlZFRvdWNoZXNbMF07XG4gIH1cbiAgcmV0dXJuIGU7XG59XG5cbmZ1bmN0aW9uIGdldENvb3JkIChjb29yZCwgZSkge1xuICB2YXIgaG9zdCA9IGdldEV2ZW50SG9zdChlKTtcbiAgdmFyIG1pc3NNYXAgPSB7XG4gICAgcGFnZVg6ICdjbGllbnRYJywgLy8gSUU4XG4gICAgcGFnZVk6ICdjbGllbnRZJyAvLyBJRThcbiAgfTtcbiAgaWYgKGNvb3JkIGluIG1pc3NNYXAgJiYgIShjb29yZCBpbiBob3N0KSAmJiBtaXNzTWFwW2Nvb3JkXSBpbiBob3N0KSB7XG4gICAgY29vcmQgPSBtaXNzTWFwW2Nvb3JkXTtcbiAgfVxuICByZXR1cm4gaG9zdFtjb29yZF07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZHJhZ3VsYTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gYXRvYSAoYSwgbikgeyByZXR1cm4gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYSwgbik7IH1cbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHRpY2t5ID0gcmVxdWlyZSgndGlja3knKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBkZWJvdW5jZSAoZm4sIGFyZ3MsIGN0eCkge1xuICBpZiAoIWZuKSB7IHJldHVybjsgfVxuICB0aWNreShmdW5jdGlvbiBydW4gKCkge1xuICAgIGZuLmFwcGx5KGN0eCB8fCBudWxsLCBhcmdzIHx8IFtdKTtcbiAgfSk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgYXRvYSA9IHJlcXVpcmUoJ2F0b2EnKTtcbnZhciBkZWJvdW5jZSA9IHJlcXVpcmUoJy4vZGVib3VuY2UnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBlbWl0dGVyICh0aGluZywgb3B0aW9ucykge1xuICB2YXIgb3B0cyA9IG9wdGlvbnMgfHwge307XG4gIHZhciBldnQgPSB7fTtcbiAgaWYgKHRoaW5nID09PSB1bmRlZmluZWQpIHsgdGhpbmcgPSB7fTsgfVxuICB0aGluZy5vbiA9IGZ1bmN0aW9uICh0eXBlLCBmbikge1xuICAgIGlmICghZXZ0W3R5cGVdKSB7XG4gICAgICBldnRbdHlwZV0gPSBbZm5dO1xuICAgIH0gZWxzZSB7XG4gICAgICBldnRbdHlwZV0ucHVzaChmbik7XG4gICAgfVxuICAgIHJldHVybiB0aGluZztcbiAgfTtcbiAgdGhpbmcub25jZSA9IGZ1bmN0aW9uICh0eXBlLCBmbikge1xuICAgIGZuLl9vbmNlID0gdHJ1ZTsgLy8gdGhpbmcub2ZmKGZuKSBzdGlsbCB3b3JrcyFcbiAgICB0aGluZy5vbih0eXBlLCBmbik7XG4gICAgcmV0dXJuIHRoaW5nO1xuICB9O1xuICB0aGluZy5vZmYgPSBmdW5jdGlvbiAodHlwZSwgZm4pIHtcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgaWYgKGMgPT09IDEpIHtcbiAgICAgIGRlbGV0ZSBldnRbdHlwZV07XG4gICAgfSBlbHNlIGlmIChjID09PSAwKSB7XG4gICAgICBldnQgPSB7fTtcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGV0ID0gZXZ0W3R5cGVdO1xuICAgICAgaWYgKCFldCkgeyByZXR1cm4gdGhpbmc7IH1cbiAgICAgIGV0LnNwbGljZShldC5pbmRleE9mKGZuKSwgMSk7XG4gICAgfVxuICAgIHJldHVybiB0aGluZztcbiAgfTtcbiAgdGhpbmcuZW1pdCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IGF0b2EoYXJndW1lbnRzKTtcbiAgICByZXR1cm4gdGhpbmcuZW1pdHRlclNuYXBzaG90KGFyZ3Muc2hpZnQoKSkuYXBwbHkodGhpcywgYXJncyk7XG4gIH07XG4gIHRoaW5nLmVtaXR0ZXJTbmFwc2hvdCA9IGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgdmFyIGV0ID0gKGV2dFt0eXBlXSB8fCBbXSkuc2xpY2UoMCk7XG4gICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhciBhcmdzID0gYXRvYShhcmd1bWVudHMpO1xuICAgICAgdmFyIGN0eCA9IHRoaXMgfHwgdGhpbmc7XG4gICAgICBpZiAodHlwZSA9PT0gJ2Vycm9yJyAmJiBvcHRzLnRocm93cyAhPT0gZmFsc2UgJiYgIWV0Lmxlbmd0aCkgeyB0aHJvdyBhcmdzLmxlbmd0aCA9PT0gMSA/IGFyZ3NbMF0gOiBhcmdzOyB9XG4gICAgICBldC5mb3JFYWNoKGZ1bmN0aW9uIGVtaXR0ZXIgKGxpc3Rlbikge1xuICAgICAgICBpZiAob3B0cy5hc3luYykgeyBkZWJvdW5jZShsaXN0ZW4sIGFyZ3MsIGN0eCk7IH0gZWxzZSB7IGxpc3Rlbi5hcHBseShjdHgsIGFyZ3MpOyB9XG4gICAgICAgIGlmIChsaXN0ZW4uX29uY2UpIHsgdGhpbmcub2ZmKHR5cGUsIGxpc3Rlbik7IH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHRoaW5nO1xuICAgIH07XG4gIH07XG4gIHJldHVybiB0aGluZztcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBjdXN0b21FdmVudCA9IHJlcXVpcmUoJ2N1c3RvbS1ldmVudCcpO1xudmFyIGV2ZW50bWFwID0gcmVxdWlyZSgnLi9ldmVudG1hcCcpO1xudmFyIGRvYyA9IGdsb2JhbC5kb2N1bWVudDtcbnZhciBhZGRFdmVudCA9IGFkZEV2ZW50RWFzeTtcbnZhciByZW1vdmVFdmVudCA9IHJlbW92ZUV2ZW50RWFzeTtcbnZhciBoYXJkQ2FjaGUgPSBbXTtcblxuaWYgKCFnbG9iYWwuYWRkRXZlbnRMaXN0ZW5lcikge1xuICBhZGRFdmVudCA9IGFkZEV2ZW50SGFyZDtcbiAgcmVtb3ZlRXZlbnQgPSByZW1vdmVFdmVudEhhcmQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhZGQ6IGFkZEV2ZW50LFxuICByZW1vdmU6IHJlbW92ZUV2ZW50LFxuICBmYWJyaWNhdGU6IGZhYnJpY2F0ZUV2ZW50XG59O1xuXG5mdW5jdGlvbiBhZGRFdmVudEVhc3kgKGVsLCB0eXBlLCBmbiwgY2FwdHVyaW5nKSB7XG4gIHJldHVybiBlbC5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGZuLCBjYXB0dXJpbmcpO1xufVxuXG5mdW5jdGlvbiBhZGRFdmVudEhhcmQgKGVsLCB0eXBlLCBmbikge1xuICByZXR1cm4gZWwuYXR0YWNoRXZlbnQoJ29uJyArIHR5cGUsIHdyYXAoZWwsIHR5cGUsIGZuKSk7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50RWFzeSAoZWwsIHR5cGUsIGZuLCBjYXB0dXJpbmcpIHtcbiAgcmV0dXJuIGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgZm4sIGNhcHR1cmluZyk7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50SGFyZCAoZWwsIHR5cGUsIGZuKSB7XG4gIHZhciBsaXN0ZW5lciA9IHVud3JhcChlbCwgdHlwZSwgZm4pO1xuICBpZiAobGlzdGVuZXIpIHtcbiAgICByZXR1cm4gZWwuZGV0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGxpc3RlbmVyKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBmYWJyaWNhdGVFdmVudCAoZWwsIHR5cGUsIG1vZGVsKSB7XG4gIHZhciBlID0gZXZlbnRtYXAuaW5kZXhPZih0eXBlKSA9PT0gLTEgPyBtYWtlQ3VzdG9tRXZlbnQoKSA6IG1ha2VDbGFzc2ljRXZlbnQoKTtcbiAgaWYgKGVsLmRpc3BhdGNoRXZlbnQpIHtcbiAgICBlbC5kaXNwYXRjaEV2ZW50KGUpO1xuICB9IGVsc2Uge1xuICAgIGVsLmZpcmVFdmVudCgnb24nICsgdHlwZSwgZSk7XG4gIH1cbiAgZnVuY3Rpb24gbWFrZUNsYXNzaWNFdmVudCAoKSB7XG4gICAgdmFyIGU7XG4gICAgaWYgKGRvYy5jcmVhdGVFdmVudCkge1xuICAgICAgZSA9IGRvYy5jcmVhdGVFdmVudCgnRXZlbnQnKTtcbiAgICAgIGUuaW5pdEV2ZW50KHR5cGUsIHRydWUsIHRydWUpO1xuICAgIH0gZWxzZSBpZiAoZG9jLmNyZWF0ZUV2ZW50T2JqZWN0KSB7XG4gICAgICBlID0gZG9jLmNyZWF0ZUV2ZW50T2JqZWN0KCk7XG4gICAgfVxuICAgIHJldHVybiBlO1xuICB9XG4gIGZ1bmN0aW9uIG1ha2VDdXN0b21FdmVudCAoKSB7XG4gICAgcmV0dXJuIG5ldyBjdXN0b21FdmVudCh0eXBlLCB7IGRldGFpbDogbW9kZWwgfSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gd3JhcHBlckZhY3RvcnkgKGVsLCB0eXBlLCBmbikge1xuICByZXR1cm4gZnVuY3Rpb24gd3JhcHBlciAob3JpZ2luYWxFdmVudCkge1xuICAgIHZhciBlID0gb3JpZ2luYWxFdmVudCB8fCBnbG9iYWwuZXZlbnQ7XG4gICAgZS50YXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCA9IGUucHJldmVudERlZmF1bHQgfHwgZnVuY3Rpb24gcHJldmVudERlZmF1bHQgKCkgeyBlLnJldHVyblZhbHVlID0gZmFsc2U7IH07XG4gICAgZS5zdG9wUHJvcGFnYXRpb24gPSBlLnN0b3BQcm9wYWdhdGlvbiB8fCBmdW5jdGlvbiBzdG9wUHJvcGFnYXRpb24gKCkgeyBlLmNhbmNlbEJ1YmJsZSA9IHRydWU7IH07XG4gICAgZS53aGljaCA9IGUud2hpY2ggfHwgZS5rZXlDb2RlO1xuICAgIGZuLmNhbGwoZWwsIGUpO1xuICB9O1xufVxuXG5mdW5jdGlvbiB3cmFwIChlbCwgdHlwZSwgZm4pIHtcbiAgdmFyIHdyYXBwZXIgPSB1bndyYXAoZWwsIHR5cGUsIGZuKSB8fCB3cmFwcGVyRmFjdG9yeShlbCwgdHlwZSwgZm4pO1xuICBoYXJkQ2FjaGUucHVzaCh7XG4gICAgd3JhcHBlcjogd3JhcHBlcixcbiAgICBlbGVtZW50OiBlbCxcbiAgICB0eXBlOiB0eXBlLFxuICAgIGZuOiBmblxuICB9KTtcbiAgcmV0dXJuIHdyYXBwZXI7XG59XG5cbmZ1bmN0aW9uIHVud3JhcCAoZWwsIHR5cGUsIGZuKSB7XG4gIHZhciBpID0gZmluZChlbCwgdHlwZSwgZm4pO1xuICBpZiAoaSkge1xuICAgIHZhciB3cmFwcGVyID0gaGFyZENhY2hlW2ldLndyYXBwZXI7XG4gICAgaGFyZENhY2hlLnNwbGljZShpLCAxKTsgLy8gZnJlZSB1cCBhIHRhZCBvZiBtZW1vcnlcbiAgICByZXR1cm4gd3JhcHBlcjtcbiAgfVxufVxuXG5mdW5jdGlvbiBmaW5kIChlbCwgdHlwZSwgZm4pIHtcbiAgdmFyIGksIGl0ZW07XG4gIGZvciAoaSA9IDA7IGkgPCBoYXJkQ2FjaGUubGVuZ3RoOyBpKyspIHtcbiAgICBpdGVtID0gaGFyZENhY2hlW2ldO1xuICAgIGlmIChpdGVtLmVsZW1lbnQgPT09IGVsICYmIGl0ZW0udHlwZSA9PT0gdHlwZSAmJiBpdGVtLmZuID09PSBmbikge1xuICAgICAgcmV0dXJuIGk7XG4gICAgfVxuICB9XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBldmVudG1hcCA9IFtdO1xudmFyIGV2ZW50bmFtZSA9ICcnO1xudmFyIHJvbiA9IC9eb24vO1xuXG5mb3IgKGV2ZW50bmFtZSBpbiBnbG9iYWwpIHtcbiAgaWYgKHJvbi50ZXN0KGV2ZW50bmFtZSkpIHtcbiAgICBldmVudG1hcC5wdXNoKGV2ZW50bmFtZS5zbGljZSgyKSk7XG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBldmVudG1hcDtcbiIsIlxudmFyIE5hdGl2ZUN1c3RvbUV2ZW50ID0gZ2xvYmFsLkN1c3RvbUV2ZW50O1xuXG5mdW5jdGlvbiB1c2VOYXRpdmUgKCkge1xuICB0cnkge1xuICAgIHZhciBwID0gbmV3IE5hdGl2ZUN1c3RvbUV2ZW50KCdjYXQnLCB7IGRldGFpbDogeyBmb286ICdiYXInIH0gfSk7XG4gICAgcmV0dXJuICAnY2F0JyA9PT0gcC50eXBlICYmICdiYXInID09PSBwLmRldGFpbC5mb287XG4gIH0gY2F0Y2ggKGUpIHtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogQ3Jvc3MtYnJvd3NlciBgQ3VzdG9tRXZlbnRgIGNvbnN0cnVjdG9yLlxuICpcbiAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9DdXN0b21FdmVudC5DdXN0b21FdmVudFxuICpcbiAqIEBwdWJsaWNcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IHVzZU5hdGl2ZSgpID8gTmF0aXZlQ3VzdG9tRXZlbnQgOlxuXG4vLyBJRSA+PSA5XG4ndW5kZWZpbmVkJyAhPT0gdHlwZW9mIGRvY3VtZW50ICYmICdmdW5jdGlvbicgPT09IHR5cGVvZiBkb2N1bWVudC5jcmVhdGVFdmVudCA/IGZ1bmN0aW9uIEN1c3RvbUV2ZW50ICh0eXBlLCBwYXJhbXMpIHtcbiAgdmFyIGUgPSBkb2N1bWVudC5jcmVhdGVFdmVudCgnQ3VzdG9tRXZlbnQnKTtcbiAgaWYgKHBhcmFtcykge1xuICAgIGUuaW5pdEN1c3RvbUV2ZW50KHR5cGUsIHBhcmFtcy5idWJibGVzLCBwYXJhbXMuY2FuY2VsYWJsZSwgcGFyYW1zLmRldGFpbCk7XG4gIH0gZWxzZSB7XG4gICAgZS5pbml0Q3VzdG9tRXZlbnQodHlwZSwgZmFsc2UsIGZhbHNlLCB2b2lkIDApO1xuICB9XG4gIHJldHVybiBlO1xufSA6XG5cbi8vIElFIDw9IDhcbmZ1bmN0aW9uIEN1c3RvbUV2ZW50ICh0eXBlLCBwYXJhbXMpIHtcbiAgdmFyIGUgPSBkb2N1bWVudC5jcmVhdGVFdmVudE9iamVjdCgpO1xuICBlLnR5cGUgPSB0eXBlO1xuICBpZiAocGFyYW1zKSB7XG4gICAgZS5idWJibGVzID0gQm9vbGVhbihwYXJhbXMuYnViYmxlcyk7XG4gICAgZS5jYW5jZWxhYmxlID0gQm9vbGVhbihwYXJhbXMuY2FuY2VsYWJsZSk7XG4gICAgZS5kZXRhaWwgPSBwYXJhbXMuZGV0YWlsO1xuICB9IGVsc2Uge1xuICAgIGUuYnViYmxlcyA9IGZhbHNlO1xuICAgIGUuY2FuY2VsYWJsZSA9IGZhbHNlO1xuICAgIGUuZGV0YWlsID0gdm9pZCAwO1xuICB9XG4gIHJldHVybiBlO1xufVxuIiwiLy8gc2hpbSBmb3IgdXNpbmcgcHJvY2VzcyBpbiBicm93c2VyXG52YXIgcHJvY2VzcyA9IG1vZHVsZS5leHBvcnRzID0ge307XG5cbi8vIGNhY2hlZCBmcm9tIHdoYXRldmVyIGdsb2JhbCBpcyBwcmVzZW50IHNvIHRoYXQgdGVzdCBydW5uZXJzIHRoYXQgc3R1YiBpdFxuLy8gZG9uJ3QgYnJlYWsgdGhpbmdzLiAgQnV0IHdlIG5lZWQgdG8gd3JhcCBpdCBpbiBhIHRyeSBjYXRjaCBpbiBjYXNlIGl0IGlzXG4vLyB3cmFwcGVkIGluIHN0cmljdCBtb2RlIGNvZGUgd2hpY2ggZG9lc24ndCBkZWZpbmUgYW55IGdsb2JhbHMuICBJdCdzIGluc2lkZSBhXG4vLyBmdW5jdGlvbiBiZWNhdXNlIHRyeS9jYXRjaGVzIGRlb3B0aW1pemUgaW4gY2VydGFpbiBlbmdpbmVzLlxuXG52YXIgY2FjaGVkU2V0VGltZW91dDtcbnZhciBjYWNoZWRDbGVhclRpbWVvdXQ7XG5cbmZ1bmN0aW9uIGRlZmF1bHRTZXRUaW1vdXQoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdzZXRUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG5mdW5jdGlvbiBkZWZhdWx0Q2xlYXJUaW1lb3V0ICgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NsZWFyVGltZW91dCBoYXMgbm90IGJlZW4gZGVmaW5lZCcpO1xufVxuKGZ1bmN0aW9uICgpIHtcbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIHNldFRpbWVvdXQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBzZXRUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBkZWZhdWx0U2V0VGltb3V0O1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIGNsZWFyVGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gY2xlYXJUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICB9XG59ICgpKVxuZnVuY3Rpb24gcnVuVGltZW91dChmdW4pIHtcbiAgICBpZiAoY2FjaGVkU2V0VGltZW91dCA9PT0gc2V0VGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgLy8gaWYgc2V0VGltZW91dCB3YXNuJ3QgYXZhaWxhYmxlIGJ1dCB3YXMgbGF0dGVyIGRlZmluZWRcbiAgICBpZiAoKGNhY2hlZFNldFRpbWVvdXQgPT09IGRlZmF1bHRTZXRUaW1vdXQgfHwgIWNhY2hlZFNldFRpbWVvdXQpICYmIHNldFRpbWVvdXQpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBzZXRUaW1lb3V0KGZ1biwgMCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIC8vIHdoZW4gd2hlbiBzb21lYm9keSBoYXMgc2NyZXdlZCB3aXRoIHNldFRpbWVvdXQgYnV0IG5vIEkuRS4gbWFkZG5lc3NcbiAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9IGNhdGNoKGUpe1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gV2hlbiB3ZSBhcmUgaW4gSS5FLiBidXQgdGhlIHNjcmlwdCBoYXMgYmVlbiBldmFsZWQgc28gSS5FLiBkb2Vzbid0IHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKG51bGwsIGZ1biwgMCk7XG4gICAgICAgIH0gY2F0Y2goZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvclxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQuY2FsbCh0aGlzLCBmdW4sIDApO1xuICAgICAgICB9XG4gICAgfVxuXG5cbn1cbmZ1bmN0aW9uIHJ1bkNsZWFyVGltZW91dChtYXJrZXIpIHtcbiAgICBpZiAoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBjbGVhclRpbWVvdXQpIHtcbiAgICAgICAgLy9ub3JtYWwgZW52aXJvbWVudHMgaW4gc2FuZSBzaXR1YXRpb25zXG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgLy8gaWYgY2xlYXJUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBkZWZhdWx0Q2xlYXJUaW1lb3V0IHx8ICFjYWNoZWRDbGVhclRpbWVvdXQpICYmIGNsZWFyVGltZW91dCkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0KG1hcmtlcik7XG4gICAgfSBjYXRjaCAoZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgIHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0LmNhbGwobnVsbCwgbWFya2VyKTtcbiAgICAgICAgfSBjYXRjaCAoZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvci5cbiAgICAgICAgICAgIC8vIFNvbWUgdmVyc2lvbnMgb2YgSS5FLiBoYXZlIGRpZmZlcmVudCBydWxlcyBmb3IgY2xlYXJUaW1lb3V0IHZzIHNldFRpbWVvdXRcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbCh0aGlzLCBtYXJrZXIpO1xuICAgICAgICB9XG4gICAgfVxuXG5cblxufVxudmFyIHF1ZXVlID0gW107XG52YXIgZHJhaW5pbmcgPSBmYWxzZTtcbnZhciBjdXJyZW50UXVldWU7XG52YXIgcXVldWVJbmRleCA9IC0xO1xuXG5mdW5jdGlvbiBjbGVhblVwTmV4dFRpY2soKSB7XG4gICAgaWYgKCFkcmFpbmluZyB8fCAhY3VycmVudFF1ZXVlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBpZiAoY3VycmVudFF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBxdWV1ZSA9IGN1cnJlbnRRdWV1ZS5jb25jYXQocXVldWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICB9XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBkcmFpblF1ZXVlKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBkcmFpblF1ZXVlKCkge1xuICAgIGlmIChkcmFpbmluZykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciB0aW1lb3V0ID0gcnVuVGltZW91dChjbGVhblVwTmV4dFRpY2spO1xuICAgIGRyYWluaW5nID0gdHJ1ZTtcblxuICAgIHZhciBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgd2hpbGUobGVuKSB7XG4gICAgICAgIGN1cnJlbnRRdWV1ZSA9IHF1ZXVlO1xuICAgICAgICBxdWV1ZSA9IFtdO1xuICAgICAgICB3aGlsZSAoKytxdWV1ZUluZGV4IDwgbGVuKSB7XG4gICAgICAgICAgICBpZiAoY3VycmVudFF1ZXVlKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudFF1ZXVlW3F1ZXVlSW5kZXhdLnJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICAgICAgbGVuID0gcXVldWUubGVuZ3RoO1xuICAgIH1cbiAgICBjdXJyZW50UXVldWUgPSBudWxsO1xuICAgIGRyYWluaW5nID0gZmFsc2U7XG4gICAgcnVuQ2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xufVxuXG5wcm9jZXNzLm5leHRUaWNrID0gZnVuY3Rpb24gKGZ1bikge1xuICAgIHZhciBhcmdzID0gbmV3IEFycmF5KGFyZ3VtZW50cy5sZW5ndGggLSAxKTtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICAgICAgICB9XG4gICAgfVxuICAgIHF1ZXVlLnB1c2gobmV3IEl0ZW0oZnVuLCBhcmdzKSk7XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCA9PT0gMSAmJiAhZHJhaW5pbmcpIHtcbiAgICAgICAgcnVuVGltZW91dChkcmFpblF1ZXVlKTtcbiAgICB9XG59O1xuXG4vLyB2OCBsaWtlcyBwcmVkaWN0aWJsZSBvYmplY3RzXG5mdW5jdGlvbiBJdGVtKGZ1biwgYXJyYXkpIHtcbiAgICB0aGlzLmZ1biA9IGZ1bjtcbiAgICB0aGlzLmFycmF5ID0gYXJyYXk7XG59XG5JdGVtLnByb3RvdHlwZS5ydW4gPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5mdW4uYXBwbHkobnVsbCwgdGhpcy5hcnJheSk7XG59O1xucHJvY2Vzcy50aXRsZSA9ICdicm93c2VyJztcbnByb2Nlc3MuYnJvd3NlciA9IHRydWU7XG5wcm9jZXNzLmVudiA9IHt9O1xucHJvY2Vzcy5hcmd2ID0gW107XG5wcm9jZXNzLnZlcnNpb24gPSAnJzsgLy8gZW1wdHkgc3RyaW5nIHRvIGF2b2lkIHJlZ2V4cCBpc3N1ZXNcbnByb2Nlc3MudmVyc2lvbnMgPSB7fTtcblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbnByb2Nlc3Mub24gPSBub29wO1xucHJvY2Vzcy5hZGRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLm9uY2UgPSBub29wO1xucHJvY2Vzcy5vZmYgPSBub29wO1xucHJvY2Vzcy5yZW1vdmVMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUFsbExpc3RlbmVycyA9IG5vb3A7XG5wcm9jZXNzLmVtaXQgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kTGlzdGVuZXIgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kT25jZUxpc3RlbmVyID0gbm9vcDtcblxucHJvY2Vzcy5saXN0ZW5lcnMgPSBmdW5jdGlvbiAobmFtZSkgeyByZXR1cm4gW10gfVxuXG5wcm9jZXNzLmJpbmRpbmcgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5iaW5kaW5nIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5cbnByb2Nlc3MuY3dkID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gJy8nIH07XG5wcm9jZXNzLmNoZGlyID0gZnVuY3Rpb24gKGRpcikge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5jaGRpciBpcyBub3Qgc3VwcG9ydGVkJyk7XG59O1xucHJvY2Vzcy51bWFzayA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gMDsgfTtcbiIsInZhciBzaSA9IHR5cGVvZiBzZXRJbW1lZGlhdGUgPT09ICdmdW5jdGlvbicsIHRpY2s7XG5pZiAoc2kpIHtcbiAgdGljayA9IGZ1bmN0aW9uIChmbikgeyBzZXRJbW1lZGlhdGUoZm4pOyB9O1xufSBlbHNlIHtcbiAgdGljayA9IGZ1bmN0aW9uIChmbikgeyBzZXRUaW1lb3V0KGZuLCAwKTsgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aWNrOyIsInZhciBuZXh0VGljayA9IHJlcXVpcmUoJ3Byb2Nlc3MvYnJvd3Nlci5qcycpLm5leHRUaWNrO1xudmFyIGFwcGx5ID0gRnVuY3Rpb24ucHJvdG90eXBlLmFwcGx5O1xudmFyIHNsaWNlID0gQXJyYXkucHJvdG90eXBlLnNsaWNlO1xudmFyIGltbWVkaWF0ZUlkcyA9IHt9O1xudmFyIG5leHRJbW1lZGlhdGVJZCA9IDA7XG5cbi8vIERPTSBBUElzLCBmb3IgY29tcGxldGVuZXNzXG5cbmV4cG9ydHMuc2V0VGltZW91dCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRUaW1lb3V0LCB3aW5kb3csIGFyZ3VtZW50cyksIGNsZWFyVGltZW91dCk7XG59O1xuZXhwb3J0cy5zZXRJbnRlcnZhbCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRJbnRlcnZhbCwgd2luZG93LCBhcmd1bWVudHMpLCBjbGVhckludGVydmFsKTtcbn07XG5leHBvcnRzLmNsZWFyVGltZW91dCA9XG5leHBvcnRzLmNsZWFySW50ZXJ2YWwgPSBmdW5jdGlvbih0aW1lb3V0KSB7IHRpbWVvdXQuY2xvc2UoKTsgfTtcblxuZnVuY3Rpb24gVGltZW91dChpZCwgY2xlYXJGbikge1xuICB0aGlzLl9pZCA9IGlkO1xuICB0aGlzLl9jbGVhckZuID0gY2xlYXJGbjtcbn1cblRpbWVvdXQucHJvdG90eXBlLnVucmVmID0gVGltZW91dC5wcm90b3R5cGUucmVmID0gZnVuY3Rpb24oKSB7fTtcblRpbWVvdXQucHJvdG90eXBlLmNsb3NlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2NsZWFyRm4uY2FsbCh3aW5kb3csIHRoaXMuX2lkKTtcbn07XG5cbi8vIERvZXMgbm90IHN0YXJ0IHRoZSB0aW1lLCBqdXN0IHNldHMgdXAgdGhlIG1lbWJlcnMgbmVlZGVkLlxuZXhwb3J0cy5lbnJvbGwgPSBmdW5jdGlvbihpdGVtLCBtc2Vjcykge1xuICBjbGVhclRpbWVvdXQoaXRlbS5faWRsZVRpbWVvdXRJZCk7XG4gIGl0ZW0uX2lkbGVUaW1lb3V0ID0gbXNlY3M7XG59O1xuXG5leHBvcnRzLnVuZW5yb2xsID0gZnVuY3Rpb24oaXRlbSkge1xuICBjbGVhclRpbWVvdXQoaXRlbS5faWRsZVRpbWVvdXRJZCk7XG4gIGl0ZW0uX2lkbGVUaW1lb3V0ID0gLTE7XG59O1xuXG5leHBvcnRzLl91bnJlZkFjdGl2ZSA9IGV4cG9ydHMuYWN0aXZlID0gZnVuY3Rpb24oaXRlbSkge1xuICBjbGVhclRpbWVvdXQoaXRlbS5faWRsZVRpbWVvdXRJZCk7XG5cbiAgdmFyIG1zZWNzID0gaXRlbS5faWRsZVRpbWVvdXQ7XG4gIGlmIChtc2VjcyA+PSAwKSB7XG4gICAgaXRlbS5faWRsZVRpbWVvdXRJZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gb25UaW1lb3V0KCkge1xuICAgICAgaWYgKGl0ZW0uX29uVGltZW91dClcbiAgICAgICAgaXRlbS5fb25UaW1lb3V0KCk7XG4gICAgfSwgbXNlY3MpO1xuICB9XG59O1xuXG4vLyBUaGF0J3Mgbm90IGhvdyBub2RlLmpzIGltcGxlbWVudHMgaXQgYnV0IHRoZSBleHBvc2VkIGFwaSBpcyB0aGUgc2FtZS5cbmV4cG9ydHMuc2V0SW1tZWRpYXRlID0gdHlwZW9mIHNldEltbWVkaWF0ZSA9PT0gXCJmdW5jdGlvblwiID8gc2V0SW1tZWRpYXRlIDogZnVuY3Rpb24oZm4pIHtcbiAgdmFyIGlkID0gbmV4dEltbWVkaWF0ZUlkKys7XG4gIHZhciBhcmdzID0gYXJndW1lbnRzLmxlbmd0aCA8IDIgPyBmYWxzZSA6IHNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKTtcblxuICBpbW1lZGlhdGVJZHNbaWRdID0gdHJ1ZTtcblxuICBuZXh0VGljayhmdW5jdGlvbiBvbk5leHRUaWNrKCkge1xuICAgIGlmIChpbW1lZGlhdGVJZHNbaWRdKSB7XG4gICAgICAvLyBmbi5jYWxsKCkgaXMgZmFzdGVyIHNvIHdlIG9wdGltaXplIGZvciB0aGUgY29tbW9uIHVzZS1jYXNlXG4gICAgICAvLyBAc2VlIGh0dHA6Ly9qc3BlcmYuY29tL2NhbGwtYXBwbHktc2VndVxuICAgICAgaWYgKGFyZ3MpIHtcbiAgICAgICAgZm4uYXBwbHkobnVsbCwgYXJncyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmbi5jYWxsKG51bGwpO1xuICAgICAgfVxuICAgICAgLy8gUHJldmVudCBpZHMgZnJvbSBsZWFraW5nXG4gICAgICBleHBvcnRzLmNsZWFySW1tZWRpYXRlKGlkKTtcbiAgICB9XG4gIH0pO1xuXG4gIHJldHVybiBpZDtcbn07XG5cbmV4cG9ydHMuY2xlYXJJbW1lZGlhdGUgPSB0eXBlb2YgY2xlYXJJbW1lZGlhdGUgPT09IFwiZnVuY3Rpb25cIiA/IGNsZWFySW1tZWRpYXRlIDogZnVuY3Rpb24oaWQpIHtcbiAgZGVsZXRlIGltbWVkaWF0ZUlkc1tpZF07XG59OyJdfQ==
.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}
\ No newline at end of file
!function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).dragula=e()}(function(){return function o(r,i,u){function c(t,e){if(!i[t]){if(!r[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(a)return a(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=i[t]={exports:{}},r[t][0].call(n.exports,function(e){return c(r[t][1][e]||e)},n,n.exports,o,r,i,u)}return i[t].exports}for(var a="function"==typeof require&&require,e=0;e<u.length;e++)c(u[e]);return c}({1:[function(e,t,n){"use strict";var o={},r="(?:^|\\s)",i="(?:\\s|$)";function u(e){var t=o[e];return t?t.lastIndex=0:o[e]=t=new RegExp(r+e+i,"g"),t}t.exports={add:function(e,t){var n=e.className;n.length?u(t).test(n)||(e.className+=" "+t):e.className=t},rm:function(e,t){e.className=e.className.replace(u(t)," ").trim()}}},{}],2:[function(e,t,n){(function(r){"use strict";var M=e("contra/emitter"),k=e("crossvent"),j=e("./classes"),R=document,q=R.documentElement;function U(e,t,n,o){r.navigator.pointerEnabled?k[t](e,{mouseup:"pointerup",mousedown:"pointerdown",mousemove:"pointermove"}[n],o):r.navigator.msPointerEnabled?k[t](e,{mouseup:"MSPointerUp",mousedown:"MSPointerDown",mousemove:"MSPointerMove"}[n],o):(k[t](e,{mouseup:"touchend",mousedown:"touchstart",mousemove:"touchmove"}[n],o),k[t](e,n,o))}function K(e){if(void 0!==e.touches)return e.touches.length;if(void 0!==e.which&&0!==e.which)return e.which;if(void 0!==e.buttons)return e.buttons;e=e.button;return void 0!==e?1&e?1:2&e?3:4&e?2:0:void 0}function z(e,t){return void 0!==r[t]?r[t]:(q.clientHeight?q:R.body)[e]}function H(e,t,n){var o=(e=e||{}).className||"";return e.className+=" gu-hide",n=R.elementFromPoint(t,n),e.className=o,n}function V(){return!1}function $(){return!0}function G(e){return e.width||e.right-e.left}function J(e){return e.height||e.bottom-e.top}function Q(e){return e.parentNode===R?null:e.parentNode}function W(e){return"INPUT"===e.tagName||"TEXTAREA"===e.tagName||"SELECT"===e.tagName||function e(t){if(!t)return!1;if("false"===t.contentEditable)return!1;if("true"===t.contentEditable)return!0;return e(Q(t))}(e)}function Z(t){return t.nextElementSibling||function(){var e=t;for(;e=e.nextSibling,e&&1!==e.nodeType;);return e}()}function ee(e,t){var t=(n=t).targetTouches&&n.targetTouches.length?n.targetTouches[0]:n.changedTouches&&n.changedTouches.length?n.changedTouches[0]:n,n={pageX:"clientX",pageY:"clientY"};return e in n&&!(e in t)&&n[e]in t&&(e=n[e]),t[e]}t.exports=function(e,t){var l,f,s,d,m,o,r,v,p,h,n;1===arguments.length&&!1===Array.isArray(e)&&(t=e,e=[]);var i,g=null,y=t||{};void 0===y.moves&&(y.moves=$),void 0===y.accepts&&(y.accepts=$),void 0===y.invalid&&(y.invalid=function(){return!1}),void 0===y.containers&&(y.containers=e||[]),void 0===y.isContainer&&(y.isContainer=V),void 0===y.copy&&(y.copy=!1),void 0===y.copySortSource&&(y.copySortSource=!1),void 0===y.revertOnSpill&&(y.revertOnSpill=!1),void 0===y.removeOnSpill&&(y.removeOnSpill=!1),void 0===y.direction&&(y.direction="vertical"),void 0===y.ignoreInputTextSelection&&(y.ignoreInputTextSelection=!0),void 0===y.mirrorContainer&&(y.mirrorContainer=R.body);var w=M({containers:y.containers,start:function(e){e=S(e);e&&C(e)},end:O,cancel:L,remove:X,destroy:function(){c(!0),N({})},canMove:function(e){return!!S(e)},dragging:!1});return!0===y.removeOnSpill&&w.on("over",function(e){j.rm(e,"gu-hide")}).on("out",function(e){w.dragging&&j.add(e,"gu-hide")}),c(),w;function u(e){return-1!==w.containers.indexOf(e)||y.isContainer(e)}function c(e){e=e?"remove":"add";U(q,e,"mousedown",E),U(q,e,"mouseup",N)}function a(e){U(q,e?"remove":"add","mousemove",x)}function b(e){e=e?"remove":"add";k[e](q,"selectstart",T),k[e](q,"click",T)}function T(e){i&&e.preventDefault()}function E(e){var t,n;o=e.clientX,r=e.clientY,1!==K(e)||e.metaKey||e.ctrlKey||(n=S(t=e.target))&&(i=n,a(),"mousedown"===e.type&&(W(t)?t.focus():e.preventDefault()))}function x(e){if(i)if(0!==K(e)){if(!(void 0!==e.clientX&&Math.abs(e.clientX-o)<=(y.slideFactorX||0)&&void 0!==e.clientY&&Math.abs(e.clientY-r)<=(y.slideFactorY||0))){if(y.ignoreInputTextSelection){var t=ee("clientX",e)||0,n=ee("clientY",e)||0;if(W(R.elementFromPoint(t,n)))return}n=i;a(!0),b(),O(),C(n);n=function(e){e=e.getBoundingClientRect();return{left:e.left+z("scrollLeft","pageXOffset"),top:e.top+z("scrollTop","pageYOffset")}}(s);d=ee("pageX",e)-n.left,m=ee("pageY",e)-n.top,j.add(h||s,"gu-transit"),function(){if(l)return;var e=s.getBoundingClientRect();(l=s.cloneNode(!0)).style.width=G(e)+"px",l.style.height=J(e)+"px",j.rm(l,"gu-transit"),j.add(l,"gu-mirror"),y.mirrorContainer.appendChild(l),U(q,"add","mousemove",P),j.add(y.mirrorContainer,"gu-unselectable"),w.emit("cloned",l,s,"mirror")}(),P(e)}}else N({})}function S(e){if(!(w.dragging&&l||u(e))){for(var t=e;Q(e)&&!1===u(Q(e));){if(y.invalid(e,t))return;if(!(e=Q(e)))return}var n=Q(e);if(n)if(!y.invalid(e,t))if(y.moves(e,n,t,Z(e)))return{item:e,source:n}}}function C(e){var t,n;t=e.item,n=e.source,("boolean"==typeof y.copy?y.copy:y.copy(t,n))&&(h=e.item.cloneNode(!0),w.emit("cloned",h,e.item,"copy")),f=e.source,s=e.item,v=p=Z(e.item),w.dragging=!0,w.emit("drag",s,f)}function O(){var e;w.dragging&&_(e=h||s,Q(e))}function I(){a(!(i=!1)),b(!0)}function N(e){var t,n;I(),w.dragging&&(t=h||s,n=ee("clientX",e)||0,e=ee("clientY",e)||0,(e=B(H(l,n,e),n,e))&&(h&&y.copySortSource||!h||e!==f)?_(t,e):(y.removeOnSpill?X:L)())}function _(e,t){var n=Q(e);h&&y.copySortSource&&t===f&&n.removeChild(s),A(t)?w.emit("cancel",e,f,f):w.emit("drop",e,t,f,p),Y()}function X(){var e,t;w.dragging&&((t=Q(e=h||s))&&t.removeChild(e),w.emit(h?"cancel":"remove",e,t,f),Y())}function L(e){var t,n,o;w.dragging&&(t=0<arguments.length?e:y.revertOnSpill,!1===(e=A(o=Q(n=h||s)))&&t&&(h?o&&o.removeChild(h):f.insertBefore(n,v)),e||t?w.emit("cancel",n,f,f):w.emit("drop",n,o,f,p),Y())}function Y(){var e=h||s;I(),l&&(j.rm(y.mirrorContainer,"gu-unselectable"),U(q,"remove","mousemove",P),Q(l).removeChild(l),l=null),e&&j.rm(e,"gu-transit"),n&&clearTimeout(n),w.dragging=!1,g&&w.emit("out",e,g,f),w.emit("dragend",e),f=s=h=v=p=n=g=null}function A(e,t){t=void 0!==t?t:l?p:Z(h||s);return e===f&&t===v}function B(t,n,o){for(var r=t;r&&!function(){if(!1===u(r))return!1;var e=D(r,t),e=F(r,e,n,o);if(A(r,e))return!0;return y.accepts(s,r,f,e)}();)r=Q(r);return r}function P(e){if(l){e.preventDefault();var t=ee("clientX",e)||0,n=ee("clientY",e)||0,o=t-d,r=n-m;l.style.left=o+"px",l.style.top=r+"px";var i=h||s,e=H(l,t,n),o=B(e,t,n),u=null!==o&&o!==g;!u&&null!==o||(g&&a("out"),g=o,u&&a("over"));r=Q(i);if(o!==f||!h||y.copySortSource){var c,e=D(o,e);if(null!==e)c=F(o,e,t,n);else{if(!0!==y.revertOnSpill||h)return void(h&&r&&r.removeChild(i));c=v,o=f}(null===c&&u||c!==i&&c!==Z(i))&&(p=c,o.insertBefore(i,c),w.emit("shadow",i,o,f))}else r&&r.removeChild(i)}function a(e){w.emit(e,i,g,f)}}function D(e,t){for(var n=t;n!==e&&Q(n)!==e;)n=Q(n);return n===q?null:n}function F(r,t,i,u){var c="horizontal"===y.direction;return(t!==r?function(){var e=t.getBoundingClientRect();if(c)return n(i>e.left+G(e)/2);return n(u>e.top+J(e)/2)}:function(){var e,t,n,o=r.children.length;for(e=0;e<o;e++){if(t=r.children[e],n=t.getBoundingClientRect(),c&&n.left+n.width/2>i)return t;if(!c&&n.top+n.height/2>u)return t}return null})();function n(e){return e?Z(t):t}}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./classes":1,"contra/emitter":5,crossvent:6}],3:[function(e,t,n){t.exports=function(e,t){return Array.prototype.slice.call(e,t)}},{}],4:[function(e,t,n){"use strict";var o=e("ticky");t.exports=function(e,t,n){e&&o(function(){e.apply(n||null,t||[])})}},{ticky:10}],5:[function(e,t,n){"use strict";var c=e("atoa"),a=e("./debounce");t.exports=function(r,e){var i=e||{},u={};return void 0===r&&(r={}),r.on=function(e,t){return u[e]?u[e].push(t):u[e]=[t],r},r.once=function(e,t){return t._once=!0,r.on(e,t),r},r.off=function(e,t){var n=arguments.length;if(1===n)delete u[e];else if(0===n)u={};else{e=u[e];if(!e)return r;e.splice(e.indexOf(t),1)}return r},r.emit=function(){var e=c(arguments);return r.emitterSnapshot(e.shift()).apply(this,e)},r.emitterSnapshot=function(o){var e=(u[o]||[]).slice(0);return function(){var t=c(arguments),n=this||r;if("error"===o&&!1!==i.throws&&!e.length)throw 1===t.length?t[0]:t;return e.forEach(function(e){i.async?a(e,t,n):e.apply(n,t),e._once&&r.off(o,e)}),r}},r}},{"./debounce":4,atoa:3}],6:[function(n,o,e){(function(r){"use strict";var i=n("custom-event"),u=n("./eventmap"),c=r.document,e=function(e,t,n,o){return e.addEventListener(t,n,o)},t=function(e,t,n,o){return e.removeEventListener(t,n,o)},a=[];function l(e,t,n){t=function(e,t,n){var o,r;for(o=0;o<a.length;o++)if((r=a[o]).element===e&&r.type===t&&r.fn===n)return o}(e,t,n);if(t){n=a[t].wrapper;return a.splice(t,1),n}}r.addEventListener||(e=function(e,t,n){return e.attachEvent("on"+t,function(e,t,n){var o=l(e,t,n)||function(n,o){return function(e){var t=e||r.event;t.target=t.target||t.srcElement,t.preventDefault=t.preventDefault||function(){t.returnValue=!1},t.stopPropagation=t.stopPropagation||function(){t.cancelBubble=!0},t.which=t.which||t.keyCode,o.call(n,t)}}(e,n);return a.push({wrapper:o,element:e,type:t,fn:n}),o}(e,t,n))},t=function(e,t,n){n=l(e,t,n);if(n)return e.detachEvent("on"+t,n)}),o.exports={add:e,remove:t,fabricate:function(e,t,n){var o=-1===u.indexOf(t)?new i(t,{detail:n}):function(){var e;c.createEvent?(e=c.createEvent("Event")).initEvent(t,!0,!0):c.createEventObject&&(e=c.createEventObject());return e}();e.dispatchEvent?e.dispatchEvent(o):e.fireEvent("on"+t,o)}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./eventmap":7,"custom-event":8}],7:[function(e,r,t){(function(e){"use strict";var t=[],n="",o=/^on/;for(n in e)o.test(n)&&t.push(n.slice(2));r.exports=t}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],8:[function(e,n,t){(function(e){var t=e.CustomEvent;n.exports=function(){try{var e=new t("cat",{detail:{foo:"bar"}});return"cat"===e.type&&"bar"===e.detail.foo}catch(e){}}()?t:"undefined"!=typeof document&&"function"==typeof document.createEvent?function(e,t){var n=document.createEvent("CustomEvent");return t?n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail):n.initCustomEvent(e,!1,!1,void 0),n}:function(e,t){var n=document.createEventObject();return n.type=e,t?(n.bubbles=Boolean(t.bubbles),n.cancelable=Boolean(t.cancelable),n.detail=t.detail):(n.bubbles=!1,n.cancelable=!1,n.detail=void 0),n}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],9:[function(e,t,n){var o,r,t=t.exports={};function i(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function c(t){if(o===setTimeout)return setTimeout(t,0);if((o===i||!o)&&setTimeout)return o=setTimeout,setTimeout(t,0);try{return o(t,0)}catch(e){try{return o.call(null,t,0)}catch(e){return o.call(this,t,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:i}catch(e){o=i}try{r="function"==typeof clearTimeout?clearTimeout:u}catch(e){r=u}}();var a,l=[],f=!1,s=-1;function d(){f&&a&&(f=!1,a.length?l=a.concat(l):s=-1,l.length&&m())}function m(){if(!f){var e=c(d);f=!0;for(var t=l.length;t;){for(a=l,l=[];++s<t;)a&&a[s].run();s=-1,t=l.length}a=null,f=!1,function(t){if(r===clearTimeout)return clearTimeout(t);if((r===u||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(t);try{r(t)}catch(e){try{return r.call(null,t)}catch(e){return r.call(this,t)}}}(e)}}function v(e,t){this.fun=e,this.array=t}function p(){}t.nextTick=function(e){var t=new Array(arguments.length-1);if(1<arguments.length)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];l.push(new v(e,t)),1!==l.length||f||c(m)},v.prototype.run=function(){this.fun.apply(null,this.array)},t.title="browser",t.browser=!0,t.env={},t.argv=[],t.version="",t.versions={},t.on=p,t.addListener=p,t.once=p,t.off=p,t.removeListener=p,t.removeAllListeners=p,t.emit=p,t.prependListener=p,t.prependOnceListener=p,t.listeners=function(e){return[]},t.binding=function(e){throw new Error("process.binding is not supported")},t.cwd=function(){return"/"},t.chdir=function(e){throw new Error("process.chdir is not supported")},t.umask=function(){return 0}},{}],10:[function(e,n,t){(function(t){var e="function"==typeof t?function(e){t(e)}:function(e){setTimeout(e,0)};n.exports=e}).call(this,e("timers").setImmediate)},{timers:11}],11:[function(a,e,l){(function(e,t){var o=a("process/browser.js").nextTick,n=Function.prototype.apply,r=Array.prototype.slice,i={},u=0;function c(e,t){this._id=e,this._clearFn=t}l.setTimeout=function(){return new c(n.call(setTimeout,window,arguments),clearTimeout)},l.setInterval=function(){return new c(n.call(setInterval,window,arguments),clearInterval)},l.clearTimeout=l.clearInterval=function(e){e.close()},c.prototype.unref=c.prototype.ref=function(){},c.prototype.close=function(){this._clearFn.call(window,this._id)},l.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},l.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},l._unrefActive=l.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;0<=t&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},l.setImmediate="function"==typeof e?e:function(e){var t=u++,n=!(arguments.length<2)&&r.call(arguments,1);return i[t]=!0,o(function(){i[t]&&(n?e.apply(null,n):e.call(null),l.clearImmediate(t))}),t},l.clearImmediate="function"==typeof t?t:function(e){delete i[e]}}).call(this,a("timers").setImmediate,a("timers").clearImmediate)},{"process/browser.js":9,timers:11}]},{},[2])(2)});
\ No newline at end of file
......@@ -23,26 +23,31 @@
const callback_success = function(response) {
let table_html = '';
let unresolved_constraint_violations = 0;
if(response.length > 0) {
// Update violation count badge
$('#violationCountBadge').html(response.length).removeClass('bg-success').addClass('bg-warning');
// Update violations table
for(let i=0;i<response.length;i++) {
if(response[i].manually_resolved)
if(response[i].manually_resolved) {
table_html += '<tr class="text-muted"><td class="nowrap">{% fa6_icon "check" "fas" %}</td>';
else
}
else {
table_html += '<tr><td></td>';
unresolved_constraint_violations++;
}
table_html += "<td>" + response[i].level_display + "</td><td>" + response[i].type_display + "</td><td>" + response[i].details + "</td><td class='nowrap'>" + response[i].timestamp_display + "</td><td><a href='" + response[i].edit_url + "'><i class='btn btn-primary fa fa-pen'></i></a></td></tr>";
}
}
else {
// Update violation count badge
$('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
// Update violations table
table_html ='<tr class="text-muted"><td colspan="5" class="text-center">{% trans "No violations" %}</td></tr>'
}
// Update violation count badge
if(unresolved_constraint_violations > 0)
$('#violationCountBadge').html(unresolved_constraint_violations).removeClass('bg-success').addClass('bg-warning');
else
$('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
// Show violation list (potentially empty) in violations table
$('#violationsTableBody').html(table_html);
......
......@@ -15,6 +15,7 @@
<script src="{% static "common/vendor/sortable/Sortable.min.js" %}"></script>
<script src="{% static "common/vendor/sortable/jquery-sortable.js" %}"></script>
<script src="{% static "AKScheduling/vendor/dragula/dragula.js" %}"></script>
<style>
.ak-list {
......@@ -30,8 +31,18 @@
.track-delete {
cursor: pointer;
}
.card-header {
cursor: move;
}
.card {
padding:0!important;
}
</style>
<link rel="stylesheet" href="{% static "AKScheduling/vendor/dragula/dragula.css" %}">
<script>
document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication
......@@ -188,6 +199,13 @@
});
}
});
// Make track containers sortable (when dragging the headers)
dragula([$('#workspace')[0]], {
moves: function (el, container, handle) {
return handle.classList.contains('card-header');
}
});
});
</script>
{% endblock extrahead %}
......@@ -206,7 +224,7 @@
<ul data-id="None" data-sync="false" class="ak-list">
{% for ak in aks_without_track %}
<li data-ak-id="{{ ak.pk }}" data-bs-toggle="tooltip" data-placement="top" title="">
{{ ak.name }} ({{ ak.category }})
{{ ak.name }} <span style="color:{{ ak.category.color }}">({{ ak.category }})</span>
</li>
{% endfor %}
</ul>
......@@ -225,7 +243,7 @@
<ul data-track-id="{{ track.pk }}" data-name="{{ track }}" data-sync="true" class="ak-list">
{% for ak in track.aks_with_category %}
<li data-ak-id="{{ ak.pk }}" data-bs-toggle="tooltip" data-placement="top" title="">
{{ ak.name }} ({{ ak.category }})
{{ ak.name }} <span style="color:{{ ak.category.color }}">({{ ak.category }})</span>
</li>
{% endfor %}
</ul>
......
......@@ -157,6 +157,7 @@
$('#id_start').val(info.startStr);
$('#id_end').val(info.endStr);
$('#id_room').val(info.resource._resource.id);
$('#id_room_name').val(info.resource._resource.title);
$('#id_duration').val(Math.abs(info.end-info.start)/1000/60/60);
$('#id_ak').val("");
$('#newAKSlotModal').modal('show');
......@@ -215,20 +216,24 @@
if(response.length > 0) {
// Update violations table
for(let i=0;i<response.length;i++) {
if(response[i].manually_resolved)
table_html += '<tr class="text-muted"><td class="nowrap">{% fa6_icon "check" "fas" %} ';
let icon_html = '';
let muted_html = '';
if(response[i].manually_resolved) {
icon_html = '{% fa6_icon "check" "fas" %} ';
muted_html = 'text-muted';
}
else {
table_html += '<tr><td>';
unresolved_violations_count++;
}
if(response[i].level_display==='{% trans "Violation" %}')
table_html += '{% fa6_icon "exclamation-triangle" "fas" %}';
icon_html += '{% fa6_icon "exclamation-triangle" "fas" %}';
else
table_html += '{% fa6_icon "info-circle" "fas" %}';
icon_html += '{% fa6_icon "info-circle" "fas" %}';
table_html += '<tr class="'+ muted_html+ '"><td class="nowrap">' + icon_html;
table_html += "</td><td class='small'>" + response[i].type_display + "</td></tr>";
table_html += "<tr><td colspan='2' class='small'>" + response[i].details + "</td></tr>"
table_html += "<tr class='" + muted_html + "'><td colspan='2' class='small'>" + response[i].details + "</td></tr>"
}
}
else {
......@@ -238,7 +243,7 @@
// Update violation count badge
if(unresolved_violations_count > 0)
$('#violationCountBadge').html(response.length).removeClass('bg-success').addClass('bg-warning');
$('#violationCountBadge').html(unresolved_violations_count).removeClass('bg-success').addClass('bg-warning');
else
$('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
......@@ -350,15 +355,17 @@
<h5 class="mt-2">{{ track_slots.grouper }}</h5>
{% endif %}
{% for slot in track_slots.list %}
<div class="unscheduled-slot badge bg-primary" style='background-color: {{ slot.ak.category.color }}'
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ slot.ak.details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}", "url": "{% url "admin:AKModel_akslot_change" slot.pk %}"}' data-details="{{ slot.ak.details }}">{{ slot.ak.short_name }}
<div class="unscheduled-slot badge" style='background-color: {% with slot.ak.category.color as color %} {% if color %}{{ color }}{% else %}#000000;{% endif %}{% endwith %}'
{% with slot.ak.details as details %}
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}", "url": "{% url "admin:AKModel_akslot_change" slot.pk %}"}' data-details="{{ details }}">{{ slot.ak.short_name }}
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }}
{% endwith %}
</div>
{% endfor %}
{% endfor %}
</div>
<div class="tab-pane fade" id="violations">
<table class="table table-striped mt-4 mb-4">
<table class="table mt-4 mb-4">
<thead>
<tr>
<th>{% trans "Level" %}</th>
......
......@@ -13,14 +13,19 @@
{% block content %}
<h4 class="mt-4 mb-4">{% trans "AKs with public notes" %}</h4>
{% for ak in aks_with_comment %}
<a href="{{ ak.edit_url }}">{{ ak }}</a><br>{{ ak.notes }}<br><br>
<a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{{ ak.notes }}<br><br>
{% empty %}
-
{% endfor %}
<h4 class="mt-4 mb-4">{% trans "AKs without availabilities" %}</h4>
{% for ak in aks_without_availabilities %}
<a href="{{ ak.edit_url }}">{{ ak }}</a><br>
<a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %}
-<br>
{% endfor %}
......@@ -30,7 +35,10 @@
<h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4>
{% for ak in ak_wishes_with_slots %}
<a href="{{ ak.detail_url }}">{{ ak }}</a> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a><br>
<a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a>
<a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %}
-<br>
{% endfor %}
......@@ -39,7 +47,9 @@
<h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4>
{% for ak in aks_without_slots %}
<a href="{{ ak.detail_url }}">{{ ak }}</a><br>
<a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %}
-<br>
{% endfor %}
......
{% load i18n %}
<div class="text-center">
<a href="{% url 'admin:constraint-violations' slug=event.slug %}">
<h1>{{ constraint_violations_count }}</h1>
{% blocktrans count constraint_violations_count=constraint_violations_count %}
<h3>Constraint Violation</h3>
{% plural %}
<h3>Constraint Violations</h3>
{% endblocktrans %}
</a>
</div>
import json
from datetime import timedelta
from django.test import TestCase
from AKModel.tests import BasicViewTests
from django.utils import timezone
from AKModel.tests import BasicViewTests
from AKModel.models import AKSlot, Event, Room
class ModelViewTests(BasicViewTests, TestCase):
"""
Tests for AKScheduling
"""
fixtures = ['model.json']
VIEWS_STAFF_ONLY = [
# Views
('admin:schedule', {'event_slug': 'kif42'}),
('admin:slots_unscheduled', {'event_slug': 'kif42'}),
('admin:constraint-violations', {'slug': 'kif42'}),
......@@ -14,4 +23,66 @@ class ModelViewTests(BasicViewTests, TestCase):
('admin:autocreate-availabilities', {'event_slug': 'kif42'}),
('admin:tracks_manage', {'event_slug': 'kif42'}),
('admin:enter-interest', {'event_slug': 'kif42', 'pk': 1}),
# API (Read)
('model:scheduling-resources-list', {'event_slug': 'kif42'}, 403),
('model:scheduling-constraint-violations-list', {'event_slug': 'kif42'}, 403),
('model:scheduling-events', {'event_slug': 'kif42'}),
('model:scheduling-room-availabilities', {'event_slug': 'kif42'}),
('model:scheduling-default-slots', {'event_slug': 'kif42'}),
]
def test_scheduling_of_slot_update(self):
"""
Test rescheduling a slot to a different time or room
"""
self.client.force_login(self.admin_user)
event = Event.get_by_slug('kif42')
# Get the first already scheduled slot belonging to this event
slot = event.akslot_set.filter(start__isnull=False).first()
pk = slot.pk
room_id = slot.room_id
events_api_url = f"/kif42/api/scheduling-event/{pk}/"
# Create updated time
offset = timedelta(hours=1)
new_start_time = slot.start + offset
new_end_time = slot.end + offset
new_start_time_string = timezone.localtime(new_start_time, event.timezone).strftime("%Y-%m-%d %H:%M:%S")
new_end_time_string = timezone.localtime(new_end_time, event.timezone).strftime("%Y-%m-%d %H:%M:%S")
# Try API call
response = self.client.put(
events_api_url,
json.dumps({
'start': new_start_time_string,
'end': new_end_time_string,
'roomId': room_id,
}),
content_type = 'application/json'
)
self.assertEqual(response.status_code, 200, "PUT to API endpoint did not work")
# Make sure API call did update the slot as expected
slot = AKSlot.objects.get(pk=pk)
self.assertEqual(new_start_time, slot.start, "Update did not work")
# Test updating room
new_room = Room.objects.exclude(pk=room_id).first()
# Try second API call
response = self.client.put(
events_api_url,
json.dumps({
'start': new_start_time_string,
'end': new_end_time_string,
'roomId': new_room.pk,
}),
content_type = 'application/json'
)
self.assertEqual(response.status_code, 200, "Second PUT to API endpoint did not work")
# Make sure API call did update the slot as expected
slot = AKSlot.objects.get(pk=pk)
self.assertEqual(new_room.pk, slot.room.pk, "Update did not work")
......@@ -5,13 +5,17 @@ from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView, DetailView, UpdateView
from AKModel.metaviews import status_manager
from AKModel.metaviews.status import TemplateStatusWidget
from AKModel.models import AKSlot, AKTrack, Event, AK, AKCategory
from AKModel.metaviews.admin import EventSlugMixin, FilterByEventSlugMixin, AdminViewMixin, IntermediateAdminView
from AKScheduling.forms import AKInterestForm
from AKSubmission.forms import AKAddSlotForm
from AKScheduling.forms import AKInterestForm, AKAddSlotForm
class UnscheduledSlotsAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
"""
Admin view: Get a list of all unscheduled slots
"""
template_name = "admin/AKScheduling/unscheduled.html"
model = AKSlot
context_object_name = "akslots"
......@@ -26,12 +30,20 @@ class UnscheduledSlotsAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView
class SchedulingAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
"""
Admin view: Scheduler
View and adapt the schedule of an event. This view heavily uses JavaScript to display a calendar view plus
a list of unscheduled slots and to allow dragging slots in and into the calendar
"""
template_name = "admin/AKScheduling/scheduling.html"
model = AKSlot
context_object_name = "slots_unscheduled"
def get_queryset(self):
return super().get_queryset().filter(start__isnull=True).select_related('event', 'ak').order_by('ak__track')
return super().get_queryset().filter(start__isnull=True).select_related('event', 'ak', 'ak__track',
'ak__category').prefetch_related('ak__types', 'ak__owners', 'ak__conflicts', 'ak__prerequisites',
'ak__requirements', 'ak__conflict').order_by('ak__track', 'ak')
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
......@@ -47,6 +59,12 @@ class SchedulingAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
class TrackAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
"""
Admin view: Distribute AKs to tracks
Again using JavaScript, the user can here see a list of all AKs split-up by tracks and can move them to other or
even new tracks using drag and drop. The state is then automatically synchronized via API calls in the background
"""
template_name = "admin/AKScheduling/manage_tracks.html"
model = AKTrack
context_object_name = "tracks"
......@@ -58,6 +76,12 @@ class TrackAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
class ConstraintViolationsAdminView(AdminViewMixin, DetailView):
"""
Admin view: Inspect and adjust all constraint violations of the event
This view populates a table of constraint violations via background API call (JavaScript), offers the option to
see details or edit each of them and provides an auto-reload feature.
"""
template_name = "admin/AKScheduling/constraint_violations.html"
model = Event
context_object_name = "event"
......@@ -69,6 +93,10 @@ class ConstraintViolationsAdminView(AdminViewMixin, DetailView):
class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView):
"""
Admin view: List all AKs that require special attention via scheduling, e.g., because of free-form comments,
since there are slots even though it is a wish, or no slots even though it is an AK etc.
"""
template_name = "admin/AKScheduling/special_attention.html"
model = Event
context_object_name = "event"
......@@ -77,12 +105,16 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView):
context = super().get_context_data(**kwargs)
context["title"] = f"{_('AKs requiring special attention for')} {context['event']}"
aks = AK.objects.filter(event=context["event"]).annotate(Count('owners', distinct=True)).annotate(Count('akslot', distinct=True)).annotate(Count('availabilities', distinct=True))
# Load all "special" AKs from the database using annotations to reduce the amount of necessary queries
aks = (AK.objects.filter(event=context["event"]).annotate(Count('owners', distinct=True))
.annotate(Count('akslot', distinct=True)).annotate(Count('availabilities', distinct=True)))
aks_with_comment = []
ak_wishes_with_slots = []
aks_without_availabilities = []
aks_without_slots = []
# Loop over all AKs of this event and identify all relevant factors that make the AK "special" and add them to
# the respective lists if the AK fullfills an condition
for ak in aks:
if ak.notes != "":
aks_with_comment.append(ak)
......@@ -105,6 +137,14 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView):
class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMixin, UpdateView):
"""
Admin view: Form view to quickly store information about the interest in an AK
(e.g., during presentation of the AK list)
The view offers a field to update interest and manually set a comment for the current AK, but also features links
to the AKs before and probably coming up next, as well as links to other AKs sorted by category, for quick
and hazzle-free navigation during the AK presentation
"""
template_name = "admin/AKScheduling/interest.html"
model = AK
context_object_name = "ak"
......@@ -114,6 +154,13 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
def get_success_url(self):
return self.request.path
def form_valid(self, form):
# Don't create a history entry for this change
form.instance.skip_history_when_saving = True
r = super().form_valid(form)
del form.instance.skip_history_when_saving
return r
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = f"{_('Enter interest')}"
......@@ -127,6 +174,8 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
last_ak = None
next_is_next = False
# Building the right navigation is a bit tricky since wishes have to be treated as an own category here
# Hence, depending on the AK we are currently at (displaying the form for) we need to either:
# Find other AK wishes (regardless of the category)...
if context['ak'].wish:
other_aks = [ak for ak in context['event'].ak_set.prefetch_related('owners').all() if ak.wish]
......@@ -134,6 +183,7 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
else:
other_aks = [ak for ak in context['ak'].category.ak_set.prefetch_related('owners').all() if not ak.wish]
# Use that list of other AKs belonging to this category to identify the previous and next AK (if any)
for other_ak in other_aks:
if next_is_next:
context['next_ak'] = other_ak
......@@ -143,6 +193,7 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
next_is_next = True
last_ak = other_ak
# Gather information for link lists for all categories (and wishes)
for category in context['event'].akcategory_set.prefetch_related('ak_set').all():
aks_for_category = []
for ak in category.ak_set.prefetch_related('owners').all():
......@@ -152,6 +203,8 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
aks_for_category.append(ak)
categories_with_aks.append((category, aks_for_category))
# Make sure wishes have the right order (since the list was filled category by category before, this requires
# explicitly reordering them by their primary key)
ak_wishes.sort(key=lambda x: x.pk)
categories_with_aks.append(
(AKCategory(name=_("Wishes"), pk=0, description="-"), ak_wishes))
......@@ -162,6 +215,16 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView):
"""
Admin action view: Allow to delete all unscheduled slots for wishes
The view will render a preview of all slots that are affected by this. It is not possible to manually choose
which slots should be deleted (either all or none) and the functionality will therefore delete slots that were
created in the time between rendering of the preview and running the action ofter confirmation as well.
Due to the automated slot cleanup functionality for wishes in the AKSubmission app, this functionality should be
rarely needed/used
"""
title = _('Cleanup: Delete unscheduled slots for wishes')
def get_success_url(self):
......@@ -181,6 +244,15 @@ class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView):
class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView):
"""
Admin action view: Allow to automatically create default availabilities (event start to end) for all AKs without
any manually specified availability information
The view will render a preview of all AKs that are affected by this. It is not possible to manually choose
which AKs should be affected (either all or none) and the functionality will therefore create availability entries
for AKs that were created in the time between rendering of the preview and running the action ofter confirmation
as well.
"""
title = _('Create default availabilities for AKs')
def get_success_url(self):
......@@ -195,6 +267,8 @@ class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView):
)
def form_valid(self, form):
# Local import to prevent cyclic imports
# pylint: disable=import-outside-toplevel
from AKModel.availability.models import Availability
success_count = 0
......@@ -203,7 +277,7 @@ class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView):
availability = Availability.with_event_length(event=self.event, ak=ak)
availability.save()
success_count += 1
except:
except: # pylint: disable=bare-except
messages.add_message(
self.request, messages.WARNING,
_("Could not create default availabilities for AK: {ak}").format(ak=ak)
......@@ -214,3 +288,22 @@ class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView):
_("Created default availabilities for {count} AKs").format(count=success_count)
)
return super().form_valid(form)
@status_manager.register(name="scheduling_constraint_violations")
class CVWidget(TemplateStatusWidget):
"""
Status page widget: Constraint violations
"""
required_context_type = "event"
title = _("Constraint Violations")
template_name = "admin/AKScheduling/status/cvs.html"
def render_status(self, context: {}) -> str:
return "success" if context["constraint_violations_count"] == 0 else "warning"
def get_context_data(self, context) -> dict:
context = super().get_context_data(context)
context["constraint_violations_count"] = (context["event"].constraintviolation_set
.filter(manually_resolved=False).count())
return context
# Register your models here.
from datetime import datetime
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django.utils.datetime_safe import datetime
from AKModel.models import AK
......@@ -17,17 +18,21 @@ def ak_interest_indication_active(event, current_timestamp):
:return: True if indication is allowed, False if not
:rtype: Bool
"""
return event.active and (event.interest_start is None or (event.interest_start <= current_timestamp and (
event.interest_end is None or current_timestamp <= event.interest_end)))
return (event.active and event.interest_start is not None and event.interest_end is not None
and event.interest_start <= current_timestamp <= event.interest_end)
@api_view(['POST'])
def increment_interest_counter(request, event_slug, pk, **kwargs):
"""
Increment interest counter for AK
This view either returns an HTTP 200 if the counter was incremented,
an HTTP 403 if indicating interest is currently not allowed,
or an HTTP 404 if there is no matching AK for the given primary key and event slug.
"""
try:
ak = AK.objects.get(pk=pk)
ak = AK.objects.get(pk=pk, event__slug=event_slug)
# Check whether interest indication is currently allowed
current_timestamp = datetime.now().astimezone(ak.event.timezone)
if ak_interest_indication_active(ak.event, current_timestamp):
......
......@@ -2,4 +2,7 @@ from django.apps import AppConfig
class AksubmissionConfig(AppConfig):
"""
App configuration (default, only specifies name of the app)
"""
name = 'AKSubmission'
"""
Submission-specific forms
"""
import itertools
import re
......@@ -7,10 +11,21 @@ from django.utils.translation import gettext_lazy as _
from AKModel.availability.forms import AvailabilitiesFormMixin
from AKModel.availability.models import Availability
from AKModel.models import AK, AKOwner, AKCategory, AKRequirement, AKSlot, AKOrgaMessage, Event
from AKModel.models import AK, AKOwner, AKCategory, AKRequirement, AKSlot, AKOrgaMessage, AKType
class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
"""
Base form to add and edit AKs
Contains suitable widgets for the different data types, restricts querysets (e.g., of requirements) to entries
belonging to the event this AK belongs to.
Prepares initial slot creation (by accepting multiple input formats and a list of slots to generate),
automatically generate short names and wiki links if necessary
Will be modified/used by :class:`AKSubmissionForm` (that allows to add slots and excludes links)
and :class:`AKWishForm`
"""
required_css_class = 'required'
split_string = re.compile('[,;]')
......@@ -18,11 +33,11 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
model = AK
fields = ['name',
'short_name',
'link',
'protocol_link',
'owners',
'description',
'category',
'types',
'reso',
'present',
'requirements',
......@@ -34,6 +49,7 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
widgets = {
'requirements': forms.CheckboxSelectMultiple,
'types': forms.CheckboxSelectMultiple,
'event': forms.HiddenInput,
}
......@@ -47,7 +63,14 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
self.fields["prerequisites"].widget.attrs = {'class': 'chosen-select'}
self.fields['category'].queryset = AKCategory.objects.filter(event=self.initial.get('event'))
self.fields['types'].queryset = AKType.objects.filter(event=self.initial.get('event'))
# Don't ask for types if there are no types configured for this event
if self.fields['types'].queryset.count() == 0:
self.fields.pop('types')
self.fields['requirements'].queryset = AKRequirement.objects.filter(event=self.initial.get('event'))
# Don't ask for requirements if there are no requirements configured for this event
if self.fields['requirements'].queryset.count() == 0:
self.fields.pop('requirements')
self.fields['prerequisites'].queryset = AK.objects.filter(event=self.initial.get('event')).exclude(
pk=self.instance.pk)
self.fields['conflicts'].queryset = AK.objects.filter(event=self.initial.get('event')).exclude(
......@@ -57,7 +80,14 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
@staticmethod
def _clean_duration(duration):
# Handle different duration formats (h:mm and decimal comma instead of point)
"""
Clean/convert input format for the duration(s) of the slot(s)
Handle different duration formats (h:mm and decimal comma instead of point)
:param duration: raw input, either with ":", "," or "."
:return: normalized duration (point-separated hour float)
"""
if ":" in duration:
h, m = duration.split(":")
duration = int(h) + int(m) / 60
......@@ -65,40 +95,47 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
duration = float(duration.replace(",", "."))
try:
float(duration)
except ValueError:
duration = float(duration)
except ValueError as exc:
raise ValidationError(
_('"%(duration)s" is not a valid duration'),
code='invalid',
params={'duration': duration},
)
) from exc
return duration
def clean(self):
"""
Normalize/clean inputs
Generate a (not yet used) short name if field was left blank,
create a list of normalized slot durations
:return: cleaned inputs
"""
cleaned_data = super().clean()
# Generate short name if not given
short_name = self.cleaned_data["short_name"]
if len(short_name) == 0:
short_name = self.cleaned_data['name']
# First try to split AK name at positions with semantic value (e.g., where the full name is separated
# by a ':'), if not possible, do a hard cut at the maximum specified length
short_name = short_name.partition(':')[0]
short_name = short_name.partition(' - ')[0]
short_name = short_name.partition(' (')[0]
short_name = short_name[:AK._meta.get_field('short_name').max_length]
# Check whether this short name already exists...
for i in itertools.count(1):
# ...and either use it...
if not AK.objects.filter(short_name=short_name, event=self.cleaned_data["event"]).exists():
break
# ... or postfix a number starting at 1 and growing until an unused short name is found
digits = len(str(i))
short_name = '{}-{}'.format(short_name[:-(digits + 1)], i)
short_name = f'{short_name[:-(digits + 1)]}-{i}'
cleaned_data["short_name"] = short_name
# Generate wiki link
if self.cleaned_data["event"].base_url:
link = self.cleaned_data["event"].base_url + self.cleaned_data["name"].replace(" ", "_")
# Truncate links longer than 200 characters (default length of URL fields in django)
self.cleaned_data["link"] = link[:200]
# Get durations from raw durations field
if "durations" in cleaned_data:
cleaned_data["durations"] = [self._clean_duration(d) for d in self.cleaned_data["durations"].split()]
......@@ -106,35 +143,63 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
class AKSubmissionForm(AKForm):
"""
Form for Submitting new AKs
Is a special variant of :class:`AKForm` that does not allow to manually edit wiki and protocol links and enforces
the generation of at least one slot.
"""
class Meta(AKForm.Meta):
exclude = ['link', 'protocol_link']
# Exclude fields again that were previously included in the parent class
exclude = ['link', 'protocol_link'] #pylint: disable=modelform-uses-exclude
widgets = AKForm.Meta.widgets | {
'types': forms.CheckboxSelectMultiple(attrs={'checked' : 'checked'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add field for durations
# Add field for durations (cleaning will be handled by parent class)
self.fields["durations"] = forms.CharField(
widget=forms.Textarea,
label=_("Duration(s)"),
help_text=_(
"Enter at least one planned duration (in hours). If your AK should have multiple slots, use multiple lines"),
initial=
self.initial.get('event').default_slot
"Enter at least one planned duration (in hours). "
"If your AK should have multiple slots, use multiple lines"),
initial=self.initial.get('event').default_slot
)
def clean_availabilities(self):
"""
Automatically improve availabilities entered.
If the user did not specify availabilities assume the full event duration is possible
:return: cleaned availabilities
(either user input or one availability for the full length of the event if user input was empty)
"""
availabilities = super().clean_availabilities()
# If the user did not specify availabilities assume the full event duration is possible
if len(availabilities) == 0:
availabilities.append(Availability.with_event_length(event=self.cleaned_data["event"]))
return availabilities
class AKWishForm(AKForm):
"""
Form for submitting or editing wishes
Is a special variant of :class:`AKForm` that does not allow to specify owner(s) or
manually edit wiki and protocol links
"""
class Meta(AKForm.Meta):
exclude = ['owners', 'link', 'protocol_link']
# Exclude fields again that were previously included in the parent class
exclude = ['owners', 'link', 'protocol_link'] #pylint: disable=modelform-uses-exclude
widgets = AKForm.Meta.widgets | {
'types': forms.CheckboxSelectMultiple(attrs={'checked': 'checked'}),
}
class AKOwnerForm(forms.ModelForm):
"""
Form to create/edit AK owners
"""
required_css_class = 'required'
class Meta:
......@@ -146,6 +211,9 @@ class AKOwnerForm(forms.ModelForm):
class AKDurationForm(forms.ModelForm):
"""
Form to add an additional slot to a given AK
"""
class Meta:
model = AKSlot
fields = ['duration', 'ak', 'event']
......@@ -156,6 +224,9 @@ class AKDurationForm(forms.ModelForm):
class AKOrgaMessageForm(forms.ModelForm):
"""
Form to create a confidential message to the organizers belonging to a given AK
"""
class Meta:
model = AKOrgaMessage
fields = ['ak', 'text', 'event']
......@@ -164,14 +235,3 @@ class AKOrgaMessageForm(forms.ModelForm):
'event': forms.HiddenInput,
'text': forms.Textarea,
}
class AKAddSlotForm(forms.Form):
start = forms.CharField(label=_("Start"), disabled=True)
end = forms.CharField(label=_("End"), disabled=True)
duration = forms.CharField(label=_("Duration"), disabled=True)
room = forms.IntegerField(label=_("Room"), disabled=True)
def __init__(self, event):
super().__init__()
self.fields['ak'] = forms.ModelChoiceField(event.ak_set.all(), label=_("AK"))
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-05-15 20:03+0200\n"
"POT-Creation-Date: 2025-03-25 15:58+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,16 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: AKSubmission/forms.py:71
#: AKSubmission/forms.py:101
#, python-format
msgid "\"%(duration)s\" is not a valid duration"
msgstr "\"%(duration)s\" ist keine gültige Dauer"
#: AKSubmission/forms.py:117
#: AKSubmission/forms.py:164
msgid "Duration(s)"
msgstr "Dauer(n)"
#: AKSubmission/forms.py:119
#: AKSubmission/forms.py:166
msgid ""
"Enter at least one planned duration (in hours). If your AK should have "
"multiple slots, use multiple lines"
......@@ -34,33 +34,6 @@ msgstr ""
"Mindestens eine geplante Dauer (in Stunden) angeben. Wenn der AK mehrere "
"Slots haben soll, mehrere Zeilen verwenden"
#: AKSubmission/forms.py:170
#: AKSubmission/templates/AKSubmission/ak_detail.html:309
msgid "Start"
msgstr "Start"
#: AKSubmission/forms.py:171
#: AKSubmission/templates/AKSubmission/ak_detail.html:310
msgid "End"
msgstr "Ende"
#: AKSubmission/forms.py:172
#: AKSubmission/templates/AKSubmission/ak_detail.html:239
#: AKSubmission/templates/AKSubmission/akslot_delete.html:35
msgid "Duration"
msgstr "Dauer"
#: AKSubmission/forms.py:173
#: AKSubmission/templates/AKSubmission/ak_detail.html:241
msgid "Room"
msgstr "Raum"
#: AKSubmission/forms.py:177
#: AKSubmission/templates/AKSubmission/ak_history.html:11
#: AKSubmission/templates/AKSubmission/akslot_delete.html:31
msgid "AK"
msgstr "AK"
#: AKSubmission/templates/AKSubmission/ak_detail.html:22
#: AKSubmission/templates/AKSubmission/ak_edit.html:13
#: AKSubmission/templates/AKSubmission/ak_history.html:16
......@@ -74,163 +47,176 @@ msgstr "AK"
#: AKSubmission/templates/AKSubmission/submission_overview.html:7
#: AKSubmission/templates/AKSubmission/submission_overview.html:11
#: AKSubmission/templates/AKSubmission/submission_overview.html:36
#: AKSubmission/templates/AKSubmission/submit_new.html:31
#: AKSubmission/templates/AKSubmission/submit_new.html:38
#: AKSubmission/templates/AKSubmission/submit_new_wish.html:13
msgid "AK Submission"
msgstr "AK-Eintragung"
#: AKSubmission/templates/AKSubmission/ak_detail.html:77
#: AKSubmission/templates/AKSubmission/ak_detail.html:126
#: AKSubmission/templates/AKSubmission/ak_interest_script.html:50
msgid "Interest indication currently not allowed. Sorry."
msgstr "Interessenangabe aktuell nicht erlaubt. Sorry."
#: AKSubmission/templates/AKSubmission/ak_detail.html:79
#: AKSubmission/templates/AKSubmission/ak_detail.html:128
#: AKSubmission/templates/AKSubmission/ak_interest_script.html:52
msgid "Could not save your interest. Sorry."
msgstr "Interesse konnte nicht gespeichert werden. Sorry."
#: AKSubmission/templates/AKSubmission/ak_detail.html:100
#: AKSubmission/templates/AKSubmission/ak_detail.html:149
msgid "Interest"
msgstr "Interesse"
#: AKSubmission/templates/AKSubmission/ak_detail.html:102
#: AKSubmission/templates/AKSubmission/ak_table.html:55
#: AKSubmission/templates/AKSubmission/ak_detail.html:151
#: AKSubmission/templates/AKSubmission/ak_table.html:65
msgid "Show Interest"
msgstr "Interesse bekunden"
#: AKSubmission/templates/AKSubmission/ak_detail.html:108
#: AKSubmission/templates/AKSubmission/ak_table.html:46
#: AKSubmission/templates/AKSubmission/ak_detail.html:157
#: AKSubmission/templates/AKSubmission/ak_table.html:56
msgid "Open external link"
msgstr "Externen Link öffnen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:113
#: AKSubmission/templates/AKSubmission/ak_detail.html:162
msgid "Open protocol link"
msgstr "Protokolllink öffnen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:118
#: AKSubmission/templates/AKSubmission/ak_detail.html:167
#: AKSubmission/templates/AKSubmission/ak_history.html:19
#: AKSubmission/templates/AKSubmission/ak_history.html:31
msgid "History"
msgstr "Versionsgeschichte"
#: AKSubmission/templates/AKSubmission/ak_detail.html:121
#: AKSubmission/templates/AKSubmission/ak_detail.html:170
#: AKSubmission/templates/AKSubmission/akmessage_add.html:8
#: AKSubmission/templates/AKSubmission/akmessage_add.html:16
#: AKSubmission/templates/AKSubmission/akmessage_add.html:22
msgid "Add confidential message to organizers"
msgstr "Sende eine private Nachricht an das Organisationsteam"
#: AKSubmission/templates/AKSubmission/ak_detail.html:124
#: AKSubmission/templates/AKSubmission/ak_detail.html:269
#: AKSubmission/templates/AKSubmission/ak_detail.html:173
#: AKSubmission/templates/AKSubmission/ak_detail.html:326
#: AKSubmission/templates/AKSubmission/ak_edit.html:16
#: AKSubmission/templates/AKSubmission/ak_table.html:51
#: AKSubmission/templates/AKSubmission/ak_table.html:61
msgid "Edit"
msgstr "Bearbeiten"
#: AKSubmission/templates/AKSubmission/ak_detail.html:129
#: AKSubmission/templates/AKSubmission/ak_detail.html:178
#: AKSubmission/templates/AKSubmission/ak_history.html:31
#: AKSubmission/templates/AKSubmission/ak_table.html:34
#: AKSubmission/templates/AKSubmission/ak_table.html:37
msgid "AK Wish"
msgstr "AK-Wunsch"
#: AKSubmission/templates/AKSubmission/ak_detail.html:136
#: AKSubmission/templates/AKSubmission/ak_detail.html:186
#, python-format
msgid ""
"\n"
" This AK currently takes place for another "
"%(featured_slot_remaining)s minute(s) in %(room)s.\n"
" &nbsp;\n"
" "
"This AK currently takes place for another <span v-html=\"timeUntilEnd\">"
"%(featured_slot_remaining)s</span> minute(s) in %(room)s.&nbsp;"
msgstr ""
"\n"
" Dieser AK findet noch %(featured_slot_remaining)s "
"Minute(n) in %(room)s statt.&nbsp;\n"
"Dieser AK findet noch <span v-html=\"timeUntilEnd\">"
"%(featured_slot_remaining)s</span> Minute(n) in %(room)s statt.&nbsp;\n"
" "
#: AKSubmission/templates/AKSubmission/ak_detail.html:142
#: AKSubmission/templates/AKSubmission/ak_detail.html:189
#, python-format
msgid ""
"\n"
" This AK starts in %(featured_slot_remaining)s "
"minute(s) in %(room)s.&nbsp;\n"
" "
"This AK starts in <span v-html=\"timeUntilStart\">"
"%(featured_slot_remaining)s</span> minute(s) in %(room)s.&nbsp;"
msgstr ""
"\n"
" This AK beginnt in %(featured_slot_remaining)s "
"Minute(n) in %(room)s.&nbsp;\n"
"Dieser AK beginnt in <span v-html=\"timeUntilStart\">"
"%(featured_slot_remaining)s</span> Minute(n) in %(room)s.&nbsp;\n"
" "
#: AKSubmission/templates/AKSubmission/ak_detail.html:149
#: AKSubmission/templates/AKSubmission/ak_detail.html:277
#: AKSubmission/templates/AKSubmission/ak_detail.html:194
#: AKSubmission/templates/AKSubmission/ak_detail.html:334
msgid "Go to virtual room"
msgstr "Zum virtuellen Raum"
#: AKSubmission/templates/AKSubmission/ak_detail.html:158
#: AKSubmission/templates/AKSubmission/ak_detail.html:205
#: AKSubmission/templates/AKSubmission/ak_table.html:10
msgid "Who?"
msgstr "Wer?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:164
#: AKSubmission/templates/AKSubmission/ak_detail.html:211
#: AKSubmission/templates/AKSubmission/ak_history.html:36
#: AKSubmission/templates/AKSubmission/ak_table.html:11
msgid "Category"
msgstr "Kategorie"
#: AKSubmission/templates/AKSubmission/ak_detail.html:171
#: AKSubmission/templates/AKSubmission/ak_detail.html:218
#: AKSubmission/templates/AKSubmission/ak_table.html:13
msgid "Types"
msgstr "Typen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:228
#: AKSubmission/templates/AKSubmission/ak_history.html:37
msgid "Track"
msgstr "Track"
#: AKSubmission/templates/AKSubmission/ak_detail.html:177
#, fuzzy
#| msgid "Present results of this AK"
#: AKSubmission/templates/AKSubmission/ak_detail.html:234
msgid "Present this AK"
msgstr "Die Ergebnisse dieses AKs vorstellen"
msgstr "Diesen AK vorstellen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:182
#: AKSubmission/templates/AKSubmission/ak_detail.html:239
msgid "(Category Default)"
msgstr "(Kategorievoreinstellung)"
#: AKSubmission/templates/AKSubmission/ak_detail.html:188
#: AKSubmission/templates/AKSubmission/ak_detail.html:245
msgid "Reso intention?"
msgstr "Resoabsicht?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:195
#: AKSubmission/templates/AKSubmission/ak_detail.html:252
msgid "Requirements"
msgstr "Anforderungen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:208
#: AKSubmission/templates/AKSubmission/ak_detail.html:265
msgid "Conflicting AKs"
msgstr "AK-Konflikte"
#: AKSubmission/templates/AKSubmission/ak_detail.html:216
#: AKSubmission/templates/AKSubmission/ak_detail.html:273
msgid "Prerequisite AKs"
msgstr "Vorausgesetzte AKs"
#: AKSubmission/templates/AKSubmission/ak_detail.html:224
#: AKSubmission/templates/AKSubmission/ak_detail.html:281
msgid "Notes"
msgstr "Notizen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:237
#: AKSubmission/templates/AKSubmission/ak_detail.html:294
msgid "When?"
msgstr "Wann?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:272
#: AKSubmission/templates/AKSubmission/ak_detail.html:296
#: AKSubmission/templates/AKSubmission/akslot_delete.html:35
msgid "Duration"
msgstr "Dauer"
#: AKSubmission/templates/AKSubmission/ak_detail.html:298
msgid "Room"
msgstr "Raum"
#: AKSubmission/templates/AKSubmission/ak_detail.html:329
msgid "Delete"
msgstr "Löschen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:283
#: AKSubmission/templates/AKSubmission/ak_detail.html:340
msgid "Schedule"
msgstr "Schedule"
#: AKSubmission/templates/AKSubmission/ak_detail.html:295
#: AKSubmission/templates/AKSubmission/ak_detail.html:352
msgid "Add another slot"
msgstr "Einen neuen AK-Slot hinzufügen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:305
#: AKSubmission/templates/AKSubmission/ak_detail.html:362
msgid "Possible Times"
msgstr "Mögliche Zeiten"
#: AKSubmission/templates/AKSubmission/ak_detail.html:366
msgid "Start"
msgstr "Start"
#: AKSubmission/templates/AKSubmission/ak_detail.html:367
msgid "End"
msgstr "Ende"
#: AKSubmission/templates/AKSubmission/ak_edit.html:8
#: AKSubmission/templates/AKSubmission/ak_history.html:11
#: AKSubmission/templates/AKSubmission/ak_overview.html:8
......@@ -260,6 +246,11 @@ msgstr ""
"Person hinzufügen, die noch nicht in der Liste ist. Ungespeicherte "
"Änderungen in diesem Formular gehen verloren."
#: AKSubmission/templates/AKSubmission/ak_history.html:11
#: AKSubmission/templates/AKSubmission/akslot_delete.html:31
msgid "AK"
msgstr "AK"
#: AKSubmission/templates/AKSubmission/ak_history.html:27
msgid "Back"
msgstr "Zurück"
......@@ -274,16 +265,16 @@ msgid "Time"
msgstr "Zeit"
#: AKSubmission/templates/AKSubmission/ak_history.html:48
#: AKSubmission/templates/AKSubmission/ak_table.html:25
#: AKSubmission/templates/AKSubmission/ak_table.html:28
msgid "Present results of this AK"
msgstr "Die Ergebnisse dieses AKs vorstellen"
#: AKSubmission/templates/AKSubmission/ak_history.html:52
#: AKSubmission/templates/AKSubmission/ak_table.html:29
#: AKSubmission/templates/AKSubmission/ak_table.html:32
msgid "Intends to submit a resolution"
msgstr "Beabsichtigt eine Resolution einzureichen"
#: AKSubmission/templates/AKSubmission/ak_list.html:6 AKSubmission/views.py:42
#: AKSubmission/templates/AKSubmission/ak_list.html:6 AKSubmission/views.py:82
msgid "All AKs"
msgstr "Alle AKs"
......@@ -299,11 +290,11 @@ msgstr "AK-Liste"
msgid "Add AK"
msgstr "AK hinzufügen"
#: AKSubmission/templates/AKSubmission/ak_table.html:42
#: AKSubmission/templates/AKSubmission/ak_table.html:52
msgid "Details"
msgstr "Details"
#: AKSubmission/templates/AKSubmission/ak_table.html:66
#: AKSubmission/templates/AKSubmission/ak_table.html:76
msgid "There are no AKs in this category yet"
msgstr "Es gibt noch keine AKs in dieser Kategorie"
......@@ -314,7 +305,7 @@ msgstr "Senden"
#: AKSubmission/templates/AKSubmission/akmessage_add.html:31
#: AKSubmission/templates/AKSubmission/akowner_create_update.html:26
#: AKSubmission/templates/AKSubmission/akslot_add_update.html:29
#: AKSubmission/templates/AKSubmission/submit_new.html:52
#: AKSubmission/templates/AKSubmission/submit_new.html:59
msgid "Reset Form"
msgstr "Formular leeren"
......@@ -322,7 +313,7 @@ msgstr "Formular leeren"
#: AKSubmission/templates/AKSubmission/akowner_create_update.html:30
#: AKSubmission/templates/AKSubmission/akslot_add_update.html:33
#: AKSubmission/templates/AKSubmission/akslot_delete.html:45
#: AKSubmission/templates/AKSubmission/submit_new.html:56
#: AKSubmission/templates/AKSubmission/submit_new.html:63
msgid "Cancel"
msgstr "Abbrechen"
......@@ -390,8 +381,8 @@ msgstr "Ich leite bisher keine AKs"
#: AKSubmission/templates/AKSubmission/submission_overview.html:67
#: AKSubmission/templates/AKSubmission/submit_new.html:9
#: AKSubmission/templates/AKSubmission/submit_new.html:34
#: AKSubmission/templates/AKSubmission/submit_new.html:41
#: AKSubmission/templates/AKSubmission/submit_new.html:48
msgid "New AK"
msgstr "Neuer AK"
......@@ -405,78 +396,84 @@ msgstr ""
"Dieses Event is nicht aktiv. Es können keine AKs hinzugefügt oder bearbeitet "
"werden"
#: AKSubmission/templates/AKSubmission/submit_new.html:48
#: AKSubmission/templates/AKSubmission/submit_new.html:29
msgid ""
"only relevant for KIF-AKs - determines whether the AK appears in the slides "
"for the closing plenary session"
msgstr "nur relevant für KIF-AKs - entscheidet, ob der AK in den Folien fürs Abschlussplenum auftaucht"
#: AKSubmission/templates/AKSubmission/submit_new.html:55
msgid "Submit"
msgstr "Eintragen"
#: AKSubmission/views.py:73
#: AKSubmission/views.py:125
msgid "Wishes"
msgstr "Wünsche"
#: AKSubmission/views.py:73
#: AKSubmission/views.py:125
msgid "AKs one would like to have"
msgstr ""
"AKs die sich gewünscht wurden, aber bei denen noch nicht klar ist, wer sie "
"macht. Falls du dir das vorstellen kannst, trag dich einfach ein"
#: AKSubmission/views.py:93
#: AKSubmission/views.py:167
msgid "Currently planned AKs"
msgstr "Aktuell geplante AKs"
#: AKSubmission/views.py:186
#: AKSubmission/views.py:305
msgid "Event inactive. Cannot create or update."
msgstr "Event inaktiv. Hinzufügen/Bearbeiten nicht möglich."
#: AKSubmission/views.py:202
#: AKSubmission/views.py:330
msgid "AK successfully created"
msgstr "AK erfolgreich angelegt"
#: AKSubmission/views.py:252
#: AKSubmission/views.py:404
msgid "AK successfully updated"
msgstr "AK erfolgreich aktualisiert"
#: AKSubmission/views.py:290
#: AKSubmission/views.py:455
#, python-brace-format
msgid "Added '{owner}' as new owner of '{ak.name}'"
msgstr "'{owner}' als neue Leitung von '{ak.name}' hinzugefügt"
#: AKSubmission/views.py:333
msgid "Person Info successfully updated"
msgstr "Personen-Info erfolgreich aktualisiert"
#: AKSubmission/views.py:355
#: AKSubmission/views.py:558
msgid "No user selected"
msgstr "Keine Person ausgewählt"
#: AKSubmission/views.py:382
#: AKSubmission/views.py:574
msgid "Person Info successfully updated"
msgstr "Personen-Info erfolgreich aktualisiert"
#: AKSubmission/views.py:610
msgid "AK Slot successfully added"
msgstr "AK-Slot erfolgreich angelegt"
#: AKSubmission/views.py:395
#: AKSubmission/views.py:629
msgid "You cannot edit a slot that has already been scheduled"
msgstr "Bereits geplante AK-Slots können nicht mehr bearbeitet werden"
#: AKSubmission/views.py:405
#: AKSubmission/views.py:639
msgid "AK Slot successfully updated"
msgstr "AK-Slot erfolgreich aktualisiert"
#: AKSubmission/views.py:417
#: AKSubmission/views.py:657
msgid "You cannot delete a slot that has already been scheduled"
msgstr "Bereits geplante AK-Slots können nicht mehr gelöscht werden"
#: AKSubmission/views.py:427
#: AKSubmission/views.py:667
msgid "AK Slot successfully deleted"
msgstr "AK-Slot erfolgreich angelegt"
#: AKSubmission/views.py:434
#: AKSubmission/views.py:679
msgid "Messages"
msgstr "Nachrichten"
#: AKSubmission/views.py:444
#: AKSubmission/views.py:689
msgid "Delete all messages"
msgstr "Alle Nachrichten löschen"
#: AKSubmission/views.py:467
#: AKSubmission/views.py:716
msgid "Message to organizers successfully saved"
msgstr "Nachricht an die Organisator*innen erfolgreich gespeichert"
......
......@@ -3,14 +3,15 @@ from django.conf import settings
from django.core.mail import EmailMessage
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse_lazy
from AKModel.models import AKOrgaMessage, AKSlot
@receiver(post_save, sender=AKOrgaMessage)
def orga_message_saved_handler(sender, instance: AKOrgaMessage, created, **kwargs):
# React to newly created Orga message by sending an email
def orga_message_saved_handler(sender, instance: AKOrgaMessage, created, **kwargs): # pylint: disable=unused-argument
"""
React to newly created Orga message by sending an email
"""
if created and settings.SEND_MAILS:
host = 'https://' + settings.ALLOWED_HOSTS[0] if len(settings.ALLOWED_HOSTS) > 0 else 'http://127.0.0.1:8000'
......@@ -26,10 +27,12 @@ def orga_message_saved_handler(sender, instance: AKOrgaMessage, created, **kwarg
@receiver(post_save, sender=AKSlot)
def slot_created_handler(sender, instance: AKSlot, created, **kwargs):
# React to slots that are created after the plan was already published by sending an email
if created and settings.SEND_MAILS and apps.is_installed("AKPlan") and not instance.event.plan_hidden and instance.room is None and instance.start is None:
def slot_created_handler(sender, instance: AKSlot, created, **kwargs): # pylint: disable=unused-argument
"""
React to slots that are created after the plan was already published by sending an email
"""
if created and settings.SEND_MAILS and apps.is_installed("AKPlan") \
and not instance.event.plan_hidden and instance.room is None and instance.start is None: # pylint: disable=too-many-boolean-expressions,line-too-long
host = 'https://' + settings.ALLOWED_HOSTS[0] if len(settings.ALLOWED_HOSTS) > 0 else 'http://127.0.0.1:8000'
url = f"{host}{instance.ak.detail_url}"
......
......@@ -27,6 +27,55 @@
{% block imports %}
{% include "AKPlan/plan_akslot.html" %}
<script type="module">
const { createApp } = Vue
function getCurrentTimestamp() {
return Date.now() / 1000
}
createApp({
delimiters: ["[[", "]]"],
data() {
return {
featuredSlot: "{% if featured_slot %}true{% else %}false{% endif %}",
timer: null,
now: getCurrentTimestamp(),
akStart: "{{ featured_slot.start | date:'U' }}",
akEnd: "{{ featured_slot.end | date:'U' }}",
showBoxWithoutJS: false,
}
},
computed: {
showFeatured() {
return this.featuredSlot && this.now < this.akEnd
},
isBefore() {
return this.featuredSlot && this.now < this.akStart
},
isDuring() {
return this.featuredSlot && this.akStart < this.now && this.now < this.akEnd
},
timeUntilStart() {
return Math.ceil((this.akStart - this.now) / 60)
},
timeUntilEnd() {
return Math.floor((this.akEnd - this.now) / 60)
}
},
mounted: function () {
if(this.featuredSlot) {
this.timer = setInterval(() => {
this.now = getCurrentTimestamp()
}, 10000)
}
},
beforeUnmount() {
clearInterval(this.timer)
}
}).mount('#app')
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication
......@@ -128,30 +177,28 @@
<h2>{% if ak.wish %}{% trans "AK Wish" %}: {% endif %}{{ ak.name }}</h2>
{# Show current or upcoming slot featured in a box on top of the plage #}
{% if featured_slot_type != "NONE" %}
<div class="card border-success mt-3 mb-3">
<div class="card-body font-weight-bold">
{% if featured_slot_type == "CURRENT" %}
{% blocktrans with room=featured_slot.room %}
This AK currently takes place for another {{ featured_slot_remaining }} minute(s) in {{ room }}.
&nbsp;
{% endblocktrans %}
{% elif featured_slot_type == "UPCOMING" %}
{% blocktrans with room=featured_slot.room %}
This AK starts in {{ featured_slot_remaining }} minute(s) in {{ room }}.&nbsp;
{% endblocktrans %}
{% endif %}
<div id="app">
{# Show current or upcoming slot featured in a box on top of the plage #}
{% if featured_slot_type != "NONE" %}
<div class="card border-success mt-3 mb-3" v-show="showFeatured">
<div class="card-body font-weight-bold">
<span v-show="isDuring" style="{% if not featured_slot_type == "CURRENT" %}display:none;{% endif %}">
{% blocktrans with room=featured_slot.room %}This AK currently takes place for another <span v-html="timeUntilEnd">{{ featured_slot_remaining }}</span> minute(s) in {{ room }}.&nbsp;{% endblocktrans %}
</span>
<span v-show="isBefore" style="{% if not featured_slot_type == "UPCOMING" %}display:none;{% endif %}">
{% blocktrans with room=featured_slot.room %}This AK starts in <span v-html="timeUntilStart">{{ featured_slot_remaining }}</span> minute(s) in {{ room }}.&nbsp;{% endblocktrans %}
</span>
{% if "AKOnline"|check_app_installed and featured_slot.room.virtual and featured_slot.room.virtual.url != '' %}
<a class="btn btn-success" target="_parent" href="{{ featured_slot.room.virtual.url }}">
{% fa6_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %}
</a>
{% endif %}
{% if "AKOnline"|check_app_installed and featured_slot.room.virtual and featured_slot.room.virtual.url != '' %}
<a class="btn btn-success" target="_parent" href="{{ featured_slot.room.virtual.url }}">
{% fa6_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %}
</a>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% endif %}
</div>
<table class="table table-borderless">
<tr>
......@@ -166,6 +213,16 @@
{% category_linked_badge ak.category ak.event.slug %}
</td>
</tr>
{% if ak.types.count > 0 %}
<tr>
<td>{% trans "Types" %}</td>
<td>
{% for type in ak.types.all %}
<span class="badge bg-info">{{ type }}</span>
{% endfor %}
</td>
</tr>
{% endif %}
{% if ak.track %}
<tr>
<td>{% trans 'Track' %}</td>
......
......@@ -9,6 +9,9 @@
<th>{% trans "Name" %}</th>
<th>{% trans "Who?" %}</th>
<th>{% trans 'Category' %}</th>
{% if show_types %}
<th>{% trans 'Types' %}</th>
{% endif %}
<th></th>
</tr>
</thead>
......@@ -37,6 +40,13 @@
{% endif %}
</td>
<td>{% category_linked_badge ak.category event.slug %}</td>
{% if show_types %}
<td>
{% for aktype in ak.types.all %}
<span class="badge bg-info">{{ aktype }}</span>
{% endfor %}
</td>
{% endif %}
<td class="text-end" style="white-space: nowrap;">
<a href="{{ ak.detail_url }}" data-bs-toggle="tooltip"
title="{% trans 'Details' %}"
......
......@@ -23,6 +23,13 @@
);
});
</script>
<style>
#id_present_helptext::after {
content: " ({% trans "only relevant for KIF-AKs - determines whether the AK appears in the slides for the closing plenary session" %})";
color: #6c757d;
}
</style>
{% endblock %}
{% block breadcrumbs %}
......