<commit_before>/*
    worklet.singlethread.worker.js

    Copyright (C) 2018 Steven Yi, Victor Lazzarini

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
    02110-1301 USA
*/

import * as Comlink from "comlink";
import MessagePortState from "@utils/message-port-state";
import { writeToFs, lsFs, llFs, readFromFs, rmrfFs } from "@root/filesystem";
import libcsoundFactory from "@root/libcsound";
import loadWasm from "@root/module";
import { assoc, pipe } from "ramda";
import { clearArray } from "@utils/clear-array";
import { logSinglethreadWorkletWorker as log } from "@root/logger";

let libraryCsound;
let combined;
const rtmidiQueue = [];

const callUncloned = async (k, arguments_) => {
  const caller = combined.get(k);
  const returnValue = caller && caller.apply({}, arguments_ || []);
  return returnValue;
};

class WorkletSinglethreadWorker extends AudioWorkletProcessor {
  static get parameterDescriptors() {
    return [];
  }

  constructor(options) {
    super(options);
    this.options = options;
    this.initialize = this.initialize.bind(this);
    this.pause = this.pause.bind(this);
    this.process = this.process.bind(this);
    this.resume = this.resume.bind(this);
    this.start = this.start.bind(this);
    this.needsStartNotification = false;
    this.isPaused = false;
    this.callUncloned = () => console.error("Csound worklet thread is still uninitialized!");
    this.port.start();
    Comlink.expose(this, this.port);
    this.workerMessagePort = new MessagePortState();
    this.initializeMessagePort = ({ messagePort, rtmidiPort }) => {
      this.workerMessagePort.post = (messageLog) => messagePort.postMessage({ log: messageLog });
      this.workerMessagePort.broadcastPlayState = (playStateChange) => {
        if (this.workerMessagePort.workerState !== playStateChange) {
          this.workerMessagePort.workerState = playStateChange;
        }
        messagePort.postMessage({ playStateChange });
      };
      this.workerMessagePort.ready = true;
      log(`initRtMidiEventPort`)();
      this.rtmidiPort = rtmidiPort;
      this.rtmidiPort.addEventListener("message", ({ data: payload }) => {
        rtmidiQueue.push(payload);
      });
      this.rtmidiPort.start();
    };
  }

  async initialize(wasmDataURI, withPlugins) {
    log("initializing worklet.singlethread.worker")();
    let resolver;
    const waiter = new Promise((resolve) => {
      resolver = resolve;
    });
    loadWasm({ wasmDataURI, withPlugins, messagePort: this.workerMessagePort }).then(
      ([wasm, wasmFs]) => {
        this.wasm = wasm;
        this.wasmFs = wasmFs;

        libraryCsound = libcsoundFactory(wasm);
        this.callUncloned = callUncloned;
        this.csound = libraryCsound.csoundCreate(0);
        this.result = 0;
        this.running = false;
        this.started = false;
        // this.sampleRate = sampleRate;
        this.resetCsound(false);

        const csoundCreate = (v) => {
          return this.csound;
        };
        const allAPI = pipe(
          assoc("writeToFs", writeToFs(wasmFs)),
          assoc("readFromFs", readFromFs(wasmFs)),
          assoc("lsFs", lsFs(wasmFs)),
          assoc("llFs", llFs(wasmFs)),
          assoc("rmrfFs", rmrfFs(wasmFs)),
          assoc("csoundCreate", csoundCreate),
          assoc("csoundReset", this.resetCsound.bind(true)),
          assoc("csoundStart", this.start.bind(this)),
          assoc("csoundStop", this.stop.bind(this)),
          assoc("wasm", wasm),
        )(libraryCsound);
        combined = new Map(Object.entries(allAPI));
        log("wasm initialized and api generated")();
        resolver();
      },
    );
    log("waiting on wasm initialization to complete")();
    await waiter;
  }

  async resetCsound(callReset) {
    // no start = no reset
    if (!this.workerMessagePort) {
      return;
    }
    if (
      this.workerMessagePort.workerState !== "realtimePerformanceEnded" &&
      this.workerMessagePort.workerState !== "realtimePerformanceStarted"
    ) {
      // reset can't be called until performance has started or ended!
      return -1;
    }
    if (this.workerMessagePort.workerState === "realtimePerformanceStarted") {
      this.workerMessagePort.broadcastPlayState("realtimePerformanceEnded");
    }

    this.running = false;
    this.started = false;
    this.result = 0;

    const cs = this.csound;

    if (callReset) {
      libraryCsound.csoundReset(cs);
    }

    libraryCsound.csoundSetMidiCallbacks(cs);
    libraryCsound.csoundSetOption(cs, "--sample-rate=" + this.sampleRate);
    this.nchnls = -1;
    this.nchnls_i = -1;
    delete this.csoundOutputBuffer;
  }

  stop() {
    this.workerMessagePort.broadcastPlayState("realtimePerformanceEnded");
    if (this.csound) {
      libraryCsound.csoundStop(this.csound);
    }
  }

  pause() {
    if (!this.isPaused) {
      this.workerMessagePort.broadcastPlayState("realtimePerformancePaused");
      this.isPaused = true;
    }
  }

  resume() {
    if (this.isPaused) {
      this.workerMessagePort.broadcastPlayState("realtimePerformanceResumed");
      this.isPaused = false;
    }
  }

  process(inputs, outputs) {
    if (this.isPaused || !this.csoundOutputBuffer || !this.running) {
      const output = outputs[0];
      const bufferLength = output[0].length;
      for (let index = 0; index < bufferLength; index++) {
        for (let channel = 0; channel < output.numberOfChannels; channel++) {
          const outputChannel = output[channel];
          outputChannel[index] = 0;
        }
      }
      return true;
    }

    // if we are starting, we need to bordcast it
    // this late in order to avoid timing issues
    if (this.needsStartNotification) {
      this.needsStartNotification = false;
      this.workerMessagePort.broadcastPlayState("realtimePerformanceStarted");
    }

    if (rtmidiQueue.length > 0) {
      rtmidiQueue.forEach((event) => {
        libraryCsound.csoundPushMidiMessage(this.csound, event[0], event[1], event[2]);
      });
      clearArray(rtmidiQueue);
    }

    const input = inputs[0];
    const output = outputs[0];

    const bufferLength = output[0].length;

    let csOut = this.csoundOutputBuffer;
    let csIn = this.csoundInputBuffer;
    const ksmps = this.ksmps;
    const zerodBFS = this.zerodBFS;

    let cnt = this.cnt;
    const nchnls = this.nchnls;
    const nchnlsIn = this.nchnls_i;
    let result = this.result;

    for (let index = 0; index < bufferLength; index++, cnt++) {
      if (cnt >= ksmps && result === 0) {
        // if we need more samples from Csound
        result = libraryCsound.csoundPerformKsmps(this.csound);
        cnt = 0;

        if (result !== 0) {
          this.running = false;
          this.started = false;
          libraryCsound.csoundCleanup(this.csound);
          // this.firePlayStateChange();
        }
      }

      /* Check if MEMGROWTH occured from csoundPerformKsmps or otherwise. If so,
      rest output ant input buffers to new pointer locations. */
      if (!csOut || csOut.length === 0) {
        csOut = this.csoundOutputBuffer = new Float64Array(
          this.wasm.exports.memory.buffer,
          libraryCsound.csoundGetSpout(this.csound),
          ksmps * nchnls,
        );
      }

      if (!csIn || csIn.length === 0) {
        csIn = this.csoundInputBuffer = new Float64Array(
          this.wasm.exports.memory.buffer,
          libraryCsound.csoundGetSpin(this.csound),
          ksmps * nchnlsIn,
        );
      }

      // handle 1->1, 1->2, 2->1, 2->2 input channel count mixing and nchnls_i
      const inputChanMax = Math.min(this.nchnls_i, input.length);
      for (let channel = 0; channel < inputChanMax; channel++) {
        const inputChannel = input[channel];
        csIn[cnt * nchnlsIn + channel] = inputChannel[index] * zerodBFS;
      }

      // Channel mixing matches behavior of:
      // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#Up-mixing_and_down-mixing

      // handle 1->1, 1->2, 2->1, 2->2 output channel count mixing and nchnls
      if (this.nchnls === output.length) {
        for (const [channel, outputChannel] of output.entries()) {
          if (result === 0) outputChannel[index] = csOut[cnt * nchnls + channel] / zerodBFS;
          else outputChannel[index] = 0;
        }
      } else if (this.nchnls === 2 && output.length === 1) {
        const outputChannel = output[0];
        if (result === 0) {
          const left = csOut[cnt * nchnls] / zerodBFS;
          const right = csOut[cnt * nchnls + 1] / zerodBFS;
          outputChannel[index] = 0.5 * (left + right);
        } else {
          outputChannel[index] = 0;
        }
      } else if (this.nchnls === 1 && output.length === 2) {
        const outChan0 = output[0];
        const outChan1 = output[1];

        if (result === 0) {
          const value = csOut[cnt * nchnls] / zerodBFS;
          outChan0[index] = value;
          outChan1[index] = value;
        } else {
          outChan0[index] = 0;
          outChan1[index] = 0;
        }
      } else {
        // FIXME: we do not support other cases at this time
      }
    }

    this.cnt = cnt;
    this.result = result;

    return true;
  }

  async start() {
    let returnValueValue = -1;
    if (!this.started) {
      log("worklet thread is starting..")();
      const cs = this.csound;
      const ksmps = libraryCsound.csoundGetKsmps(cs);
      this.ksmps = ksmps;
      this.cnt = ksmps;
      this.nchnls = libraryCsound.csoundGetNchnls(cs);
      this.nchnls_i = libraryCsound.csoundGetNchnlsInput(cs);

      this.zerodBFS = libraryCsound.csoundGet0dBFS(cs);
      returnValueValue = libraryCsound.csoundStart(cs);
      log("csoundStart called with {} return val", returnValueValue)();
      this.csoundOutputBuffer = new Float64Array(
        this.wasm.exports.memory.buffer,
        libraryCsound.csoundGetSpout(cs),
        ksmps * this.nchnls,
      );
      this.csoundInputBuffer = new Float64Array(
        this.wasm.exports.memory.buffer,
        libraryCsound.csoundGetSpin(cs),
        ksmps * this.nchnls_i,
      );

      this.started = true;
      this.needsStartNotification = true;
    } else {
      log("worklet was asked to start but it already has!")();
    }
    this.running = true;
    return returnValueValue;
  }
}

registerProcessor("csound-singlethread-worklet-processor", WorkletSinglethreadWorker);
<commit_msg>fix sample rate setting and first-time reset for AW single-thread<commit_after>/*
    worklet.singlethread.worker.js

    Copyright (C) 2018 Steven Yi, Victor Lazzarini

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
    02110-1301 USA
*/

import * as Comlink from "comlink";
import MessagePortState from "@utils/message-port-state";
import { writeToFs, lsFs, llFs, readFromFs, rmrfFs } from "@root/filesystem";
import libcsoundFactory from "@root/libcsound";
import loadWasm from "@root/module";
import { assoc, pipe } from "ramda";
import { clearArray } from "@utils/clear-array";
import { logSinglethreadWorkletWorker as log } from "@root/logger";

let libraryCsound;
let combined;
const rtmidiQueue = [];

const callUncloned = async (k, arguments_) => {
  const caller = combined.get(k);
  const returnValue = caller && caller.apply({}, arguments_ || []);
  return returnValue;
};

class WorkletSinglethreadWorker extends AudioWorkletProcessor {
  static get parameterDescriptors() {
    return [];
  }

  constructor(options) {
    super(options);
    // eslint-disable-next-line no-undef
    this.sampleRate = sampleRate;
    this.options = options;
    this.initialize = this.initialize.bind(this);
    this.pause = this.pause.bind(this);
    this.process = this.process.bind(this);
    this.resume = this.resume.bind(this);
    this.start = this.start.bind(this);
    this.needsStartNotification = false;
    this.isPaused = false;
    this.callUncloned = () => console.error("Csound worklet thread is still uninitialized!");
    this.port.start();
    Comlink.expose(this, this.port);
    this.workerMessagePort = new MessagePortState();
    this.initializeMessagePort = ({ messagePort, rtmidiPort }) => {
      this.workerMessagePort.post = (messageLog) => messagePort.postMessage({ log: messageLog });
      this.workerMessagePort.broadcastPlayState = (playStateChange) => {
        if (this.workerMessagePort.workerState !== playStateChange) {
          this.workerMessagePort.workerState = playStateChange;
        }
        messagePort.postMessage({ playStateChange });
      };
      this.workerMessagePort.ready = true;
      log(`initRtMidiEventPort`)();
      this.rtmidiPort = rtmidiPort;
      this.rtmidiPort.addEventListener("message", ({ data: payload }) => {
        rtmidiQueue.push(payload);
      });
      this.rtmidiPort.start();
    };
  }

  async initialize(wasmDataURI, withPlugins) {
    log("initializing worklet.singlethread.worker")();
    let resolver;
    const waiter = new Promise((resolve) => {
      resolver = resolve;
    });
    loadWasm({ wasmDataURI, withPlugins, messagePort: this.workerMessagePort }).then(
      ([wasm, wasmFs]) => {
        this.wasm = wasm;
        this.wasmFs = wasmFs;

        libraryCsound = libcsoundFactory(wasm);
        this.callUncloned = callUncloned;
        this.csound = libraryCsound.csoundCreate(0);
        this.result = 0;
        this.running = false;
        this.started = false;
        this.resetCsound(false);

        const csoundCreate = (v) => {
          return this.csound;
        };
        const allAPI = pipe(
          assoc("writeToFs", writeToFs(wasmFs)),
          assoc("readFromFs", readFromFs(wasmFs)),
          assoc("lsFs", lsFs(wasmFs)),
          assoc("llFs", llFs(wasmFs)),
          assoc("rmrfFs", rmrfFs(wasmFs)),
          assoc("csoundCreate", csoundCreate),
          assoc("csoundReset", this.resetCsound.bind(true)),
          assoc("csoundStart", this.start.bind(this)),
          assoc("csoundStop", this.stop.bind(this)),
          assoc("wasm", wasm),
        )(libraryCsound);
        combined = new Map(Object.entries(allAPI));
        log("wasm initialized and api generated")();
        resolver();
      },
    );
    log("waiting on wasm initialization to complete")();
    await waiter;
  }

  async resetCsound(callReset) {
    // no start = no reset
    if (callReset && !this.workerMessagePort) {
      return -1;
    }
    if (callReset &&
      this.workerMessagePort.workerState !== "realtimePerformanceEnded" &&
      this.workerMessagePort.workerState !== "realtimePerformanceStarted"
    ) {
      // reset can't be called until performance has started or ended!
      return -1;
    }
    if (callReset && this.workerMessagePort.workerState === "realtimePerformanceStarted") {
      this.workerMessagePort.broadcastPlayState("realtimePerformanceEnded");
    }

    this.running = false;
    this.started = false;
    this.result = 0;

    const cs = this.csound;

    if (callReset) {
      libraryCsound.csoundReset(cs);
    }

    libraryCsound.csoundSetMidiCallbacks(cs);
    libraryCsound.csoundSetOption(cs, "-iadc");
    libraryCsound.csoundSetOption(cs, "-odac");
    libraryCsound.csoundSetOption(cs, "--sample-rate=" + this.sampleRate);
    this.nchnls = -1;
    this.nchnls_i = -1;
    delete this.csoundOutputBuffer;
  }

  stop() {
    this.workerMessagePort.broadcastPlayState("realtimePerformanceEnded");
    if (this.csound) {
      libraryCsound.csoundStop(this.csound);
    }
  }

  pause() {
    if (!this.isPaused) {
      this.workerMessagePort.broadcastPlayState("realtimePerformancePaused");
      this.isPaused = true;
    }
  }

  resume() {
    if (this.isPaused) {
      this.workerMessagePort.broadcastPlayState("realtimePerformanceResumed");
      this.isPaused = false;
    }
  }

  process(inputs, outputs) {
    if (this.isPaused || !this.csoundOutputBuffer || !this.running) {
      const output = outputs[0];
      const bufferLength = output[0].length;
      for (let index = 0; index < bufferLength; index++) {
        for (let channel = 0; channel < output.numberOfChannels; channel++) {
          const outputChannel = output[channel];
          outputChannel[index] = 0;
        }
      }
      return true;
    }

    // if we are starting, we need to bordcast it
    // this late in order to avoid timing issues
    if (this.needsStartNotification) {
      this.needsStartNotification = false;
      this.workerMessagePort.broadcastPlayState("realtimePerformanceStarted");
    }

    if (rtmidiQueue.length > 0) {
      rtmidiQueue.forEach((event) => {
        libraryCsound.csoundPushMidiMessage(this.csound, event[0], event[1], event[2]);
      });
      clearArray(rtmidiQueue);
    }

    const input = inputs[0];
    const output = outputs[0];

    const bufferLength = output[0].length;

    let csOut = this.csoundOutputBuffer;
    let csIn = this.csoundInputBuffer;
    const ksmps = this.ksmps;
    const zerodBFS = this.zerodBFS;

    let cnt = this.cnt;
    const nchnls = this.nchnls;
    const nchnlsIn = this.nchnls_i;
    let result = this.result;

    for (let index = 0; index < bufferLength; index++, cnt++) {
      if (cnt >= ksmps && result === 0) {
        // if we need more samples from Csound
        result = libraryCsound.csoundPerformKsmps(this.csound);
        cnt = 0;

        if (result !== 0) {
          this.running = false;
          this.started = false;
          libraryCsound.csoundCleanup(this.csound);
          this.workerMessagePort.broadcastPlayState("realtimePerformanceEnded");
          // if (this.stopPromiz) {
          //   this.stopPromiz();
          //   delete this.stopPromiz;
          // }
        }
      }

      /* Check if MEMGROWTH occured from csoundPerformKsmps or otherwise. If so,
      rest output ant input buffers to new pointer locations. */
      if (!csOut || csOut.length === 0) {
        csOut = this.csoundOutputBuffer = new Float64Array(
          this.wasm.exports.memory.buffer,
          libraryCsound.csoundGetSpout(this.csound),
          ksmps * nchnls,
        );
      }

      if (!csIn || csIn.length === 0) {
        csIn = this.csoundInputBuffer = new Float64Array(
          this.wasm.exports.memory.buffer,
          libraryCsound.csoundGetSpin(this.csound),
          ksmps * nchnlsIn,
        );
      }

      // handle 1->1, 1->2, 2->1, 2->2 input channel count mixing and nchnls_i
      const inputChanMax = Math.min(this.nchnls_i, input.length);
      for (let channel = 0; channel < inputChanMax; channel++) {
        const inputChannel = input[channel];
        csIn[cnt * nchnlsIn + channel] = inputChannel[index] * zerodBFS;
      }

      // Channel mixing matches behavior of:
      // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#Up-mixing_and_down-mixing

      // handle 1->1, 1->2, 2->1, 2->2 output channel count mixing and nchnls
      if (this.nchnls === output.length) {
        for (const [channel, outputChannel] of output.entries()) {
          if (result === 0) outputChannel[index] = csOut[cnt * nchnls + channel] / zerodBFS;
          else outputChannel[index] = 0;
        }
      } else if (this.nchnls === 2 && output.length === 1) {
        const outputChannel = output[0];
        if (result === 0) {
          const left = csOut[cnt * nchnls] / zerodBFS;
          const right = csOut[cnt * nchnls + 1] / zerodBFS;
          outputChannel[index] = 0.5 * (left + right);
        } else {
          outputChannel[index] = 0;
        }
      } else if (this.nchnls === 1 && output.length === 2) {
        const outChan0 = output[0];
        const outChan1 = output[1];

        if (result === 0) {
          const value = csOut[cnt * nchnls] / zerodBFS;
          outChan0[index] = value;
          outChan1[index] = value;
        } else {
          outChan0[index] = 0;
          outChan1[index] = 0;
        }
      } else {
        // FIXME: we do not support other cases at this time
      }
    }

    this.cnt = cnt;
    this.result = result;

    return true;
  }

  async start() {
    let returnValueValue = -1;
    if (!this.started) {
      log("worklet thread is starting..")();
      const cs = this.csound;
      const ksmps = libraryCsound.csoundGetKsmps(cs);
      this.ksmps = ksmps;
      this.cnt = ksmps;
      this.nchnls = libraryCsound.csoundGetNchnls(cs);
      this.nchnls_i = libraryCsound.csoundGetNchnlsInput(cs);

      this.zerodBFS = libraryCsound.csoundGet0dBFS(cs);
      returnValueValue = libraryCsound.csoundStart(cs);
      log("csoundStart called with {} return val", returnValueValue)();
      this.csoundOutputBuffer = new Float64Array(
        this.wasm.exports.memory.buffer,
        libraryCsound.csoundGetSpout(cs),
        ksmps * this.nchnls,
      );
      this.csoundInputBuffer = new Float64Array(
        this.wasm.exports.memory.buffer,
        libraryCsound.csoundGetSpin(cs),
        ksmps * this.nchnls_i,
      );

      this.started = true;
      this.needsStartNotification = true;
    } else {
      log("worklet was asked to start but it already has!")();
    }
    this.running = true;
    return returnValueValue;
  }
}

registerProcessor("csound-singlethread-worklet-processor", WorkletSinglethreadWorker);
<|endoftext|>


<commit_before>// 2015-12-28
define(['jquery', 'domReady!'], function($) {return (
	/**
	 * @param {Object} config
	 * @param {String} config.id
	 */
	function(config) {
		/** @type {jQuery} HTMLFieldSetElement */
		var $element = $(document.getElementById(config.id));
		(function() {
			var $toolbar = $('<div/>').addClass('toolbar');
			$element.after($toolbar);
			var button = function(title, onClick) {
				var $result = $('<button>');
				$result.append($('<span/>').append(title));
				$result.click(function(event) {
					event.preventDefault();
					onClick();
				});
				$toolbar.append($result);
				return $result;
			};
			button('Add', function() {
				/** @type {jQuery} HTMLFieldSetElement */
				var $template = $element.children('.df-name-template');
				var $item = $template.clone(true).removeClass('df-hidden');
				/**
				 * 2015-12-30
				 * Нумерация строк начинается с нуля:
				 *
				 * @type {Number}
				 */
				var ordering = $element.children('.df-field').length;
				$('link', $item).remove();
				$('*', $item).add($item);
				$element.append($item);
				debugger;
			});
			button('Delete', function() {});
		})();
	}
);});<commit_msg>no message<commit_after>// 2015-12-28
define(['jquery', 'domReady!'], function($) {return (
	/**
	 * @param {Object} config
	 * @param {String} config.id
	 */
	function(config) {
		/** @type {jQuery} HTMLFieldSetElement */
		var $element = $(document.getElementById(config.id));
		/** @type {jQuery} HTMLFieldSetElement */
		var $template = $element.children('.df-name-template');
		(function() {
			var $toolbar = $('<div/>').addClass('toolbar');
			$element.after($toolbar);
			var button = function(title, onClick) {
				var $result = $('<button>');
				$result.append($('<span/>').append(title));
				$result.click(function(event) {
					event.preventDefault();
					onClick();
				});
				$toolbar.append($result);
				return $result;
			};
			button('Add', function() {
				/** @type {jQuery} HTMLFieldSetElement */
				var $item = $template.clone(true).removeClass('df-hidden');
				/**
				 * 2015-12-30
				 * Нумерация элементов начинается с нуля:
				 * http://code.dmitry-fedyuk.com/m2/all/blob/3d99a5fa88177f8aca5d69f6f9b4ca4865735b9f/Framework/Data/Form/Element/ArrayT.php#L22
				 * При этом один из элементов является шаблоном (невидима),
				 * поэтому порядковый номер нового элемента
				 * равен количеству уже имеющихся элементов минус один
				 * или же (что то же самое) количеству братьев элемента-шаблона.
				 * @type {Number}
				 */
				var ordering = $template.siblings('.df-field').length;
				$('link', $item).remove();
				/**
				 * 2015-12-30
				 * «As of jQuery 1.6, the .attr() method returns undefined
				 * for attributes that have not been set.»
				 * http://api.jquery.com/attr/#entry-longdesc
				 * @param {jQuery} $element HTMLElement
				 * @param {String} attrName
				 * @param {String} from
				 * @param {String} to
				 */
				var replaceAttr = function($element, attrName, from, to) {
					/** @type {?String} */
					var value = $element.attr(attrName);
					if ('undefined' !== typeof value) {
						$element.attr(attrName, value.replace(from, to));
					}
				};
				$('*', $item).add($item).each(function() {
					var $this = $(this);
					replaceAttr($this, 'class', 'df-name-template', 'df-name-' + ordering);
					$.each(['id', 'data-ui-id', 'name'], function() {
						replaceAttr($this, this, '[template]', '[' + ordering + ']');
					});
				});
				$element.append($item);
			});
			button('Delete', function() {});
		})();
		/**
		 * 2015-12-30
		 * По аналогии с http://code.dmitry-fedyuk.com/m2e/markdown/blob/d030a44bfe75765d54d68acf106e2fbb9bd66b4c/view/adminhtml/web/main.js#L364
		 *
		 * По-правильному надо обрабатывать не beforeSubmit, а submit,
		 * потому что при beforeSubmit мы ещё не знаем, будет ли форма отправлена на сервер:
		 * вдруг валидаторы запретят отправку на сервер.
		 * Поэтому перед удалением шаблона нам важно быть уверенным,
		 * что форма действительно отправляется на сервер.
		 * Однако система почему-то игнорирует удаление полей формы на submit,
		 * поэтому прицепился пока на beforeSubmit.
		 */
		$element.closest('form').bind('beforeSubmit', function() {$template.remove();});
	}
);});<|endoftext|>


<commit_before>'use strict';

const Service = require('kermit/Service');

class NotificationService extends Service {
  bootstrap() {
    const sm = this.serviceManager;

    this.subscriptionRepository = sm.get('subscriptionRepository');
    this.releaseNotesUpdateService = sm.get('releaseNotesUpdateService');
    this.emailService = sm.get('emailService');
    this.sparky = sm.get('sparkPost');
    this.logger = sm.get('logging');

    return this;
  }

  sendReleaseNotesUpdateNotification(releaseNotes, releaseNotesUpdate, callback = () => {}) {
    this.loadReleaseNotesSubscriptions(
      releaseNotes._id,
      (subscriptionLookupErr, subscriptions) => {
        if (subscriptionLookupErr) {
          return void callback(subscriptionLookupErr);
        }

        if (subscriptions && subscriptions.length) {
          const emailRecipients = subscriptions.map(subscription => subscription.email);
          const newReleases = this.releaseNotesUpdateService.calculateUpdates(
            releaseNotes, releaseNotesUpdate
          );

          this.emailService.compose(
            'release-notes-update', {
              releaseNotes,
              releases: newReleases,
            }, (emailComposeErr, content) => {
              if (emailComposeErr) {
                this.logger.error(
                  `Update notification composition error for @${releaseNotes.scope}/${releaseNotes.name}`,
                  emailComposeErr
                );

                return void callback(emailComposeErr);
              }

              this.sendEmailNotification(
                { recipients: emailRecipients, content, releaseNotes },
                callback
              );
            }
          );
        } else {
          this.logger.info(`No update notifications sent for @${releaseNotes.scope}/${releaseNotes.name}`);

          callback();
        }
      }
    )
  }

  sendEmailNotification({ recipients, releaseNotes, content }, callback) {
    this.sparky.sendMessage({
      recipients,
      content: {
        subject: `New version of @${releaseNotes.scope}/${releaseNotes.name} released`,
        from: 'Release Notes <notifications@release-notes.com>',
        text: content.text,
        html: content.html,
      },
    }, (emailTransmissionErr, emailTransmissionResponse) => {
      if (emailTransmissionErr) {
        this.logger.error(
          `Update notification transmission error for @${releaseNotes.scope}/${releaseNotes.name}`,
          emailTransmissionErr
        );

        return void callback(emailTransmissionErr);
      }

      this.logger.info(
        `Update notification transmitted to ${recipients.length} subscribers for @${releaseNotes.scope}/${releaseNotes.name}`,
        emailTransmissionResponse
      );

      callback();
    });
  }

  loadReleaseNotesSubscriptions(id, callback) {
    this.subscriptionRepository.findByReleaseNotesId(
      id,
      (subscriptionLookupErr, subscriptions) => {
        if (subscriptionLookupErr) {
          this.logger.error(
            `Error loading subscriptions of release notes #${id}:`,
            subscriptionLookupErr.message
          );

          return void callback(subscriptionLookupErr);
        }

        callback(null, subscriptions);
      }
    );
  }
}

module.exports = NotificationService;
<commit_msg>Do not send update notifications when there are no new releases.<commit_after>'use strict';

const Service = require('kermit/Service');

class NotificationService extends Service {
  bootstrap() {
    const sm = this.serviceManager;

    this.subscriptionRepository = sm.get('subscriptionRepository');
    this.releaseNotesUpdateService = sm.get('releaseNotesUpdateService');
    this.emailService = sm.get('emailService');
    this.sparky = sm.get('sparkPost');
    this.logger = sm.get('logging');

    return this;
  }

  sendReleaseNotesUpdateNotification(releaseNotes, releaseNotesUpdate, callback = () => {}) {
    this.loadReleaseNotesSubscriptions(
      releaseNotes._id,
      (subscriptionLookupErr, subscriptions) => {
        if (subscriptionLookupErr) {
          return void callback(subscriptionLookupErr);
        }

        if (!subscriptions || !subscriptions.length) {
          this.logger.info(`No subscriptions, no update notifications sent for @${releaseNotes.scope}/${releaseNotes.name}`);

          return void callback();
        }

        const emailRecipients = subscriptions.map(subscription => subscription.email);
        const newReleases = this.releaseNotesUpdateService.calculateUpdates(
          releaseNotes, releaseNotesUpdate
        );

        if (!newReleases || !newReleases.length) {
          this.logger.info(`No new releases, no update notifications sent for @${releaseNotes.scope}/${releaseNotes.name}`);

          return void callback();
        }

        this.emailService.compose(
          'release-notes-update', {
            releaseNotes,
            releases: newReleases,
          }, (emailComposeErr, content) => {
            if (emailComposeErr) {
              this.logger.error(
                `Update notification composition error for @${releaseNotes.scope}/${releaseNotes.name}`,
                emailComposeErr
              );

              return void callback(emailComposeErr);
            }

            this.sendEmailNotification(
              { recipients: emailRecipients, content, releaseNotes },
              callback
            );
          }
        );
      }
    )
  }

  sendEmailNotification({ recipients, releaseNotes, content }, callback) {
    this.sparky.sendMessage({
      recipients,
      content: {
        subject: `New version of @${releaseNotes.scope}/${releaseNotes.name} released`,
        from: 'Release Notes <notifications@release-notes.com>',
        text: content.text,
        html: content.html,
      },
    }, (emailTransmissionErr, emailTransmissionResponse) => {
      if (emailTransmissionErr) {
        this.logger.error(
          `Update notification transmission error for @${releaseNotes.scope}/${releaseNotes.name}`,
          emailTransmissionErr
        );

        return void callback(emailTransmissionErr);
      }

      this.logger.info(
        `Update notification transmitted to ${recipients.length} subscribers for @${releaseNotes.scope}/${releaseNotes.name}`,
        emailTransmissionResponse
      );

      callback();
    });
  }

  loadReleaseNotesSubscriptions(id, callback) {
    this.subscriptionRepository.findByReleaseNotesId(
      id,
      (subscriptionLookupErr, subscriptions) => {
        if (subscriptionLookupErr) {
          this.logger.error(
            `Error loading subscriptions of release notes #${id}:`,
            subscriptionLookupErr.message
          );

          return void callback(subscriptionLookupErr);
        }

        callback(null, subscriptions);
      }
    );
  }
}

module.exports = NotificationService;
<|endoftext|>


<commit_before>function switchPage(page) {
  switch(page){
    case 1:
    document.getElementById("firstPage").style.display = "block";
    document.getElementById("secondPage").style.display = "none";
    break;
    case 2:
    document.getElementById("firstPage").style.display = "none";
    document.getElementById("secondPage").style.display = "block";
    break;
  }
}

switchPage(1);

/*function coloredButton(){
  if (getElementById("firstPage") === true){
    getElementById("firstPageButton").class = "w3-button w3-black";
  } else {
    getElementById("secondPageButton").class = "w3-button w3-black";
  }
}*/
<commit_msg>page buttons<commit_after>function switchPage(page) {
  switch(page){
    case 1:
    document.getElementById("firstPage").style.display = "block";
    document.getElementById("secondPage").style.display = "none";
    break;
    case 2:
    document.getElementById("firstPage").style.display = "none";
    document.getElementById("secondPage").style.display = "block";
    break;
  }
  coloredButton(page);
}

switchPage(1);

function coloredButton(page){
  switch (page) {
    case 1:
    document.getElementById("firstPageButton").classList.add("w3-black");
    document.getElementById("secondPageButton").classList.remove("w3-black");
    break;
    case 2:
    document.getElementById("secondPageButton").classList.add("w3-black");
    document.getElementById("firstPageButton").classList.remove("w3-black");
    break;

  }
}
<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS';
import makeWclUrl from 'common/makeWclUrl';
import SpellIcon from 'common/SpellIcon';

import LazyLoadStatisticBox from 'Main/LazyLoadStatisticBox';

import ModuleComponent from 'Parser/Core/ModuleComponent';

// Protection of Tyr is applied to everyone that benefits from the AM effect. This is simply the easiest way to see if someone is affected by AM, other more robust solutions take a lot more effort/complexity.
const PROTECTION_OF_TYR_ID = 211210;
const DEVOTION_AURA_DAMAGE_REDUCTION = 0.2;

function formatThousands(number) {
  return (Math.round(number || 0) + '').replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}
function formatNumber(number) {
  if (number > 1000000) {
    return `${(number / 1000000).toFixed(2)}m`;
  }
  if (number > 10000) {
    return `${Math.round(number / 1000)}k`;
  }
  return formatThousands(number);
}

class DevotionAura extends ModuleComponent {
  get damageReducedDuringAuraMastery() {
    return this.state.totalDamageTakenDuringAuraMastery / (1 - DEVOTION_AURA_DAMAGE_REDUCTION) * DEVOTION_AURA_DAMAGE_REDUCTION;
  }
  get averagePassiveDamageReduction() {
    return DEVOTION_AURA_DAMAGE_REDUCTION / this.owner.playerCount;
  }
  get damageReducedOutsideAuraMastery() {
    const averagePassiveDamageReduction = this.averagePassiveDamageReduction;
    return this.state.totalDamageTakenOutsideAuraMastery / (1 - averagePassiveDamageReduction) * averagePassiveDamageReduction;
  }
  get damageReduced() {
    return this.damageReducedDuringAuraMastery + this.damageReducedOutsideAuraMastery;
  }

  constructor(props) {
    super(props);
    this.state = {
      active: this.owner.selectedCombatant.hasTalent(SPELLS.DEVOTION_AURA_TALENT.id),
      totalDamageTakenDuringAuraMastery: 0,
      totalDamageTakenOutsideAuraMastery: 0,
    };
  }

  load() {
    const amDamageTakenPromise = fetch(makeWclUrl(`report/tables/damage-taken/${this.owner.report.code}`, {
      start: this.owner.fight.start_time,
      end: this.owner.fight.end_time,
      filter: `(IN RANGE FROM type='applybuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' TO type='removebuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' GROUP BY target ON target END)`,
    }))
      .then(response => response.json())
      .then((json) => {
        console.log('Received AM damage taken', json);
        if (json.status === 400 || json.status === 401) {
          throw json.error;
        } else {
          this.setState({
            totalDamageTakenDuringAuraMastery: json.entries.reduce((damageTaken, entry) => damageTaken + entry.total, 0),
          });
        }
      });
    const regularDamageTakenPromise = fetch(makeWclUrl(`report/tables/damage-taken/${this.owner.report.code}`, {
      start: this.owner.fight.start_time,
      end: this.owner.fight.end_time,
      filter: `(NOT IN RANGE FROM type='applybuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' TO type='removebuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' GROUP BY target ON target END)`,
    }))
      .then(response => response.json())
      .then((json) => {
        console.log('Received regular damage taken', json);
        if (json.status === 400 || json.status === 401) {
          throw json.error;
        } else {
          this.setState({
            totalDamageTakenOutsideAuraMastery: json.entries.reduce((damageTaken, entry) => damageTaken + entry.total, 0),
          });
        }
      });

    return Promise.all([
      amDamageTakenPromise,
      regularDamageTakenPromise,
    ]);
  }

  render() {
    const fightDuration = this.owner.fightDuration;

    const averagePassiveDamageReductionPercentage = Math.round(this.averagePassiveDamageReduction * 10000) / 100;

    return (
      <LazyLoadStatisticBox
        loader={this.load.bind(this)}
        icon={<SpellIcon id={SPELLS.DEVOTION_AURA_TALENT.id} />}
        value={`${formatNumber(this.damageReduced / fightDuration * 1000)} DRPS`}
        label="Estimated damage reduced"
        tooltip={`The total estimated damage reduced during Aura Mastery was ${formatThousands(this.damageReducedDuringAuraMastery)} (${formatNumber(this.damageReducedDuringAuraMastery / fightDuration * 1000)} DRPS). This has a high accuracy.

          The total estimated damage reduced outside Aura Mastery was ${formatThousands(this.damageReducedOutsideAuraMastery)} (${formatNumber(this.damageReducedOutsideAuraMastery / fightDuration * 1000)} DRPS). This has medium accuracy.

          Calculating exact Devotion Aura damage reduced is near impossible and takes a very long time. This gets the total damage taken during and outside Aura Mastery and calculates the damage reduced for those totals by taking 20% of the original damage taken during Aura Mastery and ${averagePassiveDamageReductionPercentage}% (20% / ${this.owner.playerCount} members in the raid) of all damage taken outside Aura Mastery. This ${averagePassiveDamageReductionPercentage}% is the estimated average damage reduced assuming this averages out to be the same on all members. More extensive tests that go over all damage events and that is aware of the exact Devotion Aura reduction at each event have shown that this is a close approximation.`}
      />
    );
  }
}

export default DevotionAura;
<commit_msg>Words<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS';
import makeWclUrl from 'common/makeWclUrl';
import SpellIcon from 'common/SpellIcon';

import LazyLoadStatisticBox from 'Main/LazyLoadStatisticBox';

import ModuleComponent from 'Parser/Core/ModuleComponent';

// Protection of Tyr is applied to everyone that benefits from the AM effect. This is simply the easiest way to see if someone is affected by AM, other more robust solutions take a lot more effort/complexity.
const PROTECTION_OF_TYR_ID = 211210;
const DEVOTION_AURA_DAMAGE_REDUCTION = 0.2;

function formatThousands(number) {
  return (Math.round(number || 0) + '').replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}
function formatNumber(number) {
  if (number > 1000000) {
    return `${(number / 1000000).toFixed(2)}m`;
  }
  if (number > 10000) {
    return `${Math.round(number / 1000)}k`;
  }
  return formatThousands(number);
}

class DevotionAura extends ModuleComponent {
  get damageReducedDuringAuraMastery() {
    return this.state.totalDamageTakenDuringAuraMastery / (1 - DEVOTION_AURA_DAMAGE_REDUCTION) * DEVOTION_AURA_DAMAGE_REDUCTION;
  }
  get averagePassiveDamageReduction() {
    return DEVOTION_AURA_DAMAGE_REDUCTION / this.owner.playerCount;
  }
  get damageReducedOutsideAuraMastery() {
    const averagePassiveDamageReduction = this.averagePassiveDamageReduction;
    return this.state.totalDamageTakenOutsideAuraMastery / (1 - averagePassiveDamageReduction) * averagePassiveDamageReduction;
  }
  get damageReduced() {
    return this.damageReducedDuringAuraMastery + this.damageReducedOutsideAuraMastery;
  }

  constructor(props) {
    super(props);
    this.state = {
      active: this.owner.selectedCombatant.hasTalent(SPELLS.DEVOTION_AURA_TALENT.id),
      totalDamageTakenDuringAuraMastery: 0,
      totalDamageTakenOutsideAuraMastery: 0,
    };
  }

  load() {
    const amDamageTakenPromise = fetch(makeWclUrl(`report/tables/damage-taken/${this.owner.report.code}`, {
      start: this.owner.fight.start_time,
      end: this.owner.fight.end_time,
      filter: `(IN RANGE FROM type='applybuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' TO type='removebuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' GROUP BY target ON target END)`,
    }))
      .then(response => response.json())
      .then((json) => {
        console.log('Received AM damage taken', json);
        if (json.status === 400 || json.status === 401) {
          throw json.error;
        } else {
          this.setState({
            totalDamageTakenDuringAuraMastery: json.entries.reduce((damageTaken, entry) => damageTaken + entry.total, 0),
          });
        }
      });
    const regularDamageTakenPromise = fetch(makeWclUrl(`report/tables/damage-taken/${this.owner.report.code}`, {
      start: this.owner.fight.start_time,
      end: this.owner.fight.end_time,
      filter: `(NOT IN RANGE FROM type='applybuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' TO type='removebuff' AND ability.id=${PROTECTION_OF_TYR_ID} AND source.name='${this.owner.selectedCombatant.name}' GROUP BY target ON target END)`,
    }))
      .then(response => response.json())
      .then((json) => {
        console.log('Received regular damage taken', json);
        if (json.status === 400 || json.status === 401) {
          throw json.error;
        } else {
          this.setState({
            totalDamageTakenOutsideAuraMastery: json.entries.reduce((damageTaken, entry) => damageTaken + entry.total, 0),
          });
        }
      });

    return Promise.all([
      amDamageTakenPromise,
      regularDamageTakenPromise,
    ]);
  }

  render() {
    const fightDuration = this.owner.fightDuration;

    const averagePassiveDamageReductionPercentage = Math.round(this.averagePassiveDamageReduction * 10000) / 100;

    return (
      <LazyLoadStatisticBox
        loader={this.load.bind(this)}
        icon={<SpellIcon id={SPELLS.DEVOTION_AURA_TALENT.id} />}
        value={`${formatNumber(this.damageReduced / fightDuration * 1000)} DRPS`}
        label="Estimated damage reduced"
        tooltip={`The total estimated damage reduced during Aura Mastery was ${formatThousands(this.damageReducedDuringAuraMastery)} (${formatNumber(this.damageReducedDuringAuraMastery / fightDuration * 1000)} DRPS). This has a high accuracy.

          The total estimated damage reduced outside Aura Mastery was ${formatThousands(this.damageReducedOutsideAuraMastery)} (${formatNumber(this.damageReducedOutsideAuraMastery / fightDuration * 1000)} DRPS). This has medium accuracy.

          Calculating exact Devotion Aura damage reduced is near impossible and takes a very long time. This gets the total damage taken during and outside Aura Mastery and calculates the damage reduced for those totals by taking 20% of the original damage taken during Aura Mastery and ${averagePassiveDamageReductionPercentage}% (20% / ${this.owner.playerCount} members in the raid) of all damage taken outside Aura Mastery. This ${averagePassiveDamageReductionPercentage}% is the estimated average damage reduced assuming this averages out to be the same on all members. More extensive tests that go over all damage events and that is aware of the exact Devotion Aura reduction at each event have shown that this is usually a close approximation.`}
      />
    );
  }
}

export default DevotionAura;
<|endoftext|>


<commit_before>/*
Copyright (C) 2012 Thomas Lekanger

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var net = require('net'),
	util = require('util'),
	event = require('./Event');
var Parser = require('./Parser');
var Channel = require('./Channel');
var FloodProtection = require('./FloodProtection');
var Server = function(server, port, irc){
	var self = this;
	if(server){
		self.server = server;
	}
	if(port){
		self.port = port;
	}else{
		self.port = 6667;
	}
	if(irc){
		self.irc = irc;
	}
	if(irc.event){
		self.event = irc.event;
	}
	self.userNick = '';
	self.socket = new net.Socket;
	self.channels = {};

	/* Flood protection */
	self.FloodProtection = new FloodProtection(self, 250, 750);
};
Server.prototype = {
	connect: function(){
		var self = this,
			serverKey = 0,
			backlog = '',
			parser = new Parser(self, self.socket, self.event);
		self.socket.on('connect', function(){
			self.log('Connected to ' + self.server + ':' + self.port);
		});
		self.socket.on('data', function(data){
			backlog += data;
			var n = backlog.indexOf('\n');
			// got a \n? emit one or more 'line' events
			while(~n){
				self.socket.emit('line', backlog.substring(0, n));
				backlog = backlog.substring(n + 1);
				n = backlog.indexOf('\n');
			}
		});
		self.socket.on('line', function(line){
			try{
				parser.parse(line);
			}catch(e){
				throw e;
			}
		});
		self.socket.on('close', function(){
			// Reconnect
		});

		self.socket.setEncoding('utf8');
		self.socket.setNoDelay();
		self.socket.connect(self.port, self.server);

		//self.raw 'NICK ' + 'YattaIRC'
		//self.raw 'USER ' + 'yatta' + ' 8 * :' + 'Hiro Nakamura'
		serverKey = Object.keys(self.irc.servers).length;
		self.irc.servers[serverKey] = self;

		return serverKey;
	},
	log: function(){
		console.log.apply(this, arguments);
	},
	raw: function(msg){
		var self = this,
			socket = self.socket,
			time = new Date().getTime();
		self.log('[RAW]', msg);

		self.FloodProtection.send(msg);
	},
	pong: function(source){
		var self = this;
		self.raw('PONG ' + source);
	},
	nick: function(nick){
		var self = this;
		self.userNick = nick;
		self.raw('NICK ' + nick);
	},
	user: function(user, mode, realname){
		var self = this;
		self.raw('USER ' + user + ' ' + mode + ' * :' + realname );
	},
	join: function(channel){
		var self = this;
		self.raw('JOIN ' + channel);
	},
	part: function(channel){
		var self = this;
		self.raw('PART ' + channel);
	},
	op: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' +o ' + user);
	},
	deop: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' -o ' + user);
	},
	kick: function(channel, user, message){
		var self = this;
		self.raw('KICK ' + channel + ' ' + user + ':' + message);
	},
	ban: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' +b ' + user);
	},
	message: function(target, message){
		var self = this;
		self.raw('PRIVMSG ' + target + ' :' + message);
	},
	getChannel: function(channel){
		var self = this;
		if(self.channels && self.channels[channel]){
			return self.channels[channel];
		}
		return null;
	},
	getOrInitiateChannel: function(channel){
		var self = this, c;
		if(channel && self.channels && self.channels[channel]){
			return self.channels[channel];
		}else{
			c = new Channel(self, channel);
			self.channels[channel] = c;
			return c;
		}
	}
};
module.exports = Server;
<commit_msg>Do not throw on error<commit_after>/*
Copyright (C) 2012 Thomas Lekanger

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var net = require('net'),
	util = require('util'),
	event = require('./Event');
var Parser = require('./Parser');
var Channel = require('./Channel');
var FloodProtection = require('./FloodProtection');
var Server = function(server, port, irc){
	var self = this;
	if(server){
		self.server = server;
	}
	if(port){
		self.port = port;
	}else{
		self.port = 6667;
	}
	if(irc){
		self.irc = irc;
	}
	if(irc.event){
		self.event = irc.event;
	}
	self.userNick = '';
	self.socket = new net.Socket;
	self.channels = {};

	/* Flood protection */
	self.FloodProtection = new FloodProtection(self, 250, 750);
};
Server.prototype = {
	connect: function(){
		var self = this,
			serverKey = 0,
			backlog = '',
			parser = new Parser(self, self.socket, self.event);
		self.socket.on('connect', function(){
			self.log('Connected to ' + self.server + ':' + self.port);
		});
		self.socket.on('data', function(data){
			backlog += data;
			var n = backlog.indexOf('\n');
			// got a \n? emit one or more 'line' events
			while(~n){
				self.socket.emit('line', backlog.substring(0, n));
				backlog = backlog.substring(n + 1);
				n = backlog.indexOf('\n');
			}
		});
		self.socket.on('line', function(line){
			try{
				parser.parse(line);
			}catch(e){
				console.log(e);
			}
		});
		self.socket.on('close', function(){
			// Reconnect
		});

		self.socket.setEncoding('utf8');
		self.socket.setNoDelay();
		self.socket.connect(self.port, self.server);

		//self.raw 'NICK ' + 'YattaIRC'
		//self.raw 'USER ' + 'yatta' + ' 8 * :' + 'Hiro Nakamura'
		serverKey = Object.keys(self.irc.servers).length;
		self.irc.servers[serverKey] = self;

		return serverKey;
	},
	log: function(){
		console.log.apply(this, arguments);
	},
	raw: function(msg){
		var self = this,
			socket = self.socket,
			time = new Date().getTime();
		self.log('[RAW]', msg);

		self.FloodProtection.send(msg);
	},
	pong: function(source){
		var self = this;
		self.raw('PONG ' + source);
	},
	nick: function(nick){
		var self = this;
		self.userNick = nick;
		self.raw('NICK ' + nick);
	},
	user: function(user, mode, realname){
		var self = this;
		self.raw('USER ' + user + ' ' + mode + ' * :' + realname );
	},
	join: function(channel){
		var self = this;
		self.raw('JOIN ' + channel);
	},
	part: function(channel){
		var self = this;
		self.raw('PART ' + channel);
	},
	op: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' +o ' + user);
	},
	deop: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' -o ' + user);
	},
	kick: function(channel, user, message){
		var self = this;
		self.raw('KICK ' + channel + ' ' + user + ':' + message);
	},
	ban: function(channel, user){
		var self = this;
		self.raw('MODE ' + channel + ' +b ' + user);
	},
	message: function(target, message){
		var self = this;
		self.raw('PRIVMSG ' + target + ' :' + message);
	},
	getChannel: function(channel){
		var self = this;
		if(self.channels && self.channels[channel]){
			return self.channels[channel];
		}
		return null;
	},
	getOrInitiateChannel: function(channel){
		var self = this, c;
		if(channel && self.channels && self.channels[channel]){
			return self.channels[channel];
		}else{
			c = new Channel(self, channel);
			self.channels[channel] = c;
			return c;
		}
	}
};
module.exports = Server;
<|endoftext|>


<commit_before>/* global _ */

angular.module('hack4sweden').controller("StartCtrl", function($scope, $rootScope, $log, JobResource, settings) {
  $scope.state = {
    searchTerm: "",
    countyCode: 1,
    data: []
  };

  $scope.counties = settings.countyCodes;

  $scope.search = function() {
    $scope.state.searched = true;
    $rootScope.showLoading(true);
    JobResource.getList($scope.state.searchTerm, $scope.state.countyCode).then(function(response) {
      $rootScope.showLoading(false);
      $scope.state.data = response.data.matchningslista.matchningdata;
    });
  };

  window.scope = $scope;
});
<commit_msg>scroll to top when searching<commit_after>/* global _, window */

angular.module('hack4sweden').controller("StartCtrl", function($scope, $rootScope, $log, JobResource, settings) {
  $scope.state = {
    searchTerm: "",
    countyCode: 1,
    data: []
  };

  $scope.counties = settings.countyCodes;

  $scope.search = function() {
    $scope.state.data = [];
    window.scrollTo(0,0);

    $scope.state.searched = true;
    $rootScope.showLoading(true);
    JobResource.getList($scope.state.searchTerm, $scope.state.countyCode).then(function(response) {
      $rootScope.showLoading(false);
      $scope.state.data = response.data.matchningslista.matchningdata;
    });
  };

  window.scope = $scope;
});
<|endoftext|>


<commit_before>pref("extensions.command_keys.modifiers", "control");<commit_msg>We don't need prefs anymore.<commit_after><|endoftext|>


<commit_before>import _clone from 'lodash-es/clone';
import _groupBy from 'lodash-es/groupBy';
import _reduce from 'lodash-es/reduce';
import _some from 'lodash-es/some';
import _union from 'lodash-es/union';

import { dispatch as d3_dispatch } from 'd3-dispatch';

import { osmEntity } from '../osm';
import { utilRebind } from '../util/rebind';
import {
	utilQsString,
	utilStringQs
} from '../util';

export function rendererFeatures(context) {
    var traffic_roads = {
        'motorway': true,
        'motorway_link': true,
        'trunk': true,
        'trunk_link': true,
        'primary': true,
        'primary_link': true,
        'secondary': true,
        'secondary_link': true,
        'tertiary': true,
        'tertiary_link': true,
        'residential': true,
        'unclassified': true,
        'living_street': true
    };

    var service_roads = {
        'service': true,
        'road': true,
        'track': true
    };

    var paths = {
        'path': true,
        'footway': true,
        'cycleway': true,
        'bridleway': true,
        'steps': true,
        'pedestrian': true,
        'corridor': true
    };

    var past_futures = {
        'proposed': true,
        'construction': true,
        'abandoned': true,
        'dismantled': true,
        'disused': true,
        'razed': true,
        'demolished': true,
        'obliterated': true
    };

    var dispatch = d3_dispatch('change', 'redraw'),
        _cullFactor = 1,
        _cache = {},
        _features = {},
        _stats = {},
        _keys = [],
        _hidden = [];

    function update() {
        if (!window.mocha) {
            var q = utilStringQs(window.location.hash.substring(1));
            var disabled = features.disabled();
            if (disabled.length) {
                q.disable_features = disabled.join(',');
            } else {
                delete q.disable_features;
            }
            window.location.replace('#' + utilQsString(q, true));
            context.storage('disabled-features', disabled.join(','));
        }
        _hidden = features.hidden();
        dispatch.call('change');
        dispatch.call('redraw');
    }

    function defineFeature(k, filter, max) {
        var isEnabled = true;

        _keys.push(k);
        _features[k] = {
            filter: filter,
            enabled: isEnabled,   // whether the user wants it enabled..
            count: 0,
            currentMax: (max || Infinity),
            defaultMax: (max || Infinity),
            enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
            disable: function() { this.enabled = false; this.currentMax = 0; },
            hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
            autoHidden: function() { return this.hidden() && this.currentMax > 0; }
        };
    }

    defineFeature('points', function isPoint(entity, resolver, geometry) {
        return geometry === 'point';
    }, 200);

    defineFeature('traffic_roads', function isTrafficRoad(entity) {
        return traffic_roads[entity.tags.highway];
    });

    defineFeature('service_roads', function isServiceRoad(entity) {
        return service_roads[entity.tags.highway];
    });

    defineFeature('paths', function isPath(entity) {
        return paths[entity.tags.highway];
    });

    defineFeature('buildings', function isBuilding(entity) {
        return (
            !!entity.tags['building:part'] ||
            (!!entity.tags.building && entity.tags.building !== 'no') ||
            entity.tags.parking === 'multi-storey' ||
            entity.tags.parking === 'sheds' ||
            entity.tags.parking === 'carports' ||
            entity.tags.parking === 'garage_boxes'
        );
    }, 250);

    defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
        return geometry === 'area' &&
            !_features.buildings.filter(entity) &&
            !_features.water.filter(entity);
    });

    defineFeature('boundaries', function isBoundary(entity) {
        return (
            !!entity.tags.boundary
        ) && !(
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        );
    });

    defineFeature('water', function isWater(entity) {
        return (
            !!entity.tags.waterway ||
            entity.tags.natural === 'water' ||
            entity.tags.natural === 'coastline' ||
            entity.tags.natural === 'bay' ||
            entity.tags.landuse === 'pond' ||
            entity.tags.landuse === 'basin' ||
            entity.tags.landuse === 'reservoir' ||
            entity.tags.landuse === 'salt_pond'
        );
    });

    defineFeature('rail', function isRail(entity) {
        return (
            !!entity.tags.railway ||
            entity.tags.landuse === 'railway'
        ) && !(
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        );
    });

    defineFeature('power', function isPower(entity) {
        return !!entity.tags.power;
    });

    // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
    defineFeature('past_future', function isPastFuture(entity) {
        if (
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        ) { return false; }

        var strings = Object.keys(entity.tags);

        for (var i = 0; i < strings.length; i++) {
            var s = strings[i];
            if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
        }
        return false;
    });

    // Lines or areas that don't match another feature filter.
    // IMPORTANT: The 'others' feature must be the last one defined,
    //   so that code in getMatches can skip this test if `hasMatch = true`
    defineFeature('others', function isOther(entity, resolver, geometry) {
        return (geometry === 'line' || geometry === 'area');
    });

    function features() {}

    features.features = function() {
        return _features;
    };

    features.keys = function() {
        return _keys;
    };

    features.enabled = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].enabled; });
        }
        return _features[k] && _features[k].enabled;
    };

    features.disabled = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return !_features[k].enabled; });
        }
        return _features[k] && !_features[k].enabled;
    };

    features.hidden = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].hidden(); });
        }
        return _features[k] && _features[k].hidden();
    };

    features.autoHidden = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].autoHidden(); });
        }
        return _features[k] && _features[k].autoHidden();
    };

    features.enable = function(k) {
        if (_features[k] && !_features[k].enabled) {
            _features[k].enable();
            update();
        }
    };

    features.disable = function(k) {
        if (_features[k] && _features[k].enabled) {
            _features[k].disable();
            update();
        }
    };

    features.toggle = function(k) {
        if (_features[k]) {
            (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
            update();
        }
    };

    features.resetStats = function() {
        for (var i = 0; i < _keys.length; i++) {
            _features[_keys[i]].count = 0;
        }
        dispatch.call('change');
    };

    features.gatherStats = function(d, resolver, dimensions) {
        var needsRedraw = false,
            type = _groupBy(d, function(ent) { return ent.type; }),
            entities = [].concat(type.relation || [], type.way || [], type.node || []),
            currHidden, geometry, matches, i, j;

        for (i = 0; i < _keys.length; i++) {
            _features[_keys[i]].count = 0;
        }

        // adjust the threshold for point/building culling based on viewport size..
        // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
        _cullFactor = dimensions[0] * dimensions[1] / 1000000;

        for (i = 0; i < entities.length; i++) {
            geometry = entities[i].geometry(resolver);
            if (!(geometry === 'vertex' || geometry === 'relation')) {
                matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
                for (j = 0; j < matches.length; j++) {
                    _features[matches[j]].count++;
                }
            }
        }

        currHidden = features.hidden();
        if (currHidden !== _hidden) {
            _hidden = currHidden;
            needsRedraw = true;
            dispatch.call('change');
        }

        return needsRedraw;
    };

    features.stats = function() {
        for (var i = 0; i < _keys.length; i++) {
            _stats[_keys[i]] = _features[_keys[i]].count;
        }

        return _stats;
    };

    features.clear = function(d) {
        for (var i = 0; i < d.length; i++) {
            features.clearEntity(d[i]);
        }
    };

    features.clearEntity = function(entity) {
        delete _cache[osmEntity.key(entity)];
    };

    features.reset = function() {
        _cache = {};
    };

    features.getMatches = function(entity, resolver, geometry) {
        if (geometry === 'vertex' || geometry === 'relation') return {};

        var ent = osmEntity.key(entity);
        if (!_cache[ent]) {
            _cache[ent] = {};
        }

        if (!_cache[ent].matches) {
            var matches = {},
                hasMatch = false;

            for (var i = 0; i < _keys.length; i++) {
                if (_keys[i] === 'others') {
                    if (hasMatch) continue;

                    // Multipolygon members:
                    // If an entity...
                    //   1. is a way that hasn't matched other 'interesting' feature rules,
                    //   2. and it belongs to a single parent multipolygon relation
                    // ...then match whatever feature rules the parent multipolygon has matched.
                    // see #2548, #2887
                    //
                    // IMPORTANT:
                    // For this to work, getMatches must be called on relations before ways.
                    //
                    if (entity.type === 'way') {
                        var parents = features.getParents(entity, resolver, geometry);
                        if (parents.length === 1 && parents[0].isMultipolygon()) {
                            var pkey = osmEntity.key(parents[0]);
                            if (_cache[pkey] && _cache[pkey].matches) {
                                matches = _clone(_cache[pkey].matches);
                                continue;
                            }
                        }
                    }
                }

                if (_features[_keys[i]].filter(entity, resolver, geometry)) {
                    matches[_keys[i]] = hasMatch = true;
                }
            }
            _cache[ent].matches = matches;
        }

        return _cache[ent].matches;
    };

    features.getParents = function(entity, resolver, geometry) {
        if (geometry === 'point') return [];

        var ent = osmEntity.key(entity);
        if (!_cache[ent]) {
            _cache[ent] = {};
        }

        if (!_cache[ent].parents) {
            var parents = [];
            if (geometry === 'vertex') {
                parents = resolver.parentWays(entity);
            } else {   // 'line', 'area', 'relation'
                parents = resolver.parentRelations(entity);
            }
            _cache[ent].parents = parents;
        }
        return _cache[ent].parents;
    };

    features.isHiddenFeature = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version) return false;

        var matches = features.getMatches(entity, resolver, geometry);

        for (var i = 0; i < _hidden.length; i++) {
            if (matches[_hidden[i]]) return true;
        }
        return false;
    };

    features.isHiddenChild = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version || geometry === 'point') return false;

        var parents = features.getParents(entity, resolver, geometry);
        if (!parents.length) return false;

        for (var i = 0; i < parents.length; i++) {
            if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
                return false;
            }
        }
        return true;
    };

    features.hasHiddenConnections = function(entity, resolver) {
        if (!_hidden.length) return false;
        var childNodes, connections;

        if (entity.type === 'midpoint') {
            childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
            connections = [];
        } else {
            childNodes = entity.nodes ? resolver.childNodes(entity) : [];
            connections = features.getParents(entity, resolver, entity.geometry(resolver));
        }

        // gather ways connected to child nodes..
        connections = _reduce(childNodes, function(result, e) {
            return resolver.isShared(e) ? _union(result, resolver.parentWays(e)) : result;
        }, connections);

        return connections.length ? _some(connections, function(e) {
            return features.isHidden(e, resolver, e.geometry(resolver));
        }) : false;
    };

    features.isHidden = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version) return false;

        var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
        return fn(entity, resolver, geometry);
    };

    features.filter = function(d, resolver) {
        if (!_hidden.length) return d;

        var result = [];
        for (var i = 0; i < d.length; i++) {
            var entity = d[i];
            if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
                result.push(entity);
            }
        }
        return result;
    };

    features.init = function() {
        var storage = context.storage('disabled-features');
        if (storage) {
            var storageDisabled = storage.replace(/;/g, ',').split(',');
            storageDisabled.forEach(features.disable);
        }

        var q = utilStringQs(window.location.hash.substring(1));
        if (q.disable_features) {
            var hashDisabled = q.disable_features.replace(/;/g, ',').split(',');
            hashDisabled.forEach(features.disable);
        }
    };

    return utilRebind(features, dispatch, 'on');
}
<commit_msg>Add the range filter feature<commit_after>import _clone from 'lodash-es/clone';
import _groupBy from 'lodash-es/groupBy';
import _reduce from 'lodash-es/reduce';
import _some from 'lodash-es/some';
import _union from 'lodash-es/union';

import { dispatch as d3_dispatch } from 'd3-dispatch';

import { osmEntity } from '../osm';
import { utilRebind } from '../util/rebind';
import {
	utilQsString,
	utilStringQs
} from '../util';

export function rendererFeatures(context) {
    var traffic_roads = {
        'motorway': true,
        'motorway_link': true,
        'trunk': true,
        'trunk_link': true,
        'primary': true,
        'primary_link': true,
        'secondary': true,
        'secondary_link': true,
        'tertiary': true,
        'tertiary_link': true,
        'residential': true,
        'unclassified': true,
        'living_street': true
    };

    var service_roads = {
        'service': true,
        'road': true,
        'track': true
    };

    var paths = {
        'path': true,
        'footway': true,
        'cycleway': true,
        'bridleway': true,
        'steps': true,
        'pedestrian': true,
        'corridor': true
    };

    var past_futures = {
        'proposed': true,
        'construction': true,
        'abandoned': true,
        'dismantled': true,
        'disused': true,
        'razed': true,
        'demolished': true,
        'obliterated': true
    };

    var dispatch = d3_dispatch('change', 'redraw'),
        _cullFactor = 1,
        _cache = {},
        _features = {},
        _stats = {},
        _keys = [],
        _hidden = [];

    function update() {
        if (!window.mocha) {
            var q = utilStringQs(window.location.hash.substring(1));
            var disabled = features.disabled();
            if (disabled.length) {
                q.disable_features = disabled.join(',');
            } else {
                delete q.disable_features;
            }
            window.location.replace('#' + utilQsString(q, true));
            context.storage('disabled-features', disabled.join(','));
        }
        _hidden = features.hidden();
        dispatch.call('change');
        dispatch.call('redraw');
    }

    function defineFeature(k, filter, max) {
        var isEnabled = true;

        _keys.push(k);
        _features[k] = {
            filter: filter,
            enabled: isEnabled,   // whether the user wants it enabled..
            count: 0,
            currentMax: (max || Infinity),
            defaultMax: (max || Infinity),
            enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
            disable: function() { this.enabled = false; this.currentMax = 0; },
            hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
            autoHidden: function() { return this.hidden() && this.currentMax > 0; }
        };
    }

    defineFeature('date_range', function isWithinRange(entity) {

      // Function to parse time (YYYY-MM-DD) to an INT
      var valueToInt = function(value) {
        return (typeof value === 'string') ?
          parseInt(value.replace(/-/g, ''), 10) :
          0;
      }

      // Convert the date range from the entity to a number
      entity.tags = entity.tags || {};
      var entityRange = {
        'start_date': valueToInt(entity.tags['start_date']),
        'end_date': valueToInt(entity.tags['end_date'])
      };

      // These will be pulled from the on-screen controls
      // Maybe put them in browser storage?
      var selectedRange = {
        'start_date': -Infinity, //valueToInt(entity['start_date']),
        'end_date': Infinity // valueToInt(entity['end_date'])
      }
      if (context.features().dateRange) {
        selectedRange.start_date = context.features().dateRange[0];
        selectedRange.end_date = context.features().dateRange[1];
      }

      // console.log('v---------------------------------------------v');
      // console.log('selectedRange', selectedRange);
      // console.log('entityRange', entityRange);
      // console.log('Return', !((selectedRange['start_date'] <= entityRange['end_date']) && (entityRange['start_date'] <= selectedRange['end_date'])));
      // console.log('^---------------------------------------------^');

      // Within range if:
      //    the entity started before the selected range started
      //    and also ended after the selected range started
      return !((selectedRange['start_date'] <= entityRange['end_date']) &&
        (entityRange['start_date'] <= selectedRange['end_date']));

    });

    defineFeature('points', function isPoint(entity, resolver, geometry) {
        return geometry === 'point';
    }, 200);

    defineFeature('traffic_roads', function isTrafficRoad(entity) {
        return traffic_roads[entity.tags.highway];
    });

    defineFeature('service_roads', function isServiceRoad(entity) {
        return service_roads[entity.tags.highway];
    });

    defineFeature('paths', function isPath(entity) {
        return paths[entity.tags.highway];
    });

    defineFeature('buildings', function isBuilding(entity) {
        return (
            !!entity.tags['building:part'] ||
            (!!entity.tags.building && entity.tags.building !== 'no') ||
            entity.tags.parking === 'multi-storey' ||
            entity.tags.parking === 'sheds' ||
            entity.tags.parking === 'carports' ||
            entity.tags.parking === 'garage_boxes'
        );
    }, 250);

    defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
        return geometry === 'area' &&
            !_features.buildings.filter(entity) &&
            !_features.water.filter(entity);
    });

    defineFeature('boundaries', function isBoundary(entity) {
        return (
            !!entity.tags.boundary
        ) && !(
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        );
    });

    defineFeature('water', function isWater(entity) {
        return (
            !!entity.tags.waterway ||
            entity.tags.natural === 'water' ||
            entity.tags.natural === 'coastline' ||
            entity.tags.natural === 'bay' ||
            entity.tags.landuse === 'pond' ||
            entity.tags.landuse === 'basin' ||
            entity.tags.landuse === 'reservoir' ||
            entity.tags.landuse === 'salt_pond'
        );
    });

    defineFeature('rail', function isRail(entity) {
        return (
            !!entity.tags.railway ||
            entity.tags.landuse === 'railway'
        ) && !(
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        );
    });

    defineFeature('power', function isPower(entity) {
        return !!entity.tags.power;
    });

    // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
    defineFeature('past_future', function isPastFuture(entity) {
        if (
            traffic_roads[entity.tags.highway] ||
            service_roads[entity.tags.highway] ||
            paths[entity.tags.highway]
        ) { return false; }

        var strings = Object.keys(entity.tags);

        for (var i = 0; i < strings.length; i++) {
            var s = strings[i];
            if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
        }
        return false;
    });

    // Lines or areas that don't match another feature filter.
    // IMPORTANT: The 'others' feature must be the last one defined,
    //   so that code in getMatches can skip this test if `hasMatch = true`
    defineFeature('others', function isOther(entity, resolver, geometry) {
        return (geometry === 'line' || geometry === 'area');
    });

    function features() {}

    features.features = function() {
        return _features;
    };

    features.keys = function() {
        return _keys;
    };

    features.enabled = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].enabled; });
        }
        return _features[k] && _features[k].enabled;
    };

    features.disabled = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return !_features[k].enabled; });
        }
        return _features[k] && !_features[k].enabled;
    };

    features.hidden = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].hidden(); });
        }
        return _features[k] && _features[k].hidden();
    };

    features.autoHidden = function(k) {
        if (!arguments.length) {
            return _keys.filter(function(k) { return _features[k].autoHidden(); });
        }
        return _features[k] && _features[k].autoHidden();
    };

    features.enable = function(k) {
        if (_features[k] && !_features[k].enabled) {
            _features[k].enable();
            update();
        }
    };

    features.disable = function(k) {
        if (_features[k] && _features[k].enabled) {
            _features[k].disable();
            update();
        }
    };

    features.toggle = function(k) {
        if (_features[k]) {
            (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
            update();
        }
    };

    features.resetStats = function() {
        for (var i = 0; i < _keys.length; i++) {
            _features[_keys[i]].count = 0;
        }
        dispatch.call('change');
    };

    features.gatherStats = function(d, resolver, dimensions) {
        var needsRedraw = false,
            type = _groupBy(d, function(ent) { return ent.type; }),
            entities = [].concat(type.relation || [], type.way || [], type.node || []),
            currHidden, geometry, matches, i, j;

        for (i = 0; i < _keys.length; i++) {
            _features[_keys[i]].count = 0;
        }

        // adjust the threshold for point/building culling based on viewport size..
        // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
        _cullFactor = dimensions[0] * dimensions[1] / 1000000;

        for (i = 0; i < entities.length; i++) {
            geometry = entities[i].geometry(resolver);
            if (!(geometry === 'vertex' || geometry === 'relation')) {
                matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
                for (j = 0; j < matches.length; j++) {
                    _features[matches[j]].count++;
                }
            }
        }

        currHidden = features.hidden();
        if (currHidden !== _hidden) {
            _hidden = currHidden;
            needsRedraw = true;
            dispatch.call('change');
        }

        return needsRedraw;
    };

    features.stats = function() {
        for (var i = 0; i < _keys.length; i++) {
            _stats[_keys[i]] = _features[_keys[i]].count;
        }

        return _stats;
    };

    features.clear = function(d) {
        for (var i = 0; i < d.length; i++) {
            features.clearEntity(d[i]);
        }
    };

    features.clearEntity = function(entity) {
        delete _cache[osmEntity.key(entity)];
    };

    features.reset = function() {
        _cache = {};
    };

    features.getMatches = function(entity, resolver, geometry) {
        if (geometry === 'vertex' || geometry === 'relation') return {};

        var ent = osmEntity.key(entity);
        if (!_cache[ent]) {
            _cache[ent] = {};
        }

        if (!_cache[ent].matches) {
            var matches = {},
                hasMatch = false;

            for (var i = 0; i < _keys.length; i++) {
                if (_keys[i] === 'others') {
                    if (hasMatch) continue;

                    // Multipolygon members:
                    // If an entity...
                    //   1. is a way that hasn't matched other 'interesting' feature rules,
                    //   2. and it belongs to a single parent multipolygon relation
                    // ...then match whatever feature rules the parent multipolygon has matched.
                    // see #2548, #2887
                    //
                    // IMPORTANT:
                    // For this to work, getMatches must be called on relations before ways.
                    //
                    if (entity.type === 'way') {
                        var parents = features.getParents(entity, resolver, geometry);
                        if (parents.length === 1 && parents[0].isMultipolygon()) {
                            var pkey = osmEntity.key(parents[0]);
                            if (_cache[pkey] && _cache[pkey].matches) {
                                matches = _clone(_cache[pkey].matches);
                                continue;
                            }
                        }
                    }
                }

                if (_features[_keys[i]].filter(entity, resolver, geometry)) {
                    matches[_keys[i]] = hasMatch = true;
                }
            }
            _cache[ent].matches = matches;
        }

        return _cache[ent].matches;
    };

    features.getParents = function(entity, resolver, geometry) {
        if (geometry === 'point') return [];

        var ent = osmEntity.key(entity);
        if (!_cache[ent]) {
            _cache[ent] = {};
        }

        if (!_cache[ent].parents) {
            var parents = [];
            if (geometry === 'vertex') {
                parents = resolver.parentWays(entity);
            } else {   // 'line', 'area', 'relation'
                parents = resolver.parentRelations(entity);
            }
            _cache[ent].parents = parents;
        }
        return _cache[ent].parents;
    };

    features.isHiddenFeature = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version) return false;

        var matches = features.getMatches(entity, resolver, geometry);

        for (var i = 0; i < _hidden.length; i++) {
            if (matches[_hidden[i]]) return true;
        }
        return false;
    };

    features.isHiddenChild = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version || geometry === 'point') return false;

        var parents = features.getParents(entity, resolver, geometry);
        if (!parents.length) return false;

        for (var i = 0; i < parents.length; i++) {
            if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
                return false;
            }
        }
        return true;
    };

    features.hasHiddenConnections = function(entity, resolver) {
        if (!_hidden.length) return false;
        var childNodes, connections;

        if (entity.type === 'midpoint') {
            childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
            connections = [];
        } else {
            childNodes = entity.nodes ? resolver.childNodes(entity) : [];
            connections = features.getParents(entity, resolver, entity.geometry(resolver));
        }

        // gather ways connected to child nodes..
        connections = _reduce(childNodes, function(result, e) {
            return resolver.isShared(e) ? _union(result, resolver.parentWays(e)) : result;
        }, connections);

        return connections.length ? _some(connections, function(e) {
            return features.isHidden(e, resolver, e.geometry(resolver));
        }) : false;
    };

    features.isHidden = function(entity, resolver, geometry) {
        if (!_hidden.length) return false;
        if (!entity.version) return false;

        var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
        return fn(entity, resolver, geometry);
    };

    features.filter = function(d, resolver) {
        if (!_hidden.length) return d;

        var result = [];
        for (var i = 0; i < d.length; i++) {
            var entity = d[i];
            if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
                result.push(entity);
            }
        }
        return result;
    };

    features.init = function() {
        var storage = context.storage('disabled-features');
        if (storage) {
            var storageDisabled = storage.replace(/;/g, ',').split(',');
            storageDisabled.forEach(features.disable);
        }

        var q = utilStringQs(window.location.hash.substring(1));
        if (q.disable_features) {
            var hashDisabled = q.disable_features.replace(/;/g, ',').split(',');
            hashDisabled.forEach(features.disable);
        }
    };

    return utilRebind(features, dispatch, 'on');
}
<|endoftext|>


<commit_before>var args = $.args;
<commit_msg>Add comment<commit_after>// Arguments passed into this controller can be accessed off of the `$.args` object directly or:
var args = $.args;
<|endoftext|>


<commit_before>"use strict";
var conn = require('./ForceConnection.js').connection;
var listCustomObjects = require('./forceModel/listCustomObjects.js');
var describeSObject = require('./forceModel/describeSObject.js');
var logger = require('../services/logger.js');

var init = function(apiModel) {
    apiModel.registerSecuredRoute('get'
        , 'dashboard'
        , '/dashboard'
        , dashboard
        , {
            'token':   {'required': true, 'dataType': 'string'}
          }
        , 'View dashboard');
}

var dashboard =function(req, res, next) {
    listCustomObjects.init(conn);

    //describeSObject.init(conn);
    function render(result) {
        //var a = describeSObject.execute(result[0].name);
        res.render('dashboard', {
            title: 'Dashboard',
            token: req.query.token,
            objects: result
        });
    }
    var p = listCustomObjects.execute();

    p.then(function(objects) {
        var objectMap = {};

        objects.map(function(x) {
            if (!objectMap[x.name]) {
                objectMap[x.name] = x;
            }
        });

        var results = objects.map(function(x) {
            var customChildren = [];
            for (var i  in x.childRelationships) {
                if (objectMap[x.childRelationships[i].childSObject]) {
                    customChildren.push(x.childRelationships[i]);
                }
            }
            x.childRelationships = customChildren;
            return x;
        });

        render(results)
    });
}

exports.init = init;
<commit_msg>Add uri to object for describing sObject.  Route to be implemented in #7<commit_after>"use strict";
var conn = require('./ForceConnection.js').connection;
var listCustomObjects = require('./forceModel/listCustomObjects.js');
var describeSObject = require('./forceModel/describeSObject.js');
var logger = require('../services/logger.js');

var init = function(apiModel) {
    apiModel.registerSecuredRoute('get'
        , 'dashboard'
        , '/dashboard'
        , dashboard
        , {
            'token':   {'required': true, 'dataType': 'string'}
          }
        , 'View dashboard');
}

var dashboard =function(req, res, next) {
    listCustomObjects.init(conn);

    //describeSObject.init(conn);
    function render(result) {
        //var a = describeSObject.execute(result[0].name);
        res.render('dashboard', {
            title: 'Dashboard',
            token: req.query.token,
            objects: result
        });
    }
    var p = listCustomObjects.execute();

    p.then(function(objects) {
        var objectMap = {};

        objects.map(function(x) {
            x.describeUri = '/describe/' + x.name + '?token=' + req.query.token;
            if (!objectMap[x.name]) {
                objectMap[x.name] = x;
            }
        });

        var results = objects.map(function(x) {
            var customChildren = [];
            for (var i  in x.childRelationships) {
                if (objectMap[x.childRelationships[i].childSObject]) {
                    customChildren.push(x.childRelationships[i]);
                }
            }
            x.childRelationships = customChildren;
            return x;
        });

        render(results)
    });
}

exports.init = init;
<|endoftext|>


<commit_before>// https://github.com/michael-ciniawsky/postcss-load-config

module.exports = {
  "plugins": {
    // to edit target browsers: use "browserslist" field in package.json
    "autoprefixer": {}
  }
}
<commit_msg>preetify postcss config<commit_after>// https://github.com/michael-ciniawsky/postcss-load-config

module.exports = {
  plugins: {
    // to edit target browsers: use "browserslist" field in package.json
    'autoprefixer': {}
  }
}
<|endoftext|>