<commit_before>/// <reference path="../../../../argos-sdk/libraries/ext/ext-core-debug.js"/>
/// <reference path="../../../../argos-sdk/libraries/sdata/sdata-client-debug"/>
/// <reference path="../../../../argos-sdk/libraries/Simplate.js"/>
/// <reference path="../../../../argos-sdk/src/View.js"/>
/// <reference path="../../../../argos-sdk/src/List.js"/>

Ext.namespace("Mobile.SalesLogix");

Mobile.SalesLogix.AreaCategoryIssueLookup = Ext.extend(Sage.Platform.Mobile.List, {
    //Templates
    contentTemplate: new Simplate([
        '
{%: $.$descriptor %}
'
    ]),

    //Localization
    titleText: 'Accounts',
    resourceKind: 'areaCategoryIssues',

    //View Properties
    expose: false,
    hideSearch: true,
    id: 'areacategoryissue_lookup',
    queryOrderBy: 'Area,Category,Issue',
    querySelect: [
        'Area',
        'Category',
        'Issue'
    ],
    show: function(options) {
        this.active = options.where;

        options.where = false;

        Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.show.apply(this, arguments);
    },
    requestData: function() {
        if (this.cache)
        {
            this.processFeed();
        }
        else
        {
            Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.requestData.apply(this, arguments);
        }
    },
    processFeed: function(feed) {
        // assume order is preserved
        if (feed)
        {
            this.createCacheFrom(feed);
        }

        var use = this.cache;

        if (use && this.active && this.active['Area']) use = use[this.active['Area']];
        if (use && this.active && this.active['Category']) use = use[this.active['Category']];

        feed = this.buildFeedFrom(use);

        Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.processFeed.apply(this, [feed]);
    },
    createCacheFrom: function(feed) {
        this.cache = {};

        for (var i = 0; i < feed['$resources'].length; i++)
        {
            var entry = feed['$resources'][i],
                area = this.cache[entry['Area']] || (this.cache[entry['Area']] = {}),
                category = area[entry['Category']] || (area[entry['Category']] = {});

            category[entry['Issue']] = true
        }
    },
    buildFeedFrom: function(segment) {
        var list = [];

        for (var n in segment)
        {
            list.push({
                '$key': n,
                '$descriptor': n
            });
        }

        return {'$resources': list};
    },
    hasMoreData: function() {
        return false; // todo: implement paging?
    },
    refreshRequiredFor: function(options) {
        return true; // todo: implement refresh detection?
    },
    formatSearchQuery: function(query) {

    }
});<commit_msg>changed the base page size for the area/category/issue lookup view.<commit_after>/// <reference path="../../../../argos-sdk/libraries/ext/ext-core-debug.js"/>
/// <reference path="../../../../argos-sdk/libraries/sdata/sdata-client-debug"/>
/// <reference path="../../../../argos-sdk/libraries/Simplate.js"/>
/// <reference path="../../../../argos-sdk/src/View.js"/>
/// <reference path="../../../../argos-sdk/src/List.js"/>

Ext.namespace("Mobile.SalesLogix");

Mobile.SalesLogix.AreaCategoryIssueLookup = Ext.extend(Sage.Platform.Mobile.List, {
    //Templates
    contentTemplate: new Simplate([
        '
{%: $.$descriptor %}
'
    ]),

    //Localization
    titleText: 'Accounts',
    resourceKind: 'areaCategoryIssues',

    //View Properties
    pageSize: 200,
    expose: false,
    hideSearch: true,
    id: 'areacategoryissue_lookup',
    queryOrderBy: 'Area,Category,Issue',
    querySelect: [
        'Area',
        'Category',
        'Issue'
    ],
    show: function(options) {
        this.active = options.where;

        options.where = false;

        Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.show.apply(this, arguments);
    },
    requestData: function() {
        if (this.cache)
        {
            this.processFeed();
        }
        else
        {
            Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.requestData.apply(this, arguments);
        }
    },
    processFeed: function(feed) {
        // assume order is preserved
        if (feed)
        {
            this.createCacheFrom(feed);
        }

        var use = this.cache;

        if (use && this.active && this.active['Area']) use = use[this.active['Area']];
        if (use && this.active && this.active['Category']) use = use[this.active['Category']];

        feed = this.buildFeedFrom(use);

        Mobile.SalesLogix.AreaCategoryIssueLookup.superclass.processFeed.apply(this, [feed]);
    },
    createCacheFrom: function(feed) {
        this.cache = {};

        for (var i = 0; i < feed['$resources'].length; i++)
        {
            var entry = feed['$resources'][i],
                area = this.cache[entry['Area']] || (this.cache[entry['Area']] = {}),
                category = area[entry['Category']] || (area[entry['Category']] = {});

            category[entry['Issue']] = true
        }
    },
    buildFeedFrom: function(segment) {
        var list = [];

        for (var n in segment)
        {
            list.push({
                '$key': n,
                '$descriptor': n
            });
        }

        return {'$resources': list};
    },
    hasMoreData: function() {
        return false; // todo: implement paging?
    },
    refreshRequiredFor: function(options) {
        return true; // todo: implement refresh detection?
    },
    formatSearchQuery: function(query) {

    }
});<|endoftext|>


<commit_before>import ModalView from 'eoxc/src/core/views/ModalView';
import OpenLayersMapView from 'eoxc/src/contrib/OpenLayers/OpenLayersMapView';
import RecordDetailsView from 'eoxc/src/search/views/RecordDetailsView';

import FiltersModel from 'eoxc/src/core/models/FiltersModel';
import HighlightModel from 'eoxc/src/core/models/HighlightModel';
import MapModel from 'eoxc/src/core/models/MapModel';

import template from './RecordsDetailsModalView.hbs';

const RecordsDetailsModalView = ModalView.extend({
  template,
  templateHelpers() {
    return {
      title: '',
      hasNext: this.currentRecordIndex < this.records.length - 1,
      hasPrev: this.currentRecordIndex > 0,
      hasMore: this.records.length > 1,
    };
  },

  events: {
    'click .records-next': 'onRecordsNextClicked',
    'click .records-prev': 'onRecordsPrevClicked',
    'shown.bs.modal': 'onModalShown',
    'change .is-selected': 'onDownloadSelectionChange',
  },

  initialize(options) {
    this.records = options.records;
    this.currentRecordIndex = 0;

    this.baseLayersCollection = options.baseLayersCollection;
    this.overlayLayersCollection = options.overlayLayersCollection;
    this.layersCollection = options.layersCollection;
    this.highlightFillColor = options.highlightFillColor;
    this.highlightStrokeColor = options.highlightStrokeColor;

    this.filterFillColor = options.filterFillColor;
    this.filterStrokeColor = options.filterStrokeColor;
    this.filterOutsideColor = options.filterOutsideColor;

    this.mapModel = new MapModel({ center: [0, 0], zoom: 5 });
    this.highlightModel = new HighlightModel();
    this.filtersModel = new FiltersModel();
  },

  onModalShown() {
    this.updateRecord(...this.records[this.currentRecordIndex]);
  },

  updateRecord(recordModel, searchModel) {
    const time = recordModel.get('properties').time;
    const layerModel = searchModel.get('layerModel');
    this.mapModel.set('time', time);
    this.mapView = new OpenLayersMapView({
      mapModel: this.mapModel,
      filtersModel: this.filtersModel,
      highlightModel: this.highlightModel,
      baseLayersCollection: this.baseLayersCollection,
      overlayLayersCollection: this.overlayLayersCollection,
      layersCollection: this.layersCollection,
      highlightFillColor: this.highlightFillColor,
      highlightStrokeColor: this.highlightStrokeColor,
      filterFillColor: this.filterFillColor,
      filterStrokeColor: this.filterStrokeColor,
      filterOutsideColor: this.filterOutsideColor,
      staticHighlight: true,
    });
    const detailsView = new RecordDetailsView({
      model: recordModel,
      mapModel: this.mapModel,
      mapView: this.mapView,
    });
    this.showChildView('content', detailsView);

    this.filtersModel.set('area', recordModel.attributes.geometry);

    this.mapModel.show(recordModel.attributes);
    this.highlightModel.highlight(recordModel.attributes);

    this.$('.modal-title').text(`${layerModel.get('displayName')} - ${time[0].toISOString()}`);
    this.$('.records-prev').toggleClass('disabled', !(this.currentRecordIndex > 0));
    this.$('.records-next').toggleClass('disabled', !(this.currentRecordIndex < this.records.length - 1));
    this.$('.current-record').text(this.currentRecordIndex + 1);
    this.$('.record-count').text(this.records.length);

    const downloadSelection = searchModel.get('downloadSelection');
    const isSelectedForDownload = downloadSelection.findIndex(model => (
      model.get('id') === recordModel.get('id')
    )) !== -1;
    this.$('.is-selected').prop('checked', isSelectedForDownload);
  },

  onRecordsPrevClicked() {
    if (this.currentRecordIndex > 0) {
      this.currentRecordIndex -= 1;
      this.updateRecord(...this.records[this.currentRecordIndex]);
    }
  },

  onRecordsNextClicked() {
    if (this.currentRecordIndex < this.records.length - 1) {
      this.currentRecordIndex += 1;
      this.updateRecord(...this.records[this.currentRecordIndex]);
    }
  },

  onDownloadSelectionChange() {
    const [recordModel, searchModel] = this.records[this.currentRecordIndex];
    const downloadSelection = searchModel.get('downloadSelection');
    if (this.$('.is-selected').is(':checked')) {
      downloadSelection.add(recordModel);
    } else {
      downloadSelection.remove(recordModel);
    }
  },
});

export default RecordsDetailsModalView;
<commit_msg>Only enable currently inspected layer in record details modal.<commit_after>import ModalView from 'eoxc/src/core/views/ModalView';
import OpenLayersMapView from 'eoxc/src/contrib/OpenLayers/OpenLayersMapView';
import RecordDetailsView from 'eoxc/src/search/views/RecordDetailsView';

import FiltersModel from 'eoxc/src/core/models/FiltersModel';
import HighlightModel from 'eoxc/src/core/models/HighlightModel';
import MapModel from 'eoxc/src/core/models/MapModel';
import LayersCollection from 'eoxc/src/core/models/LayersCollection';

import template from './RecordsDetailsModalView.hbs';

const RecordsDetailsModalView = ModalView.extend({
  template,
  templateHelpers() {
    return {
      title: '',
      hasNext: this.currentRecordIndex < this.records.length - 1,
      hasPrev: this.currentRecordIndex > 0,
      hasMore: this.records.length > 1,
    };
  },

  events: {
    'click .records-next': 'onRecordsNextClicked',
    'click .records-prev': 'onRecordsPrevClicked',
    'shown.bs.modal': 'onModalShown',
    'change .is-selected': 'onDownloadSelectionChange',
  },

  initialize(options) {
    this.records = options.records;
    this.currentRecordIndex = 0;

    this.baseLayersCollection = options.baseLayersCollection;
    this.overlayLayersCollection = options.overlayLayersCollection;
    this.layersCollection = options.layersCollection;
    this.highlightFillColor = options.highlightFillColor;
    this.highlightStrokeColor = options.highlightStrokeColor;

    this.filterFillColor = options.filterFillColor;
    this.filterStrokeColor = options.filterStrokeColor;
    this.filterOutsideColor = options.filterOutsideColor;

    this.mapModel = new MapModel({ center: [0, 0], zoom: 5 });
    this.highlightModel = new HighlightModel();
    this.filtersModel = new FiltersModel();
  },

  onModalShown() {
    this.updateRecord(...this.records[this.currentRecordIndex]);
  },

  updateRecord(recordModel, searchModel) {
    const time = recordModel.get('properties').time;
    const layerModel = searchModel.get('layerModel');
    this.mapModel.set('time', time);
    this.mapView = new OpenLayersMapView({
      mapModel: this.mapModel,
      filtersModel: this.filtersModel,
      highlightModel: this.highlightModel,
      baseLayersCollection: this.baseLayersCollection,
      overlayLayersCollection: this.overlayLayersCollection,
      layersCollection: new LayersCollection([layerModel]),
      highlightFillColor: this.highlightFillColor,
      highlightStrokeColor: this.highlightStrokeColor,
      filterFillColor: this.filterFillColor,
      filterStrokeColor: this.filterStrokeColor,
      filterOutsideColor: this.filterOutsideColor,
      staticHighlight: true,
    });
    const detailsView = new RecordDetailsView({
      model: recordModel,
      mapModel: this.mapModel,
      mapView: this.mapView,
    });
    this.showChildView('content', detailsView);

    this.filtersModel.set('area', recordModel.attributes.geometry);

    this.mapModel.show(recordModel.attributes);
    this.highlightModel.highlight(recordModel.attributes);

    this.$('.modal-title').text(`${layerModel.get('displayName')} - ${time[0].toISOString()}`);
    this.$('.records-prev').toggleClass('disabled', !(this.currentRecordIndex > 0));
    this.$('.records-next').toggleClass('disabled', !(this.currentRecordIndex < this.records.length - 1));
    this.$('.current-record').text(this.currentRecordIndex + 1);
    this.$('.record-count').text(this.records.length);

    const downloadSelection = searchModel.get('downloadSelection');
    const isSelectedForDownload = downloadSelection.findIndex(model => (
      model.get('id') === recordModel.get('id')
    )) !== -1;
    this.$('.is-selected').prop('checked', isSelectedForDownload);
  },

  onRecordsPrevClicked() {
    if (this.currentRecordIndex > 0) {
      this.currentRecordIndex -= 1;
      this.updateRecord(...this.records[this.currentRecordIndex]);
    }
  },

  onRecordsNextClicked() {
    if (this.currentRecordIndex < this.records.length - 1) {
      this.currentRecordIndex += 1;
      this.updateRecord(...this.records[this.currentRecordIndex]);
    }
  },

  onDownloadSelectionChange() {
    const [recordModel, searchModel] = this.records[this.currentRecordIndex];
    const downloadSelection = searchModel.get('downloadSelection');
    if (this.$('.is-selected').is(':checked')) {
      downloadSelection.add(recordModel);
    } else {
      downloadSelection.remove(recordModel);
    }
  },
});

export default RecordsDetailsModalView;
<|endoftext|>


<commit_before>import {noUndefinedGet} from "util";
import {Float32Array, Float64Array} from "platform/platform";
import {AUDIO_VISUALIZER_READY_EVENT_NAME} from "visualization/AudioVisualizerBackend";
import WorkerFrontend from "WorkerFrontend";

const ALPHA = 0.1;

export default class AudioVisualizer extends WorkerFrontend {
    constructor(opts, deps) {
        super(AUDIO_VISUALIZER_READY_EVENT_NAME, deps.workerWrapper);
        opts = noUndefinedGet(opts);
        this.page = deps.page;
        this.visualizerCanvas = null;

        this.maxFrequency = opts.maxFrequency;
        this.minFrequency = opts.minFrequency;
        this.bufferSize = opts.bufferSize;
        this.baseSmoothingConstant = opts.baseSmoothingConstant;
        this.targetFps = opts.targetFps;

        this._receivedFrame = this._receivedFrame.bind(this);

        this._frameSkip = 1;
        this._sourceNode = null;
        this._channelData = [new Float32Array(this.bufferSize), new Float32Array(this.bufferSize)];
        this._bins = null;
        this._paused = false;
        this._resolveGetBinsPromise = null;
        this._awaitingBackendResponse = false;
        this._frameHandle = -1;
        this._numericFrameId = 0;
        this._binsUpdatedForFrameId = -1;
        this._lastRafArg = -1;
        this._actualFps = 0;
        this._workerFps = 0;
        this._init();
    }

    get actualFps() {
        return Math.ceil(this._actualFps);
    }

    get workerFps() {
        return Math.floor(this._workerFps);
    }

    _adjustFrameRate() {
        const {targetFps, actualFps} = this;

        if (actualFps > 1.1 * targetFps && this._frameSkip < 8) {
            this._frameSkip <<= 2;
        } else if (actualFps < targetFps * 0.9 && this._frameSkip > 1) {
            this._frameSkip >>= 2;
        }
    }

    _getBins(frameDescriptor) {
        this._awaitingBackendResponse = true;
        return new Promise((resolve) => {
            this._resolveGetBinsPromise = resolve;
            const channelData = this._channelData.map(v => v.buffer);
            const bins = this._bins.buffer;
            const transferList = channelData.concat(bins);
            this.postMessage({
                action: `getBins`,
                args: {
                    frameDescriptor,
                    channelData,
                    bins,
                    binCount: this.binCount()
                }
            }, transferList);
        });
    }

    receiveMessage(event) {
        try {
            const {channelData, bins} = event.data;
            for (let i = 0; i < channelData.length; ++i) {
                this._channelData[i] = new Float32Array(channelData[i]);
            }
            this._bins = new Float64Array(bins);
            this._resolveGetBinsPromise();
            this._resolveGetBinsPromise = null;
        } finally {
            this._awaitingBackendResponse = false;
        }
    }

    _shouldSkipFrame() {
        return (this._numericFrameId & (this._frameSkip - 1)) !== 0;
    }

    _requestAnimationFrame() {
        this._frameHandle = this.page.requestAnimationFrame(this._receivedFrame);
        this._numericFrameId++;
    }

    async _requestBinsAndAnimationFrame() {
        this._requestAnimationFrame();

        if (this.visualizerCanvas.needsToDraw() &&
            !this._paused &&
            !this._awaitingBackendResponse) {
            const frameId = this._numericFrameId;
            const {actualFps} = this;
            const secondsInFuture = actualFps > 0 ? (1000 / actualFps / 1000) : 0;
            const frameDescriptor = this._sourceNode.getUpcomingSamples(this._channelData,
                                                                        secondsInFuture);

            if (!frameDescriptor.channelDataFilled) {
                return;
            }

            const binCount = this.binCount();
            if (!this._bins || this._bins.length !== binCount) {
                this._bins = new Float64Array(binCount);
            }

            const now = performance.now();
            await this._getBins(frameDescriptor);
            this._binsUpdatedForFrameId = frameId;
            const fps = 1000 / (performance.now() - now);
            this._workerFps = (this._workerFps * (1 - ALPHA)) + fps * ALPHA;
        }
    }

    _receivedFrame(now) {
        if (!this._shouldSkipFrame()) {
            const then = this._lastRafArg;
            this._lastRafArg = now;
            if (then !== -1) {
                const elapsed = now - then;
                const fps = 1000 / elapsed;
                if (fps > 1) {
                    this._actualFps = (this._actualFps * (1 - ALPHA)) + fps * ALPHA;
                    this._adjustFrameRate();
                }
            }
            const frameId = this._numericFrameId;

            if (this._binsUpdatedForFrameId === frameId) {
                this.visualizerCanvas.drawBins(now, this._bins);
            } else if (this.visualizerCanvas.needsToDraw() && this._paused) {
                this.visualizerCanvas.drawIdleBins(now);
            } else {
                // TODO: This happens when fps is too high yet worker is too
                // Slow.
            }
            this._requestBinsAndAnimationFrame();
        } else {
            this._requestAnimationFrame();
        }
    }

    setCanvas(visualizerCanvas) {
        this.visualizerCanvas = visualizerCanvas;
    }

    async _init() {
        await this.ready();
        const {maxFrequency,
                minFrequency,
                bufferSize,
                baseSmoothingConstant} = this;
        this.postMessage({
            action: `configure`,
            args: {
                maxFrequency,
                minFrequency,
                bufferSize,
                baseSmoothingConstant
            }
        });
    }

    connectSourceNode(sourceNode) {
        this._lastRafArg = -1;
        this.page.cancelAnimationFrame(this._frameHandle);
        this._sourceNode = sourceNode;
        this._requestBinsAndAnimationFrame();
    }

    disconnectSourceNode(sourceNode) {
        if (this._sourceNode === sourceNode) {
            this._lastRafArg = -1;
            this.page.cancelAnimationFrame(this._frameHandle);
            this._sourceNode = null;
        }
    }

    binCount() {
        return this.visualizerCanvas.getNumBins();
    }

    pause() {
        if (this._paused) return;
        this._paused = true;
    }

    resume() {
        if (!this._paused) return;
        this._paused = false;
    }
}
<commit_msg>dont reduce fps too much<commit_after>import {noUndefinedGet} from "util";
import {Float32Array, Float64Array} from "platform/platform";
import {AUDIO_VISUALIZER_READY_EVENT_NAME} from "visualization/AudioVisualizerBackend";
import WorkerFrontend from "WorkerFrontend";

const ALPHA = 0.1;

export default class AudioVisualizer extends WorkerFrontend {
    constructor(opts, deps) {
        super(AUDIO_VISUALIZER_READY_EVENT_NAME, deps.workerWrapper);
        opts = noUndefinedGet(opts);
        this.page = deps.page;
        this.visualizerCanvas = null;

        this.maxFrequency = opts.maxFrequency;
        this.minFrequency = opts.minFrequency;
        this.bufferSize = opts.bufferSize;
        this.baseSmoothingConstant = opts.baseSmoothingConstant;
        this.targetFps = opts.targetFps;

        this._receivedFrame = this._receivedFrame.bind(this);

        this._frameSkip = 1;
        this._sourceNode = null;
        this._channelData = [new Float32Array(this.bufferSize), new Float32Array(this.bufferSize)];
        this._bins = null;
        this._paused = false;
        this._resolveGetBinsPromise = null;
        this._awaitingBackendResponse = false;
        this._frameHandle = -1;
        this._numericFrameId = 0;
        this._nextBinUpdateRequestId = 0;
        this._binsUpdatedForRequestId = -1;
        this._lastRafArg = -1;
        this._actualFps = 0;
        this._workerFps = 0;
        this._init();
    }

    get actualFps() {
        return Math.ceil(this._actualFps);
    }

    get workerFps() {
        return Math.floor(this._workerFps);
    }

    _adjustFrameRate() {
        const {targetFps, actualFps} = this;

        if (actualFps > targetFps * 1.1) {
            if (actualFps / 2 > 0.9 * targetFps && this._frameSkip < 8) {
                this._frameSkip <<= 1;
            }
        } else if (actualFps < targetFps * 0.9 && this._frameSkip > 1) {
            this._frameSkip >>= 1;
        }
    }

    _getBins(frameDescriptor) {
        this._awaitingBackendResponse = true;
        return new Promise((resolve) => {
            this._resolveGetBinsPromise = resolve;
            const channelData = this._channelData.map(v => v.buffer);
            const bins = this._bins.buffer;
            const transferList = channelData.concat(bins);
            this.postMessage({
                action: `getBins`,
                args: {
                    frameDescriptor,
                    channelData,
                    bins,
                    binCount: this.binCount()
                }
            }, transferList);
        });
    }

    receiveMessage(event) {
        try {
            const {channelData, bins} = event.data;
            for (let i = 0; i < channelData.length; ++i) {
                this._channelData[i] = new Float32Array(channelData[i]);
            }
            this._bins = new Float64Array(bins);
            this._resolveGetBinsPromise();
            this._resolveGetBinsPromise = null;
        } finally {
            this._awaitingBackendResponse = false;
        }
    }

    _shouldSkipFrame() {
        return (this._numericFrameId & (this._frameSkip - 1)) !== 0;
    }

    _requestAnimationFrame() {
        this._frameHandle = this.page.requestAnimationFrame(this._receivedFrame);
        this._numericFrameId++;
    }

    async _requestBinsAndAnimationFrame() {
        this._requestAnimationFrame();

        const requestId = ++this._nextBinUpdateRequestId;
        if (this.visualizerCanvas.needsToDraw() &&
            !this._paused &&
            !this._awaitingBackendResponse) {
            const {actualFps} = this;
            const secondsInFuture = actualFps > 0 ? (1000 / actualFps / 1000) : 0;
            const frameDescriptor = this._sourceNode.getUpcomingSamples(this._channelData,
                                                                        secondsInFuture);

            if (!frameDescriptor.channelDataFilled) {
                return;
            }

            const binCount = this.binCount();
            if (!this._bins || this._bins.length !== binCount) {
                this._bins = new Float64Array(binCount);
            }

            const now = performance.now();
            await this._getBins(frameDescriptor);
            this._binsUpdatedForRequestId = requestId;
            const fps = 1000 / (performance.now() - now);
            this._workerFps = (this._workerFps * (1 - ALPHA)) + fps * ALPHA;
        }
    }

    _receivedFrame(now) {
        if (!this._shouldSkipFrame()) {
            const then = this._lastRafArg;
            this._lastRafArg = now;
            if (then !== -1) {
                const elapsed = now - then;
                const fps = 1000 / elapsed;
                if (fps > 1) {
                    this._actualFps = (this._actualFps * (1 - ALPHA)) + fps * ALPHA;
                    this._adjustFrameRate();
                }
            }

            if (this._binsUpdatedForRequestId === this._nextBinUpdateRequestId) {
                this.visualizerCanvas.drawBins(now, this._bins);
            } else if (this.visualizerCanvas.needsToDraw() && this._paused) {
                this.visualizerCanvas.drawIdleBins(now);
            } else {
                // TODO: This happens when fps is too high yet worker is too
                // Slow.
            }
            this._requestBinsAndAnimationFrame();
        } else {
            this._requestAnimationFrame();
        }
    }

    setCanvas(visualizerCanvas) {
        this.visualizerCanvas = visualizerCanvas;
    }

    async _init() {
        await this.ready();
        const {maxFrequency,
                minFrequency,
                bufferSize,
                baseSmoothingConstant} = this;
        this.postMessage({
            action: `configure`,
            args: {
                maxFrequency,
                minFrequency,
                bufferSize,
                baseSmoothingConstant
            }
        });
    }

    connectSourceNode(sourceNode) {
        this._lastRafArg = -1;
        this.page.cancelAnimationFrame(this._frameHandle);
        this._sourceNode = sourceNode;
        this._requestBinsAndAnimationFrame();
    }

    disconnectSourceNode(sourceNode) {
        if (this._sourceNode === sourceNode) {
            this._lastRafArg = -1;
            this.page.cancelAnimationFrame(this._frameHandle);
            this._sourceNode = null;
        }
    }

    binCount() {
        return this.visualizerCanvas.getNumBins();
    }

    pause() {
        if (this._paused) return;
        this._paused = true;
    }

    resume() {
        if (!this._paused) return;
        this._paused = false;
    }
}
<|endoftext|>


<commit_before>"use strict";

class PoemData {
  static get DATA_FILE() {
    return "/data.json";
  }

  static load() {
    var self = this;
    return new Promise(function(resolve, reject) {
      fetch(self.DATA_FILE).then(
        data => data.json()
      ).then(
        json => resolve(json)
      ).catch(
        error => reject("Error fetching " + self.DATA_FILE)
      );
    });
  }
}

export { PoemData };
<commit_msg>Automatically prepend pathname to data URL.<commit_after>"use strict";

class PoemData {
  static get DATA_FILE() {
    return (window.location.pathname || "/") + "data.json";
  }

  static load() {
    var self = this;
    return new Promise(function(resolve, reject) {
      fetch(self.DATA_FILE).then(
        data => data.json()
      ).then(
        json => resolve(json)
      ).catch(
        error => reject("Error fetching " + self.DATA_FILE)
      );
    });
  }
}

export { PoemData };
<|endoftext|>


<commit_before>"use strict";
require('player', 'data.playersdefinition');
var Colorifier;

/**
 * Image processor that can generate images of player's buildings and units
 * from universal images by shifting specified colors to the player's own
 * color while preserving gradients.
 */
Colorifier = function () {
    var processData, decodeColor, getDistance, getTargetColor;

    Player.createGenericPlayers();

    /**
     * Generates colorized images for all defined players.
     *
     * @param {Image} image Source image.
     * @param {String} source Color that will be changed.
     * @param {Number} maxDistance The maximum distance from the source color
     *        of color that will be repainted.
     * @return {Array} Array of canvas elements containing recolored images.
     */
    this.colorifyForPlayers = function (image, source, maxDistance) {
        var images, player, i;
        images = [];
        for (i = 0; player = Player.getPlayer(i++);) {
            images.push(this.colorify(image, source, player.color,
                    maxDistance));
        }
        return images;
    };

    /**
     * Generates a image colorized by the target color while preserving the
     * gradients.
     *
     * @param {Image} image Source image.
     * @param {String} source Color that will be repainted.
     * @param {String} target The color which will be used to repaint the
     *        image.
     * @param {Number} maxDistance The maximum distance from the source color
     *        of color that will be repainted.
     * @return {HTMLCanvasElement} Canvas element containing the repainted
     *         image.
     */
    this.colorify = function (image, source, target, maxDistance) {
        var canvas, context, data;
        canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        context = canvas.getContext('2d');
        context.drawImage(image, 0, 0);
        data = context.getImageData(0, 0, image.width, image.height);
        processData(data, decodeColor(source), decodeColor(target),
                maxDistance);
        context.putImageData(data, 0, 0);
        return canvas;
    };

    processData = function (pixelData, source, target, maxDistance) {
        var data, i, r, g, b, length, distance, newColor;
        data = pixelData.data;
        length = data.length;
        for (i = 0; i < length; i += 4) {
            r = data[i];
            g = data[i + 1];
            b = data[i + 2];
            distance = getDistance(r, g, b, source.r, source.g, source.b);
            if (distance <= maxDistance) {
                newColor = getTargetColor(source, {
                    r: r,
                    g: g,
                    b: b
                }, target);
                data[i] = newColor.r;
                data[i + 1] = newColor.g;
                data[i + 2] = newColor.b;
            }
        }
    };

    getTargetColor = function (source, pixel, target) {
        var rd, gd, bd;
        rd = target.r - source.r;
        gd = target.g - source.g;
        bd = target.b - source.b;
        return {
            r: Math.max(0, Math.min(255, pixel.r + rd)),
            g: Math.max(0, Math.min(255, pixel.g + gd)),
            b: Math.max(0, Math.min(255, pixel.b + bd))
        };
    };

    getDistance = function (r1, g1, b1, r2, g2, b2) {
        return Math.sqrt(Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) +
                Math.pow(b1 - b2, 2));
    };

    decodeColor = function (color) {
        color = color.length == 7 ? color.substring(1) : color;
        return {
            r: parseInt(color.substring(0, 2), 16),
            g: parseInt(color.substring(2, 4), 16),
            b: parseInt(color.substring(4, 6), 16)
        };
    };
};
<commit_msg>colorifier bugfig (requesting too big image data)<commit_after>"use strict";
require('player', 'data.playersdefinition');
var Colorifier;

/**
 * Image processor that can generate images of player's buildings and units
 * from universal images by shifting specified colors to the player's own
 * color while preserving gradients.
 */
Colorifier = function () {
    var processData, decodeColor, getDistance, getTargetColor;

    Player.createGenericPlayers();

    /**
     * Generates colorized images for all defined players.
     *
     * @param {Image} image Source image.
     * @param {String} source Color that will be changed.
     * @param {Number} maxDistance The maximum distance from the source color
     *        of color that will be repainted.
     * @return {Array} Array of canvas elements containing recolored images.
     */
    this.colorifyForPlayers = function (image, source, maxDistance) {
        var images, player, i;
        images = [];
        for (i = 0; player = Player.getPlayer(i++);) {
            images.push(this.colorify(image, source, player.color,
                    maxDistance));
        }
        return images;
    };

    /**
     * Generates a image colorized by the target color while preserving the
     * gradients.
     *
     * @param {Image} image Source image.
     * @param {String} source Color that will be repainted.
     * @param {String} target The color which will be used to repaint the
     *        image.
     * @param {Number} maxDistance The maximum distance from the source color
     *        of color that will be repainted.
     * @return {HTMLCanvasElement} Canvas element containing the repainted
     *         image.
     */
    this.colorify = function (image, source, target, maxDistance) {
        var canvas, context, data;
        canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        context = canvas.getContext('2d');
        context.drawImage(image, 0, 0);
        data = context.getImageData(0, 0, image.width - 1, image.height - 1);
        processData(data, decodeColor(source), decodeColor(target),
                maxDistance);
        context.putImageData(data, 0, 0);
        return canvas;
    };

    processData = function (pixelData, source, target, maxDistance) {
        var data, i, r, g, b, length, distance, newColor;
        data = pixelData.data;
        length = data.length;
        for (i = 0; i < length; i += 4) {
            r = data[i];
            g = data[i + 1];
            b = data[i + 2];
            distance = getDistance(r, g, b, source.r, source.g, source.b);
            if (distance <= maxDistance) {
                newColor = getTargetColor(source, {
                    r: r,
                    g: g,
                    b: b
                }, target);
                data[i] = newColor.r;
                data[i + 1] = newColor.g;
                data[i + 2] = newColor.b;
            }
        }
    };

    getTargetColor = function (source, pixel, target) {
        var rd, gd, bd;
        rd = target.r - source.r;
        gd = target.g - source.g;
        bd = target.b - source.b;
        return {
            r: Math.max(0, Math.min(255, pixel.r + rd)),
            g: Math.max(0, Math.min(255, pixel.g + gd)),
            b: Math.max(0, Math.min(255, pixel.b + bd))
        };
    };

    getDistance = function (r1, g1, b1, r2, g2, b2) {
        return Math.sqrt(Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) +
                Math.pow(b1 - b2, 2));
    };

    decodeColor = function (color) {
        color = color.length == 7 ? color.substring(1) : color;
        return {
            r: parseInt(color.substring(0, 2), 16),
            g: parseInt(color.substring(2, 4), 16),
            b: parseInt(color.substring(4, 6), 16)
        };
    };
};
<|endoftext|>


<commit_before>;(function(context, MM) {

    var easey_handlers = {};

    easey_handlers.TouchHandler = function() {
        var handler = {},
            map,
            panner,
            maxTapTime = 250,
            maxTapDistance = 30,
            maxDoubleTapDelay = 350,
            locations = {},
            taps = [],
            wasPinching = false,
            lastPinchCenter = null,
            p0 = new MM.Point(0, 0),
            p1 = new MM.Point(0, 0);

        function focusMap(e) {
            map.parent.focus();
        }

        function clearLocations() {
            for (var loc in locations) {
                if (locations.hasOwnProperty(loc)) {
                    delete locations[loc];
                }
            }
        }

        function updateTouches (e) {
            for (var i = 0; i < e.touches.length; i += 1) {
                var t = e.touches[i];
                if (t.identifier in locations) {
                    var l = locations[t.identifier];
                    l.x = t.clientX;
                    l.y = t.clientY;
                    l.scale = e.scale;
                } else {
                    locations[t.identifier] = {
                        scale: e.scale,
                        startPos: { x: t.clientX, y: t.screenY },
                        startZoom: map.zoom(),
                        x: t.clientX,
                        y: t.clientY,
                        time: new Date().getTime()
                    };
                }
            }
        }

        function touchStartMachine(e) {
            MM.addEvent(e.touches[0].target, 'touchmove',
                touchMoveMachine);
            MM.addEvent(e.touches[0].target, 'touchend',
                touchEndMachine);
            if (e.touches[1]) {
                MM.addEvent(e.touches[1].target, 'touchmove',
                    touchMoveMachine);
                MM.addEvent(e.touches[1].target, 'touchend',
                    touchEndMachine);
            }
            updateTouches(e);
            panner.down(e.touches[0]);
            return MM.cancelEvent(e);
        }

        function touchMoveMachine(e) {
            switch (e.touches.length) {
                case 1:
                    panner.move(e.touches[0]);
                    break;
                case 2:
                    onPinching(e);
                    break;
            }
            updateTouches(e);
            return MM.cancelEvent(e);
        }

        // Handle a tap event - mainly watch for a doubleTap
        function onTap(tap) {
            if (taps.length &&
                (tap.time - taps[0].time) < maxDoubleTapDelay) {
                onDoubleTap(tap);
                taps = [];
                return;
            }
            taps = [tap];
        }

        // Handle a double tap by zooming in a single zoom level to a
        // round zoom.
        function onDoubleTap(tap) {
            // zoom in to a round number
            easey().map(map)
            .to(map.pointCoordinate(tap).zoomTo(map.getZoom() + 1))
            .path('about').run(200, function() {
                map.dispatchCallback('zoomed');
                clearLocations();
            });
        }

        function isTouchable() {
            var el = document.createElement('div');
            el.setAttribute('ongesturestart', 'return;');
            return (typeof el.ongesturestart === 'function');
        }

        function onPinching(e) {
            // use the first two touches and their previous positions
            var t0 = e.touches[0],
                t1 = e.touches[1];
            p0.x = t0.clientX;
            p0.y = t0.clientY;
            p1.x = t1.clientX;
            p1.y = t1.clientY;
            l0 = locations[t0.identifier],
            l1 = locations[t1.identifier];

            // mark these touches so they aren't used as taps/holds
            l0.wasPinch = true;
            l1.wasPinch = true;

            // scale about the center of these touches
            var center = MM.Point.interpolate(p0, p1, 0.5);

            map.zoomByAbout(
                Math.log(e.scale) / Math.LN2 - Math.log(l0.scale) / Math.LN2,
                center);

            // pan from the previous center of these touches
            prevX = l0.x + (l1.x - l0.x) * 0.5;
            prevY = l0.y + (l1.y - l0.y) * 0.5;
            map.panBy(center.x - prevX,
                      center.y - prevY);
            wasPinching = true;
            lastPinchCenter = center;
        }

        // When a pinch event ends, round the zoom of the map.
        function onPinched(touch) {
            var z = map.getZoom(), // current zoom
                tz = locations[touch.identifier].startZoom > z ? Math.floor(z) : Math.ceil(z);
            easey().map(map).point(lastPinchCenter).zoom(tz)
                .path('about').run(300);
            clearLocations();
            wasPinching = false;
        }

        function touchEndMachine(e) {
            MM.removeEvent(e.target, 'touchmove',
                touchMoveMachine);
            MM.removeEvent(e.target, 'touchend',
                touchEndMachine);
            var now = new Date().getTime();

            // round zoom if we're done pinching
            if (e.touches.length === 0 && wasPinching) {
                onPinched(e.changedTouches[0]);
            }

            panner.up();

            // Look at each changed touch in turn.
            for (var i = 0; i < e.changedTouches.length; i += 1) {
                var t = e.changedTouches[i],
                loc = locations[t.identifier];
                // if we didn't see this one (bug?)
                // or if it was consumed by pinching already
                // just skip to the next one
                if (!loc || loc.wasPinch) {
                    continue;
                }

                // we now know we have an event object and a
                // matching touch that's just ended. Let's see
                // what kind of event it is based on how long it
                // lasted and how far it moved.
                var pos = { x: t.clientX, y: t.clientY },
                time = now - loc.time,
                travel = MM.Point.distance(pos, loc.startPos);
                if (travel > maxTapDistance) {
                    // we will to assume that the drag has been handled separately
                } else if (time > maxTapTime) {
                    // close in space, but not in time: a hold
                    pos.end = now;
                    pos.duration = time;
                    onHold(pos);
                } else {
                    // close in both time and space: a tap
                    pos.time = now;
                    onTap(pos);
                }
            }

            // Weird, sometimes an end event doesn't get thrown
            // for a touch that nevertheless has disappeared.
            // Still, this will eventually catch those ids:

            var validTouchIds = {};
            for (var j = 0; j < e.touches.length; j++) {
                validTouchIds[e.touches[j].identifier] = true;
            }
            for (var id in locations) {
                if (!(id in validTouchIds)) {
                    delete validTouchIds[id];
                }
            }

            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            // Fail early if this isn't a touch device.
            // TODO: move to add fn
            if (!isTouchable()) return false;

            MM.addEvent(map.parent, 'touchstart',
                touchStartMachine);
            panner = panning(0.10);
        };

        handler.remove = function() {
            // Fail early if this isn't a touch device.
            if (!isTouchable()) return false;

            MM.removeEvent(map.parent, 'touchstart',
                touchStartMachine);
            panner.remove();
        };

        return handler;
    };

    easey_handlers.DoubleClickHandler = function() {
        var handler = {},
            map;

        function doubleClick(e) {
            // Ensure that this handler is attached once.
            // Get the point on the map that was double-clicked
            var point = MM.getMousePoint(e, map);
            z = map.getZoom() + (e.shiftKey ? -1 : 1);
            // use shift-double-click to zoom out
            easey().map(map)
                .to(map.pointCoordinate(MM.getMousePoint(e, map)).zoomTo(z))
                .path('about').run(100, function() {
                map.dispatchCallback('zoomed');
            });
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            MM.addEvent(map.parent, 'dblclick', doubleClick);
            return handler;
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'dblclick', doubleClick);
        };

        return handler;
    };

    easey_handlers.MouseWheelHandler = function() {
        var handler = {},
            map,
            _zoomDiv,
            ea = easey(),
            prevTime,
            precise = false;

        function mouseWheel(e) {
            var delta = 0;
            prevTime = prevTime || new Date().getTime();

            try {
                _zoomDiv.scrollTop = 1000;
                _zoomDiv.dispatchEvent(e);
                delta = 1000 - _zoomDiv.scrollTop;
            } catch (error) {
                delta = e.wheelDelta || (-e.detail * 5);
            }

            // limit mousewheeling to once every 200ms
            var timeSince = new Date().getTime() - prevTime;

            function dispatchZoomed() {
                map.dispatchCallback('zoomed');
            }

            if (!ea.running()) {
              var point = MM.getMousePoint(e, map),
                  z = map.getZoom();
              ea.map(map)
                .easing('easeOut')
                .to(map.pointCoordinate(MM.getMousePoint(e, map)).zoomTo(z + (delta > 0 ? 1 : -1)))
                .path('about').run(100, dispatchZoomed);
                prevTime = new Date().getTime();
            } else if (timeSince > 150){
                ea.zoom(ea.to().zoom + (delta > 0 ? 1 : -1)).from(map.coordinate).resetRun();
                prevTime = new Date().getTime();
            }

            // Cancel the event so that the page doesn't scroll
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            _zoomDiv = document.body.appendChild(document.createElement('div'));
            _zoomDiv.style.cssText = 'visibility:hidden;top:0;height:0;width:0;overflow-y:scroll';
            var innerDiv = _zoomDiv.appendChild(document.createElement('div'));
            innerDiv.style.height = '2000px';
            MM.addEvent(map.parent, 'mousewheel', mouseWheel);
            return handler;
        };

        handler.precise = function(x) {
            if (!arguments.length) return precise;
            precise = x;
            return handler;
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'mousewheel', mouseWheel);
            _zoomDiv.parentNode.removeChild(_zoomDiv);
        };

        return handler;
    };

    easey_handlers.DragHandler = function() {
        var handler = {},
            map,
            panner;

        function focusMap(e) {
            map.parent.focus();
        }

        function mouseDown(e) {
            if (e.shiftKey || e.button == 2) return;
            MM.addEvent(document, 'mousemove', mouseMove);
            MM.addEvent(document, 'mouseup', mouseUp);
            panner.down(e);
            map.parent.style.cursor = 'move';
            return MM.cancelEvent(e);
        }

        function mouseMove(e) {
            panner.move(e);
            return MM.cancelEvent(e);
        }

        function mouseUp(e) {
            MM.removeEvent(document, 'mousemove', mouseMove);
            MM.removeEvent(document, 'mouseup', mouseUp);
            panner.up();
            map.parent.style.cursor = '';
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            MM.addEvent(map.parent, 'click', focusMap);
            MM.addEvent(map.parent, 'mousedown', mouseDown);
            panner = panning();
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'click', focusMap);
            MM.removeEvent(map.parent, 'mousedown', mouseDown);
            panner.up();
            panner.remove();
        };

        return handler;
    };

    function panning(drag) {

        var p = {};
        drag = drag || 0.15;

        var speed = { x: 0, y: 0 },
            dir = { x: 0, y: 0 },
            removed = false,
            nowPoint = null,
            oldPoint = null,
            moveTime = null,
            prevMoveTime = null,
            animatedLastPoint = true,
            t,
            prevT = new Date().getTime();

        p.down = function(e) {
            nowPoint = oldPoint = MM.getMousePoint(e, map);
            moveTime = prevMoveTime = +new Date();
        };

        p.move = function(e) {
            if (nowPoint) {
                if (animatedLastPoint) {
                    oldPoint = nowPoint;
                    prevMoveTime = moveTime;
                    animatedLastPoint = false;
                }
                nowPoint = MM.getMousePoint(e, map);
                moveTime = +new Date();
            }
        };

        p.up = function() {
            if (+new Date() - prevMoveTime < 50) {
                dt = Math.max(1, moveTime - prevMoveTime);
                dir.x = nowPoint.x - oldPoint.x;
                dir.y = nowPoint.y - oldPoint.y;
                speed.x = dir.x / dt;
                speed.y = dir.y / dt;
            } else {
                speed.x = 0;
                speed.y = 0;
            }
            nowPoint = oldPoint = null;
            moveTime = null;
        };

        p.remove = function() {
            removed = true;
        };

        function animate(t) {
            var dt = Math.max(1, t - prevT);
            if (nowPoint && oldPoint) {
                if (!animatedLastPoint) {
                    dir.x = nowPoint.x - oldPoint.x;
                    dir.y = nowPoint.y - oldPoint.y;
                    map.panBy(dir.x, dir.y);
                    animatedLastPoint = true;
                }
            } else {
                // Rough time based animation accuracy
                // using a linear approximation approach
                speed.x *= Math.pow(1 - drag, dt * 60 / 1000);
                speed.y *= Math.pow(1 - drag, dt * 60 / 1000);
                if (Math.abs(speed.x) < 0.001) {
                    speed.x = 0;
                }
                if (Math.abs(speed.y) < 0.001) {
                    speed.y = 0;
                }
                if (speed.x || speed.y) {
                    map.panBy(speed.x * dt, speed.y * dt);
                }
            }
            prevT = t;
            if (!removed) MM.getFrame(animate);
        }

        MM.getFrame(animate);
        return p;
    }

    this.easey_handlers = easey_handlers;

})(this, MM);
<commit_msg>Remove call to non-existant onHold<commit_after>;(function(context, MM) {

    var easey_handlers = {};

    easey_handlers.TouchHandler = function() {
        var handler = {},
            map,
            panner,
            maxTapTime = 250,
            maxTapDistance = 30,
            maxDoubleTapDelay = 350,
            locations = {},
            taps = [],
            wasPinching = false,
            lastPinchCenter = null,
            p0 = new MM.Point(0, 0),
            p1 = new MM.Point(0, 0);

        function focusMap(e) {
            map.parent.focus();
        }

        function clearLocations() {
            for (var loc in locations) {
                if (locations.hasOwnProperty(loc)) {
                    delete locations[loc];
                }
            }
        }

        function updateTouches (e) {
            for (var i = 0; i < e.touches.length; i += 1) {
                var t = e.touches[i];
                if (t.identifier in locations) {
                    var l = locations[t.identifier];
                    l.x = t.clientX;
                    l.y = t.clientY;
                    l.scale = e.scale;
                } else {
                    locations[t.identifier] = {
                        scale: e.scale,
                        startPos: { x: t.clientX, y: t.screenY },
                        startZoom: map.zoom(),
                        x: t.clientX,
                        y: t.clientY,
                        time: new Date().getTime()
                    };
                }
            }
        }

        function touchStartMachine(e) {
            MM.addEvent(e.touches[0].target, 'touchmove',
                touchMoveMachine);
            MM.addEvent(e.touches[0].target, 'touchend',
                touchEndMachine);
            if (e.touches[1]) {
                MM.addEvent(e.touches[1].target, 'touchmove',
                    touchMoveMachine);
                MM.addEvent(e.touches[1].target, 'touchend',
                    touchEndMachine);
            }
            updateTouches(e);
            panner.down(e.touches[0]);
            return MM.cancelEvent(e);
        }

        function touchMoveMachine(e) {
            switch (e.touches.length) {
                case 1:
                    panner.move(e.touches[0]);
                    break;
                case 2:
                    onPinching(e);
                    break;
            }
            updateTouches(e);
            return MM.cancelEvent(e);
        }

        // Handle a tap event - mainly watch for a doubleTap
        function onTap(tap) {
            if (taps.length &&
                (tap.time - taps[0].time) < maxDoubleTapDelay) {
                onDoubleTap(tap);
                taps = [];
                return;
            }
            taps = [tap];
        }

        // Handle a double tap by zooming in a single zoom level to a
        // round zoom.
        function onDoubleTap(tap) {
            // zoom in to a round number
            easey().map(map)
            .to(map.pointCoordinate(tap).zoomTo(map.getZoom() + 1))
            .path('about').run(200, function() {
                map.dispatchCallback('zoomed');
                clearLocations();
            });
        }

        function isTouchable() {
            var el = document.createElement('div');
            el.setAttribute('ongesturestart', 'return;');
            return (typeof el.ongesturestart === 'function');
        }

        function onPinching(e) {
            // use the first two touches and their previous positions
            var t0 = e.touches[0],
                t1 = e.touches[1];
            p0.x = t0.clientX;
            p0.y = t0.clientY;
            p1.x = t1.clientX;
            p1.y = t1.clientY;
            l0 = locations[t0.identifier],
            l1 = locations[t1.identifier];

            // mark these touches so they aren't used as taps/holds
            l0.wasPinch = true;
            l1.wasPinch = true;

            // scale about the center of these touches
            var center = MM.Point.interpolate(p0, p1, 0.5);

            map.zoomByAbout(
                Math.log(e.scale) / Math.LN2 - Math.log(l0.scale) / Math.LN2,
                center);

            // pan from the previous center of these touches
            prevX = l0.x + (l1.x - l0.x) * 0.5;
            prevY = l0.y + (l1.y - l0.y) * 0.5;
            map.panBy(center.x - prevX,
                      center.y - prevY);
            wasPinching = true;
            lastPinchCenter = center;
        }

        // When a pinch event ends, round the zoom of the map.
        function onPinched(touch) {
            var z = map.getZoom(), // current zoom
                tz = locations[touch.identifier].startZoom > z ? Math.floor(z) : Math.ceil(z);
            easey().map(map).point(lastPinchCenter).zoom(tz)
                .path('about').run(300);
            clearLocations();
            wasPinching = false;
        }

        function touchEndMachine(e) {
            MM.removeEvent(e.target, 'touchmove',
                touchMoveMachine);
            MM.removeEvent(e.target, 'touchend',
                touchEndMachine);
            var now = new Date().getTime();

            // round zoom if we're done pinching
            if (e.touches.length === 0 && wasPinching) {
                onPinched(e.changedTouches[0]);
            }

            panner.up();

            // Look at each changed touch in turn.
            for (var i = 0; i < e.changedTouches.length; i += 1) {
                var t = e.changedTouches[i],
                loc = locations[t.identifier];
                // if we didn't see this one (bug?)
                // or if it was consumed by pinching already
                // just skip to the next one
                if (!loc || loc.wasPinch) {
                    continue;
                }

                // we now know we have an event object and a
                // matching touch that's just ended. Let's see
                // what kind of event it is based on how long it
                // lasted and how far it moved.
                var pos = { x: t.clientX, y: t.clientY },
                time = now - loc.time,
                travel = MM.Point.distance(pos, loc.startPos);
                if (travel > maxTapDistance) {
                    // we will to assume that the drag has been handled separately
                } else if (time > maxTapTime) {
                    // close in space, but not in time: a hold
                    pos.end = now;
                    pos.duration = time;
                } else {
                    // close in both time and space: a tap
                    pos.time = now;
                    onTap(pos);
                }
            }

            // Weird, sometimes an end event doesn't get thrown
            // for a touch that nevertheless has disappeared.
            // Still, this will eventually catch those ids:

            var validTouchIds = {};
            for (var j = 0; j < e.touches.length; j++) {
                validTouchIds[e.touches[j].identifier] = true;
            }
            for (var id in locations) {
                if (!(id in validTouchIds)) {
                    delete validTouchIds[id];
                }
            }

            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            // Fail early if this isn't a touch device.
            // TODO: move to add fn
            if (!isTouchable()) return false;

            MM.addEvent(map.parent, 'touchstart',
                touchStartMachine);
            panner = panning(0.10);
        };

        handler.remove = function() {
            // Fail early if this isn't a touch device.
            if (!isTouchable()) return false;

            MM.removeEvent(map.parent, 'touchstart',
                touchStartMachine);
            panner.remove();
        };

        return handler;
    };

    easey_handlers.DoubleClickHandler = function() {
        var handler = {},
            map;

        function doubleClick(e) {
            // Ensure that this handler is attached once.
            // Get the point on the map that was double-clicked
            var point = MM.getMousePoint(e, map);
            z = map.getZoom() + (e.shiftKey ? -1 : 1);
            // use shift-double-click to zoom out
            easey().map(map)
                .to(map.pointCoordinate(MM.getMousePoint(e, map)).zoomTo(z))
                .path('about').run(100, function() {
                map.dispatchCallback('zoomed');
            });
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            MM.addEvent(map.parent, 'dblclick', doubleClick);
            return handler;
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'dblclick', doubleClick);
        };

        return handler;
    };

    easey_handlers.MouseWheelHandler = function() {
        var handler = {},
            map,
            _zoomDiv,
            ea = easey(),
            prevTime,
            precise = false;

        function mouseWheel(e) {
            var delta = 0;
            prevTime = prevTime || new Date().getTime();

            try {
                _zoomDiv.scrollTop = 1000;
                _zoomDiv.dispatchEvent(e);
                delta = 1000 - _zoomDiv.scrollTop;
            } catch (error) {
                delta = e.wheelDelta || (-e.detail * 5);
            }

            // limit mousewheeling to once every 200ms
            var timeSince = new Date().getTime() - prevTime;

            function dispatchZoomed() {
                map.dispatchCallback('zoomed');
            }

            if (!ea.running()) {
              var point = MM.getMousePoint(e, map),
                  z = map.getZoom();
              ea.map(map)
                .easing('easeOut')
                .to(map.pointCoordinate(MM.getMousePoint(e, map)).zoomTo(z + (delta > 0 ? 1 : -1)))
                .path('about').run(100, dispatchZoomed);
                prevTime = new Date().getTime();
            } else if (timeSince > 150){
                ea.zoom(ea.to().zoom + (delta > 0 ? 1 : -1)).from(map.coordinate).resetRun();
                prevTime = new Date().getTime();
            }

            // Cancel the event so that the page doesn't scroll
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            _zoomDiv = document.body.appendChild(document.createElement('div'));
            _zoomDiv.style.cssText = 'visibility:hidden;top:0;height:0;width:0;overflow-y:scroll';
            var innerDiv = _zoomDiv.appendChild(document.createElement('div'));
            innerDiv.style.height = '2000px';
            MM.addEvent(map.parent, 'mousewheel', mouseWheel);
            return handler;
        };

        handler.precise = function(x) {
            if (!arguments.length) return precise;
            precise = x;
            return handler;
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'mousewheel', mouseWheel);
            _zoomDiv.parentNode.removeChild(_zoomDiv);
        };

        return handler;
    };

    easey_handlers.DragHandler = function() {
        var handler = {},
            map,
            panner;

        function focusMap(e) {
            map.parent.focus();
        }

        function mouseDown(e) {
            if (e.shiftKey || e.button == 2) return;
            MM.addEvent(document, 'mousemove', mouseMove);
            MM.addEvent(document, 'mouseup', mouseUp);
            panner.down(e);
            map.parent.style.cursor = 'move';
            return MM.cancelEvent(e);
        }

        function mouseMove(e) {
            panner.move(e);
            return MM.cancelEvent(e);
        }

        function mouseUp(e) {
            MM.removeEvent(document, 'mousemove', mouseMove);
            MM.removeEvent(document, 'mouseup', mouseUp);
            panner.up();
            map.parent.style.cursor = '';
            return MM.cancelEvent(e);
        }

        handler.init = function(x) {
            map = x;
            MM.addEvent(map.parent, 'click', focusMap);
            MM.addEvent(map.parent, 'mousedown', mouseDown);
            panner = panning();
        };

        handler.remove = function() {
            MM.removeEvent(map.parent, 'click', focusMap);
            MM.removeEvent(map.parent, 'mousedown', mouseDown);
            panner.up();
            panner.remove();
        };

        return handler;
    };

    function panning(drag) {

        var p = {};
        drag = drag || 0.15;

        var speed = { x: 0, y: 0 },
            dir = { x: 0, y: 0 },
            removed = false,
            nowPoint = null,
            oldPoint = null,
            moveTime = null,
            prevMoveTime = null,
            animatedLastPoint = true,
            t,
            prevT = new Date().getTime();

        p.down = function(e) {
            nowPoint = oldPoint = MM.getMousePoint(e, map);
            moveTime = prevMoveTime = +new Date();
        };

        p.move = function(e) {
            if (nowPoint) {
                if (animatedLastPoint) {
                    oldPoint = nowPoint;
                    prevMoveTime = moveTime;
                    animatedLastPoint = false;
                }
                nowPoint = MM.getMousePoint(e, map);
                moveTime = +new Date();
            }
        };

        p.up = function() {
            if (+new Date() - prevMoveTime < 50) {
                dt = Math.max(1, moveTime - prevMoveTime);
                dir.x = nowPoint.x - oldPoint.x;
                dir.y = nowPoint.y - oldPoint.y;
                speed.x = dir.x / dt;
                speed.y = dir.y / dt;
            } else {
                speed.x = 0;
                speed.y = 0;
            }
            nowPoint = oldPoint = null;
            moveTime = null;
        };

        p.remove = function() {
            removed = true;
        };

        function animate(t) {
            var dt = Math.max(1, t - prevT);
            if (nowPoint && oldPoint) {
                if (!animatedLastPoint) {
                    dir.x = nowPoint.x - oldPoint.x;
                    dir.y = nowPoint.y - oldPoint.y;
                    map.panBy(dir.x, dir.y);
                    animatedLastPoint = true;
                }
            } else {
                // Rough time based animation accuracy
                // using a linear approximation approach
                speed.x *= Math.pow(1 - drag, dt * 60 / 1000);
                speed.y *= Math.pow(1 - drag, dt * 60 / 1000);
                if (Math.abs(speed.x) < 0.001) {
                    speed.x = 0;
                }
                if (Math.abs(speed.y) < 0.001) {
                    speed.y = 0;
                }
                if (speed.x || speed.y) {
                    map.panBy(speed.x * dt, speed.y * dt);
                }
            }
            prevT = t;
            if (!removed) MM.getFrame(animate);
        }

        MM.getFrame(animate);
        return p;
    }

    this.easey_handlers = easey_handlers;

})(this, MM);
<|endoftext|>


<commit_before>/**
 *
 * This is an example for what you should include in your own Timeline
 * development, it pulls in files individually for debugging purposes.
 * Assumes you've mounted /timeline and /ajax on a server.
 *
 */

requirejs.config({
    "baseUrl": "/timeline/api/",
    "paths": {
        "jquery": "lib/jquery",
        "i18n": "lib/i18n"
    },
    "config": {
        // Uncomment to force use of chosen locale for testing
        // "i18n": {
        //     "locale": "zh"
        // },
        "simile-ajax": {
            "bundle": false
        },
        "timeline": {
            "bundle": false,
            "ajax": "/ajax/api/"
        }
    },
    "packages": [
        {
            "name": "simile-ajax",
            "main": "simile-ajax",
            "location": "/ajax/api/"
        }
    ]
});

require(["timeline"], function(Timeline) {
    window.Timeline = Timeline;
});
<commit_msg>provide code block example<commit_after>/**
 *
 * This is an example for what you should include in your own Timeline
 * development, it pulls in files individually for debugging purposes.
 * Assumes you've mounted /timeline and /ajax on a server.
 *
 * Surround your Timeline code with the following, substituting the path
 * as appropriate.
 *
 * require(["path/to/this/file-without-js"], function() {
 *     require(["timeline"], function(Timeline) {
 *         window.Timeline = Timeline;
 *         // Your Timeline code here.
 *     });
 * });
 *
 */

requirejs.config({
    "baseUrl": "/timeline/api/",
    "paths": {
        "jquery": "lib/jquery",
        "i18n": "lib/i18n"
    },
    "config": {
        // Uncomment to force use of chosen locale for testing
        // "i18n": {
        //     "locale": "zh"
        // },
        "simile-ajax": {
            "bundle": false,
            "prefix": "/ajax/api/"
        },
        "timeline": {
            "bundle": false,
            "prefix": "/timeline/api/",
            "ajax": "/ajax/api"
        }
    },
    "packages": [
        {
            "name": "simile-ajax",
            "main": "simile-ajax",
            "location": "/ajax/api/"
        }
    ]
});
<|endoftext|>


<commit_before>"use strict";

var CSLEDIT = CSLEDIT || {};

CSLEDIT.editReferences = (function () {
	var init = function (listElement, callback, citation, defaultCheckedRefs) {
		var index = 0,
			checked;

		listElement.children().remove();

		// create menus
		$.each(CSLEDIT.exampleData.jsonDocuments, function (itemName, item) {
			var description = '' + item.type +
				'<\/strong>: ' + item.title,
				additionalOptions = CSLEDIT.exampleData.additionalOptions[index];

			if (typeof additionalOptions !== "undefined") {
				description += '
(' + additionalOptions.description + ')';
			}

			listElement.append('
<input type="checkbox" value="' +
				index + '" \/>' + description + '<\/li>');
			index++;
		});

		checked = CSLEDIT.storage.getItem('CSLEDIT.citation' + citation);
		if (checked === null || checked === "") {
			checked = defaultCheckedRefs;
		} else {
			checked = JSON.parse(checked);
		}

		// select the first 3
		listElement.find('input').val(checked).on('change', function () {
			updateCitations(listElement, callback, citation);
		});

		// update with no callback
		updateCitations(listElement, function () {}, citation);
	};

	var updateCitations = function (listElement, callback, citation) {
		var citationItem,
			citationItems = [],
			checked = [],
			additionalOptions;

		listElement.find('input').each( function (index) {
			if ($(this).is(':checked')) {
				citationItem = {id:"ITEM-" + (index + 1)};
				additionalOptions = CSLEDIT.exampleData.additionalOptions[index];
				if (typeof additionalOptions !== "undefined") {
					// add options to citationItem
					$.each (additionalOptions.options, function (key, value) {
						citationItem[key] = value;
					});
				}
				console.log('adding citation item: ' + JSON.stringify(citationItem));
				citationItems.push(citationItem);
				checked.push(index);
			}
		});

		CSLEDIT.storage.setItem('CSLEDIT.citation' + citation, JSON.stringify(checked));

		CSLEDIT.exampleData.citationsItems[citation].citationItems = citationItems;

		callback();
	};

	return {
		init : init
	};
}());
<commit_msg>Improve edit reference UI by making the checkbox descriptions proper clickable labels.<commit_after>"use strict";

var CSLEDIT = CSLEDIT || {};

CSLEDIT.editReferences = (function () {
	var init = function (listElement, callback, citation, defaultCheckedRefs) {
		var index = 0,
			checked;

		listElement.children().remove();

		// create menus
		$.each(CSLEDIT.exampleData.jsonDocuments, function (itemName, item) {
			var description = '' + item.type +
				'<\/strong>: ' + item.title,
				additionalOptions = CSLEDIT.exampleData.additionalOptions[index];

			if (typeof additionalOptions !== "undefined") {
				description += '
(' + additionalOptions.description + ')';
			}

			listElement.append('
<input type="checkbox" ' +
				'id="ref' + citation + '-' + index + '" value="' +
				index + '" \/> ' + description + '<\/label><\/li>');
			index++;
		});

		checked = CSLEDIT.storage.getItem('CSLEDIT.citation' + citation);
		if (checked === null || checked === "") {
			checked = defaultCheckedRefs;
		} else {
			checked = JSON.parse(checked);
		}

		// select the first 3
		listElement.find('input').val(checked).on('change', function () {
			updateCitations(listElement, callback, citation);
		});

		// update with no callback
		updateCitations(listElement, function () {}, citation);
	};

	var updateCitations = function (listElement, callback, citation) {
		var citationItem,
			citationItems = [],
			checked = [],
			additionalOptions;

		listElement.find('input').each( function (index) {
			if ($(this).is(':checked')) {
				citationItem = {id:"ITEM-" + (index + 1)};
				additionalOptions = CSLEDIT.exampleData.additionalOptions[index];
				if (typeof additionalOptions !== "undefined") {
					// add options to citationItem
					$.each (additionalOptions.options, function (key, value) {
						citationItem[key] = value;
					});
				}
				console.log('adding citation item: ' + JSON.stringify(citationItem));
				citationItems.push(citationItem);
				checked.push(index);
			}
		});

		CSLEDIT.storage.setItem('CSLEDIT.citation' + citation, JSON.stringify(checked));

		CSLEDIT.exampleData.citationsItems[citation].citationItems = citationItems;

		callback();
	};

	return {
		init : init
	};
}());
<|endoftext|>