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

'use strict';

const assert = require('chai').assert;
const PersonalityTraitDescriptions = require('../lib/index');

describe('names', () => {

  it('get default (English) trait description: Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions();
    assert.equal(traitDescriptions.description('Agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get default (English) trait description: Gregariousness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions();
    assert.equal(traitDescriptions.description('Gregariousness'), 'Fondness for the company of others; sociability.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[5], 'Eagerness to trying new activities and experiencing new things.');
  });

  it('get English trait description: Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en' });
    assert.equal(traitDescriptions.description('Agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get English trait description: Activity level', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en' });
    assert.equal(traitDescriptions.description('Activity level'), 'Pace of living; level of busyness.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[15], 'Will-power; the capacity to begin tasks and follow through to completion in spite of boredom or distractions.');
  });

  it('get Spanish trait description: Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es' });
    assert.equal(traitDescriptions.description('Agreeableness'), 'Más alto: Valoran llevarse bien con otros. Tienen una visión más optimista de la naturaleza humana. \nMás bajo: Valoran sus propios intereses por sobre los de otros. Son más escépticos de los motivos de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Más alto: Auto-disciplinados, conscientes de sus deberes, o tendientes a querer alcanzar logros por encima de las medidas o expectativas externas. \nMás bajo: Más propensos a preferir lo espontáneo por sobre lo planificado.');
  });

  it('get Spanish trait description: Trust', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es' });
    assert.equal(traitDescriptions.description('Trust'), 'Nivel de creencia en la sinceridad y buenas intenciones de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[50], 'Éxito personal a través de la demostración de competencia de acuerdo a estándares sociales.');
  });

  // Japanese - to be implemented.  This is a placeholder.
  it('get Japanese trait description: Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja' });
    assert.equal(traitDescriptions.description('Agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  // V3 tests
  it('get English trait description: big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get English trait description: need_harmony', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('need_harmony'), 'A need to appreciate or please other people, their viewpoints, and feelings.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[47], 'Respect, commitment, and acceptance of the customs and ideas that one\'s culture and/or religion provides.');
  });

  it('get English trait description: big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), 'Más alto: Valoran llevarse bien con otros. Tienen una visión más optimista de la naturaleza humana. \nMás bajo: Valoran sus propios intereses por sobre los de otros. Son más escépticos de los motivos de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[48], 'Independencia en las acciones, los pensamientos, sentimientos y disposición a nuevas experiencias.');
  });

  it('get English trait description: facet_morality', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('facet_morality'), 'Franqueza y expresión genuina; cándido, franco.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[29], 'Tendencia a experimentar –pero no necesariamente expresar– ira o frustración.');
  });

  // Japanese - to be implemented.  This is a placeholder.
  it('get Japanese trait description: big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });
});
<commit_msg>test.descriptions.js: added tests for Japanese language for both v2 and v3.<commit_after>/**
 * Copyright 2016 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const assert = require('chai').assert;
const PersonalityTraitDescriptions = require('../lib/index');

describe('names', () => {

  it('get default (English) trait description (v2): Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions();
    assert.equal(traitDescriptions.description('Agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get default (English) trait description (v2): Gregariousness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions();
    assert.equal(traitDescriptions.description('Gregariousness'), 'Fondness for the company of others; sociability.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[5], 'Eagerness to trying new activities and experiencing new things.');
  });

  it('get English trait description (v2): Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get English trait description (v2): Activity level', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Activity level'), 'Pace of living; level of busyness.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[15], 'Will-power; the capacity to begin tasks and follow through to completion in spite of boredom or distractions.');
  });

  it('get Spanish trait description (v2): Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Agreeableness'), 'Más alto: Valoran llevarse bien con otros. Tienen una visión más optimista de la naturaleza humana. \nMás bajo: Valoran sus propios intereses por sobre los de otros. Son más escépticos de los motivos de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Más alto: Auto-disciplinados, conscientes de sus deberes, o tendientes a querer alcanzar logros por encima de las medidas o expectativas externas. \nMás bajo: Más propensos a preferir lo espontáneo por sobre lo planificado.');
  });

  it('get Spanish trait description (v2): Trust', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Trust'), 'Nivel de creencia en la sinceridad y buenas intenciones de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[50], 'Éxito personal a través de la demostración de competencia de acuerdo a estándares sociales.');
  });

  it('get Japanese trait description (v2): Agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Agreeableness'), '高い人： 他人とうまくやっていくことを重要視するタイプです。人間の本性に関して楽観的な見方をしています。\n低い人： 他人より自分の興味を優先するタイプです。 他人の言葉の裏を考えてしまう傾向があります。');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], '高い人： 自己統制をし、誠実、あるいは外部の期待や評価に応えようとするタイプです。\n低い人： 計画に従うより、自発的に物事を行うことを好むタイプです。');
  });

  it('get Japanese trait description (v2): Trust', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja', 'version' : 'v2' });
    assert.equal(traitDescriptions.description('Trust'), '他人から誠実さと善意に対して信頼されている度合い。');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[50], '社会的な基準に基づいて能力を実証することによる個人的な成功。');
  });

  // V3 tests
  it('get English trait description (v3): big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), 'Higher: Value getting along with others. They have a more optimistic view of human nature.\nLower: Value self interests over others. They are more skeptical of others\' motives.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], 'Higher: More self-disciplined, dutiful, or aiming for achievement against measures or outside expectations.\nLower: More likely to prefer the spontaneous over the planned.');
  });

  it('get English trait description (v3): need_harmony', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'en', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('need_harmony'), 'A need to appreciate or please other people, their viewpoints, and feelings.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[47], 'Respect, commitment, and acceptance of the customs and ideas that one\'s culture and/or religion provides.');
  });

  it('get Spanish trait description (v3): big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), 'Más alto: Valoran llevarse bien con otros. Tienen una visión más optimista de la naturaleza humana. \nMás bajo: Valoran sus propios intereses por sobre los de otros. Son más escépticos de los motivos de los demás.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[48], 'Independencia en las acciones, los pensamientos, sentimientos y disposición a nuevas experiencias.');
  });

  it('get Spanish trait description (v3): facet_morality', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'es', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('facet_morality'), 'Franqueza y expresión genuina; cándido, franco.');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[29], 'Tendencia a experimentar –pero no necesariamente expresar– ira o frustración.');
  });

  it('get Japanese trait description (v3): big5_agreeableness', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('big5_agreeableness'), '高い人： 他人とうまくやっていくことを重要視するタイプです。人間の本性に関して楽観的な見方をしています。\n低い人： 他人より自分の興味を優先するタイプです。 他人の言葉の裏を考えてしまう傾向があります。');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[1], '高い人： 自己統制をし、誠実、あるいは外部の期待や評価に応えようとするタイプです。\n低い人： 計画に従うより、自発的に物事を行うことを好むタイプです。');
  });

  it('get Japanese trait description (v3): need_harmony', () => {
    const traitDescriptions = new PersonalityTraitDescriptions({ locale : 'ja', 'version' : 'v3' });
    assert.equal(traitDescriptions.description('need_harmony'), '他人、彼らの見解、及びおよび感覚を高く評価したり喜ばせる必要性。');
    assert.equal(traitDescriptions.descriptions().length, 52);
    assert.equal(traitDescriptions.descriptions()[47], '文化や宗教の違いによる習慣やアイディアに対する尊敬、約束、および受け入れ。');
  });
});
<|endoftext|>


<commit_before>import Device from '../device';
import Logger from '../../service/logger';
const WebOsLibrary = require('../../helpers/WebOS.js');

class DeviceWebOs extends Device {

  /**
   * This is extendable class for LG WebOs devices
   *
   * @class Device_WebOs
   * @extends Device
   * @constructor
   */
  constructor(config) {
    super();
    this.initEvents();
    this.initPlayerClass();
    Logger.addLog('Device_WebOS', 'info', 'Arcelik Device Initialized');
    this.Player.createVideoElement = this.createVideoElement;
    this.Config = Object.assign(this.Config, config); // Merges default config with user config
    this.addWebOSLib();
  }

  /**
   * WebOs native api's are required to use webos.js file.
   * This method provides necessary libraries.
   *
   * @for Device_WebOs
   * @method addWebOsLib
   * @return {Boolean} true
   */
  addWebOSLib() {
/*    this.webOSLibrary = document.createElement('script');
    this.webOSLibrary.setAttribute('type', 'text/javascript');
    this.webOSLibrary.setAttribute('src', 'lib/webOS.js');
    document.body.appendChild(this.webOSLibrary);*/
    this.WebOsLibrary = WebOsLibrary;
    Logger.addLog('Device_WebOs', 'info', 'WebOs Library loaded successfully', this.WebOsLibrary);
    return true;
  }

  /**
   * Abstract Player createVideoElement function.
   *
   * @abstract
   * @for Device_WebOs
   * @method createVideoElement
   * @return {Boolean} true
   */
  createVideoElement() {
    if (this.videoElement) {
      this.deleteVideoElement();
    }
    console.log(this.Config);
    this.videoElement = document.createElement('video');
    this.videoElement.style.position = 'absolute';
    this.videoElement.setAttribute('width', this.Config.width);
    this.videoElement.setAttribute('height', this.Config.height);
    this.videoElement.setAttribute('id', '');
    this.videoElement.setAttribute('class', 'player');
    document.body.appendChild(this.videoElement);
    this.setPlayerInfo('WEBOS');
    this.registerVideoEvents();
    Logger.addLog('Player', 'info', 'Player Element Created and Registered Video Events');
    this.initAudioClass();
    return null;
  }
}

export default DeviceWebOs;
<commit_msg>Added Network For LG Devices<commit_after>import Device from '../device';
import Logger from '../../service/logger';
// const WebOsLibrary = require('../../helpers/WebOS.js');

class DeviceWebOs extends Device {

  /**
   * This is extendable class for LG WebOs devices
   *
   * @class Device_WebOs
   * @extends Device
   * @constructor
   */
  constructor(config) {
    super();
    this.initEvents();
    this.initPlayerClass();
    this.initNetworkClass();
    this.initKeyListener();
    Logger.addLog('Device_WebOS', 'info', 'Arcelik Device Initialized');
    this.Player.createVideoElement = this.createVideoElement;
    this.Config = Object.assign(this.Config, config); // Merges default config with user config
    this.addWebOSLib();
  }

  /**
   * WebOs native api's are required to use webos.js file.
   * This method provides necessary libraries.
   *
   * @for Device_WebOs
   * @method addWebOsLib
   * @return {Boolean} true
   */
  addWebOSLib() {
/*    this.webOSLibrary = document.createElement('script');
    this.webOSLibrary.setAttribute('type', 'text/javascript');
    this.webOSLibrary.setAttribute('src', 'lib/webOS.js');
    document.body.appendChild(this.webOSLibrary);*/
    // this.WebOsLibrary = WebOsLibrary;
    Logger.addLog('Device_WebOs', 'info', 'WebOs Library loaded successfully', this.WebOsLibrary);
    return true;
  }

  /**
   * Abstract Player createVideoElement function.
   *
   * @abstract
   * @for Device_WebOs
   * @method createVideoElement
   * @return {Boolean} true
   */
  createVideoElement() {
    if (this.videoElement) {
      this.deleteVideoElement();
    }
    console.log(this.Config);
    this.videoElement = document.createElement('video');
    this.videoElement.style.position = 'absolute';
    this.videoElement.setAttribute('width', this.Config.width);
    this.videoElement.setAttribute('height', this.Config.height);
    this.videoElement.setAttribute('id', '');
    this.videoElement.setAttribute('class', 'player');
    document.body.appendChild(this.videoElement);
    this.setPlayerInfo('WEBOS');
    this.registerVideoEvents();
    Logger.addLog('Player', 'info', 'Player Element Created and Registered Video Events');
    this.initAudioClass();
    return null;
  }
}

export default DeviceWebOs;
<|endoftext|>


<commit_before>var coordinates = require('../utils/coordinates');
var debug = require('debug');

var error = debug('core:propertyTypes:warn');

var propertyTypes = module.exports.propertyTypes = {};

// Built-in property types.
registerPropertyType('array', [], arrayParse, arrayStringify);
registerPropertyType('boolean', false, boolParse);
registerPropertyType('int', 0, intParse);
registerPropertyType('number', 0, numberParse);
registerPropertyType('selector', '', selectorParse, selectorStringify);
registerPropertyType('src', '', srcParse);
registerPropertyType('string', '', defaultParse, defaultStringify);
registerPropertyType('vec3', { x: 0, y: 0, z: 0 }, vec3Parse, coordinates.stringify);

/**
 * Register a parser for re-use such that when someone uses `type` in the schema,
 * `schema.process` will set the property `parse` and `stringify`.
 *
 * @param {string} type - Type name.
 * @param [defaultValue=null] -
 *   Default value to use if component does not define default value.
 * @param {function} [parse=defaultParse] - Parse string function.
 * @param {function} [stringify=defaultStringify] - Stringify to DOM function.
 */
function registerPropertyType (type, defaultValue, parse, stringify) {
  if ('type' in propertyTypes) {
    error('Property type "' + type + '" is already registered.');
    return;
  }

  propertyTypes[type] = {
    default: defaultValue,
    parse: parse || defaultParse,
    stringify: stringify || defaultStringify
  };
}
module.exports.registerPropertyType = registerPropertyType;

function arrayParse (value) {
  if (Array.isArray(value)) { return value; }
  return value === null || value.length === 0 ?
    [] :
    value.split(',').map(function(str) {
      return str.trim();
    });
}

function arrayStringify (value) {
  return value.join(', ');
}

function defaultParse (value) {
  return value;
}

function defaultStringify (value) {
  if (value === null) { return 'null'; }
  return value.toString();
}

function boolParse (value) {
  return value !== 'false' && value !== false;
}

function intParse (value) {
  return parseInt(value, 10);
}

function numberParse (value) {
  return parseFloat(value, 10);
}

function selectorParse (value) {
  if (!value) { return null; }
  if (typeof value !== 'string') { return value; }
  return document.querySelector(value);
}

function selectorStringify (value) {
  if (value.getAttribute) {
    return '#' + value.getAttribute('id');
  }
  return defaultStringify(value);
}

/**
 * `src` parser for assets.
 *
 * @param {string} value - Can either be `url(<value>)` or a selector to an asset.
 * @returns {string} Parsed value from `url(<value>)` or src from `<someasset src>`.
 */
function srcParse (value) {
  var parsedUrl = value.match(/\url\((.+)\)/);
  if (parsedUrl) { return parsedUrl[1]; }

  var el = selectorParse(value);
  if (el) { return el.getAttribute('src'); }

  return '';
}

function vec3Parse (value) {
  return coordinates.parse(value, this.default);
}
<commit_msg>ok linter installed.. ugh like five of them now<commit_after>var coordinates = require('../utils/coordinates');
var debug = require('debug');

var error = debug('core:propertyTypes:warn');

var propertyTypes = module.exports.propertyTypes = {};

// Built-in property types.
registerPropertyType('array', [], arrayParse, arrayStringify);
registerPropertyType('boolean', false, boolParse);
registerPropertyType('int', 0, intParse);
registerPropertyType('number', 0, numberParse);
registerPropertyType('selector', '', selectorParse, selectorStringify);
registerPropertyType('src', '', srcParse);
registerPropertyType('string', '', defaultParse, defaultStringify);
registerPropertyType('vec3', { x: 0, y: 0, z: 0 }, vec3Parse, coordinates.stringify);

/**
 * Register a parser for re-use such that when someone uses `type` in the schema,
 * `schema.process` will set the property `parse` and `stringify`.
 *
 * @param {string} type - Type name.
 * @param [defaultValue=null] -
 *   Default value to use if component does not define default value.
 * @param {function} [parse=defaultParse] - Parse string function.
 * @param {function} [stringify=defaultStringify] - Stringify to DOM function.
 */
function registerPropertyType (type, defaultValue, parse, stringify) {
  if ('type' in propertyTypes) {
    error('Property type "' + type + '" is already registered.');
    return;
  }

  propertyTypes[type] = {
    default: defaultValue,
    parse: parse || defaultParse,
    stringify: stringify || defaultStringify
  };
}
module.exports.registerPropertyType = registerPropertyType;

function arrayParse (value) {
  if (Array.isArray(value)) { return value; }
  return value === null || value.length === 0
    ? []
    : value.split(',').map(function (str) {
      return str.trim();
    });
}

function arrayStringify (value) {
  return value.join(', ');
}

function defaultParse (value) {
  return value;
}

function defaultStringify (value) {
  if (value === null) { return 'null'; }
  return value.toString();
}

function boolParse (value) {
  return value !== 'false' && value !== false;
}

function intParse (value) {
  return parseInt(value, 10);
}

function numberParse (value) {
  return parseFloat(value, 10);
}

function selectorParse (value) {
  if (!value) { return null; }
  if (typeof value !== 'string') { return value; }
  return document.querySelector(value);
}

function selectorStringify (value) {
  if (value.getAttribute) {
    return '#' + value.getAttribute('id');
  }
  return defaultStringify(value);
}

/**
 * `src` parser for assets.
 *
 * @param {string} value - Can either be `url(<value>)` or a selector to an asset.
 * @returns {string} Parsed value from `url(<value>)` or src from `<someasset src>`.
 */
function srcParse (value) {
  var parsedUrl = value.match(/\url\((.+)\)/);
  if (parsedUrl) { return parsedUrl[1]; }

  var el = selectorParse(value);
  if (el) { return el.getAttribute('src'); }

  return '';
}

function vec3Parse (value) {
  return coordinates.parse(value, this.default);
}
<|endoftext|>


<commit_before>/**
 * Server Modul
 * @module ActsServer
 * @author Markus Gilg
 * @requires node_http
 * @requires node_https
 * @requires npm_connect
 * @requires npm_socket.io
 * @requires FileSystemHelper
 * @requires RequestHelper
 */
'use strict';

const CONNECT    = require('connect');
const IO         = require('socket.io');
const COMPRESS   = require("compression");
const HELM       = require("helmet");
const WEBSOCKETS = require('./../extensions/websocket.ext');
const CORS       = require("./../extensions/cors.ext");
const BODYPARSER = require("./../extensions/parser.ext");
const REDIRECT   = require("./../extensions/redirect.ext");
const STATICFILE = require("./../extensions/staticfiles.ext");
const DYNAMICAPI = require("./../extensions/dynamicapi.ext");
const ACCESS     = require("./../extensions/accesshandler");
const FILE       = require('./../common/filesystem.helper');
const REQU       = require('./../common/request.helper');
const APP        = CONNECT();

let _cfg        = null;
let _core       = null;
let _logger     = null;
let _socketio   = null;
let _ssloptions = null;
let _plugins    = null;
let _http       = require('http');

/**
 * try to load all Plugins
 * @function loadPlugins
 * @private
 * @param {Array} plugins Array of Plugins to load
 * @param {Object} server Server Instance
 * @param {Object} cfg Server Configuration
 */
const loadPlugins = function (plugins, server, cfg) {
    if (typeof plugins !== typeof [] || plugins.length < 1) {
        _logger.debug("no plugins loaded");
        return;
    }
    for (let i = 0, length = plugins.length; i < length; i++) {
        const plugin = plugins[i];
        try {
            require(plugin)(server, cfg);
            _logger.debug("plugin " + plugin + " successfully loaded");
        } catch (err) {
            _logger.debug("error while load plugin " + plugin);
            _logger.error(err);
        }
    }
};

/**
 * read the certificate Information when using ssl
 * @function handleSsl
 * @private
 */
const handleSsl = function () {
    if (_cfg.server.ssl.usessl) {
        _http = require('https');
        const cas = [];
        for (var i = 0, len = _cfg.server.ssl.certificationauthority.length; i < len; i++) {
            const obj = _cfg.server.ssl.certificationauthority[i];
            cas.push(FILE.getFileContent(obj));
        }
        _ssloptions = {
            ca: cas,
            cert: FILE.getFileContent(_cfg.server.ssl.certificate),
            key: FILE.getFileContent(_cfg.server.ssl.privatekey)
        };
    }
};

/**
 * load the Default Modules
 * @method initStandardModules
 * @private
 */
const initStandardModules = function () {
    // helmet security
    APP.use(HELM());

    // CORS Module first
    let tmpCors = new CORS(_cfg, _logger);
    APP.use(tmpCors.checkRequest);

    // load redirect module
    let tmpRedirect = new REDIRECT(_cfg, _logger);
    APP.use(tmpRedirect.handle);

    // parse request bodys
    let tmpParser = new BODYPARSER(_cfg, _logger);
    APP.use(tmpParser.parse);

    // gzip compression
    if (_cfg.server.compress) {
        APP.use(handleCompress);
    }

    // load plugins
    loadPlugins(_plugins, APP, _cfg);

    // use folder for static files from config file
    let tmpStaticFile = new STATICFILE(_cfg, _logger);
    APP.use(tmpStaticFile.request);

    // use dynamic api
    let tmpDynamicApi = new DYNAMICAPI(_cfg, _logger);
    APP.use(tmpDynamicApi.request);
};

/**
 * log server Infos of current running Server
 * @event serverRunning
 * @private
 */
const serverRunning = function () {
    let folder = 'Server Folder: ' + _cfg.serverdir;
    let server = 'Server running ' + (_cfg.server.ssl.usessl ? 'https' : 'http') + '://' + _cfg.server.address + ':' + _cfg.server.port;
    console.info(folder);
    _logger.info(folder);
    console.info(server);
    _logger.info(server);
    if (typeof this === 'function') {
        this();
    }
};

/**
 * handle Client Connect
 * @event handleClientConnect
 * @private
 * @param {Object} socket Node Socket Object
 */
const handleClientConnect = function (socket) {
    _logger.debug('socket connect ' + socket.remoteAddress);
    ACCESS.onClientConnect(socket);
};

/**
 * handle CLient Errors
 * @event handleClientError
 * @private
 * @param {Object} err Error Object
 * @param {Object} socket Node Socket Object
 */
const handleClientError = function (err, socket) {
    _logger.debug('client create a error');
    _logger.warning(err);
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
};

/**
 * handle Server was closed
 * @event handleServerClose
 * @private
 */
const handleServerClose = function () {
  _logger.debug('server is shutting down');
};

/**
 * compress in gzip when activate
 * @event handleCompress
 * @private
 */
const handleCompress = function (req, res, next) {
    if (req.headers['accept-encoding'] === 'compress, gzip') {
        COMPRESS(_cfg)(req, res, next);
    } else {
        next();
    }
};

/**
 * boot the Server
 * @function startServer
 * @param {Function} cb callback when Server is started
 */
const startServer = function (cb) {
    _logger.debug('try to start server');
    try {
        //start Server
        let server;
        if (_cfg.server.ssl.usessl) {
            _logger.debug('ssl was enabled', _ssloptions);
            server = _http.createServer(_ssloptions, APP);
        } else {
            server = _http.createServer(APP);
        }

        // create sockets
        if (_cfg.server.websockets.usewebsockets && _socketio === null) {
            _logger.debug('websockets was enabled');
            _socketio = IO(server);
            let tmpWebsockets = new WEBSOCKETS(_cfg, _logger);
            _socketio.on('connection', tmpWebsockets.setEventsOnSocket);
        }

        // handle Server Events
        server.on('clientError', handleClientError);
        server.on('close', handleServerClose);
        server.on('connection', handleClientConnect);

        APP.use(REQU.notFound);

        server.listen(_cfg.server.port, _cfg.server.address, serverRunning.bind(cb));
    } catch (ex) {
        _logger.error(ex);
    }
};

class ActsServer {
    constructor (cfg, plugins, logger) {
        _logger = logger;
        _cfg = cfg;

        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.webroot));
        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.api.routepath));
        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.websockets.socketpath));
        handleSsl();
        initStandardModules();
    }

    /**
     * Start the Server
     * @function start
     * @param {Function} cb callback when Server is started
     */
    start (cb) {
        return startServer(cb);
    }
}
module.exports = ActsServer;<commit_msg>using acceshandler<commit_after>/**
 * Server Modul
 * @module ActsServer
 * @author Markus Gilg
 * @requires node_http
 * @requires node_https
 * @requires npm_connect
 * @requires npm_socket.io
 * @requires FileSystemHelper
 * @requires RequestHelper
 */
'use strict';

const CONNECT    = require('connect');
const IO         = require('socket.io');
const COMPRESS   = require("compression");
const HELM       = require("helmet");
const WEBSOCKETS = require('./../extensions/websocket.ext');
const CORS       = require("./../extensions/cors.ext");
const BODYPARSER = require("./../extensions/parser.ext");
const REDIRECT   = require("./../extensions/redirect.ext");
const STATICFILE = require("./../extensions/staticfiles.ext");
const DYNAMICAPI = require("./../extensions/dynamicapi.ext");
const ACCESS     = require("./../extensions/accesshandler.ext");
const FILE       = require('./../common/filesystem.helper');
const REQU       = require('./../common/request.helper');
const APP        = CONNECT();

let _cfg        = null;
let _core       = null;
let _logger     = null;
let _socketio   = null;
let _ssloptions = null;
let _plugins    = null;
let _http       = require('http');
let _access     = null;

/**
 * try to load all Plugins
 * @function loadPlugins
 * @private
 * @param {Array} plugins Array of Plugins to load
 * @param {Object} server Server Instance
 * @param {Object} cfg Server Configuration
 */
const loadPlugins = function (plugins, server, cfg) {
    if (typeof plugins !== typeof [] || plugins.length < 1) {
        _logger.debug("no plugins loaded");
        return;
    }
    for (let i = 0, length = plugins.length; i < length; i++) {
        const plugin = plugins[i];
        try {
            require(plugin)(server, cfg);
            _logger.debug("plugin " + plugin + " successfully loaded");
        } catch (err) {
            _logger.debug("error while load plugin " + plugin);
            _logger.error(err);
        }
    }
};

/**
 * read the certificate Information when using ssl
 * @function handleSsl
 * @private
 */
const handleSsl = function () {
    if (_cfg.server.ssl.usessl) {
        _http = require('https');
        const cas = [];
        for (var i = 0, len = _cfg.server.ssl.certificationauthority.length; i < len; i++) {
            const obj = _cfg.server.ssl.certificationauthority[i];
            cas.push(FILE.getFileContent(obj));
        }
        _ssloptions = {
            ca: cas,
            cert: FILE.getFileContent(_cfg.server.ssl.certificate),
            key: FILE.getFileContent(_cfg.server.ssl.privatekey)
        };
    }
};

/**
 * load the Default Modules
 * @method initStandardModules
 * @private
 */
const initStandardModules = function () {
    // helmet security
    APP.use(HELM());

    // CORS Module first
    let tmpCors = new CORS(_cfg, _logger);
    APP.use(tmpCors.checkRequest);

    // load redirect module
    let tmpRedirect = new REDIRECT(_cfg, _logger);
    APP.use(tmpRedirect.handle);

    // parse request bodys
    let tmpParser = new BODYPARSER(_cfg, _logger);
    APP.use(tmpParser.parse);

    // gzip compression
    if (_cfg.server.compress) {
        APP.use(handleCompress);
    }

    // load plugins
    loadPlugins(_plugins, APP, _cfg);

    // use folder for static files from config file
    let tmpStaticFile = new STATICFILE(_cfg, _logger);
    APP.use(tmpStaticFile.request);

    // use dynamic api
    let tmpDynamicApi = new DYNAMICAPI(_cfg, _logger);
    APP.use(tmpDynamicApi.request);
};

/**
 * log server Infos of current running Server
 * @event serverRunning
 * @private
 */
const serverRunning = function () {
    let folder = 'Server Folder: ' + _cfg.serverdir;
    let server = 'Server running ' + (_cfg.server.ssl.usessl ? 'https' : 'http') + '://' + _cfg.server.address + ':' + _cfg.server.port;
    console.info(folder);
    _logger.info(folder);
    console.info(server);
    _logger.info(server);
    if (typeof this === 'function') {
        this();
    }
};

/**
 * handle Client Connect
 * @event handleClientConnect
 * @private
 * @param {Object} socket Node Socket Object
 */
const handleClientConnect = function (socket) {
    _logger.debug('socket connect ' + socket.remoteAddress);
    _access.onClientConnect(socket);
};

/**
 * handle CLient Errors
 * @event handleClientError
 * @private
 * @param {Object} err Error Object
 * @param {Object} socket Node Socket Object
 */
const handleClientError = function (err, socket) {
    _logger.debug('client create a error');
    _logger.warning(err);
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
};

/**
 * handle Server was closed
 * @event handleServerClose
 * @private
 */
const handleServerClose = function () {
  _logger.debug('server is shutting down');
};

/**
 * compress in gzip when activate
 * @event handleCompress
 * @private
 */
const handleCompress = function (req, res, next) {
    if (req.headers['accept-encoding'] === 'compress, gzip') {
        COMPRESS(_cfg)(req, res, next);
    } else {
        next();
    }
};

/**
 * boot the Server
 * @function startServer
 * @param {Function} cb callback when Server is started
 */
const startServer = function (cb) {
    _logger.debug('try to start server');
    try {
        //start Server
        let server;
        if (_cfg.server.ssl.usessl) {
            _logger.debug('ssl was enabled', _ssloptions);
            server = _http.createServer(_ssloptions, APP);
        } else {
            server = _http.createServer(APP);
        }

        // create sockets
        if (_cfg.server.websockets.usewebsockets && _socketio === null) {
            _logger.debug('websockets was enabled');
            _socketio = IO(server);
            let tmpWebsockets = new WEBSOCKETS(_cfg, _logger);
            _socketio.on('connection', tmpWebsockets.setEventsOnSocket);
        }

        // handle Server Events
        server.on('clientError', handleClientError);
        server.on('close', handleServerClose);
        server.on('connection', handleClientConnect);

        APP.use(REQU.notFound);

        server.listen(_cfg.server.port, _cfg.server.address, serverRunning.bind(cb));
    } catch (ex) {
        _logger.error(ex);
    }
};

class ActsServer {
    constructor (cfg, plugins, logger) {
        _logger = logger;
        _cfg = cfg;
        _access = new ACCESS(_cfg, _logger);

        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.webroot));
        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.api.routepath));
        FILE.createDirectoryRecursive(FILE.joinPath(_cfg.serverdir, _cfg.server.websockets.socketpath));
        handleSsl();
        initStandardModules();
    }

    /**
     * Start the Server
     * @function start
     * @param {Function} cb callback when Server is started
     */
    start (cb) {
        return startServer(cb);
    }
}
module.exports = ActsServer;<|endoftext|>


<commit_before>import { Router } from 'express';
import { Vote } from '../models';
import errorCatcher from './helpers/errorCatcher';
import checkTeamRole from './helpers/checkTeamRole';
import loggedIn from './helpers/loggedIn';
import { votePosted, voteDeleted } from '../actions/restaurants';

export default () => {
  const router = new Router({ mergeParams: true });

  const notFound = (res) => {
    res.status(404).json({ error: true, data: { message: 'Vote not found.' } });
  };

  return router
    .post(
      '/',
      loggedIn,
      checkTeamRole(),
      async (req, res) => {
        const restaurantId = parseInt(req.params.restaurant_id, 10);
        try {
          const count = await Vote.recentForRestaurantAndUser(restaurantId, req.user.id);

          if (count === 0) {
            try {
              const obj = await Vote.create({
                restaurant_id: restaurantId,
                user_id: req.user.id
              });

              const json = obj.toJSON();
              req.wss.broadcast(votePosted(json));
              res.status(201).send({ error: false, data: obj });
            } catch (err) {
              const error = { message: 'Could not vote. Did you already vote today?' };
              errorCatcher(res, error);
            }
          } else {
            const error = { message: 'Could not vote. Did you already vote today?' };
            errorCatcher(res, error);
          }
        } catch (err) {
          errorCatcher(res, err);
        }
      }
    )
    .delete(
      '/:id',
      loggedIn,
      checkTeamRole(),
      async (req, res) => {
        const id = parseInt(req.params.id, 10);

        try {
          const count = await Vote.destroy({ where: { id, user_id: req.user.id } });

          if (count === 0) {
            notFound(res);
          } else {
            req.wss.broadcast(voteDeleted(parseInt(req.params.restaurant_id, 10), req.user.id, id));
            res.status(204).send();
          }
        } catch (err) {
          errorCatcher(res, err);
        }
      }
    );
};
<commit_msg>Remove erroneous error<commit_after>import { Router } from 'express';
import { Vote } from '../models';
import errorCatcher from './helpers/errorCatcher';
import checkTeamRole from './helpers/checkTeamRole';
import loggedIn from './helpers/loggedIn';
import { votePosted, voteDeleted } from '../actions/restaurants';

export default () => {
  const router = new Router({ mergeParams: true });

  const notFound = (res) => {
    res.status(404).json({ error: true, data: { message: 'Vote not found.' } });
  };

  return router
    .post(
      '/',
      loggedIn,
      checkTeamRole(),
      async (req, res) => {
        const restaurantId = parseInt(req.params.restaurant_id, 10);
        try {
          const count = await Vote.recentForRestaurantAndUser(restaurantId, req.user.id);

          if (count === 0) {
            try {
              const obj = await Vote.create({
                restaurant_id: restaurantId,
                user_id: req.user.id
              });

              const json = obj.toJSON();
              req.wss.broadcast(votePosted(json));
              res.status(201).send({ error: false, data: obj });
            } catch (err) {
              errorCatcher(res, err);
            }
          } else {
            const error = { message: 'Could not vote. Did you already vote today?' };
            errorCatcher(res, error);
          }
        } catch (err) {
          errorCatcher(res, err);
        }
      }
    )
    .delete(
      '/:id',
      loggedIn,
      checkTeamRole(),
      async (req, res) => {
        const id = parseInt(req.params.id, 10);

        try {
          const count = await Vote.destroy({ where: { id, user_id: req.user.id } });

          if (count === 0) {
            notFound(res);
          } else {
            req.wss.broadcast(voteDeleted(parseInt(req.params.restaurant_id, 10), req.user.id, id));
            res.status(204).send();
          }
        } catch (err) {
          errorCatcher(res, err);
        }
      }
    );
};
<|endoftext|>


<commit_before>// Global hooks that run for every service
const { GeneralError, NotAuthenticated, Timeout } = require('@feathersjs/errors');
const { iff, isProvider } = require('feathers-hooks-common');
const { Configuration } = require('@schul-cloud/commons');
const logger = require('./logger');
const {
	sanitizeHtml: { sanitizeDeep },
} = require('./utils');
const { getRedisClient, redisGetAsync, redisSetAsync, extractDataFromJwt, getRedisData } = require('./utils/redis');
const { LEAD_TIME } = require('../config/globals');

const sanitizeDataHook = (context) => {
	if ((context.data || context.result) && context.path && context.path !== 'authentication') {
		sanitizeDeep(context.type === 'before' ? context.data : context.result, context.path, 0, context.safeAttributes);
	}
	return context;
};

const removeObjectIdInData = (context) => {
	if (context.data && context.data._id) {
		delete context.data._id;
	}
	return context;
};

const displayInternRequests = (level) => (context) => {
	if (context.params.provider === 'rest') {
		return context;
	}
	const { id, params, path, data, method } = context;

	if (['accounts'].includes(path) && level < 4) {
		return context;
	}
	const out = {
		path,
		method,
	};
	if (id) {
		out.id = id;
	}
	Object.keys(params).forEach((key) => {
		if (params.key) {
			out[key] = params.key;
		}
	});
	if (data) {
		out.data = data;
	}

	// eslint-disable-next-line no-console
	console.log('[intern]');
	// eslint-disable-next-line no-console
	console.log(out);
	// eslint-disable-next-line no-console
	console.log(' ');

	return context;
};

/**
 * Routes as (regular expressions) which should be ignored for the auto-logout feature.
 */
const AUTO_LOGOUT_BLACKLIST = [/^accounts\/jwtTimer$/, /^authentication$/, /wopi\//, /roster\//];

/**
 * for authenticated requests, if a redis connection is defined, check if the users jwt is whitelisted.
 * if so, the expiration timer is reset, if not the user is logged out automatically.
 * @param {Object} context feathers context
 */
const handleAutoLogout = async (context) => {
	const ignoreRoute =
		typeof context.path === 'string' && AUTO_LOGOUT_BLACKLIST.some((entry) => context.path.match(entry));
	const redisClientExists = !!getRedisClient();
	const authorizedRequest = ((context.params || {}).authentication || {}).accessToken;
	if (!ignoreRoute && redisClientExists && authorizedRequest) {
		const { redisIdentifier, privateDevice } = extractDataFromJwt(context.params.authentication.accessToken);
		const redisResponse = await redisGetAsync(redisIdentifier);
		const redisData = getRedisData({ privateDevice });
		const { expirationInSeconds } = redisData;
		if (redisResponse) {
			await redisSetAsync(redisIdentifier, JSON.stringify(redisData), 'EX', expirationInSeconds);
		} else {
			// ------------------------------------------------------------------------
			// this is so we can ensure a fluid release without booting out all users.
			if (Configuration.get('JWT_WHITELIST_ACCEPT_ALL')) {
				await redisSetAsync(redisIdentifier, JSON.stringify(redisData), 'EX', expirationInSeconds);
				return context;
			}
			// ------------------------------------------------------------------------
			throw new NotAuthenticated('Session was expired due to inactivity - autologout.');
		}
	}
	return context;
};

/**
 * For errors without error code create GeneralError with code 500.
 * @param {context} context
 */
const errorHandler = (context) => {
	if (context.error) {
		context.error.code = context.error.code || context.error.statusCode;
		if (!context.error.code && !context.error.type) {
			const catchedError = context.error;
			if (catchedError.hook) {
				// too much for logging...
				delete catchedError.hook;
			}
			context.error = new GeneralError(context.error.message || 'Server Error', context.error.stack);
			context.error.catchedError = catchedError;
		}
		context.error.code = context.error.code || 500;

		if (context.error.hook) {
			// too much for logging...
			delete context.error.hook;
		}
		return context;
	}
	context.app.logger.warning('Error with no error key is throw. Error logic can not handle it.');

	throw new GeneralError('server error');
};

// adding in this position will detect intern request to
const leadTimeDetection = (context) => {
	if (context.params.leadTime) {
		const timeDelta = Date.now() - context.params.leadTime;
		if (timeDelta >= LEAD_TIME) {
			// TODO: can replaced if we throw an error slow Query after merging new error pipline
			const {
				path,
				id,
				method,
				params: { query, headers, originalUrl },
			} = context;

			const error = {
				name: 'SlowQuery',
				message: `Slow query warning at route ${context.path}`,
				code: 408,
				path,
				method,
				query,
				timeDelta,
				originalUrl,
			};

			if (id) {
				error.id = id;
			}

			if (headers) {
				//	error.connection = headers.connection;
				error.requestId = headers.requestId;
			}
			logger.error(error);
		}
	}
};

function setupAppHooks(app) {
	const before = {
		all: [iff(isProvider('external'), handleAutoLogout)],
		find: [],
		get: [],
		create: [iff(isProvider('external'), [sanitizeDataHook, removeObjectIdInData])],
		update: [iff(isProvider('external'), [sanitizeDataHook])],
		patch: [iff(isProvider('external'), [sanitizeDataHook])],
		remove: [],
	};

	const after = {
		all: [],
		find: [iff(isProvider('external'), [sanitizeDataHook])],
		get: [iff(isProvider('external'), [sanitizeDataHook])],
		create: [],
		update: [],
		patch: [],
		remove: [],
	};

	const error = {
		all: [errorHandler],
		find: [],
		get: [],
		create: [],
		update: [],
		patch: [],
		remove: [],
	};

	// DISPLAY_REQUEST_LEVEL is set by requestLogger middleware in production it is force to 0
	// level 2+ adding intern request
	if (app.get('DISPLAY_REQUEST_LEVEL') > 1) {
		before.all.unshift(displayInternRequests(app.get('DISPLAY_REQUEST_LEVEL')));
	}
	if (LEAD_TIME) {
		['find', 'get', 'create', 'update', 'patch', 'remove'].forEach((m) => {
			if (Array.isArray(after[m])) {
				after[m].push(leadTimeDetection);
			}
		});
	}
	app.hooks({ before, after, error });
}

module.exports = {
	handleAutoLogout,
	sanitizeDataHook,
	setupAppHooks,
};
<commit_msg>linter<commit_after>// Global hooks that run for every service
const { GeneralError, NotAuthenticated } = require('@feathersjs/errors');
const { iff, isProvider } = require('feathers-hooks-common');
const { Configuration } = require('@schul-cloud/commons');
const logger = require('./logger');
const {
	sanitizeHtml: { sanitizeDeep },
} = require('./utils');
const { getRedisClient, redisGetAsync, redisSetAsync, extractDataFromJwt, getRedisData } = require('./utils/redis');
const { LEAD_TIME } = require('../config/globals');

const sanitizeDataHook = (context) => {
	if ((context.data || context.result) && context.path && context.path !== 'authentication') {
		sanitizeDeep(context.type === 'before' ? context.data : context.result, context.path, 0, context.safeAttributes);
	}
	return context;
};

const removeObjectIdInData = (context) => {
	if (context.data && context.data._id) {
		delete context.data._id;
	}
	return context;
};

const displayInternRequests = (level) => (context) => {
	if (context.params.provider === 'rest') {
		return context;
	}
	const { id, params, path, data, method } = context;

	if (['accounts'].includes(path) && level < 4) {
		return context;
	}
	const out = {
		path,
		method,
	};
	if (id) {
		out.id = id;
	}
	Object.keys(params).forEach((key) => {
		if (params.key) {
			out[key] = params.key;
		}
	});
	if (data) {
		out.data = data;
	}

	// eslint-disable-next-line no-console
	console.log('[intern]');
	// eslint-disable-next-line no-console
	console.log(out);
	// eslint-disable-next-line no-console
	console.log(' ');

	return context;
};

/**
 * Routes as (regular expressions) which should be ignored for the auto-logout feature.
 */
const AUTO_LOGOUT_BLACKLIST = [/^accounts\/jwtTimer$/, /^authentication$/, /wopi\//, /roster\//];

/**
 * for authenticated requests, if a redis connection is defined, check if the users jwt is whitelisted.
 * if so, the expiration timer is reset, if not the user is logged out automatically.
 * @param {Object} context feathers context
 */
const handleAutoLogout = async (context) => {
	const ignoreRoute =
		typeof context.path === 'string' && AUTO_LOGOUT_BLACKLIST.some((entry) => context.path.match(entry));
	const redisClientExists = !!getRedisClient();
	const authorizedRequest = ((context.params || {}).authentication || {}).accessToken;
	if (!ignoreRoute && redisClientExists && authorizedRequest) {
		const { redisIdentifier, privateDevice } = extractDataFromJwt(context.params.authentication.accessToken);
		const redisResponse = await redisGetAsync(redisIdentifier);
		const redisData = getRedisData({ privateDevice });
		const { expirationInSeconds } = redisData;
		if (redisResponse) {
			await redisSetAsync(redisIdentifier, JSON.stringify(redisData), 'EX', expirationInSeconds);
		} else {
			// ------------------------------------------------------------------------
			// this is so we can ensure a fluid release without booting out all users.
			if (Configuration.get('JWT_WHITELIST_ACCEPT_ALL')) {
				await redisSetAsync(redisIdentifier, JSON.stringify(redisData), 'EX', expirationInSeconds);
				return context;
			}
			// ------------------------------------------------------------------------
			throw new NotAuthenticated('Session was expired due to inactivity - autologout.');
		}
	}
	return context;
};

/**
 * For errors without error code create GeneralError with code 500.
 * @param {context} context
 */
const errorHandler = (context) => {
	if (context.error) {
		context.error.code = context.error.code || context.error.statusCode;
		if (!context.error.code && !context.error.type) {
			const catchedError = context.error;
			if (catchedError.hook) {
				// too much for logging...
				delete catchedError.hook;
			}
			context.error = new GeneralError(context.error.message || 'Server Error', context.error.stack);
			context.error.catchedError = catchedError;
		}
		context.error.code = context.error.code || 500;

		if (context.error.hook) {
			// too much for logging...
			delete context.error.hook;
		}
		return context;
	}
	context.app.logger.warning('Error with no error key is throw. Error logic can not handle it.');

	throw new GeneralError('server error');
};

// adding in this position will detect intern request to
const leadTimeDetection = (context) => {
	if (context.params.leadTime) {
		const timeDelta = Date.now() - context.params.leadTime;
		if (timeDelta >= LEAD_TIME) {
			// TODO: can replaced if we throw an error slow Query after merging new error pipline
			const {
				path,
				id,
				method,
				params: { query, headers, originalUrl },
			} = context;

			const error = {
				name: 'SlowQuery',
				message: `Slow query warning at route ${context.path}`,
				code: 408,
				path,
				method,
				query,
				timeDelta,
				originalUrl,
			};

			if (id) {
				error.id = id;
			}

			if (headers) {
				//	error.connection = headers.connection;
				error.requestId = headers.requestId;
			}
			logger.error(error);
		}
	}
};

function setupAppHooks(app) {
	const before = {
		all: [iff(isProvider('external'), handleAutoLogout)],
		find: [],
		get: [],
		create: [iff(isProvider('external'), [sanitizeDataHook, removeObjectIdInData])],
		update: [iff(isProvider('external'), [sanitizeDataHook])],
		patch: [iff(isProvider('external'), [sanitizeDataHook])],
		remove: [],
	};

	const after = {
		all: [],
		find: [iff(isProvider('external'), [sanitizeDataHook])],
		get: [iff(isProvider('external'), [sanitizeDataHook])],
		create: [],
		update: [],
		patch: [],
		remove: [],
	};

	const error = {
		all: [errorHandler],
		find: [],
		get: [],
		create: [],
		update: [],
		patch: [],
		remove: [],
	};

	// DISPLAY_REQUEST_LEVEL is set by requestLogger middleware in production it is force to 0
	// level 2+ adding intern request
	if (app.get('DISPLAY_REQUEST_LEVEL') > 1) {
		before.all.unshift(displayInternRequests(app.get('DISPLAY_REQUEST_LEVEL')));
	}
	if (LEAD_TIME) {
		['find', 'get', 'create', 'update', 'patch', 'remove'].forEach((m) => {
			if (Array.isArray(after[m])) {
				after[m].push(leadTimeDetection);
			}
		});
	}
	app.hooks({ before, after, error });
}

module.exports = {
	handleAutoLogout,
	sanitizeDataHook,
	setupAppHooks,
};
<|endoftext|>


<commit_before>'use strict';

define(['angular'], function(angular) {

  var app = angular.module('portal.widgets.controllers', []);

  /**
   * Base widget controller (for /partials/widget-card.html). First point of access for all widget types -- determines
   * the type and sets launch button url for un-typed (basic) widgets.
   */
  app.controller('WidgetCardController', ['$scope', '$log', '$localStorage', 'widgetService', function($scope, $log, $localStorage, widgetService) {
    /**
     * Check for widget types that require extra configuration (including null/undefined case), default to provided
     * widget type.
     * @param widget The widget object provided by widgetService
     * @returns {*} A string for the widget type
     */
    var widgetType = function widgetType(widget) {
      // Check for types that need handling
      switch(widget.widgetType) {
        case 'list-of-links':
          // If the list of links only has one link and it's the same as the launch button url, display a basic widget
          if (widget.widgetConfig.links.length === 1 && widget.altMaxUrl && widget.widgetConfig.links[0].href === widget.url) {
            return 'basic';
          } else {
            return 'list-of-links'
          }
        case 'generic':
          // DEPRECATED: Included for backwards compatibility. Use 'custom' instead.
          return 'custom';
        case null:
          // If widgetType doesn't exist, show a basic widget
          return 'basic';
        default:
          return widget.widgetType;
      }
    };

    /**
     * Initial widget setup -- gets data for a single widget from the provided fname attribute
     */
    var initializeWidget = function(fname) {
      // Initialize scope variables
      $scope.widget = {};
      $scope.widgetType = '';

      // Temporarily necessary for CSS to enforce max-width on widgets not constrained by a responsive grid (i.e. not in ng-portal)
      $scope.isSingleWidget = true;

      // Get widget data for provided app (fname)
      widgetService.getSingleWidgetData(fname)
        .then(function(data) {
          // Set scope variables
          if (data) {
            $scope.widget = data;
            $scope.widgetType = widgetType(data);
          }
        })
        .catch(function(error) {
          $log.warn('WidgetCardController couldn\'t get data for: ' + fname);
          $log.error(error);
        });
    };

    /**
     * Set the launch button url for static content, exclusive mode, and basic widgets
     * @returns {*}
     */
    $scope.renderUrl = function renderUrl() {
      var widget = $scope.widget;
      // Check for static content or exclusive mode portlets (rare)
      if (widget.altMaxUrl == false) {
        if (widget.staticContent != null) {
          return 'static/' + widget.fname;
        } else if (widget.renderOnWeb || $localStorage.webPortletRender) {
          return 'exclusive/' + widget.fname;
        }
      } else {
        // For basic widget, just use the provided url
        return widget.url;
      }
    };

    // Initialize the widget
    if ($scope.fname) {
      initializeWidget($scope.fname);
    } else {
      $log.warn('WidgetCardController didn\'t get an fname.');
    }

  }]);

  // OPTION LINK widget type
  app.controller('OptionLinkController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Set up default configuration if no config exists
     */
    var configInit = function() {
      $scope.config = {
        singleElement: false,
        arrayName: 'array',
        value: 'value',
        display: 'display'
      };
    };

    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      if ($scope.widget.widgetURL && $scope.widget.widgetType) {
        // Initialize widget data
        $scope.widget.widgetData = [];
        // Get data and set widget content
        widgetService.getWidgetJson($scope.widget).then(function(data) {
          if (data) {
            if ($scope.config.singleElement) {
              // Set the default selected url
              $scope.widget.selectedUrl = $scope.widget.widgetData[$scope.config.value];
            } else if ($scope.widget.widgetData[$scope.config.arrayName] && $scope.widget.widgetData[$scope.config.arrayName].length > 0) {
              // If multiple values come back, set selected url to the first entry
              $scope.widget.selectedUrl = $scope.widget.widgetData[$scope.config.arrayName][0][$scope.config.value];
            }
          } else {
            $log.warn('OptionLinkController couldn\'t get json for: ' + $scope.widget.fname);
          }
        });
      }
    };

    // Set default values if no config was received
    if (!$scope.config) {
      configInit();
      $log.warn('OptionLinkController didn\'t receive a widget config');
    }
    // Set up widget content
    populateWidgetContent();
  }]);

  // SEARCH WITH LINKS widget type
  app.controller('SearchWithLinksController', ['$scope', '$sce', function($scope, $sce) {
    // Have faith our entity files aren't trying to bamboozle us
    $scope.secureURL = $sce.trustAsResourceUrl($scope.config.actionURL);
  }]);

  // RSS widget type
  app.controller('RssWidgetController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Turn the provided date string into an actual Date so it can be filtered into something prettier.
     * @param dateString A hideously ugly date string
     * @returns {*} A Date object or null
     */
    $scope.getPrettyDate = function(dateString) {
      // Create a new date if a date string was provided, otherwise return null
      return dateString ? new Date(dateString) : null;
    };

    /**
     * Set defaults if any values are missing from provided widgetConfig
     */
    var checkForWidgetConfig = function() {
      if (!$scope.config) {
        $scope.config = {};
      }
      if (!$scope.config.lim) {
        $scope.config.lim = 5;
      }
      if (!$scope.config.titleLim) {
        $scope.config.titleLim = 40;
      }
      if (!$scope.config.showShowing) {
        $scope.config.showShowing = false;
      }
    };

    /**
     * Initialize rss widget
     */
    var initializeRssWidget = function() {
      // Trigger loading spinner
      $scope.loading = true;

      // Only initialize if widget's json provides all the stuff we need
      if ($scope.widget && $scope.widget.widgetURL) {

        // Make sure config has values
        checkForWidgetConfig();

        // Get rss feed from provided url
        widgetService.getRssAsJson($scope.widget.widgetURL)
          .then(function(result) {
            // If we got data, load it and turn off loading spinner
            $scope.data = result;
            $scope.loading = false;

            // Check for errors or emptiness and update display as needed
            if ($scope.status !== 'ok') {
              $scope.error = true;
              $scope.loading = false;
            } else {
              if (!$scope.items || $scope.items.length == 0) {
                $scope.isEmpty = true;
                $scope.loading = false;
                $scope.error = true;
              } else {
                // Show 'show more' if the feed is longer than the display limit
                if (!$scope.config.showShowing && $scope.items.length > $scope.config.lim) {
                  $scope.config.showShowing = true;
                }
              }
            }
          })
          .catch(function(error) {
            // If the service couldn't get data, display error messages
            $log.warn('Couldn\'t get rss as JSON');
            $log.error(error);

            $scope.error = true;
            $scope.isEmpty = true;
            $scope.loading = false;
          });
      }
    };

    // Initialize the widget
    initializeRssWidget();
  }]);

  // CUSTOM & GENERIC widget types
  app.controller('CustomWidgetController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      if ($scope.widget.widgetURL && $scope.widget.widgetType) {
        // Trigger loading spinner
        $scope.loading = true;

        // Fetch additional widget data
        widgetService.getWidgetJson($scope.widget).then(function(data) {
          $scope.loading = false;
          if (data) {
            $scope.widget.widgetData = data;
            $scope.content = $scope.widget.widgetData;
            if (angular.isArray($scope.content) && $scope.content.length == 0) {
              $scope.isEmpty = true;
            } else if ($scope.widget.widgetConfig && $scope.widget.widgetConfig.evalString && eval($scope.widget.widgetConfig.evalString)) {
              $scope.isEmpty = true;
            }
          } else {
            $log.warn('Got nothing back from widget fetch from: ' + $scope.widget.widgetURL);
            $scope.isEmpty = true;
          }
        }, function() {
          // After we get widget data, turn off loading spinner
          $scope.loading = false;
        });
      }
    };

    /**
     * Initialize scope variables before getting widget content
     * @param template The provided custom HTML template
     */
    var initializeCustomWidget = function(template) {
      $scope.content = [];
      $scope.template = template;
      $scope.isEmpty = false;
      $scope.widget.widgetData = [];
      populateWidgetContent();
    };

    // Initialize widget if we received a custom HTML template, otherwise display empty widget and log a warning
    if ($scope.widget.widgetTemplate) {
      initializeCustomWidget($scope.widget.widgetTemplate);
    } else {
      $scope.loading = false;
      $scope.isEmpty = true;
      $log.warn($scope.widget.fname + ' said it\'s a custom/generic widget, but didn\'t provide a template.');
    }
  }]);

  // WEATHER widget type
  app.controller('WeatherWidgetController', ['$scope', '$log', '$q', 'widgetService', 'keyValueService', function($scope, $log, $q, widgetService, keyValueService) {
    // Local variables
    var fetchKey = 'userWeatherPreference';

    /**
     * Change temperature unit of measurement
     */
    $scope.cycleUnits = function() {
      var userPreference = $scope.nextUnits;
      var value = {};

      // Switch temperature based on which unit is next
      switch(userPreference) {
        case 'F':
          convertKelvinToFahrenheit();
          $scope.currentUnits = 'F';
          $scope.nextUnits = 'C';
          break;
        case 'C':
          convertFahrenheitToCelsius();
          $scope.currentUnits = 'C';
          $scope.nextUnits = 'K';
          break;
        case 'K':
          convertCelsiusToKelvin();
          $scope.currentUnits = 'K';
          $scope.nextUnits = 'F';
          break;
        default:
          convertCelsiusToKelvin();
          $scope.currentUnits = 'K';
          $scope.nextUnits = 'F';
      }

      // Set user preference
      value.userWeatherPreference = $scope.currentUnits;

      // Remember the user's preferred unit of measurement
      keyValueService.setValue(fetchKey, value);
    };

    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      // Declare asynchronous promises to be executed in $q.all()
      var widgetPromise = widgetService.getWidgetJson($scope.widget);
      var userPreferencesPromise = keyValueService.getValue(fetchKey);

      // Execute promises, then resolve when both have returned something
      $q.all( [widgetPromise, userPreferencesPromise] ).then(function(data) {

        // Turn off loading spinner
        $scope.loading = false;

        // If we got some data, populate widget content
        if (data) {
          // Set local variables
          var allTheWeathers = data[0];
          var myPref = data[1];
          var userPreference = myPref.userWeatherPreference;

          // Set scope variables
          if (allTheWeathers) {
            $scope.weatherData = allTheWeathers.weathers;
          }
          $scope.currentUnits = 'F';
          $scope.nextUnits = 'C';

          // If the user doesn't have a preference, default to Fahrenheit
          if (userPreference === null || userPreference === '' || typeof userPreference === 'undefined') {
            userPreference = 'F';
          }

          // Cycle units until we match with the user's preference
          while (userPreference != $scope.currentUnits) {
            $scope.cycleUnits();
          }
        } else {
          // Log any errors getting data
          $scope.error = true;
          $log.warn('WeatherWidgetController could\'t get widget json');
        }

        // Return to resolve $q.all()
        return data;
      }).catch(function() {
        $scope.loading = false;
        $scope.error = true;
        $log.warn('WeatherWidgetController wrote a check it simply couldn\'t cash');
      });
    };

    /**
     * Convert from Fahrenheit to Celsius
     */
    var convertFahrenheitToCelsius = function() {
      var ratio = (5 / 9);
      var offset = 32;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature -
          offset) * ratio;
        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature -
            offset) * ratio;
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature -
            offset) * ratio;
        }
      }
    };

    /**
     * Convert from Celsius to Fahrenheit
     */
    var convertCelsiusToFahrenheit = function() {
      var ratio = (9 / 5);
      var offset = 32;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature *
          ratio) + offset;

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature *
            ratio) + offset;
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature *
            ratio) + offset;
        }
      }
    };

    /**
     * Convert from Celsius to Kelvin
     */
    var convertCelsiusToKelvin = function() {
      var offset = 273;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature + offset);

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature + offset);
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature + offset);
        }
      }
    };

    /**
     * Convert from Kelvin to Fahrenheit (via Celsius)
     */
    var convertKelvinToFahrenheit = function() {
      convertKelvinToCelsius();
      convertCelsiusToFahrenheit();
    };

    /**
     * Convert from Kelvin to Celsius
     */
    var convertKelvinToCelsius = function() {
      var offset = 273;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature - offset);

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature - offset);
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature - offset);
        }
      }
    };

    // Initialize weather widget
    $scope.loading = false;
    if ($scope.widget.widgetURL) {
      $scope.loading = true;
      $scope.weatherData = [];
      $scope.currentUnits = 'F';
      $scope.nextUnits = 'C';
      populateWidgetContent();
    } else {
      $log.warn('WeatherWidgetController did not receive a widgetURL');
    }

  }]);

});
<commit_msg>Restore filteredArray functionality<commit_after>'use strict';

define(['angular'], function(angular) {

  var app = angular.module('portal.widgets.controllers', []);

  /**
   * Base widget controller (for /partials/widget-card.html). First point of access for all widget types -- determines
   * the type and sets launch button url for un-typed (basic) widgets.
   */
  app.controller('WidgetCardController', ['$scope', '$log', '$localStorage', 'widgetService', function($scope, $log, $localStorage, widgetService) {
    /**
     * Check for widget types that require extra configuration (including null/undefined case), default to provided
     * widget type.
     * @param widget The widget object provided by widgetService
     * @returns {*} A string for the widget type
     */
    var widgetType = function widgetType(widget) {
      // Check for types that need handling
      switch(widget.widgetType) {
        case 'list-of-links':
          // If the list of links only has one link and it's the same as the launch button url, display a basic widget
          if (widget.widgetConfig.links.length === 1 && widget.altMaxUrl && widget.widgetConfig.links[0].href === widget.url) {
            return 'basic';
          } else {
            return 'list-of-links'
          }
        case 'generic':
          // DEPRECATED: Included for backwards compatibility. Use 'custom' instead.
          return 'custom';
        case null:
          // If widgetType doesn't exist, show a basic widget
          return 'basic';
        default:
          return widget.widgetType;
      }
    };

    /**
     * Initial widget setup -- gets data for a single widget from the provided fname attribute
     */
    var initializeWidget = function(fname) {
      // Initialize scope variables
      $scope.widget = {};
      $scope.widgetType = '';

      // Temporarily necessary for CSS to enforce max-width on widgets not constrained by a responsive grid (i.e. not in ng-portal)
      $scope.isSingleWidget = true;

      // Get widget data for provided app (fname)
      widgetService.getSingleWidgetData(fname)
        .then(function(data) {
          // Set scope variables
          if (data) {
            $scope.widget = data;
            $scope.widgetType = widgetType(data);
          }
        })
        .catch(function(error) {
          $log.warn('WidgetCardController couldn\'t get data for: ' + fname);
          $log.error(error);
        });
    };

    /**
     * Set the launch button url for static content, exclusive mode, and basic widgets
     * @returns {*}
     */
    $scope.renderUrl = function renderUrl() {
      var widget = $scope.widget;
      // Check for static content or exclusive mode portlets (rare)
      if (widget.altMaxUrl == false) {
        if (widget.staticContent != null) {
          return 'static/' + widget.fname;
        } else if (widget.renderOnWeb || $localStorage.webPortletRender) {
          return 'exclusive/' + widget.fname;
        }
      } else {
        // For basic widget, just use the provided url
        return widget.url;
      }
    };

    // Initialize the widget
    if ($scope.fname) {
      initializeWidget($scope.fname);
    } else {
      $log.warn('WidgetCardController didn\'t get an fname.');
    }

  }]);

  // OPTION LINK widget type
  app.controller('OptionLinkController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Set up default configuration if no config exists
     */
    var configInit = function() {
      $scope.config = {
        singleElement: false,
        arrayName: 'array',
        value: 'value',
        display: 'display'
      };
    };

    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      if ($scope.widget.widgetURL && $scope.widget.widgetType) {
        // Initialize widget data
        $scope.widget.widgetData = [];
        // Get data and set widget content
        widgetService.getWidgetJson($scope.widget).then(function(data) {
          if (data) {
            if ($scope.config.singleElement) {
              // Set the default selected url
              $scope.widget.selectedUrl = $scope.widget.widgetData[$scope.config.value];
            } else if ($scope.widget.widgetData[$scope.config.arrayName] && $scope.widget.widgetData[$scope.config.arrayName].length > 0) {
              // If multiple values come back, set selected url to the first entry
              $scope.widget.selectedUrl = $scope.widget.widgetData[$scope.config.arrayName][0][$scope.config.value];
            }
          } else {
            $log.warn('OptionLinkController couldn\'t get json for: ' + $scope.widget.fname);
          }
        });
      }
    };

    // Set default values if no config was received
    if (!$scope.config) {
      configInit();
      $log.warn('OptionLinkController didn\'t receive a widget config');
    }
    // Set up widget content
    populateWidgetContent();
  }]);

  // SEARCH WITH LINKS widget type
  app.controller('SearchWithLinksController', ['$scope', '$sce', function($scope, $sce) {
    // Have faith our entity files aren't trying to bamboozle us
    $scope.secureURL = $sce.trustAsResourceUrl($scope.config.actionURL);
  }]);

  // RSS widget type
  app.controller('RssWidgetController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Turn the provided date string into an actual Date so it can be filtered into something prettier.
     * @param dateString A hideously ugly date string
     * @returns {*} A Date object or null
     */
    $scope.getPrettyDate = function(dateString) {
      // Create a new date if a date string was provided, otherwise return null
      return dateString ? new Date(dateString) : null;
    };

    /**
     * Set defaults if any values are missing from provided widgetConfig
     */
    var checkForWidgetConfig = function() {
      if (!$scope.config) {
        $scope.config = {};
      }
      if (!$scope.config.lim) {
        $scope.config.lim = 5;
      }
      if (!$scope.config.titleLim) {
        $scope.config.titleLim = 40;
      }
      if (!$scope.config.showShowing) {
        $scope.config.showShowing = false;
      }
    };

    /**
     * Initialize rss widget
     */
    var initializeRssWidget = function() {
      // Trigger loading spinner
      $scope.loading = true;

      // Only initialize if widget's json provides all the stuff we need
      if ($scope.widget && $scope.widget.widgetURL) {

        // Make sure config has values
        checkForWidgetConfig();

        // Get rss feed from provided url
        widgetService.getRssAsJson($scope.widget.widgetURL)
          .then(function(result) {
            // If we got data, load it and turn off loading spinner
            $scope.data = result;
            $scope.loading = false;

            // Check for errors or emptiness and update display as needed
            if ($scope.status !== 'ok') {
              $scope.error = true;
              $scope.loading = false;
            } else {
              if (!$scope.items || $scope.items.length == 0) {
                $scope.isEmpty = true;
                $scope.loading = false;
                $scope.error = true;
              } else {
                // Show 'show more' if the feed is longer than the display limit
                if (!$scope.config.showShowing && $scope.items.length > $scope.config.lim) {
                  $scope.config.showShowing = true;
                }
              }
            }
          })
          .catch(function(error) {
            // If the service couldn't get data, display error messages
            $log.warn('Couldn\'t get rss as JSON');
            $log.error(error);

            $scope.error = true;
            $scope.isEmpty = true;
            $scope.loading = false;
          });
      }
    };

    // Initialize the widget
    initializeRssWidget();
  }]);

  // CUSTOM & GENERIC widget types
  app.controller('CustomWidgetController', ['$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      if ($scope.widget.widgetURL && $scope.widget.widgetType) {
        // Trigger loading spinner
        $scope.loading = true;

        // Fetch additional widget data
        widgetService.getWidgetJson($scope.widget).then(function(data) {
          $scope.loading = false;
          if (data) {
            $scope.widget.widgetData = data;
            $scope.content = $scope.widget.widgetData;
            if (angular.isArray($scope.content) && $scope.content.length == 0) {
              $scope.isEmpty = true;
            } else if ($scope.widget.widgetConfig && $scope.widget.widgetConfig.evalString && eval($scope.widget.widgetConfig.evalString)) {
              $scope.isEmpty = true;
            }
          } else {
            $log.warn('Got nothing back from widget fetch from: ' + $scope.widget.widgetURL);
            $scope.isEmpty = true;
          }
        }, function() {
          // After we get widget data, turn off loading spinner
          $scope.loading = false;
        });
      }
    };

    /**
     * Filter array for provided values of a given object -- used by some custom widgets (i.e. Leave Balances)
     *
     * @param {Array<Object>} array The array to filter
     * @param {Object} object The array entry to search through
     * @param {Array<String>} strings The string values to test against each entry
     * @returns {Array<Object>} An array containing only the desired
     */
    $scope.filteredArray = function(array, object, strings) {
      $log.log('Filtering array for strings: ' + strings);
      $log.log(array);
      if (array && object && strings) {
        var filteredArray = array.filter(function(entry) {
          for (var i = 0; i < strings.length; i++) {
            if (entry[object].indexOf(strings[i]) != -1) {
              return true;
            }
          }
        });
        $log.log('Filtered array:');
        $log.log(filteredArray);
        return filteredArray;
      } else {
        return [];
      }
    };

    /**
     * Initialize scope variables before getting widget content
     * @param template The provided custom HTML template
     */
    var initializeCustomWidget = function(template) {
      $scope.content = [];
      $scope.template = template;
      $scope.isEmpty = false;
      $scope.widget.widgetData = [];
      populateWidgetContent();
    };

    // Initialize widget if we received a custom HTML template, otherwise display empty widget and log a warning
    if ($scope.widget.widgetTemplate) {
      initializeCustomWidget($scope.widget.widgetTemplate);
    } else {
      $scope.loading = false;
      $scope.isEmpty = true;
      $log.warn($scope.widget.fname + ' said it\'s a custom/generic widget, but didn\'t provide a template.');
    }
  }]);

  // WEATHER widget type
  app.controller('WeatherWidgetController', ['$scope', '$log', '$q', 'widgetService', 'keyValueService', function($scope, $log, $q, widgetService, keyValueService) {
    // Local variables
    var fetchKey = 'userWeatherPreference';

    /**
     * Change temperature unit of measurement
     */
    $scope.cycleUnits = function() {
      var userPreference = $scope.nextUnits;
      var value = {};

      // Switch temperature based on which unit is next
      switch(userPreference) {
        case 'F':
          convertKelvinToFahrenheit();
          $scope.currentUnits = 'F';
          $scope.nextUnits = 'C';
          break;
        case 'C':
          convertFahrenheitToCelsius();
          $scope.currentUnits = 'C';
          $scope.nextUnits = 'K';
          break;
        case 'K':
          convertCelsiusToKelvin();
          $scope.currentUnits = 'K';
          $scope.nextUnits = 'F';
          break;
        default:
          convertCelsiusToKelvin();
          $scope.currentUnits = 'K';
          $scope.nextUnits = 'F';
      }

      // Set user preference
      value.userWeatherPreference = $scope.currentUnits;

      // Remember the user's preferred unit of measurement
      keyValueService.setValue(fetchKey, value);
    };

    /**
     * Fetch additional widget data
     */
    var populateWidgetContent = function() {
      // Declare asynchronous promises to be executed in $q.all()
      var widgetPromise = widgetService.getWidgetJson($scope.widget);
      var userPreferencesPromise = keyValueService.getValue(fetchKey);

      // Execute promises, then resolve when both have returned something
      $q.all( [widgetPromise, userPreferencesPromise] ).then(function(data) {

        // Turn off loading spinner
        $scope.loading = false;

        // If we got some data, populate widget content
        if (data) {
          // Set local variables
          var allTheWeathers = data[0];
          var myPref = data[1];
          var userPreference = myPref.userWeatherPreference;

          // Set scope variables
          if (allTheWeathers) {
            $scope.weatherData = allTheWeathers.weathers;
          }
          $scope.currentUnits = 'F';
          $scope.nextUnits = 'C';

          // If the user doesn't have a preference, default to Fahrenheit
          if (userPreference === null || userPreference === '' || typeof userPreference === 'undefined') {
            userPreference = 'F';
          }

          // Cycle units until we match with the user's preference
          while (userPreference != $scope.currentUnits) {
            $scope.cycleUnits();
          }
        } else {
          // Log any errors getting data
          $scope.error = true;
          $log.warn('WeatherWidgetController could\'t get widget json');
        }

        // Return to resolve $q.all()
        return data;
      }).catch(function() {
        $scope.loading = false;
        $scope.error = true;
        $log.warn('WeatherWidgetController wrote a check it simply couldn\'t cash');
      });
    };

    /**
     * Convert from Fahrenheit to Celsius
     */
    var convertFahrenheitToCelsius = function() {
      var ratio = (5 / 9);
      var offset = 32;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature -
          offset) * ratio;
        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature -
            offset) * ratio;
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature -
            offset) * ratio;
        }
      }
    };

    /**
     * Convert from Celsius to Fahrenheit
     */
    var convertCelsiusToFahrenheit = function() {
      var ratio = (9 / 5);
      var offset = 32;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature *
          ratio) + offset;

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature *
            ratio) + offset;
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature *
            ratio) + offset;
        }
      }
    };

    /**
     * Convert from Celsius to Kelvin
     */
    var convertCelsiusToKelvin = function() {
      var offset = 273;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature + offset);

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature + offset);
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature + offset);
        }
      }
    };

    /**
     * Convert from Kelvin to Fahrenheit (via Celsius)
     */
    var convertKelvinToFahrenheit = function() {
      convertKelvinToCelsius();
      convertCelsiusToFahrenheit();
    };

    /**
     * Convert from Kelvin to Celsius
     */
    var convertKelvinToCelsius = function() {
      var offset = 273;
      for (var i = 0; i < $scope.weatherData.length; i++) {
        $scope.weatherData[i].currentWeather.temperature =
          ($scope.weatherData[i].currentWeather.temperature - offset);

        for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
          $scope.weatherData[i].forecast[j].highTemperature =
            ($scope.weatherData[i].forecast[j].highTemperature - offset);
          $scope.weatherData[i].forecast[j].lowTemperature =
            ($scope.weatherData[i].forecast[j].lowTemperature - offset);
        }
      }
    };

    // Initialize weather widget
    $scope.loading = false;
    if ($scope.widget.widgetURL) {
      $scope.loading = true;
      $scope.weatherData = [];
      $scope.currentUnits = 'F';
      $scope.nextUnits = 'C';
      populateWidgetContent();
    } else {
      $log.warn('WeatherWidgetController did not receive a widgetURL');
    }

  }]);

});
<|endoftext|>