<commit_before>function WalkontableSettings(instance, settings) {
  var that = this;
  this.instance = instance;

  //default settings. void 0 means it is required, null means it can be empty
  this.defaults = {
    table: void 0,

    //presentation mode
    scrollH: 'auto', //values: scroll (always show scrollbar), auto (show scrollbar if table does not fit in the container), none (never show scrollbar)
    scrollV: 'auto', //values: see above
    stretchH: 'hybrid', //values: hybrid, all, last, none
    currentRowClassName: null,
    currentColumnClassName: null,

    //data source
    data: void 0,
    offsetRow: 0,
    offsetColumn: 0,
    fixedColumnsLeft: 0,
    fixedRowsTop: 0,
    rowHeaders: null, //this must be a function in format: function (row, TH) {}
    columnHeaders: null, //this must be a function in format: function (col, TH) {}
    totalRows: void 0,
    totalColumns: void 0,
    width: null,
    height: null,
    cellRenderer: function (row, column, TD) {
      var cellData = that.getSetting('data', row, column);
      that.instance.wtDom.avoidInnerHTML(TD, cellData === void 0 || cellData === null ? '' : cellData);
    },
    columnWidth: 50,
    selections: null,

    //callbacks
    onCellMouseDown: null,
    onCellMouseOver: null,
    onCellDblClick: null,
    onCellCornerMouseDown: null,
    onCellCornerDblClick: null,
    beforeDraw: null,
    onDraw: null,

    //constants
    scrollbarWidth: 10,
    scrollbarHeight: 10,
    rowHeaderWidth: 50
  };

  //reference to settings
  this.settings = {};
  for (var i in this.defaults) {
    if (this.defaults.hasOwnProperty(i)) {
      if (settings[i] !== void 0) {
        this.settings[i] = settings[i];
      }
      else if (this.defaults[i] === void 0) {
        throw new Error('A required setting "' + i + '" was not provided');
      }
      else {
        this.settings[i] = this.defaults[i];
      }
    }
  }
}

/**
 * generic methods
 */

WalkontableSettings.prototype.update = function (settings, value) {
  if (value === void 0) { //settings is object
    for (var i in settings) {
      if (settings.hasOwnProperty(i)) {
        this.settings[i] = settings[i];
      }
    }
  }
  else { //if value is defined then settings is the key
    this.settings[settings] = value;
  }
  return this.instance;
};

WalkontableSettings.prototype.getSetting = function (key, param1, param2, param3) {
  if (this[key]) {
    return this[key](param1, param2, param3);
  }
  else {
    return this._getSetting(key, param1, param2, param3);
  }
};

WalkontableSettings.prototype._getSetting = function (key, param1, param2, param3) {
  if (typeof this.settings[key] === 'function') {
    return this.settings[key](param1, param2, param3);
  }
  else if (param1 !== void 0 && Object.prototype.toString.call(this.settings[key]) === '[object Array]' && param1 !== void 0) {
    return this.settings[key][param1];
  }
  else {
    return this.settings[key];
  }
};

WalkontableSettings.prototype.has = function (key) {
  return !!this.settings[key]
};

/**
 * specific methods
 */

WalkontableSettings.prototype.rowHeight = function (row) {
  var visible_r = this.instance.wtTable.rowFilter.sourceToVisible(row);
  var size = this.instance.wtTable.rowStrategy.getSize(visible_r);
  if (size !== void 0) {
    return size;
  }
  return 20;
};

WalkontableSettings.prototype.columnWidth = function (column) {
  return Math.min(200, this._getSetting('columnWidth', column));
};<commit_msg>remove hardcoded rowHeaderWidth<commit_after>function WalkontableSettings(instance, settings) {
  var that = this;
  this.instance = instance;

  //default settings. void 0 means it is required, null means it can be empty
  this.defaults = {
    table: void 0,

    //presentation mode
    scrollH: 'auto', //values: scroll (always show scrollbar), auto (show scrollbar if table does not fit in the container), none (never show scrollbar)
    scrollV: 'auto', //values: see above
    stretchH: 'hybrid', //values: hybrid, all, last, none
    currentRowClassName: null,
    currentColumnClassName: null,

    //data source
    data: void 0,
    offsetRow: 0,
    offsetColumn: 0,
    fixedColumnsLeft: 0,
    fixedRowsTop: 0,
    rowHeaders: null, //this must be a function in format: function (row, TH) {}
    columnHeaders: null, //this must be a function in format: function (col, TH) {}
    totalRows: void 0,
    totalColumns: void 0,
    width: null,
    height: null,
    cellRenderer: function (row, column, TD) {
      var cellData = that.getSetting('data', row, column);
      that.instance.wtDom.avoidInnerHTML(TD, cellData === void 0 || cellData === null ? '' : cellData);
    },
    columnWidth: 50,
    selections: null,

    //callbacks
    onCellMouseDown: null,
    onCellMouseOver: null,
    onCellDblClick: null,
    onCellCornerMouseDown: null,
    onCellCornerDblClick: null,
    beforeDraw: null,
    onDraw: null,

    //constants
    scrollbarWidth: 10,
    scrollbarHeight: 10
  };

  //reference to settings
  this.settings = {};
  for (var i in this.defaults) {
    if (this.defaults.hasOwnProperty(i)) {
      if (settings[i] !== void 0) {
        this.settings[i] = settings[i];
      }
      else if (this.defaults[i] === void 0) {
        throw new Error('A required setting "' + i + '" was not provided');
      }
      else {
        this.settings[i] = this.defaults[i];
      }
    }
  }
}

/**
 * generic methods
 */

WalkontableSettings.prototype.update = function (settings, value) {
  if (value === void 0) { //settings is object
    for (var i in settings) {
      if (settings.hasOwnProperty(i)) {
        this.settings[i] = settings[i];
      }
    }
  }
  else { //if value is defined then settings is the key
    this.settings[settings] = value;
  }
  return this.instance;
};

WalkontableSettings.prototype.getSetting = function (key, param1, param2, param3) {
  if (this[key]) {
    return this[key](param1, param2, param3);
  }
  else {
    return this._getSetting(key, param1, param2, param3);
  }
};

WalkontableSettings.prototype._getSetting = function (key, param1, param2, param3) {
  if (typeof this.settings[key] === 'function') {
    return this.settings[key](param1, param2, param3);
  }
  else if (param1 !== void 0 && Object.prototype.toString.call(this.settings[key]) === '[object Array]' && param1 !== void 0) {
    return this.settings[key][param1];
  }
  else {
    return this.settings[key];
  }
};

WalkontableSettings.prototype.has = function (key) {
  return !!this.settings[key]
};

/**
 * specific methods
 */

WalkontableSettings.prototype.rowHeight = function (row) {
  var visible_r = this.instance.wtTable.rowFilter.sourceToVisible(row);
  var size = this.instance.wtTable.rowStrategy.getSize(visible_r);
  if (size !== void 0) {
    return size;
  }
  return 20;
};

WalkontableSettings.prototype.columnWidth = function (column) {
  return Math.min(200, this._getSetting('columnWidth', column));
};<|endoftext|>


<commit_before>/************************************************************

 * Problem: https://leetcode.com/problems/binary-tree-inorder-traversal/
    Given a binary tree, return the inorder traversal of its nodes' values.

 * Example 1:
        1
        \
         2
        /
       3
    return [1,3,2]

 * @param {TreeNode} root
 * @return {number[]}

 * Analysis: In theory, inorder should still be DFS, so we can try that.

************************************************************/

import { TreeNode } from "../_.util/binaryTree";

export const bTreeInOrderTraversal = {};

/**
 * Solution 1: use dfs
 *
 * "N" is node count
 * Time complexity: O(N)
 * Space complexity: O(N)
 */
bTreeInOrderTraversal.dfs = (root) => {
    const isNode = (node) => {
            return (node instanceof TreeNode) && node.val !== null;
        },
        result = [],
        dfsVal = (node) => {
            if (isNode(node)) {
                if (isNode(node.left)) {
                    dfsVal(node.left);
                }

                result.push(node.val);

                if (isNode(node.right)) {
                    dfsVal(node.right);
                }
            }
        };

    dfsVal(root);

    return result;
};

/************************************************************

 * Lessons:
   1. In-order is basically dfs, and dfs is recursion.

************************************************************/
<commit_msg>added a tip.<commit_after>/************************************************************

 * Problem: https://leetcode.com/problems/binary-tree-inorder-traversal/
    Given a binary tree, return the inorder traversal of its nodes' values.

 * Example 1:
        1
        \
         2
        /
       3
    return [1,3,2]

 * @param {TreeNode} root
 * @return {number[]}

 * Analysis: In theory, inorder should still be DFS, so we can try that.

************************************************************/

import { TreeNode } from "../_.util/binaryTree";

export const bTreeInOrderTraversal = {};

/**
 * Solution 1: use dfs
 *
 * "N" is node count
 * Time complexity: O(N)
 * Space complexity: O(N)
 */
bTreeInOrderTraversal.dfs = (root) => {
    const isNode = (node) => {
            return (node instanceof TreeNode) && node.val !== null;
        },
        result = [],
        dfsVal = (node) => {
            if (isNode(node)) {
                if (isNode(node.left)) {
                    dfsVal(node.left);
                }

                result.push(node.val);

                if (isNode(node.right)) {
                    dfsVal(node.right);
                }
            }
        };

    dfsVal(root);

    return result;
};

/************************************************************

 * Lessons:
   1. In-order is basically dfs, and dfs is recursion.
   2. One thing to note is that in-order for BST will traverse the tree in sorted order.

************************************************************/
<|endoftext|>


<commit_before>function ArtworkEditController(artwork, movements, $state, $scope, $http) {
    this.data = artwork.data["artwork"]
    this.movements = movements.data.movements
    $scope.selection = [];

    $scope.toggleSelection = function(movementId) {
      var id = $scope.selection.indexOf(movementId)
      if(id > -1){
        $scope.selection.splice(id, 1)
      } else {
        $scope.selection.push(movementId)
      }
    }

    $scope.updateArtwork = function(newArtwork) {
      newArtwork.movements = $scope.selection
      $http.patch('http://localhost:3000/artworks/' + newArtwork.id, newArtwork)
    }

}

angular
  .module('app')
  .controller('ArtworkEditController', ArtworkEditController)
<commit_msg>add artwork movements to .selection<commit_after>function ArtworkEditController(artwork, movements, $state, $scope, $http) {
    this.data = artwork.data["artwork"]
    this.movements = movements.data.movements
    $scope.selection = [];

    artwork.data.artwork.movements.forEach(function(movement) {
      $scope.selection.push(movement.id)
    })

    $scope.toggleSelection = function(movementId) {
      var id = $scope.selection.indexOf(movementId)
      if(id > -1){
        $scope.selection.splice(id, 1)
      } else {
        $scope.selection.push(movementId)
      }
    }

    $scope.updateArtwork = function(newArtwork) {
      newArtwork.movements = $scope.selection
      $http.patch('http://localhost:3000/artworks/' + newArtwork.id, newArtwork)
    }

}

angular
  .module('app')
  .controller('ArtworkEditController', ArtworkEditController)
<|endoftext|>


<commit_before>function ArtworkEditController(artwork, movements, $scope, $http) {
    this.data = artwork.data["artwork"]
    this.movements = movements.data.movements
    $scope.selection = [];

    $scope.toggleSelection = function(movementId) {
      var id = $scope.selection.indexOf(movementId)
      if(id > -1){
        $scope.selection.splice(id, 1)
      } else {
        $scope.selection.push(movementId)
      }
    }
}

angular
  .module('app')
  .controller('ArtworkEditController', ArtworkEditController)
<commit_msg>add .updateArtwork function to ArtworkEditController<commit_after>function ArtworkEditController(artwork, movements, $scope, $http) {
    this.data = artwork.data["artwork"]
    this.movements = movements.data.movements
    $scope.selection = [];

    $scope.toggleSelection = function(movementId) {
      var id = $scope.selection.indexOf(movementId)
      if(id > -1){
        $scope.selection.splice(id, 1)
      } else {
        $scope.selection.push(movementId)
      }
    }

    $scope.updateArtwork = function() {
      $scope.artwork.movements = $scope.selection
      $http.patch('http://localhost:3000/artworks/' + $scope.artwork.id, $scope.artwork)
      $state.reload();
      alert("artwork successfully updated!")
    }

}

angular
  .module('app')
  .controller('ArtworkEditController', ArtworkEditController)
<|endoftext|>


<commit_before>import { isValidLink } from 'discourse/lib/click-track';
import { number } from 'discourse/lib/formatter';

const _decorators = [];

// Don't call this directly: use `plugin-api/decorateCooked`
export function addDecorator(cb) {
  _decorators.push(cb);
}

export default class PostCooked {

  constructor(attrs, getModel) {
    this.attrs = attrs;
    this.expanding = false;
    this._highlighted = false;
    this.getModel = getModel;
  }

  update(prev) {
    if (prev.attrs.cooked !== this.attrs.cooked) {
      return this.init();
    }
  }

  init() {
    const $html = $(`<div class='cooked'>${this.attrs.cooked}</div>`);
    this._insertQuoteControls($html);
    this._showLinkCounts($html);
    this._fixImageSizes($html);
    this._applySearchHighlight($html);

    _decorators.forEach(cb => cb($html, this.getModel));
    return $html[0];
  }

  _applySearchHighlight($html) {
    const highlight = this.attrs.highlightTerm;

    if (highlight && highlight.length > 2) {
      if (this._highlighted) {
         $html.unhighlight();
      }
      $html.highlight(highlight.split(/\s+/));
      this._highlighted = true;

    } else if (this._highlighted) {
      $html.unhighlight();
      this._highlighted = false;
    }
  }

  _fixImageSizes($html) {
    const maxImageWidth = Discourse.SiteSettings.max_image_width;
    const maxImageHeight = Discourse.SiteSettings.max_image_height;

    let maxWindowWidth;
    $html.find('img:not(.avatar)').each((idx,img) => {
      // deferring work only for posts with images
      // we got to use screen here, cause nothing is rendered yet.
      // long term we may want to allow for weird margins that are enforced, instead of hardcoding at 70/20
      maxWindowWidth = maxWindowWidth || $(window).width() - (this.attrs.mobileView ? 20 : 70);
      if (maxImageWidth < maxWindowWidth) {
        maxWindowWidth = maxImageWidth;
      }

      const aspect = img.height / img.width;
      if (img.width > maxWindowWidth) {
        img.width = maxWindowWidth;
        img.height = parseInt(maxWindowWidth * aspect,10);
      }

      // very unlikely but lets fix this too
      if (img.height > maxImageHeight) {
        img.height = maxImageHeight;
        img.width = parseInt(maxWindowWidth / aspect,10);
      }
    });
  }

  _showLinkCounts($html) {
    const linkCounts = this.attrs.linkCounts;
    if (!linkCounts) { return; }

    linkCounts.forEach(lc => {
      if (!lc.clicks || lc.clicks < 1) { return; }

      $html.find('a[href]').each((i, e) => {
        const $link = $(e);
        const href = $link.attr('href');

        let valid = !lc.internal && href === lc.url;

        // this might be an attachment
        if (lc.internal) { valid = href.indexOf(lc.url) >= 0; }

        // don't display badge counts on category badge & oneboxes (unless when explicitely stated)
        if (valid && isValidLink($link)) {
          const title = I18n.t("topic_map.clicks", {count: lc.clicks});
          $link.append(`<span class='badge badge-notification clicks' title='${title}'>${number(lc.clicks)}</span>`);
        }
      });
    });
  }

  _toggleQuote($aside) {
    if (this.expanding) { return; }

    this.expanding = true;

    $aside.data('expanded', !$aside.data('expanded'));

    const finished = () => this.expanding = false;

    if ($aside.data('expanded')) {
      this._updateQuoteElements($aside, 'chevron-up');
      // Show expanded quote
      const $blockQuote = $('blockquote', $aside);
      $aside.data('original-contents', $blockQuote.html());

      const originalText = $blockQuote.text().trim();
      $blockQuote.html(I18n.t("loading"));
      let topicId = this.attrs.topicId;
      if ($aside.data('topic')) {
        topicId = $aside.data('topic');
      }

      const postId = parseInt($aside.data('post'), 10);
      topicId = parseInt(topicId, 10);

      Discourse.ajax(`/posts/by_number/${topicId}/${postId}`).then(result => {
        const div = $("

");
        div.html(result.cooked);
        div.highlight(originalText, {caseSensitive: true, element: 'span', className: 'highlighted'});
        $blockQuote.showHtml(div, 'fast', finished);
      });
    } else {
      // Hide expanded quote
      this._updateQuoteElements($aside, 'chevron-down');
      $('blockquote', $aside).showHtml($aside.data('original-contents'), 'fast', finished);
    }
    return false;
  }

  _urlForPostNumber(postNumber) {
    return (postNumber > 0) ? `${this.topicUrl}/${postNumber}` : this.topicUrl;
  }

  _updateQuoteElements($aside, desc) {
    let navLink = "";
    const quoteTitle = I18n.t("post.follow_quote");
    const postNumber = $aside.data('post');

    if (postNumber) {

      // If we have a topic reference
      const asideTopicId = parseInt($aside.data('topic'));

      if (asideTopicId) {
        // If it's the same topic as ours, build the URL from the topic object
        if (this.attrs.topicId === asideTopicId) {
          navLink = `<a href='${this._urlForPostNumber(postNumber)}' title='${quoteTitle}' class='back'></a>`;
        } else {
          // Made up slug should be replaced with canonical URL
          const asideLink = Discourse.getURL("/t/via-quote/") + asideTopicId + "/" + postNumber;
          navLink = `<a href='${asideLink}' title='${quoteTitle}' class='quote-other-topic'></a>`;
        }

      } else {
        // assume the same topic
        navLink = `<a href='${this._urlForPostNumber(postNumber)}' title='${quoteTitle}' class='back'></a>`;
      }
    }
    // Only add the expand/contract control if it's not a full post
    let expandContract = "";
    if (!$aside.data('full')) {
      expandContract = `<i class='fa fa-${desc}' title='${I18n.t("post.expand_collapse")}'></i>`;
      $('.title', $aside).css('cursor', 'pointer');
    }
    $('.quote-controls', $aside).html(expandContract + navLink);
  }

  _insertQuoteControls($html) {
    const $quotes = $html.find('aside.quote');
    if ($quotes.length === 0) { return; }

    $quotes.each((i, e) => {
      const $aside = $(e);
      if ($aside.data('post')) {
        this._updateQuoteElements($aside, 'chevron-down');
        const $title = $('.title', $aside);

        // Unless it's a full quote, allow click to expand
        if (!($aside.data('full') || $title.data('has-quote-controls'))) {
          $title.on('click', e2 => {
            if ($(e2.target).is('a')) return true;
            this._toggleQuote($aside);
          });
          $title.data('has-quote-controls', true);
        }
      }
    });
  }
}

PostCooked.prototype.type = 'Widget';
<commit_msg>Backported double click fix<commit_after>import { isValidLink } from 'discourse/lib/click-track';
import { number } from 'discourse/lib/formatter';

const _decorators = [];

// Don't call this directly: use `plugin-api/decorateCooked`
export function addDecorator(cb) {
  _decorators.push(cb);
}

export default class PostCooked {

  constructor(attrs, getModel) {
    this.attrs = attrs;
    this.expanding = false;
    this._highlighted = false;
    this.getModel = getModel;
  }

  update(prev) {
    if (prev.attrs.cooked !== this.attrs.cooked) {
      return this.init();
    }
  }

  init() {
    const $html = $(`<div class='cooked'>${this.attrs.cooked}</div>`);
    this._insertQuoteControls($html);
    this._showLinkCounts($html);
    this._fixImageSizes($html);
    this._applySearchHighlight($html);

    _decorators.forEach(cb => cb($html, this.getModel));
    return $html[0];
  }

  _applySearchHighlight($html) {
    const highlight = this.attrs.highlightTerm;

    if (highlight && highlight.length > 2) {
      if (this._highlighted) {
         $html.unhighlight();
      }
      $html.highlight(highlight.split(/\s+/));
      this._highlighted = true;

    } else if (this._highlighted) {
      $html.unhighlight();
      this._highlighted = false;
    }
  }

  _fixImageSizes($html) {
    const maxImageWidth = Discourse.SiteSettings.max_image_width;
    const maxImageHeight = Discourse.SiteSettings.max_image_height;

    let maxWindowWidth;
    $html.find('img:not(.avatar)').each((idx,img) => {
      // deferring work only for posts with images
      // we got to use screen here, cause nothing is rendered yet.
      // long term we may want to allow for weird margins that are enforced, instead of hardcoding at 70/20
      maxWindowWidth = maxWindowWidth || $(window).width() - (this.attrs.mobileView ? 20 : 70);
      if (maxImageWidth < maxWindowWidth) {
        maxWindowWidth = maxImageWidth;
      }

      const aspect = img.height / img.width;
      if (img.width > maxWindowWidth) {
        img.width = maxWindowWidth;
        img.height = parseInt(maxWindowWidth * aspect,10);
      }

      // very unlikely but lets fix this too
      if (img.height > maxImageHeight) {
        img.height = maxImageHeight;
        img.width = parseInt(maxWindowWidth / aspect,10);
      }
    });
  }

  _showLinkCounts($html) {
    const linkCounts = this.attrs.linkCounts;
    if (!linkCounts) { return; }

    linkCounts.forEach(lc => {
      if (!lc.clicks || lc.clicks < 1) { return; }

      $html.find('a[href]').each((i, e) => {
        const $link = $(e);
        const href = $link.attr('href');

        let valid = href === lc.url;

        // this might be an attachment
        if (lc.internal && /^\/uploads\//.test(lc.url)) {
          valid = href.indexOf(lc.url) >= 0;
        }

        // don't display badge counts on category badge & oneboxes (unless when explicitely stated)
        if (valid && isValidLink($link)) {
          const title = I18n.t("topic_map.clicks", {count: lc.clicks});
          $link.append(`<span class='badge badge-notification clicks' title='${title}'>${number(lc.clicks)}</span>`);
        }
      });
    });
  }

  _toggleQuote($aside) {
    if (this.expanding) { return; }

    this.expanding = true;

    $aside.data('expanded', !$aside.data('expanded'));

    const finished = () => this.expanding = false;

    if ($aside.data('expanded')) {
      this._updateQuoteElements($aside, 'chevron-up');
      // Show expanded quote
      const $blockQuote = $('blockquote', $aside);
      $aside.data('original-contents', $blockQuote.html());

      const originalText = $blockQuote.text().trim();
      $blockQuote.html(I18n.t("loading"));
      let topicId = this.attrs.topicId;
      if ($aside.data('topic')) {
        topicId = $aside.data('topic');
      }

      const postId = parseInt($aside.data('post'), 10);
      topicId = parseInt(topicId, 10);

      Discourse.ajax(`/posts/by_number/${topicId}/${postId}`).then(result => {
        const div = $("

");
        div.html(result.cooked);
        div.highlight(originalText, {caseSensitive: true, element: 'span', className: 'highlighted'});
        $blockQuote.showHtml(div, 'fast', finished);
      });
    } else {
      // Hide expanded quote
      this._updateQuoteElements($aside, 'chevron-down');
      $('blockquote', $aside).showHtml($aside.data('original-contents'), 'fast', finished);
    }
    return false;
  }

  _urlForPostNumber(postNumber) {
    return (postNumber > 0) ? `${this.topicUrl}/${postNumber}` : this.topicUrl;
  }

  _updateQuoteElements($aside, desc) {
    let navLink = "";
    const quoteTitle = I18n.t("post.follow_quote");
    const postNumber = $aside.data('post');

    if (postNumber) {

      // If we have a topic reference
      const asideTopicId = parseInt($aside.data('topic'));

      if (asideTopicId) {
        // If it's the same topic as ours, build the URL from the topic object
        if (this.attrs.topicId === asideTopicId) {
          navLink = `<a href='${this._urlForPostNumber(postNumber)}' title='${quoteTitle}' class='back'></a>`;
        } else {
          // Made up slug should be replaced with canonical URL
          const asideLink = Discourse.getURL("/t/via-quote/") + asideTopicId + "/" + postNumber;
          navLink = `<a href='${asideLink}' title='${quoteTitle}' class='quote-other-topic'></a>`;
        }

      } else {
        // assume the same topic
        navLink = `<a href='${this._urlForPostNumber(postNumber)}' title='${quoteTitle}' class='back'></a>`;
      }
    }
    // Only add the expand/contract control if it's not a full post
    let expandContract = "";
    if (!$aside.data('full')) {
      expandContract = `<i class='fa fa-${desc}' title='${I18n.t("post.expand_collapse")}'></i>`;
      $('.title', $aside).css('cursor', 'pointer');
    }
    $('.quote-controls', $aside).html(expandContract + navLink);
  }

  _insertQuoteControls($html) {
    const $quotes = $html.find('aside.quote');
    if ($quotes.length === 0) { return; }

    $quotes.each((i, e) => {
      const $aside = $(e);
      if ($aside.data('post')) {
        this._updateQuoteElements($aside, 'chevron-down');
        const $title = $('.title', $aside);

        // Unless it's a full quote, allow click to expand
        if (!($aside.data('full') || $title.data('has-quote-controls'))) {
          $title.on('click', e2 => {
            if ($(e2.target).is('a')) return true;
            this._toggleQuote($aside);
          });
          $title.data('has-quote-controls', true);
        }
      }
    });
  }
}

PostCooked.prototype.type = 'Widget';
<|endoftext|>


<commit_before>/*= require filtered_search/filtered_search_dropdown */

/* global droplabFilter */

(() => {
  class DropdownHint extends gl.FilteredSearchDropdown {
    constructor(droplab, dropdown, input, filter) {
      super(droplab, dropdown, input, filter);
      this.config = {
        droplabFilter: {
          template: 'hint',
          filterFunction: gl.DropdownUtils.filterHint,
        },
      };
    }

    itemClicked(e) {
      const { selected } = e.detail;

      if (selected.tagName === 'LI') {
        if (selected.hasAttribute('data-value')) {
          this.dismissDropdown();
        } else {
          const token = selected.querySelector('.js-filter-hint').innerText.trim();
          const tag = selected.querySelector('.js-filter-tag').innerText.trim();

          if (tag.length) {
            gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''));
          }
          this.dismissDropdown();
          this.dispatchInputEvent();
        }
      }
    }

    renderContent() {
      const dropdownData = [{
        icon: 'fa-pencil',
        hint: 'author:',
        tag: '<author>',
      }, {
        icon: 'fa-user',
        hint: 'assignee:',
        tag: '<assignee>',
      }, {
        icon: 'fa-clock-o',
        hint: 'milestone:',
        tag: '<milestone>',
      }, {
        icon: 'fa-tag',
        hint: 'label:',
        tag: '<label>',
      }];

      this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
      this.droplab.setData(this.hookId, dropdownData);
    }

    init() {
      this.droplab.addHook(this.input, this.dropdown, [droplabFilter], this.config).init();
    }
  }

  window.gl = window.gl || {};
  gl.DropdownHint = DropdownHint;
})();
<commit_msg>Add symbols<commit_after>/*= require filtered_search/filtered_search_dropdown */

/* global droplabFilter */

(() => {
  class DropdownHint extends gl.FilteredSearchDropdown {
    constructor(droplab, dropdown, input, filter) {
      super(droplab, dropdown, input, filter);
      this.config = {
        droplabFilter: {
          template: 'hint',
          filterFunction: gl.DropdownUtils.filterHint,
        },
      };
    }

    itemClicked(e) {
      const { selected } = e.detail;

      if (selected.tagName === 'LI') {
        if (selected.hasAttribute('data-value')) {
          this.dismissDropdown();
        } else {
          const token = selected.querySelector('.js-filter-hint').innerText.trim();
          const tag = selected.querySelector('.js-filter-tag').innerText.trim();

          if (tag.length) {
            gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''));
          }
          this.dismissDropdown();
          this.dispatchInputEvent();
        }
      }
    }

    renderContent() {
      const dropdownData = [{
        icon: 'fa-pencil',
        hint: 'author:',
        tag: '<@author>',
      }, {
        icon: 'fa-user',
        hint: 'assignee:',
        tag: '<@assignee>',
      }, {
        icon: 'fa-clock-o',
        hint: 'milestone:',
        tag: '<%milestone>',
      }, {
        icon: 'fa-tag',
        hint: 'label:',
        tag: '<~label>',
      }];

      this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
      this.droplab.setData(this.hookId, dropdownData);
    }

    init() {
      this.droplab.addHook(this.input, this.dropdown, [droplabFilter], this.config).init();
    }
  }

  window.gl = window.gl || {};
  gl.DropdownHint = DropdownHint;
})();
<|endoftext|>


<commit_before>(function () {
	'use strict';

	/**
	 *	OCRManager constructor
	 */
	function OCRManager(container, editor) {
		this.container = container;
		this.editor = editor;
		this.selectedImage;
		this.disable();
		this.dialog = undefined;
	}

	/**
	*	 for now, no need to remove it
	*/
	OCRManager.prototype.attachListeners = function () {
		var self = this;

		this.container.on('click', function (evt) {
			self.onClick(evt.data);
		});
	};

	/**
	 * set the button to active state
	 */
	OCRManager.prototype.setActive = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_ON);
	};

	/**
	 * set the button to inactive state
	 */
	OCRManager.prototype.setInactive = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_OFF);
	};

	/**
	 * disable the button
	 */
	OCRManager.prototype.disable = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_DISABLED);
	};

	/**
	 * enable the button
	 */
	OCRManager.prototype.enable = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_OFF);
	};

	/**
	 *	returns true if the button is disabled
	 */
	OCRManager.prototype.isDisabled = function () {
		return this.editor.ui.get('Ocerisation').getState() === CKEDITOR.TRISTATE_DISABLED;
	};

	/**
	 * on click calback
	 *		check if click event is on an image
	 *		then check if the image is base64 encoded
	 */
	OCRManager.prototype.onClick = function (event) {
		var selectedElement = event.getTarget();
		if (selectedElement.getName() === 'img' && this.isDataUrl(selectedElement.getAttribute('src'))) {
			this.selectedImage = selectedElement;
			this.enable();
		} else {
			this.selectedImagSrc = undefined;
			this.disable();
		}

	};

	//https://gist.github.com/bgrins/6194623
	OCRManager.prototype.isDataUrl = function (str) {
		//https://gist.github.com/bgrins/6194623
		var rgx = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
		return !!str.match(rgx);
	}

	/**
	 * ocerization entry method
	 */
	OCRManager.prototype.ocerize = function () {

		var self = this;
		if (this.selectedImage.getName() === 'img' && this.isDataUrl(this.selectedImage.getAttribute('src'))) {
			this.setActive();
			var dataToOcerize = this.selectedImage.getAttribute('src');

			if (!this.dialog) {
				this.dialog = this.editor.openDialog('loading');
			} else {
				//if dialog already instanciated => show and remove listeners
				this.dialog.show();
				this.dialog.removeAllListeners();
			}

			this.dialog.on('show', function (evt) {

				var params = '{"id":"' + localStorage.getItem('compteId') + '","encodedImg":"' + dataToOcerize + '"}';
				var oReq = new XMLHttpRequest();
				oReq.open('POST', '/oceriser', true);
				oReq.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

				oReq.onload = function () {
					// production
					//var imageOpt = oReq.response;
					//integration
					var imageOpt = JSON.parse(oReq.response).data;
					var ocerisedTxt;
					// l'océrisation se passe ici
					tesseractJS.FS_createDataFile('/', 'tempInput.jpg', imageOpt, true, true);
					var fnct_TESSERACT_Minimal = tesseractJS.cwrap(
						// name of C function
						'TESSERACT_Minimal',
						// return type
						'number',
						// argument types
								 ['string', 'string', 'number', 'number']
					); // arguments

					//Renvoie de l'HOCR, mettre le 3eme pram à 0 pour UTF8
					var retSTRING_Pointer = fnct_TESSERACT_Minimal('tempInput.jpg', 'fra', 0, -1);
					// Convert the resulting string to a JS string
					var retSTRING = tesseractJS.Pointer_stringify(retSTRING_Pointer);
					//Supprime le fichier temp
					try {
						tesseractJS.FS_unlink('/tempInput.jpg');
					} catch (e) {
						// catch fichier n'existe pas
						console.log(e);
					}

					// retirer popup d'attente
					self.dialog.hide();

					self.appendOcerizedText(retSTRING);
					self.setInactive();
				}

				oReq.send(params);
			});
		}
	};

	/**
	 *	adds ocerized text on top of the clicked image
	 */
	OCRManager.prototype.appendOcerizedText = function (ocerisedTxt) {
		var txtNode = new CKEDITOR.dom.element('p');
		txtNode.appendText(ocerisedTxt);
		txtNode.insertBefore(this.selectedImage);
		document.getSelection().removeAllRanges();
	};

	OCRManager.prototype.initializeTesseract = function (ocerisedTxt) {
		var oReq = new XMLHttpRequest();
		oReq.open('GET', '/bower_components/tesseractJS/tesseract-ocr/tessdata/fra.traineddata', true);
		oReq.responseType = 'blob';

		oReq.onload = function () {
			var blob = oReq.response;
			if (blob) {
				var reader = new FileReader();
				reader.onloadend = function (evt) {
					if (evt.target.readyState === FileReader.DONE) {
						var binary = '';
						var bytes = new Uint8Array(evt.target.result);
						var len = bytes.byteLength;
						for (var i = 0; i < len; i++) {
							binary += String.fromCharCode(bytes[i]);
						}

						tesseractJS.FS_createDataFile('/tesseract-ocr/tessdata/', 'fra.traineddata', binary, true, true);
					}
				};
				reader.readAsArrayBuffer(blob);
			}
		};
		oReq.send(null);
	}

	/**
	 *	Plugin initialization
	 */
	CKEDITOR.plugins.add('ocerisation', {
		icons: 'ocerisation',
		init: function (editor) {

			var dialog = CKEDITOR.dialog.add('loading', function (editor) {
				var dialogDefinition = {
					title: 'Traitement en cours...',
					minWidth: 100,
					minHeight: 60,
					resizable: CKEDITOR.DIALOG_RESIZE_NONE,
					contents: [{
						id: 'loading',
						label: 'Chargement',
						title: 'Traitement en cours...',
						elements: [
							{
								type: 'html',
								html: '
Le traitement de reconnaissance des caractères est en cours.
La durée de ce traitement peut être importante.

Veuillez patienter...
',
								align: 'center'
							}
						]
						}],
					// needs a hidden button to replace default ones
					buttons: [
						{
							type: 'button',
							id: 'hiddenBtn',
							label: 'none',
							hidden: true
						}
					]
				};
				return dialogDefinition;
			});
			var manager;

			editor.ui.addButton('Ocerisation', {
				label: 'Lancer la reconnaissance des caractères',
				command: 'ocerisation',
				toolbar: 'insert' // TODO: change

			});

			editor.addCommand('ocerisation', {
				exec: function (editor) {
					manager.ocerize();
				}
			});

			editor.on('contentDom', function (e) {
				manager = new OCRManager(this.container, editor);
				manager.attachListeners();
				manager.initializeTesseract();
			});

		}
	});

})();<commit_msg>Correction du plugin OCR<commit_after>(function () {
	'use strict';

	/**
	 *	OCRManager constructor
	 */
	function OCRManager(container, editor) {
		this.container = container;
		this.editor = editor;
		this.selectedImage;
		this.disable();
		this.dialog = undefined;
	}

	/**
	*	 for now, no need to remove it
	*/
	OCRManager.prototype.attachListeners = function () {
		var self = this;

		this.container.on('click', function (evt) {
			self.onClick(evt.data);
		});
	};

	/**
	 * set the button to active state
	 */
	OCRManager.prototype.setActive = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_ON);
	};

	/**
	 * set the button to inactive state
	 */
	OCRManager.prototype.setInactive = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_OFF);
	};

	/**
	 * disable the button
	 */
	OCRManager.prototype.disable = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_DISABLED);
	};

	/**
	 * enable the button
	 */
	OCRManager.prototype.enable = function () {
		this.editor.ui.get('Ocerisation').setState(CKEDITOR.TRISTATE_OFF);
	};

	/**
	 *	returns true if the button is disabled
	 */
	OCRManager.prototype.isDisabled = function () {
		return this.editor.ui.get('Ocerisation').getState() === CKEDITOR.TRISTATE_DISABLED;
	};

	/**
	 * on click calback
	 *		check if click event is on an image
	 *		then check if the image is base64 encoded
	 */
	OCRManager.prototype.onClick = function (event) {
		var selectedElement = event.getTarget();
		if (selectedElement.getName() === 'img' && this.isDataUrl(selectedElement.getAttribute('src'))) {
			this.selectedImage = selectedElement;
			this.enable();
		} else {
			this.selectedImagSrc = undefined;
			this.disable();
		}

	};

	//https://gist.github.com/bgrins/6194623
	OCRManager.prototype.isDataUrl = function (str) {
		//https://gist.github.com/bgrins/6194623
		var rgx = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
		return !!str.match(rgx);
	}

	/**
	 * ocerization entry method
	 */
	OCRManager.prototype.ocerize = function () {

		var self = this;
		if (this.selectedImage.getName() === 'img' && this.isDataUrl(this.selectedImage.getAttribute('src'))) {
			this.setActive();
			var dataToOcerize = this.selectedImage.getAttribute('src');

			if (!this.dialog) {
				this.dialog = this.editor.openDialog('loading');
			} else {
				//if dialog already instanciated => show and remove listeners
				this.dialog.show();
				this.dialog.removeAllListeners();
			}

			this.dialog.on('show', function (evt) {

				var params = '{"id":"' + localStorage.getItem('compteId') + '","encodedImg":"' + dataToOcerize + '"}';
				var oReq = new XMLHttpRequest();
				oReq.open('POST', '/oceriser', true);
				oReq.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

				oReq.onload = function () {
					// production
					//var imageOpt = oReq.response;
					//integration
					var imageOpt = JSON.parse(oReq.response);
					var ocerisedTxt;
					// l'océrisation se passe ici
					tesseractJS.FS_createDataFile('/', 'tempInput.jpg', imageOpt, true, true);
					var fnct_TESSERACT_Minimal = tesseractJS.cwrap(
						// name of C function
						'TESSERACT_Minimal',
						// return type
						'number',
						// argument types
								 ['string', 'string', 'number', 'number']
					); // arguments

					//Renvoie de l'HOCR, mettre le 3eme pram à 0 pour UTF8
					var retSTRING_Pointer = fnct_TESSERACT_Minimal('tempInput.jpg', 'fra', 0, -1);
					// Convert the resulting string to a JS string
					var retSTRING = tesseractJS.Pointer_stringify(retSTRING_Pointer);
					//Supprime le fichier temp
					try {
						tesseractJS.FS_unlink('/tempInput.jpg');
					} catch (e) {
						// catch fichier n'existe pas
						console.log(e);
					}

					// retirer popup d'attente
					self.dialog.hide();

					self.appendOcerizedText(retSTRING);
					self.setInactive();
				}

				oReq.send(params);
			});
		}
	};

	/**
	 *	adds ocerized text on top of the clicked image
	 */
	OCRManager.prototype.appendOcerizedText = function (ocerisedTxt) {
		var txtNode = new CKEDITOR.dom.element('p');
		txtNode.appendText(ocerisedTxt);
		txtNode.insertBefore(this.selectedImage);
		document.getSelection().removeAllRanges();
	};

	OCRManager.prototype.initializeTesseract = function (ocerisedTxt) {
		var oReq = new XMLHttpRequest();
		oReq.open('GET', '/bower_components/tesseractJS/tesseract-ocr/tessdata/fra.traineddata', true);
		oReq.responseType = 'blob';

		oReq.onload = function () {
			var blob = oReq.response;
			if (blob) {
				var reader = new FileReader();
				reader.onloadend = function (evt) {
					if (evt.target.readyState === FileReader.DONE) {
						var binary = '';
						var bytes = new Uint8Array(evt.target.result);
						var len = bytes.byteLength;
						for (var i = 0; i < len; i++) {
							binary += String.fromCharCode(bytes[i]);
						}

						tesseractJS.FS_createDataFile('/tesseract-ocr/tessdata/', 'fra.traineddata', binary, true, true);
					}
				};
				reader.readAsArrayBuffer(blob);
			}
		};
		oReq.send(null);
	}

	/**
	 *	Plugin initialization
	 */
	CKEDITOR.plugins.add('ocerisation', {
		icons: 'ocerisation',
		init: function (editor) {

			var dialog = CKEDITOR.dialog.add('loading', function (editor) {
				var dialogDefinition = {
					title: 'Traitement en cours...',
					minWidth: 100,
					minHeight: 60,
					resizable: CKEDITOR.DIALOG_RESIZE_NONE,
					contents: [{
						id: 'loading',
						label: 'Chargement',
						title: 'Traitement en cours...',
						elements: [
							{
								type: 'html',
								html: '
Le traitement de reconnaissance des caractères est en cours.
La durée de ce traitement peut être importante.

Veuillez patienter...
',
								align: 'center'
							}
						]
						}],
					// needs a hidden button to replace default ones
					buttons: [
						{
							type: 'button',
							id: 'hiddenBtn',
							label: 'none',
							hidden: true
						}
					]
				};
				return dialogDefinition;
			});
			var manager;

			editor.ui.addButton('Ocerisation', {
				label: 'Lancer la reconnaissance des caractères',
				command: 'ocerisation',
				toolbar: 'insert' // TODO: change

			});

			editor.addCommand('ocerisation', {
				exec: function (editor) {
					manager.ocerize();
				}
			});

			editor.on('contentDom', function (e) {
				manager = new OCRManager(this.container, editor);
				manager.attachListeners();
				manager.initializeTesseract();
			});

		}
	});

})();<|endoftext|>


<commit_before>/**
 * Created by bjdmeest on 4/12/2015.
 */
CKEDITOR.dialog.add('fremeTranslateDialog', function (editor) {
    var inLangs = [['Dutch', 'NL'], ['English', 'EN']];
    var outLangs = {
        'NL': [['French ', 'FR'], ['German ', 'DE'], ['English', 'EN']],
        'EN': [['Bulgarian ', 'BG'], ['Czech ', 'CS'], ['Danish ', 'DA'], ['Dutch', 'NL'], ['Finnish ', 'FI'], ['German ', 'DE'], ['Greek ', 'EL'], ['Hungarian', 'HU'], ['Italian ', 'IT'], ['Polish ', 'PL'], ['Portuguese ', 'PT'], ['Romanian ', 'RO'], ['Slovenian ', 'SL'], ['Swedish ', 'SV']]
    };
    var langDefault = ['EN', 'DE'];

    function translate(sourceText, sourceLang, targetLang, cb) {
        doRequest('POST',
            'http://api.freme-project.eu/0.4/e-translation/tilde?informat=text&outformat=json-ld&source-lang=' + sourceLang.toLowerCase() + '&target-lang=' + targetLang.toLowerCase(),
            sourceText,
            {'Content-Type': 'text/plain', Accept: 'application/json+ld'},
            function (data) {
                cb(null, data.target['@value']);
            },
            function () {
                cb(new Error('Translation error'));
            }
        );

    }

    function doRequest(method, url, data, headers, success, error) {
        $.ajax({
                method: method,
                headers: headers,
                data: data,
                url: url
            })
            .done(success)
            .fail(error);
    }

    function endIt(todo, eTransNot) {
        if (todo === 0) {
            eTransNot.update({ type: 'success', message: 'e-Translate completed!' });
        }
    }

    return {
        title: 'FREME Translate',
        minWidth: 400,
        minHeight: 200,
        contents: [
            {
                id: 'tab-main',
                label: 'Language Selection',
                elements: [
                    {
                        type: 'select',
                        id: 'lang-in',
                        label: 'Source Language',
                        items: inLangs,
                        default: langDefault[0],
                        onChange: function (api) {
                            var currVal = this.getValue();
                            var langOut = this.getDialog().getContentElement('tab-main', 'lang-out');
                            langOut.clear();
                            for (var i = 0; i < outLangs[currVal].length; i++) {
                                langOut.add(outLangs[currVal][i][0], outLangs[currVal][i][1]);
                            }
                        }
                    },
                    {
                        type: 'select',
                        id: 'lang-out',
                        label: 'Target Language',
                        items: outLangs[langDefault[0]],
                        default: langDefault[1]
                    }
                ]
            }
        ],
        onOk: function () {
            var inLang = this.getValueOf('tab-main', 'lang-in'),
                outLang = this.getValueOf('tab-main', 'lang-out'),
                doc = editor.document,
                goodTags = ['h1', 'h2', 'h3', 'blockquote', 'p'],
                todo = 0;
            // TODO take into account the async stuff..
            var eTransNot = editor.showNotification('e-Translate started!');
            for (var i = 0; i < goodTags.length; i++) {
                var currTag = goodTags[i];
                var nodes = doc.getElementsByTag(currTag);
                todo += nodes.count();
                for (var j = 0; j < nodes.count(); j++) {
                    var node = nodes.getItem(j);
                    (function (node, currTag, inLang, outLang) {
                        translate($(node.$).text(), inLang, outLang, function (err, text) {
                            todo--;
                            if (err) {
                                eTransNot.update({ type: 'warning', message: 'e-Translate failed!' });
                                return console.log(err);
                            }
                            var newNode = new CKEDITOR.dom.element(currTag);
                            newNode.setText(text);
                            newNode.setStyle('color', 'red');
                            newNode.insertAfter(node);
                            endIt(todo, eTransNot);
                        });
                    })(node, currTag, inLang, outLang);
                }
            }

        }
    };
});
<commit_msg>use current freme api instead of fixed 0.4<commit_after>/**
 * Created by bjdmeest on 4/12/2015.
 */
CKEDITOR.dialog.add('fremeTranslateDialog', function (editor) {
    var inLangs = [['Dutch', 'NL'], ['English', 'EN']];
    var outLangs = {
        'NL': [['French ', 'FR'], ['German ', 'DE'], ['English', 'EN']],
        'EN': [['Bulgarian ', 'BG'], ['Czech ', 'CS'], ['Danish ', 'DA'], ['Dutch', 'NL'], ['Finnish ', 'FI'], ['German ', 'DE'], ['Greek ', 'EL'], ['Hungarian', 'HU'], ['Italian ', 'IT'], ['Polish ', 'PL'], ['Portuguese ', 'PT'], ['Romanian ', 'RO'], ['Slovenian ', 'SL'], ['Swedish ', 'SV']]
    };
    var langDefault = ['EN', 'DE'];

    function translate(sourceText, sourceLang, targetLang, cb) {
        doRequest('POST',
            'http://api.freme-project.eu/current/e-translation/tilde?informat=text&outformat=json-ld&source-lang=' + sourceLang.toLowerCase() + '&target-lang=' + targetLang.toLowerCase(),
            sourceText,
            {'Content-Type': 'text/plain', Accept: 'application/json+ld'},
            function (data) {
                cb(null, data.target['@value']);
            },
            function () {
                cb(new Error('Translation error'));
            }
        );

    }

    function doRequest(method, url, data, headers, success, error) {
        $.ajax({
                method: method,
                headers: headers,
                data: data,
                url: url
            })
            .done(success)
            .fail(error);
    }

    function endIt(todo, eTransNot) {
        if (todo === 0) {
            eTransNot.update({ type: 'success', message: 'e-Translate completed!' });
        }
    }

    return {
        title: 'FREME Translate',
        minWidth: 400,
        minHeight: 200,
        contents: [
            {
                id: 'tab-main',
                label: 'Language Selection',
                elements: [
                    {
                        type: 'select',
                        id: 'lang-in',
                        label: 'Source Language',
                        items: inLangs,
                        default: langDefault[0],
                        onChange: function (api) {
                            var currVal = this.getValue();
                            var langOut = this.getDialog().getContentElement('tab-main', 'lang-out');
                            langOut.clear();
                            for (var i = 0; i < outLangs[currVal].length; i++) {
                                langOut.add(outLangs[currVal][i][0], outLangs[currVal][i][1]);
                            }
                        }
                    },
                    {
                        type: 'select',
                        id: 'lang-out',
                        label: 'Target Language',
                        items: outLangs[langDefault[0]],
                        default: langDefault[1]
                    }
                ]
            }
        ],
        onOk: function () {
            var inLang = this.getValueOf('tab-main', 'lang-in'),
                outLang = this.getValueOf('tab-main', 'lang-out'),
                doc = editor.document,
                goodTags = ['h1', 'h2', 'h3', 'blockquote', 'p'],
                todo = 0;
            // TODO take into account the async stuff..
            var eTransNot = editor.showNotification('e-Translate started!');
            for (var i = 0; i < goodTags.length; i++) {
                var currTag = goodTags[i];
                var nodes = doc.getElementsByTag(currTag);
                todo += nodes.count();
                for (var j = 0; j < nodes.count(); j++) {
                    var node = nodes.getItem(j);
                    (function (node, currTag, inLang, outLang) {
                        translate($(node.$).text(), inLang, outLang, function (err, text) {
                            todo--;
                            if (err) {
                                eTransNot.update({ type: 'warning', message: 'e-Translate failed!' });
                                return console.log(err);
                            }
                            var newNode = new CKEDITOR.dom.element(currTag);
                            newNode.setText(text);
                            newNode.setStyle('color', 'red');
                            newNode.insertAfter(node);
                            endIt(todo, eTransNot);
                        });
                    })(node, currTag, inLang, outLang);
                }
            }

        }
    };
});
<|endoftext|>


<commit_before>var UserMeta = require('./User.js'),
    connection = require('../sequelize.js')
//here we define our connection to user table using sequelize
var User = connection.define('users', UserMeta.attributes, UserMeta.options)

// you can define relationships here

module.exports.User = User
<commit_msg>Update models.js<commit_after>var UserMeta = require('./User.js'),
    connection = require('../sequelize.js')
//here we define our connection to user table using sequelize
//and make use of attributes defined in User.js
var User = connection.define('users', UserMeta.attributes, UserMeta.options)

// you can define relationships here

module.exports.User = User
<|endoftext|>


<commit_before>import React from 'react';

import { assertHas } from '../../helpers';

export default props => {
    assertHas(props, 'className');
    assertHas(props, 'icon');
    assertHas(props, 'title');
    assertHas(props, 'subtitle');
    assertHas(props, 'content');

    return (
        <div className={`well ${props.className}`}>

                <i className={`fa fa-${props.icon}`} />

            {props.content}

            {props.title}

            {props.subtitle}

    );
};
<commit_msg>Use PropTypes prop verification for well component.<commit_after>import React from 'react';
import PropTypes from 'prop-types';

const Well = props => {
    return (
        <div className={`well ${props.className}`}>

                <i className={`fa fa-${props.icon}`} />

            {props.content}

            {props.title}

            {props.subtitle}

    );
};

Well.propTypes = {
    // The CSS class to be applied to the well.
    className: PropTypes.string.isRequired,

    // The icon name to be added in the well.
    icon: PropTypes.string.isRequired,

    // The content to be displayed.
    content: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,

    // The title of the well.
    title: PropTypes.string.isRequired,

    // The subtitle of the well.
    subtitle: PropTypes.string.isRequired
};

export default Well;
<|endoftext|>


<commit_before>import {Store} from 'flummox';
import Immutable from 'immutable';
import fuzzysearch from 'fuzzysearch';
import {Pending, Failed} from 'common/src/fetch-result';
import FetchResult from 'common/src/fetch-result';

class KioStore extends Store {
    constructor(flux) {
        super();

        const kioActions = flux.getActions('kio');

        this.state = {
            applications: Immutable.Map(),
            versions: Immutable.Map(),
            approvals: Immutable.Map(),
            approvalTypes: Immutable.Map()
        };

        this.registerAsync(
            kioActions.fetchApplications,
            this.beginFetchApplications,
            this.receiveApplications,
            this.failFetchApplications);

        this.registerAsync(
            kioActions.fetchApplication,
            this.beginFetchApplication,
            this.receiveApplication,
            this.failFetchApplication);

        this.registerAsync(
            kioActions.fetchApplicationVersions,
            this.beginFetchApplicationVersions,
            this.receiveApplicationVersions,
            this.failFetchApplications);

        this.registerAsync(
            kioActions.fetchApplicationVersion,
            this.beginFetchApplicationVersion,
            this.receiveApplicationVersion,
            this.failFetchApplicationVersion);

        this.registerAsync(
            kioActions.fetchApprovals,
            this.beginFetchApprovals,
            this.receiveApprovals,
            this.failFetchApprovals);

        this.register(
            kioActions.fetchApprovalTypes,
            this.receiveApprovalTypes);
    }

    // intentionally left as noop for now
    beginFetchApplications() { }
    failFetchApplications() { }

    beginFetchApplicationVersions() { }
    failFetchApplicationVersions() { }

    beginFetchApprovals() { }
    failFetchApprovals() { }

    /**
     * Replaces application with `id` with a Pending state.
     *
     * @param  {String} id
     */
    beginFetchApplication(id) {
        this.setState({
            applications: this.state.applications.set(id, new Pending())
        });
    }

    /**
     * Replaces application with `id` and version `ver` with a Pending state.
     *
     * @param  {String} id
     * @param  {String} ver
     */
    beginFetchApplicationVersion(id, ver) {
        this.setState({
            versions: this.state.versions.setIn([id, ver], new Pending())
        });
    }

    /**
     * Replaces application with Failed state.
     *
     * @param  {error} err The error passed from the flux action.
     */
    failFetchApplication(err) {
        this.setState({
            applications: this.state.applications.set(err.id, new Failed(err))
        });
    }

    /**
     * Replaces application version with Failed state.
     *
     * @param  {error} err The error passed from the flux action.
     */
    failFetchApplicationVersion(err) {
        this.setState({
            versions: this.state.versions.setIn([err.id, err.ver], new Failed(err))
        });
    }

    /**
     * Adds single application to store. Just calls `receiveApplications` underneath.
     *
     * @param  {object} app
     */
    receiveApplication(app) {
        this.receiveApplications([app]);
    }

    /**
     * Adds applications to store. Overwrites applications with the same id.
     *
     * @param  {Array} apps
     */
    receiveApplications(apps) {
        let newState = apps.reduce(
                            (map, app) => map.set(app.id, Immutable.Map(app)),
                            this.state.applications);
        this.setState({
            applications: newState
        });
    }

    /**
     * Adds application versions to store.
     *
     * @param  {Array} versions
     */
    receiveApplicationVersions(versions) {
        let newState = versions.reduce(
                            (map, ver) => {
                                ver.last_modified = Date.parse(ver.last_modified);
                                return map.setIn([ver.application_id, ver.id], Immutable.Map(ver));
                            },
                            this.state.versions);
        this.setState({
            versions: newState
        });
    }

    /**
     * Adds single application version to store. Just calls `receiveApplicationVersions` underneath.
     *
     * @param  {object} ver
     */
    receiveApplicationVersion(ver) {
        this.receiveApplicationVersions([ver]);
    }

    /**
     * Returns all applications that are available (as in not Pending or Failed) RIGHT NAO!
     *
     * @param  {string} term Filters list of applications by names that contain term
     * @return {Array} Available applications
     */
    getApplications(term) {
        let availableApps = this.state.applications
                                .valueSeq()
                                .filter(app => !app.getResult)
                                .filter(app => term ?
                                                fuzzysearch(term.toLowerCase(), `${app.get('name').toLowerCase()} ${app.get('team_id').toLowerCase()}`) :
                                                true)
                                .sortBy(app => app.get('name').toLowerCase())
                                .toJS();
        return availableApps;
    }

    /**
     * Returns the application with `id`. Does not care about its state, e.g. whether or not
     * it's Pending or Failed. Returns null if there is no application with this id.
     *
     * @param  {String} id
     * @return {object} The application with this id
     */
    getApplication(id) {
        let app = this.state.applications.get(id);
        return app ?
                app.toJS() :
                false;
    }

    /**
     * Returns all versions for a given application
     *
     * @return {Array} Available versions
     */
    getApplicationVersions(id, filter) {
        let versions = this.state
                            .versions
                            .get(id);
        if (versions) {
            return versions
                        .valueSeq()
                        .filter(v => !v.getResult)
                        .filter(v => filter ?
                                        fuzzysearch(filter, v.get('id')) :
                                        true)
                        .sortBy(v => v.get('last_modified'))
                        .reverse()
                        .toJS();
        }
        return [];
    }

    /**
     * Returns the application version with `id`. Does not care about its state, e.g. whether or not
     * it's Pending or Failed. Returns null if there is no version with this id.
     *
     * @param  {String} id
     * @param  {String} ver
     * @return {object} The application version with this id and ver
     */
    getApplicationVersion(id, ver) {
        let version = this.state.versions.getIn([id, ver]);
        return version ? version.toJS() : false;
    }

    getLatestApplicationVersion(id) {
        let versions = this.getApplicationVersions(id);
        return versions.length ? versions[0] : false;
    }

    receiveApprovals([applicationId, versionId, approvals]) {
        function getKey(apr) {
            return `${apr.user_id}.${apr.approved_at}.${apr.approval_type}`;
        }

        let newState = approvals.reduce(
                            (app, approval) => {
                                // convert to timestamp
                                approval.timestamp = Date.parse(approval.approved_at);
                                return app.set(getKey(approval), Immutable.Map(approval));
                            },
                            Immutable.Map());
        this.setState({
            approvals: this.state.approvals.setIn([applicationId, versionId], newState)
        });
    }

    receiveApprovalTypes([applicationId, approvalTypes]) {
        this.setState({
            approvalTypes: this.state.approvalTypes.set(applicationId, Immutable.fromJS(approvalTypes))
        });
    }

    /**
     * Returns all used approval types in versions of an application.
     *
     * @param  {String} applicationId ID of the application
     * @return {Array}                The used approval types
     */
    getApprovalTypes(applicationId) {
        return this.state.approvalTypes.get(applicationId, []);
    }

    /**
     * Returns approvals for an application version.
     *
     * @param  {String} applicationId ID of the application
     * @param  {String} versionId     ID of the version
     * @return {Array}                Approvals sorted by date asc
     */
    getApprovals(applicationId, versionId) {
        let approvals = this.state.approvals.getIn([applicationId, versionId]);
        approvals = approvals ? approvals.valueSeq().toJS() : [];
        return approvals.sort((a, b) => a.timestamp < b.timestamp ?
                                            -1 :
                                            b.timestamp < a.timestamp ?
                                                1 : 0);
    }

    /**
     * Only for testing!
     */
    _empty() {
        this.setState({
            applications: Immutable.Map(),
            versions: Immutable.Map(),
            approvals: Immutable.Map(),
            approvalTypes: Immutable.Map()
        });
    }
}

export default KioStore;
<commit_msg>#254 fix lint<commit_after>import {Store} from 'flummox';
import Immutable from 'immutable';
import fuzzysearch from 'fuzzysearch';
import {Pending, Failed} from 'common/src/fetch-result';

class KioStore extends Store {
    constructor(flux) {
        super();

        const kioActions = flux.getActions('kio');

        this.state = {
            applications: Immutable.Map(),
            versions: Immutable.Map(),
            approvals: Immutable.Map(),
            approvalTypes: Immutable.Map()
        };

        this.registerAsync(
            kioActions.fetchApplications,
            this.beginFetchApplications,
            this.receiveApplications,
            this.failFetchApplications);

        this.registerAsync(
            kioActions.fetchApplication,
            this.beginFetchApplication,
            this.receiveApplication,
            this.failFetchApplication);

        this.registerAsync(
            kioActions.fetchApplicationVersions,
            this.beginFetchApplicationVersions,
            this.receiveApplicationVersions,
            this.failFetchApplications);

        this.registerAsync(
            kioActions.fetchApplicationVersion,
            this.beginFetchApplicationVersion,
            this.receiveApplicationVersion,
            this.failFetchApplicationVersion);

        this.registerAsync(
            kioActions.fetchApprovals,
            this.beginFetchApprovals,
            this.receiveApprovals,
            this.failFetchApprovals);

        this.register(
            kioActions.fetchApprovalTypes,
            this.receiveApprovalTypes);
    }

    // intentionally left as noop for now
    beginFetchApplications() { }
    failFetchApplications() { }

    beginFetchApplicationVersions() { }
    failFetchApplicationVersions() { }

    beginFetchApprovals() { }
    failFetchApprovals() { }

    /**
     * Replaces application with `id` with a Pending state.
     *
     * @param  {String} id
     */
    beginFetchApplication(id) {
        this.setState({
            applications: this.state.applications.set(id, new Pending())
        });
    }

    /**
     * Replaces application with `id` and version `ver` with a Pending state.
     *
     * @param  {String} id
     * @param  {String} ver
     */
    beginFetchApplicationVersion(id, ver) {
        this.setState({
            versions: this.state.versions.setIn([id, ver], new Pending())
        });
    }

    /**
     * Replaces application with Failed state.
     *
     * @param  {error} err The error passed from the flux action.
     */
    failFetchApplication(err) {
        this.setState({
            applications: this.state.applications.set(err.id, new Failed(err))
        });
    }

    /**
     * Replaces application version with Failed state.
     *
     * @param  {error} err The error passed from the flux action.
     */
    failFetchApplicationVersion(err) {
        this.setState({
            versions: this.state.versions.setIn([err.id, err.ver], new Failed(err))
        });
    }

    /**
     * Adds single application to store. Just calls `receiveApplications` underneath.
     *
     * @param  {object} app
     */
    receiveApplication(app) {
        this.receiveApplications([app]);
    }

    /**
     * Adds applications to store. Overwrites applications with the same id.
     *
     * @param  {Array} apps
     */
    receiveApplications(apps) {
        let newState = apps.reduce(
                            (map, app) => map.set(app.id, Immutable.Map(app)),
                            this.state.applications);
        this.setState({
            applications: newState
        });
    }

    /**
     * Adds application versions to store.
     *
     * @param  {Array} versions
     */
    receiveApplicationVersions(versions) {
        let newState = versions.reduce(
                            (map, ver) => {
                                ver.last_modified = Date.parse(ver.last_modified);
                                return map.setIn([ver.application_id, ver.id], Immutable.Map(ver));
                            },
                            this.state.versions);
        this.setState({
            versions: newState
        });
    }

    /**
     * Adds single application version to store. Just calls `receiveApplicationVersions` underneath.
     *
     * @param  {object} ver
     */
    receiveApplicationVersion(ver) {
        this.receiveApplicationVersions([ver]);
    }

    /**
     * Returns all applications that are available (as in not Pending or Failed) RIGHT NAO!
     *
     * @param  {string} term Filters list of applications by names that contain term
     * @return {Array} Available applications
     */
    getApplications(term) {
        let availableApps = this.state.applications
                                .valueSeq()
                                .filter(app => !app.getResult)
                                .filter(app => term ?
                                                fuzzysearch(term.toLowerCase(), `${app.get('name').toLowerCase()} ${app.get('team_id').toLowerCase()}`) :
                                                true)
                                .sortBy(app => app.get('name').toLowerCase())
                                .toJS();
        return availableApps;
    }

    /**
     * Returns the application with `id`. Does not care about its state, e.g. whether or not
     * it's Pending or Failed. Returns null if there is no application with this id.
     *
     * @param  {String} id
     * @return {object} The application with this id
     */
    getApplication(id) {
        let app = this.state.applications.get(id);
        return app ?
                app.toJS() :
                false;
    }

    /**
     * Returns all versions for a given application
     *
     * @return {Array} Available versions
     */
    getApplicationVersions(id, filter) {
        let versions = this.state
                            .versions
                            .get(id);
        if (versions) {
            return versions
                        .valueSeq()
                        .filter(v => !v.getResult)
                        .filter(v => filter ?
                                        fuzzysearch(filter, v.get('id')) :
                                        true)
                        .sortBy(v => v.get('last_modified'))
                        .reverse()
                        .toJS();
        }
        return [];
    }

    /**
     * Returns the application version with `id`. Does not care about its state, e.g. whether or not
     * it's Pending or Failed. Returns null if there is no version with this id.
     *
     * @param  {String} id
     * @param  {String} ver
     * @return {object} The application version with this id and ver
     */
    getApplicationVersion(id, ver) {
        let version = this.state.versions.getIn([id, ver]);
        return version ? version.toJS() : false;
    }

    getLatestApplicationVersion(id) {
        let versions = this.getApplicationVersions(id);
        return versions.length ? versions[0] : false;
    }

    receiveApprovals([applicationId, versionId, approvals]) {
        function getKey(apr) {
            return `${apr.user_id}.${apr.approved_at}.${apr.approval_type}`;
        }

        let newState = approvals.reduce(
                            (app, approval) => {
                                // convert to timestamp
                                approval.timestamp = Date.parse(approval.approved_at);
                                return app.set(getKey(approval), Immutable.Map(approval));
                            },
                            Immutable.Map());
        this.setState({
            approvals: this.state.approvals.setIn([applicationId, versionId], newState)
        });
    }

    receiveApprovalTypes([applicationId, approvalTypes]) {
        this.setState({
            approvalTypes: this.state.approvalTypes.set(applicationId, Immutable.fromJS(approvalTypes))
        });
    }

    /**
     * Returns all used approval types in versions of an application.
     *
     * @param  {String} applicationId ID of the application
     * @return {Array}                The used approval types
     */
    getApprovalTypes(applicationId) {
        return this.state.approvalTypes.get(applicationId, []);
    }

    /**
     * Returns approvals for an application version.
     *
     * @param  {String} applicationId ID of the application
     * @param  {String} versionId     ID of the version
     * @return {Array}                Approvals sorted by date asc
     */
    getApprovals(applicationId, versionId) {
        let approvals = this.state.approvals.getIn([applicationId, versionId]);
        approvals = approvals ? approvals.valueSeq().toJS() : [];
        return approvals.sort((a, b) => a.timestamp < b.timestamp ?
                                            -1 :
                                            b.timestamp < a.timestamp ?
                                                1 : 0);
    }

    /**
     * Only for testing!
     */
    _empty() {
        this.setState({
            applications: Immutable.Map(),
            versions: Immutable.Map(),
            approvals: Immutable.Map(),
            approvalTypes: Immutable.Map()
        });
    }
}

export default KioStore;
<|endoftext|>


<commit_before>import _clone from "lodash/clone"
import _isEmpty from "lodash/isEmpty"
import moment from "moment"
import Settings from "settings"

export const countPerDateAggregation = (fieldName, fieldConfig, data) => {
  const values = data.reduce((counter, entity) => {
    const dateFieldValue = Object.get(entity, fieldName)
    const value = dateFieldValue
      ? moment(dateFieldValue).format("YYYY-MM-DD")
      : null
    counter[value] = ++counter[value] || 1
    return counter
  }, {})
  return { values: values }
}

// TODO: maybe use a library for a list of colors
// For now, used this list of colors: https://gist.github.com/mikebmou/1323655
const CHART_COLORS = [
  "#3366cc",
  "#dc3912",
  "#ff9900",
  "#109618",
  "#990099",
  "#0099c6",
  "#dd4477",
  "#66aa00",
  "#b82e2e",
  "#316395",
  "#3366cc",
  "#994499",
  "#22aa99",
  "#aaaa11",
  "#6633cc",
  "#e67300",
  "#8b0707",
  "#651067",
  "#329262",
  "#5574a6",
  "#3b3eac",
  "#b77322",
  "#16d620",
  "#b91383",
  "#f4359e",
  "#9c5935",
  "#a9c413",
  "#2a778d",
  "#668d1c",
  "#bea413",
  "#0c5922",
  "#743411"
]

export const countPerValueAggregation = (fieldName, fieldConfig, data) => {
  const counters = data.reduce((counter, entity) => {
    const value = Object.get(entity, fieldName) || null
    const values = Array.isArray(value) ? value : [value]
    values.forEach(choice => (counter[choice] = ++counter[choice] || 1))
    return counter
  }, {})
  const legendColors = _clone(CHART_COLORS)
  const legend = fieldConfig?.choices || {}
  const legendKeys = !_isEmpty(legend)
    ? Object.keys(legend)
    : Object.keys(counters)
  legendKeys.forEach(
    key =>
      (legend[key] = {
        label: legend[key]?.label || key,
        color: legend[key]?.color || legendColors.pop()
      })
  )
  legend.null = { label: "Unspecified", color: "#bbbbbb" }
  return { values: counters, entitiesCount: data.length, legend: legend }
}

const arrayOfNumbers = arr =>
  arr.filter(n => !isNaN(parseFloat(n)) && isFinite(n)).map(n => Number(n))

export const numbersListAggregation = (fieldName, fieldConfig, data) => {
  const values = data.map(item => Object.get(item, fieldName))
  const numberValues = arrayOfNumbers(values)
  return { values: numberValues }
}

const noTaskMessage = `No ${Settings.fields.task.subLevel.shortLabel}`
const noTask = {
  uuid: "-1",
  shortName: noTaskMessage,
  longName: noTaskMessage
}

export const getReportsByTasks = reportsList => {
  if (_isEmpty(reportsList)) {
    return []
  }
  const simplifiedValues = reportsList.map(d => {
    return { reportUuid: d.uuid, tasks: d.tasks.map(p => p.uuid) }
  })
  let tasks = reportsList.map(d => d.tasks)
  tasks = [].concat
    .apply([], tasks)
    .filter(
      (item, index, d) =>
        d.findIndex(t => {
          return t.uuid === item.uuid
        }) === index
    )
    .sort((a, b) => a.shortName.localeCompare(b.shortName))
  // add No Task item, in order to relate to reports without Tasks
  tasks.push(noTask)
  return tasks.map(d => {
    const r = {}
    r.task = d
    r.reportsCount =
      d.uuid === noTask.uuid
        ? simplifiedValues.filter(item => _isEmpty(item.tasks)).length
        : simplifiedValues.filter(item => item.tasks.indexOf(d.uuid) > -1)
          .length
    return r
  })
}

export const reportsByTaskAggregation = (fieldName, fieldConfig, data) => ({
  values: getReportsByTasks(data)
})

export const valuesListAggregation = (fieldName, fieldConfig, data) => ({
  values: data.map(item => Object.get(item, fieldName))
})

export const objectsListAggregation = (fieldName, fieldConfig, data) => ({
  values: data
})

export const countPerLevelAggregation = (fieldName, fieldConfig, data) => {
  const levels = fieldConfig.levels
  if (_isEmpty(levels)) {
    return null
  }
  const levelsEndValues = levels.map(level => level.endValue)
  levelsEndValues.sort((a, b) => a - b)
  const counters = data.reduce((counter, entity) => {
    const value = Object.get(entity, fieldName) || null
    const levelEndValue =
      value !== null
        ? levelsEndValues.filter(endVal => endVal >= value)[0]
        : null
    counter[levelEndValue] = ++counter[levelEndValue] || 1
    return counter
  }, {})
  const legend = levels.reduce((res, level) => {
    res[level.endValue] = level
    return res
  }, {})
  legend.null = { label: "Unspecified", color: "#bbbbbb" }
  return { values: counters, entitiesCount: data.length, legend: legend }
}

export const likertScaleAndPieAggregation = (fieldName, fieldConfig, data) => {
  return {
    values: {
      likertScaleValues: valuesListAggregation(fieldName, fieldConfig, data),
      pieValues: countPerLevelAggregation(fieldName, fieldConfig, data)
    }
  }
}
<commit_msg>NCI-Agency/anet#2954: Change order of used chart colors<commit_after>import _clone from "lodash/clone"
import _isEmpty from "lodash/isEmpty"
import moment from "moment"
import Settings from "settings"

export const countPerDateAggregation = (fieldName, fieldConfig, data) => {
  const values = data.reduce((counter, entity) => {
    const dateFieldValue = Object.get(entity, fieldName)
    const value = dateFieldValue
      ? moment(dateFieldValue).format("YYYY-MM-DD")
      : null
    counter[value] = ++counter[value] || 1
    return counter
  }, {})
  return { values: values }
}

// TODO: maybe use a library for a list of colors
// For now, used this list of colors: https://gist.github.com/mikebmou/1323655
const CHART_COLORS = [
  "#3366cc",
  "#dc3912",
  "#ff9900",
  "#109618",
  "#990099",
  "#0099c6",
  "#dd4477",
  "#66aa00",
  "#b82e2e",
  "#316395",
  "#3366cc",
  "#994499",
  "#22aa99",
  "#aaaa11",
  "#6633cc",
  "#e67300",
  "#8b0707",
  "#651067",
  "#329262",
  "#5574a6",
  "#3b3eac",
  "#b77322",
  "#16d620",
  "#b91383",
  "#f4359e",
  "#9c5935",
  "#a9c413",
  "#2a778d",
  "#668d1c",
  "#bea413",
  "#0c5922",
  "#743411"
]

export const countPerValueAggregation = (fieldName, fieldConfig, data) => {
  const counters = data.reduce((counter, entity) => {
    const value = Object.get(entity, fieldName) || null
    const values = Array.isArray(value) ? value : [value]
    values.forEach(choice => (counter[choice] = ++counter[choice] || 1))
    return counter
  }, {})
  const legendColors = _clone(CHART_COLORS)
  const legend = fieldConfig?.choices || {}
  const legendKeys = !_isEmpty(legend)
    ? Object.keys(legend)
    : Object.keys(counters)
  legendKeys.forEach(
    key =>
      (legend[key] = {
        label: legend[key]?.label || key,
        color: legend[key]?.color || legendColors.shift()
      })
  )
  legend.null = { label: "Unspecified", color: "#bbbbbb" }
  return { values: counters, entitiesCount: data.length, legend: legend }
}

const arrayOfNumbers = arr =>
  arr.filter(n => !isNaN(parseFloat(n)) && isFinite(n)).map(n => Number(n))

export const numbersListAggregation = (fieldName, fieldConfig, data) => {
  const values = data.map(item => Object.get(item, fieldName))
  const numberValues = arrayOfNumbers(values)
  return { values: numberValues }
}

const noTaskMessage = `No ${Settings.fields.task.subLevel.shortLabel}`
const noTask = {
  uuid: "-1",
  shortName: noTaskMessage,
  longName: noTaskMessage
}

export const getReportsByTasks = reportsList => {
  if (_isEmpty(reportsList)) {
    return []
  }
  const simplifiedValues = reportsList.map(d => {
    return { reportUuid: d.uuid, tasks: d.tasks.map(p => p.uuid) }
  })
  let tasks = reportsList.map(d => d.tasks)
  tasks = [].concat
    .apply([], tasks)
    .filter(
      (item, index, d) =>
        d.findIndex(t => {
          return t.uuid === item.uuid
        }) === index
    )
    .sort((a, b) => a.shortName.localeCompare(b.shortName))
  // add No Task item, in order to relate to reports without Tasks
  tasks.push(noTask)
  return tasks.map(d => {
    const r = {}
    r.task = d
    r.reportsCount =
      d.uuid === noTask.uuid
        ? simplifiedValues.filter(item => _isEmpty(item.tasks)).length
        : simplifiedValues.filter(item => item.tasks.indexOf(d.uuid) > -1)
          .length
    return r
  })
}

export const reportsByTaskAggregation = (fieldName, fieldConfig, data) => ({
  values: getReportsByTasks(data)
})

export const valuesListAggregation = (fieldName, fieldConfig, data) => ({
  values: data.map(item => Object.get(item, fieldName))
})

export const objectsListAggregation = (fieldName, fieldConfig, data) => ({
  values: data
})

export const countPerLevelAggregation = (fieldName, fieldConfig, data) => {
  const levels = fieldConfig.levels
  if (_isEmpty(levels)) {
    return null
  }
  const levelsEndValues = levels.map(level => level.endValue)
  levelsEndValues.sort((a, b) => a - b)
  const counters = data.reduce((counter, entity) => {
    const value = Object.get(entity, fieldName) || null
    const levelEndValue =
      value !== null
        ? levelsEndValues.filter(endVal => endVal >= value)[0]
        : null
    counter[levelEndValue] = ++counter[levelEndValue] || 1
    return counter
  }, {})
  const legend = levels.reduce((res, level) => {
    res[level.endValue] = level
    return res
  }, {})
  legend.null = { label: "Unspecified", color: "#bbbbbb" }
  return { values: counters, entitiesCount: data.length, legend: legend }
}

export const likertScaleAndPieAggregation = (fieldName, fieldConfig, data) => {
  return {
    values: {
      likertScaleValues: valuesListAggregation(fieldName, fieldConfig, data),
      pieValues: countPerLevelAggregation(fieldName, fieldConfig, data)
    }
  }
}
<|endoftext|>


<commit_before>/**
 * This plugin is a primitive version of real time charts. It shows
 * different parameters of the jevo.js system, like average energy,
 * iq (energy picking speed), average code size and so on. Here
 * labels explanation:
 *
 *     ips: Iterations Per Second - amount of all organisms full
 *          code runs per one second
 *     lps: Lines Per Second - average amount of run code lines
 *          per one second
 *     org: Average amount of organisms at the moment of logging
 *     nrg: Amount of energy of average organism
 *     iq : Energy picking speed per StatusConfig.period seconds
 *     che: Changes amount of average organism
 *     fit: Fitness of average organism
 *     cod: Code size of average organism
 *
 * @author flatline
 */
const Configurable = require('./../../../../../common/src/Configurable');
const EVENTS       = require('./../../../share/Events').EVENTS;
const Config       = require('./../../../share/Config').Config;
const StatusConfig = require('./Config');

const GREEN  = 'color: #00aa00';
const RED    = 'color: #aa0000';

class Status extends Configurable {
    static _format(value, name, orgs, fixed, pad, coef = 1, perOrg = false) {
        orgs = perOrg ? orgs : 1;
        return `${name}:${((value / orgs) * coef).toFixed(fixed)}`.padEnd(pad);
    }

    constructor(manager) {
        super(manager, {Config, cfg: StatusConfig});

        this._manager      = manager;
        this._stamp        = 0;
        this._curEnergy    = 0;
        this._energy       = 0;
        this._fitness      = 0;
        this._changes      = 0;
        this._codeSize     = 0;
        this._runLines     = 1;
        this._times        = 0;
        this._oldValues    = [0, 0];
        this._onIpsCb      = this._onIps.bind(this);
        this._onOrganismCb = this._onOrganism.bind(this);

        manager.on(EVENTS.IPS, this._onIpsCb);
        manager.on(EVENTS.ORGANISM, this._onOrganismCb);
    }

    destroy() {
        this._manager.off(EVENTS.IPS, this._onIpsCb);
        this._manager.off(EVENTS.ORGANISM, this._onOrganismCb);
        this._onIpsCb      = null;
        this._onOrganismCb = null;
        this._manager      = null;
        this._oldValues    = null;
        super.destroy();
    }

    _onIps(ips, orgs) {
        if (!StatusConfig.showMessages) {return}
        const stamp     = Date.now();
        this._times++;
        if (stamp - this._stamp < StatusConfig.period) {return}

        const man = this._manager;
        this._onBeforeLog(ips, orgs);
        const format    = Status._format;
        const orgAmount = orgs.size || 1;
        const sips      = `ips:${ips.toFixed(ips < 10 ? 2 : 0)}`.padEnd(10);
        const slps      = format(this._runLines / this._times, 'lps', orgAmount, 0, 14         );
        const sorgs     = format(orgAmount,                    'org', orgAmount, 0, 10         );
        const senergy   = format(this._curEnergy,              'nrg', orgAmount, 0, 14         );
        const siq       = format(this._energy,                 'iq',  orgAmount, 3, 13, 100000 );
        const sfit      = format(this._fitness,                'fit', orgAmount, 3, 14         );
        const schanges  = format(this._changes,                'che', orgAmount, 3, 12, 1, true);
        const scode     = format(this._codeSize,               'cod', orgAmount, 1, 12, 1, true);

        console.log(`%c${sips}${slps}${sorgs}%c${siq}${senergy}${schanges}${sfit}${scode}`, GREEN, RED);
        man.hasView && man.canvas.text(5, 20, sips + man.clientId);
        this._onAfterLog(stamp);
    }

    _onOrganism(org, lines) {
	    if (!StatusConfig.showMessages) {return}
        this._runLines += lines;
    }

    _onBeforeLog(ips, orgs) {
        const olds      = this._oldValues;
        const size      = orgs.size || 1;
        const lines     = this._runLines;
        let   energy    = 0;
        let   fitness   = 0;
        let   changes   = 0;
        let   codeSize  = 0;
        let   item      = orgs.first;
        let   org;

        while(item && (org = item.val)) {
            energy   += org.energy;
            changes  += org.changes;
            fitness  += org.fitness();
            codeSize += org.vm.size;
            item      = item.next;
        }

        energy  /= size;
        fitness /= size;
        this._curEnergy = energy;
        this._energy    = (energy  - olds[0]) / lines;
        this._fitness   = (fitness - olds[1]) / lines;
        this._changes   = changes;
        this._codeSize  = codeSize;
        this._oldValues = [energy, fitness];
    }

    _onAfterLog(stamp) {
        this._times    = 0;
        this._runLines = 0;
        this._stamp    = stamp;
    }
}

module.exports = Status;<commit_msg>fixed small issue with status padding (iq parameter)<commit_after>/**
 * This plugin is a primitive version of real time charts. It shows
 * different parameters of the jevo.js system, like average energy,
 * iq (energy picking speed), average code size and so on. Here
 * labels explanation:
 *
 *     ips: Iterations Per Second - amount of all organisms full
 *          code runs per one second
 *     lps: Lines Per Second - average amount of run code lines
 *          per one second
 *     org: Average amount of organisms at the moment of logging
 *     nrg: Amount of energy of average organism
 *     iq : Energy picking speed per StatusConfig.period seconds
 *     che: Changes amount of average organism
 *     fit: Fitness of average organism
 *     cod: Code size of average organism
 *
 * @author flatline
 */
const Configurable = require('./../../../../../common/src/Configurable');
const EVENTS       = require('./../../../share/Events').EVENTS;
const Config       = require('./../../../share/Config').Config;
const StatusConfig = require('./Config');

const GREEN  = 'color: #00aa00';
const RED    = 'color: #aa0000';

class Status extends Configurable {
    static _format(value, name, orgs, fixed, pad, coef = 1, perOrg = false) {
        orgs = perOrg ? orgs : 1;
        return `${name}:${((value / orgs) * coef).toFixed(fixed)}`.padEnd(pad);
    }

    constructor(manager) {
        super(manager, {Config, cfg: StatusConfig});

        this._manager      = manager;
        this._stamp        = 0;
        this._curEnergy    = 0;
        this._energy       = 0;
        this._fitness      = 0;
        this._changes      = 0;
        this._codeSize     = 0;
        this._runLines     = 1;
        this._times        = 0;
        this._oldValues    = [0, 0];
        this._onIpsCb      = this._onIps.bind(this);
        this._onOrganismCb = this._onOrganism.bind(this);

        manager.on(EVENTS.IPS, this._onIpsCb);
        manager.on(EVENTS.ORGANISM, this._onOrganismCb);
    }

    destroy() {
        this._manager.off(EVENTS.IPS, this._onIpsCb);
        this._manager.off(EVENTS.ORGANISM, this._onOrganismCb);
        this._onIpsCb      = null;
        this._onOrganismCb = null;
        this._manager      = null;
        this._oldValues    = null;
        super.destroy();
    }

    _onIps(ips, orgs) {
        if (!StatusConfig.showMessages) {return}
        const stamp     = Date.now();
        this._times++;
        if (stamp - this._stamp < StatusConfig.period) {return}

        const man = this._manager;
        this._onBeforeLog(ips, orgs);
        const format    = Status._format;
        const orgAmount = orgs.size || 1;
        const sips      = `ips:${ips.toFixed(ips < 10 ? 2 : 0)}`.padEnd(10);
        const slps      = format(this._runLines / this._times, 'lps', orgAmount, 0, 14         );
        const sorgs     = format(orgAmount,                    'org', orgAmount, 0, 10         );
        const senergy   = format(this._curEnergy,              'nrg', orgAmount, 0, 14         );
        const siq       = format(this._energy,                 'iq',  orgAmount, 3, 14, 100000 );
        const sfit      = format(this._fitness,                'fit', orgAmount, 3, 14         );
        const schanges  = format(this._changes,                'che', orgAmount, 3, 12, 1, true);
        const scode     = format(this._codeSize,               'cod', orgAmount, 1, 12, 1, true);

        console.log(`%c${sips}${slps}${sorgs}%c${siq}${senergy}${schanges}${sfit}${scode}`, GREEN, RED);
        man.hasView && man.canvas.text(5, 20, sips + man.clientId);
        this._onAfterLog(stamp);
    }

    _onOrganism(org, lines) {
	    if (!StatusConfig.showMessages) {return}
        this._runLines += lines;
    }

    _onBeforeLog(ips, orgs) {
        const olds      = this._oldValues;
        const size      = orgs.size || 1;
        const lines     = this._runLines;
        let   energy    = 0;
        let   fitness   = 0;
        let   changes   = 0;
        let   codeSize  = 0;
        let   item      = orgs.first;
        let   org;

        while(item && (org = item.val)) {
            energy   += org.energy;
            changes  += org.changes;
            fitness  += org.fitness();
            codeSize += org.vm.size;
            item      = item.next;
        }

        energy  /= size;
        fitness /= size;
        this._curEnergy = energy;
        this._energy    = (energy  - olds[0]) / lines;
        this._fitness   = (fitness - olds[1]) / lines;
        this._changes   = changes;
        this._codeSize  = codeSize;
        this._oldValues = [energy, fitness];
    }

    _onAfterLog(stamp) {
        this._times    = 0;
        this._runLines = 0;
        this._stamp    = stamp;
    }
}

module.exports = Status;<|endoftext|>


<commit_before>Template.topFiveChart.rendered = function() {
	this.autorun(function(){
		top5MatchingScore = Template.instance().data.top5MatchingScore;

		var matchingScores = {
			industry: [],
			min: [],
			max: [],
			avg: []
		}

		for (i = 0; i < top5MatchingScore.length; i++) {
			industry =
				MasterData.findOne({
					dataType: "category", categoryId: top5MatchingScore[i].industryId}).categoryVNName.trim() +
					' (' + top5MatchingScore[i].countMatchingScore + ')';
			matchingScores.industry.push(industry);
			matchingScores.min.push(top5MatchingScore[i].minMatchingScore);
			matchingScores.max.push(top5MatchingScore[i].maxMatchingScore);
			matchingScores.avg.push(top5MatchingScore[i].avgMatchingScore);
		}

		var chartSubtitle;
		if(Template.instance().data.sortByAvg){
			chartSubtitle = '5 ngành nghề có Matching Score trung bình cao nhất'
		} else {
			chartSubtitle = '5 ngành nghề có số lượt ứng tuyển cao nhất'
		}

		$('#top-five-chart').highcharts({
			chart: {
				type: 'column'
			},
			title: {
				text: 'Biểu đồ Matching Score',
				x: -20
			},
			subtitle: {
				text: chartSubtitle,
				x: -20
			},
			xAxis: {
				categories: matchingScores.industry
			},
			yAxis: {
				title: {
					text: 'Scores'
				},
				min: 0,
				max: 100
			},
			legend: {
            	layout: 'vertical',
            	align: 'right',
            	verticalAlign: 'middle',
            	borderWidth: 0
        	},
        	series: [{
        		name: 'Min',
        		data: matchingScores.min
        	}, {
        		name: 'Max',
        		data: matchingScores.max
        	}, {
        		name: 'Average',
        		data: matchingScores.avg
        	}],
        	credits: {
        		enabled: false
        	}
		});
	});
};<commit_msg>Change the title of MatchingScore chart<commit_after>Template.topFiveChart.rendered = function() {
	this.autorun(function(){
		top5MatchingScore = Template.instance().data.top5MatchingScore;

		var matchingScores = {
			industry: [],
			min: [],
			max: [],
			avg: []
		}

		for (i = 0; i < top5MatchingScore.length; i++) {
			industry =
				MasterData.findOne({
					dataType: "category", categoryId: top5MatchingScore[i].industryId}).categoryVNName.trim() +
					' (' + top5MatchingScore[i].countMatchingScore + ')';
			matchingScores.industry.push(industry);
			matchingScores.min.push(top5MatchingScore[i].minMatchingScore);
			matchingScores.max.push(top5MatchingScore[i].maxMatchingScore);
			matchingScores.avg.push(top5MatchingScore[i].avgMatchingScore);
		}

		var chartSubtitle;
		if(Template.instance().data.sortByAvg){
			chartSubtitle = '5 ngành nghề có Matching Score trung bình cao nhất'
		} else {
			chartSubtitle = '5 ngành nghề có số lượt ứng tuyển cao nhất'
		}

		$('#top-five-chart').highcharts({
			chart: {
				type: 'column'
			},
			title: {
				text: 'Matching Score',
				x: -20
			},
			subtitle: {
				text: chartSubtitle,
				x: -20
			},
			xAxis: {
				categories: matchingScores.industry
			},
			yAxis: {
				title: {
					text: 'Scores'
				},
				min: 0,
				max: 100
			},
			legend: {
            	layout: 'vertical',
            	align: 'right',
            	verticalAlign: 'middle',
            	borderWidth: 0
        	},
        	series: [{
        		name: 'Min',
        		data: matchingScores.min
        	}, {
        		name: 'Max',
        		data: matchingScores.max
        	}, {
        		name: 'Average',
        		data: matchingScores.avg
        	}],
        	credits: {
        		enabled: false
        	}
		});
	});
};<|endoftext|>