<commit_before>import Conditions from '../../../../../resources/conditions.js';
import NetRegexes from '../../../../../resources/netregexes.js';
import { Responses } from '../../../../../resources/responses.js';
import ZoneId from '../../../../../resources/zone_id.js';

export default {
  zoneId: ZoneId.EdensPromiseLitany,
  timelineFile: 'e10n.txt',
  timelineTriggers: [
    {
      id: 'E10N Umbra Smash',
      regex: /Umbra Smash/,
      beforeSeconds: 5,
      condition: Conditions.caresAboutPhysical(),
      response: Responses.tankBuster(),
    },
  ],
  triggers: [
    {
      id: 'E10N Deepshadow Nova',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56E5', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56E5', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56E5', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56E5', capture: false }),
      condition: Conditions.caresAboutAOE(),
      response: Responses.aoe(),
    },
    {
      id: 'E10N Forward Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B4', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B4', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B4', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B4', capture: false }),
      response: Responses.getBehind(),
    },
    {
      id: 'E10N Backward Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B7', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B7', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B7', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B7', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Go Front',
        },
      },
    },
    {
      id: 'E10N Forward Shadow Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B5', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B5', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B5', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B5', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Shadow Side',
          de: 'Schatten Seite',
          fr: 'Ombre à côté',
          ja: '影同じ側へ',
          cn: '影子同侧',
          ko: '그림자 쪽으로',
        },
      },
    },
    {
      id: 'E10N Backward Shadow Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B8', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B8', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B8', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B8', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Opposite Shadow',
          de: 'Gegenüber des Schattens',
          fr: 'Ombre opposée',
          ja: '影の反対側へ',
          cn: '影子异侧',
          ko: '그림자 반대쪽으로',
        },
      },
    },
    {
      id: 'E10N Left Giga Slash',
      netRegex: NetRegexes.startsUsing({ id: '56B1', source: 'Shadowkeeper', capture: false }),
      response: Responses.goRight(),
    },
    {
      id: 'E10N Right Giga Slash',
      netRegex: NetRegexes.startsUsing({ id: '56AE', source: 'Shadowkeeper', capture: false }),
      response: Responses.goLeft(),
    },
    {
      id: 'E10N Left Right Shadow Slash',
      netRegex: NetRegexes.startsUsing({ id: ['56AF', '56B2'], source: 'Shadowkeeper' }),
      alertText: (data, matches, output) => matches.id === '56AF' ? output.left() : output.right(),
      outputStrings: {
        left: {
          en: 'Go Left of Shadows',
          de: 'Geh links vom Schatten',
          fr: 'Allez à gauche des ombres',
          ja: '影の左へ',
          cn: '影子左侧',
          ko: '그림자 왼쪽',
        },
        right: {
          en: 'Go Right of Shadows',
          de: 'Geh rechts vom Schatten',
          fr: 'Allez à droite des ombres',
          ja: '影の右へ',
          cn: '影子右侧',
          ko: '그림자 오른쪽',
        },
      },
    },
    {
      id: 'E10N Shadow\'s Edge',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '5B0B' }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '5B0B' }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '5B0B' }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '5B0B' }),
      response: Responses.tankCleave(),
    },
    {
      id: 'E10N Voidgate',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56DD', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56DD', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56DD', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56DD', capture: false }),
      delaySeconds: 10, // It's 17 seconds from the time Voidgate starts casting until towers.
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Get Puddles',
          de: 'Flächen nehmen',
          fr: 'Allez dans les zones au sol',
          ja: '踏む',
          cn: '踩圈',
          ko: '바닥 징 밟기',
        },
      },
    },
    {
      id: 'E10N Shadow Warrior',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56E2', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56E2', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56E2', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56E2', capture: false }),
      infoText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Watch Tethered Dog',
          de: 'Achte auf den verbundenen Hund',
          fr: 'Regardez le chien lié',
          ja: '線で繋がった分身を注視',
          cn: '找连线的狗',
          ko: '연결된 쫄 지켜보기',
        },
      },
    },
    {
      id: 'E10N Cloak of Shadows ',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '5B11', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '5B11', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '5B11', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '5B11', capture: false }),
      suppressSeconds: 5,
      infoText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          // TODO: this could be better if we knew where the shadow was
          en: 'Away From Black Lines',
        },
      },
    },
    {
      // There is technically an AoE marker, but by the time it shows,
      // it's too late to get out if the player is inside the boss's hitbox.
      id: 'E10N Throne Of Shadow',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56C7', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56C7', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56C7', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56C7', capture: false }),
      response: Responses.getOut('alert'),
    },
    {
      // There is technically a visual, but it comes up at precisely the same time as puddles.
      // Best to make sure the user is reminded.
      id: 'E10N Distant Scream',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56C6', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56C6', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56C6', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56C6', capture: false }),
      response: Responses.knockback('alert'),
    },
  ],
};
<commit_msg>translation for e10n (#2246)<commit_after>import Conditions from '../../../../../resources/conditions.js';
import NetRegexes from '../../../../../resources/netregexes.js';
import { Responses } from '../../../../../resources/responses.js';
import ZoneId from '../../../../../resources/zone_id.js';

export default {
  zoneId: ZoneId.EdensPromiseLitany,
  timelineFile: 'e10n.txt',
  timelineTriggers: [
    {
      id: 'E10N Umbra Smash',
      regex: /Umbra Smash/,
      beforeSeconds: 5,
      condition: Conditions.caresAboutPhysical(),
      response: Responses.tankBuster(),
    },
  ],
  triggers: [
    {
      id: 'E10N Deepshadow Nova',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56E5', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56E5', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56E5', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56E5', capture: false }),
      condition: Conditions.caresAboutAOE(),
      response: Responses.aoe(),
    },
    {
      id: 'E10N Forward Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B4', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B4', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B4', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B4', capture: false }),
      response: Responses.getBehind(),
    },
    {
      id: 'E10N Backward Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B7', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B7', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B7', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B7', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Go Front',
          de: 'Geh nach vorn',
        },
      },
    },
    {
      id: 'E10N Forward Shadow Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B5', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B5', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B5', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B5', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Shadow Side',
          de: 'Schatten Seite',
          fr: 'Ombre à côté',
          ja: '影同じ側へ',
          cn: '影子同侧',
          ko: '그림자 쪽으로',
        },
      },
    },
    {
      id: 'E10N Backward Shadow Implosion',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56B8', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56B8', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56B8', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56B8', capture: false }),
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Opposite Shadow',
          de: 'Gegenüber des Schattens',
          fr: 'Ombre opposée',
          ja: '影の反対側へ',
          cn: '影子异侧',
          ko: '그림자 반대쪽으로',
        },
      },
    },
    {
      id: 'E10N Left Giga Slash',
      netRegex: NetRegexes.startsUsing({ id: '56B1', source: 'Shadowkeeper', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ id: '56B1', source: 'Schattenkönig', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ id: '56B1', source: 'Roi De L\'Ombre', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ id: '56B1', source: '影の王', capture: false }),
      response: Responses.goRight(),
    },
    {
      id: 'E10N Right Giga Slash',
      netRegex: NetRegexes.startsUsing({ id: '56AE', source: 'Shadowkeeper', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ id: '56AE', source: 'Schattenkönig', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ id: '56AE', source: 'Roi De L\'Ombre', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ id: '56AE', source: '影の王', capture: false }),
      response: Responses.goLeft(),
    },
    {
      id: 'E10N Left Right Shadow Slash',
      netRegex: NetRegexes.startsUsing({ id: ['56AF', '56B2'], source: 'Shadowkeeper' }),
      netRegexDe: NetRegexes.startsUsing({ id: ['56AF', '56B2'], source: 'Schattenkönig' }),
      netRegexFr: NetRegexes.startsUsing({ id: ['56AF', '56B2'], source: 'Roi De L\'Ombre' }),
      netRegexJa: NetRegexes.startsUsing({ id: ['56AF', '56B2'], source: '影の王' }),
      alertText: (data, matches, output) => matches.id === '56AF' ? output.left() : output.right(),
      outputStrings: {
        left: {
          en: 'Go Left of Shadows',
          de: 'Geh links vom Schatten',
          fr: 'Allez à gauche des ombres',
          ja: '影の左へ',
          cn: '影子左侧',
          ko: '그림자 왼쪽',
        },
        right: {
          en: 'Go Right of Shadows',
          de: 'Geh rechts vom Schatten',
          fr: 'Allez à droite des ombres',
          ja: '影の右へ',
          cn: '影子右侧',
          ko: '그림자 오른쪽',
        },
      },
    },
    {
      id: 'E10N Shadow\'s Edge',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '5B0B' }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '5B0B' }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '5B0B' }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '5B0B' }),
      response: Responses.tankCleave(),
    },
    {
      id: 'E10N Voidgate',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56DD', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56DD', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56DD', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56DD', capture: false }),
      delaySeconds: 10, // It's 17 seconds from the time Voidgate starts casting until towers.
      alertText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Get Puddles',
          de: 'Flächen nehmen',
          fr: 'Allez dans les zones au sol',
          ja: '踏む',
          cn: '踩圈',
          ko: '바닥 징 밟기',
        },
      },
    },
    {
      id: 'E10N Shadow Warrior',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56E2', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56E2', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56E2', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56E2', capture: false }),
      infoText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          en: 'Watch Tethered Dog',
          de: 'Achte auf den verbundenen Hund',
          fr: 'Regardez le chien lié',
          ja: '線で繋がった分身を注視',
          cn: '找连线的狗',
          ko: '연결된 쫄 지켜보기',
        },
      },
    },
    {
      id: 'E10N Cloak of Shadows ',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '5B11', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '5B11', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '5B11', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '5B11', capture: false }),
      suppressSeconds: 5,
      infoText: (data, _, output) => output.text(),
      outputStrings: {
        text: {
          // TODO: this could be better if we knew where the shadow was
          en: 'Away From Black Lines',
          de: 'Weg von den schwarzen Linien',
        },
      },
    },
    {
      // There is technically an AoE marker, but by the time it shows,
      // it's too late to get out if the player is inside the boss's hitbox.
      id: 'E10N Throne Of Shadow',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56C7', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56C7', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56C7', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56C7', capture: false }),
      response: Responses.getOut('alert'),
    },
    {
      // There is technically a visual, but it comes up at precisely the same time as puddles.
      // Best to make sure the user is reminded.
      id: 'E10N Distant Scream',
      netRegex: NetRegexes.startsUsing({ source: 'Shadowkeeper', id: '56C6', capture: false }),
      netRegexDe: NetRegexes.startsUsing({ source: 'Schattenkönig', id: '56C6', capture: false }),
      netRegexFr: NetRegexes.startsUsing({ source: 'Roi De L\'Ombre', id: '56C6', capture: false }),
      netRegexJa: NetRegexes.startsUsing({ source: '影の王', id: '56C6', capture: false }),
      response: Responses.knockback('alert'),
    },
  ],
  timelineReplace: [
    {
      'locale': 'de',
      'replaceSync': {
        'Shadowkeeper': 'Schattenkönig',
      },
      'replaceText': {
        'Backward Implosion': 'Hintere Implosion',
        'Backward Shadow Implosion': 'Hintere Schattenimplosion',
        'Barbs Of Agony': 'Stacheln der Todesqualen',
        'Cloak Of Shadows': 'Mantel des Schattens',
        'Deepshadow Nova': 'Dunkelschatten-Nova',
        'Distant Scream': 'Ferner Schrei',
        'Fade To Shadow': 'Schattenimmersion',
        'Forward Implosion': 'Vordere Implosion',
        'Forward Shadow Implosion': 'Vordere Schattenimplosion',
        'Front/Back Shadow Implosion': 'Vordere/Hintere Schattenimplosion',
        'Left': 'Linker',
        'Right Giga Slash': 'Rechter Giga-Schlag',
        'Right Shadow Slash': 'Rechter Schattenschlag',
        'Shadow Warrior': 'Schattenkrieger',
        'Shadow\'s Edge': 'Schattenhieb',
        'Shadowy Eruption': 'Schatteneruption',
        'Spawn Shadow': 'Schattenerscheinung',
        'Throne Of Shadow': 'Schattenthron',
        'Umbra Smash': 'Schattenschlag',
        'Void Pulse': 'Nichtspulsieren',
        'Voidgate': 'Nichtsportal',
      },
    },
    {
      'locale': 'fr',
      'missingTranslations': true,
      'replaceSync': {
        'Shadowkeeper': 'Ordre royal',
      },
      'replaceText': {
        'Backward Implosion': 'Implosion dorsale',
        'Backward Shadow Implosion': 'Implosion ombrale dorsale',
        'Barbs Of Agony': 'Entrailles de l\'agonie',
        'Cloak Of Shadows': 'Cape de l\'Ombre',
        'Deepshadow Nova': 'Nova de la pleine-ombre',
        'Distant Scream': 'Hurlement de l\'Ombre',
        'Fade To Shadow': 'Immersion abyssale',
        'Forward Implosion': 'Implosion frontale',
        'Forward Shadow Implosion': 'Implosion ombrale frontale',
        'Right Giga Slash': 'Giga taillade droite',
        'Right Shadow Slash': 'Giga taillade ombrale droite',
        'Shadow Warrior': 'Ombre du roi',
        'Shadow\'s Edge': 'Taillade ombrale',
        'Shadowy Eruption': 'Éruption ombrale',
        'Spawn Shadow': 'Ombres croissantes',
        'Throne Of Shadow': 'Trône de l\'Ombre',
        'Umbra Smash': 'Fracas ombral',
        'Void Pulse': 'Pulsation du néant',
        'Voidgate': 'Porte du néant',
      },
    },
    {
      'locale': 'ja',
      'replaceSync': {
        'Shadowkeeper': '影の王命',
      },
      'replaceText': {
        '(?<!/)Backward Implosion': 'バックワード・インプロージョン',
        'Backward Shadow Implosion': 'バックワード・シャドウインプロージョン',
        'Barbs Of Agony': 'バーブス・オブ・アゴニー',
        'Cloak Of Shadows': 'クローク・オブ・シャドウ',
        'Deepshadow Nova': 'ディープシャドウノヴァ',
        'Distant Scream': '影の遠吠え',
        'Fade To Shadow': '影潜り',
        'Forward Implosion': 'フォワード・インプロージョン',
        'Forward/Backward Implosion': 'フォワード／バックワード・インプロージョン',
        'Forward Shadow Implosion': 'フォワード・シャドウインプロージョン',
        'Front/Back Shadow Implosion': 'フォワード／バックワード・シャドウインプロージョン',
        'Left/Right Giga Slash': 'レフトサイド／ライトサイド・ギガスラッシュ',
        'Left/Right Shadow Slash': 'レフトサイド／ライトサイド・シャドウギガスラッシュ',
        'Shadow Warrior': '影武者',
        'Shadow\'s Edge': 'シャドウスラッシュ',
        'Shadowy Eruption': 'シャドウエラプション',
        'Spawn Shadow': 'スポーンシャドウ',
        'Throne Of Shadow': '影の王権',
        'Umbra Smash': 'アンブラスマッシュ',
        'Void Pulse': 'ヴォイドパルセーション',
        'Voidgate': 'ヴォイドゲート',
      },
    },
  ],
};
<|endoftext|>


<commit_before>const baseunit = {
  xs: 3,
  s: 6,
  m: 12,
  l: 18,
  xl: 24
};

module.exports = baseunit;
<commit_msg>adding in explenation for file<commit_after>// WIP - possibly create a function like spacerXL, spacerS, spacerM for things lik
//        padding, margins, widths

const baseunit = {
  xs: 3,
  s: 6,
  m: 12,
  l: 18,
  xl: 24
};

module.exports = baseunit;
<|endoftext|>


<commit_before>var m = require('mithril');
var partial = require('chessground').util.partial;
var util = require('./util');
var status = require('game').status;

function result(win, stat) {
  switch (win) {
    case true:
      return '1';
    case false:
      return '0';
    default:
      return stat >= status.ids.mate ? '½' : '*';
  }
}

module.exports = function(ctrl) {
  var data = ctrl.vm.playerInfo.data;
  if (!data || data.player.id !== ctrl.vm.playerInfo.id) return m('span.square-spin');
  var nb = data.player.nb;
  var pairingsLen = data.pairings.length
  var avgOp = pairingsLen ? Math.round(data.pairings.reduce(function(a, b) {
    return a + b.op.rating;
  }, 0) / pairingsLen) : null;
  return m('div.player', {
    config: function(el, isUpdate) {
      if (!isUpdate) $('body').trigger('lichess.content_loaded');
    }
  }, [
    m('close[data-icon=L]', {
      onclick: partial(ctrl.showPlayerInfo, data.player.id)
    }),
    m('h2', [
      data.player.withdraw ? m('span.text[data-icon=b]') : m('span.rank', data.player.rank + '. '),
      util.player(data.player)
    ]),
    m('div.stats', m('table', [
      m('tr', [m('th', 'Games played'), m('td', nb.game)]),
      nb.game ? [
        m('tr', [m('th', 'Win rate'), m('td', util.ratio2percent(nb.win / nb.game))]),
        m('tr', [m('th', 'Berserk rate'), m('td', util.ratio2percent(nb.berserk / nb.game))]),
        m('tr', [m('th', 'Average opponent'), m('td', avgOp)]),
        data.player.performance ? m('tr', [m('th', 'Performance'), m('td', data.player.performance)]) : null
      ] : null
    ])),
    m('div.scroll-shadow-soft', m('table.pairings', {
      onclick: function(e) {
        var href = e.target.parentNode.getAttribute('data-href');
        if (href) window.open(href, '_blank');
      }
    }, data.pairings.map(function(p, i) {
      var res = result(p.win, p.status);
      return m('tr', {
        key: p.id,
        'data-href': '/' + p.id + '/' + p.color,
        class: 'glpt' + (res === '1' ? ' win' : (res === '0' ? ' loss' : ''))
      }, [
        m('th', Math.max(nb.game, pairingsLen) - i),
        m('td', (p.op.title ? p.op.title + ' ' : '') + p.op.name),
        m('td', p.op.rating),
        m('td', {
          'class': 'is color-icon ' + p.color
        }),
        m('td', res)
      ]);
    })))
  ]);
};
<commit_msg>Revert "Revert "temporary disable tournament performance rating""<commit_after>var m = require('mithril');
var partial = require('chessground').util.partial;
var util = require('./util');
var status = require('game').status;

function result(win, stat) {
  switch (win) {
    case true:
      return '1';
    case false:
      return '0';
    default:
      return stat >= status.ids.mate ? '½' : '*';
  }
}

module.exports = function(ctrl) {
  var data = ctrl.vm.playerInfo.data;
  if (!data || data.player.id !== ctrl.vm.playerInfo.id) return m('span.square-spin');
  var nb = data.player.nb;
  var pairingsLen = data.pairings.length
  var avgOp = pairingsLen ? Math.round(data.pairings.reduce(function(a, b) {
    return a + b.op.rating;
  }, 0) / pairingsLen) : null;
  return m('div.player', {
    config: function(el, isUpdate) {
      if (!isUpdate) $('body').trigger('lichess.content_loaded');
    }
  }, [
    m('close[data-icon=L]', {
      onclick: partial(ctrl.showPlayerInfo, data.player.id)
    }),
    m('h2', [
      data.player.withdraw ? m('span.text[data-icon=b]') : m('span.rank', data.player.rank + '. '),
      util.player(data.player)
    ]),
    m('div.stats', m('table', [
      m('tr', [m('th', 'Games played'), m('td', nb.game)]),
      nb.game ? [
        m('tr', [m('th', 'Win rate'), m('td', util.ratio2percent(nb.win / nb.game))]),
        m('tr', [m('th', 'Berserk rate'), m('td', util.ratio2percent(nb.berserk / nb.game))]),
        m('tr', [m('th', 'Average opponent'), m('td', avgOp)]),
        // data.player.performance ? m('tr', [m('th', 'Performance'), m('td', data.player.performance)]) : null
      ] : null
    ])),
    m('div.scroll-shadow-soft', m('table.pairings', {
      onclick: function(e) {
        var href = e.target.parentNode.getAttribute('data-href');
        if (href) window.open(href, '_blank');
      }
    }, data.pairings.map(function(p, i) {
      var res = result(p.win, p.status);
      return m('tr', {
        key: p.id,
        'data-href': '/' + p.id + '/' + p.color,
        class: 'glpt' + (res === '1' ? ' win' : (res === '0' ? ' loss' : ''))
      }, [
        m('th', Math.max(nb.game, pairingsLen) - i),
        m('td', (p.op.title ? p.op.title + ' ' : '') + p.op.name),
        m('td', p.op.rating),
        m('td', {
          'class': 'is color-icon ' + p.color
        }),
        m('td', res)
      ]);
    })))
  ]);
};
<|endoftext|>


<commit_before>import Factory from '../factory/Factory';

/**
 * Injector. Internal class
 *
 * Injects functions into objects. Used internally.
 * Not intended to be used on it's own.
 *
 * @static
 */
class Injector {
  /**
   * Appends a function after complete and remembers the original
   *
   * @param {Task} task task to be modified
   * @param {Function} callback function to be called after complete
   * @param {String} key map for the original callback
   * @return {Task} task modified task
   */
  static afterComplete(task, callback, key) {
    Injector.resetAfterComplete(task, key);

    const current = task._complete;

    Object.defineProperty(task, '__injected__' + key, {
      configurable: true,
      enumerable: true,
      value: current
    });

    task._complete = (self, ...args) => {
      let error;
      try {
        if (current !== undefined) {
          current(self, ...args);
        }
      } catch (e) {
        error = e;
      }
      callback(error, ...args);
    };
    return task;
  }

  /**
   * Resets the complete method to it's original state
   *
   * @param {Task} task task to be modified
   * @param {String} key map for the original callback
   * @return {Task} task modified task
   */
  static resetAfterComplete(task, key) {
    const original = task['__injected__' + key];
    if (original !== undefined) {
      delete task['__injected__' + key];

      task._complete = original;
    }
  }

  /**
   * Adds a method called chain to get Factory._CHAIN_
   *
   * Shortcut for task.get(Factory._CHAIN_)
   *
   * @param {Task} task task to be modified
   */
  static addChainGetter(task) {
    Object.defineProperty(task, 'chain', {
      get: function () {
        return this.get(Factory._CHAIN_);
      },
      configurable: true,
      enumerable: true
    });
  }

  /**
   * Remove a method called chain to get Factory._CHAIN_
   *
   * @param {Task} task task to be modified
   */
  static removeChainGetter(task) {
    delete task.chain;
  }
}

export default Injector;
<commit_msg>pass self to the callback in onComplete and return task in all methods<commit_after>import Factory from '../factory/Factory';

/**
 * Injector. Internal class
 *
 * Injects functions into objects. Used internally.
 * Not intended to be used on it's own.
 *
 * @static
 */
class Injector {
  /**
   * Appends a function after complete and remembers the original
   *
   * @param {Task} task task to be modified
   * @param {Function} callback function to be called after complete
   * @param {String} key map for the original callback
   * @return {Task} task modified task
   */
  static afterComplete(task, callback, key) {
    Injector.resetAfterComplete(task, key);

    const current = task._complete;

    Object.defineProperty(task, '__injected__' + key, {
      configurable: true,
      enumerable: true,
      value: current
    });

    task._complete = (self, ...args) => {
      let error;
      try {
        if (current !== undefined) {
          current(self, ...args);
        }
      } catch (e) {
        error = e;
      }
      callback(error, self, ...args);
    };

    return task;
  }

  /**
   * Resets the complete method to it's original state
   *
   * @param {Task} task task to be modified
   * @param {String} key map for the original callback
   * @return {Task} task modified task
   */
  static resetAfterComplete(task, key) {
    const original = task['__injected__' + key];
    if (original !== undefined) {
      delete task['__injected__' + key];

      task._complete = original;
    }

    return task;
  }

  /**
   * Adds a method called chain to get Factory._CHAIN_
   *
   * Shortcut for task.get(Factory._CHAIN_)
   *
   * @param {Task} task task to be modified
   * @return {Task} task modified task
   */
  static addChainGetter(task) {
    Object.defineProperty(task, 'chain', {
      get: function () {
        return this.get(Factory._CHAIN_);
      },
      configurable: true,
      enumerable: true
    });

    return task;
  }

  /**
   * Remove a method called chain to get Factory._CHAIN_
   *
   * @param {Task} task task to be modified
   * @return {Task} task modified task
   */
  static removeChainGetter(task) {
    delete task.chain;

    return task;
  }
}

export default Injector;
<|endoftext|>


<commit_before>// Put `items.json` in your `APIGymnastics` directory...
var items = require('items.json');
    test = require('mocha').it,
    expect = require('chai').expect;
// TODO: Setup mocha and chai...

test('this is the easy one', function(){
  expect(Array.isArray(items)).to.be.true; // What.
  // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray

  expect(items.length).to.equal(25); // It's haunting me now...
});
function averageprice(){
  var items["price"]
}

test('finding the average price', function(){
  var yourAnswer = "start with `items`; use `Array` methods";

  expect(yourAnswer).to.be.closeTo(23.63, 0.01);
});

test('finding that perfect $15 item', function(){
  /**
   * @param {Array} items to search through
   * @return {Array} of `items` with `price` between `min` and `max` USD
   */
  function pricedBetween(items, min, max){
    var min = 14;
    var max = 18;
    if (itmes >= min && itmes <= max) {
      return items;

  expect( pricedBetween(items, 14, 18) ).to.deep.equal([
    // Can you find their names _without_ code first?
  ]);
});

test('pond hopping', function(){
  expect( /* your answer */ ).to.deep.equal({
    name: "1970s Schlitz Malt Liquor Glass Beer Pitcher",
    price: 18.00
  });
});

test('just wood or _reclaimed_ wood?', function(){
  var actual = wooden(items); // Who defined `wooden`...? Oh.

  expect( actual ).to.deep.equal( /* an Array of String names... */ );
});

test('this is just SEO bait, I know it...', function(){
  var actual = octomatter(items); // Just one way to do it... Plus, "octomatter"!

  expect(actual).to.deep.equal(/* Look, I can't do _everything_ for you! */);
});

test('true craftsmen... women... people. Birds?', function(){
  expect(makerSellers(items)).to.equal(18);
});
<commit_msg>updated Etsy.js<commit_after>// Put `items.json` in your `APIGymnastics` directory...
var items = require('items.json');
    test = require('mocha').it,
    expect = require('chai').expect;
// TODO: Setup mocha and chai...

test('this is the easy one', function(){
  expect(Array.isArray(items)).to.be.true; // What.
  // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray

  expect(items.length).to.equal(25); // It's haunting me now...
});

function items(){
  return items.length
}

test('finding the average price', function(){
  var yourAnswer = "start with `items`; use `Array` methods";

  expect(yourAnswer).to.be.closeTo(23.63, 0.01);
});

function avererageprice(){
  var i = items
  for (i = 0; i =  ){
    return items
  }                       //I need help

}

test('finding that perfect $15 item', function(){
  /**
   * @param {Array} items to search through
   * @return {Array} of `items` with `price` between `min` and `max` USD
   */

  function pricedBetween(){
    var items = new array()   //because I cant figure out how to get the average price, I cant figure out
    var min = 14;             // how to test my code for this test
    var max = 18;
    var i = items
    for (i = 0; i >= min && i <= max; i++) {
      return items;
    }
  }

  expect( pricedBetween(items, 14, 18) ).to.deep.equal([
    // Can you find their names _without_ code first?
  ]);
});

test('pond hopping', function(){
  expect( /* your answer */ ).to.deep.equal({
    name: "1970s Schlitz Malt Liquor Glass Beer Pitcher",
    price: 18.00
  });
});

test('just wood or _reclaimed_ wood?', function(){
  var actual = wooden(items); // Who defined `wooden`...? Oh.

  expect( actual ).to.deep.equal( /* an Array of String names... */ );
});

test('this is just SEO bait, I know it...', function(){
  var actual = octomatter(items); // Just one way to do it... Plus, "octomatter"!

  expect(actual).to.deep.equal(/* Look, I can't do _everything_ for you! */);
});

test('true craftsmen... women... people. Birds?', function(){
  expect(makerSellers(items)).to.equal(18);
});
<|endoftext|>


<commit_before>/**
 * MIT License
 *
 * Copyright (c) 2016 Rick Lancee @ricklancee
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');
    this.pages = null;
    this.canvasEl = null;
    this.canvas = null;

    if (!this.data) {
      return;
    }

    // options
    this._fitscreen = (this.hasAttribute('fitscreen') && this.getAttribute('fitscreen') !== 'false')
      || false;
    this._pagination = true;
    this._opacity = 0.05;

    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;
    this.pageOffsetY = 0;

    // Init
    this._createCanvas();

    // TODO: Show spinner?

    this._addEventListeners();

    // When data is loaded save it
    this._loadData().then(data => {
      this.pages = data;

      this._createPagination();

      const hashPagination = this._getPaginationFromHash();
      if (hashPagination) {
        this.currentPageIndex = hashPagination[0];
        this.currentPanelIndex = hashPagination[1];
      }

      this._setPage(this.currentPageIndex)
        .then(_ => {
          this._recalcPage();
          this._drawPanels(this.currentPanelIndex);
          this._setPaginationHash();
          this._setActivePagination();
          this._positionView();
        });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    this.querySelectorAll('.pagination a').forEach(link => link.classList.remove('active'));
    this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]')
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _recalcPage() {
    if (this._fitscreen) {
      this.fitscreen();
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen();
    } else {
      this.fitscreenOff();
    }

    const BCR = this.canvasEl.getBoundingClientRect();

    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen() {
    this.canvasEl.style.height = this.screenHeight + 'px';
    this.canvasEl.style.width = 'auto';
  }

  fitscreenOff() {
    this.canvasEl.style.height = '';
    this.canvasEl.style.width = '';
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _loadImage(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    if (this.currentPanelIndex > this.pages.length - 1) {
      this.currentPageIndex = this.pages.length - 1;
      console.log('last page');
      return;
    }

    this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  previousPage() {
    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    if (this.currentPanelIndex < 0) {
      this.currentPageIndex = 0;
      console.log('First Page');
      return;
    }

    this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const currentPanel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];

    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (currentPanel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (currentPanel.x * this.pageDimensions.width / 100) + offsetX;

    window.scrollTo(panelX, panelY);
  }
}
<commit_msg>reduced opcaity<commit_after>/**
 * MIT License
 *
 * Copyright (c) 2016 Rick Lancee @ricklancee
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');
    this.pages = null;
    this.canvasEl = null;
    this.canvas = null;

    if (!this.data) {
      return;
    }

    // options
    this._fitscreen = (this.hasAttribute('fitscreen') && this.getAttribute('fitscreen') !== 'false')
      || false;
    this._pagination = true;
    this._opacity = 0.025;

    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;
    this.pageOffsetY = 0;

    // Init
    this._createCanvas();

    // TODO: Show spinner?

    this._addEventListeners();

    // When data is loaded save it
    this._loadData().then(data => {
      this.pages = data;

      this._createPagination();

      const hashPagination = this._getPaginationFromHash();
      if (hashPagination) {
        this.currentPageIndex = hashPagination[0];
        this.currentPanelIndex = hashPagination[1];
      }

      this._setPage(this.currentPageIndex)
        .then(_ => {
          this._recalcPage();
          this._drawPanels(this.currentPanelIndex);
          this._setPaginationHash();
          this._setActivePagination();
          this._positionView();
        });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    this.querySelectorAll('.pagination a').forEach(link => link.classList.remove('active'));
    this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]')
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _recalcPage() {
    if (this._fitscreen) {
      this.fitscreen();
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen();
    } else {
      this.fitscreenOff();
    }

    const BCR = this.canvasEl.getBoundingClientRect();

    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen() {
    this.canvasEl.style.height = this.screenHeight + 'px';
    this.canvasEl.style.width = 'auto';
  }

  fitscreenOff() {
    this.canvasEl.style.height = '';
    this.canvasEl.style.width = '';
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _loadImage(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    if (this.currentPanelIndex > this.pages.length - 1) {
      this.currentPageIndex = this.pages.length - 1;
      console.log('last page');
      return;
    }

    this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  previousPage() {
    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    if (this.currentPanelIndex < 0) {
      this.currentPageIndex = 0;
      console.log('First Page');
      return;
    }

    this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const currentPanel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];
    if (!currentPanel) {
      return;
    }
    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (currentPanel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (currentPanel.x * this.pageDimensions.width / 100) + offsetX;

    window.scrollTo(panelX, panelY);
  }
}
<|endoftext|>


<commit_before>var _ = require('koa-route')
var koa = require('koa')
var serve = require('koa-static')
var render = require('koa-ejs')
var path = require('path')
var fs = require('fs');
var assert = require('assert');
var request = require('koa-request');

var socket = require('koa-socket');

var session = require('koa-session')
var parse = require('co-body')
var csrf = require('koa-csrf')

var app = koa();

require('koa-csrf')(app)

var email = require('./app_modules/email')

var auth = require('./app_modules/auth')
var db = require('./app_modules/db')
var user = require('./app_modules/user');
var utils = require('./app_modules/utils')
const ENV = process.env;
const SERVICE = auth.service(ENV.HLRDESK_HOST, ENV.PORT, '/signin', !ENV.HLRDESK_DEV);

if(ENV.HLRDESK_DEV) {
  app.use(require('./app_modules/koa-sassy').Sassy);
}

process.on("uncaughtException", function(err) {
  email.serverCrash(err.message, err.stack);
  throw err;
});

app.keys = ['TODO MAKE ME AN ENV VARIABLE', 'I SHOULD NOT BE HARDCODED', 'MY DOG HAS NO NOSE', 'HOW DOES HE SMELL?', 'AWFUL'];
app.use(session());

app.use(serve(path.join(__dirname, '..', 'public')));

if(ENV.NODE_TEST === 'true') {
  // e.g., /logmein?as=prabbit
  // see tests/sessions/* for available users
  require('./app_modules/mock-login')(app);
  var util = require('util');
  var log_file = fs.createWriteStream(path.join(__dirname, '/../', 'debug.log'), {flags : 'a'});
  log_file.write(new Date() + '\n');
  console.error = function(d) { //
    log_file.write(util.format(d) + '\n\n');
  };
}

app.use(function*(next){
  const WHITELIST = ['/signin', '/logmein', '/logout', '/rss'];
  //const GREYLIST = WHITELIST.concat(['/calendar']);
  const GREYLIST = WHITELIST;
  if (!this.session.user && WHITELIST.indexOf(this.request.path) === -1){
    this.session.login_redirect = this.request.path + this.request.search;
    this.redirect('https://cas.byu.edu/cas/login?service=' + SERVICE);
    return;
  }
  var is_admin = yield auth.isAdmin(this.session.user)
  if ( this.session.user && !is_admin && GREYLIST.indexOf(this.request.path) === -1){
    this.redirect('https://hlr.byu.edu/schedule/');
    return;
  }
  else{
    yield next;
  }
})

render(app, {
  root: path.join(__dirname, 'templates'),
  layout: 'layout',
  viewExt: 'html',
  cache: !ENV.HLRDESK_DEV,
  debug: ENV.HLRDESK_DEV,
  locals: {
    title: 'HLRDesk',
    token: function() {
      return this.session.token;
    }
  }
});

app.use(function *(next) {
  if(this.request.header['x-requested-with'] === 'XMLHttpRequest')
  {
    this.USE_LAYOUT = false;
  }
  else
  {
    if (this.session){
      var is_admin = yield auth.isAdmin(this.session.user)
      if (is_admin){
        this.USE_LAYOUT = 'layout';
        //TODO: May add read message check here. Likely the best spot
      }
      else{
        this.USE_LAYOUT = 'simple_layout';
      }
    }
    else{
      this.USE_LAYOUT = 'layout';
    }
  }
  captionClass = "undefined";
  yield next;
});

app.use(_.get("/", function *(next) {
  var rdir = this.session.login_redirect;
  if(rdir) {
    this.redirect(rdir);
    delete this.session.login_redirect;
    yield next;
    return;
  }
  yield this.render('layout', {layout: false, body:"", captionClass: "aboveLoader"});
  yield next;
}));

app.use(_.get("/message", function *() {
  var client = db();
  var all_messages = yield client.query("SELECT * FROM messages;");
  var all_tasks = yield client.query("Select * FROM tasks ORDER BY priority ASC");
  yield this.render('msg', {layout: this.USE_LAYOUT, all_messages: all_messages, all_tasks: all_tasks});
}));

app.use(_.get('/check-in', function *() {
  var inv = require('./app_modules/inventory');
  var items = yield inv.checked_out;

  var users = items.reduce(function(prev, item) {
    if(prev.indexOf(item.owner) === -1) {
      prev.push(item.owner);
    }
    return prev;
  }, []);

  var userDetails = yield user.contactInfo.apply(this, users);

  yield this.render('check-in', {
    items: items,
    user: userDetails,
    admin: this.session.user,
    moment: require('moment'),
    title: "Check In",
    layout: this.USE_LAYOUT
  });
}));

app.use(_.get('/check-out', function *() {
  yield this.render('check-out', {
    title: "Check Out",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
  });
}));

app.use(_.get('/edit-catalog', function *() {
  var client = db();
  var media_types = yield client.query("SELECT * FROM media ORDER BY media ASC;");
  var lang = yield client.query("SELECT * FROM languages ORDER BY name ASC;");
  var locations = yield client.query("SELECT * FROM locations ORDER BY name ASC;");
  yield this.render('edit-catalog', {
    title: "Edit Item",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
    media_types: media_types,
    lang: lang,
    locations: locations
  });
}));

app.use(_.get('/inventory.csv', function *() {
  var that = this;
  var json2csv = require('json2csv');
  var client = db();
  var all_items = yield client.query("SELECT * FROM inventory;");
  var fields = Object.keys(all_items.rows[0]);
  this.set({
    'Content-Type': 'text/csv',
    'Content-Disposition': 'attachment; filename=inventory.csv'
  });
  json2csv({ data: all_items.rows, fields: fields }, function(err, csv) {
    if (err) throw err;
    that.body = csv;
  });
}));

app.use(_.get('/viewHistory', function *() {
  var client = db();
  yield this.render('view-history', {
    title: "Item History",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
  });
}));

app.use(_.get("/extras", function *() {
  yield this.render('extras', {layout: this.USE_LAYOUT });
}));

app.use(_.get("/calendar", function *(next) {
  var token = ENV.SCHEDTOKEN;
  var response = yield request('http://'+ENV.SCHEDHOST+'/rooms?token='+token+'&format=json');
  var rooms = JSON.parse(response.body);
  yield this.render('calendar', {layout: this.USE_LAYOUT, date: new Date(), user: this.session.user, isAdmin:isAdmin, rooms: rooms});
}));

app.use(_.get("/signin", function *(next){
  ticket=this.request.query.ticket;
  try {
    var obj = yield auth.cas_login(ticket, SERVICE);
  } catch (e){
    this.redirect('https://cas.byu.edu/cas/login?service=' + SERVICE);
  }
  auth.login(this, obj);
  if (yield auth.isAdmin(this.session.user)){
    this.redirect('/');
  }
  else {
    this.redirect('https://hlr.byu.edu/schedule/');
  }
  yield next;
}));

app.use(_.get("/languages", function*() {
  yield this.render('languages', {
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list
  });
}));

app.use(_.get("/locations", function*() {
  yield this.render('locations', {
    layout: this.USE_LAYOUT,
    locations: yield require('./app_modules/location').list
  });
}));

app.use(_.get("/media", function*() {
  yield this.render('media', {
    layout: this.USE_LAYOUT,
    media: yield require('./app_modules/media').list,
    csrf: this.csrf
  });
}));

app.use(_.post('/media', function*() {
  var media = require('./app_modules/media');
  var body = yield parse(this); // co-body or something

  try {
    this.assertCSRF(body.csrf);
    assert(yield auth.isAdmin(this.session.user));
  }catch(err) {
    this.status = 403
    this.body = {
      message: 'Unauthorized'
    };
    return;
  }

  if(body['delete']) {
    yield media.remove(body['delete']);
  }
  else if(body['create']) {
    yield media.add(body['new-media']);
  }

  this.redirect('/media');
}));

app.use(_.get("/logout", function *(){
  this.session = null;
  var s = this.request.query.service || null;
  var url = 'https://cas.byu.edu/cas/logout' + (s ? '?service=' + s : '');
  this.redirect(url);
}));

app.use(_.post("/employees",function *(){
  var client = db();
  var body = yield parse(this) // co-body or something
  this.assertCSRF(body.csrf);
  if (body.action == "add") {
    try {
      var status = yield auth.mkadmin(this.session.user, body, true);
      if(!status){
        this.redirect('/employees?status='+status+'&toMk='+to_mk);
      }
      else{
        this.redirect('/employees');
      }
    }
    catch (err) {
      console.error(err);
      this.status = 403
      this.body = {
        message: 'This CSRF token is invalid!'
      }
      return
    }
  } else if (body.action == "remove") {
    try {
      to_del=body.user;
      override = (body.override=="true");
      status = yield auth.deladmin(this.session.user, to_del, override);
      if(!status){
        this.redirect('/employees?status='+status+'&toDel='+to_del);
      }
      else{
        this.redirect('/employees');
      }
    }
    catch (err) {
      this.status = 403
      this.body = {
        message: 'This CSRF token is invalid!'
      }
      return
    }
  }
  else
  {
    this.status = 400;
    this.body = {
      message: 'Invalid data'
    }
  }
}));

app.use(_.get("/employees",function *(){
  var isAdmin = yield auth.isAdmin(this.session.user);
  var client = db();
  var allAdmins = yield client.query("SELECT netid, email, telephone, admin FROM users WHERE admin = TRUE;");
  var netids = allAdmins.rows.map(function (a){return a.netid});
  var ldapDetails = yield user.ldapInfo.apply(null, netids);
  var names = Object.keys(ldapDetails).map(function(k){return ldapDetails[k].cn});
  if (isAdmin){
    if (this.request.query.status == "false"){
      yield this.render('mkadmin', {layout: this.USE_LAYOUT, csrf: this.csrf, allAdminsFromDB: allAdmins, alert : true, to_mk : this.request.query.toMk, namesFromDB : names});
    }
    else{
      yield this.render('mkadmin', {layout: this.USE_LAYOUT, csrf: this.csrf, allAdminsFromDB: allAdmins, alert : false, to_mk : undefined, namesFromDB : names});
    }
  }
  else{
    this.status = 403
    this.body = {
      message: 'Wrong place friend!'
    }
    return
  }
}));

app.use(_.get("/rss",function *(){
  var rss = require('rss');
  var newsbox = yield require('./app_modules/newsbox').list;
  var feed = new rss({
    title: 'NewsBox',
    feed_url: utils.gen_url(ENV.HLRDESK_HOST, ENV.PORT, '/rss', !ENV.HLRDESK_DEV),
    site_url: utils.gen_url(ENV.HLRDESK_HOST, ENV.PORT, '/', !ENV.HLRDESK_DEV)
  });
  newsbox.forEach(function(news, index) {
    feed.item({
      title:  news.heading,
      description: news.body,
      url: news.img_link
    });
  });
  this.set('Content-Type', 'application/rss+xml');
  this.body = feed.xml();
}));

app.use(_.get("/newsbox",function *(){
  yield this.render('newsbox', {
    layout: this.USE_LAYOUT,
    newsbox: yield require('./app_modules/newsbox').list
  });
}));

socket.start(app);

socket.use(function*(next){
  this.socket.user = yield auth.getUser(this.data.token);
  if(!this.socket.user){
    this.socket.emit('expired token', SERVICE);
  }
  yield next;
});

// Load in all socket files
fs.readdir(path.join(__dirname, 'sockets'), function (err, files) {
  if(err) {
    throw new Error(err);
  }
  var socket_files = files.filter(function(file) {
    return file.match(/\.js$/);
  });
  socket_files.forEach(function(file){
    try {
      require(path.join(__dirname, 'sockets', file))(socket, app);
    }
    catch(e) {
      console.error("Initializing sockets/" + file + " failed.", e);
      throw e;
    }
  });
});

module.exports = app.server;
<commit_msg>Fixed issues with admins being redirected.<commit_after>var _ = require('koa-route')
var koa = require('koa')
var serve = require('koa-static')
var render = require('koa-ejs')
var path = require('path')
var fs = require('fs');
var assert = require('assert');
var request = require('koa-request');

var socket = require('koa-socket');

var session = require('koa-session')
var parse = require('co-body')
var csrf = require('koa-csrf')

var app = koa();

require('koa-csrf')(app)

var email = require('./app_modules/email')

var auth = require('./app_modules/auth')
var db = require('./app_modules/db')
var user = require('./app_modules/user');
var utils = require('./app_modules/utils')
const ENV = process.env;
const SERVICE = auth.service(ENV.HLRDESK_HOST, ENV.PORT, '/signin', !ENV.HLRDESK_DEV);

if(ENV.HLRDESK_DEV) {
  app.use(require('./app_modules/koa-sassy').Sassy);
}

process.on("uncaughtException", function(err) {
  email.serverCrash(err.message, err.stack);
  throw err;
});

app.keys = ['TODO MAKE ME AN ENV VARIABLE', 'I SHOULD NOT BE HARDCODED', 'MY DOG HAS NO NOSE', 'HOW DOES HE SMELL?', 'AWFUL'];
app.use(session());

app.use(serve(path.join(__dirname, '..', 'public')));

if(ENV.NODE_TEST === 'true') {
  // e.g., /logmein?as=prabbit
  // see tests/sessions/* for available users
  require('./app_modules/mock-login')(app);
  var util = require('util');
  var log_file = fs.createWriteStream(path.join(__dirname, '/../', 'debug.log'), {flags : 'a'});
  log_file.write(new Date() + '\n');
  console.error = function(d) { //
    log_file.write(util.format(d) + '\n\n');
  };
}

app.use(function*(next){
  const WHITELIST = ['/signin', '/logmein', '/logout', '/rss'];
  //const GREYLIST = WHITELIST.concat(['/calendar']);
  const GREYLIST = WHITELIST;
  if (!this.session.user && WHITELIST.indexOf(this.request.path) === -1){
    this.session.login_redirect = this.request.path + this.request.search;
    this.redirect('https://cas.byu.edu/cas/login?service=' + SERVICE);
    return;
  }
  var is_admin = yield auth.isAdmin(this.session.user)
  if ( this.session.user && !is_admin && GREYLIST.indexOf(this.request.path) === -1){
    /*this.redirect('https://hlr.byu.edu/schedule/');
    return;*/

    this.session = null; //automatically log out if not admin
    var s = this.request.query.service || null;
    var url = 'https://cas.byu.edu/cas/logout' + (s ? '?service=' + s : '');
    this.redirect(url);
  }
  else{
    yield next;
  }
})

render(app, {
  root: path.join(__dirname, 'templates'),
  layout: 'layout',
  viewExt: 'html',
  cache: !ENV.HLRDESK_DEV,
  debug: ENV.HLRDESK_DEV,
  locals: {
    title: 'HLRDesk',
    token: function() {
      return this.session.token;
    }
  }
});

app.use(function *(next) {
  if(this.request.header['x-requested-with'] === 'XMLHttpRequest')
  {
    this.USE_LAYOUT = false;
  }
  else
  {
    if (this.session){
      var is_admin = yield auth.isAdmin(this.session.user)
      if (is_admin){
        this.USE_LAYOUT = 'layout';
        //TODO: May add read message check here. Likely the best spot
      }
      else{
        this.USE_LAYOUT = 'simple_layout';
      }
    }
    else{
      this.USE_LAYOUT = 'layout';
    }
  }
  captionClass = "undefined";
  yield next;
});

app.use(_.get("/", function *(next) {
  var rdir = this.session.login_redirect;
  if(rdir) {
    this.redirect(rdir);
    delete this.session.login_redirect;
    yield next;
    return;
  }
  yield this.render('layout', {layout: false, body:"", captionClass: "aboveLoader"});
  yield next;
}));

app.use(_.get("/message", function *() {
  var client = db();
  var all_messages = yield client.query("SELECT * FROM messages;");
  var all_tasks = yield client.query("Select * FROM tasks ORDER BY priority ASC");
  yield this.render('msg', {layout: this.USE_LAYOUT, all_messages: all_messages, all_tasks: all_tasks});
}));

app.use(_.get('/check-in', function *() {
  var inv = require('./app_modules/inventory');
  var items = yield inv.checked_out;

  var users = items.reduce(function(prev, item) {
    if(prev.indexOf(item.owner) === -1) {
      prev.push(item.owner);
    }
    return prev;
  }, []);

  var userDetails = yield user.contactInfo.apply(this, users);

  yield this.render('check-in', {
    items: items,
    user: userDetails,
    admin: this.session.user,
    moment: require('moment'),
    title: "Check In",
    layout: this.USE_LAYOUT
  });
}));

app.use(_.get('/check-out', function *() {
  yield this.render('check-out', {
    title: "Check Out",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
  });
}));

app.use(_.get('/edit-catalog', function *() {
  var client = db();
  var media_types = yield client.query("SELECT * FROM media ORDER BY media ASC;");
  var lang = yield client.query("SELECT * FROM languages ORDER BY name ASC;");
  var locations = yield client.query("SELECT * FROM locations ORDER BY name ASC;");
  yield this.render('edit-catalog', {
    title: "Edit Item",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
    media_types: media_types,
    lang: lang,
    locations: locations
  });
}));

app.use(_.get('/inventory.csv', function *() {
  var that = this;
  var json2csv = require('json2csv');
  var client = db();
  var all_items = yield client.query("SELECT * FROM inventory;");
  var fields = Object.keys(all_items.rows[0]);
  this.set({
    'Content-Type': 'text/csv',
    'Content-Disposition': 'attachment; filename=inventory.csv'
  });
  json2csv({ data: all_items.rows, fields: fields }, function(err, csv) {
    if (err) throw err;
    that.body = csv;
  });
}));

app.use(_.get('/viewHistory', function *() {
  var client = db();
  yield this.render('view-history', {
    title: "Item History",
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list,
    media: yield require('./app_modules/media').list,
  });
}));

app.use(_.get("/extras", function *() {
  yield this.render('extras', {layout: this.USE_LAYOUT });
}));

app.use(_.get("/calendar", function *(next) {
  var token = ENV.SCHEDTOKEN;
  var response = yield request('http://'+ENV.SCHEDHOST+'/rooms?token='+token+'&format=json');
  var rooms = JSON.parse(response.body);
  yield this.render('calendar', {layout: this.USE_LAYOUT, date: new Date(), user: this.session.user, isAdmin:isAdmin, rooms: rooms});
}));

app.use(_.get("/signin", function *(next){
  ticket=this.request.query.ticket;
  try {
    var obj = yield auth.cas_login(ticket, SERVICE);
  } catch (e){
    this.redirect('https://cas.byu.edu/cas/login?service=' + SERVICE);
  }
  auth.login(this, obj);
  if (yield auth.isAdmin(this.session.user)){
    this.redirect('/');
  }
  yield next;
}));

app.use(_.get("/languages", function*() {
  yield this.render('languages', {
    layout: this.USE_LAYOUT,
    languages: yield require('./app_modules/language').list
  });
}));

app.use(_.get("/locations", function*() {
  yield this.render('locations', {
    layout: this.USE_LAYOUT,
    locations: yield require('./app_modules/location').list
  });
}));

app.use(_.get("/media", function*() {
  yield this.render('media', {
    layout: this.USE_LAYOUT,
    media: yield require('./app_modules/media').list,
    csrf: this.csrf
  });
}));

app.use(_.post('/media', function*() {
  var media = require('./app_modules/media');
  var body = yield parse(this); // co-body or something

  try {
    this.assertCSRF(body.csrf);
    assert(yield auth.isAdmin(this.session.user));
  }catch(err) {
    this.status = 403
    this.body = {
      message: 'Unauthorized'
    };
    return;
  }

  if(body['delete']) {
    yield media.remove(body['delete']);
  }
  else if(body['create']) {
    yield media.add(body['new-media']);
  }

  this.redirect('/media');
}));

app.use(_.get("/logout", function *(){
  this.session = null;
  var s = this.request.query.service || null;
  var url = 'https://cas.byu.edu/cas/logout' + (s ? '?service=' + s : '');
  this.redirect(url);
}));

app.use(_.post("/employees",function *(){
  var client = db();
  var body = yield parse(this) // co-body or something
  this.assertCSRF(body.csrf);
  if (body.action == "add") {
    try {
      var status = yield auth.mkadmin(this.session.user, body, true);
      if(!status){
        this.redirect('/employees?status='+status+'&toMk='+to_mk);
      }
      else{
        this.redirect('/employees');
      }
    }
    catch (err) {
      console.error(err);
      this.status = 403
      this.body = {
        message: 'This CSRF token is invalid!'
      }
      return
    }
  } else if (body.action == "remove") {
    try {
      to_del=body.user;
      override = (body.override=="true");
      status = yield auth.deladmin(this.session.user, to_del, override);
      if(!status){
        this.redirect('/employees?status='+status+'&toDel='+to_del);
      }
      else{
        this.redirect('/employees');
      }
    }
    catch (err) {
      this.status = 403
      this.body = {
        message: 'This CSRF token is invalid!'
      }
      return
    }
  }
  else
  {
    this.status = 400;
    this.body = {
      message: 'Invalid data'
    }
  }
}));

app.use(_.get("/employees",function *(){
  var isAdmin = yield auth.isAdmin(this.session.user);
  var client = db();
  var allAdmins = yield client.query("SELECT netid, email, telephone, admin FROM users WHERE admin = TRUE;");
  var netids = allAdmins.rows.map(function (a){return a.netid});
  var ldapDetails = yield user.ldapInfo.apply(null, netids);
  var names = Object.keys(ldapDetails).map(function(k){return ldapDetails[k].cn});
  if (isAdmin){
    if (this.request.query.status == "false"){
      yield this.render('mkadmin', {layout: this.USE_LAYOUT, csrf: this.csrf, allAdminsFromDB: allAdmins, alert : true, to_mk : this.request.query.toMk, namesFromDB : names});
    }
    else{
      yield this.render('mkadmin', {layout: this.USE_LAYOUT, csrf: this.csrf, allAdminsFromDB: allAdmins, alert : false, to_mk : undefined, namesFromDB : names});
    }
  }
  else{
    this.status = 403
    this.body = {
      message: 'Wrong place friend!'
    }
    return
  }
}));

app.use(_.get("/rss",function *(){
  var rss = require('rss');
  var newsbox = yield require('./app_modules/newsbox').list;
  var feed = new rss({
    title: 'NewsBox',
    feed_url: utils.gen_url(ENV.HLRDESK_HOST, ENV.PORT, '/rss', !ENV.HLRDESK_DEV),
    site_url: utils.gen_url(ENV.HLRDESK_HOST, ENV.PORT, '/', !ENV.HLRDESK_DEV)
  });
  newsbox.forEach(function(news, index) {
    feed.item({
      title:  news.heading,
      description: news.body,
      url: news.img_link
    });
  });
  this.set('Content-Type', 'application/rss+xml');
  this.body = feed.xml();
}));

app.use(_.get("/newsbox",function *(){
  yield this.render('newsbox', {
    layout: this.USE_LAYOUT,
    newsbox: yield require('./app_modules/newsbox').list
  });
}));

socket.start(app);

socket.use(function*(next){
  this.socket.user = yield auth.getUser(this.data.token);
  if(!this.socket.user){
    this.socket.emit('expired token', SERVICE);
  }
  yield next;
});

// Load in all socket files
fs.readdir(path.join(__dirname, 'sockets'), function (err, files) {
  if(err) {
    throw new Error(err);
  }
  var socket_files = files.filter(function(file) {
    return file.match(/\.js$/);
  });
  socket_files.forEach(function(file){
    try {
      require(path.join(__dirname, 'sockets', file))(socket, app);
    }
    catch(e) {
      console.error("Initializing sockets/" + file + " failed.", e);
      throw e;
    }
  });
});

module.exports = app.server;
<|endoftext|>


<commit_before>var Request = require(__dirname + '/request.js');

function Bot(name, folder, allConfigurations){
  this.name = name;
  this.folder = folder;

  this.allConfigurations = allConfigurations || [];
  this.currentConfiguration = {};
  this.accessToken = {};

  this.accessTokensFolder = __dirname + '/../access_tokens/' + this.folder + '/';
  this.rateLimitsFolder = __dirname + '/../rate_limits/' + this.folder + '/';

  this.useModels = true;

  this.maxAttemptForDownload = 3;
  this.delayBeforeNewAttemptDownload = 1000;

  this.remainingRequest = 100;
  this.remainingTime = 60*60;
}

Bot.prototype = new Request();
Bot.prototype.constructor = Bot;

Bot.prototype.getName = function() {
  return this.name;
};

Bot.prototype.setName = function(name) {
  this.name = name;
};

Bot.prototype.getFolder = function() {
  return this.folder;
};

Bot.prototype.setFolder = function(folder) {
  this.folder = folder;
};

Bot.prototype.getCurrentConfigurationName = function() {
  return this.currentConfiguration.name || "";
};

Bot.prototype.setCurrentConfiguration = function(configuration) {
  this.currentConfiguration = configuration;
};

Bot.prototype.getCurrentConfiguration = function() {
  return this.currentConfiguration;
};

Bot.prototype.setAllConfigurations = function(configurations) {
  this.allConfigurations = configurations;
};

Bot.prototype.getAllConfigurations = function(configurations) {
  return this.allConfigurations;
};

Bot.prototype.useConfigurationByName = function(configurationName) {
  var i = 0;
  var countConfigurations = this.allConfigurations.length;

  for(; i < countConfigurations; i++) {
    if(this.allConfigurations[i].name == configurationName) {
      this.currentConfiguration = this.allConfigurations[i];
      return;
    }
  }

  throw this.RError('BOT-001', "Configuration named %s is not found", configurationName);
};

Bot.prototype.getUserAccessTokenFile = function(user) {
  var filepath = this.accessTokensFolder + user + '.tok';
  var fileContent;

  if(this.isFileExist(filepath)) {
    return JSON.parse(require('fs').readFileSync(filepath));
  }
  else {
    throw this.RError('BOT-002', "Access Token File not found for user %s", user);
  }
};

Bot.prototype.loadUserAccessToken = function(user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  if(accessTokens.length > 0) {
    this.accessToken = accessTokens[0];
  }
  else {
    throw this.RError('BOT-003', "Access Token empty for user %s", user);
  }
};

Bot.prototype.loadUserAccessTokenCompatible = function(user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  var idx = 0;
  var countAccessToken = accessTokens.length;
  if(countAccessToken < 1) {
    throw this.RError('BOT-003', "Access Token empty for user %s", user);
  }

  if(this.currentConfiguration.name === undefined) {
    throw this.RError('BOT-004', "Configuration name is undefined");
  }

  for(; idx < countAccessToken; idx++) {
    if(accessTokens[idx].configuration_name === this.currentConfiguration.name) {
      this.accessToken = accessTokens[idx];
      return;
    }
  }

  throw this.RError('BOT-005', "Access Token incompatible for user %s and configuration %s", user, this.currentConfiguration.name);
};

Bot.prototype.enableModels = function() {
  this.useModels = true;
};

Bot.prototype.disableModels = function() {
  this.useModels = false;
};

Bot.prototype.setAccessToken = function(accessToken) {
  this.accessToken = accessToken;
};

Bot.prototype.getAuthorizeCodeUrl = function() {
  throw this.RError('BOT-006', "Implement getAuthorizeCodeUrl");
};

Bot.prototype.getAccessTokenByUrl = function(code, callback) {
  throw this.RError('BOT-007', "Implement getAccessTokenByUrl");
};

Bot.prototype.isUserAccessTokenCompatibleWithCurrentConfiguration = function (user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  var idx = 0;
  var countAccessToken = accessTokens.length;
  if(countAccessToken < 1) {
    return false;
  }

  if(this.currentConfiguration.name === undefined) {
    return false;
  }

  for(; idx < countAccessToken; idx++) {
    if(accessTokens[idx].configuration_name === this.currentConfiguration.name) {
      return true;
    }
  }

  return false;
};

Bot.prototype.isAccessTokenSetted = function () {
  return Object.keys(this.accessToken).length > 0;
};

Bot.prototype.download = function(url, destination, callback, countAttempt) {
  var that = this;
  var wrapper;
  var file;
  var request;

  if(url.substr(0,5) === "https") {
    wrapper = require('https');
  }
  else if(url.substr(0,4) === "http") {
    wrapper = require('http');
  }
  else {
    callback('Download only http or https ressource');
    return;
  }

  countAttempt = countAttempt || 0;

  file = require('fs').createWriteStream(destination);
  request = wrapper.get(url, function(response) {
    if(response.statusCode >= 400) {
      countAttempt++;

      if(countAttempt > that.maxAttemptForDownload) {
        callback('Download failed for ' + url);
        return;
      }

      setTimeout(function(){
        that.download(url, destination, callback, countAttempt);
      }, that.delayBeforeNewAttemptDownload);

      return;
    }

    response.pipe(file);

    file.on('finish', function() {
      file.close(callback);
    });

  }).on('error', function(error) {
    require('fs').unlink(destination);

    if (callback) {
      callback(error.message);
    }
  });
};

// todo
Bot.prototype.updateRateLimit = function(remainingRateLimit) {
  var rateLimitApp = getRateLimitByName(this.getAppName());

  if(globalUser !== undefined && rateLimitApp[globalUser] !== undefined) {
    rateLimitApp[globalUser]['last_access'] = new Date().getTime();
    rateLimitApp[globalUser]['remaining'] = parseInt(remainingRateLimit);

    saveRateLimitByName(this.getAppName(), JSON.stringify(rateLimitApp));
  }
};

// todo / implement
Bot.prototype.getClientRateLimit = function(client, callback) {
  var appRateLimit = {};
  var lastAccess = new Date().getTime();

  fs.readdirSync(__dirname + '/../oauth_access_cache/').forEach(function(file) {
    if(file.match(/\.tok$/) !== null) {
      var user = file.replace('.tok', '');
      var tokenJson = JSON.parse(fs.readFileSync(this.rateLimitsFolder + user + '.tok'));
      for (var i = 0; i < tokenJson.length; i++) {
        if(tokenJson[i].app_name === client.getAppName()) {
          appRateLimit[user] = {"last_access": lastAccess, "remaining": this.remainingRequest};
        }
      }
    }
  });

  callback(appRateLimit);
};

// todo / implement
Bot.prototype.getRateLimitByName = function(name, forceRefresh) {
  // search in folder rate_limit_cache
  var rateLimitJson = null;

  if(forceRefresh !== undefined && forceRefresh === true) {
    return rateLimitJson;
  }

  try {
    var rateLimitFileStats = require('fs').statSync(this.rateLimitsFolder + name + '.json');
    var _date = new Date();
    _date.setSeconds(_date.getSeconds() - this.remainingTime);
    // if file is still fresh, we can read and return it
    if(rateLimitFileStats.mtime.getTime() > _date.getTime()) {
      rateLimitJson = require('fs').readFileSync(this.rateLimitsFolder + name + '.json', 'utf8');
      rateLimitJson = JSON.parse(rateLimitJson);
    }
  } catch (e) {
    //
  }

  return rateLimitJson;
};

// todo / implement
Bot.prototype.saveRateLimitByName = function(name, data) {
  require('fs').writeFileSync(this.rateLimitsFolder + name + '.json', JSON.stringify(data), 'utf8');
};

Bot.prototype.isFileExist = function(filepath) {
  try {
    return require('fs').lstatSync(filepath);
  }
  catch(e){
    return false;
  }
};

Bot.prototype.RError = function (code, message, file, lineNumber) {
  var _RError = require(__dirname + '/rerror.js');
  return new _RError(code, message, file, lineNumber);
};

module.exports = Bot;<commit_msg>models<commit_after>var Request = require(__dirname + '/request.js');

function Bot(name, folder, allConfigurations){
  this.name = name;
  this.folder = folder;

  this.allConfigurations = allConfigurations || [];
  this.currentConfiguration = {};
  this.accessToken = {};

  this.accessTokensFolder = __dirname + '/../access_tokens/' + this.folder + '/';
  this.rateLimitsFolder = __dirname + '/../rate_limits/' + this.folder + '/';
  this.modelsFolder =  __dirname + '/../models/' + this.folder + '/';

  this.useModels = true;

  this.maxAttemptForDownload = 3;
  this.delayBeforeNewAttemptDownload = 1000;

  this.remainingRequest = 100;
  this.remainingTime = 60*60;
}

Bot.prototype = new Request();
Bot.prototype.constructor = Bot;

Bot.prototype.getName = function() {
  return this.name;
};

Bot.prototype.setName = function(name) {
  this.name = name;
};

Bot.prototype.getFolder = function() {
  return this.folder;
};

Bot.prototype.setFolder = function(folder) {
  this.folder = folder;
};

Bot.prototype.getCurrentConfigurationName = function() {
  return this.currentConfiguration.name || "";
};

Bot.prototype.setCurrentConfiguration = function(configuration) {
  this.currentConfiguration = configuration;
};

Bot.prototype.getCurrentConfiguration = function() {
  return this.currentConfiguration;
};

Bot.prototype.setAllConfigurations = function(configurations) {
  this.allConfigurations = configurations;
};

Bot.prototype.getAllConfigurations = function(configurations) {
  return this.allConfigurations;
};

Bot.prototype.useConfigurationByName = function(configurationName) {
  var i = 0;
  var countConfigurations = this.allConfigurations.length;

  for(; i < countConfigurations; i++) {
    if(this.allConfigurations[i].name == configurationName) {
      this.currentConfiguration = this.allConfigurations[i];
      return;
    }
  }

  throw this.RError('BOT-001', "Configuration named %s is not found", configurationName);
};

Bot.prototype.getUserAccessTokenFile = function(user) {
  var filepath = this.accessTokensFolder + user + '.tok';
  var fileContent;

  if(this.isFileExist(filepath)) {
    return JSON.parse(require('fs').readFileSync(filepath));
  }
  else {
    throw this.RError('BOT-002', "Access Token File not found for user %s", user);
  }
};

Bot.prototype.loadUserAccessToken = function(user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  if(accessTokens.length > 0) {
    this.accessToken = accessTokens[0];
  }
  else {
    throw this.RError('BOT-003', "Access Token empty for user %s", user);
  }
};

Bot.prototype.loadUserAccessTokenCompatible = function(user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  var idx = 0;
  var countAccessToken = accessTokens.length;
  if(countAccessToken < 1) {
    throw this.RError('BOT-003', "Access Token empty for user %s", user);
  }

  if(this.currentConfiguration.name === undefined) {
    throw this.RError('BOT-004', "Configuration name is undefined");
  }

  for(; idx < countAccessToken; idx++) {
    if(accessTokens[idx].configuration_name === this.currentConfiguration.name) {
      this.accessToken = accessTokens[idx];
      return;
    }
  }

  throw this.RError('BOT-005', "Access Token incompatible for user %s and configuration %s", user, this.currentConfiguration.name);
};

Bot.prototype.enableModels = function() {
  this.useModels = true;
};

Bot.prototype.disableModels = function() {
  this.useModels = false;
};

Bot.prototype.setAccessToken = function(accessToken) {
  this.accessToken = accessToken;
};

Bot.prototype.getAuthorizeCodeUrl = function() {
  throw this.RError('BOT-006', "Implement getAuthorizeCodeUrl");
};

Bot.prototype.getAccessTokenByUrl = function(code, callback) {
  throw this.RError('BOT-007', "Implement getAccessTokenByUrl");
};

Bot.prototype.isUserAccessTokenCompatibleWithCurrentConfiguration = function (user) {
  var accessTokens = this.getUserAccessTokenFile(user);
  var idx = 0;
  var countAccessToken = accessTokens.length;
  if(countAccessToken < 1) {
    return false;
  }

  if(this.currentConfiguration.name === undefined) {
    return false;
  }

  for(; idx < countAccessToken; idx++) {
    if(accessTokens[idx].configuration_name === this.currentConfiguration.name) {
      return true;
    }
  }

  return false;
};

Bot.prototype.isAccessTokenSetted = function () {
  return Object.keys(this.accessToken).length > 0;
};

Bot.prototype.download = function(url, destination, callback, countAttempt) {
  var that = this;
  var wrapper;
  var file;
  var request;

  if(url.substr(0,5) === "https") {
    wrapper = require('https');
  }
  else if(url.substr(0,4) === "http") {
    wrapper = require('http');
  }
  else {
    callback('Download only http or https ressource');
    return;
  }

  countAttempt = countAttempt || 0;

  file = require('fs').createWriteStream(destination);
  request = wrapper.get(url, function(response) {
    if(response.statusCode >= 400) {
      countAttempt++;

      if(countAttempt > that.maxAttemptForDownload) {
        callback('Download failed for ' + url);
        return;
      }

      setTimeout(function(){
        that.download(url, destination, callback, countAttempt);
      }, that.delayBeforeNewAttemptDownload);

      return;
    }

    response.pipe(file);

    file.on('finish', function() {
      file.close(callback);
    });

  }).on('error', function(error) {
    require('fs').unlink(destination);

    if (callback) {
      callback(error.message);
    }
  });
};

// todo
Bot.prototype.updateRateLimit = function(remainingRateLimit) {
  var rateLimitApp = getRateLimitByName(this.getAppName());

  if(globalUser !== undefined && rateLimitApp[globalUser] !== undefined) {
    rateLimitApp[globalUser]['last_access'] = new Date().getTime();
    rateLimitApp[globalUser]['remaining'] = parseInt(remainingRateLimit);

    saveRateLimitByName(this.getAppName(), JSON.stringify(rateLimitApp));
  }
};

// todo / implement
Bot.prototype.getClientRateLimit = function(client, callback) {
  var appRateLimit = {};
  var lastAccess = new Date().getTime();

  fs.readdirSync(__dirname + '/../oauth_access_cache/').forEach(function(file) {
    if(file.match(/\.tok$/) !== null) {
      var user = file.replace('.tok', '');
      var tokenJson = JSON.parse(fs.readFileSync(this.rateLimitsFolder + user + '.tok'));
      for (var i = 0; i < tokenJson.length; i++) {
        if(tokenJson[i].app_name === client.getAppName()) {
          appRateLimit[user] = {"last_access": lastAccess, "remaining": this.remainingRequest};
        }
      }
    }
  });

  callback(appRateLimit);
};

// todo / implement
Bot.prototype.getRateLimitByName = function(name, forceRefresh) {
  // search in folder rate_limit_cache
  var rateLimitJson = null;

  if(forceRefresh !== undefined && forceRefresh === true) {
    return rateLimitJson;
  }

  try {
    var rateLimitFileStats = require('fs').statSync(this.rateLimitsFolder + name + '.json');
    var _date = new Date();
    _date.setSeconds(_date.getSeconds() - this.remainingTime);
    // if file is still fresh, we can read and return it
    if(rateLimitFileStats.mtime.getTime() > _date.getTime()) {
      rateLimitJson = require('fs').readFileSync(this.rateLimitsFolder + name + '.json', 'utf8');
      rateLimitJson = JSON.parse(rateLimitJson);
    }
  } catch (e) {
    //
  }

  return rateLimitJson;
};

// todo / implement
Bot.prototype.saveRateLimitByName = function(name, data) {
  require('fs').writeFileSync(this.rateLimitsFolder + name + '.json', JSON.stringify(data), 'utf8');
};

Bot.prototype.isFileExist = function(filepath) {
  try {
    return require('fs').lstatSync(filepath);
  }
  catch(e){
    return false;
  }
};

Bot.prototype.RError = function (code, message, file, lineNumber) {
  var _RError = require(__dirname + '/rerror.js');
  return new _RError(code, message, file, lineNumber);
};

module.exports = Bot;<|endoftext|>


<commit_before>var dns = require('node-dns'),
  dns_server = dns.createServer();

var cache = require('./db');

var dns_port = process.env.DNS_PORT || 53;
var dns_host = process.env.DNS_HOST || '127.0.0.1';
var dns_root = process.env.DNS_ROOT || 'e164.arpa'
var reverse_dns_in_a = false;

dns_server.on('request', function (request, response) {

  /* Parse Query Name */
  var name = request.question[0].name;
  var dns_type = parseInt(request.question[0].type);
  if (!name || !name.endsWith(dns_root)) { response.send(); return; }

  console.log("TYPE",dns_type);

  if (dns_type != 1 && dns_type != 35) { response.send(); return; }
  /* Resolve Target Number */
  //var target = name.replace(dns_root,"").split(".").join("").split("").reverse().join("");

  var target;

  /* type ==  1 - IN A*/
  if(dns_type == 1)
  {
      if(reverse_dns_in_a) target = name.replace(dns_root,"").split(".").reverse().filter(Number).join(".");
      else target = name.replace(dns_root,"").split(".").filter(Number).join(".");
  }
  /* DNS type 35 == NAPTR - ENUM */
  else if (dns_type == 35) {
      target = name.replace(dns_root,"").split(".").reverse().filter(Number).join(".");
  }

  for (var i = target.length, len = 0; i > len; i--) {
     if (cache.get(target.substring(0,i))) {

		//console.log('MATCH!',target);

		if(dns_type == 1)
		{
		    response.additional.push(dns.A({
		              ttl: 300,
		              name: name,
		              address: target
                    }));
		}
		else {

		    response.additional.push(dns.NAPTR({
		          ttl: 300,
		          name: name,
		          order: 10,
		          preference: 100,
		          flags: 'u',
		          service: 'E2U+sip',
		          regexp: '!^.*$!sip:'+target+'@invalid!',
		          replacement:''
                     }));
                }

		response.send();
		return;
     }
  }

  response.send();

});

dns_server.on('error', function (err, buff, req, res) {
  console.log(err.stack);
});

dns_server.serve(dns_port,dns_host);

/* Export */

module.exports = app => dns_server;

<commit_msg>Update dns.js<commit_after>var dns = require('node-dns'),
  dns_server = dns.createServer();

var cache = require('./db');

var dns_port = process.env.DNS_PORT || 53;
var dns_host = process.env.DNS_HOST || '127.0.0.1';
var dns_root = process.env.DNS_ROOT || 'e164.arpa'
var reverse_dns_in_a = false;

dns_server.on('request', function (request, response) {

  /* Parse Query Name */
  var name = request.question[0].name;
  var dns_type = parseInt(request.question[0].type);
  if (!name || !name.endsWith(dns_root)) { response.send(); return; }

  //console.log("TYPE",dns_type);

  if (dns_type != 1 && dns_type != 35) { response.send(); return; }
  /* Resolve Target Number */
  //var target = name.replace(dns_root,"").split(".").join("").split("").reverse().join("");

  var target;

  /* type ==  1 - IN A*/
  if(dns_type == 1)
  {
      if(reverse_dns_in_a) target = name.replace(dns_root,"").split(".").reverse().filter(Number).join(".");
      else target = name.replace(dns_root,"").split(".").filter(Number).join(".");
  }
  /* DNS type 35 == NAPTR - ENUM */
  else if (dns_type == 35) {
      target = name.replace(dns_root,"").split(".").reverse().filter(Number).join(".");
  }

  for (var i = target.length, len = 0; i > len; i--) {
     if (cache.get(target.substring(0,i))) {

		//console.log('MATCH!',target);

		if(dns_type == 1)
		{
		    response.answer.push(dns.A({
		              ttl: 300,
		              name: name,
		              address: target
                    }));
		}
		else {

		    response.additional.push(dns.NAPTR({
		          ttl: 300,
		          name: name,
		          order: 10,
		          preference: 100,
		          flags: 'u',
		          service: 'E2U+sip',
		          regexp: '!^.*$!sip:'+target+'@invalid!',
		          replacement:''
                     }));
                }

		response.send();
		return;
     }
  }

  response.send();

});

dns_server.on('error', function (err, buff, req, res) {
  console.log(err.stack);
});

dns_server.serve(dns_port,dns_host);

/* Export */

module.exports = app => dns_server;

<|endoftext|>


<commit_before>import { configs } from '../index'

let handler = null

export const setErrorHandler = (errorHandler) => {
  handler = errorHandler
}

/**
 * Provides a catch-all function for all requests. If the catch-all
 * function returns `true` it prevents the original promise to continue.
 */
const GlobalErrorHandler = () => ({
  response (next) {
    return new configs.Promise((resolve, reject) => {
      next()
        .then((response) => resolve(response))
        .catch((response) => {
          let proceed = true
          handler && (proceed = !(handler(response) === true))
          proceed && reject(response)
        })
    })
  }
})

export default GlobalErrorHandler
<commit_msg>Name global error handler<commit_after>import { configs } from '../index'

let handler = null

export const setErrorHandler = (errorHandler) => {
  handler = errorHandler
}

/**
 * Provides a catch-all function for all requests. If the catch-all
 * function returns `true` it prevents the original promise to continue.
 */
const GlobalErrorHandlerMiddleware = () => ({
  response (next) {
    return new configs.Promise((resolve, reject) => {
      next()
        .then((response) => resolve(response))
        .catch((response) => {
          let proceed = true
          handler && (proceed = !(handler(response) === true))
          proceed && reject(response)
        })
    })
  }
})

export default GlobalErrorHandlerMiddleware
<|endoftext|>