<commit_before>import { Class } from '../../core/Core.js';
import { lookupKey } from '../../dom/DOMEvent.js';
import { create as el } from '../../dom/Element.js';
import { loc } from '../../localisation/i18n.js';
import { MenuButtonView } from '../menu/MenuButtonView.js';
import { MenuView } from '../menu/MenuView.js';
import { PopOverView } from '../panels/PopOverView.js';
import { RootView } from '../RootView.js';
import { View } from '../View.js';
import { ViewEventsController } from '../ViewEventsController.js';

import /* { property, observes } from */ '../../foundation/Decorators.js';

// ---

/*global window */

const toView = function (name) {
    return name === '-'
        ? el('span.v-Toolbar-divider')
        : name === '*'
        ? null
        : this._views[name];
};

const OverflowMenuView = Class({
    Name: 'OverflowMenuView',

    Extends: MenuButtonView,

    didEnterDocument() {
        OverflowMenuView.parent.didEnterDocument.call(this);
        this.setShortcuts(null, '', {}, this.get('shortcuts'));
        return this;
    },

    willLeaveDocument() {
        this.setShortcuts(null, '', this.get('shortcuts'), {});
        return OverflowMenuView.parent.willLeaveDocument.call(this);
    },

    shortcuts: function () {
        const views = this.getFromPath('menuView.options');
        return views
            ? views.reduce((acc, view) => {
                  const shortcut = view.get('shortcut');
                  if (shortcut) {
                      shortcut.split(' ').forEach((key) => {
                          acc[key] = view;
                      });
                  }
                  return acc;
              }, {})
            : {};
    }.property('menuView'),

    setShortcuts: function (_, __, oldShortcuts, shortcuts) {
        if (this.get('isInDocument')) {
            const kbShortcuts = ViewEventsController.kbShortcuts;
            if (!shortcuts) {
                shortcuts = this.get('shortcuts');
            }
            for (const key in oldShortcuts) {
                kbShortcuts.deregister(key, this, 'activateButton');
            }
            for (const key in shortcuts) {
                kbShortcuts.register(key, this, 'activateButton');
            }
        }
    }.observes('shortcuts'),

    activateButton(event) {
        const key = lookupKey(event);
        const button = this.get('shortcuts')[key];
        if (button instanceof MenuButtonView) {
            this.activate(event);
        }
        button.activate(event);
    },
});

const viewIsBeforeFlex = function (view, flex) {
    const layer = view.get('layer');
    const childNodes = flex.parentNode.childNodes;
    let l = childNodes.length;
    while (l--) {
        const node = childNodes[l];
        if (node === layer) {
            return false;
        }
        if (node === flex) {
            return true;
        }
    }
    return true;
};

const ToolbarView = Class({
    Name: 'ToolbarView',

    Extends: View,

    className: 'v-Toolbar',

    config: 'standard',
    minimumGap: 20,
    preventOverlap: false,
    popOverOptions: null,
    overflowMenuType: '',

    init: function (/* ...mixins */) {
        ToolbarView.parent.constructor.apply(this, arguments);
        this._views = {
            overflow: new OverflowMenuView({
                type: this.get('overflowMenuType'),
                label: loc('More'),
                shortcut: '.',
                popOverView: this.popOverView || new PopOverView(),
                popOverOptions: this.get('popOverOptions'),
            }),
        };
        this._configs = {
            standard: {
                left: [],
                right: [],
            },
        };
        this._measureView = null;
        this._widths = {};
        this._flex = null;
    },

    destroy() {
        const views = this._views;
        for (const name in views) {
            const view = views[name];
            if (!view.get('parentView')) {
                view.destroy();
            }
        }
        ToolbarView.parent.destroy.call(this);
    },

    registerView(name, view, _dontMeasure) {
        this._views[name] = view;
        if (
            !_dontMeasure &&
            this.get('isInDocument') &&
            this.get('preventOverlap')
        ) {
            this.preMeasure().postMeasure();
        }
        return this;
    },

    registerViews(views) {
        for (const name in views) {
            this.registerView(name, views[name], true);
        }
        if (this.get('isInDocument') && this.get('preventOverlap')) {
            this.preMeasure().postMeasure();
        }
        return this;
    },

    registerConfig(name, config) {
        this._configs[name] = config;
        if (this.get('config') === name) {
            this.computedPropertyDidChange('config');
        }
        return this;
    },

    registerConfigs(configs) {
        for (const name in configs) {
            this.registerConfig(name, configs[name]);
        }
        return this;
    },

    getView(name) {
        return this._views[name];
    },

    getConfig(config) {
        return this._configs[config] || null;
    },

    // ---

    leftConfig: function () {
        const configs = this._configs;
        const config = configs[this.get('config')];
        return (config && config.left) || configs.standard.left;
    }.property('config'),

    rightConfig: function () {
        const configs = this._configs;
        const config = configs[this.get('config')];
        return (config && config.right) || configs.standard.right;
    }.property('config'),

    left: function () {
        let leftConfig = this.get('leftConfig');
        if (this.get('preventOverlap')) {
            const rightConfig = this.get('rightConfig');
            const widths = this._widths;
            let pxWidth = parseInt(
                window.getComputedStyle(this.get('layer')).width,
                10,
            );
            if (!pxWidth) {
                const rootView = this.getParent(RootView);
                pxWidth = rootView ? rootView.get('pxWidth') : 1024;
            }
            pxWidth -= this.get('minimumGap');
            let i;
            let l;
            for (i = 0, l = rightConfig.length; i < l; i += 1) {
                pxWidth -= widths[rightConfig[i]];
            }
            for (i = 0, l = leftConfig.length; i < l; i += 1) {
                const config = leftConfig[i];
                if (config === '*') {
                    break;
                } else {
                    pxWidth -= widths[config];
                }
            }
            if (pxWidth < 0 || i < l) {
                pxWidth -= widths.overflow;

                while (pxWidth < 0 && i--) {
                    pxWidth += widths[leftConfig[i]];
                }

                if (i < 0) {
                    i = 0;
                } else if (leftConfig[i] === '-') {
                    i -= 1;
                }

                this._views.overflow.set(
                    'menuView',
                    new MenuView({
                        showFilter: false,
                        options: leftConfig
                            .slice(i)
                            .map(toView, this)
                            .filter((view) => view instanceof View),
                    }),
                );

                if (i > 0) {
                    leftConfig = leftConfig.slice(0, i);
                    leftConfig.push('overflow');
                } else {
                    leftConfig = ['overflow'];
                }
            }
        }
        return leftConfig.map(toView, this);
    }.property('leftConfig', 'rightConfig', 'pxWidth'),

    right: function () {
        return this.get('rightConfig').map(toView, this);
    }.property('rightConfig'),

    preMeasure() {
        this.insertView(
            (this._measureView = new View({
                className: 'v-Toolbar-measure',
                layerStyles: {},
                childViews: Object.values(this._views).filter(
                    (view) => !view.get('parentView'),
                ),
                draw(layer) {
                    return [
                        el('span.v-Toolbar-divider'),
                        View.prototype.draw.call(this, layer),
                    ];
                },
            })),
            null,
            'top',
        );
        return this;
    },

    postMeasure() {
        const widths = this._widths;
        const views = this._views;
        const measureView = this._measureView;
        const unused = measureView.get('childViews');
        const container = measureView.get('layer');
        const containerBoundingClientRect = container.getBoundingClientRect();
        const firstButton = unused.length ? unused[0].get('layer') : null;

        for (const name in views) {
            widths[name] = views[name].get('pxWidth') || widths[name];
        }

        // Want to include any left/right margin, so get difference between
        // edge of first button and start of container
        widths['-'] =
            (firstButton
                ? firstButton.getBoundingClientRect().left
                : containerBoundingClientRect.right) -
            containerBoundingClientRect.left;

        this.removeView(measureView);
        let l = unused.length;
        while (l--) {
            measureView.removeView(unused[l]);
        }
        measureView.destroy();
        this._measureView = null;

        return this;
    },

    willEnterDocument() {
        ToolbarView.parent.willEnterDocument.call(this);
        if (this.get('preventOverlap')) {
            this.preMeasure();
        }
        return this;
    },

    didEnterDocument() {
        ToolbarView.parent.didEnterDocument.call(this);
        if (this.get('preventOverlap')) {
            this.postMeasure();
        }
        return this;
    },

    draw(/* layer */) {
        return [
            this.get('left'),
            (this._flex = el('div.v-Toolbar-flex')),
            this.get('right'),
        ];
    },

    toolbarNeedsRedraw: function (self, property, oldValue) {
        if (oldValue) {
            this.propertyNeedsRedraw(self, property, oldValue);
        }
    }.observes('left', 'right'),

    redrawLeft(layer, oldViews) {
        this.redrawSide(layer, true, oldViews, this.get('left'));
    },
    redrawRight(layer, oldViews) {
        this.redrawSide(layer, false, oldViews, this.get('right'));
    },

    redrawSide(layer, isLeft, oldViews, newViews) {
        let start = 0;
        let isEqual = true;
        const flex = this._flex;

        for (let i = start, l = oldViews.length; i < l; i += 1) {
            const view = oldViews[i];
            if (view instanceof View) {
                if (isEqual && view === newViews[i]) {
                    start += 1;
                } else {
                    isEqual = false;
                    // Check it hasn't already swapped sides!
                    if (viewIsBeforeFlex(view, flex) === isLeft) {
                        this.removeView(view);
                    }
                }
            } else {
                if (isEqual && !(newViews[i] instanceof View)) {
                    start += 1;
                    newViews[i] = view;
                } else {
                    layer.removeChild(view);
                }
            }
        }
        for (let i = start, l = newViews.length; i < l; i += 1) {
            const view = newViews[i];
            if (view instanceof View) {
                const parent = view.get('parentView');
                if (parent) {
                    parent.removeView(view);
                }
                this.insertView(
                    view,
                    isLeft ? flex : layer,
                    isLeft ? 'before' : 'bottom',
                );
            } else if (view) {
                layer.insertBefore(view, isLeft ? flex : null);
            }
        }
    },

    preventOverlapDidChange: function () {
        if (this.get('preventOverlap') && this.get('isInDocument')) {
            this.preMeasure().postMeasure().computedPropertyDidChange('left');
        }
    }
        .queue('after')
        .observes('preventOverlap')
        .on('button:resize'),
});

ToolbarView.OverflowMenuView = OverflowMenuView;

export { ToolbarView, OverflowMenuView };
<commit_msg>Fix edge case measuring toolbar size<commit_after>import { Class } from '../../core/Core.js';
import { lookupKey } from '../../dom/DOMEvent.js';
import { create as el } from '../../dom/Element.js';
import { loc } from '../../localisation/i18n.js';
import { MenuButtonView } from '../menu/MenuButtonView.js';
import { MenuView } from '../menu/MenuView.js';
import { PopOverView } from '../panels/PopOverView.js';
import { RootView } from '../RootView.js';
import { View } from '../View.js';
import { ViewEventsController } from '../ViewEventsController.js';

import /* { property, observes } from */ '../../foundation/Decorators.js';

// ---

/*global window */

const toView = function (name) {
    return name === '-'
        ? el('span.v-Toolbar-divider')
        : name === '*'
        ? null
        : this._views[name];
};

const OverflowMenuView = Class({
    Name: 'OverflowMenuView',

    Extends: MenuButtonView,

    didEnterDocument() {
        OverflowMenuView.parent.didEnterDocument.call(this);
        this.setShortcuts(null, '', {}, this.get('shortcuts'));
        return this;
    },

    willLeaveDocument() {
        this.setShortcuts(null, '', this.get('shortcuts'), {});
        return OverflowMenuView.parent.willLeaveDocument.call(this);
    },

    shortcuts: function () {
        const views = this.getFromPath('menuView.options');
        return views
            ? views.reduce((acc, view) => {
                  const shortcut = view.get('shortcut');
                  if (shortcut) {
                      shortcut.split(' ').forEach((key) => {
                          acc[key] = view;
                      });
                  }
                  return acc;
              }, {})
            : {};
    }.property('menuView'),

    setShortcuts: function (_, __, oldShortcuts, shortcuts) {
        if (this.get('isInDocument')) {
            const kbShortcuts = ViewEventsController.kbShortcuts;
            if (!shortcuts) {
                shortcuts = this.get('shortcuts');
            }
            for (const key in oldShortcuts) {
                kbShortcuts.deregister(key, this, 'activateButton');
            }
            for (const key in shortcuts) {
                kbShortcuts.register(key, this, 'activateButton');
            }
        }
    }.observes('shortcuts'),

    activateButton(event) {
        const key = lookupKey(event);
        const button = this.get('shortcuts')[key];
        if (button instanceof MenuButtonView) {
            this.activate(event);
        }
        button.activate(event);
    },
});

const viewIsBeforeFlex = function (view, flex) {
    const layer = view.get('layer');
    const childNodes = flex.parentNode.childNodes;
    let l = childNodes.length;
    while (l--) {
        const node = childNodes[l];
        if (node === layer) {
            return false;
        }
        if (node === flex) {
            return true;
        }
    }
    return true;
};

const ToolbarView = Class({
    Name: 'ToolbarView',

    Extends: View,

    className: 'v-Toolbar',

    config: 'standard',
    minimumGap: 20,
    preventOverlap: false,
    popOverOptions: null,
    overflowMenuType: '',

    init: function (/* ...mixins */) {
        ToolbarView.parent.constructor.apply(this, arguments);
        this._views = {
            overflow: new OverflowMenuView({
                type: this.get('overflowMenuType'),
                label: loc('More'),
                shortcut: '.',
                popOverView: this.popOverView || new PopOverView(),
                popOverOptions: this.get('popOverOptions'),
            }),
        };
        this._configs = {
            standard: {
                left: [],
                right: [],
            },
        };
        this._measureView = null;
        this._widths = {};
        this._flex = null;
    },

    destroy() {
        const views = this._views;
        for (const name in views) {
            const view = views[name];
            if (!view.get('parentView')) {
                view.destroy();
            }
        }
        ToolbarView.parent.destroy.call(this);
    },

    registerView(name, view, _dontMeasure) {
        this._views[name] = view;
        if (
            !_dontMeasure &&
            this.get('isInDocument') &&
            this.get('preventOverlap')
        ) {
            this.preMeasure().postMeasure();
        }
        return this;
    },

    registerViews(views) {
        for (const name in views) {
            this.registerView(name, views[name], true);
        }
        if (this.get('isInDocument') && this.get('preventOverlap')) {
            this.preMeasure().postMeasure();
        }
        return this;
    },

    registerConfig(name, config) {
        this._configs[name] = config;
        if (this.get('config') === name) {
            this.computedPropertyDidChange('config');
        }
        return this;
    },

    registerConfigs(configs) {
        for (const name in configs) {
            this.registerConfig(name, configs[name]);
        }
        return this;
    },

    getView(name) {
        return this._views[name];
    },

    getConfig(config) {
        return this._configs[config] || null;
    },

    // ---

    leftConfig: function () {
        const configs = this._configs;
        const config = configs[this.get('config')];
        return (config && config.left) || configs.standard.left;
    }.property('config'),

    rightConfig: function () {
        const configs = this._configs;
        const config = configs[this.get('config')];
        return (config && config.right) || configs.standard.right;
    }.property('config'),

    left: function () {
        let leftConfig = this.get('leftConfig');
        if (this.get('preventOverlap')) {
            const rightConfig = this.get('rightConfig');
            const widths = this._widths;
            const computedWidth = window.getComputedStyle(
                this.get('layer'),
            ).width;
            // If not yet laid out by the browser, this will return either the
            // empty string (if not in the document), or sometimes a %
            // (e.g. "100%") if display:none; neither are usable, so treat both
            // as 0 and get fallback width instead.
            let pxWidth = computedWidth.endsWith('px')
                ? parseInt(computedWidth, 10)
                : 0;
            if (!pxWidth) {
                const rootView = this.getParent(RootView);
                pxWidth = rootView ? rootView.get('pxWidth') : 1024;
            }
            pxWidth -= this.get('minimumGap');
            let i;
            let l;
            for (i = 0, l = rightConfig.length; i < l; i += 1) {
                pxWidth -= widths[rightConfig[i]];
            }
            for (i = 0, l = leftConfig.length; i < l; i += 1) {
                const config = leftConfig[i];
                if (config === '*') {
                    break;
                } else {
                    pxWidth -= widths[config];
                }
            }
            if (pxWidth < 0 || i < l) {
                pxWidth -= widths.overflow;

                while (pxWidth < 0 && i--) {
                    pxWidth += widths[leftConfig[i]];
                }

                if (i < 0) {
                    i = 0;
                } else if (leftConfig[i] === '-') {
                    i -= 1;
                }

                this._views.overflow.set(
                    'menuView',
                    new MenuView({
                        showFilter: false,
                        options: leftConfig
                            .slice(i)
                            .map(toView, this)
                            .filter((view) => view instanceof View),
                    }),
                );

                if (i > 0) {
                    leftConfig = leftConfig.slice(0, i);
                    leftConfig.push('overflow');
                } else {
                    leftConfig = ['overflow'];
                }
            }
        }
        return leftConfig.map(toView, this);
    }.property('leftConfig', 'rightConfig', 'pxWidth'),

    right: function () {
        return this.get('rightConfig').map(toView, this);
    }.property('rightConfig'),

    preMeasure() {
        this.insertView(
            (this._measureView = new View({
                className: 'v-Toolbar-measure',
                layerStyles: {},
                childViews: Object.values(this._views).filter(
                    (view) => !view.get('parentView'),
                ),
                draw(layer) {
                    return [
                        el('span.v-Toolbar-divider'),
                        View.prototype.draw.call(this, layer),
                    ];
                },
            })),
            null,
            'top',
        );
        return this;
    },

    postMeasure() {
        const widths = this._widths;
        const views = this._views;
        const measureView = this._measureView;
        const unused = measureView.get('childViews');
        const container = measureView.get('layer');
        const containerBoundingClientRect = container.getBoundingClientRect();
        const firstButton = unused.length ? unused[0].get('layer') : null;

        for (const name in views) {
            widths[name] = views[name].get('pxWidth') || widths[name];
        }

        // Want to include any left/right margin, so get difference between
        // edge of first button and start of container
        widths['-'] =
            (firstButton
                ? firstButton.getBoundingClientRect().left
                : containerBoundingClientRect.right) -
            containerBoundingClientRect.left;

        this.removeView(measureView);
        let l = unused.length;
        while (l--) {
            measureView.removeView(unused[l]);
        }
        measureView.destroy();
        this._measureView = null;

        return this;
    },

    willEnterDocument() {
        ToolbarView.parent.willEnterDocument.call(this);
        if (this.get('preventOverlap')) {
            this.preMeasure();
        }
        return this;
    },

    didEnterDocument() {
        ToolbarView.parent.didEnterDocument.call(this);
        if (this.get('preventOverlap')) {
            this.postMeasure();
        }
        return this;
    },

    draw(/* layer */) {
        return [
            this.get('left'),
            (this._flex = el('div.v-Toolbar-flex')),
            this.get('right'),
        ];
    },

    toolbarNeedsRedraw: function (self, property, oldValue) {
        if (oldValue) {
            this.propertyNeedsRedraw(self, property, oldValue);
        }
    }.observes('left', 'right'),

    redrawLeft(layer, oldViews) {
        this.redrawSide(layer, true, oldViews, this.get('left'));
    },
    redrawRight(layer, oldViews) {
        this.redrawSide(layer, false, oldViews, this.get('right'));
    },

    redrawSide(layer, isLeft, oldViews, newViews) {
        let start = 0;
        let isEqual = true;
        const flex = this._flex;

        for (let i = start, l = oldViews.length; i < l; i += 1) {
            const view = oldViews[i];
            if (view instanceof View) {
                if (isEqual && view === newViews[i]) {
                    start += 1;
                } else {
                    isEqual = false;
                    // Check it hasn't already swapped sides!
                    if (viewIsBeforeFlex(view, flex) === isLeft) {
                        this.removeView(view);
                    }
                }
            } else {
                if (isEqual && !(newViews[i] instanceof View)) {
                    start += 1;
                    newViews[i] = view;
                } else {
                    layer.removeChild(view);
                }
            }
        }
        for (let i = start, l = newViews.length; i < l; i += 1) {
            const view = newViews[i];
            if (view instanceof View) {
                const parent = view.get('parentView');
                if (parent) {
                    parent.removeView(view);
                }
                this.insertView(
                    view,
                    isLeft ? flex : layer,
                    isLeft ? 'before' : 'bottom',
                );
            } else if (view) {
                layer.insertBefore(view, isLeft ? flex : null);
            }
        }
    },

    preventOverlapDidChange: function () {
        if (this.get('preventOverlap') && this.get('isInDocument')) {
            this.preMeasure().postMeasure().computedPropertyDidChange('left');
        }
    }
        .queue('after')
        .observes('preventOverlap')
        .on('button:resize'),
});

ToolbarView.OverflowMenuView = OverflowMenuView;

export { ToolbarView, OverflowMenuView };
<|endoftext|>


<commit_before>// EventReference instances are keys, and objects with information about a
// callback are values.
var allHandlersPrivateData = new WeakMap();

function useCallback(emitter, eventReference, args) {
  var privateData = allHandlersPrivateData.get(eventReference);

  privateData.callback.apply(emitter, args);
  privateData.count--;

  return privateData.count === 0;
}

function getEventName(EventReference) {
  return allHandlersPrivateData.get(EventReference).eventName;
}

// EventReference instances are used as keys to get information about event
// callbacks. An event can also be cancelled with an EventReference instance.
export function EventReference(eventName, callback, count) {
  allHandlersPrivateData.set(this, {eventName: eventName, callback: callback, count: count});
}

// This WeapMap instance has EventEmitter instances as keys, and Map instances
// as values. The Map instances have event names as keys, and Set instances as
// values. The Set instance associated with an event name contains all the
// EventHandler instances associated with the event with that name for that
// emitter.
var allEventsForAllEmitters = new WeakMap();

function checkArgs(eventName, callback, count) {
  if (typeof eventName !== 'string') {
    throw new TypeError('Event name must be a string.');
  }

  if (typeof callback !== 'function') {
    throw new TypeError('Callback must be a function.');
  }

  if (typeof count !== 'number') {
    throw new TypeError('When given, count must be a number.');
  }

  if (count !== Infinity && (Math.floor(count) !== count || count < 1)) {
    throw new RangeError('Count must not be set to an integer less than zero or a non-integer.');
  }
}

export function EventEmitter() {
  if (!(this instanceof EventEmitter)) {
    return new EventEmitter();
  }

  allEventsForAllEmitters.set(this, new Map());
}

EventEmitter.prototype.on = function (eventName, callback, uncheckedCount) {
  var count = uncheckedCount === undefined ? Infinity : uncheckedCount;

  checkArgs(eventName, callback, count);

  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName);
  var eventReference = new EventReference(eventName, callback, count);

  if (allEventsForThisEventName) {
    allEventsForThisEventName.add(eventReference);
  } else {
    allEventsForThisEmitter.set(eventName, new Set([eventReference]));
  }

  return eventReference;
};

EventEmitter.prototype.off = function (handler) {
  var eventName = getEventName(handler);
  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName);

  allEventsForThisEventName.delete(handler);
};

EventEmitter.prototype.trigger = function (eventName) {
  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName) || [];
  var args = Array.prototype.slice.call(arguments, 1);
  var emitter = this;

  allEventsForThisEventName.forEach(function (eventReference) {
    var done = useCallback(emitter, eventReference, args);

    if (done) {
      allEventsForThisEventName.delete(eventReference);
    }
  });
};
<commit_msg>Support for Safari 7.1+ and IE11.<commit_after>// EventReference instances are keys, and objects with information about a
// callback are values.
var allHandlersPrivateData = new WeakMap();

function useCallback(emitter, eventReference, args) {
  var privateData = allHandlersPrivateData.get(eventReference);

  privateData.callback.apply(emitter, args);
  privateData.count--;

  return privateData.count === 0;
}

function getEventName(EventReference) {
  return allHandlersPrivateData.get(EventReference).eventName;
}

// EventReference instances are used as keys to get information about event
// callbacks. An event can also be cancelled with an EventReference instance.
export function EventReference(eventName, callback, count) {
  allHandlersPrivateData.set(this, {eventName: eventName, callback: callback, count: count});
}

// This WeapMap instance has EventEmitter instances as keys, and Map instances
// as values. The Map instances have event names as keys, and Set instances as
// values. The Set instance associated with an event name contains all the
// EventHandler instances associated with the event with that name for that
// emitter.
var allEventsForAllEmitters = new WeakMap();

function checkArgs(eventName, callback, count) {
  if (typeof eventName !== 'string') {
    throw new TypeError('Event name must be a string.');
  }

  if (typeof callback !== 'function') {
    throw new TypeError('Callback must be a function.');
  }

  if (typeof count !== 'number') {
    throw new TypeError('When given, count must be a number.');
  }

  if (count !== Infinity && (Math.floor(count) !== count || count < 1)) {
    throw new RangeError('Count must not be set to an integer less than zero or a non-integer.');
  }
}

export function EventEmitter() {
  if (!(this instanceof EventEmitter)) {
    return new EventEmitter();
  }

  allEventsForAllEmitters.set(this, new Map());
}

EventEmitter.prototype.on = function (eventName, callback, uncheckedCount) {
  var count = uncheckedCount === undefined ? Infinity : uncheckedCount;

  checkArgs(eventName, callback, count);

  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName);
  var eventReference = new EventReference(eventName, callback, count);

  if (!allEventsForThisEventName) {
    allEventsForThisEventName = new Set();
    allEventsForThisEmitter.set(eventName, allEventsForThisEventName);
  }

  allEventsForThisEventName.add(eventReference);

  return eventReference;
};

EventEmitter.prototype.off = function (handler) {
  var eventName = getEventName(handler);
  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName);

  allEventsForThisEventName.delete(handler);
};

EventEmitter.prototype.trigger = function (eventName) {
  var allEventsForThisEmitter = allEventsForAllEmitters.get(this);
  var allEventsForThisEventName = allEventsForThisEmitter.get(eventName) || [];
  var args = Array.prototype.slice.call(arguments, 1);
  var emitter = this;

  allEventsForThisEventName.forEach(function (eventReference) {
    var done = useCallback(emitter, eventReference, args);

    if (done) {
      allEventsForThisEventName.delete(eventReference);
    }
  });
};
<|endoftext|>


<commit_before>'use strict';

import util from 'gulp-util';

export const LOG = util.log;
export const COLORS = util.colors;

import {DEFAULT_SERVER_PORT} from './const.js';

//launch your task with `--port 9002` for example
export const SERVER_PORT = util.env.port || DEFAULT_SERVER_PORT;

//launch your task with `--open` `--open false` for example
export const OPEN = util.env.open === 'false' ? false : true;

//launch your task with `--disable-watch` for example
export const DISABLE_WATCH = util.env['disable-watch'];

var environment = (util.env.env || 'dev').toLowerCase();
if (environment === true) {
  environment = 'dev';
}
else if (['dev', 'prod', 'test'].indexOf(environment) === -1) {
  throw new Error('--env flag only accepts dev/prod/test')
}
LOG(COLORS.yellow('### Running in ' + environment + ' ###'));

export const ENV = environment;
<commit_msg>chore(*) add process.env.ENV support to gulp tasks<commit_after>'use strict';

import util from 'gulp-util';

export const LOG = util.log;
export const COLORS = util.colors;

import {DEFAULT_SERVER_PORT} from './const.js';

//launch your task with `--port 9002` for example
export const SERVER_PORT = util.env.port || DEFAULT_SERVER_PORT;

//launch your task with `--open` `--open false` for example
export const OPEN = util.env.open === 'false' ? false : true;

//launch your task with `--disable-watch` for example
export const DISABLE_WATCH = util.env['disable-watch'];

// if no --env flag, environment variable ENV is taken,
// if none of them is set, 'dev' is set by default
// if you pass by --env flag, it will be reflected in process.env.ENV
var environment = ((util.env.env === true ? 'dev' : util.env.env) || process.env.ENV || 'dev').toLowerCase();
if (['dev', 'prod', 'test'].indexOf(environment) === -1) {
  throw new Error('--env flag only accepts dev/prod/test')
}
if(!process.env.ENV){
  LOG(COLORS.yellow('[INFOS] Setting process.env.ENV=' + environment));
  process.env.ENV = environment;
}
LOG(COLORS.yellow('### Running in ' + environment + ' ###'));

export const ENV = environment;
<|endoftext|>


<commit_before>// @flow

import * as React from 'react'
import {StyleSheet, View, Animated, Dimensions, Platform} from 'react-native'
import {TabBarIcon} from '../../components/tabbar-icon'
import * as c from '../../components/colors'
import {CourseSearchBar} from '../components/searchbar'
import debounce from 'lodash/debounce'
import {updateCourseData} from '../../../flux/parts/sis'
import type {CourseType} from '../../../lib/course-search'
import type {ReduxState} from '../../../flux'
import type {TopLevelViewPropsType} from '../../types'
import {connect} from 'react-redux'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import toPairs from 'lodash/toPairs'
import {CourseSearchResultsList} from './list'
import LoadingView from '../../components/loading'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	allCourses: Array<CourseType>,
	courseDataState: string,
}

type ReduxDispatchProps = {
	updateCourseData: () => any,
}

type Props = ReactProps &
	ReduxStateProps &
	ReduxDispatchProps & {
		navigation: {state: {params: {}}},
	}

type State = {
	searchResults: Array<{title: string, data: Array<CourseType>}>,
	searchActive: boolean,
}

class CourseSearchView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Course Search',
		tabBarIcon: TabBarIcon('search'),
		title: 'SIS',
	}

	state = {
		searchResults: [],
		searchActive: false,
	}

	componentDidMount() {
		this.props.updateCourseData()
	}

	searchBar: any = null
	headerOpacity = new Animated.Value(1)
	searchBarTop = new Animated.Value(71)
	containerHeight = new Animated.Value(125)

	_performSearch = (text: string | Object) => {
		const query = text.toLowerCase()
		let results = this.props.allCourses.filter(course => {
			const section = course.section ? course.section.toLowerCase() : ''
			return (
				course.name.toLowerCase().includes(query) ||
				(course.instructors || []).some(name =>
					name.toLowerCase().includes(query),
				) ||
				`${course.departments
					.join('/')
					.toLowerCase()} ${course.number.toString()}${section}`.startsWith(
					query,
				)
			)
		})

		let grouped = groupBy(results, r => r.term)
		let groupedCourses = toPairs(grouped).map(([key, value]) => ({
			title: key,
			data: value,
		}))
		let sortedCourses = sortBy(groupedCourses, course => course.title).reverse()
		this.setState({searchResults: sortedCourses})
	}

	// We need to make the search run slightly behind the UI,
	// so I'm slowing it down by 50ms. 0ms also works, but seems
	// rather pointless.
	performSearch = debounce(this._performSearch, 50)

	onFocus = () => {
		Animated.timing(this.headerOpacity, {
			toValue: 0,
			duration: 800,
		}).start()
		Animated.timing(this.searchBarTop, {
			toValue: 10,
			duration: 800,
		}).start()
		Animated.timing(this.containerHeight, {
			toValue: 64,
			duration: 800,
		}).start()
		this.setState(() => ({searchActive: true}))
	}

	onCancel = () => {
		Animated.timing(this.headerOpacity, {
			toValue: 1,
			duration: 800,
		}).start()
		Animated.timing(this.searchBarTop, {
			toValue: 71,
			duration: 800,
		}).start()
		Animated.timing(this.containerHeight, {
			toValue: 125,
			duration: 800,
		}).start()
		this.setState(() => ({searchActive: false}))
	}

	render() {
		const screenWidth = Dimensions.get('window').width
		const searchBarWidth = screenWidth - 20
		const headerAnimation = {opacity: this.headerOpacity}
		const searchBarAnimation = {
			top: this.searchBarTop,
		}
		const containerAnimation = {height: this.containerHeight}
		const {searchActive} = this.state
		const loadingCourseData = this.props.courseDataState === 'updating'
		if (loadingCourseData) {
			return <LoadingView text="Loading Course Data..." />
		}

		return (
			<View style={styles.container}>
				<Animated.View
					style={[styles.searchContainer, styles.common, containerAnimation]}
				>
					<Animated.Text style={[styles.header, headerAnimation]}>
						Search Courses
					</Animated.Text>
					<Animated.View
						style={[
							styles.searchBarWrapper,
							{width: searchBarWidth},
							searchBarAnimation,
						]}
					>
						<CourseSearchBar
							getRef={ref => (this.searchBar = ref)}
							onCancel={this.onCancel}
							onFocus={this.onFocus}
							onSearchButtonPress={text => {
								if (Platform.OS === 'ios') {
									this.searchBar.blur()
								}
								this.performSearch(text)
							}}
							searchActive={searchActive}
						/>
					</Animated.View>
				</Animated.View>
				{searchActive ? (
					<CourseSearchResultsList
						navigation={this.props.navigation}
						terms={this.state.searchResults}
					/>
				) : (
					<View />
				)}
			</View>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		allCourses: state.sis ? state.sis.allCourses : [],
		courseDataState: state.sis ? state.sis.courseDataState : '',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		updateCourseData: () => dispatch(updateCourseData()),
	}
}

export default connect(mapState, mapDispatch)(CourseSearchView)

let styles = StyleSheet.create({
	container: {
		flex: 1,
	},

	common: {
		backgroundColor: c.white,
	},

	searchContainer: {
		margin: 0,
	},

	searchBarWrapper: {
		position: 'absolute',
		left: 10,
	},

	header: {
		fontSize: 30,
		fontWeight: 'bold',
		padding: 22,
		paddingLeft: 17,
	},
})
<commit_msg>add new placeholder for searchbar<commit_after>// @flow

import * as React from 'react'
import {StyleSheet, View, Animated, Dimensions, Platform} from 'react-native'
import {TabBarIcon} from '../../components/tabbar-icon'
import * as c from '../../components/colors'
import {CourseSearchBar} from '../components/searchbar'
import debounce from 'lodash/debounce'
import {updateCourseData} from '../../../flux/parts/sis'
import type {CourseType} from '../../../lib/course-search'
import type {ReduxState} from '../../../flux'
import type {TopLevelViewPropsType} from '../../types'
import {connect} from 'react-redux'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import toPairs from 'lodash/toPairs'
import {CourseSearchResultsList} from './list'
import LoadingView from '../../components/loading'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	allCourses: Array<CourseType>,
	courseDataState: string,
}

type ReduxDispatchProps = {
	updateCourseData: () => any,
}

type Props = ReactProps &
	ReduxStateProps &
	ReduxDispatchProps & {
		navigation: {state: {params: {}}},
	}

type State = {
	searchResults: Array<{title: string, data: Array<CourseType>}>,
	searchActive: boolean,
}

class CourseSearchView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Course Search',
		tabBarIcon: TabBarIcon('search'),
		title: 'SIS',
	}

	state = {
		searchResults: [],
		searchActive: false,
	}

	componentDidMount() {
		this.props.updateCourseData()
	}

	searchBar: any = null
	headerOpacity = new Animated.Value(1)
	searchBarTop = new Animated.Value(71)
	containerHeight = new Animated.Value(125)

	_performSearch = (text: string | Object) => {
		const query = text.toLowerCase()
		let results = this.props.allCourses.filter(course => {
			const section = course.section ? course.section.toLowerCase() : ''
			return (
				course.name.toLowerCase().includes(query) ||
				(course.instructors || []).some(name =>
					name.toLowerCase().includes(query),
				) ||
				`${course.departments
					.join('/')
					.toLowerCase()} ${course.number.toString()}${section}`.startsWith(
					query,
				)
			)
		})

		let grouped = groupBy(results, r => r.term)
		let groupedCourses = toPairs(grouped).map(([key, value]) => ({
			title: key,
			data: value,
		}))
		let sortedCourses = sortBy(groupedCourses, course => course.title).reverse()
		this.setState({searchResults: sortedCourses})
	}

	// We need to make the search run slightly behind the UI,
	// so I'm slowing it down by 50ms. 0ms also works, but seems
	// rather pointless.
	performSearch = debounce(this._performSearch, 50)

	onFocus = () => {
		Animated.timing(this.headerOpacity, {
			toValue: 0,
			duration: 800,
		}).start()
		Animated.timing(this.searchBarTop, {
			toValue: 10,
			duration: 800,
		}).start()
		Animated.timing(this.containerHeight, {
			toValue: 64,
			duration: 800,
		}).start()
		this.setState(() => ({searchActive: true}))
	}

	onCancel = () => {
		Animated.timing(this.headerOpacity, {
			toValue: 1,
			duration: 800,
		}).start()
		Animated.timing(this.searchBarTop, {
			toValue: 71,
			duration: 800,
		}).start()
		Animated.timing(this.containerHeight, {
			toValue: 125,
			duration: 800,
		}).start()
		this.setState(() => ({searchActive: false}))
	}

	render() {
		const screenWidth = Dimensions.get('window').width
		const searchBarWidth = screenWidth - 20
		const headerAnimation = {opacity: this.headerOpacity}
		const searchBarAnimation = {
			top: this.searchBarTop,
		}
		const containerAnimation = {height: this.containerHeight}
		const {searchActive} = this.state
		const loadingCourseData = this.props.courseDataState === 'updating'
		if (loadingCourseData) {
			return <LoadingView text="Loading Course Data..." />
		}

		return (
			<View style={styles.container}>
				<Animated.View
					style={[styles.searchContainer, styles.common, containerAnimation]}
				>
					<Animated.Text style={[styles.header, headerAnimation]}>
						Search Courses
					</Animated.Text>
					<Animated.View
						style={[
							styles.searchBarWrapper,
							{width: searchBarWidth},
							searchBarAnimation,
						]}
					>
						<CourseSearchBar
							getRef={ref => (this.searchBar = ref)}
							onCancel={this.onCancel}
							onFocus={this.onFocus}
							onSearchButtonPress={text => {
								if (Platform.OS === 'ios') {
									this.searchBar.blur()
								}
								this.performSearch(text)
							}}
							placeholder={"Search Class & Lab"}
							searchActive={searchActive}
						/>
					</Animated.View>
				</Animated.View>
				{searchActive ? (
					<CourseSearchResultsList
						navigation={this.props.navigation}
						terms={this.state.searchResults}
					/>
				) : (
					<View />
				)}
			</View>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		allCourses: state.sis ? state.sis.allCourses : [],
		courseDataState: state.sis ? state.sis.courseDataState : '',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		updateCourseData: () => dispatch(updateCourseData()),
	}
}

export default connect(mapState, mapDispatch)(CourseSearchView)

let styles = StyleSheet.create({
	container: {
		flex: 1,
	},

	common: {
		backgroundColor: c.white,
	},

	searchContainer: {
		margin: 0,
	},

	searchBarWrapper: {
		position: 'absolute',
		left: 10,
	},

	header: {
		fontSize: 30,
		fontWeight: 'bold',
		padding: 22,
		paddingLeft: 17,
	},
})
<|endoftext|>


<commit_before>let coreDependencies = require('ima/build.js');
let path = require('path');
let sharedTasksState = require('./gulpState');
let macroTasks = require('./macroTasks.js');

let appDependencies;
try {
    appDependencies = require(path.resolve('.', 'app/build.js'));
} catch (error) {
    console.info(error.message);
    console.info('The default application dependencies will be used.');

    appDependencies = {
        js: [],
        languages: [],
        less: [],
        vendors: {
            common: [],
            server: [],
            client: []
        },
        bundle: {
            js: [],
            es: [],
            css: []
        }
    };
}

let babelConfig = {
    esVendor: {
        presets: ['react'],
        plugins: ['external-helpers-2']
    },

    vendor: {
        presets: ['react'],
        plugins: ['external-helpers-2']
    },

    esApp: {
        presets: ['react'],
        plugins: ['transform-es2015-modules-systemjs', 'external-helpers-2']
    },

    app: {
        presets: [],
        plugins: []
    },

    server: {
        presets: ['react'],
        plugins: ['transform-es2015-modules-commonjs']
    }
};
let $Debug = true;
let legacyCompactMode = false;

if (['production', 'prod', 'test'].includes(process.env.NODE_ENV)) {
    const esPlugins = [
        'transform-react-constant-elements',
        'transform-react-inline-elements'
    ];
    babelConfig.esApp.plugins = babelConfig.esApp.plugins.concat(esPlugins);
    babelConfig.esVendor.plugins = babelConfig.esVendor.plugins.concat(esPlugins);
    babelConfig.app.presets = ['es2017', 'es2016', ['es2015', { loose: true }]];
    babelConfig.vendor.presets = ['es2017', 'es2016', ['es2015', { loose: true }],  'react'];
    $Debug = false;
    legacyCompactMode = true;
}

if (
    ['dev', undefined].includes(process.env.NODE_ENV) &&
    (process.argv.some(arg => /^--legacy-compat-mode$/.test(arg)) ||
    Object.keys(process.env).includes('npm_config_legacy_compat_mode'))
) {
    babelConfig.app.presets = ['es2017', 'es2016', ['es2015', { loose: true }]];
    babelConfig.vendor.presets = ['es2017', 'es2016', ['es2015', { loose: true }],  'react'];
    legacyCompactMode = true;
}

exports.legacyCompactMode = legacyCompactMode;
exports.babelConfig = babelConfig;

exports.uglifyCompression = {
    global_defs: {
        $Debug: $Debug
    },
    ema: 6,
    dead_code: true
};

exports.vendorDependencies = {
    common: coreDependencies.vendors.common.concat(appDependencies.vendors.common),
    server: coreDependencies.vendors.server.concat(appDependencies.vendors.server),
    client: coreDependencies.vendors.client.concat(appDependencies.vendors.client),
    test: coreDependencies.vendors.test.concat(appDependencies.vendors.test)
};

exports.tasks = {
    dev: macroTasks.DEFAULT_DEV_SUBTASKS,
    build: macroTasks.DEFAULT_BUILD_SUBTASKS,
    spa: macroTasks.DEFAULT_SPA_SUBTASKS
};

exports.files = {
    vendor: {
        src: {
            client: 'vendor.client.src.js'
        },
        name: {
            server: 'vendor.server.js',
            esClient: 'vendor.client.es.js',
            client: 'vendor.client.js'
        },
        dest: {
            server: './build/ima/',
            client: './build/static/js/',
            tmp: './build/ima/'
        },
        watch: ['./app/build.js', './ima/build.js']
    },
    app: {
        name: {
            server: 'app.server.js',
            client: 'app.client.js',
            esClient: 'app.client.es.js'
        },
        clearServerSide: ['production', 'prod', 'test'].includes(process.env.NODE_ENV),
        src: [].concat(appDependencies.js, appDependencies.mainjs),
        dest: {
            server: './build/ima/',
            client: './build/static/js/'
        },
        watch:['./app/**/*.{js,jsx}', './app/main.js', '!./app/environment.js']
    },
    server: {
        cwd: '/',
        src: ['./server/*.js', './server/**/*.js'],
        base: './server/',
        dest: './build/',
        watch: ['./server/*.js', './server/**/*.js', './app/*.js', '!./server/ima/config/*.js']
    },
    less: {
        cwd: '/',
        base: './app/assets/less/',
        name: './app/assets/less/app.less',
        src: appDependencies.less,
        dest: './build/static/css/',
        watch: ['./app/**/*.less', '!./app/assets/bower/']
    },
    locale: {
        src: appDependencies.languages,
        dest:{
            server: './build/ima/locale/',
            client: './build/static/js/locale/'
        },
        watch: ['./app/**/*.json']
    },
    shim : {
        name: 'shim.js',
        src: [
            './node_modules/ima/polyfill/collectionEnumeration.js'
        ],
        dest: {
            client: './build/static/js/',
            server: './build/ima/'
        }
    },
    polyfill: {
        js: {
            name: 'polyfill.js',
            src: [
                './node_modules/babel-polyfill/dist/polyfill.min.js',
                './node_modules/custom-event-polyfill/custom-event-polyfill.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        },
        es: {
            name: 'polyfill.es.js',
            src: [
                './node_modules/custom-event-polyfill/custom-event-polyfill.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        },
        fetch: {
            name: 'fetch-polyfill.js',
            src: [
                './node_modules/whatwg-fetch/fetch.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        }
    },
    bundle: {
        js: {
            name: 'app.bundle.min.js',
            src: appDependencies.bundle.js,
            dest: './build/static/js/'
        },
        es: {
            name: 'app.bundle.es.min.js',
            src: appDependencies.bundle.es,
            dest: './build/static/js/'
        },
        css: {
            name: 'app.bundle.min.css',
            src: appDependencies.bundle.css,
            dest: './build/static/css/'
        }
    }
};

exports.onTerminate = () => {
    if (sharedTasksState.karmaServer) {
        sharedTasksState.karmaServer.stop();
    }

    setTimeout(() => {
        process.exit();
    });
};
<commit_msg>#! fixed typo<commit_after>let coreDependencies = require('ima/build.js');
let path = require('path');
let sharedTasksState = require('./gulpState');
let macroTasks = require('./macroTasks.js');

let appDependencies;
try {
    appDependencies = require(path.resolve('.', 'app/build.js'));
} catch (error) {
    console.info(error.message);
    console.info('The default application dependencies will be used.');

    appDependencies = {
        js: [],
        languages: [],
        less: [],
        vendors: {
            common: [],
            server: [],
            client: []
        },
        bundle: {
            js: [],
            es: [],
            css: []
        }
    };
}

let babelConfig = {
    esVendor: {
        presets: ['react'],
        plugins: ['external-helpers-2']
    },

    vendor: {
        presets: ['react'],
        plugins: ['external-helpers-2']
    },

    esApp: {
        presets: ['react'],
        plugins: ['transform-es2015-modules-systemjs', 'external-helpers-2']
    },

    app: {
        presets: [],
        plugins: []
    },

    server: {
        presets: ['react'],
        plugins: ['transform-es2015-modules-commonjs']
    }
};
let $Debug = true;
let legacyCompactMode = false;

if (['production', 'prod', 'test'].includes(process.env.NODE_ENV)) {
    const esPlugins = [
        'transform-react-constant-elements',
        'transform-react-inline-elements'
    ];
    babelConfig.esApp.plugins = babelConfig.esApp.plugins.concat(esPlugins);
    babelConfig.esVendor.plugins = babelConfig.esVendor.plugins.concat(esPlugins);
    babelConfig.app.presets = ['es2017', 'es2016', ['es2015', { loose: true }]];
    babelConfig.vendor.presets = ['es2017', 'es2016', ['es2015', { loose: true }],  'react'];
    $Debug = false;
    legacyCompactMode = true;
}

if (
    ['dev', undefined].includes(process.env.NODE_ENV) &&
    (process.argv.some(arg => /^--legacy-compat-mode$/.test(arg)) ||
    Object.keys(process.env).includes('npm_config_legacy_compat_mode'))
) {
    babelConfig.app.presets = ['es2017', 'es2016', ['es2015', { loose: true }]];
    babelConfig.vendor.presets = ['es2017', 'es2016', ['es2015', { loose: true }],  'react'];
    legacyCompactMode = true;
}

exports.legacyCompactMode = legacyCompactMode;
exports.babelConfig = babelConfig;

exports.uglifyCompression = {
    global_defs: {
        $Debug: $Debug
    },
    ecma: 6,
    dead_code: true
};

exports.vendorDependencies = {
    common: coreDependencies.vendors.common.concat(appDependencies.vendors.common),
    server: coreDependencies.vendors.server.concat(appDependencies.vendors.server),
    client: coreDependencies.vendors.client.concat(appDependencies.vendors.client),
    test: coreDependencies.vendors.test.concat(appDependencies.vendors.test)
};

exports.tasks = {
    dev: macroTasks.DEFAULT_DEV_SUBTASKS,
    build: macroTasks.DEFAULT_BUILD_SUBTASKS,
    spa: macroTasks.DEFAULT_SPA_SUBTASKS
};

exports.files = {
    vendor: {
        src: {
            client: 'vendor.client.src.js'
        },
        name: {
            server: 'vendor.server.js',
            esClient: 'vendor.client.es.js',
            client: 'vendor.client.js'
        },
        dest: {
            server: './build/ima/',
            client: './build/static/js/',
            tmp: './build/ima/'
        },
        watch: ['./app/build.js', './ima/build.js']
    },
    app: {
        name: {
            server: 'app.server.js',
            client: 'app.client.js',
            esClient: 'app.client.es.js'
        },
        clearServerSide: ['production', 'prod', 'test'].includes(process.env.NODE_ENV),
        src: [].concat(appDependencies.js, appDependencies.mainjs),
        dest: {
            server: './build/ima/',
            client: './build/static/js/'
        },
        watch:['./app/**/*.{js,jsx}', './app/main.js', '!./app/environment.js']
    },
    server: {
        cwd: '/',
        src: ['./server/*.js', './server/**/*.js'],
        base: './server/',
        dest: './build/',
        watch: ['./server/*.js', './server/**/*.js', './app/*.js', '!./server/ima/config/*.js']
    },
    less: {
        cwd: '/',
        base: './app/assets/less/',
        name: './app/assets/less/app.less',
        src: appDependencies.less,
        dest: './build/static/css/',
        watch: ['./app/**/*.less', '!./app/assets/bower/']
    },
    locale: {
        src: appDependencies.languages,
        dest:{
            server: './build/ima/locale/',
            client: './build/static/js/locale/'
        },
        watch: ['./app/**/*.json']
    },
    shim : {
        name: 'shim.js',
        src: [
            './node_modules/ima/polyfill/collectionEnumeration.js'
        ],
        dest: {
            client: './build/static/js/',
            server: './build/ima/'
        }
    },
    polyfill: {
        js: {
            name: 'polyfill.js',
            src: [
                './node_modules/babel-polyfill/dist/polyfill.min.js',
                './node_modules/custom-event-polyfill/custom-event-polyfill.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        },
        es: {
            name: 'polyfill.es.js',
            src: [
                './node_modules/custom-event-polyfill/custom-event-polyfill.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        },
        fetch: {
            name: 'fetch-polyfill.js',
            src: [
                './node_modules/whatwg-fetch/fetch.js'
            ],
            dest: {
                client: './build/static/js/'
            }
        }
    },
    bundle: {
        js: {
            name: 'app.bundle.min.js',
            src: appDependencies.bundle.js,
            dest: './build/static/js/'
        },
        es: {
            name: 'app.bundle.es.min.js',
            src: appDependencies.bundle.es,
            dest: './build/static/js/'
        },
        css: {
            name: 'app.bundle.min.css',
            src: appDependencies.bundle.css,
            dest: './build/static/css/'
        }
    }
};

exports.onTerminate = () => {
    if (sharedTasksState.karmaServer) {
        sharedTasksState.karmaServer.stop();
    }

    setTimeout(() => {
        process.exit();
    });
};
<|endoftext|>


<commit_before>// バックで環境設定などのややこしい処理をします。
var sphero = require("sphero");

var loopInterval = 1000;
var events = {};
module.exports = {
  addEventListener: function(eventName, fn) {
    if (typeof events[eventName] === "undefined") {
      events[eventName] = [];
    }
    events[eventName].push(fn);
  },
  connect: function(port, callback) {
    var orb = sphero(port);
    orb.connect(function() {
      orb.detectCollisions(); // 衝突判定を有効化
      console.log("準備開始");
      orb.startCalibration(); // 位置関係の補正
      setTimeout(function() {
        console.log("準備終了");
        orb.finishCalibration()}, 10000);
      callback(orb);
    });
    orb.on("collision", function() {
      raiseEvent("collision");
    });
    return orb;
  }
};

function raiseEvent(eventName) {
  if (typeof events[eventName] !== "undefined") {
    events[eventName].forEach(i => {
      i();
    });
  }
}
<commit_msg>callback(orb) の順とセミコロンの抜けを修正<commit_after>// バックで環境設定などのややこしい処理をします。
var sphero = require("sphero");

var loopInterval = 1000;
var events = {};
module.exports = {
  addEventListener: function(eventName, fn) {
    if (typeof events[eventName] === "undefined") {
      events[eventName] = [];
    }
    events[eventName].push(fn);
  },
  connect: function(port, callback) {
    var orb = sphero(port);
    orb.connect(function() {
      orb.detectCollisions(); // 衝突判定を有効化
      console.log("準備開始");
      orb.startCalibration(); // 位置関係の補正
      setTimeout(function() {
        console.log("準備終了");
        orb.finishCalibration();
	callback(orb);
      }, 10000);
    });
    orb.on("collision", function() {
      raiseEvent("collision");
    });
    return orb;
  }
};

function raiseEvent(eventName) {
  if (typeof events[eventName] !== "undefined") {
    events[eventName].forEach(i => {
      i();
    });
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var util        = require('util'),
    net         = require('net'),
    byline      = require('byline'),
    sets        = require('simplesets'),
    dict        = require('dict'),
    optimist    = require('optimist');
var argv= optimist
    // Host
    .default('host','127.0.0.1')
    .alias('host','h')
    .describe('host',"Host to bind to")

    // Port
    .default('port',8051)
    .alias('port','p')
    .describe('port',"Port to bind to")

    // Should we actually read data from the client
    .boolean('read')
    .default('read',true)
    .describe('read',"Actually read data from the client")

    // Timeout
    .default('timeout',0)
    .alias('timeout','t')
    .describe('timeout',"How long to hang, in seconds, 0 means forever")

    // Disconnect
    .boolean('disconnect')
    .default('disconnect',false)
    .describe('disconnect',"Disconnect instead of hanging")

    // Misc stuff
    .boolean('help')
    // We're done
    .argv;

// If we want help, just give the help and be done
if ( argv.help ) {
    optimist.showHelp()
    return;
}

var clients = new sets.Set();

// Timeout stuff

function runTimeout() {
    var work = clients.array();
    var timeout = argv.timeout * 1000;
    var now = new Date().getTime();
    work.forEach( function(client, idx, a) {
        if ( now - client._localData.lastTime > timeout ) {
            client.end();
        }
    });
}

if ( argv.timeout > 0 ) {
    setInterval(runTimeout,1000)
}

// Client state machine
var ST_INVALID = -2;
var ST_HANG = -1;
var ST_PRE = 0;
var ST_READ_HEADER = 1;
var ST_DONE_HEADER = 2;

var RX_METHOD_LINE = /^(GET|POST) (\/[^ ]*) (HTTP\/[0-9\.]+)$/;
var RX_HEADER = /^([A-Za-z0-9-]+): (.+)$/;
var RX_SPLIT_URI = /^\/([a-zA-Z0-9]+):([^\/]*)(\/.*)?$/;

function parseUri(uri) {
    var rv = dict({});
    var part;
    var rest = uri;
    while ( rest && ( part = rest.match( RX_SPLIT_URI ) ) ) {
        var key = part[1];
        var value = part[2];
        rest = part[3];

        if ( rv.has(key) ) {
            throw "repeated key " + key;
        } else if ( key == 'redir' ) {
            var nValue = Number(value);
            if ( value == "true" ) {
                nValue = 1;
            } else if ( value == "false" ) {
                nValue = 0;
            } else if ( value == "loop" ) {
                nValue = -1;
            } else if ( isNaN(nValue) ) {
                throw "invalid value for 'redir': " + value;
            }
            if ( nValue ) {
                rv.set('redir',nValue);
                rv.set('_orig_redir',value);
            }
        } else if ( key == 'next' ) {
            rv.set('next',rest);
            rest = undefined;
        } else if ( key == 'disconnect' ) {
            rv.set('disconnect', !!value);
        } else {
            throw "invalid key " + key;
        }
    }
    return rv;
}

function deparseUri(data) {
    // Rework some things
    if ( data.has('_orig_redir') )
        data.set('redir', data.get('_orig_redir') );

    var next = data.get('next');
    var rv = "";
    data.forEach( function(v,k) {
        if ( k.match(/^_/) ) return; // Skip internal ones
        if ( k == 'next' ) return; // Skip next

        rv = rv + "/" + k + ":" + v;
    } );
    if ( next )
        rv = rv + "/next:" + next;
    return rv;
}

function respondError(c,error) {
    c.write("HTTP/1.1 500 Infernal Server Error\r\n");
    c.write("\r\n" + error + "\r\n");
    c.end();
}

function respondRedirect(c,dest) {
    var localData = c._localData;
    var host = localData.headers['Host']
    if ( !host ) {
        host = argv.host + ":" + argv.port;
    }
    if ( ! dest ) {
        return respondError( c, "Invalid destination" );
    }

    c.write("HTTP/1.1 302 Found\r\n");
    c.write( "Location: http://" + host + dest + "\r\n");
    c.write("\r\nYou can has redirect\r\n");
    c.end();
}

function doRespond(c) {
    var localData = c._localData;
    var uriData = localData.uriData;

    localData.lastTime = new Date().getTime();

    if ( localData.returnError ) {
        return respondError( c, localData.returnError );
    } else if ( uriData.has('redir') ) {
        var redir = uriData.get('redir');
        if ( redir < 0 ) { // negative numbers are infinite
            if ( uriData.has('next') )
                return respondError( c, "infinite redirect and 'next' do not mix" );
        } else if ( redir-1 <= 0 ) {
            uriData.delete('redir');
            uriData.delete('_orig_redir');
        } else {
            uriData.set('redir', redir-1);
            uriData.delete('_orig_redir');
        }
        return respondRedirect( c, deparseUri( uriData ) );
    } else if ( uriData.has('next') ) { // Redir should take priority
        return respondRedirect( c, uriData.get('next') );
    }
}

function gotLine(c,line,stream) {
    var localData = c._localData;

    localData.lastTime = new Date().getTime();
    var state = localData.state;
    if ( state == ST_PRE ) {
        var data = line.match(RX_METHOD_LINE);
        if ( data ) {
            console.log( line );
            localData.method = data[1];
            localData.uri = data[2];
            localData.headers = {};
            state = ST_READ_HEADER;
            try {
                localData.uriData = parseUri( localData.uri );
                if ( localData.uriData.has('disconnect') ) {
                    localData.disconnect = !! localData.uriData.get('disconnect');
                }
            } catch (e) {
                localData.returnError = e;
                console.log("URI Parse error: " + e);
                localData.disconnect = false;
            }
        } else {
            state = ST_INVALID;
        }
    } else if ( state == ST_READ_HEADER ) {
        if ( line == "" ) {
            state = ST_DONE_HEADER;
            doRespond(c);
        } else {
            var data = line.match(RX_HEADER);
            localData.headers[ data[1] ] = data[2];
        }
    } else if ( state == ST_DONE_HEADER ) {
        state = ST_INVALID;
    }

    if ( state == ST_HANG ) {
        if ( localData.disconnect ) {
            c.end();
        } else {
            c.pause();
        }
    } else if ( state == ST_INVALID ) {
        console.log("Reached invalid state");
        c.end();
    }

    localData.state = state;
}

function _dumpDict( dict ) {
    var rv = {};
    if ( dict )
        dict.forEach( function(v,k) { rv[k] = v; } );
    return rv;
}

function onServerConnect(c) {
    clients.add( c );
    c._localData = {
        "lastTime": new Date().getTime(),
        "state": ST_PRE,
        "disconnect": argv.disconnect
    };
    c.setEncoding('utf8');
    c.setKeepAlive(false);

    c.on('end', function() {
        clients.remove( c );
    });

    // If we don't want to read anything, never even start
    if ( argv.read ) {
        lineStream = byline.createStream( c, {"keepEmptyLines": true} );
        lineStream.on('data',function(line) { gotLine(c,line,this); });
    } else if ( c._localData.disconnect ) {
        c.end();
    } else {
        c.pause();
    }
}

var server = net.createServer( onServerConnect );
server.listen( argv.port, argv.host, function() {
    console.log("Server listening")
});
<commit_msg>Add hanging on client phases, set timeout from request, set delay<commit_after>#!/usr/bin/env node
var util        = require('util'),
    net         = require('net'),
    byline      = require('byline'),
    sets        = require('simplesets'),
    dict        = require('dict'),
    optimist    = require('optimist');
var argv= optimist
    // Host
    .default('host','127.0.0.1')
    .alias('host','h')
    .describe('host',"Host to bind to")

    // Port
    .default('port',8051)
    .alias('port','p')
    .describe('port',"Port to bind to")

    // Should we actually read data from the client
    .boolean('read')
    .default('read',true)
    .describe('read',"Actually read data from the client")

    // Timeout
    .default('timeout',0)
    .alias('timeout','t')
    .describe('timeout',"How long to hang, in seconds, 0 means forever")

    // Disconnect
    .boolean('disconnect')
    .default('disconnect',false)
    .describe('disconnect',"Disconnect instead of hanging")

    // Misc stuff
    .boolean('help')
    // We're done
    .argv;

// If we want help, just give the help and be done
if ( argv.help ) {
    optimist.showHelp()
    return;
}

var clients = new sets.Set();

// Timeout stuff
var timeout = argv.timeout * 1000;

function runTimeout() {
    var work = clients.array();
    var now = new Date().getTime();
    work.forEach( function(client, idx, a) {
        if ( now - client._localData.lastTime > client._localData.timeout ) {
            client.end();
        }
    });
}

if ( argv.timeout > 0 ) {
    setInterval(runTimeout,1000)
}

// Client state machine
var ST_INVALID = -2;
var ST_HANG = -1;
var ST_PRE = 0;
var ST_READ_HEADER = 1;
var ST_RESPOND = 2;

var RX_METHOD_LINE = /^(GET|POST) (\/[^ ]*) (HTTP\/[0-9\.]+)$/;
var RX_HEADER = /^([A-Za-z0-9-]+): (.+)$/;
var RX_SPLIT_URI = /^\/([a-zA-Z0-9]+):([^\/]*)(\/.*)?$/;

function parseUri(uri) {
    var rv = dict({});
    var part;
    var rest = uri;
    while ( rest && ( part = rest.match( RX_SPLIT_URI ) ) ) {
        var key = part[1];
        var value = part[2];
        rest = part[3];

        if ( rv.has(key) ) {
            throw "repeated key " + key;
        } else if ( key == 'redir' ) {
            var nValue = Number(value);
            if ( value == "true" ) {
                nValue = 1;
            } else if ( value == "false" ) {
                nValue = 0;
            } else if ( value == "loop" ) {
                nValue = -1;
            } else if ( isNaN(nValue) ) {
                throw "invalid value for 'redir': " + value;
            }
            if ( nValue ) {
                rv.set('redir',nValue);
                rv.set('_orig_redir',value);
            }
        } else if ( key == 'next' ) {
            rv.set('next',rest);
            rest = undefined;
        } else if ( key == 'disconnect' ) {
            rv.set('disconnect', !!value);
        } else if ( key == 'hangon' ) {
            var nValue;
            if ( value == "read_header" )
                nValue = ST_READ_HEADER;
            else if ( value == "respond" )
                nValue = ST_RESPOND;

            if (typeof nValue === 'undefined') {
                throw "invalid value for 'hangon': " + value;
            } else {
                rv.set('hangon',nValue);
                rv.set('_orig_hangon',value);
            }
        } else if ( key == "timeout" ) {
            var nValue = Number(value);
            if ( isNaN(nValue) ) {
                throw "invalid value for 'timeout': " + value;
            }
            rv.set('timeout',nValue);
        } else if ( key == "delay" ) {
            var nValue = Number(value);
            if ( isNaN(nValue) ) {
                throw "invalid value for 'delay': " + value;
            }
            rv.set('delay',nValue);
        } else {
            throw "invalid key " + key;
        }
    }
    return rv;
}

function deparseUri(data) {
    // Rework some things
    ['redir','hangon'].forEach( function(key,idx,a) {
        if ( data.has('_orig_' + key) )
            data.set(key, data.get('_orig_' + key) );
    } );

    var next = data.get('next');
    var rv = "";
    data.forEach( function(v,k) {
        if ( k.match(/^_/) ) return; // Skip internal ones
        if ( k == 'next' ) return; // Skip next

        rv = rv + "/" + k + ":" + v;
    } );
    if ( next )
        rv = rv + "/next:" + next;
    return rv;
}

function respondError(c,error) {
    c.write("HTTP/1.1 500 Infernal Server Error\r\n");
    c.write("\r\n" + error + "\r\n");
    c.end();
}

function respondRedirect(c,dest) {
    var localData = c._localData;
    var host = localData.headers['Host']
    if ( !host ) {
        host = argv.host + ":" + argv.port;
    }
    if ( ! dest ) {
        return respondError( c, "Invalid destination" );
    }

    c.write("HTTP/1.1 302 Found\r\n");
    c.write( "Location: http://" + host + dest + "\r\n");
    c.write("\r\nYou can has redirect\r\n");
    c.end();
}

function doRespond(c) {
    var localData = c._localData;
    var uriData = localData.uriData;

    localData.lastTime = new Date().getTime();

    if ( localData.returnError ) {
        return respondError( c, localData.returnError );
    } else if ( uriData.has('redir') ) {
        var redir = uriData.get('redir');
        if ( redir < 0 ) { // negative numbers are infinite
            if ( uriData.has('next') )
                return respondError( c, "infinite redirect and 'next' do not mix" );
        } else if ( redir-1 <= 0 ) {
            uriData.delete('redir');
            uriData.delete('_orig_redir');
        } else {
            uriData.set('redir', redir-1);
            uriData.delete('_orig_redir');
        }
        return respondRedirect( c, deparseUri( uriData ) );
    } else if ( uriData.has('next') ) { // Redir should take priority
        return respondRedirect( c, uriData.get('next') );
    }
}

function gotLine(c,line,stream) {
    var localData = c._localData;

    localData.lastTime = new Date().getTime();
    localData.timeout = timeout;
    localData.hangState = undefined;

    var state = localData.state;
    if ( localData.hangState == state )
        state = ST_HANG;

    if ( state == ST_PRE ) {
        var data = line.match(RX_METHOD_LINE);
        if ( data ) {
            console.log( line );
            localData.method = data[1];
            localData.uri = data[2];
            localData.headers = {};
            state = ST_READ_HEADER;
            try {
                localData.uriData = parseUri( localData.uri );
                if ( localData.uriData.has('disconnect') )
                    localData.disconnect = !! localData.uriData.get('disconnect');
                if ( localData.uriData.has('timeout') )
                    localData.timeout = localData.uriData.get('timeout');
                if ( localData.uriData.has('hangon') )
                    localData.hangState = localData.uriData.get('hangon');
                if ( localData.uriData.has('delay') )
                    if ( localData.uriData.get('delay') > localData.timeout )
                        throw "Delay longer than timeout";
            } catch (e) {
                localData.returnError = e;
                console.log("URI Parse error: " + e);
                localData.disconnect = false;
            }
        } else {
            state = ST_INVALID;
        }
    } else if ( state == ST_READ_HEADER ) {
        if ( line == "" ) {
            state = ST_RESPOND;
            if ( localData.uriData.has('delay') ) {
                setTimeout( function() { doRespond(c); }, localData.uriData.get('delay') * 1000 );
            } else {
                doRespond(c);
            }
        } else {
            var data = line.match(RX_HEADER);
            localData.headers[ data[1] ] = data[2];
        }
    } else if ( state == ST_RESPOND ) {
        state = ST_INVALID;
    }

    if ( state == ST_HANG ) {
        if ( localData.disconnect ) {
            c.end();
        } else {
            c.pause();
        }
    } else if ( state == ST_INVALID ) {
        console.log("Reached invalid state");
        c.end();
    }

    localData.state = state;
}

function _dumpDict( dict ) {
    var rv = {};
    if ( dict )
        dict.forEach( function(v,k) { rv[k] = v; } );
    return rv;
}

function onServerConnect(c) {
    clients.add( c );
    c._localData = {
        "lastTime": new Date().getTime(),
        "state": ST_PRE,
        "disconnect": argv.disconnect
    };
    c.setEncoding('utf8');
    c.setKeepAlive(false);

    c.on('error', function(e) {}); // We honestly do not care here.
    c.on('end', function() {
        clients.remove( c );
    });

    // If we don't want to read anything, never even start
    if ( argv.read ) {
        lineStream = byline.createStream( c, {"keepEmptyLines": true} );
        lineStream.on('data',function(line) { gotLine(c,line,this); });
    } else if ( c._localData.disconnect ) {
        c.end();
    } else {
        c.pause();
    }
}

var server = net.createServer( onServerConnect );
server.listen( argv.port, argv.host, function() {
    console.log("Server listening")
});
<|endoftext|>


<commit_before>/*
 * Chrome-Last.fm-Scrobbler BandCamp.com Connector by George Pollard
 *                (loosely based on Yasin Okumus's MySpace connector)
 * v0.1
 */
 var lastTrack = null;

$(function(){
	// bind page unload function to discard current "now listening"
	cancel();
});

var durationPart = ".track_info .time";
var durationRegex = /[ \n]*(\d+):(\d+)[ \n]*\/[ \n]*(\d+):(\d+)[ \n]*/;
function parseDuration(match){
	try{
		var m = durationRegex.exec(match);
		return {current: parseInt(m[1],10)*60 + parseInt(m[2],10), total: parseInt(m[3],10)*60 + parseInt(m[4],10)};
	}catch(err){
		return 0;
	}
}

function parseArtist()
{
	var byLine = "";
	if (isAlbum())
	{
		byLine = $("dt.hiddenAccess:contains('band name') ~ dd").text();
	}
	else // isTrack
	{
		byLine = $(".albumTitle nobr").text();
	}

	var artist = $.trim($.trim(byLine).substring(2));
	if(!artist) {
		artist = $('span[itemprop=byArtist]').text();
	}

	return artist;
}

function parseTitle()
{
	if (isAlbum())
	{
		return $(".track_info .title").text();
	}
	else //isTrack
	{
		return $(".trackTitle").first().text();
	}
}

function isAlbum()
{
	return document.location.toString().indexOf('bandcamp.com/album/') >= 0;
}

function cancel(){
	$(window).unload(function() {
		// reset the background scrobbler song data
		chrome.extension.sendRequest({type: 'reset'});
		return true;
	});
}

console.log('BandCampScrobbler: loaded');

$(durationPart).bind('DOMSubtreeModified',function(e){
	var duration = parseDuration($(durationPart).text());

	//console.log('duration - ' + duration.current + ' / ' + duration.total);

	if(duration.current > 0) { // it's playing

		var artist = parseArtist();
		var track = parseTitle();

		var dashIndex = track.indexOf('-');
		if (artist == 'Various Artists' && dashIndex >= 0)
		{
			artist = track.substring(0, dashIndex);
			track = track.substring(dashIndex + 1);
		}

		artist = $.trim(artist);
		track = $.trim(track);

		if (lastTrack != track)
		{
			lastTrack = track;

			//console.log("BandCampScrobbler: scrobbling '" + track + "' by " + artist);
			chrome.extension.sendRequest({type: 'validate', artist: artist, track: track}, function(response) {
				if (response != false){
					chrome.extension.sendRequest({type: 'nowPlaying', artist: response.artist, track: response.track, duration: duration.total});
				}
				else
				{
					chrome.extension.sendRequest({type: 'nowPlaying', duration: duration.total});
				}
			});
		}
	}
});
<commit_msg>Bugfix - track name was duplicated, breaking sync to last.fm<commit_after>/*
 * Chrome-Last.fm-Scrobbler BandCamp.com Connector by George Pollard
 *                (loosely based on Yasin Okumus's MySpace connector)
 * v0.1
 */
 var lastTrack = null;

$(function(){
	// bind page unload function to discard current "now listening"
	cancel();
});

var durationPart = ".track_info .time";
var durationRegex = /[ \n]*(\d+):(\d+)[ \n]*\/[ \n]*(\d+):(\d+)[ \n]*/;
function parseDuration(match){
	try{
		var m = durationRegex.exec(match);
		return {current: parseInt(m[1],10)*60 + parseInt(m[2],10), total: parseInt(m[3],10)*60 + parseInt(m[4],10)};
	}catch(err){
		return 0;
	}
}

function parseArtist()
{
	var byLine = "";
	if (isAlbum())
	{
		byLine = $("dt.hiddenAccess:contains('band name') ~ dd").text();
	}
	else // isTrack
	{
		byLine = $(".albumTitle nobr").text();
	}

	var artist = $.trim($.trim(byLine).substring(2));
	if(!artist) {
		artist = $('span[itemprop=byArtist]').text();
	}

	return artist;
}

function parseTitle()
{
	if (isAlbum())
	{
		return $(".track_info .title").first().text();
	}
	else //isTrack
	{
		return $(".trackTitle").first().text();
	}
}

function isAlbum()
{
	return document.location.toString().indexOf('bandcamp.com/album/') >= 0;
}

function cancel(){
	$(window).unload(function() {
		// reset the background scrobbler song data
		chrome.extension.sendRequest({type: 'reset'});
		return true;
	});
}

console.log('BandCampScrobbler: loaded');

$(durationPart).bind('DOMSubtreeModified',function(e){
	var duration = parseDuration($(durationPart).text());

	//console.log('duration - ' + duration.current + ' / ' + duration.total);

	if(duration.current > 0) { // it's playing

		var artist = parseArtist();
		var track = parseTitle();

		var dashIndex = track.indexOf('-');
		if (artist == 'Various Artists' && dashIndex >= 0)
		{
			artist = track.substring(0, dashIndex);
			track = track.substring(dashIndex + 1);
		}

		artist = $.trim(artist);
		track = $.trim(track);

		if (lastTrack != track)
		{
			lastTrack = track;

			//console.log("BandCampScrobbler: scrobbling '" + track + "' by " + artist);
			chrome.extension.sendRequest({type: 'validate', artist: artist, track: track}, function(response) {
				if (response != false){
					chrome.extension.sendRequest({type: 'nowPlaying', artist: response.artist, track: response.track, duration: duration.total});
				}
				else
				{
					chrome.extension.sendRequest({type: 'nowPlaying', duration: duration.total});
				}
			});
		}
	}
});
<|endoftext|>


<commit_before>Cypress.Commands.add('openSelectDialog', (selectId, option) => {
  cy
    .get(`a#${selectId}`).click()
    .get('td').contains(option).click()
    .get('.modal-footer .btn-primary').contains('Select').click();
});

Cypress.Commands.add('visitInstanceCreateForm', () => {
  cy
    .route('http://localhost:8080/api/marketplace-plugins/', 'fixture:offerings/marketplacePlugins.json')
    .route('http://localhost:8080/api/marketplace-orders/**', [])
    .route('http://localhost:8080/api/marketplace-offerings/**', 'fixture:offerings/openstackInstance.json')
    .route('http://localhost:8080/api/marketplace-categories/**', 'fixture:offerings/offeringCategory.json')
    .route('http://localhost:8080/api/service-settings/**', 'fixture:offerings/serviceSettings.json')
    .route('http://localhost:8080/api/openstacktenant-images/**', 'fixture:offerings/images.json')
    .route('http://localhost:8080/api/openstacktenant-flavors/**', 'fixture:offerings/flavors.json')
    .route('http://localhost:8080/api/keys/**', 'fixture:offerings/sshKeys.json')
    .route('http://localhost:8080/api/openstacktenant-security-groups/**', 'fixture:offerings/securityGroups.json')
    .route('http://localhost:8080/api/openstacktenant-subnets/**', 'fixture:offerings/subnets.json')
    .route('http://localhost:8080/api/openstacktenant-floating-ips/**', 'fixture:offerings/floatingIps.json')
    .route('http://localhost:8080/api/openstacktenant-instance-availability-zones/**', [])
    .visit('/#/organizations/bf6d515c9e6e445f9c339021b30fc96b/marketplace-offering/3bcdcdb0987545f0b50e6eed26bb49d6/');
});

Cypress.Commands.add('buttonShouldBeDisabled', btnClass =>
  cy.get(btnClass).should('have.class', 'disabled')
);
<commit_msg>Fix Cypress tests [WAL-2735]<commit_after>Cypress.Commands.add('openSelectDialog', (selectId, option) => {
  cy
    .get(`a#${selectId}`).click()
    .get('td').contains(option).click()
    .get('.modal-footer .btn-primary').contains('Select').click();
});

Cypress.Commands.add('visitInstanceCreateForm', () => {
  cy
    .route('http://localhost:8080/api/marketplace-plugins/', 'fixture:offerings/marketplacePlugins.json')
    .route('http://localhost:8080/api/marketplace-orders/**', [])
    .route('http://localhost:8080/api/marketplace-offerings/**', 'fixture:offerings/openstackInstance.json')
    .route('http://localhost:8080/api/marketplace-categories/**', 'fixture:offerings/offeringCategory.json')
    .route('http://localhost:8080/api/service-settings/**', 'fixture:offerings/serviceSettings.json')
    .route('http://localhost:8080/api/openstacktenant-images/**', 'fixture:offerings/images.json')
    .route('http://localhost:8080/api/openstacktenant-flavors/**', 'fixture:offerings/flavors.json')
    .route('http://localhost:8080/api/keys/**', 'fixture:offerings/sshKeys.json')
    .route('http://localhost:8080/api/openstacktenant-security-groups/**', 'fixture:offerings/securityGroups.json')
    .route('http://localhost:8080/api/openstacktenant-subnets/**', 'fixture:offerings/subnets.json')
    .route('http://localhost:8080/api/openstacktenant-floating-ips/**', 'fixture:offerings/floatingIps.json')
    .route('http://localhost:8080/api/openstacktenant-instance-availability-zones/**', [])
    .route('http://localhost:8080/api/openstacktenant-volume-types/**', [])
    .visit('/#/organizations/bf6d515c9e6e445f9c339021b30fc96b/marketplace-offering/3bcdcdb0987545f0b50e6eed26bb49d6/');
});

Cypress.Commands.add('buttonShouldBeDisabled', btnClass =>
  cy.get(btnClass).should('have.class', 'disabled')
);
<|endoftext|>