<commit_before>const textrApostrophes = require('typographic-apostrophes')
const textrApostrophesForPlurals = require('typographic-apostrophes-for-possessive-plurals')
const textrCopyright = require('typographic-copyright')
const textrEllipses = require('typographic-ellipses')
const textrEmDashes = require('typographic-em-dashes')
const textrEnDashes = require('typographic-en-dashes')
const textrRegisteredTrademark = require('typographic-registered-trademark')
const textrSingleSpaces = require('typographic-single-spaces')
const textrTrademark = require('typographic-trademark')

const textrColon = require('typographic-colon/src')
const textrEmDash = require('typographic-em-dash/src')
const textrExclamationMark = require('typographic-exclamation-mark/src')
const textrGuillemets = require('typographic-guillemets/src')
const textrPercent = require('typographic-percent/src')
const textrPermille = require('typographic-permille/src')
const textrQuestionMark = require('typographic-question-mark/src')
const textrSemicolon = require('typographic-semicolon/src')

const remarkConfig = {
  maxNesting: 100,
  reParse: {
    gfm: true,
    commonmark: false,
    footnotes: true,
    pedantic: false,
    /* sets list of known blocks to nothing, otherwise
hey
 would become
    <h3>hey</h3> instead of
<h3>hey</h3>
 */
    blocks: [],
  },

  textr: {
    plugins: [
      textrApostrophes,
      textrApostrophesForPlurals,
      textrEllipses,
      textrEmDashes,
      textrEnDashes,
      textrCopyright,
      textrRegisteredTrademark,
      textrSingleSpaces,
      textrTrademark,

      textrColon,
      textrEmDash,
      textrExclamationMark,
      textrGuillemets,
      textrPercent,
      textrPermille,
      textrQuestionMark,
      textrSemicolon,
    ],
    options: {
      locale: 'fr',
    },
  },

  autolinkHeadings: {
    behaviour: 'append',
  },

  headingShifter: 0,

  remark2rehype: {
    allowDangerousHTML: true,
  },

  rehypeHighlight: {
    ignoreMissing: true,
    plainText: ['console'],
    aliases: {tex: ['latex']},
  },

  footnotesTitles: 'Retourner au texte de la note $id',

  alignBlocks: {
    center: 'align-center',
    right: 'align-right',
  },

  customBlocks: {
    secret: {
      classes: 'custom-block-spoiler',
      title: 'optional',
    },
    s: {
      classes: 'custom-block-spoiler',
      title: 'optional',
    },
    information: {
      classes: 'custom-block-information',
      title: 'optional',
    },
    i: {
      classes: 'custom-block-information',
      title: 'optional',
    },
    question: {
      classes: 'custom-block-question',
      title: 'optional',
    },
    q: {
      classes: 'custom-block-question',
      title: 'optional',
    },
    attention: {
      classes: 'custom-block-warning',
      title: 'optional',
    },
    a: {
      classes: 'custom-block-warning',
      title: 'optional',
    },
    erreur: {
      classes: 'custom-block-error',
      title: 'optional',
    },
    e: {
      classes: 'custom-block-error',
      title: 'optional',
    },
    neutre: {
      classes: 'custom-block-neutral',
      title: 'required',
    },
    n: {
      classes: 'custom-block-neutral',
      title: 'required',
    },
  },

  escapeEscaped: ['&'],

  emoticons: {
    emoticons: {
      ':ange:': '/static/smileys/ange.png',
      ':colere:': '/static/smileys/angry.gif',
      'o_O': '/static/smileys/blink.gif',
      ';)': '/static/smileys/clin.png',
      ':diable:': '/static/smileys/diable.png',
      ':D': '/static/smileys/heureux.png',
      '^^': '/static/smileys/hihi.png',
      ':o': '/static/smileys/huh.png',
      ':p': '/static/smileys/langue.png',
      ':magicien:': '/static/smileys/magicien.png',
      ':colere2:': '/static/smileys/mechant.png',
      ':ninja:': '/static/smileys/ninja.png',
      'x(': '/static/smileys/pinch.png',
      '>_<': '/static/smileys/pinch.png',
      'X/': '/static/smileys/pinch.png',
      ':pirate:': '/static/smileys/pirate.png',
      ":'(": '/static/smileys/pleure.png',
      ':lol:': '/static/smileys/rire.gif',
      ':honte:': '/static/smileys/rouge.png',
      ':-°': '/static/smileys/siffle.png',
      ':)': '/static/smileys/smile.png',
      ':soleil:': '/static/smileys/soleil.png',
      ':(': '/static/smileys/triste.png',
      ':euh:': '/static/smileys/unsure.gif',
      ':waw:': '/static/smileys/waw.png',
      ':zorro:': '/static/smileys/zorro.png',
      '^(;,;)^': '/static/smileys/cthulhu.png',
    },
    classes: 'smiley',
  },

  math: {
    inlineMathDouble: true,
  },

  katex: {
    inlineMathDoubleDisplay: true,
  },

  iframes: {
    'www.dailymotion.com': {
      tag: 'iframe',
      width: 480,
      height: 270,
      disabled: false,
      replace: [
        ['video/', 'embed/video/'],
      ],
      thumbnail: {
        format: 'http://www.dailymotion.com/thumbnail/video/{id}',
        id: '.+/(.+)$',
      },
    },
    'www.vimeo.com': {
      tag: 'iframe',
      width: 500,
      height: 281,
      disabled: false,
      replace: [
        ['http://', 'https://'],
        ['www.', ''],
        ['vimeo.com/', 'player.vimeo.com/video/'],
      ],
    },
    'vimeo.com': {
      tag: 'iframe',
      width: 500,
      height: 281,
      disabled: false,
      replace: [
        ['http://', 'https://'],
        ['www.', ''],
        ['vimeo.com/', 'player.vimeo.com/video/'],
      ],
    },
    'www.youtube.com': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['http://', 'https://'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      droppedQueryParameters: ['feature'],
      removeAfter: '&',
    },
    'youtube.com': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['http://', 'https://'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      droppedQueryParameters: ['feature'],
      removeAfter: '&',
    },
    'youtu.be': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['youtu.be', 'www.youtube.com/embed'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      droppedQueryParameters: ['feature'],
      removeAfter: '&',
    },
    'www.ina.fr': {
      tag: 'iframe',
      width: 620,
      height: 349,
      disabled: false,
      replace: [
        ['www.', 'player.'],
        ['/video/', '/player/embed/'],
      ],
      append: '/1/1b0bd203fbcd702f9bc9b10ac3d0fc21/560/315/1/148db8',
      removeFileName: true,
    },
    'www.jsfiddle.net': {
      tag: 'iframe',
      width: 560,
      height: 560,
      disabled: false,
      replace: [
        ['http://', 'https://'],
      ],
      append: 'embedded/result,js,html,css/',
      match: /https?:\/\/(www\.)?jsfiddle\.net\/([\w\d]+\/[\w\d]+\/\d+\/?|[\w\d]+\/\d+\/?|[\w\d]+\/?)$/,
      thumbnail: {
        format: 'http://www.unixstickers.com/image/data/stickers' +
        '/jsfiddle/JSfiddle-blue-w-type.sh.png',
      },
    },
    'jsfiddle.net': {
      tag: 'iframe',
      width: 560,
      height: 560,
      disabled: false,
      replace: [
        ['http://', 'https://'],
      ],
      append: 'embedded/result,js,html,css/',
      match: /https?:\/\/(www\.)?jsfiddle\.net\/([\w\d]+\/[\w\d]+\/\d+\/?|[\w\d]+\/\d+\/?|[\w\d]+\/?)$/,
      thumbnail: {
        format: 'http://www.unixstickers.com/image/data/stickers' +
        '/jsfiddle/JSfiddle-blue-w-type.sh.png',
      },
    },
  },

  captions: {
    external: {
      table: 'Table:',
      gridTable: 'Table:',
      code: 'Code:',
      math: 'Equation:',
      iframe: 'Video:',
    },
    internal: {
      math: 'Equation:',
      inlineMath: 'Equation:',
      image: 'Figure:',
    },
  },

  ping: {
    pingUsername: (_username) => true,
    userURL: (username) => `/membres/voir/${username}/`,
    usernameRegex: /\B@(?:\*\*([^*]+)\*\*|(\w+))/,
  },

  disableTokenizers: {},

  imagesDownload: {
    disabled: true,
    downloadDestination: './img/',
    maxlength: 1000000,
    dirSizeLimit: 10000000,
  },
}

module.exports = remarkConfig
<commit_msg>fix(remark-iframes): Fix youtube link client side<commit_after>const textrApostrophes = require('typographic-apostrophes')
const textrApostrophesForPlurals = require('typographic-apostrophes-for-possessive-plurals')
const textrCopyright = require('typographic-copyright')
const textrEllipses = require('typographic-ellipses')
const textrEmDashes = require('typographic-em-dashes')
const textrEnDashes = require('typographic-en-dashes')
const textrRegisteredTrademark = require('typographic-registered-trademark')
const textrSingleSpaces = require('typographic-single-spaces')
const textrTrademark = require('typographic-trademark')

const textrColon = require('typographic-colon/src')
const textrEmDash = require('typographic-em-dash/src')
const textrExclamationMark = require('typographic-exclamation-mark/src')
const textrGuillemets = require('typographic-guillemets/src')
const textrPercent = require('typographic-percent/src')
const textrPermille = require('typographic-permille/src')
const textrQuestionMark = require('typographic-question-mark/src')
const textrSemicolon = require('typographic-semicolon/src')

const remarkConfig = {
  maxNesting: 100,
  reParse: {
    gfm: true,
    commonmark: false,
    footnotes: true,
    pedantic: false,
    /* sets list of known blocks to nothing, otherwise
hey
 would become
    <h3>hey</h3> instead of
<h3>hey</h3>
 */
    blocks: [],
  },

  textr: {
    plugins: [
      textrApostrophes,
      textrApostrophesForPlurals,
      textrEllipses,
      textrEmDashes,
      textrEnDashes,
      textrCopyright,
      textrRegisteredTrademark,
      textrSingleSpaces,
      textrTrademark,

      textrColon,
      textrEmDash,
      textrExclamationMark,
      textrGuillemets,
      textrPercent,
      textrPermille,
      textrQuestionMark,
      textrSemicolon,
    ],
    options: {
      locale: 'fr',
    },
  },

  autolinkHeadings: {
    behaviour: 'append',
  },

  headingShifter: 0,

  remark2rehype: {
    allowDangerousHTML: true,
  },

  rehypeHighlight: {
    ignoreMissing: true,
    plainText: ['console'],
    aliases: {tex: ['latex']},
  },

  footnotesTitles: 'Retourner au texte de la note $id',

  alignBlocks: {
    center: 'align-center',
    right: 'align-right',
  },

  customBlocks: {
    secret: {
      classes: 'custom-block-spoiler',
      title: 'optional',
    },
    s: {
      classes: 'custom-block-spoiler',
      title: 'optional',
    },
    information: {
      classes: 'custom-block-information',
      title: 'optional',
    },
    i: {
      classes: 'custom-block-information',
      title: 'optional',
    },
    question: {
      classes: 'custom-block-question',
      title: 'optional',
    },
    q: {
      classes: 'custom-block-question',
      title: 'optional',
    },
    attention: {
      classes: 'custom-block-warning',
      title: 'optional',
    },
    a: {
      classes: 'custom-block-warning',
      title: 'optional',
    },
    erreur: {
      classes: 'custom-block-error',
      title: 'optional',
    },
    e: {
      classes: 'custom-block-error',
      title: 'optional',
    },
    neutre: {
      classes: 'custom-block-neutral',
      title: 'required',
    },
    n: {
      classes: 'custom-block-neutral',
      title: 'required',
    },
  },

  escapeEscaped: ['&'],

  emoticons: {
    emoticons: {
      ':ange:': '/static/smileys/ange.png',
      ':colere:': '/static/smileys/angry.gif',
      'o_O': '/static/smileys/blink.gif',
      ';)': '/static/smileys/clin.png',
      ':diable:': '/static/smileys/diable.png',
      ':D': '/static/smileys/heureux.png',
      '^^': '/static/smileys/hihi.png',
      ':o': '/static/smileys/huh.png',
      ':p': '/static/smileys/langue.png',
      ':magicien:': '/static/smileys/magicien.png',
      ':colere2:': '/static/smileys/mechant.png',
      ':ninja:': '/static/smileys/ninja.png',
      'x(': '/static/smileys/pinch.png',
      '>_<': '/static/smileys/pinch.png',
      'X/': '/static/smileys/pinch.png',
      ':pirate:': '/static/smileys/pirate.png',
      ":'(": '/static/smileys/pleure.png',
      ':lol:': '/static/smileys/rire.gif',
      ':honte:': '/static/smileys/rouge.png',
      ':-°': '/static/smileys/siffle.png',
      ':)': '/static/smileys/smile.png',
      ':soleil:': '/static/smileys/soleil.png',
      ':(': '/static/smileys/triste.png',
      ':euh:': '/static/smileys/unsure.gif',
      ':waw:': '/static/smileys/waw.png',
      ':zorro:': '/static/smileys/zorro.png',
      '^(;,;)^': '/static/smileys/cthulhu.png',
    },
    classes: 'smiley',
  },

  math: {
    inlineMathDouble: true,
  },

  katex: {
    inlineMathDoubleDisplay: true,
  },

  iframes: {
    'www.dailymotion.com': {
      tag: 'iframe',
      width: 480,
      height: 270,
      disabled: false,
      replace: [
        ['video/', 'embed/video/'],
      ],
      thumbnail: {
        format: 'http://www.dailymotion.com/thumbnail/video/{id}',
        id: '.+/(.+)$',
      },
    },
    'www.vimeo.com': {
      tag: 'iframe',
      width: 500,
      height: 281,
      disabled: false,
      replace: [
        ['http://', 'https://'],
        ['www.', ''],
        ['vimeo.com/', 'player.vimeo.com/video/'],
      ],
    },
    'vimeo.com': {
      tag: 'iframe',
      width: 500,
      height: 281,
      disabled: false,
      replace: [
        ['http://', 'https://'],
        ['www.', ''],
        ['vimeo.com/', 'player.vimeo.com/video/'],
      ],
    },
    'www.youtube.com': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['http://', 'https://'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      removeAfter: '&',
    },
    'youtube.com': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['http://', 'https://'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      removeAfter: '&',
    },
    'youtu.be': {
      tag: 'iframe',
      width: 560,
      height: 315,
      disabled: false,
      replace: [
        ['watch?v=', 'embed/'],
        ['youtu.be', 'www.youtube.com/embed'],
      ],
      thumbnail: {
        format: 'http://img.youtube.com/vi/{id}/0.jpg',
        id: '.+/(.+)$',
      },
      removeAfter: '&',
    },
    'www.ina.fr': {
      tag: 'iframe',
      width: 620,
      height: 349,
      disabled: false,
      replace: [
        ['www.', 'player.'],
        ['/video/', '/player/embed/'],
      ],
      append: '/1/1b0bd203fbcd702f9bc9b10ac3d0fc21/560/315/1/148db8',
      removeFileName: true,
    },
    'www.jsfiddle.net': {
      tag: 'iframe',
      width: 560,
      height: 560,
      disabled: false,
      replace: [
        ['http://', 'https://'],
      ],
      append: 'embedded/result,js,html,css/',
      match: /https?:\/\/(www\.)?jsfiddle\.net\/([\w\d]+\/[\w\d]+\/\d+\/?|[\w\d]+\/\d+\/?|[\w\d]+\/?)$/,
      thumbnail: {
        format: 'http://www.unixstickers.com/image/data/stickers' +
        '/jsfiddle/JSfiddle-blue-w-type.sh.png',
      },
    },
    'jsfiddle.net': {
      tag: 'iframe',
      width: 560,
      height: 560,
      disabled: false,
      replace: [
        ['http://', 'https://'],
      ],
      append: 'embedded/result,js,html,css/',
      match: /https?:\/\/(www\.)?jsfiddle\.net\/([\w\d]+\/[\w\d]+\/\d+\/?|[\w\d]+\/\d+\/?|[\w\d]+\/?)$/,
      thumbnail: {
        format: 'http://www.unixstickers.com/image/data/stickers' +
        '/jsfiddle/JSfiddle-blue-w-type.sh.png',
      },
    },
  },

  captions: {
    external: {
      table: 'Table:',
      gridTable: 'Table:',
      code: 'Code:',
      math: 'Equation:',
      iframe: 'Video:',
    },
    internal: {
      math: 'Equation:',
      inlineMath: 'Equation:',
      image: 'Figure:',
    },
  },

  ping: {
    pingUsername: (_username) => true,
    userURL: (username) => `/membres/voir/${username}/`,
    usernameRegex: /\B@(?:\*\*([^*]+)\*\*|(\w+))/,
  },

  disableTokenizers: {},

  imagesDownload: {
    disabled: true,
    downloadDestination: './img/',
    maxlength: 1000000,
    dirSizeLimit: 10000000,
  },
}

module.exports = remarkConfig
<|endoftext|>


<commit_before>/* jshint node: true */
/* global window: false */
/* global navigator: false */

'use strict';

var browsers = {
  chrome: /Chrom(?:e|ium)\/([0-9]+)\./,
  firefox: /Firefox\/([0-9]+)\./,
  opera: /Opera\/([0-9]+)\./
};

/**
## rtc-core/detect

A browser detection helper for accessing prefix-free versions of the various
WebRTC types.

### Example Usage

If you wanted to get the native `RTCPeerConnection` prototype in any browser
you could do the following:

```js
var detect = require('rtc-core/detect'); // also available in rtc/detect
var RTCPeerConnection = detect('RTCPeerConnection');
```

This would provide whatever the browser prefixed version of the
RTCPeerConnection is available (`webkitRTCPeerConnection`,
`mozRTCPeerConnection`, etc).
**/
var detect = module.exports = function(target, prefixes) {
  var prefixIdx;
  var prefix;
  var testName;
  var hostObject = this || (typeof window != 'undefined' ? window : undefined);

  // if we have no host object, then abort
  if (! hostObject) {
    return;
  }

  // initialise to default prefixes
  // (reverse order as we use a decrementing for loop)
  prefixes = (prefixes || ['ms', 'o', 'moz', 'webkit']).concat('');

  // iterate through the prefixes and return the class if found in global
  for (prefixIdx = prefixes.length; prefixIdx--; ) {
    prefix = prefixes[prefixIdx];

    // construct the test class name
    // if we have a prefix ensure the target has an uppercase first character
    // such that a test for getUserMedia would result in a
    // search for webkitGetUserMedia
    testName = prefix + (prefix ?
                            target.charAt(0).toUpperCase() + target.slice(1) :
                            target);

    if (typeof hostObject[testName] != 'undefined') {
      // update the last used prefix
      detect.browser = detect.browser || prefix.toLowerCase();

      // return the host object member
      return hostObject[target] = hostObject[testName];
    }
  }
};

// detect mozilla (yes, this feels dirty)
detect.moz = typeof navigator != 'undefined' && !!navigator.mozGetUserMedia;

// time to do some useragent sniffing - it feels dirty because it is :/
if (typeof navigator != 'undefined') {
  Object.keys(browsers).forEach(function(key) {
    var match = browsers[key].exec(navigator.userAgent);
    if (match) {
      detect.browser = key;
      detect.version = parseInt(match[1], 10);
    }
  });
}
else {
  detect.browser = 'node';
  detect.version = '?'; // TODO: get node version
}<commit_msg>Rename attribute to browserVersion for version detection<commit_after>/* jshint node: true */
/* global window: false */
/* global navigator: false */

'use strict';

var browsers = {
  chrome: /Chrom(?:e|ium)\/([0-9]+)\./,
  firefox: /Firefox\/([0-9]+)\./,
  opera: /Opera\/([0-9]+)\./
};

/**
## rtc-core/detect

A browser detection helper for accessing prefix-free versions of the various
WebRTC types.

### Example Usage

If you wanted to get the native `RTCPeerConnection` prototype in any browser
you could do the following:

```js
var detect = require('rtc-core/detect'); // also available in rtc/detect
var RTCPeerConnection = detect('RTCPeerConnection');
```

This would provide whatever the browser prefixed version of the
RTCPeerConnection is available (`webkitRTCPeerConnection`,
`mozRTCPeerConnection`, etc).
**/
var detect = module.exports = function(target, prefixes) {
  var prefixIdx;
  var prefix;
  var testName;
  var hostObject = this || (typeof window != 'undefined' ? window : undefined);

  // if we have no host object, then abort
  if (! hostObject) {
    return;
  }

  // initialise to default prefixes
  // (reverse order as we use a decrementing for loop)
  prefixes = (prefixes || ['ms', 'o', 'moz', 'webkit']).concat('');

  // iterate through the prefixes and return the class if found in global
  for (prefixIdx = prefixes.length; prefixIdx--; ) {
    prefix = prefixes[prefixIdx];

    // construct the test class name
    // if we have a prefix ensure the target has an uppercase first character
    // such that a test for getUserMedia would result in a
    // search for webkitGetUserMedia
    testName = prefix + (prefix ?
                            target.charAt(0).toUpperCase() + target.slice(1) :
                            target);

    if (typeof hostObject[testName] != 'undefined') {
      // update the last used prefix
      detect.browser = detect.browser || prefix.toLowerCase();

      // return the host object member
      return hostObject[target] = hostObject[testName];
    }
  }
};

// detect mozilla (yes, this feels dirty)
detect.moz = typeof navigator != 'undefined' && !!navigator.mozGetUserMedia;

// time to do some useragent sniffing - it feels dirty because it is :/
if (typeof navigator != 'undefined') {
  Object.keys(browsers).forEach(function(key) {
    var match = browsers[key].exec(navigator.userAgent);
    if (match) {
      detect.browser = key;
      detect.browserVersion = detect.version = parseInt(match[1], 10);
    }
  });
}
else {
  detect.browser = 'node';
  detect.browserVersion = detect.version = '?'; // TODO: get node version
}<|endoftext|>


<commit_before>// JS and CSS bundles
//
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css

// Images and fonts so that views can link to them
//
//= link_tree ../images
<commit_msg>Revert "Add manifest.js to app/assets directory"<commit_after><|endoftext|>


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

'use strict';

// MODULES //

var isVectorLike = require( '@stdlib/assert/is-vector-like' );
var isArrayLikeObject = require( '@stdlib/assert/is-array-like-object' );
var isFloat64Array = require( '@stdlib/assert/is-float64array' );
var array = require( '@stdlib/ndarray/array' );
var dot = require( '@stdlib/blas/base/ddot' ).ndarray;

// MAIN //

/**
* Computes the dot product of `x` and `y`.
*
* ## Notes
*
* -   Casting is **not** safe in order to accommodate generic arrays and array-like objects.
* -   For best performance, provide 1-dimensional `ndarrays` whose underlying data type is `float64`.
*
* @param {(ArrayLike|VectorLike)} x - first input array
* @param {(ArrayLike|VectorLike)} y - second input array
* @throws {TypeError} first argument must be either an array-like object or a 1-dimensional ndarray
* @throws {TypeError} second argument must be either an array-like object or a 1-dimensional ndarray
* @throws {TypeError} first argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray
* @throws {TypeError} second argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray
* @throws {RangeError} input arrays must be the same length
* @returns {number} dot product of `x` and `y`
*
* @example
* var Float64Array = require( '@stdlib/array/float64' );
* var array = require( '@stdlib/ndarray/array' );
*
* var x = array( new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] ) );
* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
*
* var z = ddot( x, y );
* // returns -5.0
*
* @example
* var x = [ 4.0, 2.0, -3.0, 5.0, -1.0 ];
* var y = [ 2.0, 6.0, -1.0, -4.0, 8.0 ];
*
* var z = ddot( x, y );
* // returns -5.0
*/
function ddot( x, y ) {
	var opts;
	var bool;

	bool = (
		isVectorLike( x ) &&
		isVectorLike( y ) &&
		isFloat64Array( x.data ) &&
		isFloat64Array( y.data )
	);
	if ( bool === false ) {
		if ( !isArrayLikeObject( x ) ) {
			throw new TypeError( 'invalid argument. First argument must be either an array-like object or a 1-dimensional ndarray. Value: `' + x + '`.' );
		}
		if ( !isArrayLikeObject( y ) ) {
			throw new TypeError( 'invalid argument. Second argument must be either an array-like object or a 1-dimensional ndarray. Value: `' + y + '`.' );
		}
		opts = {
			'dtype': 'float64',
			'casting': 'unsafe'
		};
		x = array( x, opts );
		y = array( y, opts );
		if ( x.ndims !== 1 ) {
			throw new TypeError( 'invalid argument. First argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray. Value: `' + x + '`.' );
		}
		if ( y.ndims !== 1 ) {
			throw new TypeError( 'invalid argument. Second argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray. Value: `' + y + '`.' );
		}
	}
	if ( x.length !== y.length ) {
		throw new RangeError( 'invalid argument. Arrays must be the same length. First argument length: ' + x.length + '. Second argument length: ' + y.length + '.' );
	}
	return dot( x.length, x.data, x.strides[ 0 ], x.offset, y.data, y.strides[ 0 ], y.offset ); // eslint-disable-line max-len
}

// EXPORTS //

module.exports = ddot;
<commit_msg>Use more widespread field for testing for dimensionality<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2020 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var isVectorLike = require( '@stdlib/assert/is-vector-like' );
var isArrayLikeObject = require( '@stdlib/assert/is-array-like-object' );
var isFloat64Array = require( '@stdlib/assert/is-float64array' );
var array = require( '@stdlib/ndarray/array' );
var dot = require( '@stdlib/blas/base/ddot' ).ndarray;

// MAIN //

/**
* Computes the dot product of `x` and `y`.
*
* ## Notes
*
* -   Casting is **not** safe in order to accommodate generic arrays and array-like objects.
* -   For best performance, provide 1-dimensional `ndarrays` whose underlying data type is `float64`.
*
* @param {(ArrayLike|VectorLike)} x - first input array
* @param {(ArrayLike|VectorLike)} y - second input array
* @throws {TypeError} first argument must be either an array-like object or a 1-dimensional ndarray
* @throws {TypeError} second argument must be either an array-like object or a 1-dimensional ndarray
* @throws {TypeError} first argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray
* @throws {TypeError} second argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray
* @throws {RangeError} input arrays must be the same length
* @returns {number} dot product of `x` and `y`
*
* @example
* var Float64Array = require( '@stdlib/array/float64' );
* var array = require( '@stdlib/ndarray/array' );
*
* var x = array( new Float64Array( [ 4.0, 2.0, -3.0, 5.0, -1.0 ] ) );
* var y = array( new Float64Array( [ 2.0, 6.0, -1.0, -4.0, 8.0 ] ) );
*
* var z = ddot( x, y );
* // returns -5.0
*
* @example
* var x = [ 4.0, 2.0, -3.0, 5.0, -1.0 ];
* var y = [ 2.0, 6.0, -1.0, -4.0, 8.0 ];
*
* var z = ddot( x, y );
* // returns -5.0
*/
function ddot( x, y ) {
	var opts;
	var bool;

	bool = (
		isVectorLike( x ) &&
		isVectorLike( y ) &&
		isFloat64Array( x.data ) &&
		isFloat64Array( y.data )
	);
	if ( bool === false ) {
		if ( !isArrayLikeObject( x ) ) {
			throw new TypeError( 'invalid argument. First argument must be either an array-like object or a 1-dimensional ndarray. Value: `' + x + '`.' );
		}
		if ( !isArrayLikeObject( y ) ) {
			throw new TypeError( 'invalid argument. Second argument must be either an array-like object or a 1-dimensional ndarray. Value: `' + y + '`.' );
		}
		opts = {
			'dtype': 'float64',
			'casting': 'unsafe'
		};
		x = array( x, opts );
		y = array( y, opts );
		if ( x.shape.length !== 1 ) {
			throw new TypeError( 'invalid argument. First argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray. Value: `' + x + '`.' );
		}
		if ( y.shape.length !== 1 ) {
			throw new TypeError( 'invalid argument. Second argument must be either an array-like object or ndarray which can be cast to a 1-dimensional ndarray. Value: `' + y + '`.' );
		}
	}
	if ( x.length !== y.length ) {
		throw new RangeError( 'invalid argument. Arrays must be the same length. First argument length: ' + x.length + '. Second argument length: ' + y.length + '.' );
	}
	return dot( x.length, x.data, x.strides[ 0 ], x.offset, y.data, y.strides[ 0 ], y.offset ); // eslint-disable-line max-len
}

// EXPORTS //

module.exports = ddot;
<|endoftext|>


<commit_before>const os = require('os');

const { isWebClientLegacy } = require('../config');
const { bash, script } = require('../helpers/cli');
const { debug, info } = require('../helpers/log')('proton-bundler');

const SOURCE_FILE_INDEX = "find dist -maxdepth 1 -type f -name 'index*.js'";

/**
 * Get the configuration for a deployement
 *     [Warning] you must have  NODE_ENV=production
 * @param  {Array} flags flags used to bundle
 * @return {Object}       app's config
 */
async function getNewConfig(api, flags = process.argv.slice(3), isCurrent = false) {
    if (isWebClientLegacy()) {
        const branch = !isCurrent ? api.replace('+proxy', '') : 'dev';
        const { stdout = '' } = await bash('NODE_ENV=dist ./tasks/setupConfig.js', [
            '--print-config',
            `--branch deploy-${branch}`,
            ...flags
        ]);
        debug(stdout, 'stdout config angular');

        /*
            We need to rename the key sentry.sentry to sentry.dsn to match new config format from proton-pack
            because only Legacy Angular is with this weird format.
         */
        const { sentry = {}, ...json } = JSON.parse(stdout);
        const { sentry: dsn } = sentry;
        const newConfig = {
            ...json,
            sentry: {
                ...sentry,
                dsn
            }
        };

        debug(newConfig, 'new config angular with right dsn');

        return newConfig;
    }

    // we don't use npx as for the CI we cached it so it's faster
    const { stdout = '' } = await bash('./node_modules/proton-pack/bin/protonPack print-config', flags);
    debug(stdout, 'stdout config app');
    return JSON.parse(stdout);
}

function sed(rule, files) {
    // Because for the lulz. cf https://myshittycode.com/2014/07/24/os-x-sed-extra-characters-at-the-end-of-l-command-error/
    if (os.platform() === 'darwin') {
        return bash(`sed -i '' '${rule}' $(${files})`);
    }
    return bash(`sed -i '${rule}' "$(${files})"`);
}

async function writeNewConfig(api) {
    const {
        sentry: { dsn: currentSentryDSN },
        secureUrl: currentSecureURL
    } = await getNewConfig(api, ['--api proxy'], true);
    const {
        apiUrl,
        sentry: { dsn: newSentryDSN },
        secureUrl: newSecureURL
    } = await getNewConfig(api);

    debug({
        current: {
            currentSentryDSN,
            currentSecureURL
        },
        newConfig: {
            newSentryDSN,
            newSecureURL
        }
    });

    if (isWebClientLegacy()) {
        await sed(`s#apiUrl:"/api"#apiUrl:"${apiUrl}"#;`, SOURCE_FILE_INDEX);
        info(`replace current api by ${apiUrl} inside the main index`);

        await sed(`s#sentry:"${currentSentryDSN}"#sentry:"${newSentryDSN}"#;`, SOURCE_FILE_INDEX);
        info('replace sentry config inside the main index');

        await sed(`s#secure:"${currentSecureURL}"#secure:"${newSecureURL}"#;`, SOURCE_FILE_INDEX);
        return info('replace secureURL config inside the main index');
    }

    await sed(`s#="/api"#="${apiUrl}"#;`, SOURCE_FILE_INDEX);
    info(`replace current api by ${apiUrl} inside the main index`);

    await sed(`s#="${currentSentryDSN}"#="${newSentryDSN}"#;`, SOURCE_FILE_INDEX);
    info('replace sentry config inside the main index');

    await sed(`s#="${currentSecureURL}"#="${newSecureURL}"#;`, SOURCE_FILE_INDEX);
    info('replace secureURL config inside the main index');
}

/**
 * Create a new bundle from an existing one
 * To validate the bundle, we use SRI <3 <3 <3 it's easy to validate a new build with them.
 * They crash if it doesn't match <3 <3 <3
 *     - Generate hash for files with the modifications
 *     - Replace them inside index.js
 *     - Then we replace the SRI of index.js inside index.html
 * @return {Promise}
 */
async function main({ api }) {
    await bash('cp -r dist distProd');
    info('made a copy of the current dist');

    await writeNewConfig(api);

    const { stdout } = await script('manageSRI.sh write-html');
    info('update new index.html');
    debug(stdout, 'write new SRIs HTML');

    const { stdout: stdoutSed } = await script('manageSRI.sh write-env', [api]);
    info('update new .env');
    debug(stdoutSed, 'write new APP_ENV');

    /**
     * Check if the sub-build is valid
     *     - Correct SRI for updated files
     *     - Correct SRI for the index
     *     - Right config for A/B
     *     - Right config for Sentry
     * Stop the process if it fails
     */
    await script('manageSRI.sh validate', [api]);
}

module.exports = main;
<commit_msg>Fix legacy env proxy api detection (#28)<commit_after>const os = require('os');

const { isWebClientLegacy } = require('../config');
const { bash, script } = require('../helpers/cli');
const { debug, info } = require('../helpers/log')('proton-bundler');

const SOURCE_FILE_INDEX = "find dist -maxdepth 1 -type f -name 'index*.js'";

/**
 * Get the configuration for a deployement
 *     [Warning] you must have  NODE_ENV=production
 * @param  {Array} flags flags used to bundle
 * @return {Object}       app's config
 */
async function getNewConfig(api, flags = process.argv.slice(3), isCurrent = false) {
    debug({ api, flags, isCurrent }, 'flags getNewConfig');
    if (isWebClientLegacy()) {
        /**
         * Compat layer for old build as it binds dev API env instead of old :/
         * It doesn't make sense, but the main thing is we should upgrade the config setup on legacy
         * to match how it's done via proton-pack today for the react applications.
         * It will be easier to use than what we have today, build on top of obsolete config -> branches
         * so quick and dirty is the way sadly
         */
        const getBranch = () => {
            if (api.includes('old')) {
                return api.replace('+proxy', '');
            }
            return !isCurrent ? api.replace('+proxy', '') : 'dev';
        };

        const branch = getBranch();
        const cleanApi = (api) => {
            // When we deploy to these targets we must use the proxy api
            if (/prod|old|beta|tor/.test(api) && api.includes('+proxy')) {
                return (item) => item.replace(/\w+\+proxy/, 'proxy');
            }
            return (a) => a;
        };

        const { stdout = '' } = await bash('NODE_ENV=dist ./tasks/setupConfig.js', [
            '--print-config',
            `--branch deploy-${branch}`,
            ...flags.map(cleanApi(api))
        ]);
        debug(stdout, 'stdout config angular');

        /*
            We need to rename the key sentry.sentry to sentry.dsn to match new config format from proton-pack
            because only Legacy Angular is with this weird format.
         */
        const { sentry = {}, ...json } = JSON.parse(stdout);
        const { sentry: dsn } = sentry;
        const newConfig = {
            ...json,
            sentry: {
                ...sentry,
                dsn
            }
        };

        debug(newConfig, 'new config angular with right dsn');
        return newConfig;
    }

    // we don't use npx as for the CI we cached it so it's faster
    const { stdout = '' } = await bash('./node_modules/proton-pack/bin/protonPack print-config', flags);
    debug(stdout, 'stdout config app');
    return JSON.parse(stdout);
}

function sed(rule, files) {
    // Because for the lulz. cf https://myshittycode.com/2014/07/24/os-x-sed-extra-characters-at-the-end-of-l-command-error/
    if (os.platform() === 'darwin') {
        return bash(`sed -i '' '${rule}' $(${files})`);
    }
    return bash(`sed -i '${rule}' "$(${files})"`);
}

async function writeNewConfig(api) {
    const {
        sentry: { dsn: currentSentryDSN },
        secureUrl: currentSecureURL
    } = await getNewConfig(api, ['--api proxy'], true);
    const {
        apiUrl,
        sentry: { dsn: newSentryDSN },
        secureUrl: newSecureURL
    } = await getNewConfig(api);

    debug({
        current: {
            currentSentryDSN,
            currentSecureURL
        },
        newConfig: {
            newSentryDSN,
            newSecureURL
        }
    });

    if (isWebClientLegacy()) {
        await sed(`s#apiUrl:"/api"#apiUrl:"${apiUrl}"#;`, SOURCE_FILE_INDEX);
        info(`replace current api by ${apiUrl} inside the main index`);

        await sed(`s#sentry:"${currentSentryDSN}"#sentry:"${newSentryDSN}"#;`, SOURCE_FILE_INDEX);
        info('replace sentry config inside the main index');

        await sed(`s#secure:"${currentSecureURL}"#secure:"${newSecureURL}"#;`, SOURCE_FILE_INDEX);
        return info('replace secureURL config inside the main index');
    }

    await sed(`s#="/api"#="${apiUrl}"#;`, SOURCE_FILE_INDEX);
    info(`replace current api by ${apiUrl} inside the main index`);

    await sed(`s#="${currentSentryDSN}"#="${newSentryDSN}"#;`, SOURCE_FILE_INDEX);
    info('replace sentry config inside the main index');

    await sed(`s#="${currentSecureURL}"#="${newSecureURL}"#;`, SOURCE_FILE_INDEX);
    info('replace secureURL config inside the main index');
}

/**
 * Create a new bundle from an existing one
 * To validate the bundle, we use SRI <3 <3 <3 it's easy to validate a new build with them.
 * They crash if it doesn't match <3 <3 <3
 *     - Generate hash for files with the modifications
 *     - Replace them inside index.js
 *     - Then we replace the SRI of index.js inside index.html
 * @return {Promise}
 */
async function main({ api }) {
    await bash('cp -r dist distProd');
    info('made a copy of the current dist');

    await writeNewConfig(api);

    const { stdout } = await script('manageSRI.sh write-html');
    info('update new index.html');
    debug(stdout, 'write new SRIs HTML');

    const { stdout: stdoutSed } = await script('manageSRI.sh write-env', [api]);
    info('update new .env');
    debug(stdoutSed, 'write new APP_ENV');

    /**
     * Check if the sub-build is valid
     *     - Correct SRI for updated files
     *     - Correct SRI for the index
     *     - Right config for A/B
     *     - Right config for Sentry
     * Stop the process if it fails
     */
    await script('manageSRI.sh validate', [api]);
}

module.exports = main;
<|endoftext|>


<commit_before>// Copyright (c) 2011 Google, Inc.
//
// 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.

/**
 * @fileoverview Multiple Dygraphs palettes, grouped by global, series, etc..
 *
 * @author konigsberg@google.com (Robert Konigsberg)
 */

function MultiPalette() {
  this.palettes = {};
  this.root = null;
  this.filterBar = null;
  // This is meant to be overridden by a palette host.
  this.activePalette = null;
  this.onchange = function() {};
}

MultiPalette.optionSetValues = {
  "global": "global",
  "x": "x axis",
  "y": "y axis",
  "y2": "y2 axis",
};

MultiPalette.prototype.create = function(parentElement) {
  var self = this;

  this.root = $("<div>").addClass("palette").appendTo(parentElement);
  var header = $("<div>").addClass("header").appendTo(this.root);
  // Selector for series and axes.
  var selectorRow = $("<div>").appendTo(header);
  var optionSelector = $("<select>")
      .change(function(x) {
        self.activate(optionSelector.val());
      });

  selectorRow
      .append($("<span>").text("Option Set:"))
      .append(optionSelector);

  var filter = function() {
    $.each(self.palettes, function(key, value) {
      value.filter(self.filterBar.val());
    });
  }

  this.filterBar = $("<input>", { type : "search" })
      .keyup(filter)
      .click(filter);

  header.append($("<div>")
      .append($("<span>").text("Filter:"))
      .append($("<span>").append(this.filterBar))
      .append($("<span>")
          .append($("<a>")
              .attr("href", "#")
              .addClass("link")
              .text("Redraw")
              .css("float", "right")
              .css("padding-right", "8px")
              .click(function() { self.onchange(); }))));

  $.each(MultiPalette.optionSetValues, function(key, value) {
     $(optionSelector)
         .append($("<option></option>")
         .attr("value", key)
         .text(value));
      var palette = new Palette(key);
      palette.create(self.root);
      palette.root.style.display = "none";
      palette.onchange = function() {
        self.onchange();
      };
      self.palettes[key] = palette;
  });

  this.activate("global");
}

MultiPalette.prototype.activate = function(key) {
  if (this.activePalette) {
    this.activePalette.root.style.display = "none";
  }
  this.activePalette = this.palettes[key];
  this.activePalette.root.style.display = "block";
}

/**
 * Read from palette
 */
MultiPalette.prototype.read = function() {
  var results = this.palettes.global.read();
  results.axes = {};
  var clearIfEmpty = function(hash, key) {
    var val = hash[key];
    if ($.isEmptyObject(val)) {
      delete hash[key];
    }
  }
  results.axes.x = this.palettes.x.read();
  results.axes.y = this.palettes.y.read();
  results.axes.y2 = this.palettes.y2.read();
  clearIfEmpty(results.axes, "x");
  clearIfEmpty(results.axes, "y");
  clearIfEmpty(results.axes, "y2");
  clearIfEmpty(results, "axes");
  return results;
}

/**
 * Write to palette from hash.
 */
MultiPalette.prototype.write = function(hash) {
  this.palettes.global.write(hash);
  if (hash.hasOwnProperty("axes")) {
    var axes = hash.axes;
    this.palettes.x.write(axes["x"]);
    this.palettes.y.write(axes["y"]);
    this.palettes.y2.write(axes["y2"]);
  }
}
<commit_msg>Add "to hash" feature to make palette more useful!<commit_after>// Copyright (c) 2011 Google, Inc.
//
// 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.

/**
 * @fileoverview Multiple Dygraphs palettes, grouped by global, series, etc..
 *
 * @author konigsberg@google.com (Robert Konigsberg)
 */

function MultiPalette() {
  this.palettes = {};
  this.root = null;
  this.filterBar = null;
  // This is meant to be overridden by a palette host.
  this.activePalette = null;
  this.onchange = function() {};
}

MultiPalette.optionSetValues = {
  "global": "global",
  "x": "x axis",
  "y": "y axis",
  "y2": "y2 axis",
};

MultiPalette.prototype.create = function(parentElement) {
  var self = this;

  this.root = $("<div>").addClass("palette").appendTo(parentElement);
  var header = $("<div>").addClass("header").appendTo(this.root);
  // Selector for series and axes.
  var selectorRow = $("<div>").appendTo(header);
  var optionSelector = $("<select>")
      .change(function(x) {
        self.activate(optionSelector.val());
      });

  selectorRow
      .append($("<span>").text("Option Set:"))
      .append(optionSelector)
       .append($("<span>")
          .append($("<a>")
              .addClass("link")
              .text("to hash")
              .css("float", "right")
              .css("padding-right", "8px")
              .click(function() { self.showHash(); })));

  var filter = function() {
    $.each(self.palettes, function(key, value) {
      value.filter(self.filterBar.val());
    });
  }

  this.filterBar = $("<input>", { type : "search" })
      .keyup(filter)
      .click(filter);

  header.append($("<div>")
      .append($("<span>").text("Filter:"))
      .append($("<span>").append(this.filterBar))
      .append($("<span>")
          .append($("<a>")
              .addClass("link")
              .text("Redraw")
              .css("float", "right")
              .css("padding-right", "8px")
              .click(function() { self.onchange(); }))));

  $.each(MultiPalette.optionSetValues, function(key, value) {
     $(optionSelector)
         .append($("<option></option>")
         .attr("value", key)
         .text(value));
      var palette = new Palette(key);
      palette.create(self.root);
      palette.root.style.display = "none";
      palette.onchange = function() {
        self.onchange();
      };
      self.palettes[key] = palette;
  });

  this.activate("global");
}

MultiPalette.prototype.activate = function(key) {
  if (this.activePalette) {
    this.activePalette.root.style.display = "none";
  }
  this.activePalette = this.palettes[key];
  this.activePalette.root.style.display = "block";
}

MultiPalette.prototype.showHash = function() {
  var hash = this.read();
  var textarea = new TextArea();
  textarea.cancel.style.display = "none";

  /*
   * JSON.stringify isn't built to be nice to functions. The following fixes
   * this.
   *
   * First, val.toString only does part of the work, turning it into
   * "function () {\n  alert(\"p-click!\");\n}",
   *
   * {start,end}Marker make the surrounding quotes easy to find, and then
   * remove them. It also converts the instances of \n and \" so the
   * result looks like:
   * function () {
   *   alert("p-click!");
   * }",
   */
  var startMarker = "<~%!<";
  var endMarker = ">!%~>";
  var replacer = function(key, val) {
    if (typeof val === 'function') {
      return startMarker + val.toString() + endMarker;
    }
    return val;
  }
  var text = JSON.stringify(hash, replacer, 2);
  console.log(text);
  while(true) {
    var start = text.indexOf(startMarker);
    var end = text.indexOf(endMarker);
    if (start == -1) {
      break;
    }
    var substring = text.substring(start + startMarker.length, end);
    while(substring.indexOf("\\n") >= 0) {
      substring = substring.replace("\\n", "\n");
    }
    while(substring.indexOf("\\\"") >= 0) {
      substring = substring.replace("\\\"", "\"");
    }
    text = text.substring(0, start - 1)
        + substring
        + text.substring(end + endMarker.length + 1);
  }
  textarea.show("options", text);
}

/**
 * Read from palette
 */
MultiPalette.prototype.read = function() {
  var results = this.palettes.global.read();
  results.axes = {};
  var clearIfEmpty = function(hash, key) {
    var val = hash[key];
    if ($.isEmptyObject(val)) {
      delete hash[key];
    }
  }
  results.axes.x = this.palettes.x.read();
  results.axes.y = this.palettes.y.read();
  results.axes.y2 = this.palettes.y2.read();
  clearIfEmpty(results.axes, "x");
  clearIfEmpty(results.axes, "y");
  clearIfEmpty(results.axes, "y2");
  clearIfEmpty(results, "axes");
  return results;
}

/**
 * Write to palette from hash.
 */
MultiPalette.prototype.write = function(hash) {
  this.palettes.global.write(hash);
  if (hash.hasOwnProperty("axes")) {
    var axes = hash.axes;
    this.palettes.x.write(axes["x"]);
    this.palettes.y.write(axes["y"]);
    this.palettes.y2.write(axes["y2"]);
  }
}
<|endoftext|>


<commit_before>/*
 @author Zakai Hamilton
 @component DbEvents
 */

screens.db.events = function DbEvents(me, packages) {
    return "server";
};

screens.db.events.participants = function DbEventsParticipants(me, packages) {
    const { storage } = packages;
    me.init = () => storage.db.extension(me);
    me.cache = {};
    me.options = { capped: true, size: 242880, max: 250 };
    return "server";
};

screens.db.events.msg = function DbEventsParticipants(me, packages) {
    const { core, storage } = packages;
    me.lastMsgId = 0;
    me.busy = false;
    me.init = async () => {
        storage.db.extension(me);
        setInterval(me.handleNextMsg, 10000);
    };
    me.options = { capped: true, size: 32000, max: 100 };
    me.handleNextMsg = async function () {
        if (!me.storage.db.supported()) {
            return;
        }
        if (me.busy) {
            return;
        }
        me.busy = true;
        try {
            var list = await me.list();
            if (!list) {
                return;
            }
            if (!list.length) {
                return;
            }
            var index = list.findIndex((item) => item._id === me.lastMsgId);
            if (index == -1) {
                me.lastMsgId = list[list.length - 1]._id;
                return;
            }
            for (index++; index < list.length; index++) {
                var item = list[index];
                me.log("running message: " + index + " - " + item.date + " - " + JSON.stringify(item.args));
                await core.message.send.apply(null, item.args);
                me.lastMsgId = item._id;
            }
        }
        catch (err) {
            me.log_error("Cannot handle messages : " + err);
        }
        finally {
            me.busy = false;
        }
    };
    me.send = function (method) {
        var args = Array.prototype.slice.call(arguments, 0);
        me.push({ date: new Date().toString(), args });
    };
    return "server";
};<commit_msg>bug fix<commit_after>/*
 @author Zakai Hamilton
 @component DbEvents
 */

screens.db.events = function DbEvents(me, packages) {
    return "server";
};

screens.db.events.participants = function DbEventsParticipants(me, packages) {
    const { storage } = packages;
    me.init = () => storage.db.extension(me);
    me.cache = {};
    me.options = { capped: true, size: 242880, max: 250 };
    return "server";
};

screens.db.events.msg = function DbEventsParticipants(me, packages) {
    const { core, storage } = packages;
    me.lastMsgId = 0;
    me.busy = false;
    me.init = async () => {
        storage.db.extension(me);
        setInterval(me.handleNextMsg, 10000);
    };
    me.options = { capped: true, size: 32000, max: 100 };
    me.handleNextMsg = async function () {
        if (!me.storage.db.supported()) {
            return;
        }
        if (me.busy) {
            return;
        }
        me.busy = true;
        try {
            var list = await me.list();
            if (!list) {
                return;
            }
            if (!list.length) {
                return;
            }
            var index = list.findIndex((item) => item._id === me.lastMsgId);
            if (index == -1) {
                me.lastMsgId = list[list.length - 1]._id;
                return;
            }
            for (index++; index < list.length; index++) {
                var item = list[index];
                me.log("running message: " + index + " - " + item.date + " - " + JSON.stringify(item.args));
                try {
                    await core.message.send.apply(null, item.args);
                }
                catch (err) {
                    me.log_error("Cannot handle message" + JSON.stringify(item.args) + " err:" + err);
                }
                me.lastMsgId = item._id;
            }
        }
        catch (err) {
            me.log_error("Cannot handle messages : " + err);
        }
        finally {
            me.busy = false;
        }
    };
    me.send = function (method) {
        var args = Array.prototype.slice.call(arguments, 0);
        me.push({ date: new Date().toString(), args });
    };
    return "server";
};<|endoftext|>


<commit_before>const Resp = require('respjs');
const { EventEmitter } = require('events');
const { QUEUED_STR_BUF, OK_STR_BUF } = require('./constants');

/**
 * Class to handle a redis connection and provide events to react on for
 * all incoming Redis commands
 */
class RedisHandler extends EventEmitter {
    /**
     * Initialize and register all data handlers to send out events on new commands
     * @param socket Network Socket/Connection
     * @param options options objects, currently mainly for logger
     */
    constructor(socket, options) {
        super();

        options = options || {};
        this.options = options;
        this.log = options.log || console;
        this.logScope = options.logScope || '';
        if (this.logScope.length) {
            this.logScope += ' ';
        }
        this.socket = socket;

        this.socketId = `${this.logScope + socket.remoteAddress}:${socket.remotePort}`;
        this.initialized = false;
        this.stop = false;

        this.activeMultiCalls = [];
        this.writeQueue = [];

        this.handleBuffers = false;
        const respOptions = {};
        if (options.handleAsBuffers) {
            this.handleBuffers = true;
            respOptions.bufBulk = true;
        }
        this.resp = new Resp(respOptions);

        this.resp.on('error', err => {
            this.log.error(`${this.socketId} (Init=${this.initialized}) Redis error:${err}`);
            if (this.initialized) {
                this.sendError(null, new Error(`PARSER ERROR ${err}`)); // TODO
            } else {
                this.close();
            }
        });

        this.resp.on('data', data => this._handleCommand(data));

        socket.on('data', data => {
            if (this.options.enhancedLogging) {
                this.log.silly(
                    `${this.socketId} New Redis request: ${
                        data.length > 1024
                            ? `${data
                                  .toString()
                                  .replace(/[\r\n]+/g, '')
                                  .substring(0, 100)} -- ${data.length} bytes`
                            : data.toString().replace(/[\r\n]+/g, '')
                    }`
                );
            }
            this.resp.write(data);
        });

        socket.on('error', err => {
            if (!this.stop) {
                this.log.debug(`${this.socketId} Redis Socket error: ${err}`);
            }
            if (this.socket) {
                this.socket.destroy();
            }
        });
    }

    /**
     * Handle one incoming command, assign responseId and emit event to be handled
     * @param data Array RESP data
     * @private
     */
    _handleCommand(data) {
        let command = data.splice(0, 1)[0];
        if (this.handleBuffers) {
            // Command and pot. first parameter should always be Buffer
            command = command.toString('utf-8');
            if (Buffer.isBuffer(data[0])) {
                data[0] = data[0].toString('utf-8');
            }
            // Binary data only relevant for GET and SET in our case
            if (command !== 'set' && data.length > 1) {
                for (let i = 1; i < data.length; i++) {
                    if (Buffer.isBuffer(data[i])) {
                        data[i] = data[i].toString('utf-8');
                    }
                }
            }
        }

        const t = process.hrtime();
        const responseId = t[0] * 1e3 + t[1] / 1e6;

        if (this.options.enhancedLogging) {
            this.log.silly(
                `${this.socketId} Parser result: id=${responseId}, command=${command}, data=${
                    JSON.stringify(data).length > 1024
                        ? `${JSON.stringify(data).substring(0, 100)} -- ${JSON.stringify(data).length} bytes`
                        : JSON.stringify(data)
                }`
            );
        }

        if (command === 'multi') {
            if (this.activeMultiCalls.length && !this.activeMultiCalls[0].execCalled) {
                // should never happen
                this.log.warn(`${this.socketId} Conflicting multi call`);
            }
            this._handleMulti();
            return;
        }

        // multi active and exec not called yet
        if (this.activeMultiCalls.length && !this.activeMultiCalls[0].execCalled && command !== 'exec') {
            // store all response ids so we know which need to be in the multi call
            this.activeMultiCalls[0].responseIds.push(responseId);
            // add it for the correct order will be overwritten with correct response
            this.activeMultiCalls[0].responseMap.set(responseId, null);
        } else {
            // multi response ids should not be pushed - we will answer combined
            this.writeQueue.push({ id: responseId, data: false });
        }

        if (command === 'exec') {
            this._handleExec(responseId);
            return;
        }

        if (command === 'info') {
            this.initialized = true;
        }

        if (this.listenerCount(command) !== 0) {
            setImmediate(() => this.emit(command, data, responseId));
        } else {
            this.sendError(responseId, new Error(`${command} NOT SUPPORTED`));
        }
    }

    /**
     * Check if the response to a certain command can be send out directly or
     * if it needs to wait till earlier responses are ready
     * @param responseId ID of the response
     * @param data Buffer to send out
     * @private
     */
    _sendQueued(responseId, data) {
        let idx = 0;

        while (this.writeQueue.length && idx < this.writeQueue.length) {
            // we found the queue entry that matches with the responseId, so store the data so be sent out
            if (this.writeQueue[idx].id === responseId) {
                this.writeQueue[idx].data = data;
                // if we found it not on first index we are done because we can not send it out,
                // need to wait for index 0 to have a complete answer
                if (idx > 0) {
                    break;
                }
            }
            // when data for queue entry 0 are preset (!== false) we can send it, remove the first entry
            // and check the other entries if they have completed responses too
            if (idx === 0 && this.writeQueue[idx].data !== false) {
                const response = this.writeQueue.shift();
                if (this.options.enhancedLogging) {
                    this.log.silly(
                        `${this.socketId} Redis response (${response.id}): ${
                            response.data.length > 1024
                                ? `${data.length} bytes`
                                : response.data.toString().replace(/[\r\n]+/g, '')
                        }`
                    );
                }

                this._write(response.data);
                // We sended out first queue entry but no further response is ready
                // and we do not need to check the whole queue, so we are done here
                if (this.writeQueue.length && this.writeQueue[idx].data === false) {
                    break;
                }
            } else {
                // we have not found the response on the current index, try next one
                idx++;
            }
        }

        if (idx > 0) {
            if (this.options.enhancedLogging) {
                this.log.silly(`${this.socketId} Redis response (${responseId}): Response queued`);
            }
        }
    }

    /**
     * Really write out a response to the network connection
     * @param data Buffer to send out
     * @private
     */
    _write(data) {
        this.socket.write(data);
    }

    /**
     * Guard to make sure a response is valid to be sent out
     * @param responseId ID of the response
     * @param data Buffer to send out
     */
    sendResponse(responseId, data) {
        // handle responses without a specific request like publishing data, so send directly
        if (responseId === null) {
            if (this.options.enhancedLogging) {
                this.log.silly(`${this.socketId} Redis response DIRECT: ${data.toString().replace(/[\r\n]+/g, '')}`);
            }
            return this._write(data);
        }
        if (!responseId) {
            throw new Error('Invalid implementation: no responseId provided!');
        }
        if (!data) {
            this.log.warn(`${this.socketId} Not able to write ${JSON.stringify(data)}`);
            data = Resp.encodeError(new Error(`INVALID RESPONSE: ${JSON.stringify(data)}`));
        }

        setImmediate(() => this._sendQueued(responseId, data));
    }

    /**
     * Close network connection
     */
    close() {
        this.log.silly(`${this.socketId} close Redis connection`);
        this.stop = true;
        this.socket.end();
    }

    /**
     * Return if socket/handler active or closed
     * @returns {boolean} is Handler/Connection active (not closed)
     */
    isActive() {
        return !this.stop;
    }

    /**
     * Encode RESP's Null value to RESP buffer and send out
     * @param responseId ID of the response
     */
    sendNull(responseId) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeNull());
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeNull());
    }

    /**
     * Encode RESP's Null Array value to RESP buffer and send out
     * @param responseId ID of the response
     */
    sendNullArray(responseId) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeNullArray());
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeNullArray());
    }

    /**
     * Encode string to RESP buffer and send out
     * @param responseId ID od the response
     * @param str String to encode
     */
    sendString(responseId, str) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeString(str));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeString(str));
    }

    /**
     * Encode error object to RESP buffer and send out
     * @param responseId ID of the response
     * @param error Error object with error details to send out
     */
    sendError(responseId, error) {
        this.log.warn(`${this.socketId} Error from InMemDB: ${error}`);

        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeError(error));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeError(error));
    }

    /**
     * Encode integer to RESP buffer and send out
     * @param responseId ID of the response
     * @param num Integer to send out
     */
    sendInteger(responseId, num) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeInteger(num));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeInteger(num));
    }

    /**
     * Encode RESP's bulk string to RESP buffer and send out
     * @param responseId ID of the response
     * @param str String to send out
     */
    sendBulk(responseId, str) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeBulk(str));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeBulk(str));
    }

    /**
     * Encode RESP's bulk buffer to RESP buffer.
     * @param responseId ID of the response
     * @param buf Buffer to send out
     */
    sendBufBulk(responseId, buf) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeBufBulk(buf));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeBufBulk(buf));
    }

    /**
     * Encode an Array depending on the type of the elements
     * @param arr Array to encode
     * @returns {Array} Array with Buffers with encoded values
     */
    encodeRespArray(arr) {
        for (let i = 0; i < arr.length; i++) {
            if (Array.isArray(arr[i])) {
                arr[i] = this.encodeRespArray(arr[i]);
            } else if (Buffer.isBuffer(arr[i])) {
                arr[i] = Resp.encodeBufBulk(arr[i]);
            } else if (arr[i] === null) {
                arr[i] = Resp.encodeNull();
            } else if (typeof arr[i] === 'number') {
                arr[i] = Resp.encodeInteger(arr[i]);
            } else {
                arr[i] = Resp.encodeBulk(arr[i]);
            }
        }
        return arr;
    }

    /**
     * Encode a array values to buffers and send out
     * @param {number} responseId ID of the response
     * @param {any[]} arr Array to send out
     */
    sendArray(responseId, arr) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeArray(this.encodeRespArray(arr)));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeArray(this.encodeRespArray(arr)));
    }

    /**
     * Handles a 'multi' command
     *
     * @private
     */
    _handleMulti() {
        this.activeMultiCalls.unshift({
            responseIds: [],
            execCalled: false,
            responseCount: 0,
            responseMap: new Map()
        });
    }

    /**
     * Handles an 'exec' command
     *
     * @param {number} responseId ID of the response
     * @private
     */
    _handleExec(responseId) {
        if (!this.activeMultiCalls[0]) {
            this.sendError(responseId, new Error('EXEC without MULTI'));
            return;
        }

        this.activeMultiCalls[0].execId = responseId;
        this.activeMultiCalls[0].execCalled = true;

        // maybe we have all fullfilled yet
        if (this.activeMultiCalls[0].responseCount === this.activeMultiCalls[0].responseIds.length) {
            const multiRespObj = this.activeMultiCalls.shift();
            this._sendExecResponse(multiRespObj);
        }
    }

    /**
     * Builds up the exec response and sends it
     * @param {Record<string, any>} multiObj the multi object to send out
     *
     * @private
     */
    _sendExecResponse(multiObj) {
        // collect all 'QUEUED' answers
        const queuedStrArr = new Array(multiObj.responseCount).fill(QUEUED_STR_BUF);

        this._sendQueued(
            multiObj.execId,
            Buffer.concat([OK_STR_BUF, ...queuedStrArr, Resp.encodeArray(Array.from(multiObj.responseMap.values()))])
        );
    }

    /**
     * Handles a multi response
     *
     * @param {number} responseId ID of the response
     * @param {number} index index of the multi call
     * @param {Buffer} buf buffer to include in response
     * @private
     */
    _handleMultiResponse(responseId, index, buf) {
        this.activeMultiCalls[index].responseMap.set(responseId, buf);
        this.activeMultiCalls[index].responseCount++;
        if (this.activeMultiCalls[index].responseCount === this.activeMultiCalls[index].responseIds.length) {
            const multiRespObj = this.activeMultiCalls.splice(index, 1)[0];
            this._sendExecResponse(multiRespObj);
        }
    }
}

module.exports = RedisHandler;
<commit_msg>ensure to not send answer to multi call without exec call beeing processed (#1853)<commit_after>const Resp = require('respjs');
const { EventEmitter } = require('events');
const { QUEUED_STR_BUF, OK_STR_BUF } = require('./constants');

/**
 * Class to handle a redis connection and provide events to react on for
 * all incoming Redis commands
 */
class RedisHandler extends EventEmitter {
    /**
     * Initialize and register all data handlers to send out events on new commands
     * @param socket Network Socket/Connection
     * @param options options objects, currently mainly for logger
     */
    constructor(socket, options) {
        super();

        options = options || {};
        this.options = options;
        this.log = options.log || console;
        this.logScope = options.logScope || '';
        if (this.logScope.length) {
            this.logScope += ' ';
        }
        this.socket = socket;

        this.socketId = `${this.logScope + socket.remoteAddress}:${socket.remotePort}`;
        this.initialized = false;
        this.stop = false;

        this.activeMultiCalls = [];
        this.writeQueue = [];

        this.handleBuffers = false;
        const respOptions = {};
        if (options.handleAsBuffers) {
            this.handleBuffers = true;
            respOptions.bufBulk = true;
        }
        this.resp = new Resp(respOptions);

        this.resp.on('error', err => {
            this.log.error(`${this.socketId} (Init=${this.initialized}) Redis error:${err}`);
            if (this.initialized) {
                this.sendError(null, new Error(`PARSER ERROR ${err}`)); // TODO
            } else {
                this.close();
            }
        });

        this.resp.on('data', data => this._handleCommand(data));

        socket.on('data', data => {
            if (this.options.enhancedLogging) {
                this.log.silly(
                    `${this.socketId} New Redis request: ${
                        data.length > 1024
                            ? `${data
                                  .toString()
                                  .replace(/[\r\n]+/g, '')
                                  .substring(0, 100)} -- ${data.length} bytes`
                            : data.toString().replace(/[\r\n]+/g, '')
                    }`
                );
            }
            this.resp.write(data);
        });

        socket.on('error', err => {
            if (!this.stop) {
                this.log.debug(`${this.socketId} Redis Socket error: ${err}`);
            }
            if (this.socket) {
                this.socket.destroy();
            }
        });
    }

    /**
     * Handle one incoming command, assign responseId and emit event to be handled
     * @param data Array RESP data
     * @private
     */
    _handleCommand(data) {
        let command = data.splice(0, 1)[0];
        if (this.handleBuffers) {
            // Command and pot. first parameter should always be Buffer
            command = command.toString('utf-8');
            if (Buffer.isBuffer(data[0])) {
                data[0] = data[0].toString('utf-8');
            }
            // Binary data only relevant for GET and SET in our case
            if (command !== 'set' && data.length > 1) {
                for (let i = 1; i < data.length; i++) {
                    if (Buffer.isBuffer(data[i])) {
                        data[i] = data[i].toString('utf-8');
                    }
                }
            }
        }

        const t = process.hrtime();
        const responseId = t[0] * 1e3 + t[1] / 1e6;

        if (this.options.enhancedLogging) {
            this.log.silly(
                `${this.socketId} Parser result: id=${responseId}, command=${command}, data=${
                    JSON.stringify(data).length > 1024
                        ? `${JSON.stringify(data).substring(0, 100)} -- ${JSON.stringify(data).length} bytes`
                        : JSON.stringify(data)
                }`
            );
        }

        if (command === 'multi') {
            if (this.activeMultiCalls.length && !this.activeMultiCalls[0].execCalled) {
                // should never happen
                this.log.warn(`${this.socketId} Conflicting multi call`);
            }
            this._handleMulti();
            return;
        }

        // multi active and exec not called yet
        if (this.activeMultiCalls.length && !this.activeMultiCalls[0].execCalled && command !== 'exec') {
            // store all response ids so we know which need to be in the multi call
            this.activeMultiCalls[0].responseIds.push(responseId);
            // add it for the correct order will be overwritten with correct response
            this.activeMultiCalls[0].responseMap.set(responseId, null);
        } else {
            // multi response ids should not be pushed - we will answer combined
            this.writeQueue.push({ id: responseId, data: false });
        }

        if (command === 'exec') {
            this._handleExec(responseId);
            return;
        }

        if (command === 'info') {
            this.initialized = true;
        }

        if (this.listenerCount(command) !== 0) {
            setImmediate(() => this.emit(command, data, responseId));
        } else {
            this.sendError(responseId, new Error(`${command} NOT SUPPORTED`));
        }
    }

    /**
     * Check if the response to a certain command can be send out directly or
     * if it needs to wait till earlier responses are ready
     * @param responseId ID of the response
     * @param data Buffer to send out
     * @private
     */
    _sendQueued(responseId, data) {
        let idx = 0;

        while (this.writeQueue.length && idx < this.writeQueue.length) {
            // we found the queue entry that matches with the responseId, so store the data so be sent out
            if (this.writeQueue[idx].id === responseId) {
                this.writeQueue[idx].data = data;
                // if we found it not on first index we are done because we can not send it out,
                // need to wait for index 0 to have a complete answer
                if (idx > 0) {
                    break;
                }
            }
            // when data for queue entry 0 are preset (!== false) we can send it, remove the first entry
            // and check the other entries if they have completed responses too
            if (idx === 0 && this.writeQueue[idx].data !== false) {
                const response = this.writeQueue.shift();
                if (this.options.enhancedLogging) {
                    this.log.silly(
                        `${this.socketId} Redis response (${response.id}): ${
                            response.data.length > 1024
                                ? `${data.length} bytes`
                                : response.data.toString().replace(/[\r\n]+/g, '')
                        }`
                    );
                }

                this._write(response.data);
                // We sended out first queue entry but no further response is ready
                // and we do not need to check the whole queue, so we are done here
                if (this.writeQueue.length && this.writeQueue[idx].data === false) {
                    break;
                }
            } else {
                // we have not found the response on the current index, try next one
                idx++;
            }
        }

        if (idx > 0) {
            if (this.options.enhancedLogging) {
                this.log.silly(`${this.socketId} Redis response (${responseId}): Response queued`);
            }
        }
    }

    /**
     * Really write out a response to the network connection
     * @param data Buffer to send out
     * @private
     */
    _write(data) {
        this.socket.write(data);
    }

    /**
     * Guard to make sure a response is valid to be sent out
     * @param responseId ID of the response
     * @param data Buffer to send out
     */
    sendResponse(responseId, data) {
        // handle responses without a specific request like publishing data, so send directly
        if (responseId === null) {
            if (this.options.enhancedLogging) {
                this.log.silly(`${this.socketId} Redis response DIRECT: ${data.toString().replace(/[\r\n]+/g, '')}`);
            }
            return this._write(data);
        }
        if (!responseId) {
            throw new Error('Invalid implementation: no responseId provided!');
        }
        if (!data) {
            this.log.warn(`${this.socketId} Not able to write ${JSON.stringify(data)}`);
            data = Resp.encodeError(new Error(`INVALID RESPONSE: ${JSON.stringify(data)}`));
        }

        setImmediate(() => this._sendQueued(responseId, data));
    }

    /**
     * Close network connection
     */
    close() {
        this.log.silly(`${this.socketId} close Redis connection`);
        this.stop = true;
        this.socket.end();
    }

    /**
     * Return if socket/handler active or closed
     * @returns {boolean} is Handler/Connection active (not closed)
     */
    isActive() {
        return !this.stop;
    }

    /**
     * Encode RESP's Null value to RESP buffer and send out
     * @param responseId ID of the response
     */
    sendNull(responseId) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeNull());
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeNull());
    }

    /**
     * Encode RESP's Null Array value to RESP buffer and send out
     * @param responseId ID of the response
     */
    sendNullArray(responseId) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeNullArray());
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeNullArray());
    }

    /**
     * Encode string to RESP buffer and send out
     * @param responseId ID od the response
     * @param str String to encode
     */
    sendString(responseId, str) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeString(str));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeString(str));
    }

    /**
     * Encode error object to RESP buffer and send out
     * @param responseId ID of the response
     * @param error Error object with error details to send out
     */
    sendError(responseId, error) {
        this.log.warn(`${this.socketId} Error from InMemDB: ${error}`);

        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeError(error));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeError(error));
    }

    /**
     * Encode integer to RESP buffer and send out
     * @param responseId ID of the response
     * @param num Integer to send out
     */
    sendInteger(responseId, num) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeInteger(num));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeInteger(num));
    }

    /**
     * Encode RESP's bulk string to RESP buffer and send out
     * @param responseId ID of the response
     * @param str String to send out
     */
    sendBulk(responseId, str) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeBulk(str));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeBulk(str));
    }

    /**
     * Encode RESP's bulk buffer to RESP buffer.
     * @param responseId ID of the response
     * @param buf Buffer to send out
     */
    sendBufBulk(responseId, buf) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeBufBulk(buf));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeBufBulk(buf));
    }

    /**
     * Encode an Array depending on the type of the elements
     * @param arr Array to encode
     * @returns {Array} Array with Buffers with encoded values
     */
    encodeRespArray(arr) {
        for (let i = 0; i < arr.length; i++) {
            if (Array.isArray(arr[i])) {
                arr[i] = this.encodeRespArray(arr[i]);
            } else if (Buffer.isBuffer(arr[i])) {
                arr[i] = Resp.encodeBufBulk(arr[i]);
            } else if (arr[i] === null) {
                arr[i] = Resp.encodeNull();
            } else if (typeof arr[i] === 'number') {
                arr[i] = Resp.encodeInteger(arr[i]);
            } else {
                arr[i] = Resp.encodeBulk(arr[i]);
            }
        }
        return arr;
    }

    /**
     * Encode a array values to buffers and send out
     * @param {number} responseId ID of the response
     * @param {any[]} arr Array to send out
     */
    sendArray(responseId, arr) {
        for (const i in this.activeMultiCalls) {
            if (this.activeMultiCalls[i].responseIds.includes(responseId)) {
                this._handleMultiResponse(responseId, i, Resp.encodeArray(this.encodeRespArray(arr)));
                return;
            }
        }

        this.sendResponse(responseId, Resp.encodeArray(this.encodeRespArray(arr)));
    }

    /**
     * Handles a 'multi' command
     *
     * @private
     */
    _handleMulti() {
        this.activeMultiCalls.unshift({
            responseIds: [],
            execCalled: false,
            responseCount: 0,
            responseMap: new Map()
        });
    }

    /**
     * Handles an 'exec' command
     *
     * @param {number} responseId ID of the response
     * @private
     */
    _handleExec(responseId) {
        if (!this.activeMultiCalls[0]) {
            this.sendError(responseId, new Error('EXEC without MULTI'));
            return;
        }

        this.activeMultiCalls[0].execId = responseId;
        this.activeMultiCalls[0].execCalled = true;

        // maybe we have all fullfilled yet
        if (this.activeMultiCalls[0].responseCount === this.activeMultiCalls[0].responseIds.length) {
            const multiRespObj = this.activeMultiCalls.shift();
            this._sendExecResponse(multiRespObj);
        }
    }

    /**
     * Builds up the exec response and sends it
     * @param {Record<string, any>} multiObj the multi object to send out
     *
     * @private
     */
    _sendExecResponse(multiObj) {
        // collect all 'QUEUED' answers
        const queuedStrArr = new Array(multiObj.responseCount).fill(QUEUED_STR_BUF);

        this._sendQueued(
            multiObj.execId,
            Buffer.concat([OK_STR_BUF, ...queuedStrArr, Resp.encodeArray(Array.from(multiObj.responseMap.values()))])
        );
    }

    /**
     * Handles a multi response
     *
     * @param {number} responseId ID of the response
     * @param {number} index index of the multi call
     * @param {Buffer} buf buffer to include in response
     * @private
     */
    _handleMultiResponse(responseId, index, buf) {
        this.activeMultiCalls[index].responseMap.set(responseId, buf);
        this.activeMultiCalls[index].responseCount++;
        if (
            this.activeMultiCalls[index].execCalled &&
            this.activeMultiCalls[index].responseCount === this.activeMultiCalls[index].responseIds.length
        ) {
            const multiRespObj = this.activeMultiCalls.splice(index, 1)[0];
            this._sendExecResponse(multiRespObj);
        }
    }
}

module.exports = RedisHandler;
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, compose } from 'redux';
import flatten from 'flatten';
import invariant from 'invariant';
import window from 'global/window';
import { returnSelf, isArray } from './utils';

export default function({
  reducers,
  initialState,
  plugin,
  sagaMiddleware,
  promiseMiddleware,
  createOpts: { setupMiddlewares = returnSelf },
}) {
  // extra enhancers
  const extraEnhancers = plugin.get('extraEnhancers');
  invariant(
    isArray(extraEnhancers),
    `[app.start] extraEnhancers should be array, but got ${typeof extraEnhancers}`
  );

  const extraMiddlewares = plugin.get('onAction');
  const middlewares = setupMiddlewares([
    promiseMiddleware,
    sagaMiddleware,
    ...flatten(extraMiddlewares),
  ]);

  const composeEnhancers =
  process.env.NODE_ENV !== "production" &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    : compose;

  const enhancers = [applyMiddleware(...middlewares), ...extraEnhancers];

  return createStore(reducers, initialState, composeEnhancers(...enhancers));
}
<commit_msg>feat: open redux-devtool trace history (#2096)<commit_after>import { createStore, applyMiddleware, compose } from 'redux';
import flatten from 'flatten';
import invariant from 'invariant';
import window from 'global/window';
import { returnSelf, isArray } from './utils';

export default function({
  reducers,
  initialState,
  plugin,
  sagaMiddleware,
  promiseMiddleware,
  createOpts: { setupMiddlewares = returnSelf },
}) {
  // extra enhancers
  const extraEnhancers = plugin.get('extraEnhancers');
  invariant(
    isArray(extraEnhancers),
    `[app.start] extraEnhancers should be array, but got ${typeof extraEnhancers}`,
  );

  const extraMiddlewares = plugin.get('onAction');
  const middlewares = setupMiddlewares([
    promiseMiddleware,
    sagaMiddleware,
    ...flatten(extraMiddlewares),
  ]);

  const composeEnhancers =
    process.env.NODE_ENV !== 'production' &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, maxAge: 30 })
      : compose;

  const enhancers = [applyMiddleware(...middlewares), ...extraEnhancers];

  return createStore(reducers, initialState, composeEnhancers(...enhancers));
}
<|endoftext|>