<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

const NON_BINARY_PROP_NAMES_TO_ALIASES = {
  General_Category: 'gc',
  Script: 'sc',
  Script_Extensions: 'scx',
};

const NON_BINARY_ALIASES_TO_PROP_NAMES = inverseMap(
  NON_BINARY_PROP_NAMES_TO_ALIASES
);

const BINARY_PROP_NAMES_TO_ALIASES = {
  ASCII: 'ASCII',
  ASCII_Hex_Digit: 'AHex',
  Alphabetic: 'Alpha',
  Any: 'Any',
  Assigned: 'Assigned',
  Bidi_Control: 'Bidi_C',
  Bidi_Mirrored: 'Bidi_M',
  Case_Ignorable: 'CI',
  Cased: 'Cased',
  Changes_When_Casefolded: 'CWCF',
  Changes_When_Casemapped: 'CWCM',
  Changes_When_Lowercased: 'CWL',
  Changes_When_NFKC_Casefolded: 'CWKCF',
  Changes_When_Titlecased: 'CWT',
  Changes_When_Uppercased: 'CWU',
  Dash: 'Dash',
  Default_Ignorable_Code_Point: 'DI',
  Deprecated: 'Dep',
  Diacritic: 'Dia',
  Emoji: 'Emoji',
  Emoji_Component: 'Emoji_Component',
  Emoji_Modifier: 'Emoji_Modifier',
  Emoji_Modifier_Base: 'Emoji_Modifier_Base',
  Emoji_Presentation: 'Emoji_Presentation',
  Extender: 'Ext',
  Grapheme_Base: 'Gr_Base',
  Grapheme_Extend: 'Gr_Ext',
  Hex_Digit: 'Hex',
  IDS_Binary_Operator: 'IDSB',
  IDS_Trinary_Operator: 'IDST',
  ID_Continue: 'IDC',
  ID_Start: 'IDS',
  Ideographic: 'Ideo',
  Join_Control: 'Join_C',
  Logical_Order_Exception: 'LOE',
  Lowercase: 'Lower',
  Math: 'Math',
  Noncharacter_Code_Point: 'NChar',
  Pattern_Syntax: 'Pat_Syn',
  Pattern_White_Space: 'Pat_WS',
  Quotation_Mark: 'QMark',
  Radical: 'Radical',
  Regional_Indicator: 'RI',
  Sentence_Terminal: 'STerm',
  Soft_Dotted: 'SD',
  Terminal_Punctuation: 'Term',
  Unified_Ideograph: 'UIdeo',
  Uppercase: 'Upper',
  Variation_Selector: 'VS',
  White_Space: 'space',
  XID_Continue: 'XIDC',
  XID_Start: 'XIDS',
};

const BINARY_ALIASES_TO_PROP_NAMES = inverseMap(BINARY_PROP_NAMES_TO_ALIASES);

const GENERAL_CATEGORY_VALUE_TO_ALIASES = {
  Cased_Letter: 'LC',
  Close_Punctuation: 'Pe',
  Connector_Punctuation: 'Pc',
  Control: ['Cc', 'cntrl'],
  Currency_Symbol: 'Sc',
  Dash_Punctuation: 'Pd',
  Decimal_Number: ['Nd', 'digit'],
  Enclosing_Mark: 'Me',
  Final_Punctuation: 'Pf',
  Format: 'Cf',
  Initial_Punctuation: 'Pi',
  Letter: 'L',
  Letter_Number: 'Nl',
  Line_Separator: 'Zl',
  Lowercase_Letter: 'Ll',
  Mark: ['M', 'Combining_Mark'],
  Math_Symbol: 'Sm',
  Modifier_Letter: 'Lm',
  Modifier_Symbol: 'Sk',
  Nonspacing_Mark: 'Mn',
  Number: 'N',
  Open_Punctuation: 'Ps',
  Other: 'C',
  Other_Letter: 'Lo',
  Other_Number: 'No',
  Other_Punctuation: 'Po',
  Other_Symbol: 'So',
  Paragraph_Separator: 'Zp',
  Private_Use: 'Co',
  Punctuation: ['P', 'punct'],
  Separator: 'Z',
  Space_Separator: 'Zs',
  Spacing_Mark: 'Mc',
  Surrogate: 'Cs',
  Symbol: 'S',
  Titlecase_Letter: 'Lt',
  Unassigned: 'Cn',
  Uppercase_Letter: 'Lu',
};

const GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES = inverseMap(
  GENERAL_CATEGORY_VALUE_TO_ALIASES
);

const SCRIPT_VALUE_TO_ALIASES = {
  Adlam: 'Adlm',
  Ahom: 'Ahom',
  Anatolian_Hieroglyphs: 'Hluw',
  Arabic: 'Arab',
  Armenian: 'Armn',
  Avestan: 'Avst',
  Balinese: 'Bali',
  Bamum: 'Bamu',
  Bassa_Vah: 'Bass',
  Batak: 'Batk',
  Bengali: 'Beng',
  Bhaiksuki: 'Bhks',
  Bopomofo: 'Bopo',
  Brahmi: 'Brah',
  Braille: 'Brai',
  Buginese: 'Bugi',
  Buhid: 'Buhd',
  Canadian_Aboriginal: 'Cans',
  Carian: 'Cari',
  Caucasian_Albanian: 'Aghb',
  Chakma: 'Cakm',
  Cham: 'Cham',
  Cherokee: 'Cher',
  Common: 'Zyyy',
  Coptic: ['Copt', 'Qaac'],
  Cuneiform: 'Xsux',
  Cypriot: 'Cprt',
  Cyrillic: 'Cyrl',
  Deseret: 'Dsrt',
  Devanagari: 'Deva',
  Duployan: 'Dupl',
  Egyptian_Hieroglyphs: 'Egyp',
  Elbasan: 'Elba',
  Ethiopic: 'Ethi',
  Georgian: 'Geor',
  Glagolitic: 'Glag',
  Gothic: 'Goth',
  Grantha: 'Gran',
  Greek: 'Grek',
  Gujarati: 'Gujr',
  Gurmukhi: 'Guru',
  Han: 'Hani',
  Hangul: 'Hang',
  Hanunoo: 'Hano',
  Hatran: 'Hatr',
  Hebrew: 'Hebr',
  Hiragana: 'Hira',
  Imperial_Aramaic: 'Armi',
  Inherited: ['Zinh', 'Qaai'],
  Inscriptional_Pahlavi: 'Phli',
  Inscriptional_Parthian: 'Prti',
  Javanese: 'Java',
  Kaithi: 'Kthi',
  Kannada: 'Knda',
  Katakana: 'Kana',
  Kayah_Li: 'Kali',
  Kharoshthi: 'Khar',
  Khmer: 'Khmr',
  Khojki: 'Khoj',
  Khudawadi: 'Sind',
  Lao: 'Laoo',
  Latin: 'Latn',
  Lepcha: 'Lepc',
  Limbu: 'Limb',
  Linear_A: 'Lina',
  Linear_B: 'Linb',
  Lisu: 'Lisu',
  Lycian: 'Lyci',
  Lydian: 'Lydi',
  Mahajani: 'Mahj',
  Malayalam: 'Mlym',
  Mandaic: 'Mand',
  Manichaean: 'Mani',
  Marchen: 'Marc',
  Masaram_Gondi: 'Gonm',
  Meetei_Mayek: 'Mtei',
  Mende_Kikakui: 'Mend',
  Meroitic_Cursive: 'Merc',
  Meroitic_Hieroglyphs: 'Mero',
  Miao: 'Plrd',
  Modi: 'Modi',
  Mongolian: 'Mong',
  Mro: 'Mroo',
  Multani: 'Mult',
  Myanmar: 'Mymr',
  Nabataean: 'Nbat',
  New_Tai_Lue: 'Talu',
  Newa: 'Newa',
  Nko: 'Nkoo',
  Nushu: 'Nshu',
  Ogham: 'Ogam',
  Ol_Chiki: 'Olck',
  Old_Hungarian: 'Hung',
  Old_Italic: 'Ital',
  Old_North_Arabian: 'Narb',
  Old_Permic: 'Perm',
  Old_Persian: 'Xpeo',
  Old_South_Arabian: 'Sarb',
  Old_Turkic: 'Orkh',
  Oriya: 'Orya',
  Osage: 'Osge',
  Osmanya: 'Osma',
  Pahawh_Hmong: 'Hmng',
  Palmyrene: 'Palm',
  Pau_Cin_Hau: 'Pauc',
  Phags_Pa: 'Phag',
  Phoenician: 'Phnx',
  Psalter_Pahlavi: 'Phlp',
  Rejang: 'Rjng',
  Runic: 'Runr',
  Samaritan: 'Samr',
  Saurashtra: 'Saur',
  Sharada: 'Shrd',
  Shavian: 'Shaw',
  Siddham: 'Sidd',
  SignWriting: 'Sgnw',
  Sinhala: 'Sinh',
  Sora_Sompeng: 'Sora',
  Soyombo: 'Soyo',
  Sundanese: 'Sund',
  Syloti_Nagri: 'Sylo',
  Syriac: 'Syrc',
  Tagalog: 'Tglg',
  Tagbanwa: 'Tagb',
  Tai_Le: 'Tale',
  Tai_Tham: 'Lana',
  Tai_Viet: 'Tavt',
  Takri: 'Takr',
  Tamil: 'Taml',
  Tangut: 'Tang',
  Telugu: 'Telu',
  Thaana: 'Thaa',
  Thai: 'Thai',
  Tibetan: 'Tibt',
  Tifinagh: 'Tfng',
  Tirhuta: 'Tirh',
  Ugaritic: 'Ugar',
  Vai: 'Vaii',
  Warang_Citi: 'Wara',
  Yi: 'Yiii',
  Zanabazar_Square: 'Zanb',
};

const SCRIPT_VALUE_ALIASES_TO_VALUE = inverseMap(SCRIPT_VALUE_TO_ALIASES);

function inverseMap(data) {
  const inverse = {};

  for (let name in data) {
    if (!data.hasOwnProperty(name)) {
      continue;
    }
    const value = data[name];
    if (Array.isArray(value)) {
      for (let i = 0; i < value.length; i++) {
        inverse[value[i]] = name;
      }
    } else {
      inverse[value] = name;
    }
  }

  return inverse;
}

function isValidName(name) {
  return (
    NON_BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) ||
    BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function isValidValue(name, value) {
  if (isGeneralCategoryName(name)) {
    return isGeneralCategoryValue(value);
  }

  if (isScriptCategoryName(name)) {
    return isScriptCategoryValue(value);
  }

  return false;
}

function isAlias(name) {
  return (
    NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function isGeneralCategoryName(name) {
  return name === 'General_Category' || name == 'gc';
}

function isScriptCategoryName(name) {
  return (
    name === 'Script' ||
    name === 'Script_Extensions' ||
    name === 'sc' ||
    name === 'scx'
  );
}

function isGeneralCategoryValue(value) {
  return (
    GENERAL_CATEGORY_VALUE_TO_ALIASES.hasOwnProperty(value) ||
    GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)
  );
}

function isScriptCategoryValue(value) {
  return (
    SCRIPT_VALUE_TO_ALIASES.hasOwnProperty(value) ||
    SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)
  );
}

function isBinaryPropertyName(name) {
  return (
    BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function getCanonicalName(name) {
  if (NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
    return NON_BINARY_ALIASES_TO_PROP_NAMES[name];
  }

  if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
    return BINARY_ALIASES_TO_PROP_NAMES[name];
  }

  return null;
}

function getCanonicalValue(value) {
  if (GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)) {
    return GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES[value];
  }

  if (SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)) {
    return SCRIPT_VALUE_ALIASES_TO_VALUE[value];
  }

  if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(value)) {
    return BINARY_ALIASES_TO_PROP_NAMES[value];
  }

  return null;
}

module.exports = {
  isAlias,
  isValidName,
  isValidValue,
  isGeneralCategoryValue,
  isScriptCategoryValue,
  isBinaryPropertyName,
  getCanonicalName,
  getCanonicalValue,

  NON_BINARY_PROP_NAMES_TO_ALIASES,
  NON_BINARY_ALIASES_TO_PROP_NAMES,

  BINARY_PROP_NAMES_TO_ALIASES,
  BINARY_ALIASES_TO_PROP_NAMES,

  GENERAL_CATEGORY_VALUE_TO_ALIASES,
  GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES,

  SCRIPT_VALUE_TO_ALIASES,
  SCRIPT_VALUE_ALIASES_TO_VALUE,
};
<commit_msg>Update Unicode property lists per Unicode v11<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

const NON_BINARY_PROP_NAMES_TO_ALIASES = {
  General_Category: 'gc',
  Script: 'sc',
  Script_Extensions: 'scx',
};

const NON_BINARY_ALIASES_TO_PROP_NAMES = inverseMap(
  NON_BINARY_PROP_NAMES_TO_ALIASES
);

const BINARY_PROP_NAMES_TO_ALIASES = {
  ASCII: 'ASCII',
  ASCII_Hex_Digit: 'AHex',
  Alphabetic: 'Alpha',
  Any: 'Any',
  Assigned: 'Assigned',
  Bidi_Control: 'Bidi_C',
  Bidi_Mirrored: 'Bidi_M',
  Case_Ignorable: 'CI',
  Cased: 'Cased',
  Changes_When_Casefolded: 'CWCF',
  Changes_When_Casemapped: 'CWCM',
  Changes_When_Lowercased: 'CWL',
  Changes_When_NFKC_Casefolded: 'CWKCF',
  Changes_When_Titlecased: 'CWT',
  Changes_When_Uppercased: 'CWU',
  Dash: 'Dash',
  Default_Ignorable_Code_Point: 'DI',
  Deprecated: 'Dep',
  Diacritic: 'Dia',
  Emoji: 'Emoji',
  Emoji_Component: 'Emoji_Component',
  Emoji_Modifier: 'Emoji_Modifier',
  Emoji_Modifier_Base: 'Emoji_Modifier_Base',
  Emoji_Presentation: 'Emoji_Presentation',
  Extended_Pictographic: 'Extended_Pictographic',
  Extender: 'Ext',
  Grapheme_Base: 'Gr_Base',
  Grapheme_Extend: 'Gr_Ext',
  Hex_Digit: 'Hex',
  IDS_Binary_Operator: 'IDSB',
  IDS_Trinary_Operator: 'IDST',
  ID_Continue: 'IDC',
  ID_Start: 'IDS',
  Ideographic: 'Ideo',
  Join_Control: 'Join_C',
  Logical_Order_Exception: 'LOE',
  Lowercase: 'Lower',
  Math: 'Math',
  Noncharacter_Code_Point: 'NChar',
  Pattern_Syntax: 'Pat_Syn',
  Pattern_White_Space: 'Pat_WS',
  Quotation_Mark: 'QMark',
  Radical: 'Radical',
  Regional_Indicator: 'RI',
  Sentence_Terminal: 'STerm',
  Soft_Dotted: 'SD',
  Terminal_Punctuation: 'Term',
  Unified_Ideograph: 'UIdeo',
  Uppercase: 'Upper',
  Variation_Selector: 'VS',
  White_Space: 'space',
  XID_Continue: 'XIDC',
  XID_Start: 'XIDS',
};

const BINARY_ALIASES_TO_PROP_NAMES = inverseMap(BINARY_PROP_NAMES_TO_ALIASES);

const GENERAL_CATEGORY_VALUE_TO_ALIASES = {
  Cased_Letter: 'LC',
  Close_Punctuation: 'Pe',
  Connector_Punctuation: 'Pc',
  Control: ['Cc', 'cntrl'],
  Currency_Symbol: 'Sc',
  Dash_Punctuation: 'Pd',
  Decimal_Number: ['Nd', 'digit'],
  Enclosing_Mark: 'Me',
  Final_Punctuation: 'Pf',
  Format: 'Cf',
  Initial_Punctuation: 'Pi',
  Letter: 'L',
  Letter_Number: 'Nl',
  Line_Separator: 'Zl',
  Lowercase_Letter: 'Ll',
  Mark: ['M', 'Combining_Mark'],
  Math_Symbol: 'Sm',
  Modifier_Letter: 'Lm',
  Modifier_Symbol: 'Sk',
  Nonspacing_Mark: 'Mn',
  Number: 'N',
  Open_Punctuation: 'Ps',
  Other: 'C',
  Other_Letter: 'Lo',
  Other_Number: 'No',
  Other_Punctuation: 'Po',
  Other_Symbol: 'So',
  Paragraph_Separator: 'Zp',
  Private_Use: 'Co',
  Punctuation: ['P', 'punct'],
  Separator: 'Z',
  Space_Separator: 'Zs',
  Spacing_Mark: 'Mc',
  Surrogate: 'Cs',
  Symbol: 'S',
  Titlecase_Letter: 'Lt',
  Unassigned: 'Cn',
  Uppercase_Letter: 'Lu',
};

const GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES = inverseMap(
  GENERAL_CATEGORY_VALUE_TO_ALIASES
);

const SCRIPT_VALUE_TO_ALIASES = {
  Adlam: 'Adlm',
  Ahom: 'Ahom',
  Anatolian_Hieroglyphs: 'Hluw',
  Arabic: 'Arab',
  Armenian: 'Armn',
  Avestan: 'Avst',
  Balinese: 'Bali',
  Bamum: 'Bamu',
  Bassa_Vah: 'Bass',
  Batak: 'Batk',
  Bengali: 'Beng',
  Bhaiksuki: 'Bhks',
  Bopomofo: 'Bopo',
  Brahmi: 'Brah',
  Braille: 'Brai',
  Buginese: 'Bugi',
  Buhid: 'Buhd',
  Canadian_Aboriginal: 'Cans',
  Carian: 'Cari',
  Caucasian_Albanian: 'Aghb',
  Chakma: 'Cakm',
  Cham: 'Cham',
  Cherokee: 'Cher',
  Common: 'Zyyy',
  Coptic: ['Copt', 'Qaac'],
  Cuneiform: 'Xsux',
  Cypriot: 'Cprt',
  Cyrillic: 'Cyrl',
  Deseret: 'Dsrt',
  Devanagari: 'Deva',
  Dogra: 'Dogr',
  Duployan: 'Dupl',
  Egyptian_Hieroglyphs: 'Egyp',
  Elbasan: 'Elba',
  Ethiopic: 'Ethi',
  Georgian: 'Geor',
  Glagolitic: 'Glag',
  Gothic: 'Goth',
  Grantha: 'Gran',
  Greek: 'Grek',
  Gujarati: 'Gujr',
  Gunjala_Gondi: 'Gong',
  Gurmukhi: 'Guru',
  Han: 'Hani',
  Hangul: 'Hang',
  Hanifi_Rohingya: 'Rohg',
  Hanunoo: 'Hano',
  Hatran: 'Hatr',
  Hebrew: 'Hebr',
  Hiragana: 'Hira',
  Imperial_Aramaic: 'Armi',
  Inherited: ['Zinh', 'Qaai'],
  Inscriptional_Pahlavi: 'Phli',
  Inscriptional_Parthian: 'Prti',
  Javanese: 'Java',
  Kaithi: 'Kthi',
  Kannada: 'Knda',
  Katakana: 'Kana',
  Kayah_Li: 'Kali',
  Kharoshthi: 'Khar',
  Khmer: 'Khmr',
  Khojki: 'Khoj',
  Khudawadi: 'Sind',
  Lao: 'Laoo',
  Latin: 'Latn',
  Lepcha: 'Lepc',
  Limbu: 'Limb',
  Linear_A: 'Lina',
  Linear_B: 'Linb',
  Lisu: 'Lisu',
  Lycian: 'Lyci',
  Lydian: 'Lydi',
  Mahajani: 'Mahj',
  Makasar: 'Maka',
  Malayalam: 'Mlym',
  Mandaic: 'Mand',
  Manichaean: 'Mani',
  Marchen: 'Marc',
  Medefaidrin: 'Medf',
  Masaram_Gondi: 'Gonm',
  Meetei_Mayek: 'Mtei',
  Mende_Kikakui: 'Mend',
  Meroitic_Cursive: 'Merc',
  Meroitic_Hieroglyphs: 'Mero',
  Miao: 'Plrd',
  Modi: 'Modi',
  Mongolian: 'Mong',
  Mro: 'Mroo',
  Multani: 'Mult',
  Myanmar: 'Mymr',
  Nabataean: 'Nbat',
  New_Tai_Lue: 'Talu',
  Newa: 'Newa',
  Nko: 'Nkoo',
  Nushu: 'Nshu',
  Ogham: 'Ogam',
  Ol_Chiki: 'Olck',
  Old_Hungarian: 'Hung',
  Old_Italic: 'Ital',
  Old_North_Arabian: 'Narb',
  Old_Permic: 'Perm',
  Old_Persian: 'Xpeo',
  Old_Sogdian: 'Sogo',
  Old_South_Arabian: 'Sarb',
  Old_Turkic: 'Orkh',
  Oriya: 'Orya',
  Osage: 'Osge',
  Osmanya: 'Osma',
  Pahawh_Hmong: 'Hmng',
  Palmyrene: 'Palm',
  Pau_Cin_Hau: 'Pauc',
  Phags_Pa: 'Phag',
  Phoenician: 'Phnx',
  Psalter_Pahlavi: 'Phlp',
  Rejang: 'Rjng',
  Runic: 'Runr',
  Samaritan: 'Samr',
  Saurashtra: 'Saur',
  Sharada: 'Shrd',
  Shavian: 'Shaw',
  Siddham: 'Sidd',
  SignWriting: 'Sgnw',
  Sinhala: 'Sinh',
  Sogdian: 'Sogd',
  Sora_Sompeng: 'Sora',
  Soyombo: 'Soyo',
  Sundanese: 'Sund',
  Syloti_Nagri: 'Sylo',
  Syriac: 'Syrc',
  Tagalog: 'Tglg',
  Tagbanwa: 'Tagb',
  Tai_Le: 'Tale',
  Tai_Tham: 'Lana',
  Tai_Viet: 'Tavt',
  Takri: 'Takr',
  Tamil: 'Taml',
  Tangut: 'Tang',
  Telugu: 'Telu',
  Thaana: 'Thaa',
  Thai: 'Thai',
  Tibetan: 'Tibt',
  Tifinagh: 'Tfng',
  Tirhuta: 'Tirh',
  Ugaritic: 'Ugar',
  Vai: 'Vaii',
  Warang_Citi: 'Wara',
  Yi: 'Yiii',
  Zanabazar_Square: 'Zanb',
};

const SCRIPT_VALUE_ALIASES_TO_VALUE = inverseMap(SCRIPT_VALUE_TO_ALIASES);

function inverseMap(data) {
  const inverse = {};

  for (let name in data) {
    if (!data.hasOwnProperty(name)) {
      continue;
    }
    const value = data[name];
    if (Array.isArray(value)) {
      for (let i = 0; i < value.length; i++) {
        inverse[value[i]] = name;
      }
    } else {
      inverse[value] = name;
    }
  }

  return inverse;
}

function isValidName(name) {
  return (
    NON_BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) ||
    BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function isValidValue(name, value) {
  if (isGeneralCategoryName(name)) {
    return isGeneralCategoryValue(value);
  }

  if (isScriptCategoryName(name)) {
    return isScriptCategoryValue(value);
  }

  return false;
}

function isAlias(name) {
  return (
    NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function isGeneralCategoryName(name) {
  return name === 'General_Category' || name == 'gc';
}

function isScriptCategoryName(name) {
  return (
    name === 'Script' ||
    name === 'Script_Extensions' ||
    name === 'sc' ||
    name === 'scx'
  );
}

function isGeneralCategoryValue(value) {
  return (
    GENERAL_CATEGORY_VALUE_TO_ALIASES.hasOwnProperty(value) ||
    GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)
  );
}

function isScriptCategoryValue(value) {
  return (
    SCRIPT_VALUE_TO_ALIASES.hasOwnProperty(value) ||
    SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)
  );
}

function isBinaryPropertyName(name) {
  return (
    BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) ||
    BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)
  );
}

function getCanonicalName(name) {
  if (NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
    return NON_BINARY_ALIASES_TO_PROP_NAMES[name];
  }

  if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
    return BINARY_ALIASES_TO_PROP_NAMES[name];
  }

  return null;
}

function getCanonicalValue(value) {
  if (GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)) {
    return GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES[value];
  }

  if (SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)) {
    return SCRIPT_VALUE_ALIASES_TO_VALUE[value];
  }

  if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(value)) {
    return BINARY_ALIASES_TO_PROP_NAMES[value];
  }

  return null;
}

module.exports = {
  isAlias,
  isValidName,
  isValidValue,
  isGeneralCategoryValue,
  isScriptCategoryValue,
  isBinaryPropertyName,
  getCanonicalName,
  getCanonicalValue,

  NON_BINARY_PROP_NAMES_TO_ALIASES,
  NON_BINARY_ALIASES_TO_PROP_NAMES,

  BINARY_PROP_NAMES_TO_ALIASES,
  BINARY_ALIASES_TO_PROP_NAMES,

  GENERAL_CATEGORY_VALUE_TO_ALIASES,
  GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES,

  SCRIPT_VALUE_TO_ALIASES,
  SCRIPT_VALUE_ALIASES_TO_VALUE,
};
<|endoftext|>


<commit_before>'use strict';

const ch = require('./changes_hunter');
const glob = require('glob');
const fs = require('fs-extra');
const _ = require('lodash');
const lh = require('./lineage_hunter');
const Pattern = require('./object_factory').Pattern;
const path = require('path');
const addPattern = require('./addPattern');
const logger = require('./log');
const readDocumentation = require('./readDocumentation');
const lineage_hunter = new lh();
const changes_hunter = new ch();

const pseudopattern_hunter = function() {};

pseudopattern_hunter.prototype.find_pseudopatterns = function(
  currentPattern,
  patternlab
) {
  const paths = patternlab.config.paths;

  //look for a pseudo pattern by checking if there is a file containing same
  //name, with ~ in it, ending in .json, .yml or .yaml
  const needle =
    currentPattern.subdir + '/' + currentPattern.fileName + '~*.(json|yml|yaml)';
  const pseudoPatterns = glob.sync(needle, {
    cwd: paths.source.patterns,
    debug: false,
    nodir: true,
  });

  if (pseudoPatterns.length > 0) {
    for (let i = 0; i < pseudoPatterns.length; i++) {
      logger.debug(
        `Found pseudoPattern variant of ${currentPattern.patternPartial}`
      );

      //we want to do everything we normally would here, except instead read the pseudoPattern data
      let variantFileFullPath;
      let variantFileData;
      try {
        variantFileFullPath = path.resolve(
          paths.source.patterns,
          pseudoPatterns[i]
        );
        variantFileData = fs.readJSONSync(variantFileFullPath);
      } catch (err) {
        logger.warning(
          `There was an error parsing pseudopattern JSON for ${
            currentPattern.relPath
          }`
        );
        logger.warning(err);
      }

      //extend any existing data with variant data
      variantFileData = _.merge(
        {},
        currentPattern.jsonFileData,
        variantFileData
      );

      const variantName = pseudoPatterns[i]
        .substring(pseudoPatterns[i].indexOf('~') + 1)
        .split('.')[0];
      const variantFilePath = path.join(
        currentPattern.subdir,
        currentPattern.fileName + '~' + variantName + '.json'
      );
      const lm = fs.statSync(variantFileFullPath);
      const patternVariant = Pattern.create(
        variantFilePath,
        variantFileData,
        {
          //use the same template as the non-variant
          template: currentPattern.template,
          fileExtension: currentPattern.fileExtension,
          extendedTemplate: currentPattern.extendedTemplate,
          isPseudoPattern: true,
          basePattern: currentPattern,
          stylePartials: currentPattern.stylePartials,
          parameteredPartials: currentPattern.parameteredPartials,

          // Only regular patterns are discovered during iterative walks
          // Need to recompile on data change or template change
          lastModified: Math.max(currentPattern.lastModified, lm.mtime),

          // use the same template engine as the non-variant
          engine: currentPattern.engine,
        },
        patternlab
      );

      changes_hunter.checkBuildState(patternVariant, patternlab);
      patternlab.graph.add(patternVariant);
      patternlab.graph.link(patternVariant, currentPattern);

      //process the companion markdown file if it exists
      readDocumentation(patternVariant, patternlab);

      //find pattern lineage
      lineage_hunter.find_lineage(patternVariant, patternlab);

      //add to patternlab object so we can look these up later.
      addPattern(patternVariant, patternlab);
    }

    // GTP: this is to emulate the behavior of the stale asynced
    // version; when we have time, we can make all the FS calls in here
    // async and see if it helps any, but it didn't when I tried it.
  }
  return Promise.resolve();
};

module.exports = new pseudopattern_hunter();
<commit_msg>Allow the pseudo data read code to handle yaml. Get the pseudo data file extension dynamically.<commit_after>'use strict';

const ch = require('./changes_hunter');
const glob = require('glob');
const fs = require('fs-extra');
const _ = require('lodash');
const lh = require('./lineage_hunter');
const Pattern = require('./object_factory').Pattern;
const path = require('path');
const addPattern = require('./addPattern');
const logger = require('./log');
const readDocumentation = require('./readDocumentation');
const lineage_hunter = new lh();
const changes_hunter = new ch();
const yaml = require('js-yaml');

const pseudopattern_hunter = function() {};

pseudopattern_hunter.prototype.find_pseudopatterns = function(
  currentPattern,
  patternlab
) {
  const paths = patternlab.config.paths;

  //look for a pseudo pattern by checking if there is a file containing same
  //name, with ~ in it, ending in .json, .yml or .yaml
  const needle =
    currentPattern.subdir + '/' + currentPattern.fileName + '~*.{json,yml,yaml}';
  const pseudoPatterns = glob.sync(needle, {
    cwd: paths.source.patterns,
    debug: false,
    nodir: true,
  });

  if (pseudoPatterns.length > 0) {
    for (let i = 0; i < pseudoPatterns.length; i++) {
      logger.debug(
        `Found pseudoPattern variant of ${currentPattern.patternPartial}`
      );

      //we want to do everything we normally would here, except instead read the pseudoPattern data
      let variantFileFullPath;
      let variantFileData;
      try {
        variantFileFullPath = path.resolve(
          paths.source.patterns,
          pseudoPatterns[i]
        );
        variantFileData = yaml.safeLoad(
          fs.readFileSync(variantFileFullPath, 'utf8')
        );
      } catch (err) {
        logger.warning(
          `There was an error parsing pseudopattern JSON for ${
            currentPattern.relPath
          }`
        );
        logger.warning(err);
      }

      //extend any existing data with variant data
      variantFileData = _.merge(
        {},
        currentPattern.jsonFileData,
        variantFileData
      );

      const variantName = pseudoPatterns[i]
        .substring(pseudoPatterns[i].indexOf('~') + 1)
        .split('.')[0];
      const variantExtension = pseudoPatterns[i].split('.').slice(-1).pop();
      const variantFilePath = path.join(
        currentPattern.subdir,
        currentPattern.fileName + '~' + variantName + '.' + variantExtension
      );
      const lm = fs.statSync(variantFileFullPath);
      const patternVariant = Pattern.create(
        variantFilePath,
        variantFileData,
        {
          //use the same template as the non-variant
          template: currentPattern.template,
          fileExtension: currentPattern.fileExtension,
          extendedTemplate: currentPattern.extendedTemplate,
          isPseudoPattern: true,
          basePattern: currentPattern,
          stylePartials: currentPattern.stylePartials,
          parameteredPartials: currentPattern.parameteredPartials,

          // Only regular patterns are discovered during iterative walks
          // Need to recompile on data change or template change
          lastModified: Math.max(currentPattern.lastModified, lm.mtime),

          // use the same template engine as the non-variant
          engine: currentPattern.engine,
        },
        patternlab
      );

      changes_hunter.checkBuildState(patternVariant, patternlab);
      patternlab.graph.add(patternVariant);
      patternlab.graph.link(patternVariant, currentPattern);

      //process the companion markdown file if it exists
      readDocumentation(patternVariant, patternlab);

      //find pattern lineage
      lineage_hunter.find_lineage(patternVariant, patternlab);

      //add to patternlab object so we can look these up later.
      addPattern(patternVariant, patternlab);
    }

    // GTP: this is to emulate the behavior of the stale asynced
    // version; when we have time, we can make all the FS calls in here
    // async and see if it helps any, but it didn't when I tried it.
  }
  return Promise.resolve();
};

module.exports = new pseudopattern_hunter();
<|endoftext|>


<commit_before>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { Animated, Easing, StyleSheet, View } from 'react-native';

import { UserAvatarWithPresence, ComponentWithOverlay, RawLabel, Touchable } from '../common';
import { IconCancel } from '../common/Icons';

const styles = StyleSheet.create({
  wrapper: {
    flexDirection: 'column',
    marginLeft: 8,
    marginVertical: 8,
  },
  text: {
    flex: 1,
    fontSize: 10,
    marginTop: 2,
    textAlign: 'center',
  },
  textFrame: {
    height: 20,
    width: 50,
    flexDirection: 'row',
  },
});

type Props = {|
  email: string,
  avatarUrl: ?string,
  fullName: string,
  onPress: (email: string) => void,
|};

export default class AvatarItem extends PureComponent<Props> {
  animatedValue = new Animated.Value(0);

  componentDidMount() {
    Animated.timing(this.animatedValue, {
      toValue: 1,
      duration: 300,
      useNativeDriver: true,
      easing: Easing.elastic(),
    }).start();
  }

  handlePress = () => {
    const { email, onPress } = this.props;
    Animated.timing(this.animatedValue, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
      easing: Easing.elastic(),
    }).start(() => onPress(email));
  };

  render() {
    const { email, avatarUrl, fullName } = this.props;
    const animatedStyle = {
      transform: [{ scale: this.animatedValue }],
    };
    const firstName = fullName.trim().split(' ')[0];

    return (
      <Animated.View style={[styles.wrapper, animatedStyle]}>
        <Touchable onPress={this.handlePress}>
          <ComponentWithOverlay
            overlaySize={20}
            overlayColor="white"
            overlayPosition="bottom-right"
            overlay={<IconCancel color="gray" size={20} />}
          >
            <UserAvatarWithPresence
              key={email}
              size={50}
              avatarUrl={avatarUrl}
              email={email}
              onPress={this.handlePress}
            />
          </ComponentWithOverlay>
        </Touchable>
        <View style={styles.textFrame}>
          <RawLabel style={styles.text} text={firstName} numberOfLines={1} />
        </View>
      </Animated.View>
    );
  }
}
<commit_msg>AvatarItem: Cut redundant onPress.<commit_after>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { Animated, Easing, StyleSheet, View } from 'react-native';

import { UserAvatarWithPresence, ComponentWithOverlay, RawLabel, Touchable } from '../common';
import { IconCancel } from '../common/Icons';

const styles = StyleSheet.create({
  wrapper: {
    flexDirection: 'column',
    marginLeft: 8,
    marginVertical: 8,
  },
  text: {
    flex: 1,
    fontSize: 10,
    marginTop: 2,
    textAlign: 'center',
  },
  textFrame: {
    height: 20,
    width: 50,
    flexDirection: 'row',
  },
});

type Props = {|
  email: string,
  avatarUrl: ?string,
  fullName: string,
  onPress: (email: string) => void,
|};

export default class AvatarItem extends PureComponent<Props> {
  animatedValue = new Animated.Value(0);

  componentDidMount() {
    Animated.timing(this.animatedValue, {
      toValue: 1,
      duration: 300,
      useNativeDriver: true,
      easing: Easing.elastic(),
    }).start();
  }

  handlePress = () => {
    const { email, onPress } = this.props;
    Animated.timing(this.animatedValue, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
      easing: Easing.elastic(),
    }).start(() => onPress(email));
  };

  render() {
    const { email, avatarUrl, fullName } = this.props;
    const animatedStyle = {
      transform: [{ scale: this.animatedValue }],
    };
    const firstName = fullName.trim().split(' ')[0];

    return (
      <Animated.View style={[styles.wrapper, animatedStyle]}>
        <Touchable onPress={this.handlePress}>
          <ComponentWithOverlay
            overlaySize={20}
            overlayColor="white"
            overlayPosition="bottom-right"
            overlay={<IconCancel color="gray" size={20} />}
          >
            <UserAvatarWithPresence key={email} size={50} avatarUrl={avatarUrl} email={email} />
          </ComponentWithOverlay>
        </Touchable>
        <View style={styles.textFrame}>
          <RawLabel style={styles.text} text={firstName} numberOfLines={1} />
        </View>
      </Animated.View>
    );
  }
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var assert = require('assert');
var crypto = require('crypto');
var dot = require('dot');
var step = require('step');
var MapConfig = require('windshaft').model.MapConfig;
var templateName = require('../../backends/template_maps').templateName;
var QueryTables = require('cartodb-query-tables');

/**
 * @constructor
 * @type {NamedMapMapConfigProvider}
 */
function NamedMapMapConfigProvider(templateMaps, pgConnection, userLimitsApi, namedLayersAdapter,
                                   turboCartoCssAdapter, owner, templateId, config, authToken, params) {
    this.templateMaps = templateMaps;
    this.pgConnection = pgConnection;
    this.userLimitsApi = userLimitsApi;
    this.namedLayersAdapter = namedLayersAdapter;
    this.turboCartoCssAdapter = turboCartoCssAdapter;

    this.owner = owner;
    this.templateName = templateName(templateId);
    this.config = config;
    this.authToken = authToken;
    this.params = params;

    this.cacheBuster = Date.now();

    // use template after call to mapConfig
    this.template = null;

    this.affectedTablesAndLastUpdate = null;

    // providing
    this.err = null;
    this.mapConfig = null;
    this.rendererParams = null;
    this.context = {};
}

module.exports = NamedMapMapConfigProvider;

NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
    if (!!this.err || this.mapConfig !== null) {
        return callback(this.err, this.mapConfig, this.rendererParams, this.context);
    }

    var self = this;

    var mapConfig = null;
    var datasource = null;
    var rendererParams;

    step(
        function getTemplate() {
            self.getTemplate(this);
        },
        function prepareParams(err, tpl) {
            assert.ifError(err);

            self.template = tpl;

            var templateParams = {};
            if (self.config) {
                try {
                    templateParams = _.isString(self.config) ? JSON.parse(self.config) : self.config;
                } catch (e) {
                    throw new Error('malformed config parameter, should be a valid JSON');
                }
            }

            return templateParams;
        },
        function instantiateTemplate(err, templateParams) {
            assert.ifError(err);
            return self.templateMaps.instance(self.template, templateParams);
        },
        function prepareLayergroup(err, _mapConfig) {
            assert.ifError(err);
            var next = this;
            self.namedLayersAdapter.getLayers(self.owner, _mapConfig.layers, self.pgConnection,
                function(err, layers, datasource) {
                    if (err) {
                        return next(err);
                    }

                    if (layers) {
                        _mapConfig.layers = layers;
                    }
                    return next(null, _mapConfig, datasource);
                }
            );
        },
        function parseTurboCartoCss(err, _mapConfig, datasource) {
            assert.ifError(err);
            var next = this;

            self.turboCartoCssAdapter.getLayers(self.owner, _mapConfig.layers, function (err, layers) {
                if (!err && layers) {
                    _mapConfig.layers = layers;
                }

                return next(null, _mapConfig, datasource);
            });
        },
        function beforeLayergroupCreate(err, _mapConfig, _datasource) {
            assert.ifError(err);
            mapConfig = _mapConfig;
            datasource = _datasource;
            rendererParams = _.extend({}, self.params, {
                user: self.owner
            });
            self.setDBParams(self.owner, rendererParams, this);
        },
        function prepareContextLimits(err) {
            assert.ifError(err);
            self.userLimitsApi.getRenderLimits(self.owner, this);
        },
        function cacheAndReturnMapConfig(err, renderLimits) {
            self.err = err;
            self.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, datasource);
            self.rendererParams = rendererParams;
            self.context.limits = renderLimits || {};
            return callback(self.err, self.mapConfig, self.rendererParams, self.context);
        }
    );
};

NamedMapMapConfigProvider.prototype.getTemplate = function(callback) {
    var self = this;

    if (!!this.err || this.template !== null) {
        return callback(this.err, this.template);
    }

    step(
        function getTemplate() {
            self.templateMaps.getTemplate(self.owner, self.templateName, this);
        },
        function checkExists(err, tpl) {
            assert.ifError(err);
            if (!tpl) {
                var notFoundErr = new Error(
                        "Template '" + self.templateName + "' of user '" + self.owner + "' not found"
                );
                notFoundErr.http_status = 404;
                throw notFoundErr;
            }
            return tpl;
        },
        function checkAuthorized(err, tpl) {
            assert.ifError(err);

            var authorized = false;
            try {
                authorized = self.templateMaps.isAuthorized(tpl, self.authToken);
            } catch (err) {
                // we catch to add http_status
                var authorizationFailedErr = new Error('Failed to authorize template');
                authorizationFailedErr.http_status = 403;
                throw authorizationFailedErr;
            }
            if ( ! authorized ) {
                var unauthorizedErr = new Error('Unauthorized template instantiation');
                unauthorizedErr.http_status = 403;
                throw unauthorizedErr;
            }

            return tpl;
        },
        function cacheAndReturnTemplate(err, template) {
            self.err = err;
            self.template = template;
            return callback(self.err, self.template);
        }
    );
};

NamedMapMapConfigProvider.prototype.getKey = function() {
    return this.createKey(false);
};

NamedMapMapConfigProvider.prototype.getCacheBuster = function() {
    return this.cacheBuster;
};

NamedMapMapConfigProvider.prototype.reset = function() {
    this.template = null;

    this.affectedTablesAndLastUpdate = null;

    this.err = null;
    this.mapConfig = null;

    this.cacheBuster = Date.now();
};

NamedMapMapConfigProvider.prototype.filter = function(key) {
    var regex = new RegExp('^' + this.createKey(true) + '.*');
    return key && key.match(regex);
};

// Configure bases for cache keys suitable for string interpolation
var baseKey = '{{=it.dbname}}:{{=it.owner}}:{{=it.templateName}}';
var rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';

var baseKeyTpl = dot.template(baseKey);
var rendererKeyTpl = dot.template(rendererKey);

NamedMapMapConfigProvider.prototype.createKey = function(base) {
    var tplValues = _.defaults({}, this.params, {
        dbname: '',
        owner: this.owner,
        templateName: this.templateName,
        authToken: this.authToken || '',
        configHash: configHash(this.config),
        layer: '',
        scale_factor: 1
    });
    return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
};

function configHash(config) {
    if (!config) {
        return '';
    }
    return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0,8);
}

module.exports.configHash = configHash;

NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) {
    var self = this;
    step(
        function setAuth() {
            self.pgConnection.setDBAuth(cdbuser, params, this);
        },
        function setConn(err) {
            assert.ifError(err);
            self.pgConnection.setDBConn(cdbuser, params, this);
        },
        function finish(err) {
            callback(err);
        }
    );
};

NamedMapMapConfigProvider.prototype.getTemplateName = function() {
    return this.templateName;
};

NamedMapMapConfigProvider.prototype.getAffectedTablesAndLastUpdatedTime = function(callback) {
    var self = this;

    if (this.affectedTablesAndLastUpdate !== null) {
        return callback(null, this.affectedTablesAndLastUpdate);
    }

    step(
        function getMapConfig() {
            self.getMapConfig(this);
        },
        function getSql(err, mapConfig) {
            assert.ifError(err);
            return mapConfig.getLayers().map(function(layer) {
                return layer.options.sql;
            }).join(';');
        },
        function getAffectedTables(err, sql) {
            assert.ifError(err);
            step(
                function getConnection() {
                    self.pgConnection.getConnection(self.owner, this);
                },
                function getAffectedTables(err, connection) {
                    assert.ifError(err);
                    QueryTables.getAffectedTablesFromQuery(connection, sql, this);
                },
                this
            );
        },
        function finish(err, result) {
            self.affectedTablesAndLastUpdate = result;
            return callback(err, result);
        }
    );
};
<commit_msg>Fixed double-check error in turbo-cartocss preprocesing.<commit_after>var _ = require('underscore');
var assert = require('assert');
var crypto = require('crypto');
var dot = require('dot');
var step = require('step');
var MapConfig = require('windshaft').model.MapConfig;
var templateName = require('../../backends/template_maps').templateName;
var QueryTables = require('cartodb-query-tables');

/**
 * @constructor
 * @type {NamedMapMapConfigProvider}
 */
function NamedMapMapConfigProvider(templateMaps, pgConnection, userLimitsApi, namedLayersAdapter,
                                   turboCartoCssAdapter, owner, templateId, config, authToken, params) {
    this.templateMaps = templateMaps;
    this.pgConnection = pgConnection;
    this.userLimitsApi = userLimitsApi;
    this.namedLayersAdapter = namedLayersAdapter;
    this.turboCartoCssAdapter = turboCartoCssAdapter;

    this.owner = owner;
    this.templateName = templateName(templateId);
    this.config = config;
    this.authToken = authToken;
    this.params = params;

    this.cacheBuster = Date.now();

    // use template after call to mapConfig
    this.template = null;

    this.affectedTablesAndLastUpdate = null;

    // providing
    this.err = null;
    this.mapConfig = null;
    this.rendererParams = null;
    this.context = {};
}

module.exports = NamedMapMapConfigProvider;

NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
    if (!!this.err || this.mapConfig !== null) {
        return callback(this.err, this.mapConfig, this.rendererParams, this.context);
    }

    var self = this;

    var mapConfig = null;
    var datasource = null;
    var rendererParams;

    step(
        function getTemplate() {
            self.getTemplate(this);
        },
        function prepareParams(err, tpl) {
            assert.ifError(err);

            self.template = tpl;

            var templateParams = {};
            if (self.config) {
                try {
                    templateParams = _.isString(self.config) ? JSON.parse(self.config) : self.config;
                } catch (e) {
                    throw new Error('malformed config parameter, should be a valid JSON');
                }
            }

            return templateParams;
        },
        function instantiateTemplate(err, templateParams) {
            assert.ifError(err);
            return self.templateMaps.instance(self.template, templateParams);
        },
        function prepareLayergroup(err, _mapConfig) {
            assert.ifError(err);
            var next = this;
            self.namedLayersAdapter.getLayers(self.owner, _mapConfig.layers, self.pgConnection,
                function(err, layers, datasource) {
                    if (err) {
                        return next(err);
                    }

                    if (layers) {
                        _mapConfig.layers = layers;
                    }
                    return next(null, _mapConfig, datasource);
                }
            );
        },
        function parseTurboCartoCss(err, _mapConfig, datasource) {
            assert.ifError(err);
            var next = this;

            self.turboCartoCssAdapter.getLayers(self.owner, _mapConfig.layers, function (err, layers) {
                if (err) {
                    return next(err);
                }

                if (layers) {
                    _mapConfig.layers = layers;
                }

                return next(null, _mapConfig, datasource);
            });
        },
        function beforeLayergroupCreate(err, _mapConfig, _datasource) {
            assert.ifError(err);
            mapConfig = _mapConfig;
            datasource = _datasource;
            rendererParams = _.extend({}, self.params, {
                user: self.owner
            });
            self.setDBParams(self.owner, rendererParams, this);
        },
        function prepareContextLimits(err) {
            assert.ifError(err);
            self.userLimitsApi.getRenderLimits(self.owner, this);
        },
        function cacheAndReturnMapConfig(err, renderLimits) {
            self.err = err;
            self.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, datasource);
            self.rendererParams = rendererParams;
            self.context.limits = renderLimits || {};
            return callback(self.err, self.mapConfig, self.rendererParams, self.context);
        }
    );
};

NamedMapMapConfigProvider.prototype.getTemplate = function(callback) {
    var self = this;

    if (!!this.err || this.template !== null) {
        return callback(this.err, this.template);
    }

    step(
        function getTemplate() {
            self.templateMaps.getTemplate(self.owner, self.templateName, this);
        },
        function checkExists(err, tpl) {
            assert.ifError(err);
            if (!tpl) {
                var notFoundErr = new Error(
                        "Template '" + self.templateName + "' of user '" + self.owner + "' not found"
                );
                notFoundErr.http_status = 404;
                throw notFoundErr;
            }
            return tpl;
        },
        function checkAuthorized(err, tpl) {
            assert.ifError(err);

            var authorized = false;
            try {
                authorized = self.templateMaps.isAuthorized(tpl, self.authToken);
            } catch (err) {
                // we catch to add http_status
                var authorizationFailedErr = new Error('Failed to authorize template');
                authorizationFailedErr.http_status = 403;
                throw authorizationFailedErr;
            }
            if ( ! authorized ) {
                var unauthorizedErr = new Error('Unauthorized template instantiation');
                unauthorizedErr.http_status = 403;
                throw unauthorizedErr;
            }

            return tpl;
        },
        function cacheAndReturnTemplate(err, template) {
            self.err = err;
            self.template = template;
            return callback(self.err, self.template);
        }
    );
};

NamedMapMapConfigProvider.prototype.getKey = function() {
    return this.createKey(false);
};

NamedMapMapConfigProvider.prototype.getCacheBuster = function() {
    return this.cacheBuster;
};

NamedMapMapConfigProvider.prototype.reset = function() {
    this.template = null;

    this.affectedTablesAndLastUpdate = null;

    this.err = null;
    this.mapConfig = null;

    this.cacheBuster = Date.now();
};

NamedMapMapConfigProvider.prototype.filter = function(key) {
    var regex = new RegExp('^' + this.createKey(true) + '.*');
    return key && key.match(regex);
};

// Configure bases for cache keys suitable for string interpolation
var baseKey = '{{=it.dbname}}:{{=it.owner}}:{{=it.templateName}}';
var rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';

var baseKeyTpl = dot.template(baseKey);
var rendererKeyTpl = dot.template(rendererKey);

NamedMapMapConfigProvider.prototype.createKey = function(base) {
    var tplValues = _.defaults({}, this.params, {
        dbname: '',
        owner: this.owner,
        templateName: this.templateName,
        authToken: this.authToken || '',
        configHash: configHash(this.config),
        layer: '',
        scale_factor: 1
    });
    return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
};

function configHash(config) {
    if (!config) {
        return '';
    }
    return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0,8);
}

module.exports.configHash = configHash;

NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) {
    var self = this;
    step(
        function setAuth() {
            self.pgConnection.setDBAuth(cdbuser, params, this);
        },
        function setConn(err) {
            assert.ifError(err);
            self.pgConnection.setDBConn(cdbuser, params, this);
        },
        function finish(err) {
            callback(err);
        }
    );
};

NamedMapMapConfigProvider.prototype.getTemplateName = function() {
    return this.templateName;
};

NamedMapMapConfigProvider.prototype.getAffectedTablesAndLastUpdatedTime = function(callback) {
    var self = this;

    if (this.affectedTablesAndLastUpdate !== null) {
        return callback(null, this.affectedTablesAndLastUpdate);
    }

    step(
        function getMapConfig() {
            self.getMapConfig(this);
        },
        function getSql(err, mapConfig) {
            assert.ifError(err);
            return mapConfig.getLayers().map(function(layer) {
                return layer.options.sql;
            }).join(';');
        },
        function getAffectedTables(err, sql) {
            assert.ifError(err);
            step(
                function getConnection() {
                    self.pgConnection.getConnection(self.owner, this);
                },
                function getAffectedTables(err, connection) {
                    assert.ifError(err);
                    QueryTables.getAffectedTablesFromQuery(connection, sql, this);
                },
                this
            );
        },
        function finish(err, result) {
            self.affectedTablesAndLastUpdate = result;
            return callback(err, result);
        }
    );
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Modal from 'react-modal';
import FileUploader from './FileUploader';
import FileList from './FileList';
import FilePickerHeader from './FilePickerHeader/FilePickerHeader';
import FilePickerFooter from './FilePickerFooter/FilePickerFooter';
import UrlUploader from './UrlUploader';
import withMuiThemeProvider from 'helpers/withMuiThemeProvider';
import styles from './styles.scss';
import _ from 'lodash';
import { DragDropContext, DragDropContextProvider } from 'react-dnd';
import HTML5Backend, { NativeTypes } from 'react-dnd-html5-backend';

import {
    shape,
    string,
    arrayOf,
    oneOfType,
    number,
    bool
  } from 'prop-types';

@withMuiThemeProvider
class FilePicker extends Component {
    state = {
        isOpen: this.props.isOpen,
        selectedTab: "upload",
        files: []
    };

    componentWillReceiveProps = (nextProps) => {
        if (this.state.isOpen !== nextProps.isOpen) {
            this.setState({isOpen: nextProps.isOpen});
        }
    }

    handleRemoveFile = index => {
        let array = this.state.files.slice();
        array.splice(index, 1);
        this.setState({files: array});
    }

    handleFilesSelected = files => {
        this.setState({files: files});
    }

    handleTabChange = value => {
        this.setState({selectedTab: value});
    }

    handleUrlUpload = file => {
        this.setState({files: [file]});
    }

    handleCloseModal = () => {
        this.setState({isOpen:false});
    }

    handleFileDrop = (item, monitor) => {
        if (monitor) {
            let newFiles = this.state.files.slice();
            if (this.props.options && this.props.options.accept) {
                monitor.getItem().files.forEach((file) => {
                    // TODO: validate mime types.
                    newFiles.push(file);
                });
                this.setState({
                    files: newFiles
                });
            } else {
                this.setState({
                    files: newFiles.concat(monitor.getItem().files)
                });
            }
		}
    }

    render () {
        let pickerOptions = this.props.options || {};
        const { FILE } = NativeTypes;
        return (
            <Modal isOpen={this.state.isOpen}
                   className={styles.modalContainer}>

                    <FilePickerHeader selectedTab={this.state.selectedTab}
                                      onSelectTab={this.handleTabChange}
                                      onCloseModal={this.handleCloseModal}/>
                    {
                        this.state.selectedTab === "upload" &&

                                <DragDropContextProvider backend={HTML5Backend}>
                                    <FileUploader onFilesSelected={this.handleFilesSelected}
                                                  acceptedFileTypes={pickerOptions.accept}
                                                  onDrop={this.handleFileDrop}
                                                  accept={[FILE]}/>
                                </DragDropContextProvider>
                                {
                                    this.state.files.length > 0  &&
                                        <FileList files={this.state.files}
                                                onRemoveFile={this.handleRemoveFile}/>
                                }

                    }
                    {
                        this.state.selectedTab === "by-url" &&

                                <UrlUploader onUrlUpload={this.handleUrlUpload}
                                             accept={pickerOptions.accept}/>

                    }
                    <FilePickerFooter onCloseModal={this.handleCloseModal}/>

            </Modal>
        );
    }
};

FilePicker.propTypes = {
    isOpen: bool,
    options: shape({
        width: number,
        height: number,
        accept: oneOfType([arrayOf(string), string])
    })
}

export default DragDropContext(HTML5Backend)(FilePicker);<commit_msg>Remove react-modal and use Material UI Dialog component instead.<commit_after>import React, { Component } from 'react';
import FileUploader from './FileUploader';
import FileList from './FileList';
import FilePickerHeader from './FilePickerHeader/FilePickerHeader';
import FilePickerFooter from './FilePickerFooter/FilePickerFooter';
import UrlUploader from './UrlUploader';
import withMuiThemeProvider from 'helpers/withMuiThemeProvider';
import styles from './styles.scss';
import _ from 'lodash';
import { DragDropContext, DragDropContextProvider } from 'react-dnd';
import HTML5Backend, { NativeTypes } from 'react-dnd-html5-backend';
import Dialog from 'material-ui/Dialog';

import {
    shape,
    string,
    arrayOf,
    oneOfType,
    number,
    bool
  } from 'prop-types';

@withMuiThemeProvider
class FilePicker extends Component {
    state = {
        isOpen: this.props.isOpen,
        selectedTab: "upload",
        files: []
    };

    componentWillReceiveProps = (nextProps) => {
        if (this.state.isOpen !== nextProps.isOpen) {
            this.setState({isOpen: nextProps.isOpen});
        }
    }

    handleRemoveFile = index => {
        let array = this.state.files.slice();
        array.splice(index, 1);
        this.setState({files: array});
    }

    handleFilesSelected = files => {
        this.setState({files: files});
    }

    handleTabChange = value => {
        this.setState({selectedTab: value});
    }

    handleUrlUpload = file => {
        this.setState({files: [file]});
    }

    handleCloseModal = () => {
        this.setState({isOpen:false});
    }

    handleFileDrop = (item, monitor) => {
        if (monitor) {
            let newFiles = this.state.files.slice();
            if (this.props.options && this.props.options.accept) {
                monitor.getItem().files.forEach((file) => {
                    // TODO: validate mime types.
                    newFiles.push(file);
                });
                this.setState({
                    files: newFiles
                });
            } else {
                this.setState({
                    files: newFiles.concat(monitor.getItem().files)
                });
            }
		}
    }

    render () {
        let pickerOptions = this.props.options || {};
        const { FILE } = NativeTypes;
        return (
            <Dialog open={this.state.isOpen}>

                    <FilePickerHeader selectedTab={this.state.selectedTab}
                                      onSelectTab={this.handleTabChange}
                                      onCloseModal={this.handleCloseModal}/>
                    {
                        this.state.selectedTab === "upload" &&

                                <DragDropContextProvider backend={HTML5Backend}>
                                    <FileUploader onFilesSelected={this.handleFilesSelected}
                                                  acceptedFileTypes={pickerOptions.accept}
                                                  onDrop={this.handleFileDrop}
                                                  accept={[FILE]}/>
                                </DragDropContextProvider>
                                {
                                    this.state.files.length > 0  &&
                                        <FileList files={this.state.files}
                                                onRemoveFile={this.handleRemoveFile}/>
                                }

                    }
                    {
                        this.state.selectedTab === "by-url" &&

                                <UrlUploader onUrlUpload={this.handleUrlUpload}
                                             accept={pickerOptions.accept}/>

                    }
                    <FilePickerFooter onCloseModal={this.handleCloseModal}/>

            </Dialog>
        );
    }
};

FilePicker.propTypes = {
    isOpen: bool,
    options: shape({
        width: number,
        height: number,
        accept: oneOfType([arrayOf(string), string])
    })
}

export default DragDropContext(HTML5Backend)(FilePicker);<|endoftext|>


<commit_before>/* Annotorious plugin to use the annotation editor from the
 * ClioPatria image_annotation cpack package as the editor for
 * shapes created in Annotorious.
 *
 * To this, we need to solve three issues:
 *
 * 1. We need to communicate the shape that the user created within
 *    annotorious to all annotation field objects of the cpack
 *    This is why the plugin has a public member variable 'currentShape'
 *    that the cpack fields can access.
 *
 * 2. The cpack data model assumes multiple annotation can be made on
 *    the same target, while annotorious can have only one.
 *    As a result, we need to collect all tags for the same target and
 *    merge them into a single annotation for annotorious.
 *    This is done in the 'cleantags' array, which is indexed on targetId.
 *
 * 3. While the annotorious editor is open, the user can add and remove
 *    individual tags using the cpack user interface. But we can only
 *    add/rm the final merged tag using the annotorious API when the
 *    editor is closed. As a result, we need to do some bookkeeping
 *    ourselves in the _dirtytag variable, and update annotorious when
 *    the editor is closed by flushing out all changes made to the current
 *    dirty tag.
 *
 * Apart from these issues above, the approach is quite straight forward:
 *
 * 1. We simple move the cpack editor as a plugin field into the annotorious
 *    editor.
 * 2. We hijack the cancel/save buttons of annotorious for our own purposes.
 * 3. We bind handler functions on all relevant events generated by annotorious.
 *
 * @author: Jacco van Ossenbruggen
 *
 * */

annotorious.plugin.DenichePlugin = function(opt_config_options) {
	/** @public **/
	this.currentShape = null; // Should be accessible by cpack objects

	/** @private **/
	this._cleantags = [];	// tags annotorious already knows about
	this._dirtytag = null;	// tag annotorious doesn't know yet
	this._saveButtons = {};	 // we have multiple buttons if we have multiple images per page
	this._cancelButtons = {};
}

annotorious.plugin.DenichePlugin.states = { EMPTY:'empty', SOME:'some' };

annotorious.plugin.DenichePlugin.prototype.onInitAnnotator = function(annotator) {
    // move the cpack editor into the annotorious editor:
    var el =  annotator.element;
    var fieldsId = el.getElementsByTagName('img')[0].getAttribute('fields');
    var fieldsEl = document.getElementById(fieldsId);
    annotator.editor.addField(fieldsEl);

    // get the annotorious save and cancel button so we can manipulate them:
    var oSelf = this;
    YUI().use('node', 'event', function(Y) {
    	var node = Y.one(el);
    	oSelf._saveButtons[fieldsId]   = node.one('.annotorious-editor-button-save').getDOMNode();
    	oSelf._cancelButtons[fieldsId] = node.one('.annotorious-editor-button-cancel').getDOMNode();
    	oSelf._saveButtons[fieldsId].innerHTML = "Done";
    });

    // install all handlers on events created by annotorious:
    this.installHandlers();

    if (this._anno.fields) {
	fields = this._anno.fields[fieldsId];
	for(i in fields) {
	    f = fields[i];
	    if (f.get('lazy')) f.getTags();
	}
    }
}

annotorious.plugin.DenichePlugin.prototype.initPlugin = function(anno) {
	this._anno = anno;	// make sure we know annotorious ...
	this._anno._deniche = this; // and annotorious knows us
}

annotorious.plugin.DenichePlugin.prototype.toggleButtons = function(state, fieldsId) {
	console.log(state);
	console.log(fieldsId);
	if (!this._cancelButtons[fieldsId]) return;
	if (!state) state = annotorious.plugin.DenichePlugin.states.SOME;
	if (state == annotorious.plugin.DenichePlugin.states.SOME) {
		this._cancelButtons[fieldsId].style.display="none";
		this._saveButtons[fieldsId].style.display="inline-block";
	} else if (state == annotorious.plugin.DenichePlugin.states.EMPTY) {
		this._saveButtons[fieldsId].style.display="none";
		this._cancelButtons[fieldsId].style.display="inline-block";
	}
}

annotorious.plugin.DenichePlugin.prototype.filterTags = function(targetId, fieldsId) {
	var oSelf = this;
	YUI().use('node', 'event', function(Y) {
		var selector = '#'+ fieldsId + ' li.tagitem';
		if (!fieldsId) selector = 'li.tagitem';
		Y.all(selector).each(function(tagNode) {
			if (targetId == tagNode.getAttribute('targetId')) {
				tagNode.show();
			} else {
				tagNode.hide();
			}
		});
	});
}

annotorious.plugin.DenichePlugin.prototype.removeAnnotation = function (label, targetId) {
	var old = this._dirtytag;
	if (!old) old = this._cleantags[targetId];
	if (old) {
		var annotation = JSON.parse(JSON.stringify(old)); // deep copy
		var index = old.compound_text.indexOf(label);
		annotation.compound_text = old.compound_text.splice(index, 1);
		annotation.text = old.compound_text.join(', ');
		this._dirtytag = annotation;
	}

}

annotorious.plugin.DenichePlugin.prototype.addAnnotation = function (annotation, update) {
	var old = this._dirtytag;
	if (!old) old = this._cleantags[annotation.targetId];
	if (old) {
		// extend new annotation by merging in old one
		annotation.compound_text = old.compound_text;
		annotation.compound_text.push(annotation.text);
		annotation.text = annotation.compound_text.join(', ');
	} else {
		annotation.compound_text = [ annotation.text ];
	}

	if (update) {
		this._cleantags[annotation.targetId] = annotation;
		this._anno.addAnnotation(annotation, old);
	} else {
		this._dirtytag = annotation;
	}
}

annotorious.plugin.DenichePlugin.prototype.flushDirtyAnnotation = function(original) {
		var dirty = this._dirtytag;
		this._dirtytag = null;
		if (dirty) {
			if (dirty.text) {
				this._anno.addAnnotation(dirty,original);
				this._cleantags[dirty.targetId] = dirty;
			} else {
				this._anno.removeAnnotation(original);
				this._cleantags[dirty.targetId] = null;
			}
		}
}

annotorious.plugin.DenichePlugin.prototype.installHandlers = function() {
	var oSelf = this;
	this._anno.addHandler('onSelectionCompleted', function(ev) {
		oSelf.currentShape = ev.shape;
		var currentFieldsId = ev.mouseEvent.target.parentNode.getElementsByTagName('img')[0].getAttribute('fields');
		oSelf.toggleButtons(annotorious.plugin.DenichePlugin.states.EMPTY, currentFieldsId);
	});

	this._anno.addHandler('onEditorShown', function(annotation) {
		console.log('onEditorShown');
		oSelf._dirtytag = null;
		if (annotation && annotation.shapes) {
			oSelf.currentShape = annotation.shapes[0];
			oSelf.toggleButtons(annotorious.plugin.DenichePlugin.states.SOME, annotation.fieldsId);
			oSelf.filterTags(annotation.targetId, annotation.fieldsId); // only show tags for this shape
		} else {
			oSelf.filterTags(null, null);	// hide all tags
		}
	});

	this._anno.addHandler('onAnnotationCreated', function(original) {
		oSelf.flushDirtyAnnotation(original);
	});

	this._anno.addHandler('onAnnotationUpdated', function(original) {
		oSelf.flushDirtyAnnotation(original);
	});
}

anno.addPlugin('DenichePlugin', {});
<commit_msg>comment out old console.log debug statements<commit_after>/* Annotorious plugin to use the annotation editor from the
 * ClioPatria image_annotation cpack package as the editor for
 * shapes created in Annotorious.
 *
 * To this, we need to solve three issues:
 *
 * 1. We need to communicate the shape that the user created within
 *    annotorious to all annotation field objects of the cpack.
 *    This is why the plugin has a public member variable 'currentShape'
 *    that the cpack fields can access.
 *
 * 2. The cpack data model assumes multiple annotation can be made on
 *    the same target, while annotorious can have only one.
 *    As a result, we need to collect all tags for the same target and
 *    merge them into a single annotation for annotorious.
 *    This is done in the 'cleantags' array, which is indexed on targetId.
 *
 * 3. While the annotorious editor is open, the user can add and remove
 *    individual tags using the cpack user interface. But we can only
 *    add/rm the final merged tag using the annotorious API when the
 *    editor is closed. As a result, we need to do some bookkeeping
 *    ourselves in the _dirtytag variable, and update annotorious when
 *    the editor is closed by flushing out all changes made to the current
 *    dirty tag.
 *
 * Apart from these issues above, the approach is quite straight forward:
 *
 * 1. We simple move the cpack editor as a plugin field into the annotorious
 *    editor.
 * 2. We hijack the cancel/save buttons of annotorious for our own purposes.
 * 3. We bind handler functions on all relevant events generated by annotorious.
 *
 * @author: Jacco van Ossenbruggen
 *
 * */

annotorious.plugin.DenichePlugin = function(opt_config_options) {
	/** @public **/
	this.currentShape = null; // Should be accessible by cpack objects

	/** @private **/
	this._cleantags = [];	// tags annotorious already knows about
	this._dirtytag = null;	// tag annotorious doesn't know yet
	this._saveButtons = {};	 // we have multiple buttons if we have multiple images per page
	this._cancelButtons = {};
}

annotorious.plugin.DenichePlugin.states = { EMPTY:'empty', SOME:'some' };

annotorious.plugin.DenichePlugin.prototype.onInitAnnotator = function(annotator) {
    // move the cpack editor into the annotorious editor:
    var el =  annotator.element;
    var fieldsId = el.getElementsByTagName('img')[0].getAttribute('fields');
    var fieldsEl = document.getElementById(fieldsId);
    annotator.editor.addField(fieldsEl);

    // get the annotorious save and cancel button so we can manipulate them:
    var oSelf = this;
    YUI().use('node', 'event', function(Y) {
    	var node = Y.one(el);
    	oSelf._saveButtons[fieldsId]   = node.one('.annotorious-editor-button-save').getDOMNode();
    	oSelf._cancelButtons[fieldsId] = node.one('.annotorious-editor-button-cancel').getDOMNode();
    	oSelf._saveButtons[fieldsId].innerHTML = "Done";
    });

    // install all handlers on events created by annotorious:
    this.installHandlers();

    if (this._anno.fields) {
	fields = this._anno.fields[fieldsId];
	for(i in fields) {
	    f = fields[i];
	    if (f.get('lazy')) f.getTags();
	}
    }
}

annotorious.plugin.DenichePlugin.prototype.initPlugin = function(anno) {
	this._anno = anno;	// make sure we know annotorious ...
	this._anno._deniche = this; // and annotorious knows us
}

annotorious.plugin.DenichePlugin.prototype.toggleButtons = function(state, fieldsId) {
	// console.log('annotorious.plugin.DenichePlugin.prototype.toggleButtons');
	// console.log(state);
	// console.log(fieldsId);
	if (!this._cancelButtons[fieldsId]) return;
	if (!state) state = annotorious.plugin.DenichePlugin.states.SOME;
	if (state == annotorious.plugin.DenichePlugin.states.SOME) {
		this._cancelButtons[fieldsId].style.display="none";
		this._saveButtons[fieldsId].style.display="inline-block";
	} else if (state == annotorious.plugin.DenichePlugin.states.EMPTY) {
		this._saveButtons[fieldsId].style.display="none";
		this._cancelButtons[fieldsId].style.display="inline-block";
	}
}

annotorious.plugin.DenichePlugin.prototype.filterTags = function(targetId, fieldsId) {
	var oSelf = this;
	YUI().use('node', 'event', function(Y) {
		var selector = '#'+ fieldsId + ' li.tagitem';
		if (!fieldsId) selector = 'li.tagitem';
		Y.all(selector).each(function(tagNode) {
			if (targetId == tagNode.getAttribute('targetId')) {
				tagNode.show();
			} else {
				tagNode.hide();
			}
		});
	});
}

annotorious.plugin.DenichePlugin.prototype.removeAnnotation = function (label, targetId) {
	var old = this._dirtytag;
	if (!old) old = this._cleantags[targetId];
	if (old) {
		var annotation = JSON.parse(JSON.stringify(old)); // deep copy
		var index = old.compound_text.indexOf(label);
		annotation.compound_text = old.compound_text.splice(index, 1);
		annotation.text = old.compound_text.join(', ');
		this._dirtytag = annotation;
	}

}

annotorious.plugin.DenichePlugin.prototype.addAnnotation = function (annotation, update) {
	var old = this._dirtytag;
	if (!old) old = this._cleantags[annotation.targetId];
	if (old) {
		// extend new annotation by merging in old one
		annotation.compound_text = old.compound_text;
		annotation.compound_text.push(annotation.text);
		annotation.text = annotation.compound_text.join(', ');
	} else {
		annotation.compound_text = [ annotation.text ];
	}

	if (update) {
		this._cleantags[annotation.targetId] = annotation;
		this._anno.addAnnotation(annotation, old);
	} else {
		this._dirtytag = annotation;
	}
	// this.toggleButtons(annotorious.plugin.DenichePlugin.states.SOME, annotation.fieldsId);
}

annotorious.plugin.DenichePlugin.prototype.flushDirtyAnnotation = function(original) {
		var dirty = this._dirtytag;
		this._dirtytag = null;
		if (dirty) {
			if (dirty.text) {
				this._anno.addAnnotation(dirty,original);
				this._cleantags[dirty.targetId] = dirty;
			} else {
				this._anno.removeAnnotation(original);
				this._cleantags[dirty.targetId] = null;
			}
		}
}

annotorious.plugin.DenichePlugin.prototype.installHandlers = function() {
	var oSelf = this;
	this._anno.addHandler('onSelectionCompleted', function(ev) {
		oSelf.currentShape = ev.shape;
		var currentFieldsId = ev.mouseEvent.target.parentNode.getElementsByTagName('img')[0].getAttribute('fields');
		oSelf.toggleButtons(annotorious.plugin.DenichePlugin.states.EMPTY, currentFieldsId);
	});

	this._anno.addHandler('onEditorShown', function(annotation) {
		// console.log('onEditorShown');
		oSelf._dirtytag = null;
		if (annotation && annotation.shapes) {
			oSelf.currentShape = annotation.shapes[0];
			oSelf.toggleButtons(annotorious.plugin.DenichePlugin.states.SOME, annotation.fieldsId);
			oSelf.filterTags(annotation.targetId, annotation.fieldsId); // only show tags for this shape
		} else {
			oSelf.filterTags(null, null);	// hide all tags
		}
	});

	this._anno.addHandler('onAnnotationCreated', function(original) {
		oSelf.flushDirtyAnnotation(original);
	});

	this._anno.addHandler('onAnnotationUpdated', function(original) {
		oSelf.flushDirtyAnnotation(original);
	});
}

anno.addPlugin('DenichePlugin', {});
<|endoftext|>


<commit_before>// Chromecast (Eureka Dongle) media player: www.google.com/chromecast

var Dongle      = require('eureka-dongle')
  , util        = require('util')
  , devices     = require('./../../core/device')
  , steward     = require('./../../core/steward')
  , utility     = require('./../../core/utility')
  , media       = require('./../device-media')
  , discovery   = require('./../../discovery/discovery-ssdp')
  ;

// var logger = media.logger;

var Chromecast = exports.Device = function(deviceID, deviceUID, info) {
  var self;
  self = this;

  self.whatami = '/device/media/chromecast/video';
  self.deviceID = deviceID.toString();
  self.deviceUID = deviceUID;
  self.name = info.device.name;
  self.getName();

  self.url = info.url;
  self.sid = null;
  self.seq = 0;

  self.chromecast = new Dongle(info.url);
  self.status = 'idle';
  self.changed();
  self.refreshID = null;
};
util.inherits(Chromecast, media.Device);

Chromecast.operations = {
  'stop' : function(device, params) {/* jshint unused: false */
    device.stop('YouTube');
  }
, 'play' : function(device, params) {/* jshint unused: false */
    if (params.url) {
      device.start('YouTube', params.url);
    }
  }
};

Chromecast.prototype.perform = function(self, taskID, perform, parameter) {
  var params;
  try { params = JSON.parse(parameter); } catch(e) {}

  if (!!Chromecast.operations[perform]) {
    Chromecast.operations[perform](this.chromecast, params);
    return steward.performed(taskID);
  }

  return devices.perform(self, taskID, perform, parameter);
};

var validate_perform = function(perform, parameter) {

  var params, result = { invalid: [], requires: [] };

  try { params = JSON.parse(parameter); } catch(ex) { params = {}; }

  if (perform === 'play' && !params.url) {
    result.requires.push('url');
  }

  if (!!Chromecast.operations[perform]) return result;

  return devices.validate_perform(perform, parameter);
};

exports.start = function() {
  steward.actors.device.media.chromecast = steward.actors.device.media.chromecast ||
      { $info     : { type: '/device/media/chromecast' } };

  steward.actors.device.media.chromecast.video =
      { $info     : { type       : '/device/media/chromecast/video'
                    , observe    : [ ]
                    , perform    : [
                                     'play'
                                   , 'stop'
                                   ]
                    , properties : { name    : true
                                   , status  : [ 'idle' ]
                                   }
                    }
      , $validate : { perform    : validate_perform }
      };
  devices.makers['Eureka Dongle'] = Chromecast;
};
<commit_msg>add status events<commit_after>// Chromecast (Eureka Dongle) media player: www.google.com/chromecast

var Dongle      = require('eureka-dongle')
  , util        = require('util')
  , devices     = require('./../../core/device')
  , steward     = require('./../../core/steward')
  , utility     = require('./../../core/utility')
  , media       = require('./../device-media')
  , discovery   = require('./../../discovery/discovery-ssdp')
  ;

// var logger = media.logger;

var Chromecast = exports.Device = function(deviceID, deviceUID, info) {
  var self;
  self = this;

  self.whatami = '/device/media/chromecast/video';
  self.deviceID = deviceID.toString();
  self.deviceUID = deviceUID;
  self.name = info.device.name;
  self.getName();

  self.url = info.url;
  self.sid = null;
  self.seq = 0;

  self.chromecast = new Dongle(info.url);
  self.status = 'idle';
  self.changed();
  self.refreshID = null;

  self.chromecast.start();

  self.chromecast.on('status', function(data) {
    data = data[1];

    if (data.status.state) {
      switch (data.status.state) {

        case 2:
          self.status = 'playing';
        break;

        default:
          self.status = 'idle';
        break;
      }
    }

    self.info = {
      track : {
      }
    };

    if (data.status.title) {
      self.info.track.title = data.status.title;
    }

    if (data.status.current_time) {
      self.info.track.position = data.status.current_time * 1000;
    }

    if (data.status.duration) {
      self.info.track.duration = data.status.duration * 1000;
    }

    self.info.muted = data.status.muted ? 'on' : 'off';
    self.info.volume = data.status.volume;

    self.changed();
  });

};
util.inherits(Chromecast, media.Device);

Chromecast.operations = {
  'stop' : function(device, params) {/* jshint unused: false */
    device.stop('YouTube');
  }
, 'play' : function(device, params) {/* jshint unused: false */
    if (params.url) {
      device.start('YouTube', params.url);
    }
  }
};

Chromecast.prototype.perform = function(self, taskID, perform, parameter) {
  var params;
  try { params = JSON.parse(parameter); } catch(e) {}

  if (!!Chromecast.operations[perform]) {
    Chromecast.operations[perform](this.chromecast, params);
    return steward.performed(taskID);
  }

  return devices.perform(self, taskID, perform, parameter);
};

var validate_perform = function(perform, parameter) {

  var params, result = { invalid: [], requires: [] };

  try { params = JSON.parse(parameter); } catch(ex) { params = {}; }

  if (perform === 'play' && !params.url) {
    result.requires.push('url');
  }

  if (!!Chromecast.operations[perform]) return result;

  return devices.validate_perform(perform, parameter);
};

exports.start = function() {
  steward.actors.device.media.chromecast = steward.actors.device.media.chromecast ||
      { $info     : { type: '/device/media/chromecast' } };

  steward.actors.device.media.chromecast.video =
      { $info     : { type       : '/device/media/chromecast/video'
                    , observe    : [ ]
                    , perform    : [
                                     'play'
                                   , 'stop'
                                   ]
                    , properties : { name    : true
                                   , status  : [ 'idle', 'playing' ]
                                   , track   : { title       : true
                                               , position    : 'milliseconds'
                                               , duration    : 'milliseconds'
                                               }
                                   , volume  : 'percentage'
                                   , muted   : [ 'on', 'off' ]
                                   }
                    }
      , $validate : { perform    : validate_perform }
      };
  devices.makers['Eureka Dongle'] = Chromecast;
};
<|endoftext|>


<commit_before>var Lang = A.Lang,
	isFunction = Lang.isFunction,

	DateMath = A.DataType.DateMath,

	_DOT = '.',

	COL = 'col',
	DATA = 'data',
	DATE = 'date',
	FIRST_DAY_OF_WEEK = 'firstDayOfWeek',
	LOCALE = 'locale',
	MONTH = 'month',
	NOMONTH = 'nomonth',
	SCHEDULER = 'scheduler',
	TABLE = 'table',
	TABLE_ROW_CONTAINER = 'tableRowContainer',
	TITLE = 'title',
	VIEW_DATE = 'viewDate',

	SCHEDULER_VIEW = 'scheduler-view',
	SCHEDULER_VIEW_MONTH = 'scheduler-view-month',

	getCN = A.getClassName,

	CSS_SVM_TABLE_DATA_COL_NOMONTH = getCN(SCHEDULER_VIEW_MONTH, TABLE, DATA, COL, NOMONTH),
	CSS_SVT_TABLE_DATA_COL_TITLE = getCN(SCHEDULER_VIEW, TABLE, DATA, COL, TITLE);

var SchedulerMonthView = A.Component.create({
	NAME: SCHEDULER_VIEW_MONTH,

	ATTRS: {
		displayDaysInterval: {
			readOnly: true,
			value: 42
		},

		name: {
			value: MONTH
		},

		navigationDateFormatter: {
			value: function(date) {
				var instance = this;
				var scheduler = instance.get(SCHEDULER);

				return A.DataType.Date.format(
					date,
					{
						format: '%B %Y',
						locale: scheduler.get(LOCALE)
					}
				);
			},
			validator: isFunction
		}
	},

	EXTENDS: A.SchedulerTableView,

	prototype: {
		getAdjustedViewDate: function(val) {
			var instance = this;

			return DateMath.findMonthStart(val);
		},

		getNextDate: function() {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var date = scheduler.get(DATE);

			return DateMath.add(date, DateMath.MONTH, 1);
		},

		getPrevDate: function() {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var date = scheduler.get(DATE);

			return DateMath.subtract(date, DateMath.MONTH, 1);
		},

		plotEvents: function() {
			var instance = this;

			A.SchedulerMonthView.superclass.plotEvents.apply(instance, arguments);

			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			var monthEnd = DateMath.findMonthEnd(viewDate);
			var monthStart = DateMath.findMonthStart(viewDate);

			var currentIntervalStart = instance._findCurrentIntervalStart();

			var colTitleNodes = instance[TABLE_ROW_CONTAINER].all(_DOT+CSS_SVT_TABLE_DATA_COL_TITLE);

			colTitleNodes.each(function(colTitleNode, index) {
				var celDate = DateMath.add(currentIntervalStart, DateMath.DAY, index);

				if (DateMath.before(celDate, monthStart) || DateMath.after(celDate, monthEnd)) {
					colTitleNode.addClass(CSS_SVM_TABLE_DATA_COL_NOMONTH);
				}
			});
		},

		_findCurrentIntervalStart: function() {
			var instance = this;
			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			return instance._findFirstDayOfWeek(viewDate);
		},

		_findFirstDayOfWeek: function(date) {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var firstDayOfWeek = scheduler.get(FIRST_DAY_OF_WEEK);

			return DateMath.getFirstDayOfWeek(date, firstDayOfWeek);
		}

	}
});

A.SchedulerMonthView = SchedulerMonthView;<commit_msg>AUI-761Month view getNextDate/getPrevDate should use view date to calculate<commit_after>var Lang = A.Lang,
	isFunction = Lang.isFunction,

	DateMath = A.DataType.DateMath,

	_DOT = '.',

	COL = 'col',
	DATA = 'data',
	DATE = 'date',
	FIRST_DAY_OF_WEEK = 'firstDayOfWeek',
	LOCALE = 'locale',
	MONTH = 'month',
	NOMONTH = 'nomonth',
	SCHEDULER = 'scheduler',
	TABLE = 'table',
	TABLE_ROW_CONTAINER = 'tableRowContainer',
	TITLE = 'title',
	VIEW_DATE = 'viewDate',

	SCHEDULER_VIEW = 'scheduler-view',
	SCHEDULER_VIEW_MONTH = 'scheduler-view-month',

	getCN = A.getClassName,

	CSS_SVM_TABLE_DATA_COL_NOMONTH = getCN(SCHEDULER_VIEW_MONTH, TABLE, DATA, COL, NOMONTH),
	CSS_SVT_TABLE_DATA_COL_TITLE = getCN(SCHEDULER_VIEW, TABLE, DATA, COL, TITLE);

var SchedulerMonthView = A.Component.create({
	NAME: SCHEDULER_VIEW_MONTH,

	ATTRS: {
		displayDaysInterval: {
			readOnly: true,
			value: 42
		},

		name: {
			value: MONTH
		},

		navigationDateFormatter: {
			value: function(date) {
				var instance = this;
				var scheduler = instance.get(SCHEDULER);

				return A.DataType.Date.format(
					date,
					{
						format: '%B %Y',
						locale: scheduler.get(LOCALE)
					}
				);
			},
			validator: isFunction
		}
	},

	EXTENDS: A.SchedulerTableView,

	prototype: {
		getAdjustedViewDate: function(val) {
			var instance = this;

			return DateMath.findMonthStart(val);
		},

		getNextDate: function() {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			return DateMath.add(viewDate, DateMath.MONTH, 1);
		},

		getPrevDate: function() {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			return DateMath.subtract(viewDate, DateMath.MONTH, 1);
		},

		plotEvents: function() {
			var instance = this;

			A.SchedulerMonthView.superclass.plotEvents.apply(instance, arguments);

			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			var monthEnd = DateMath.findMonthEnd(viewDate);
			var monthStart = DateMath.findMonthStart(viewDate);

			var currentIntervalStart = instance._findCurrentIntervalStart();

			var colTitleNodes = instance[TABLE_ROW_CONTAINER].all(_DOT+CSS_SVT_TABLE_DATA_COL_TITLE);

			colTitleNodes.each(function(colTitleNode, index) {
				var celDate = DateMath.add(currentIntervalStart, DateMath.DAY, index);

				if (DateMath.before(celDate, monthStart) || DateMath.after(celDate, monthEnd)) {
					colTitleNode.addClass(CSS_SVM_TABLE_DATA_COL_NOMONTH);
				}
			});
		},

		_findCurrentIntervalStart: function() {
			var instance = this;
			var scheduler = instance.get(SCHEDULER);
			var viewDate = scheduler.get(VIEW_DATE);

			return instance._findFirstDayOfWeek(viewDate);
		},

		_findFirstDayOfWeek: function(date) {
			var instance = this;

			var scheduler = instance.get(SCHEDULER);
			var firstDayOfWeek = scheduler.get(FIRST_DAY_OF_WEEK);

			return DateMath.getFirstDayOfWeek(date, firstDayOfWeek);
		}

	}
});

A.SchedulerMonthView = SchedulerMonthView;<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';
import { createBucket } from './lib/grid/createBucket';
import { createOnAfterUpload } from './lib/fsHooks/createOnAfterUpload';
import { createInterceptDownload } from './lib/fsHooks/createInterceptDownload';
import { createOnAfterRemove } from './lib/fsHooks/createOnAfterRemove';

let attachmentBucket;
if (Meteor.isServer) {
  attachmentBucket = createBucket('attachments');
}

const insertActivity = (fileObj, activityType) =>
  Activities.insert({
    userId: fileObj.userId,
    type: 'card',
    activityType,
    attachmentId: fileObj._id,
    // this preserves the name so that notifications can be meaningful after
    // this file is removed
    attachmentName: fileObj.name,
    boardId: fileObj.meta.boardId,
    cardId: fileObj.meta.cardId,
    listId: fileObj.meta.listId,
    swimlaneId: fileObj.meta.swimlaneId,
  });

// XXX Enforce a schema for the Attachments FilesCollection
// see: https://github.com/VeliovGroup/Meteor-Files/wiki/Schema

const Attachments = new FilesCollection({
  debug: false, // Change to `true` for debugging
  collectionName: 'attachments',
  allowClientCode: false,
  onAfterUpload(fileRef) {
    createOnAfterUpload(attachmentBucket)(fileRef);
    // If the attachment doesn't have a source field
    // or its source is different than import
    if (!fileRef.meta.source || fileRef.meta.source !== 'import') {
      // Add activity about adding the attachment
      insertActivity(fileRef, 'addAttachment');
    }
  },
  interceptDownload: createInterceptDownload(attachmentBucket),
  onAfterRemove(files) {
    createOnAfterRemove(attachmentBucket)(files);
    files.forEach(fileObj => {
      insertActivity(fileObj, 'deleteAttachment');
    });
  },
  // We authorize the attachment download either:
  // - if the board is public, everyone (even unconnected) can download it
  // - if the board is private, only board members can download it
  protected(fileObj) {
    const board = Boards.findOne(fileObj.meta.boardId);
    if (board.isPublic()) {
      return true;
    }
    return board.hasMember(this.userId);
  },
});

if (Meteor.isServer) {
  Attachments.allow({
    insert(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    update(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    remove(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    fetch: ['meta'],
  });

  Meteor.startup(() => {
    Attachments.collection._ensureIndex({ cardId: 1 });
  });
}

export default Attachments;
<commit_msg>fixup! Ref: Avatars to use modern gridfs<commit_after>import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';
import { createBucket } from './lib/grid/createBucket';
import { createOnAfterUpload } from './lib/fsHooks/createOnAfterUpload';
import { createInterceptDownload } from './lib/fsHooks/createInterceptDownload';
import { createOnAfterRemove } from './lib/fsHooks/createOnAfterRemove';

let attachmentBucket;
if (Meteor.isServer) {
  attachmentBucket = createBucket('attachments');
}

const insertActivity = (fileObj, activityType) =>
  Activities.insert({
    userId: fileObj.userId,
    type: 'card',
    activityType,
    attachmentId: fileObj._id,
    // this preserves the name so that notifications can be meaningful after
    // this file is removed
    attachmentName: fileObj.name,
    boardId: fileObj.meta.boardId,
    cardId: fileObj.meta.cardId,
    listId: fileObj.meta.listId,
    swimlaneId: fileObj.meta.swimlaneId,
  });

// XXX Enforce a schema for the Attachments FilesCollection
// see: https://github.com/VeliovGroup/Meteor-Files/wiki/Schema

const Attachments = new FilesCollection({
  debug: false, // Change to `true` for debugging
  collectionName: 'attachments',
  allowClientCode: false,
  onAfterUpload: function onAfterUpload(fileRef) {
    createOnAfterUpload(attachmentBucket).call(this, fileRef);
    // If the attachment doesn't have a source field
    // or its source is different than import
    if (!fileRef.meta.source || fileRef.meta.source !== 'import') {
      // Add activity about adding the attachment
      insertActivity(fileRef, 'addAttachment');
    }
  },
  interceptDownload: createInterceptDownload(attachmentBucket),
  onAfterRemove: function onAfterRemove(files) {
    createOnAfterRemove(attachmentBucket).call(this, files);
    files.forEach(fileObj => {
      insertActivity(fileObj, 'deleteAttachment');
    });
  },
  // We authorize the attachment download either:
  // - if the board is public, everyone (even unconnected) can download it
  // - if the board is private, only board members can download it
  protected(fileObj) {
    const board = Boards.findOne(fileObj.meta.boardId);
    if (board.isPublic()) {
      return true;
    }
    return board.hasMember(this.userId);
  },
});

if (Meteor.isServer) {
  Attachments.allow({
    insert(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    update(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    remove(userId, fileObj) {
      return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
    },
    fetch: ['meta'],
  });

  Meteor.startup(() => {
    Attachments.collection._ensureIndex({ cardId: 1 });
  });
}

export default Attachments;
<|endoftext|>


<commit_before><commit_msg>Added model for tasks<commit_after>'use strict';

/**
* Module dependencies
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;

/**
* Tasks Schema
*/

var taskSchema = new Schema ({
	name: {
		type: String,
		required: true
	},
	completed: {
		type: Boolean,
		required: true
	},
	description: {
		type: String,
		required: false
	},
	assignedMembers: {
		type: Array,
		required: true
	},
	dueDates: {
		type: Date,
		required: true
	}
});

mongoose.model('Tasks',taskSchema);<|endoftext|>