<commit_before>var Emitter = require('lib/emitter');
var WindowStack = require('ui/windowstack');
var Window = require('ui/window');

var Accel = new Emitter();

module.exports = Accel;

var simply = require('ui/simply');

var state;

Accel.init = function() {
  if (state) {
    Accel.off();
  }

  state = Accel.state = {
    rate: 100,
    samples: 25,
    subscribe: false,
    subscribeMode: 'auto',
    listeners: [],
  };
};

Accel.onAddHandler = function(type, subtype) {
  if (type === 'data') {
    Accel.autoSubscribe();
  }
};

Accel.onRemoveHandler = function(type, subtype) {
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

var accelDataListenerCount = function() {
  var count = Accel.listenerCount('data');
  var wind = WindowStack.top();
  if (wind) {
    count += wind.listenerCount('accelData');
  }
  return count;
};

Accel.autoSubscribe = function() {
  if (state.subscribeMode !== 'auto') { return; }
  var subscribe = (accelDataListenerCount > 0);
  if (subscribe !== state.subscribe) {
    return Accel.config(subscribe, true);
  }
};

/**
 * The accelerometer configuration parameter for {@link simply.accelConfig}.
 * The accelerometer data stream is useful for applications such as gesture recognition when accelTap is too limited.
 * However, keep in mind that smaller batch sample sizes and faster rates will drastically impact the battery life of both the Pebble and phone because of the taxing use of the processors and Bluetooth modules.
 * @typedef {object} simply.accelConf
 * @property {number} [rate] - The rate accelerometer data points are generated in hertz. Valid values are 10, 25, 50, and 100. Initializes as 100.
 * @property {number} [samples] - The number of accelerometer data points to accumulate in a batch before calling the event handler. Valid values are 1 to 25 inclusive. Initializes as 25.
 * @property {boolean} [subscribe] - Whether to subscribe to accelerometer data events. {@link simply.accelPeek} cannot be used when subscribed. Simply.js will automatically (un)subscribe for you depending on the amount of accelData handlers registered.
 */

/**
 * Changes the accelerometer configuration.
 * See {@link simply.accelConfig}
 * @memberOf simply
 * @param {simply.accelConfig} accelConf - An object defining the accelerometer configuration.
 */
Accel.config = function(opt, auto) {
  if (typeof opt === 'undefined') {
    return {
      rate: state.rate,
      samples: state.samples,
      subscribe: state.subscribe,
    };
  } else if (typeof opt === 'boolean') {
    opt = { subscribe: opt };
  }
  for (var k in opt) {
    if (k === 'subscribe') {
      state.subscribeMode = opt[k] && !auto ? 'manual' : 'auto';
    }
    state[k] = opt[k];
  }
  return simply.impl.accelConfig.apply(this, arguments);
};

/**
 * Peeks at the current accelerometer values.
 * @memberOf simply
 * @param {simply.eventHandler} callback - A callback function that will be provided the accel data point as an event.
 */
Accel.peek = function(callback) {
  if (state.subscribe) {
    throw new Error('Cannot use accelPeek when listening to accelData events');
  }
  return simply.impl.accelPeek.apply(this, arguments);
};

/**
 * Simply.js accel tap event.
 * Use the event type 'accelTap' to subscribe to these events.
 * @typedef simply.accelTapEvent
 * @property {string} axis - The axis the tap event occurred on: 'x', 'y', or 'z'. This is also the event subtype.
 * @property {number} direction - The direction of the tap along the axis: 1 or -1.
 */

Accel.emitAccelTap = function(axis, direction) {
  var e = {
    axis: axis,
    direction: direction,
  };
  if (Window.emit('accelTap', axis, e) === false) {
    return false;
  }
  Accel.emit('tap', axis, e);
};

/**
 * Simply.js accel data point.
 * Typical values for gravity is around -1000 on the z axis.
 * @typedef simply.accelPoint
 * @property {number} x - The acceleration across the x-axis.
 * @property {number} y - The acceleration across the y-axis.
 * @property {number} z - The acceleration across the z-axis.
 * @property {boolean} vibe - Whether the watch was vibrating when measuring this point.
 * @property {number} time - The amount of ticks in millisecond resolution when measuring this point.
 */

/**
 * Simply.js accel data event.
 * Use the event type 'accelData' to subscribe to these events.
 * @typedef simply.accelDataEvent
 * @property {number} samples - The number of accelerometer samples in this event.
 * @property {simply.accelPoint} accel - The first accel in the batch. This is provided for convenience.
 * @property {simply.accelPoint[]} accels - The accelerometer samples in an array.
 */

Accel.emitAccelData = function(accels, callback) {
  var e = {
    samples: accels.length,
    accel: accels[0],
    accels: accels,
  };
  if (callback) {
    return callback(e);
  }
  if (Window.emit('accelData', null, e) === false) {
    return false;
  }
  Accel.emit('data', e);
};
<commit_msg>Fix accel auto subscribe<commit_after>var Emitter = require('lib/emitter');

var Accel = new Emitter();

module.exports = Accel;

var WindowStack = require('ui/windowstack');
var Window = require('ui/window');
var simply = require('ui/simply');

var state;

Accel.init = function() {
  if (state) {
    Accel.off();
  }

  state = Accel.state = {
    rate: 100,
    samples: 25,
    subscribe: false,
    subscribeMode: 'auto',
    listeners: [],
  };
};

Accel.onAddHandler = function(type, subtype) {
  if (type === 'data') {
    Accel.autoSubscribe();
  }
};

Accel.onRemoveHandler = function(type, subtype) {
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

var accelDataListenerCount = function() {
  var count = Accel.listenerCount('data');
  var wind = WindowStack.top();
  if (wind) {
    count += wind.listenerCount('accelData');
  }
  return count;
};

Accel.autoSubscribe = function() {
  if (state.subscribeMode !== 'auto') { return; }
  var subscribe = (accelDataListenerCount() > 0);
  if (subscribe !== state.subscribe) {
    return Accel.config(subscribe, true);
  }
};

/**
 * The accelerometer configuration parameter for {@link simply.accelConfig}.
 * The accelerometer data stream is useful for applications such as gesture recognition when accelTap is too limited.
 * However, keep in mind that smaller batch sample sizes and faster rates will drastically impact the battery life of both the Pebble and phone because of the taxing use of the processors and Bluetooth modules.
 * @typedef {object} simply.accelConf
 * @property {number} [rate] - The rate accelerometer data points are generated in hertz. Valid values are 10, 25, 50, and 100. Initializes as 100.
 * @property {number} [samples] - The number of accelerometer data points to accumulate in a batch before calling the event handler. Valid values are 1 to 25 inclusive. Initializes as 25.
 * @property {boolean} [subscribe] - Whether to subscribe to accelerometer data events. {@link simply.accelPeek} cannot be used when subscribed. Simply.js will automatically (un)subscribe for you depending on the amount of accelData handlers registered.
 */

/**
 * Changes the accelerometer configuration.
 * See {@link simply.accelConfig}
 * @memberOf simply
 * @param {simply.accelConfig} accelConf - An object defining the accelerometer configuration.
 */
Accel.config = function(opt, auto) {
  if (typeof opt === 'undefined') {
    return {
      rate: state.rate,
      samples: state.samples,
      subscribe: state.subscribe,
    };
  } else if (typeof opt === 'boolean') {
    opt = { subscribe: opt };
  }
  for (var k in opt) {
    if (k === 'subscribe') {
      state.subscribeMode = opt[k] && !auto ? 'manual' : 'auto';
    }
    state[k] = opt[k];
  }
  return simply.impl.accelConfig.apply(this, arguments);
};

/**
 * Peeks at the current accelerometer values.
 * @memberOf simply
 * @param {simply.eventHandler} callback - A callback function that will be provided the accel data point as an event.
 */
Accel.peek = function(callback) {
  if (state.subscribe) {
    throw new Error('Cannot use accelPeek when listening to accelData events');
  }
  return simply.impl.accelPeek.apply(this, arguments);
};

/**
 * Simply.js accel tap event.
 * Use the event type 'accelTap' to subscribe to these events.
 * @typedef simply.accelTapEvent
 * @property {string} axis - The axis the tap event occurred on: 'x', 'y', or 'z'. This is also the event subtype.
 * @property {number} direction - The direction of the tap along the axis: 1 or -1.
 */

Accel.emitAccelTap = function(axis, direction) {
  var e = {
    axis: axis,
    direction: direction,
  };
  if (Window.emit('accelTap', axis, e) === false) {
    return false;
  }
  Accel.emit('tap', axis, e);
};

/**
 * Simply.js accel data point.
 * Typical values for gravity is around -1000 on the z axis.
 * @typedef simply.accelPoint
 * @property {number} x - The acceleration across the x-axis.
 * @property {number} y - The acceleration across the y-axis.
 * @property {number} z - The acceleration across the z-axis.
 * @property {boolean} vibe - Whether the watch was vibrating when measuring this point.
 * @property {number} time - The amount of ticks in millisecond resolution when measuring this point.
 */

/**
 * Simply.js accel data event.
 * Use the event type 'accelData' to subscribe to these events.
 * @typedef simply.accelDataEvent
 * @property {number} samples - The number of accelerometer samples in this event.
 * @property {simply.accelPoint} accel - The first accel in the batch. This is provided for convenience.
 * @property {simply.accelPoint[]} accels - The accelerometer samples in an array.
 */

Accel.emitAccelData = function(accels, callback) {
  var e = {
    samples: accels.length,
    accel: accels[0],
    accels: accels,
  };
  if (callback) {
    return callback(e);
  }
  if (Window.emit('accelData', null, e) === false) {
    return false;
  }
  Accel.emit('data', e);
};
<|endoftext|>


<commit_before>import { attr } from './attr';
import { css } from './style';
import { doc, docEl, win } from './env';
import { height, width } from './position';
import { on, once, trigger } from './event';
import { matches, toNode, toNodes } from './selector';
import { addClass, hasClass, removeClass, removeClasses } from './class';
import { assign, clamp, each, intersectRect, isNumeric, isString, isUndefined, Promise, startsWith, toNumber } from './lang';

export const isRtl = attr(docEl, 'dir') === 'rtl';

export function isReady() {
    return doc.readyState === 'complete' || doc.readyState !== 'loading' && !docEl.doScroll;
}

export function ready(fn) {

    if (isReady()) {
        fn();
        return;
    }

    var handle = function () {
            unbind1();
            unbind2();
            fn();
        },
        unbind1 = on(doc, 'DOMContentLoaded', handle),
        unbind2 = on(win, 'load', handle);
}

export function transition(element, props, duration = 400, transition = 'linear') {

    return Promise.all(toNodes(element).map(element =>
        new Promise((resolve, reject) => {

            for (var name in props) {
                var value = css(element, name);
                if (value === '') {
                    css(element, name, value);
                }
            }

            var timer = setTimeout(() => trigger(element, 'transitionend'), duration);

            once(element, 'transitionend transitioncanceled', ({type}) => {
                clearTimeout(timer);
                removeClass(element, 'uk-transition');
                css(element, 'transition', '');
                type === 'transitioncanceled' ? reject() : resolve();
            }, false, ({target}) => element === target);

            addClass(element, 'uk-transition');
            css(element, assign({transition: `all ${duration}ms ${transition}`}, props));

        })
    ));

}

export const Transition = {

    start: transition,

    stop(element) {
        trigger(element, 'transitionend');
        return Promise.resolve();
    },

    cancel(element) {
        trigger(element, 'transitioncanceled');
    },

    inProgress(element) {
        return hasClass(element, 'uk-transition');
    }

};

var animationPrefix = 'uk-animation-',
    clsCancelAnimation = 'uk-cancel-animation';

export function animate(element, animation, duration = 200, origin, out) {

    return Promise.all(toNodes(element).map(element =>
        new Promise((resolve, reject) => {

            if (hasClass(element, clsCancelAnimation)) {
                requestAnimationFrame(() =>
                    Promise.resolve().then(() =>
                        animate.apply(null, arguments).then(resolve, reject)
                    )
                );
                return;
            }

            var cls = `${animation} ${animationPrefix}${out ? 'leave' : 'enter'}`;

            if (startsWith(animation, animationPrefix)) {

                if (origin) {
                    cls += ` ${animationPrefix}${origin}`;
                }

                if (out) {
                    cls += ` ${animationPrefix}reverse`;
                }

            }

            reset();

            once(element, 'animationend animationcancel', ({type}) => {

                var hasReset = false;

                if (type === 'animationcancel') {
                    reject();
                    reset();
                } else {
                    resolve();
                    Promise.resolve().then(() => {
                        hasReset = true;
                        reset();
                    });
                }

                requestAnimationFrame(() => {
                    if (!hasReset) {
                        addClass(element, clsCancelAnimation);

                        requestAnimationFrame(() => removeClass(element, clsCancelAnimation));
                    }
                });

            }, false, ({target}) => element === target);

            css(element, 'animationDuration', `${duration}ms`);
            addClass(element, cls);

            function reset() {
                css(element, 'animationDuration', '');
                removeClasses(element, `${animationPrefix}\\S*`);
            }

        })
    ));

}

var inProgress = new RegExp(`${animationPrefix}(enter|leave)`);
export const Animation = {

    in(element, animation, duration, origin) {
        return animate(element, animation, duration, origin, false);
    },

    out(element, animation, duration, origin) {
        return animate(element, animation, duration, origin, true);
    },

    inProgress(element) {
        return inProgress.test(attr(element, 'class'));
    },

    cancel(element) {
        trigger(element, 'animationcancel');
    }

};

export function isInView(element, top = 0, left = 0) {
    return intersectRect(toNode(element).getBoundingClientRect(), {
        top,
        left,
        bottom: top + height(win),
        right: left + width(win)
    });
}

export function scrolledOver(element) {

    element = toNode(element);

    var elHeight = element.offsetHeight,
        top = positionTop(element),
        vp = height(win),
        vh = vp + Math.min(0, top - vp),
        diff = Math.max(0, vp - (height(doc) - (top + elHeight)));

    return clamp(((vh + win.pageYOffset - top) / ((vh + (elHeight - (diff < vp ? diff : 0))) / 100)) / 100);
}

function positionTop(element) {
    var top = 0;

    do {

        top += element.offsetTop;

    } while (element = element.offsetParent);

    return top;
}

export function getIndex(i, elements, current = 0) {

    elements = toNodes(elements);

    var length = elements.length;

    i = (isNumeric(i)
        ? toNumber(i)
        : i === 'next'
            ? current + 1
            : i === 'previous'
                ? current - 1
                : index(elements, i)
    ) % length;

    return i < 0 ? i + length : i;
}

var voidElements = {
    area: true,
    base: true,
    br: true,
    col: true,
    embed: true,
    hr: true,
    img: true,
    input: true,
    keygen: true,
    link: true,
    menuitem: true,
    meta: true,
    param: true,
    source: true,
    track: true,
    wbr: true
};
export function isVoidElement(element) {
    return voidElements[toNode(element).tagName.toLowerCase()];
}

export const Dimensions = {

    ratio(dimensions, prop, value) {

        var aProp = prop === 'width' ? 'height' : 'width';

        return {
            [aProp]: Math.round(value * dimensions[aProp] / dimensions[prop]),
            [prop]: value
        };
    },

    contain(dimensions, maxDimensions) {
        dimensions = assign({}, dimensions);

        each(dimensions, (_, prop) => dimensions = dimensions[prop] > maxDimensions[prop]
            ? this.ratio(dimensions, prop, maxDimensions[prop])
            : dimensions
        );

        return dimensions;
    },

    cover(dimensions, maxDimensions) {
        dimensions = this.contain(dimensions, maxDimensions);

        each(dimensions, (_, prop) => dimensions = dimensions[prop] < maxDimensions[prop]
            ? this.ratio(dimensions, prop, maxDimensions[prop])
            : dimensions
        );

        return dimensions;
    }

};

export function preventClick() {

    var timer = setTimeout(() => trigger(doc, 'click'), 0);

    once(doc, 'click', e => {
        e.preventDefault();
        e.stopImmediatePropagation();

        clearTimeout(timer);
    }, true);

}

export function isVisible(element) {
    return toNodes(element).some(element => element.offsetHeight);
}

export const selInput = 'input,select,textarea,button';
export function isInput(element) {
    return toNodes(element).some(element => matches(element, selInput));
}

export function empty(element) {
    element = toNode(element);
    element.innerHTML = '';
    return element;
}

export function html(parent, html) {
    parent = toNode(parent);
    return isUndefined(html)
        ? parent.innerHTML
        : append(parent.hasChildNodes() ? empty(parent) : parent, html);
}

export function prepend(parent, element) {

    parent = toNode(parent);

    if (!parent.hasChildNodes()) {
        return append(parent, element);
    } else {
        return insertNodes(element, element => parent.insertBefore(element, parent.firstChild));
    }
}

export function append(parent, element) {
    parent = toNode(parent);
    return insertNodes(element, element => parent.appendChild(element));
}

export function before(ref, element) {
    ref = toNode(ref);
    return insertNodes(element, element => ref.parentNode.insertBefore(element, ref));
}

export function after(ref, element) {
    ref = toNode(ref);
    return insertNodes(element, element => ref.nextSibling
        ? before(ref.nextSibling, element)
        : append(ref.parentNode, element)
    );
}

function insertNodes(element, fn) {
    element = isString(element) ? fragment(element) : element;
    return element ? 'length' in element ? toNodes(element).map(fn) : fn(element) : null;
}

export function remove(element) {
    toNodes(element).map(element => element.parentNode && element.parentNode.removeChild(element));
}

export function wrapAll(element, structure) {

    structure = toNode(before(element, structure));

    while (structure.firstChild) {
        structure = structure.firstChild;
    }

    append(structure, element);

    return structure;
}

export function wrapInner(element, structure) {
    return toNodes(toNodes(element).map(element =>
        element.hasChildNodes ? wrapAll(toNodes(element.childNodes), structure) : append(element, structure)
    ));
}

export function unwrap(element) {
    toNodes(element)
        .map(element => element.parentNode)
        .filter((value, index, self) => self.indexOf(value) === index)
        .forEach(parent => {
            before(parent, parent.childNodes);
            remove(parent);
        });
}

var fragmentRE = /^\s*<(\w+|!)[^>]*>/,
    singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;

export function fragment(html) {

    var matches;

    if (matches = singleTagRE.exec(html)) {
        return doc.createElement(matches[1]);
    }

    var container = doc.createElement('div');
    if (fragmentRE.test(html)) {
        container.insertAdjacentHTML('beforeend', html.trim());
    } else {
        container.textContent = html;
    }

    return container.childNodes.length > 1 ? toNodes(container.childNodes) : container.firstChild;

}

export function index(element, ref) {
    return ref
        ? toNodes(element).indexOf(toNode(ref))
        : toNodes((element = toNode(element)) && element.parentNode.children).indexOf(element);
}
<commit_msg>Fix origin modifier for programmatically triggered animations<commit_after>import { attr } from './attr';
import { css } from './style';
import { doc, docEl, win } from './env';
import { height, width } from './position';
import { on, once, trigger } from './event';
import { matches, toNode, toNodes } from './selector';
import { addClass, hasClass, removeClass, removeClasses } from './class';
import { assign, clamp, each, intersectRect, isNumeric, isString, isUndefined, Promise, startsWith, toNumber } from './lang';

export const isRtl = attr(docEl, 'dir') === 'rtl';

export function isReady() {
    return doc.readyState === 'complete' || doc.readyState !== 'loading' && !docEl.doScroll;
}

export function ready(fn) {

    if (isReady()) {
        fn();
        return;
    }

    var handle = function () {
            unbind1();
            unbind2();
            fn();
        },
        unbind1 = on(doc, 'DOMContentLoaded', handle),
        unbind2 = on(win, 'load', handle);
}

export function transition(element, props, duration = 400, transition = 'linear') {

    return Promise.all(toNodes(element).map(element =>
        new Promise((resolve, reject) => {

            for (var name in props) {
                var value = css(element, name);
                if (value === '') {
                    css(element, name, value);
                }
            }

            var timer = setTimeout(() => trigger(element, 'transitionend'), duration);

            once(element, 'transitionend transitioncanceled', ({type}) => {
                clearTimeout(timer);
                removeClass(element, 'uk-transition');
                css(element, 'transition', '');
                type === 'transitioncanceled' ? reject() : resolve();
            }, false, ({target}) => element === target);

            addClass(element, 'uk-transition');
            css(element, assign({transition: `all ${duration}ms ${transition}`}, props));

        })
    ));

}

export const Transition = {

    start: transition,

    stop(element) {
        trigger(element, 'transitionend');
        return Promise.resolve();
    },

    cancel(element) {
        trigger(element, 'transitioncanceled');
    },

    inProgress(element) {
        return hasClass(element, 'uk-transition');
    }

};

var animationPrefix = 'uk-animation-',
    clsCancelAnimation = 'uk-cancel-animation';

export function animate(element, animation, duration = 200, origin, out) {

    return Promise.all(toNodes(element).map(element =>
        new Promise((resolve, reject) => {

            if (hasClass(element, clsCancelAnimation)) {
                requestAnimationFrame(() =>
                    Promise.resolve().then(() =>
                        animate.apply(null, arguments).then(resolve, reject)
                    )
                );
                return;
            }

            var cls = `${animation} ${animationPrefix}${out ? 'leave' : 'enter'}`;

            if (startsWith(animation, animationPrefix)) {

                if (origin) {
                    cls += ` uk-transform-origin-${origin}`;
                }

                if (out) {
                    cls += ` ${animationPrefix}reverse`;
                }

            }

            reset();

            once(element, 'animationend animationcancel', ({type}) => {

                var hasReset = false;

                if (type === 'animationcancel') {
                    reject();
                    reset();
                } else {
                    resolve();
                    Promise.resolve().then(() => {
                        hasReset = true;
                        reset();
                    });
                }

                requestAnimationFrame(() => {
                    if (!hasReset) {
                        addClass(element, clsCancelAnimation);

                        requestAnimationFrame(() => removeClass(element, clsCancelAnimation));
                    }
                });

            }, false, ({target}) => element === target);

            css(element, 'animationDuration', `${duration}ms`);
            addClass(element, cls);

            function reset() {
                css(element, 'animationDuration', '');
                removeClasses(element, `${animationPrefix}\\S*`);
            }

        })
    ));

}

var inProgress = new RegExp(`${animationPrefix}(enter|leave)`);
export const Animation = {

    in(element, animation, duration, origin) {
        return animate(element, animation, duration, origin, false);
    },

    out(element, animation, duration, origin) {
        return animate(element, animation, duration, origin, true);
    },

    inProgress(element) {
        return inProgress.test(attr(element, 'class'));
    },

    cancel(element) {
        trigger(element, 'animationcancel');
    }

};

export function isInView(element, top = 0, left = 0) {
    return intersectRect(toNode(element).getBoundingClientRect(), {
        top,
        left,
        bottom: top + height(win),
        right: left + width(win)
    });
}

export function scrolledOver(element) {

    element = toNode(element);

    var elHeight = element.offsetHeight,
        top = positionTop(element),
        vp = height(win),
        vh = vp + Math.min(0, top - vp),
        diff = Math.max(0, vp - (height(doc) - (top + elHeight)));

    return clamp(((vh + win.pageYOffset - top) / ((vh + (elHeight - (diff < vp ? diff : 0))) / 100)) / 100);
}

function positionTop(element) {
    var top = 0;

    do {

        top += element.offsetTop;

    } while (element = element.offsetParent);

    return top;
}

export function getIndex(i, elements, current = 0) {

    elements = toNodes(elements);

    var length = elements.length;

    i = (isNumeric(i)
        ? toNumber(i)
        : i === 'next'
            ? current + 1
            : i === 'previous'
                ? current - 1
                : index(elements, i)
    ) % length;

    return i < 0 ? i + length : i;
}

var voidElements = {
    area: true,
    base: true,
    br: true,
    col: true,
    embed: true,
    hr: true,
    img: true,
    input: true,
    keygen: true,
    link: true,
    menuitem: true,
    meta: true,
    param: true,
    source: true,
    track: true,
    wbr: true
};
export function isVoidElement(element) {
    return voidElements[toNode(element).tagName.toLowerCase()];
}

export const Dimensions = {

    ratio(dimensions, prop, value) {

        var aProp = prop === 'width' ? 'height' : 'width';

        return {
            [aProp]: Math.round(value * dimensions[aProp] / dimensions[prop]),
            [prop]: value
        };
    },

    contain(dimensions, maxDimensions) {
        dimensions = assign({}, dimensions);

        each(dimensions, (_, prop) => dimensions = dimensions[prop] > maxDimensions[prop]
            ? this.ratio(dimensions, prop, maxDimensions[prop])
            : dimensions
        );

        return dimensions;
    },

    cover(dimensions, maxDimensions) {
        dimensions = this.contain(dimensions, maxDimensions);

        each(dimensions, (_, prop) => dimensions = dimensions[prop] < maxDimensions[prop]
            ? this.ratio(dimensions, prop, maxDimensions[prop])
            : dimensions
        );

        return dimensions;
    }

};

export function preventClick() {

    var timer = setTimeout(() => trigger(doc, 'click'), 0);

    once(doc, 'click', e => {
        e.preventDefault();
        e.stopImmediatePropagation();

        clearTimeout(timer);
    }, true);

}

export function isVisible(element) {
    return toNodes(element).some(element => element.offsetHeight);
}

export const selInput = 'input,select,textarea,button';
export function isInput(element) {
    return toNodes(element).some(element => matches(element, selInput));
}

export function empty(element) {
    element = toNode(element);
    element.innerHTML = '';
    return element;
}

export function html(parent, html) {
    parent = toNode(parent);
    return isUndefined(html)
        ? parent.innerHTML
        : append(parent.hasChildNodes() ? empty(parent) : parent, html);
}

export function prepend(parent, element) {

    parent = toNode(parent);

    if (!parent.hasChildNodes()) {
        return append(parent, element);
    } else {
        return insertNodes(element, element => parent.insertBefore(element, parent.firstChild));
    }
}

export function append(parent, element) {
    parent = toNode(parent);
    return insertNodes(element, element => parent.appendChild(element));
}

export function before(ref, element) {
    ref = toNode(ref);
    return insertNodes(element, element => ref.parentNode.insertBefore(element, ref));
}

export function after(ref, element) {
    ref = toNode(ref);
    return insertNodes(element, element => ref.nextSibling
        ? before(ref.nextSibling, element)
        : append(ref.parentNode, element)
    );
}

function insertNodes(element, fn) {
    element = isString(element) ? fragment(element) : element;
    return element ? 'length' in element ? toNodes(element).map(fn) : fn(element) : null;
}

export function remove(element) {
    toNodes(element).map(element => element.parentNode && element.parentNode.removeChild(element));
}

export function wrapAll(element, structure) {

    structure = toNode(before(element, structure));

    while (structure.firstChild) {
        structure = structure.firstChild;
    }

    append(structure, element);

    return structure;
}

export function wrapInner(element, structure) {
    return toNodes(toNodes(element).map(element =>
        element.hasChildNodes ? wrapAll(toNodes(element.childNodes), structure) : append(element, structure)
    ));
}

export function unwrap(element) {
    toNodes(element)
        .map(element => element.parentNode)
        .filter((value, index, self) => self.indexOf(value) === index)
        .forEach(parent => {
            before(parent, parent.childNodes);
            remove(parent);
        });
}

var fragmentRE = /^\s*<(\w+|!)[^>]*>/,
    singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;

export function fragment(html) {

    var matches;

    if (matches = singleTagRE.exec(html)) {
        return doc.createElement(matches[1]);
    }

    var container = doc.createElement('div');
    if (fragmentRE.test(html)) {
        container.insertAdjacentHTML('beforeend', html.trim());
    } else {
        container.textContent = html;
    }

    return container.childNodes.length > 1 ? toNodes(container.childNodes) : container.firstChild;

}

export function index(element, ref) {
    return ref
        ? toNodes(element).indexOf(toNode(ref))
        : toNodes((element = toNode(element)) && element.parentNode.children).indexOf(element);
}
<|endoftext|>


<commit_before>FLOW.MonitoringDataTableView = FLOW.View.extend({
  showingDetailsDialog: false,
  cursorStart: null,

  pageNumber: function(){
	return FLOW.router.surveyedLocaleController.get('pageNumber');
  }.property('FLOW.router.surveyedLocaleController.pageNumber'),

  showDetailsDialog: function (evt) {
	FLOW.surveyInstanceControl.set('content', FLOW.store.findQuery(FLOW.SurveyInstance, {
		surveyedLocaleId: evt.context.get('keyId')
	}));
    this.toggleProperty('showingDetailsDialog');
  },

  showApprovalStatusColumn: function () {
      return FLOW.Env.enableDataApproval;
  }.property(),

  closeDetailsDialog: function () {
    this.toggleProperty('showingDetailsDialog');
  },

  showSurveyInstanceDetails: function (evt) {
    FLOW.questionAnswerControl.doQuestionAnswerQuery(evt.context.get('keyId'));
    $('.si_details').hide();
    $('tr[data-flow-id="si_details_' + evt.context.get('keyId') + '"]').show();
  },

  findSurveyedLocale: function (evt) {
	  var ident = this.get('identifier'),
	      displayName = this.get('displayName'),
	      sgId = FLOW.selectedControl.get('selectedSurveyGroup'),
	      cursorType = FLOW.metaControl.get('cursorType'),
        criteria = {};

	  if (ident) {
		  criteria.identifier = ident;
	  }

	  if (displayName) {
		  criteria.displayName = displayName;
	  }

	  if (sgId) {
		  criteria.surveyGroupId = sgId.get('keyId');
	  }

	  if (this.get('cursorStart')) {
		criteria.since = this.get('cursorStart');
	  }
      FLOW.router.surveyedLocaleController.populate(criteria);
  },

  doNextPage: function () {
	var cursorArray, cursorStart;
	cursorArray = FLOW.router.surveyedLocaleController.get('sinceArray');
	cursorStart = cursorArray.length > 0 ? cursorArray[cursorArray.length - 1] : null;
	this.set('cursorStart', cursorStart);
    this.findSurveyedLocale();
    FLOW.router.surveyedLocaleController.set('pageNumber', FLOW.router.surveyedLocaleController.get('pageNumber') + 1);
  },

  doPrevPage: function () {
	var cursorArray, cursorStart;
	cursorArray = FLOW.router.surveyedLocaleController.get('sinceArray');
	cursorStart = cursorArray.length - 3 > -1 ? cursorArray[cursorArray.length - 3] : null;
	this.set('cursorStart', cursorStart);
    this.findSurveyedLocale();
    FLOW.router.surveyedLocaleController.set('pageNumber', FLOW.router.surveyedLocaleController.get('pageNumber') - 1);
  },

  hasNextPage: function () {
    return FLOW.metaControl.get('numSLLoaded') == 20;
  }.property('FLOW.metaControl.numSLLoaded'),

  hasPrevPage: function () {
    return FLOW.router.surveyedLocaleController.get('pageNumber');
  }.property('FLOW.router.surveyedLocaleController.pageNumber'),
});

/**
 * View of each row/data point in the monitoring data tab
 */
FLOW.DataPointView = FLOW.View.extend({
    templateName: 'navData/monitoring-data-row',

    approvalStatus: [{label: Ember.String.loc('_pending'), value: 'pending'}, { label: Ember.String.loc('_approved'), value: 'approved' },{ label: Ember.String.loc('_rejected'), value: 'rejected'}],

    showDataApprovalBlock: false,

    showSurveyedLocaleDeleteButton: function() {
        return FLOW.router.surveyedLocaleController.get('userCanDelete');
    }.property(),

    showApprovalStatusColumn: function () {
        return this.get('parentView').get('showApprovalStatusColumn');
    }.property(),

    loadDataPointApprovalObserver: function () {
        if(!this.get('showDataApprovalBlock')) {
            return; // do nothing when hiding approval block
        }

        var dataPoint = this.get('content');
        if (dataPoint) {
            FLOW.router.dataPointApprovalController.loadBySurveyedLocaleId(dataPoint.get('keyId'));
        }
    }.observes('this.showDataApprovalBlock'),

    toggleShowDataApprovalBlock: function () {
        this.toggleProperty('showDataApprovalBlock');
    },
});

/**
 * View to render the status of a data point in the approval
 * status cell of each data point / row
 */
FLOW.DataPointApprovalStatusView = FLOW.View.extend({
    content: null,

    latestApprovalStepTitle: function () {
        var stepsController = FLOW.router.get('approvalStepsController');
        if(stepsController.get('firstObject')) {
            return stepsController.get('firstObject').get('title');
        }
    }.property(),
});

/**
 * The data approval view for each approval step of a data point
 */
FLOW.DataPointApprovalView = FLOW.View.extend({
    step: null,

    dataPoint: null,

    status: null,

    comment: null,

    dataPointApproval: function () {
        var approvals = FLOW.router.dataPointApprovalController.get('content');
        if(!approvals) {
            return;
        }

        var stepId = this.step && this.step.get('keyId');
        var surveyedLocaleId = this.dataPoint && this.dataPoint.get('keyId');
        var filteredApprovalList = approvals.filter(function (item){
            return (item.get('surveyedLocaleId') === surveyedLocaleId &&
                    item.get('approvalStepId') === stepId);
        })
        return filteredApprovalList.get('firstObject');
    }.property('FLOW.router.dataPointApprovalController.content'),

    isApprovedStep: function () {
        var dataPointApproval = this.get('dataPointApproval');
        return dataPointApproval;
    }.property('this.dataPointApproval'),

    /*
     *  Submit data approval properties to controller
     */
    submitDataPointApproval: function (event) {

        var dataPointApproval = Ember.Object.create({
            surveyedLocaleId: this.get('dataPoint').get('keyId'),
            approvalStepId: this.get('step').get('keyId'),
            approverUserName: null, // explicitly left empty to be set on server-side.
            status: this.get('status').toUpperCase(),
            comment: this.get('comment'),
        });

        FLOW.router.dataPointApprovalController.add(dataPointApproval);
    },
});<commit_msg>[#1664] Update the `dataApproval` property whenever an item is added to the data point approval controller<commit_after>FLOW.MonitoringDataTableView = FLOW.View.extend({
  showingDetailsDialog: false,
  cursorStart: null,

  pageNumber: function(){
	return FLOW.router.surveyedLocaleController.get('pageNumber');
  }.property('FLOW.router.surveyedLocaleController.pageNumber'),

  showDetailsDialog: function (evt) {
	FLOW.surveyInstanceControl.set('content', FLOW.store.findQuery(FLOW.SurveyInstance, {
		surveyedLocaleId: evt.context.get('keyId')
	}));
    this.toggleProperty('showingDetailsDialog');
  },

  showApprovalStatusColumn: function () {
      return FLOW.Env.enableDataApproval;
  }.property(),

  closeDetailsDialog: function () {
    this.toggleProperty('showingDetailsDialog');
  },

  showSurveyInstanceDetails: function (evt) {
    FLOW.questionAnswerControl.doQuestionAnswerQuery(evt.context.get('keyId'));
    $('.si_details').hide();
    $('tr[data-flow-id="si_details_' + evt.context.get('keyId') + '"]').show();
  },

  findSurveyedLocale: function (evt) {
	  var ident = this.get('identifier'),
	      displayName = this.get('displayName'),
	      sgId = FLOW.selectedControl.get('selectedSurveyGroup'),
	      cursorType = FLOW.metaControl.get('cursorType'),
        criteria = {};

	  if (ident) {
		  criteria.identifier = ident;
	  }

	  if (displayName) {
		  criteria.displayName = displayName;
	  }

	  if (sgId) {
		  criteria.surveyGroupId = sgId.get('keyId');
	  }

	  if (this.get('cursorStart')) {
		criteria.since = this.get('cursorStart');
	  }
      FLOW.router.surveyedLocaleController.populate(criteria);
  },

  doNextPage: function () {
	var cursorArray, cursorStart;
	cursorArray = FLOW.router.surveyedLocaleController.get('sinceArray');
	cursorStart = cursorArray.length > 0 ? cursorArray[cursorArray.length - 1] : null;
	this.set('cursorStart', cursorStart);
    this.findSurveyedLocale();
    FLOW.router.surveyedLocaleController.set('pageNumber', FLOW.router.surveyedLocaleController.get('pageNumber') + 1);
  },

  doPrevPage: function () {
	var cursorArray, cursorStart;
	cursorArray = FLOW.router.surveyedLocaleController.get('sinceArray');
	cursorStart = cursorArray.length - 3 > -1 ? cursorArray[cursorArray.length - 3] : null;
	this.set('cursorStart', cursorStart);
    this.findSurveyedLocale();
    FLOW.router.surveyedLocaleController.set('pageNumber', FLOW.router.surveyedLocaleController.get('pageNumber') - 1);
  },

  hasNextPage: function () {
    return FLOW.metaControl.get('numSLLoaded') == 20;
  }.property('FLOW.metaControl.numSLLoaded'),

  hasPrevPage: function () {
    return FLOW.router.surveyedLocaleController.get('pageNumber');
  }.property('FLOW.router.surveyedLocaleController.pageNumber'),
});

/**
 * View of each row/data point in the monitoring data tab
 */
FLOW.DataPointView = FLOW.View.extend({
    templateName: 'navData/monitoring-data-row',

    approvalStatus: [{label: Ember.String.loc('_pending'), value: 'pending'}, { label: Ember.String.loc('_approved'), value: 'approved' },{ label: Ember.String.loc('_rejected'), value: 'rejected'}],

    showDataApprovalBlock: false,

    showSurveyedLocaleDeleteButton: function() {
        return FLOW.router.surveyedLocaleController.get('userCanDelete');
    }.property(),

    showApprovalStatusColumn: function () {
        return this.get('parentView').get('showApprovalStatusColumn');
    }.property(),

    loadDataPointApprovalObserver: function () {
        if(!this.get('showDataApprovalBlock')) {
            return; // do nothing when hiding approval block
        }

        var dataPoint = this.get('content');
        if (dataPoint) {
            FLOW.router.dataPointApprovalController.loadBySurveyedLocaleId(dataPoint.get('keyId'));
        }
    }.observes('this.showDataApprovalBlock'),

    toggleShowDataApprovalBlock: function () {
        this.toggleProperty('showDataApprovalBlock');
    },
});

/**
 * View to render the status of a data point in the approval
 * status cell of each data point / row
 */
FLOW.DataPointApprovalStatusView = FLOW.View.extend({
    content: null,

    latestApprovalStepTitle: function () {
        var stepsController = FLOW.router.get('approvalStepsController');
        if(stepsController.get('firstObject')) {
            return stepsController.get('firstObject').get('title');
        }
    }.property(),
});

/**
 * The data approval view for each approval step of a data point
 */
FLOW.DataPointApprovalView = FLOW.View.extend({
    step: null,

    dataPoint: null,

    status: null,

    comment: null,

    dataPointApproval: function () {
        var approvals = FLOW.router.dataPointApprovalController.get('content');
        if(!approvals) {
            return;
        }

        var stepId = this.step && this.step.get('keyId');
        var surveyedLocaleId = this.dataPoint && this.dataPoint.get('keyId');
        var filteredApprovalList = approvals.filter(function (item){
            return (item.get('surveyedLocaleId') === surveyedLocaleId &&
                    item.get('approvalStepId') === stepId);
        })
        return filteredApprovalList.get('firstObject');
    }.property('FLOW.router.dataPointApprovalController.content.@each'),

    isApprovedStep: function () {
        var dataPointApproval = this.get('dataPointApproval');
        return dataPointApproval;
    }.property('this.dataPointApproval'),

    /*
     *  Submit data approval properties to controller
     */
    submitDataPointApproval: function (event) {

        var dataPointApproval = Ember.Object.create({
            surveyedLocaleId: this.get('dataPoint').get('keyId'),
            approvalStepId: this.get('step').get('keyId'),
            approverUserName: null, // explicitly left empty to be set on server-side.
            status: this.get('status').toUpperCase(),
            comment: this.get('comment'),
        });

        FLOW.router.dataPointApprovalController.add(dataPointApproval);
    },
});<|endoftext|>


<commit_before>/**
 * @file Popover component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TriggerPop from '../_TriggerPop';
import Theme from '../Theme';

import Position from '../_statics/Position';

import Event from '../_vendors/Event';
import Util from '../_vendors/Util';

class Popover extends Component {

    static Position = Position;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.closeTimeout = null;

    }

    /**
     * public
     */
    resetPosition = () => {
        this.refs.popover.resetPosition();
    };

    clearCloseTimeout = () => {
        if (this.closeTimeout) {
            clearTimeout(this.closeTimeout);
            this.closeTimeout = null;
        }
    };

    mouseOverHandler = () => {
        this.clearCloseTimeout();
    };

    mouseOutHandler = e => {
        const {onRequestClose} = this.props;
        this.clearCloseTimeout();
        this.closeTimeout = setTimeout(() => {
            onRequestClose && onRequestClose(e);
        }, 1000 / 6);
    };

    renderHandler = el => {
        const {triggerEl} = this.props;
        Event.addEvent(triggerEl, 'mouseover', this.mouseOverHandler);
        Event.addEvent(triggerEl, 'mouseout', this.mouseOutHandler);
        Event.addEvent(el, 'mouseover', this.mouseOverHandler);
        Event.addEvent(el, 'mouseout', this.mouseOutHandler);
    };

    destroyHandler = el => {
        const {triggerEl} = this.props;
        Event.removeEvent(triggerEl, 'mouseover', this.mouseOverHandler);
        Event.removeEvent(triggerEl, 'mouseout', this.mouseOutHandler);
        Event.removeEvent(el, 'mouseover', this.mouseOverHandler);
        Event.removeEvent(el, 'mousemove', this.mouseOutHandler);
    };

    componentWillUnmount() {
        this.clearCloseTimeout();
    }

    render() {

        const {

                className, contentClassName,

                // not passing down these props
                onRequestClose,

                ...restProps

            } = this.props,

            popoverClassName = classNames('popover', {
                [className]: className
            }),

            popoverContentClassName = classNames('popover-content', {
                [contentClassName]: contentClassName
            });

        return (
            <TriggerPop {...restProps}
                        ref="popover"
                        className={popoverClassName}
                        contentClassName={popoverContentClassName}
                        onRender={this.renderHandler}
                        onDestroy={this.destroyHandler}/>
        );
    }

}

Popover.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the content element.
     */
    contentClassName: PropTypes.string,

    modalClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    triangle: PropTypes.element,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be Popover.Position.LEFT or Popover.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * If true, popover will have animation effects.
     */
    isAnimated: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    isBlurClose: PropTypes.bool,
    shouldPreventContainerScroll: PropTypes.bool,
    isTriggerPositionFixed: PropTypes.bool,
    showModal: PropTypes.bool,

    /**
     * The function of popover render.
     */
    onRender: PropTypes.func,

    /**
     * The function of popover rendered.
     */
    onRendered: PropTypes.func,

    /**
     * The function of popover destroy.
     */
    onDestroy: PropTypes.func,

    /**
     * The function of popover destroyed.
     */
    onDestroyed: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func

};

Popover.defaultProps = {

    visible: false,
    hasTriangle: true,
    theme: Theme.DEFAULT,
    position: Position.BOTTOM,
    isAnimated: true,

    isBlurClose: true,
    shouldPreventContainerScroll: true,
    isTriggerPositionFixed: false,
    showModal: false

};

export default Popover;<commit_msg>update Popover<commit_after>/**
 * @file Popover component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TriggerPop from '../_TriggerPop';
import Theme from '../Theme';

import Position from '../_statics/Position';

import Event from '../_vendors/Event';
import Util from '../_vendors/Util';

class Popover extends Component {

    static Position = Position;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.closeTimeout = null;

    }

    /**
     * public
     */
    resetPosition = () => {
        this.refs.popover.resetPosition();
    };

    clearCloseTimeout = () => {
        if (this.closeTimeout) {
            clearTimeout(this.closeTimeout);
            this.closeTimeout = null;
        }
    };

    mouseOverHandler = () => {
        this.clearCloseTimeout();
    };

    mouseOutHandler = e => {
        const {onRequestClose} = this.props;
        this.clearCloseTimeout();
        this.closeTimeout = setTimeout(() => {
            onRequestClose && onRequestClose(e);
        }, 1000 / 6);
    };

    renderHandler = el => {
        const {triggerEl} = this.props;
        Event.addEvent(triggerEl, 'mouseover', this.mouseOverHandler);
        Event.addEvent(triggerEl, 'mouseout', this.mouseOutHandler);
        Event.addEvent(el, 'mouseover', this.mouseOverHandler);
        Event.addEvent(el, 'mouseout', this.mouseOutHandler);
    };

    destroyHandler = el => {
        const {triggerEl} = this.props;
        Event.removeEvent(triggerEl, 'mouseover', this.mouseOverHandler);
        Event.removeEvent(triggerEl, 'mouseout', this.mouseOutHandler);
        Event.removeEvent(el, 'mouseover', this.mouseOverHandler);
        Event.removeEvent(el, 'mousemove', this.mouseOutHandler);
    };

    componentWillUnmount() {
        this.clearCloseTimeout();
    }

    render() {

        const {

                className, contentClassName,

                // not passing down these props
                onRequestClose,

                ...restProps

            } = this.props,

            popoverClassName = classNames('popover', {
                [className]: className
            }),

            popoverContentClassName = classNames('popover-content', {
                [contentClassName]: contentClassName
            });

        return (
            <TriggerPop {...restProps}
                        ref="popover"
                        className={popoverClassName}
                        contentClassName={popoverContentClassName}
                        onRender={this.renderHandler}
                        onDestroy={this.destroyHandler}/>
        );
    }

}

Popover.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the content element.
     */
    contentClassName: PropTypes.string,

    modalClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    triangle: PropTypes.element,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popover alignment.The value can be Popover.Position.LEFT or Popover.Position.RIGHT.
     */
    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * If true, popover will have animation effects.
     */
    isAnimated: PropTypes.bool,

    /**
     * The depth of Paper component.
     */
    depth: PropTypes.number,

    isBlurClose: PropTypes.bool,
    shouldPreventContainerScroll: PropTypes.bool,
    isTriggerPositionFixed: PropTypes.bool,
    resetPositionWait: PropTypes.bool,
    showModal: PropTypes.bool,

    /**
     * The function of popover render.
     */
    onRender: PropTypes.func,

    /**
     * The function of popover rendered.
     */
    onRendered: PropTypes.func,

    /**
     * The function of popover destroy.
     */
    onDestroy: PropTypes.func,

    /**
     * The function of popover destroyed.
     */
    onDestroyed: PropTypes.func,

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func

};

Popover.defaultProps = {

    visible: false,
    hasTriangle: true,
    theme: Theme.DEFAULT,
    position: Position.BOTTOM,
    isAnimated: true,

    isBlurClose: true,
    shouldPreventContainerScroll: true,
    isTriggerPositionFixed: false,
    resetPositionWait: 250,
    showModal: false

};

export default Popover;
<|endoftext|>


<commit_before>//Copyright 2014, Thomas Pronk
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

/**
 * Init JASMIN namespace
 * @private
 */
if( jasmin === undefined ) { var jasmin = function() {}; }

/**
 * Constant for AJAX type requests
 * @constant
 */
jasmin.REQUEST_MANAGER_TYPE_AJAX = 1;

/**
 * Constant for img type requests
 * @constant
 */
jasmin.REQUEST_MANAGER_TYPE_IMG = 2;

/**
 * Features for making AJAX requests, loading CSS and loading images
 * is set up to actively send any outstanding requests. It can
 * be configured to (a) actively send outstanding requests or not; (b) immediately
 * send a request or to wait until the next try (for bulk sending); (c) flush
 * all open request and call a callback once all have been sent (to send remaining
 * logs at the end of a participation
 * @constructor
 * @param {Function}  fail                Fail callback called with string errorMessage as argument. Defaults to alert
 * @param {Function}  report              callback for detailed reporting on RequestManager state
 * @param {Function}  error               callback for reporting request errors
 * @param {int}       timeout             Default number of ms waited until retrying a request (can be overriden by individual request), default = 4000
 * @param {int}       retries             Default number of retries until failing (can be overriden by individual request), default = 8
 * @param {int}       active              Will always sending and retry requests (true) or only when flushing (false), default = true
 * @param {int}       checkInterval       Number of ms between checking if we should resend anything; default = 300
 */
jasmin.RequestManager = function(
   fail,
   error,
   report,
   timeout,
   retries,
   active,
   checkInterval
) {
    this.fail          = fail === undefined? function( errorMessage ) { alert( errorMessage ); } : fail;
    this.error         = error === undefined? function() {}: error;
    this.report        = report === undefined? function() {}: report;
    this.timeout       = timeout === undefined? 4000 : timeout;
    this.retries       = retries === undefined? 8 : retries;
    this.active        = active  === undefined? true : active;
    this.checkInterval = checkInterval === undefined? 300: checkInterval;

    // For flushing (sending all remaining requests)
    this.flushing        = false;
    this.flushCallback   = undefined;

    // Constants for request states
    this.STATE_OPEN   = 1;  // Request is being transmitted
    this.STATE_FAILED = 2;  // Request failed
    this.STATE_FIRST  = 3;  // Request that haven't been sent yet

    this.states = {};       // Data on current AJAX states we are working with
    this.stateCounter = 0;  // stateCounter - Assigns unique stateId for each request
    this.transactionCounter = 0; // Counts individual transactions

    this.failed = false;    // If we permanently failed; don't attempt any ajax requests anymore

    // If we're active; start checking whether there is anything to do
    if( this.active )
    {
        this.check();
    }
};

/**
 * Make a single AJAX request
 * @param {const}    type         jasmin.REQUEST_MANAGER_TYPE_AJAX (request is passed on to jQuery.ajax or jasmin.REQUEST_MANAGER_TYPE_IMG (request is url of img)
 * @param {Object}   request      The request (what do say to which url)
 * @param {function} callback     Callback called on reply (with data replied by target url as argument)
 * @param {int}      timeout      Overrides default value set in constructor
 * @param {int}      retries      Overrides default value set in constructor
 * @return stateId of the request to be sent
 * @public
 */
jasmin.RequestManager.prototype.request = function( type, request, callback, timeout, retries ) {
    // Setup state
    var counter = this.stateCounter;
    this.states[ counter ] = {
        // Copy arguments
        "type"            : type,
        "request"         : request,
        "callback"        : callback,
        "timeout"         : timeout,
        "retries"      : retries,

        // Stuff to initialize
        "state"           : this.STATE_FIRST,
        "retryCounter"  : 0,
        "handled"         : false
    };
    this.stateCounter++;

    // If active; send immediately
    if( this.active ) {
        this.sendOpenRequests();
    };

    return counter;
};

/**
 * Send all open states (that should be sent)
 * @private
 */
jasmin.RequestManager.prototype.sendOpenRequests = function() {
    // Only continue if we are active or flushing and didn't fail
    if( !( ( this.active || this.flushing ) && !this.failed ) ) {
      return;
    }

    // Get stateIds to send
    var stateIds = this.statesToSend();

    // If anything to send, then send it
    if( stateIds.length > 0 ) {
        // Send each request on the list
        var stateId;
        for( var i in stateIds ) {
            stateId = stateIds[i];
            transactionId = this.transactionCounter;
            switch( this.states[ stateId ][ "type" ] )
            {
                case jasmin.REQUEST_MANAGER_TYPE_AJAX:
                    this.ajaxRequest( stateId, transactionId );
                    break;
                case jasmin.REQUEST_MANAGER_TYPE_IMG:
                    this.imgRequest( stateId, transactionId );
                    break;
            }

            this.transactionCounter++;

            // Update state
            this.states[ stateId ][ "state"          ] = this.STATE_OPEN;
            this.states[ stateId ][ "retryCounter" ]++;
            this.states[ stateId ][ "attemptTime"    ] = new Date().getTime();
        }
    }
};

/**
 * Process all open states (do nothing, add to sendlist, or fail)
 * @return stateIds to retry
 * @private
 */
jasmin.RequestManager.prototype.statesToSend = function()
{
    var time      = new Date().getTime();        // Get time
    var sendList  = new Array();       // Requests to retry

    // Check which states to add to sendList
    var timeout, retries;
    for( var i in this.states ) {
      // Check for now and open
      switch( this.states[i][ "state" ] ) {
        // Now? immediately add to sendList
        case this.STATE_FIRST:
            sendList.push( i );
            this.report(
               "RequestManager.statesToSend: ",
               "stateId " + i + ". STATE_FIRST"
               );
            break;
        // Open? Fail if timed out
        case this.STATE_OPEN:
          // get default or overriden value of timeout
          timeout = this.states[i][ "timeout" ] === undefined? this.timeout : this.states[i][ "timeout" ];
          // If timed out, then failed, add to list
          if( time - this.states[i][ "attemptTime" ] > timeout )
          {
             this.report(
                "RequestManager.statesToSend",
                "stateId " + i + " open and timed out"
                );

             // Timed out -> state to failed
             this.states[i][ "state" ] = this.STATE_FAILED;
          }
          break;
      }

      // Handle failed request -> retry if attempts not exceeded
      if( this.states[i][ "state" ] === this.STATE_FAILED )
      {
         // get default or overriden value of retries
         retries = this.states[i][ "retries" ] === undefined? this.retries : this.states[i][ "retries" ];

         // Check if max attempts exceeded
         if( this.states[i][ "retryCounter" ] >= this.retries )
         {
            this.report(
               "RequestManager.statesToSend",
               "stateId " + i + " failed; Exceeded " + this.retries + " attempts"
               );

            // We officially failed
            if( !this.failed )
            {
               this.failed = true;
               this.fail( "RequestManager: Max attempts exceeded" );
            }
         }
         else
         {
            this.report(
               "RequestManager.statesToSend",
               "stateId " + i + " added to sendList"
               );

            sendList.push( i );
         }
      }
   }

   return sendList;
};

/**
 * Do an (asynchronous) AJAX request. Request is associative array passed on
 * to jQuery.ajax
 * @param stateId
 * @param transactionId
 * @private
 */
jasmin.RequestManager.prototype.ajaxRequest = function( stateId, transactionId )
{
    // arguments to pass to jQuery.ajax
    var ajaxArgs = this.states[ stateId ][ "request" ];

    // Report ajax
    this.report(
       "RequestManager.ajaxRequest",
       "stateId = " + stateId + ", transactionId = " + transactionId + ", ajaxArgs = " + JSON.stringify( ajaxArgs )
    );

    // Send
    var self = this;
    var ajax = $.ajax( ajaxArgs );
    ajax.done(  function( response, status ) {
        // Report replies
        self.report(
            "RequestManager AJAX done",
            "stateId " + stateId + ", transactionId " + transactionId + ", status " + status + ", received:" + JSON.stringify( response )
        );
        self.success( stateId, response );
    } );
    ajax.fail( function( response, status ){
        // Error; only report it, let the request time out and retry
        self.error(
            "RequestManager AJAX fail",
            "stateId " + stateId + ", transactionId " + transactionId + ", status " + status + ", received:" + JSON.stringify( response )
        );
    } );
};

// Downloading an img
jasmin.RequestManager.prototype.imgRequest = function( stateId, transactionId ) {
    var url = this.states[ stateId ][ "request" ];

    // Report ajax
    this.report(
       "RequestManager.imgRequest",
       "stateId = " + stateId + ", transactionId = " + transactionId + ", url = " + JSON.stringify( url )
    );

    var self = this;
    this.states[ stateId ][ "reply" ] = $( "<img>" ).attr(
        "src", url
    ).load( function() {
        // Report replies
        self.report(
            "RequestManager img load",
            "stateId " + stateId + ", transactionId " + transactionId
        );
        self.success( stateId, self.states[ stateId ][ "reply" ] );
    } ).error( function() {
        self.error(
            "RequestManager img error",
            "stateId " + stateId + ", transactionId " + transactionId
        );
    } );
};

/**
 * On success, handle request; call callback with argument reply and remove state
 * @param stateId
 * @param reply
 * @private
 */
jasmin.RequestManager.prototype.success = function( stateId, reply ) {
    // Check if state still exists and is not handled, if so, handle it
    if( this.states[ stateId ] !== undefined && !this.states[ stateId ][ "handled" ] ) {
        // Request is now handled
        this.states[ stateId ][ "handled" ] = true;

        // Call callback (if defined)
        if( this.states[ stateId ][ "callback" ] !== undefined ) {
            try {
               this.states[ stateId ][ "callback" ]( reply );
            } catch( e ) {}
        }

        // Remove state
        delete this.states[ stateId ];
    }

    // If flushing; check if all requests are sent, then call flushCallback
    if( this.flushing && $.isEmptyObject( this.states ) ) {
        this.flushing = false;
        if( this.flushCallback !== undefined ) {
            this.flushCallback();
        }
    }
};

/**
 * Check if we should send open requests
 * @private
 */
jasmin.RequestManager.prototype.check = function()
{
    // Keep sending open requests if we are active or flushing and haven't failed yet
    // NB - this expression is also tested at start of sendOpenRequests
    if( ( this.active || this.flushing ) && !this.failed  ) {
        this.sendOpenRequests();
        var self = this;
        setTimeout(
           function() {
              self.check();
           },
           this.checkInterval
        );
    }
};

/**
 * Make sure that all open requests are being sent and then call this callback
 * (open requests are retried even regardless of active)
 * @param {Function} flushCallback After flushing this callback is called
 */
jasmin.RequestManager.prototype.flush = function( flushCallback ) {
    this.flushing      = true;
	this.flushCallback = flushCallback;

    if( $.isEmptyObject( this.states ) )
    {
        if( this.flushCallback !== undefined )
        {
           this.flushCallback();
        }
    }

    this.check();
};<commit_msg>RequestManager.js now allows data to be sent to the URL<commit_after>//Copyright 2014, Thomas Pronk
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

/**
 * Init JASMIN namespace
 * @private
 */
if( jasmin === undefined ) { var jasmin = function() {}; }

/**
 * Constant for AJAX type requests
 * @constant
 */
jasmin.REQUEST_MANAGER_TYPE_AJAX = 1;

/**
 * Constant for img type requests
 * @constant
 */
jasmin.REQUEST_MANAGER_TYPE_IMG = 2;

/**
 * Features for making AJAX requests, loading CSS and loading images
 * is set up to actively send any outstanding requests. It can
 * be configured to (a) actively send outstanding requests or not; (b) immediately
 * send a request or to wait until the next try (for bulk sending); (c) flush
 * all open request and call a callback once all have been sent (to send remaining
 * logs at the end of a participation
 * @constructor
 * @param {Function}  fail                Fail callback called with string errorMessage as argument. Defaults to alert
 * @param {Function}  report              callback for detailed reporting on RequestManager state
 * @param {Function}  error               callback for reporting request errors
 * @param {int}       timeout             Default number of ms waited until retrying a request (can be overriden by individual request), default = 4000
 * @param {int}       retries             Default number of retries until failing (can be overriden by individual request), default = 8
 * @param {int}       active              Will always sending and retry requests (true) or only when flushing (false), default = true
 * @param {int}       checkInterval       Number of ms between checking if we should resend anything; default = 300
 */
jasmin.RequestManager = function(
   fail,
   error,
   report,
   timeout,
   retries,
   active,
   checkInterval
) {
    this.fail          = fail === undefined? function( errorMessage ) { alert( errorMessage ); } : fail;
    this.error         = error === undefined? function() {}: error;
    this.report        = report === undefined? function() {}: report;
    this.timeout       = timeout === undefined? 4000 : timeout;
    this.retries       = retries === undefined? 8 : retries;
    this.active        = active  === undefined? true : active;
    this.checkInterval = checkInterval === undefined? 300: checkInterval;

    // For flushing (sending all remaining requests)
    this.flushing        = false;
    this.flushCallback   = undefined;

    // Constants for request states
    this.STATE_OPEN   = 1;  // Request is being transmitted
    this.STATE_FAILED = 2;  // Request failed
    this.STATE_FIRST  = 3;  // Request that haven't been sent yet

    this.states = {};       // Data on current AJAX states we are working with
    this.stateCounter = 0;  // stateCounter - Assigns unique stateId for each request
    this.transactionCounter = 0; // Counts individual transactions

    this.failed = false;    // If we permanently failed; don't attempt any ajax requests anymore

    // If we're active; start checking whether there is anything to do
    if( this.active )
    {
        this.check();
    }
};

/**
 * Make a single request
 * @param {const}    type         jasmin.REQUEST_MANAGER_TYPE_AJAX (request is passed on to jQuery.ajax or jasmin.REQUEST_MANAGER_TYPE_IMG (request is url of img)
 * @param {Object}   request      The request (what do say to which url)
 * @param {function} callback     Callback called on reply (with data replied by target url as argument)
 * @param {int}      timeout      Overrides default value set in constructor
 * @param {int}      retries      Overrides default value set in constructor
 * @return stateId of the request to be sent
 * @public
 */
jasmin.RequestManager.prototype.request = function( type, request, callback, timeout, retries ) {
    // Setup state
    var counter = this.stateCounter;
    this.states[ counter ] = {
        // Copy arguments
        "type"            : type,
        "request"         : request,
        "callback"        : callback,
        "timeout"         : timeout,
        "retries"      : retries,

        // Stuff to initialize
        "state"           : this.STATE_FIRST,
        "retryCounter"  : 0,
        "handled"         : false
    };
    this.stateCounter++;

    // If active; send immediately
    if( this.active ) {
        this.sendOpenRequests();
    };

    return counter;
};

/**
 * Send all open states (that should be sent)
 * @private
 */
jasmin.RequestManager.prototype.sendOpenRequests = function() {
    // Only continue if we are active or flushing and didn't fail
    if( !( ( this.active || this.flushing ) && !this.failed ) ) {
      return;
    }

    // Get stateIds to send
    var stateIds = this.statesToSend();

    // If anything to send, then send it
    if( stateIds.length > 0 ) {
        // Send each request on the list
        var stateId;
        for( var i in stateIds ) {
            stateId = stateIds[i];
            transactionId = this.transactionCounter;
            switch( this.states[ stateId ][ "type" ] )
            {
                case jasmin.REQUEST_MANAGER_TYPE_AJAX:
                    this.ajaxRequest( stateId, transactionId );
                    break;
                case jasmin.REQUEST_MANAGER_TYPE_IMG:
                    this.imgRequest( stateId, transactionId );
                    break;
            }

            this.transactionCounter++;

            // Update state
            this.states[ stateId ][ "state"          ] = this.STATE_OPEN;
            this.states[ stateId ][ "retryCounter" ]++;
            this.states[ stateId ][ "attemptTime"    ] = new Date().getTime();
        }
    }
};

/**
 * Process all open states (do nothing, add to sendlist, or fail)
 * @return stateIds to retry
 * @private
 */
jasmin.RequestManager.prototype.statesToSend = function()
{
    var time      = new Date().getTime();        // Get time
    var sendList  = new Array();       // Requests to retry

    // Check which states to add to sendList
    var timeout, retries;
    for( var i in this.states ) {
      // Check for now and open
      switch( this.states[i][ "state" ] ) {
        // Now? immediately add to sendList
        case this.STATE_FIRST:
            sendList.push( i );
            this.report(
               "RequestManager.statesToSend: ",
               "stateId " + i + ". STATE_FIRST"
               );
            break;
        // Open? Fail if timed out
        case this.STATE_OPEN:
          // get default or overriden value of timeout
          timeout = this.states[i][ "timeout" ] === undefined? this.timeout : this.states[i][ "timeout" ];
          // If timed out, then failed, add to list
          if( time - this.states[i][ "attemptTime" ] > timeout )
          {
             this.report(
                "RequestManager.statesToSend",
                "stateId " + i + " open and timed out"
                );

             // Timed out -> state to failed
             this.states[i][ "state" ] = this.STATE_FAILED;
          }
          break;
      }

      // Handle failed request -> retry if attempts not exceeded
      if( this.states[i][ "state" ] === this.STATE_FAILED )
      {
         // get default or overriden value of retries
         retries = this.states[i][ "retries" ] === undefined? this.retries : this.states[i][ "retries" ];

         // Check if max attempts exceeded
         if( this.states[i][ "retryCounter" ] >= this.retries )
         {
            this.report(
               "RequestManager.statesToSend",
               "stateId " + i + " failed; Exceeded " + this.retries + " attempts"
               );

            // We officially failed
            if( !this.failed )
            {
               this.failed = true;
               this.fail( "RequestManager: Max attempts exceeded" );
            }
         }
         else
         {
            this.report(
               "RequestManager.statesToSend",
               "stateId " + i + " added to sendList"
               );

            sendList.push( i );
         }
      }
   }

   return sendList;
};

/**
 * Do an (asynchronous) AJAX request. Request is associative array passed on
 * to jQuery.ajax
 * @param stateId
 * @param transactionId
 * @private
 */
jasmin.RequestManager.prototype.ajaxRequest = function( stateId, transactionId )
{
    // arguments to pass to jQuery.ajax
    var ajaxArgs = this.states[ stateId ][ "request" ];

    // Report ajax
    this.report(
       "RequestManager.ajaxRequest",
       "stateId = " + stateId + ", transactionId = " + transactionId + ", ajaxArgs = " + JSON.stringify( ajaxArgs )
    );

    // Send
    var self = this;
    var ajax = $.ajax( ajaxArgs );
    ajax.done(  function( response, status ) {
        // Report replies
        self.report(
            "RequestManager AJAX done",
            "stateId " + stateId + ", transactionId " + transactionId + ", status " + status + ", received:" + JSON.stringify( response )
        );
        self.success( stateId, response );
    } );
    ajax.fail( function( response, status ){
        // Error; only report it, let the request time out and retry
        self.error(
            "RequestManager AJAX fail",
            "stateId " + stateId + ", transactionId " + transactionId + ", status " + status + ", received:" + JSON.stringify( response )
        );
    } );
};

// Downloading an img
jasmin.RequestManager.prototype.imgRequest = function( stateId, transactionId ) {
    var url = this.states[ stateId ][ "request" ];

    // Report ajax
    this.report(
       "RequestManager.imgRequest",
       "stateId = " + stateId + ", transactionId = " + transactionId + ", url = " + JSON.stringify( url )
    );

    var self = this;
    this.states[ stateId ][ "reply" ] = $( "<img>" ).attr(
        "src", url
    ).load( function() {
        // Report replies
        self.report(
            "RequestManager img load",
            "stateId " + stateId + ", transactionId " + transactionId
        );
        self.success( stateId, self.states[ stateId ][ "reply" ] );
    } ).error( function() {
        self.error(
            "RequestManager img error",
            "stateId " + stateId + ", transactionId " + transactionId
        );
    } );
};

/**
 * On success, handle request; call callback with argument reply and remove state
 * @param stateId
 * @param reply
 * @private
 */
jasmin.RequestManager.prototype.success = function( stateId, reply ) {
    // Check if state still exists and is not handled, if so, handle it
    if( this.states[ stateId ] !== undefined && !this.states[ stateId ][ "handled" ] ) {
        // Request is now handled
        this.states[ stateId ][ "handled" ] = true;

        // Call callback (if defined)
        if( this.states[ stateId ][ "callback" ] !== undefined ) {
            try {
               this.states[ stateId ][ "callback" ]( reply );
            } catch( e ) {}
        }

        // Remove state
        delete this.states[ stateId ];
    }

    // If flushing; check if all requests are sent, then call flushCallback
    if( this.flushing && $.isEmptyObject( this.states ) ) {
        this.flushing = false;
        if( this.flushCallback !== undefined ) {
            this.flushCallback();
        }
    }
};

/**
 * Check if we should send open requests
 * @private
 */
jasmin.RequestManager.prototype.check = function()
{
    // Keep sending open requests if we are active or flushing and haven't failed yet
    // NB - this expression is also tested at start of sendOpenRequests
    if( ( this.active || this.flushing ) && !this.failed  ) {
        this.sendOpenRequests();
        var self = this;
        setTimeout(
           function() {
              self.check();
           },
           this.checkInterval
        );
    }
};

/**
 * Make sure that all open requests are being sent and then call this callback
 * (open requests are retried even regardless of active)
 * @param {Function} flushCallback After flushing this callback is called
 */
jasmin.RequestManager.prototype.flush = function( flushCallback ) {
    this.flushing      = true;
	this.flushCallback = flushCallback;

    if( $.isEmptyObject( this.states ) )
    {
        if( this.flushCallback !== undefined )
        {
           this.flushCallback();
        }
    }

    this.check();
};<|endoftext|>


<commit_before>// database-backup -----------------------------------------------------------

// Create a backup, suitable for use by database-restore.js, in a filename
// of the form "${DB_DB}-YYYYMMDD-HHHHMMSS.sql".

// External Modules ----------------------------------------------------------

const argv = require("yargs/yargs")(process.argv.slice(2))
    .usage("$0 [--DB_DB databaseName] [--DB_HOST hostname]")
    .default("DB_DB", "guests")
    .default("DB_HOST", "localhost")
    .default("DB_USER", "guests")
    .describe("DB_DB", "Database name to be backed up from")
    .describe("DB_HOST", "Database server host name")
    .describe("DB_USER", "Database username")
    .argv;
const { execSync } = require("child_process");

// Private Functions ---------------------------------------------------------

const leftPad = (input, size) => {
    let output = String(input);
    while (output.length < size) {
        output = "0" + output;
    }
    return output;
}

const timestamp = () => {
    const date = new Date();
    return date.getFullYear()
        + leftPad(date.getMonth() + 1, 2)
        + leftPad(date.getDate(), 2)
        + "-"
        + leftPad(date.getHours(), 2)
        + leftPad(date.getMinutes(), 2)
        + leftPad(date.getSeconds(), 2);

}

// Public Script -------------------------------------------------------------

const options = {
    "DB_DB": argv["DB_DB"],
    "DB_HOST": argv["DB_HOST"],
    "DB_USER": argv["DB_USER"],
};
const filename = options["DB_DB"] + "-" + timestamp() + ".sql";
console.info(`Backing up database ${options["DB_DB"]} to ${filename}`);

const command
    = `pg_dump --host=${options["DB_HOST"]} -U options["DB_USER"] ${options["DB_DB"]} > ${filename}`;
console.info(execSync(command).toString());

console.info("Database backup is complete");
<commit_msg>Oops ... forgot replacement syntax.<commit_after>// database-backup -----------------------------------------------------------

// Create a backup, suitable for use by database-restore.js, in a filename
// of the form "${DB_DB}-YYYYMMDD-HHHHMMSS.sql".

// External Modules ----------------------------------------------------------

const argv = require("yargs/yargs")(process.argv.slice(2))
    .usage("$0 [--DB_DB databaseName] [--DB_HOST hostname]")
    .default("DB_DB", "guests")
    .default("DB_HOST", "localhost")
    .default("DB_USER", "guests")
    .describe("DB_DB", "Database name to be backed up from")
    .describe("DB_HOST", "Database server host name")
    .describe("DB_USER", "Database username")
    .argv;
const { execSync } = require("child_process");

// Private Functions ---------------------------------------------------------

const leftPad = (input, size) => {
    let output = String(input);
    while (output.length < size) {
        output = "0" + output;
    }
    return output;
}

const timestamp = () => {
    const date = new Date();
    return date.getFullYear()
        + leftPad(date.getMonth() + 1, 2)
        + leftPad(date.getDate(), 2)
        + "-"
        + leftPad(date.getHours(), 2)
        + leftPad(date.getMinutes(), 2)
        + leftPad(date.getSeconds(), 2);

}

// Public Script -------------------------------------------------------------

const options = {
    "DB_DB": argv["DB_DB"],
    "DB_HOST": argv["DB_HOST"],
    "DB_USER": argv["DB_USER"],
};
const filename = options["DB_DB"] + "-" + timestamp() + ".sql";
console.info(`Backing up database ${options["DB_DB"]} to ${filename}`);

const command
    = `pg_dump --host=${options["DB_HOST"]} -U ${options["DB_USER"]} ${options["DB_DB"]} > ${filename}`;
console.info(execSync(command).toString());

console.info("Database backup is complete");
<|endoftext|>


<commit_before>exports.run = function (message, args, bot, db) {
  console.log(message.channel.type);
  if (message.channel.type !== "DM") {
    message.channel.send(
      message.author.toString() + ", answer only in my DMs!"
    );
    if (message.deletable) {
      message.delete();
    }
    return;
  }

  let arg = args.split(" ");
  let question = arg[0];
  let answer = arg.slice(1, arg.length).join(" ");
  message.attachments.each(
    (attachment) => (answer += "\n<" + attachment.url + ">")
  );
  db.query(
    "SELECT * FROM trivia_questions WHERE id = ?",
    [question],
    function (err, questionRow) {
      if (questionRow[0] == null) {
        message.reply("That question id is invalid. Please try again.");
        return;
      }
      if (!questionRow[0].isopen) {
        message.reply(
          "Question #" + questionRow[0].id + " is no longer taking answers."
        );
        return;
      }
      if (answer.length > 1700) {
        message.reply("There's a 1700 character limit on answers. Sorry!");
        return;
      }
      db.query(
        "SELECT * FROM trivia_answers WHERE user = ? AND questionid = ?",
        [message.author.id, question],
        function (err, rows) {
          if (rows.length === 0) {
            db.query(
              "INSERT INTO trivia_answers (user, questionid, answer, time) VALUES (?,?,?,now())",
              [message.author.id, question, answer]
            );
            message.reply(
              "Your answer to question #" +
                question +
                " (" +
                questionRow[0].question +
                ") has been submitted. Thank you!"
            );
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**New answer for question #" +
                    question +
                    " from " +
                    message.author.tag +
                    "**\n" +
                    answer
                );
            }
          } else {
            db.query(
              "UPDATE trivia_answers SET answer = ?, time=now(), viewed=0 WHERE user = ? AND questionid = ?",
              [answer, message.author.id, question]
            );
            message.reply(
              "Your answer to question #" +
                question +
                " (" +
                questionRow[0].question +
                ") has been updated, replacing your previous answer (" +
                rows[0].answer +
                "). Thank you!"
            );
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**Edited answer for question #" +
                    question +
                    " from " +
                    message.author.tag +
                    "**\n" +
                    answer
                );
            }
          }
        }
      );
    }
  );
};

exports.interaction = function (interaction, bot, db) {
  const questionId = interaction.options.getInteger("id");
  const answer = interaction.options.getString("answer");
  const userId = interaction.user.id;
  const userTag =
    interaction.user.username + "#" + interaction.user.discriminator;
  db.query(
    "SELECT * FROM trivia_questions WHERE id = ?",
    [questionId],
    function (err, questionRow) {
      if (questionRow[0] == null) {
        interaction.reply({
          content: "That question id is invalid. Please try again.",
          ephemeral: true,
        });
        return;
      }
      if (!questionRow[0].isopen) {
        interaction.reply({
          content: "Question #" + questionId + " is no longer taking answers.",
          ephemeral: true,
        });
        return;
      }
      if (answer.length > 1700) {
        interaction.reply({
          content: "There's a 1700 character limit on answers. Sorry!",
          ephemeral: true,
        });
        return;
      }
      db.query(
        "SELECT * FROM trivia_answers WHERE user = ? AND questionid = ?",
        [userId, questionId],
        function (err, rows) {
          if (rows.length === 0) {
            db.query(
              "INSERT INTO trivia_answers (user, questionid, answer, time) VALUES (?,?,?,now())",
              [userId, questionId, answer]
            );
            interaction.reply({
              content:
                "Your answer to question #" +
                questionId +
                " (" +
                questionRow[0].question +
                ") has been submitted. Thank you!",
              ephemeral: true,
            });
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**New answer for question #" +
                    questionId +
                    " from " +
                    userTag +
                    "**\n" +
                    answer
                );
            }
          } else {
            db.query(
              "UPDATE trivia_answers SET answer = ?, time=now(), viewed=0 WHERE user = ? AND questionid = ?",
              [answer, userId, questionId]
            );
            interaction.reply({
              content:
                "Your answer to question #" +
                questionId +
                " (" +
                questionRow[0].question +
                ") has been updated, replacing your previous answer (" +
                rows[0].answer +
                "). Thank you!",
              ephemeral: true,
            });
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**Edited answer for question #" +
                    questionId +
                    " from " +
                    userTag +
                    "**\n" +
                    answer
                );
            }
          }
        }
      );
    }
  );
};
<commit_msg>remove debug code<commit_after>exports.run = function (message, args, bot, db) {
  if (message.channel.type !== "DM") {
    message.channel.send(
      message.author.toString() + ", answer only in my DMs!"
    );
    if (message.deletable) {
      message.delete();
    }
    return;
  }

  let arg = args.split(" ");
  let question = arg[0];
  let answer = arg.slice(1, arg.length).join(" ");
  message.attachments.each(
    (attachment) => (answer += "\n<" + attachment.url + ">")
  );
  db.query(
    "SELECT * FROM trivia_questions WHERE id = ?",
    [question],
    function (err, questionRow) {
      if (questionRow[0] == null) {
        message.reply("That question id is invalid. Please try again.");
        return;
      }
      if (!questionRow[0].isopen) {
        message.reply(
          "Question #" + questionRow[0].id + " is no longer taking answers."
        );
        return;
      }
      if (answer.length > 1700) {
        message.reply("There's a 1700 character limit on answers. Sorry!");
        return;
      }
      db.query(
        "SELECT * FROM trivia_answers WHERE user = ? AND questionid = ?",
        [message.author.id, question],
        function (err, rows) {
          if (rows.length === 0) {
            db.query(
              "INSERT INTO trivia_answers (user, questionid, answer, time) VALUES (?,?,?,now())",
              [message.author.id, question, answer]
            );
            message.reply(
              "Your answer to question #" +
                question +
                " (" +
                questionRow[0].question +
                ") has been submitted. Thank you!"
            );
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**New answer for question #" +
                    question +
                    " from " +
                    message.author.tag +
                    "**\n" +
                    answer
                );
            }
          } else {
            db.query(
              "UPDATE trivia_answers SET answer = ?, time=now(), viewed=0 WHERE user = ? AND questionid = ?",
              [answer, message.author.id, question]
            );
            message.reply(
              "Your answer to question #" +
                question +
                " (" +
                questionRow[0].question +
                ") has been updated, replacing your previous answer (" +
                rows[0].answer +
                "). Thank you!"
            );
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**Edited answer for question #" +
                    question +
                    " from " +
                    message.author.tag +
                    "**\n" +
                    answer
                );
            }
          }
        }
      );
    }
  );
};

exports.interaction = function (interaction, bot, db) {
  const questionId = interaction.options.getInteger("id");
  const answer = interaction.options.getString("answer");
  const userId = interaction.user.id;
  const userTag =
    interaction.user.username + "#" + interaction.user.discriminator;
  db.query(
    "SELECT * FROM trivia_questions WHERE id = ?",
    [questionId],
    function (err, questionRow) {
      if (questionRow[0] == null) {
        interaction.reply({
          content: "That question id is invalid. Please try again.",
          ephemeral: true,
        });
        return;
      }
      if (!questionRow[0].isopen) {
        interaction.reply({
          content: "Question #" + questionId + " is no longer taking answers.",
          ephemeral: true,
        });
        return;
      }
      if (answer.length > 1700) {
        interaction.reply({
          content: "There's a 1700 character limit on answers. Sorry!",
          ephemeral: true,
        });
        return;
      }
      db.query(
        "SELECT * FROM trivia_answers WHERE user = ? AND questionid = ?",
        [userId, questionId],
        function (err, rows) {
          if (rows.length === 0) {
            db.query(
              "INSERT INTO trivia_answers (user, questionid, answer, time) VALUES (?,?,?,now())",
              [userId, questionId, answer]
            );
            interaction.reply({
              content:
                "Your answer to question #" +
                questionId +
                " (" +
                questionRow[0].question +
                ") has been submitted. Thank you!",
              ephemeral: true,
            });
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**New answer for question #" +
                    questionId +
                    " from " +
                    userTag +
                    "**\n" +
                    answer
                );
            }
          } else {
            db.query(
              "UPDATE trivia_answers SET answer = ?, time=now(), viewed=0 WHERE user = ? AND questionid = ?",
              [answer, userId, questionId]
            );
            interaction.reply({
              content:
                "Your answer to question #" +
                questionId +
                " (" +
                questionRow[0].question +
                ") has been updated, replacing your previous answer (" +
                rows[0].answer +
                "). Thank you!",
              ephemeral: true,
            });
            if (questionRow[0].watched) {
              bot.users.cache
                .get(questionRow[0].user)
                .send(
                  "**Edited answer for question #" +
                    questionId +
                    " from " +
                    userTag +
                    "**\n" +
                    answer
                );
            }
          }
        }
      );
    }
  );
};
<|endoftext|>


<commit_before>require('object.assign').shim();
var isEmpty = require("lodash.isempty");
var flatten = require("lodash.flatten");
var ValidationStrategy = require('./JoiValidationStrategy');

var ValidationFactory = Object.assign({
  isValid: function(errors, key) {
    if (isEmpty(errors)) {
      return true;
    } else {
      return key ? isEmpty(errors[key]) : isEmpty(errors);
    }
  },

  getValidationMessages: function(errors, key) {
    if (isEmpty(errors)) {
      return [];
    } else {
      if (key) {
        return errors[key] || [];
      } else {
        return flatten(Object.keys(errors).map(function(key) { return errors[key] || []; }));
      }
    }
  }
}, ValidationStrategy);

module.exports = ValidationFactory;
<commit_msg>Use consistent quote style<commit_after>require('object.assign').shim();
var isEmpty = require('lodash.isempty');
var flatten = require('lodash.flatten');
var ValidationStrategy = require('./JoiValidationStrategy');

var ValidationFactory = Object.assign({
  isValid: function(errors, key) {
    if (isEmpty(errors)) {
      return true;
    } else {
      return key ? isEmpty(errors[key]) : isEmpty(errors);
    }
  },

  getValidationMessages: function(errors, key) {
    if (isEmpty(errors)) {
      return [];
    } else {
      if (key) {
        return errors[key] || [];
      } else {
        return flatten(Object.keys(errors).map(function(key) { return errors[key] || []; }));
      }
    }
  }
}, ValidationStrategy);

module.exports = ValidationFactory;
<|endoftext|>