<commit_before>app.Universe = {
  "games": [
    {
      "startingNumberOfLives": 9,
      "name": "Original game",
      "index": 1,
      "planets": [
        {
          "name": "OG Planet 1",
          "index": 1,
          "width": 14,
          "height": 18,
          "boltsToBeCollected": 1,
          "map": "w  w  w  w  w  w  w  w  w  w  w  w  w  w  \n\
w  X  _  _  _  L<s_  _  _  _  _  _  #  _  \n\
w  _  _  _  _  _  _  _  #  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  Lvs_  _  _  _  w  \n\
w  L>s_  +  a  _  _  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  _  _  _  _  _  w  \n\
w  _  L^s_  _  _  _  _  0  _  Ee _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  s  _  b  b  _  _  _  R  _  _  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  _  @< w  @v _  _  _  _  _  _  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  a  _  _  _  _  _  w  \n\
w  @> _  _  _  _  w  B| _  _  _  _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  w  w  "
        },
        {
          "name": "OG Planet 2",
          "index": 2,
          "width": 14,
          "height": 18,
          "boltsToBeCollected": 2,
          "map": "w  w  w  w  w  w  w  w  w  w  w  w  w  w  \n\
w  X  _  _  _  L<s_  _  _  _  _  _  #  _  \n\
w  _  _  _  _  _  _  _  #  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  Lvs_  _  _  _  w  \n\
w  L>s_  +  a  _  _  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  _  _  _  _  _  w  \n\
w  _  L^s_  _  _  _  _  0  _  Ee _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  s  _  b  b  _  _  _  R  _  _  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  _  @< w  @v _  _  _  _  _  _  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  a  _  _  _  _  _  w  \n\
w  @> _  _  _  _  w  B| _  _  _  _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  w  w  "
        }
      ]
    },
    {
      "startingNumberOfLives": 9,
      "name": "Game 2",
      "index": 2,
      "planets": [
        {
          "name": "Planet 1",
          "index": 1,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        },
        {
          "name": "Planet 2",
          "index": 2,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        },
        {
          "name": "Planet 3",
          "index": 3,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        }
      ]
    }
  ]
}<commit_msg>fix blaster notation in universe<commit_after>app.Universe = {
  "games": [
    {
      "startingNumberOfLives": 9,
      "name": "Original game",
      "index": 1,
      "planets": [
        {
          "name": "OG Planet 1",
          "index": 1,
          "width": 14,
          "height": 18,
          "boltsToBeCollected": 1,
          "map": "w  w  w  w  w  w  w  w  w  w  w  w  w  w  \n\
w  X  _  _  _  L<s_  _  _  _  _  _  #  _  \n\
w  _  _  _  _  _  _  _  #  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  Lvs_  _  _  _  w  \n\
w  L>s_  +  a  _  _  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  _  _  _  _  _  w  \n\
w  _  L^s_  _  _  _  _  0  _  Ee _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  s  _  b  b  _  _  _  R  _  _  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  _  L<bw  Lvb_  _  _  _  _  _  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  a  _  _  _  _  _  w  \n\
w  L>b_  _  _  _  w  B| _  _  _  _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  w  w  "
        },
        {
          "name": "OG Planet 2",
          "index": 2,
          "width": 14,
          "height": 18,
          "boltsToBeCollected": 2,
          "map": "w  w  w  w  w  w  w  w  w  w  w  w  w  w  \n\
w  X  _  _  _  L<s_  _  _  _  _  _  #  _  \n\
w  _  _  _  _  _  _  _  #  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  Lvs_  _  _  _  w  \n\
w  L>s_  +  a  _  _  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  _  _  _  _  _  _  _  w  \n\
w  _  L^s_  _  _  _  _  0  _  Ee _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  s  _  b  b  _  _  _  R  _  _  \n\
w  w  w  w  w  w  w  w  w  w  w  w  #  w  \n\
w  _  _  _  _  L<bw  Lvb_  _  _  _  _  _  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  _  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  +  _  _  _  _  _  w  \n\
w  _  _  _  _  _  w  a  _  _  _  _  _  w  \n\
w  L>b_  _  _  _  w  B| _  _  _  _  _  w  \n\
w  w  w  w  w  w  w  w  w  w  w  w  w  w  "
        }
      ]
    },
    {
      "startingNumberOfLives": 9,
      "name": "Game 2",
      "index": 2,
      "planets": [
        {
          "name": "Planet 1",
          "index": 1,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        },
        {
          "name": "Planet 2",
          "index": 2,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        },
        {
          "name": "Planet 3",
          "index": 3,
          "width": 16,
          "height": 33,
          "boltsToBeCollected": 5,
          "map": "_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
_.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._..\n\
"
        }
      ]
    }
  ]
}<|endoftext|>


<commit_before>/*
 * Copyright Adam Pritchard 2012
 * MIT License : http://adampritchard.mit-license.org/
 */

 "use strict";
 /*global Components:false, AddonManager:false*/
 /*jshint devel:true*/

/*
 * The background service for supplying preferences to content scripts.
 * From: https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
 *
 * Note that the stored prefs are returned raw (well, after being JSON parsed).
 * No additional processing is done, like filling in default values.
 */

var MozillaOptionsService = {
  listenRequest: function(callback) { // analogue of chrome.extension.onRequest.addListener

    return document.addEventListener('markdown_here-options-query', function(event) {
      var node = event.target, doc = node.ownerDocument;

      return callback(node.getUserData('data'), doc, function(data) {
        if (!node.getUserData('callback')) {
          return doc.documentElement.removeChild(node);
        }

        node.setUserData('response', data, null);

        var listener = doc.createEvent('HTMLEvents');
        listener.initEvent('markdown_here-options-response', true, false);
        return node.dispatchEvent(listener);
      });
    }, false, true);
  },

  requestHandler: function(request, sender, callback) {

    /*
    For information about what `addExposedProps()` is doing and why, see:
    https://blog.mozilla.org/addons/2012/08/20/exposing-objects-to-content-safely/
    The short version is that in Mozilla v17 a security feature was introduced
    (well, enforced) whereby objects shared between background scripts (i.e.,
    this code) and content scripts (i.e., the options page) have to specifically
    indicate which properties are accessible.
    This function basically undoes that by making all properties readable/writable,
    but for our purposes that's okay (this code is effectively relinquishing
    control of the object to the content script).
    This fixes issue #37 (https://github.com/adam-p/markdown-here/issues/37).
    */
    function addExposedProps(obj) {
      var key, i;

      if (typeof(obj) === 'undefined' || obj === null) {
        return;
      }

      // Note that this code path is for Objects and Arrays
      if (typeof(obj) === 'object') {
        if (!('__exposedProps__' in obj)) {
          obj['__exposedProps__'] = {};
        }

        for (key in obj) {
          if (key === '__exposedProps__') {
            continue;
          }
          obj['__exposedProps__'][key] = 'rw';
          addExposedProps(obj[key]);
        }
      }
    }

    var prefs, prefKeys, prefsObj, i;

    prefs = Components.classes['@mozilla.org/preferences-service;1']
                      .getService(Components.interfaces.nsIPrefService)
                      .getBranch('extensions.markdown-here.');

    if (request.action === 'get') {
      prefKeys = prefs.getChildList('');
      prefsObj = {};

      for (i = 0; i < prefKeys.length; i++) {
        try {
          prefsObj[prefKeys[i]] = JSON.parse(prefs.getCharPref(prefKeys[i]));
        }
        catch(e) {
          // Null values and empty strings will result in JSON exceptions
          prefsObj[prefKeys[i]] = null;
        }
      }

      addExposedProps(prefsObj);

      return callback(prefsObj);
    }
    else if (request.action === 'set') {
      for (i in request.obj) {
        prefs.setCharPref(i, JSON.stringify(request.obj[i]));
      }

      if (callback) return callback();
      return;
    }
    else if (request.action === 'clear') {
      if (typeof(request.obj) === 'string') {
        request.obj = [request.obj];
      }

      for (i = 0; i < request.obj.length; i++) {
        prefs.clearUserPref(request.obj[i]);
      }

      if (callback) return callback();
      return;
    }

    return alert('Error: no matching options service action');
  }
};

MozillaOptionsService.listenRequest(MozillaOptionsService.requestHandler);

/*
 * In order to check if this is a new version, etc., we need some code that runs
 * when the application starts. In the case of Thunderbird, our regular overlay
 * only loads when a new message is opened, so we're going to hijack this options
 * overlay code to add some version checks and startup code.
 */
(function() {
  try {
      // Firefox 4 and later; Mozilla 2 and later
      Components.utils.import("resource://gre/modules/AddonManager.jsm");
      AddonManager.getAddonByID("markdown-here@adam.pritchard", function(addon) {
          updateHandler(addon.version);
    });
  }
  catch (ex) {
      // Firefox 3.6 and before; Mozilla 1.9.2 and before
      var em = Components.classes["@mozilla.org/extensions/manager;1"]
               .getService(Components.interfaces.nsIExtensionManager);
      var addon = em.getItemForID("markdown-here@adam.pritchard");
      updateHandler(addon.version);
  }

  function updateHandler(currVer) {

    // I don't know why, but getting the extension prefs -- like last-version
    // -- doesn't seem to work when using prefsServ and providing the full pref
    // name. So we need to use prefsServ to set the sync options and prefsBranch
    // to get and set the extension options.
    var prefsServ = Components.classes['@mozilla.org/preferences-service;1']
                              .getService(Components.interfaces.nsIPrefBranch);
    var prefsBranch = prefsServ.getBranch('extensions.markdown-here.');

    var lastVersion = '';
    try {
      lastVersion = JSON.parse(prefsBranch.getCharPref('last-version'));
    }
    catch (ex) {
    }

    var localFirstRun = false;
    try {
      // No need to assign this to anything. It shouldn't exist on first run.
      JSON.parse(prefsBranch.getCharPref('local-first-run'));
    }
    catch (ex) {
      // It's only the first run if the above throws.
      localFirstRun = true;
    }

    prefsBranch.setCharPref('local-first-run', JSON.stringify(localFirstRun));

    if (currVer !== lastVersion) {
      prefsBranch.setCharPref('last-version', JSON.stringify(currVer));

      // Set the sync flags while we're at it.
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.main-css', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.syntax-css', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.last-version', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.math-enabled', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.math-value', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.hotkey', true);

      // Don't sync this one -- local only.
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.local-first-run', false);

      // This is a bit dirty. If we open the new tab immediately, it will get
      // overwritten when session restore starts creating tabs. So we'll wait a
      // couple of seconds after the last tab is restored to open ours.
      // But we'll also have to make sure we handle the case that no tabs are
      // being restored.

      var timeoutID = null;

      var tabRestored = function() {
        var postTabRestoredOptionsOpen = function() {
          document.removeEventListener('SSTabRestored', tabRestored);
          var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
                                         .getService(Components.interfaces.nsIWindowMediator);
          var optionsUrl = 'resource://markdown_here_common/options.html#changelist';
          if (navigator.userAgent.indexOf('Thunderbird') >= 0 ||
              navigator.userAgent.indexOf('Icedove') >= 0) {
              windowMediator.getMostRecentWindow('mail:3pane')
                            .document.getElementById('tabmail')
                            .openTab('contentTab', {contentPage: optionsUrl});
          }
          else if (navigator.userAgent.indexOf('Postbox') >= 0) {
              /* Haven't yet figured out how to open a tab in Postbox */
          }
          else {
              var win = windowMediator.getMostRecentWindow('navigator:browser');
              win.gBrowser.selectedTab = win.gBrowser.addTab(optionsUrl);
          }
        };

        clearTimeout(timeoutID);
        timeoutID = setTimeout(postTabRestoredOptionsOpen, 2000);
      };
      timeoutID = setTimeout(tabRestored, 1);
      document.addEventListener('SSTabRestored', tabRestored, false);
    }

    if (true || localFirstRun) {
      installButton('nav-bar', 'toolbarButton-markdown_here');

      // Note that we can't add the same button to more than one toolbar.
      // If we wanted to add the button to the addon toolbar, we'd use this
      // line.
      // The 'addon-bar' is available since Firefox 4
      //installButton('addon-bar', 'toolbarButton-markdown_here');
    }
  }

  // From: https://developer.mozilla.org/en-US/docs/Code_snippets/Toolbar?redirectlocale=en-US&redirectslug=Code_snippets%3AToolbar#Adding_button_by_default
  /**
   * Installs the toolbar button with the given ID into the given
   * toolbar, if it is not already present in the document.
   *
   * @param {string} toolbarId The ID of the toolbar to install to.
   * @param {string} id The ID of the button to install.
   * @param {string} afterId The ID of the element to insert after. @optional
   */
  function installButton(toolbarId, id, afterId) {
    if (!document.getElementById(id)) {
      var toolbar, elem;

      toolbar = document.getElementById(toolbarId);

      if (!toolbar) {
        return;
      }

      // If no afterId is given, then append the item to the toolbar
      var before = null;
      if (afterId) {
        elem = document.getElementById(afterId);
        if (elem && elem.parentNode == toolbar) {
          before = elem.nextElementSibling;
        }
      }

      toolbar.insertItem(id, before);
      toolbar.setAttribute('currentset', toolbar.currentSet);
      document.persist(toolbar.id, 'currentset');

      if (toolbarId == 'addon-bar') {
        toolbar.collapsed = false;
      }
    }
  }
})();
<commit_msg>Removed debug code that was forcibly re-adding button to Firefox toolbar on every run<commit_after>/*
 * Copyright Adam Pritchard 2012
 * MIT License : http://adampritchard.mit-license.org/
 */

 "use strict";
 /*global Components:false, AddonManager:false*/
 /*jshint devel:true*/

/*
 * The background service for supplying preferences to content scripts.
 * From: https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
 *
 * Note that the stored prefs are returned raw (well, after being JSON parsed).
 * No additional processing is done, like filling in default values.
 */

var MozillaOptionsService = {
  listenRequest: function(callback) { // analogue of chrome.extension.onRequest.addListener

    return document.addEventListener('markdown_here-options-query', function(event) {
      var node = event.target, doc = node.ownerDocument;

      return callback(node.getUserData('data'), doc, function(data) {
        if (!node.getUserData('callback')) {
          return doc.documentElement.removeChild(node);
        }

        node.setUserData('response', data, null);

        var listener = doc.createEvent('HTMLEvents');
        listener.initEvent('markdown_here-options-response', true, false);
        return node.dispatchEvent(listener);
      });
    }, false, true);
  },

  requestHandler: function(request, sender, callback) {

    /*
    For information about what `addExposedProps()` is doing and why, see:
    https://blog.mozilla.org/addons/2012/08/20/exposing-objects-to-content-safely/
    The short version is that in Mozilla v17 a security feature was introduced
    (well, enforced) whereby objects shared between background scripts (i.e.,
    this code) and content scripts (i.e., the options page) have to specifically
    indicate which properties are accessible.
    This function basically undoes that by making all properties readable/writable,
    but for our purposes that's okay (this code is effectively relinquishing
    control of the object to the content script).
    This fixes issue #37 (https://github.com/adam-p/markdown-here/issues/37).
    */
    function addExposedProps(obj) {
      var key, i;

      if (typeof(obj) === 'undefined' || obj === null) {
        return;
      }

      // Note that this code path is for Objects and Arrays
      if (typeof(obj) === 'object') {
        if (!('__exposedProps__' in obj)) {
          obj['__exposedProps__'] = {};
        }

        for (key in obj) {
          if (key === '__exposedProps__') {
            continue;
          }
          obj['__exposedProps__'][key] = 'rw';
          addExposedProps(obj[key]);
        }
      }
    }

    var prefs, prefKeys, prefsObj, i;

    prefs = Components.classes['@mozilla.org/preferences-service;1']
                      .getService(Components.interfaces.nsIPrefService)
                      .getBranch('extensions.markdown-here.');

    if (request.action === 'get') {
      prefKeys = prefs.getChildList('');
      prefsObj = {};

      for (i = 0; i < prefKeys.length; i++) {
        try {
          prefsObj[prefKeys[i]] = JSON.parse(prefs.getCharPref(prefKeys[i]));
        }
        catch(e) {
          // Null values and empty strings will result in JSON exceptions
          prefsObj[prefKeys[i]] = null;
        }
      }

      addExposedProps(prefsObj);

      return callback(prefsObj);
    }
    else if (request.action === 'set') {
      for (i in request.obj) {
        prefs.setCharPref(i, JSON.stringify(request.obj[i]));
      }

      if (callback) return callback();
      return;
    }
    else if (request.action === 'clear') {
      if (typeof(request.obj) === 'string') {
        request.obj = [request.obj];
      }

      for (i = 0; i < request.obj.length; i++) {
        prefs.clearUserPref(request.obj[i]);
      }

      if (callback) return callback();
      return;
    }

    return alert('Error: no matching options service action');
  }
};

MozillaOptionsService.listenRequest(MozillaOptionsService.requestHandler);

/*
 * In order to check if this is a new version, etc., we need some code that runs
 * when the application starts. In the case of Thunderbird, our regular overlay
 * only loads when a new message is opened, so we're going to hijack this options
 * overlay code to add some version checks and startup code.
 */
(function() {
  try {
      // Firefox 4 and later; Mozilla 2 and later
      Components.utils.import("resource://gre/modules/AddonManager.jsm");
      AddonManager.getAddonByID("markdown-here@adam.pritchard", function(addon) {
          updateHandler(addon.version);
    });
  }
  catch (ex) {
      // Firefox 3.6 and before; Mozilla 1.9.2 and before
      var em = Components.classes["@mozilla.org/extensions/manager;1"]
               .getService(Components.interfaces.nsIExtensionManager);
      var addon = em.getItemForID("markdown-here@adam.pritchard");
      updateHandler(addon.version);
  }

  function updateHandler(currVer) {

    // I don't know why, but getting the extension prefs -- like last-version
    // -- doesn't seem to work when using prefsServ and providing the full pref
    // name. So we need to use prefsServ to set the sync options and prefsBranch
    // to get and set the extension options.
    var prefsServ = Components.classes['@mozilla.org/preferences-service;1']
                              .getService(Components.interfaces.nsIPrefBranch);
    var prefsBranch = prefsServ.getBranch('extensions.markdown-here.');

    var lastVersion = '';
    try {
      lastVersion = JSON.parse(prefsBranch.getCharPref('last-version'));
    }
    catch (ex) {
    }

    var localFirstRun = false;
    try {
      // No need to assign this to anything. It shouldn't exist on first run.
      JSON.parse(prefsBranch.getCharPref('local-first-run'));
    }
    catch (ex) {
      // It's only the first run if the above throws.
      localFirstRun = true;
    }

    prefsBranch.setCharPref('local-first-run', JSON.stringify(localFirstRun));

    if (currVer !== lastVersion) {
      prefsBranch.setCharPref('last-version', JSON.stringify(currVer));

      // Set the sync flags while we're at it.
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.main-css', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.syntax-css', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.last-version', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.math-enabled', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.math-value', true);
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.hotkey', true);

      // Don't sync this one -- local only.
      prefsServ.setBoolPref('services.sync.prefs.sync.extensions.markdown-here.local-first-run', false);

      // This is a bit dirty. If we open the new tab immediately, it will get
      // overwritten when session restore starts creating tabs. So we'll wait a
      // couple of seconds after the last tab is restored to open ours.
      // But we'll also have to make sure we handle the case that no tabs are
      // being restored.

      var timeoutID = null;

      var tabRestored = function() {
        var postTabRestoredOptionsOpen = function() {
          document.removeEventListener('SSTabRestored', tabRestored);
          var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
                                         .getService(Components.interfaces.nsIWindowMediator);
          var optionsUrl = 'resource://markdown_here_common/options.html#changelist';
          if (navigator.userAgent.indexOf('Thunderbird') >= 0 ||
              navigator.userAgent.indexOf('Icedove') >= 0) {
              windowMediator.getMostRecentWindow('mail:3pane')
                            .document.getElementById('tabmail')
                            .openTab('contentTab', {contentPage: optionsUrl});
          }
          else if (navigator.userAgent.indexOf('Postbox') >= 0) {
              /* Haven't yet figured out how to open a tab in Postbox */
          }
          else {
              var win = windowMediator.getMostRecentWindow('navigator:browser');
              win.gBrowser.selectedTab = win.gBrowser.addTab(optionsUrl);
          }
        };

        clearTimeout(timeoutID);
        timeoutID = setTimeout(postTabRestoredOptionsOpen, 2000);
      };
      timeoutID = setTimeout(tabRestored, 1);
      document.addEventListener('SSTabRestored', tabRestored, false);
    }

    if (localFirstRun) {
      installButton('nav-bar', 'toolbarButton-markdown_here');

      // Note that we can't add the same button to more than one toolbar.
      // If we wanted to add the button to the addon toolbar, we'd use this
      // line.
      // The 'addon-bar' is available since Firefox 4
      //installButton('addon-bar', 'toolbarButton-markdown_here');
    }
  }

  // From: https://developer.mozilla.org/en-US/docs/Code_snippets/Toolbar?redirectlocale=en-US&redirectslug=Code_snippets%3AToolbar#Adding_button_by_default
  /**
   * Installs the toolbar button with the given ID into the given
   * toolbar, if it is not already present in the document.
   *
   * @param {string} toolbarId The ID of the toolbar to install to.
   * @param {string} id The ID of the button to install.
   * @param {string} afterId The ID of the element to insert after. @optional
   */
  function installButton(toolbarId, id, afterId) {
    if (!document.getElementById(id)) {
      var toolbar, elem;

      toolbar = document.getElementById(toolbarId);

      if (!toolbar) {
        return;
      }

      // If no afterId is given, then append the item to the toolbar
      var before = null;
      if (afterId) {
        elem = document.getElementById(afterId);
        if (elem && elem.parentNode == toolbar) {
          before = elem.nextElementSibling;
        }
      }

      toolbar.insertItem(id, before);
      toolbar.setAttribute('currentset', toolbar.currentSet);
      document.persist(toolbar.id, 'currentset');

      if (toolbarId == 'addon-bar') {
        toolbar.collapsed = false;
      }
    }
  }
})();
<|endoftext|>


<commit_before>/**
 * @file ext-imagelib.js
 *
 * @license MIT
 *
 * @copyright 2010 Alexis Deveria
 *
 */
export default {
  name: 'imagelib',
  async init ({$, decode64, importLocale, dropXMLInternalSubset}) {
    const imagelibStrings = await importLocale();

    const modularVersion = !('svgEditor' in window) ||
      !window.svgEditor ||
      window.svgEditor.modules !== false;

    const svgEditor = this;

    const {uiStrings, canvas: svgCanvas, curConfig: {extIconsPath}} = svgEditor;

    imagelibStrings.imgLibs = imagelibStrings.imgLibs.map(({name, url, description}) => {
      // Todo: Adopt some standard formatting library like `fluent.js` instead
      url = url
        .replace(/\{path\}/g, extIconsPath)
        .replace(
          /\{modularVersion\}/g,
          modularVersion
            ? (imagelibStrings.moduleEnding || '-es')
            : ''
        );
      return {name, url, description};
    });
    const allowedImageLibOrigins = imagelibStrings.imgLibs.map(({url}) => {
      try {
        return new URL(url).origin;
      } catch (err) {
        return location.origin;
      }
    });

    /**
    *
    * @returns {void}
    */
    function closeBrowser () {
      $('#imgbrowse_holder').hide();
    }

    /**
    * @param {string} url
    * @returns {void}
    */
    function importImage (url) {
      const newImage = svgCanvas.addSVGElementFromJson({
        element: 'image',
        attr: {
          x: 0,
          y: 0,
          width: 0,
          height: 0,
          id: svgCanvas.getNextId(),
          style: 'pointer-events:inherit'
        }
      });
      svgCanvas.clearSelection();
      svgCanvas.addToSelection([newImage]);
      svgCanvas.setImageURL(url);
    }

    const pending = {};

    let mode = 's';
    let multiArr = [];
    let transferStopped = false;
    let preview, submit;

    // Receive `postMessage` data
    window.addEventListener('message', async function ({origin, data: response}) { // eslint-disable-line no-shadow
      if (!response || !['string', 'object'].includes(typeof response)) {
        // Do nothing
        return;
      }
      let id;
      let type;
      try {
        // Todo: This block can be removed (and the above check changed to
        //   insist on an object) if embedAPI moves away from a string to
        //   an object (if IE9 support not needed)
        response = typeof response === 'object' ? response : JSON.parse(response);
        if (response.namespace !== 'imagelib') {
          return;
        }
        if (!allowedImageLibOrigins.includes('*') && !allowedImageLibOrigins.includes(origin)) {
          // Todo: Surface this error to user?
          console.log(`Origin ${origin} not whitelisted for posting to ${window.origin}`); // eslint-disable-line no-console
          return;
        }
        const hasName = 'name' in response;
        const hasHref = 'href' in response;

        if (!hasName && transferStopped) {
          transferStopped = false;
          return;
        }

        if (hasHref) {
          id = response.href;
          response = response.data;
        }

        // Hide possible transfer dialog box
        $('#dialog_box').hide();
        type = hasName
          ? 'meta'
          : response.charAt(0);
      } catch (e) {
        // This block is for backward compatibility (for IAN and Openclipart);
        //   should otherwise return
        if (typeof response === 'string') {
          const char1 = response.charAt(0);

          if (char1 !== '{' && transferStopped) {
            transferStopped = false;
            return;
          }

          if (char1 === '|') {
            const secondpos = response.indexOf('|', 1);
            id = response.substr(1, secondpos - 1);
            response = response.substr(secondpos + 1);
            type = response.charAt(0);
          }
        }
      }

      let entry, curMeta, svgStr, imgStr;
      switch (type) {
      case 'meta': {
        // Metadata
        transferStopped = false;
        curMeta = response;

        // Should be safe to add dynamic property as passed metadata
        pending[curMeta.id] = curMeta; // lgtm [js/remote-property-injection]

        const name = (curMeta.name || 'file');

        const message = uiStrings.notification.retrieving.replace('%s', name);

        if (mode !== 'm') {
          await $.process_cancel(message);
          transferStopped = true;
          // Should a message be sent back to the frame?

          $('#dialog_box').hide();
        } else {
          entry = $('
').text(message).data('id', curMeta.id);
          preview.append(entry);
          curMeta.entry = entry;
        }

        return;
      }
      case '<':
        svgStr = true;
        break;
      case 'd': {
        if (response.startsWith('data:image/svg+xml')) {
          const pre = 'data:image/svg+xml;base64,';
          const src = response.substring(pre.length);
          response = decode64(src);
          svgStr = true;
          break;
        } else if (response.startsWith('data:image/')) {
          imgStr = true;
          break;
        }
      }
      // Else fall through
      default:
        // TODO: See if there's a way to base64 encode the binary data stream
        // const str = 'data:;base64,' + svgedit.utilities.encode64(response, true);

        // Assume it's raw image data
        // importImage(str);

        // Don't give warning as postMessage may have been used by something else
        if (mode !== 'm') {
          closeBrowser();
        } else {
          pending[id].entry.remove();
        }
        // await $.alert('Unexpected data was returned: ' + response, function() {
        //   if (mode !== 'm') {
        //     closeBrowser();
        //   } else {
        //     pending[id].entry.remove();
        //   }
        // });
        return;
      }

      switch (mode) {
      case 's':
        // Import one
        if (svgStr) {
          svgCanvas.importSvgString(response);
        } else if (imgStr) {
          importImage(response);
        }
        closeBrowser();
        break;
      case 'm': {
        // Import multiple
        multiArr.push([(svgStr ? 'svg' : 'img'), response]);
        curMeta = pending[id];
        let title;
        if (svgStr) {
          if (curMeta && curMeta.name) {
            title = curMeta.name;
          } else {
            // Try to find a title
            // `dropXMLInternalSubset` is to help prevent the billion laughs attack
            const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement; // lgtm [js/xml-bomb]
            title = $(xml).children('title').first().text() || '(SVG #' + response.length + ')';
          }
          if (curMeta) {
            preview.children().each(function () {
              if ($(this).data('id') === id) {
                if (curMeta.preview_url) {
                  $(this).html(
                    $('').append(
                      $('').attr('src', curMeta.preview_url),
                      title
                    )
                  );
                } else {
                  $(this).text(title);
                }
                submit.removeAttr('disabled');
              }
            });
          } else {
            preview.append(
              $('
').text(title)
            );
            submit.removeAttr('disabled');
          }
        } else {
          if (curMeta && curMeta.preview_url) {
            title = curMeta.name || '';
            entry = $('').append(
              $('').attr('src', curMeta.preview_url),
              title
            );
          } else {
            entry = $('').attr('src', response);
          }

          if (curMeta) {
            preview.children().each(function () {
              if ($(this).data('id') === id) {
                $(this).html(entry);
                submit.removeAttr('disabled');
              }
            });
          } else {
            preview.append($('
').append(entry));
            submit.removeAttr('disabled');
          }
        }
        break;
      } case 'o': {
        // Open
        if (!svgStr) { break; }
        closeBrowser();
        const ok = await svgEditor.openPrep();
        if (!ok) { return; }
        svgCanvas.clear();
        svgCanvas.setSvgString(response);
        // updateCanvas();
        break;
      }
      }
    }, true);

    /**
    * @param {boolean} show
    * @returns {void}
    */
    function toggleMulti (show) {
      $('#lib_framewrap, #imglib_opts').css({right: (show ? 200 : 10)});
      if (!preview) {
        preview = $('
').css({
          position: 'absolute',
          top: 45,
          right: 10,
          width: 180,
          bottom: 45,
          background: '#fff',
          overflow: 'auto'
        }).insertAfter('#lib_framewrap');

        submit = $('<button disabled>Import selected</button>')
          .appendTo('#imgbrowse')
          .on('click touchend', function () {
            $.each(multiArr, function (i) {
              const type = this[0];
              const data = this[1];
              if (type === 'svg') {
                svgCanvas.importSvgString(data);
              } else {
                importImage(data);
              }
              svgCanvas.moveSelectedElements(i * 20, i * 20, false);
            });
            preview.empty();
            multiArr = [];
            $('#imgbrowse_holder').hide();
          }).css({
            position: 'absolute',
            bottom: 10,
            right: -10
          });
      }

      preview.toggle(show);
      submit.toggle(show);
    }

    /**
    *
    * @returns {void}
    */
    function showBrowser () {
      let browser = $('#imgbrowse');
      if (!browser.length) {
        $('

' +
        '

').insertAfter('#svg_docprops');
        browser = $('#imgbrowse');

        const allLibs = imagelibStrings.select_lib;

        const libOpts = $('
').appendTo(browser);
        const frame = $('').prependTo(browser).hide().wrap('
');

        const header = $('
').prependTo(browser).text(allLibs).css({
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%'
        });

        const cancel = $('<button>' + uiStrings.common.cancel + '</button>')
          .appendTo(browser)
          .on('click touchend', function () {
            $('#imgbrowse_holder').hide();
          }).css({
            position: 'absolute',
            top: 5,
            right: -10
          });

        const leftBlock = $('').css({position: 'absolute', top: 5, left: 10}).appendTo(browser);

        const back = $('<button hidden>' + imagelibStrings.show_list + '</button>')
          .appendTo(leftBlock)
          .on('click touchend', function () {
            frame.attr('src', 'about:blank').hide();
            libOpts.show();
            header.text(allLibs);
            back.hide();
          }).css({
            'margin-right': 5
          }).hide();

        /* const type = */ $('<select><option value=s>' +
          imagelibStrings.import_single + '</option><option value=m>' +
          imagelibStrings.import_multi + '</option><option value=o>' +
          imagelibStrings.open + '</option></select>').appendTo(leftBlock).change(function () {
          mode = $(this).val();
          switch (mode) {
          case 's':
          case 'o':
            toggleMulti(false);
            break;

          case 'm':
            // Import multiple
            toggleMulti(true);
            break;
          }
        }).css({
          'margin-top': 10
        });

        cancel.prepend($.getSvgIcon('cancel', true));
        back.prepend($.getSvgIcon('tool_imagelib', true));

        imagelibStrings.imgLibs.forEach(function ({name, url, description}) {
          $('
')
            .appendTo(libOpts)
            .text(name)
            .on('click touchend', function () {
              frame.attr(
                'src',
                url
              ).show();
              header.text(name);
              libOpts.hide();
              back.show();
            }).append(`<span>${description}</span>`);
        });
      } else {
        $('#imgbrowse_holder').show();
      }
    }

    const buttons = [{
      id: 'tool_imagelib',
      type: 'app_menu', // _flyout
      icon: extIconsPath + 'imagelib.png',
      position: 4,
      events: {
        mouseup: showBrowser
      }
    }];

    return {
      svgicons: extIconsPath + 'ext-imagelib.xml',
      buttons: imagelibStrings.buttons.map((button, i) => {
        return Object.assign(buttons[i], button);
      }),
      callback () {
        $('


<commit_before><commit_msg>added updated time for field attributes<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * 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.
 *
 */

define(function (require, exports) {
    "use strict";

    var PlayObject = require("../playobject");

    /**
     * Sets the current tool to given tool
     *
     * @param {string} tool
     *
     * @returns {PlayObject}
     */
    var setTool = function (tool) {
        return new PlayObject(
            "select",
            {
                "null": {
                    "ref": tool
                }
            }
        );
    };

    /**
     * Sets the tool options
     *
     * @param {string} tool
     * @param {Object} options
     *
     * @returns {PlayObject}
     *
     * Preconditions:
     * tool is currently selected
     */
    var setToolOptions = function (tool, options) {
        return new PlayObject(
            "set",
            {
                "null": {
                    ref: tool
                },
                "to": {
                    obj: "currentToolOptions",
                    value: options
                }
            }
        );
    };

    /**
     * Sets the global preference of whether vector tools modify layer selection or not
     * This translates to the Select: [Active Layers vs All Layers] option in the toolbar.
     *
     * @param {boolean} allLayers If true, will set the Select mode to All Layers, false for Active Layers
     *
     * @returns {PlayObject}
     */
    var setDirectSelectOptionForAllLayers = function (allLayers) {
        return {
            command: "set",
            descriptor: {
                "null": {
                    ref: [
                        {
                            "property": "generalPreferences",
                            "ref": "property"
                        },
                        {
                            "enum": "ordinal",
                            "ref": "application",
                            "value": "targetEnum"
                        }
                    ]
                },
                "to": {
                    obj: "generalPreferences",
                    value: {
                        "legacyPathDrag": true,
                        "vectorSelectionModifiesLayerSelection": allLayers
                    }
                }
            }
        };
    };

    exports.setTool = setTool;
    exports.setToolOptions = setToolOptions;
    exports.setDirectSelectOptionForAllLayers = setDirectSelectOptionForAllLayers;
});
<commit_msg>Fix the new tool function<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * 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.
 *
 */

define(function (require, exports) {
    "use strict";

    var PlayObject = require("../playobject");

    /**
     * Sets the current tool to given tool
     *
     * @param {string} tool
     *
     * @returns {PlayObject}
     */
    var setTool = function (tool) {
        return new PlayObject(
            "select",
            {
                "null": {
                    "ref": tool
                }
            }
        );
    };

    /**
     * Sets the tool options
     *
     * @param {string} tool
     * @param {Object} options
     *
     * @returns {PlayObject}
     *
     * Preconditions:
     * tool is currently selected
     */
    var setToolOptions = function (tool, options) {
        return new PlayObject(
            "set",
            {
                "null": {
                    ref: tool
                },
                "to": {
                    obj: "currentToolOptions",
                    value: options
                }
            }
        );
    };

    /**
     * Sets the global preference of whether vector tools modify layer selection or not
     * This translates to the Select: [Active Layers vs All Layers] option in the toolbar.
     *
     * @param {boolean} allLayers If true, will set the Select mode to All Layers, false for Active Layers
     *
     * @returns {PlayObject}
     */
    var setDirectSelectOptionForAllLayers = function (allLayers) {
        return new PlayObject(
            "set",
            {
                "null": {
                    ref: [
                        {
                            "property": "generalPreferences",
                            "ref": "property"
                        },
                        {
                            "enum": "ordinal",
                            "ref": "application",
                            "value": "targetEnum"
                        }
                    ]
                },
                "to": {
                    obj: "generalPreferences",
                    value: {
                        "legacyPathDrag": true,
                        "vectorSelectionModifiesLayerSelection": allLayers
                    }
                }
            }
        );
    };

    exports.setTool = setTool;
    exports.setToolOptions = setToolOptions;
    exports.setDirectSelectOptionForAllLayers = setDirectSelectOptionForAllLayers;
});
<|endoftext|>


<commit_before>'use strict'
const path = require('path');
const querystring = require("querystring");
const url = require('url');
const _ = require('./util');

// 路径配置
let ROOT = {
  PK: '',
  ONLINE: '',
  VIEW: '',
  MOCKTPL: '',
  MOCKAPI: '',
  FMPP: '',
  API_RES_HEADERS: ''
};

let VIEW_EXT = '';

// 从nei站点获取数据
function getFromNEISite(type, id, callback, req) {
  if (!ROOT.ONLINE) {
    // 不使用在线地址，则不获取数据
    return callback(null);
  }
  req = req || {};

  let params = {path: req.url, type: type, key: ROOT.PK, id: id, method: req.method};

  let url = `https://nei.netease.com/api/mockdata?` + querystring.stringify(params);
  // let url = `http://localhost:8082/api/mockdata?` + querystring.stringify(params);
  console.log(`Mock数据为NEI提供的在线接口: ${url}`);

  let isHttps = /^https:\/\//i.test(url);
  // 获取nei站点上的数据
  require(isHttps ? 'https' : 'http').get(url, function (res) {
    let ret = [];
    res.on('data', function (chunk) {
      ret.push(chunk.toString());
    });
    res.on('end', function () {
      let json = null;
      try {
        json = JSON.parse(ret.join(''));
      } catch (ex) {
        // ignore
      }
      if (json && json.code === 200) {
        // 成功
        if (json.result.error.length) {
          console.log(`错误: ${json.result.error.map(err => err.message).join(', ')}`);
        }
        // 真正的 mock 数据
        callback(json.result.json);
      } else {
        callback(ret.join(''))
      }
    });
  }).on('error', function (error) {
    callback(error.message);
  });
}

// 从 model server 加载注入给页面的模型数据
function getModelFromServer(pagePath, callback, req) {
  let modelServerOptions = ROOT.MODEL_SERVER;
  let reqUrl = modelServerOptions.path;
  if (typeof reqUrl === 'function') {
    modelServerOptions.path = pagePath;
    reqUrl = reqUrl(modelServerOptions);
  }
  // 如果没有设置path，则默认为页面的path
  if (!reqUrl) {
    reqUrl = pagePath;
  }
  if (!reqUrl.startsWith('http')) {
    // 是相对地址，加上主机地址
    reqUrl = `${modelServerOptions.host}${reqUrl}`;
  }
  // 处理查询参数
  if (modelServerOptions.queries) {
    let qs = querystring.stringify(modelServerOptions.queries);
    reqUrl += (reqUrl.includes('?') ? `&` : `?`) + qs;
  }
  let reqHeaders = req.headers;
  // 处理请求头
  if (modelServerOptions.headers) {
    Object.keys(modelServerOptions.headers).forEach(function (headerKey) {
      reqHeaders[headerKey] = modelServerOptions.headers[headerKey];
    });
  }
  let urlParseResult = url.parse(reqUrl);
  let options = {
    hostname: urlParseResult.hostname,
    port: urlParseResult.port,
    path: urlParseResult.path,
    headers: reqHeaders
  };
  require(urlParseResult.protocol.slice(0,-1)).get(options, function (res) {
    let ret = [];
    res.on('data', function (chunk) {
      ret.push(chunk.toString());
    });
    res.on('end', function () {
      let json = {};
      try {
        json = JSON.parse(ret.join(''));
      } catch (ex) {
        console.log(`请求没有返回 json 数据：${JSON.stringify(ret)}`);
      }
      callback(json);
    });
  }).on('error', function (error) {
    callback(error.message);
  });
}

// 加载数据
function load(type, id, path, callback, req) {
  let filter = null;
  try {
    let file = path + '.js';
    filter = require(file);
    delete require.cache[require.resolve(file)];
  } catch (ex) {
    // ignore
  }

  // 如果filter是个json对象
  if (filter && (typeof filter !== 'function')) {
    return callback(filter);
  }

  getFromNEISite(type, id, function (json) {
    if (json == null) {
      try {
        let file = path + '.json';
        console.log(`Mock数据为本地JSON文件: ${file}`);
        json = require(file);
        delete require.cache[require.resolve(file)];
      } catch (ex) {
        // ignore
      }
    }

    if (filter) {
      json = filter(json, req);
    }

    callback(json);
  }, req);
}

// api代理
function wrapApi(id, path) {
  return function (req, res, next) {
    load(3, id, ROOT.MOCKAPI + path, function (json) {
      // 不能直接发送数字, 数字会当作响应码
      if (typeof json == 'number') {
        json = json.toString();
      }
      // 设置api响应头
      Object.keys(ROOT.API_RES_HEADERS).forEach((key) => {
        res.set(key, ROOT.API_RES_HEADERS[key]);
      });
      res.send(json);
    }, req);
  };
}

// 页面代理
function wrapPage(index, list) {
  return function (req, res, next) {
    let conf = list[index];
    let render = function (json) {
      if (VIEW_EXT.toLowerCase() === '.ftl') {
        // ftl 模板, 需要 node fmpp 处理
        // 如果不包裹一层，传递的时候 data 上会多出 settings 等字段，见 /node-fmpp/index.js
        res.render(conf.p + VIEW_EXT, {
          json: json,
          fmppConfig: ROOT.FMPP
        });
      } else {
        res.render(conf.p + VIEW_EXT, json);
      }
    }
    if (ROOT.MODEL_SERVER) {
      getModelFromServer(conf.p, function (json) {
        render(json);
      }, req);
    } else {
      load(1, conf.i, ROOT.MOCKTPL + conf.p, function (json) {
        // if('resfmt' in res.req.query && res.req.query['resfmt'] === 'json'){
        //   if (typeof json == 'number') {
        //       json = json.toString();
        //   }
        //   res.set('Content-Type', 'application/json');
        //   res.send(json);
        //   return;
        // }
        render(json);
      }, req);
    }
  };
}

module.exports = {
  // 获取设置
  getting: function () {
    return ROOT;
  },
  // 设置接口
  setting: function (obj) {
    ROOT.PK = obj.projectKey;
    ROOT.ONLINE = obj.online;
    ROOT.VIEW = obj.view;
    ROOT.MOCKTPL = obj.mockTpl;
    ROOT.MOCKAPI = obj.mockApi;
    ROOT.FMPP = obj.fmpp;
    ROOT.API_RES_HEADERS = obj.apiResHeaders;
    ROOT.MODEL_SERVER = obj.modelServer;
    VIEW_EXT = obj.viewExt;
  },
  // 获取包装过的路由
  getRoutes: function (routes) {
    let retRoutes = {};
    Object.keys(routes || {}).forEach(function (i) {
      let item = routes[i];
      if (_.isObject(item)) {
        if (item.hasOwnProperty('list')) {
          // 页面
          let list = [];
          item.list.forEach(function (it) {
            list.push({i: it.id, p: it.path});
          });

          retRoutes[i] = {
            nei: true,
            data: item,
            handle: wrapPage(item.index, list)
          };
        } else {
          // api
          retRoutes[i] = {
            nei: true,
            data: item,
            handle: wrapApi(item.id, item.path)
          };
        }
      } else {
        retRoutes[i] = item;
      }
    });
    return retRoutes;
  }
};
<commit_msg>注入给页面的模型数据，可以从指定的服务器获取 - 在转发请求的时候，不能带上原始请求的整个 headers，只带上 cookie 即可<commit_after>'use strict'
const path = require('path');
const querystring = require("querystring");
const url = require('url');
const _ = require('./util');

// 路径配置
let ROOT = {
  PK: '',
  ONLINE: '',
  VIEW: '',
  MOCKTPL: '',
  MOCKAPI: '',
  FMPP: '',
  API_RES_HEADERS: ''
};

let VIEW_EXT = '';

// 从nei站点获取数据
function getFromNEISite(type, id, callback, req) {
  if (!ROOT.ONLINE) {
    // 不使用在线地址，则不获取数据
    return callback(null);
  }
  req = req || {};

  let params = {path: req.url, type: type, key: ROOT.PK, id: id, method: req.method};

  let url = `https://nei.netease.com/api/mockdata?` + querystring.stringify(params);
  // let url = `http://localhost:8082/api/mockdata?` + querystring.stringify(params);
  console.log(`Mock数据为NEI提供的在线接口: ${url}`);

  let isHttps = /^https:\/\//i.test(url);
  // 获取nei站点上的数据
  require(isHttps ? 'https' : 'http').get(url, function (res) {
    let ret = [];
    res.on('data', function (chunk) {
      ret.push(chunk.toString());
    });
    res.on('end', function () {
      let json = null;
      try {
        json = JSON.parse(ret.join(''));
      } catch (ex) {
        // ignore
      }
      if (json && json.code === 200) {
        // 成功
        if (json.result.error.length) {
          console.log(`错误: ${json.result.error.map(err => err.message).join(', ')}`);
        }
        // 真正的 mock 数据
        callback(json.result.json);
      } else {
        callback(ret.join(''))
      }
    });
  }).on('error', function (error) {
    callback(error.message);
  });
}

// 从 model server 加载注入给页面的模型数据
function getModelFromServer(pagePath, callback, req) {
  let modelServerOptions = ROOT.MODEL_SERVER;
  let reqUrl = modelServerOptions.path;
  if (typeof reqUrl === 'function') {
    reqUrl = reqUrl({
      host: modelServerOptions.host,
      queries: modelServerOptions.queries,
      headers: modelServerOptions.headers,
      path: pagePath
    });
  }
  // 如果没有设置path，则默认为页面的path
  if (!reqUrl) {
    reqUrl = pagePath;
  }
  if (!reqUrl.startsWith('http')) {
    // 是相对地址，加上主机地址
    reqUrl = `${modelServerOptions.host}${reqUrl}`;
  }
  // 处理查询参数
  if (modelServerOptions.queries) {
    let qs = querystring.stringify(modelServerOptions.queries);
    reqUrl += (reqUrl.includes('?') ? `&` : `?`) + qs;
  }
  let reqHeaders = {
    cookie: req.headers.cookie
  };
  // 处理请求头
  if (modelServerOptions.headers) {
    Object.keys(modelServerOptions.headers).forEach(function (headerKey) {
      reqHeaders[headerKey] = modelServerOptions.headers[headerKey];
    });
  }
  let urlParseResult = url.parse(reqUrl);
  let options = {
    hostname: urlParseResult.hostname,
    port: urlParseResult.port,
    path: urlParseResult.path,
    headers: reqHeaders,
    protocol: urlParseResult.protocol
  };
  console.log(`页面 ${pagePath} 的模型请求信息：${JSON.stringify(options)}`);
  require(urlParseResult.protocol.slice(0,-1)).get(options, function (res) {
    let ret = [];
    res.on('data', function (chunk) {
      ret.push(chunk.toString());
    });
    res.on('end', function () {
      let json = {};
      try {
        json = JSON.parse(ret.join(''));
        console.log(`页面 ${pagePath} 的模型请求返回的 json 数据为：${JSON.stringify(json)}`);
      } catch (ex) {
        console.log(`页面 ${pagePath} 的模型请求没有返回 json 数据：${JSON.stringify(ret)}`);
      }
      callback(json);
    });
  }).on('error', function (error) {
    callback(error.message);
  });
}

// 加载数据
function load(type, id, path, callback, req) {
  let filter = null;
  try {
    let file = path + '.js';
    filter = require(file);
    delete require.cache[require.resolve(file)];
  } catch (ex) {
    // ignore
  }

  // 如果filter是个json对象
  if (filter && (typeof filter !== 'function')) {
    return callback(filter);
  }

  getFromNEISite(type, id, function (json) {
    if (json == null) {
      try {
        let file = path + '.json';
        console.log(`Mock数据为本地JSON文件: ${file}`);
        json = require(file);
        delete require.cache[require.resolve(file)];
      } catch (ex) {
        // ignore
      }
    }

    if (filter) {
      json = filter(json, req);
    }

    callback(json);
  }, req);
}

// api代理
function wrapApi(id, path) {
  return function (req, res, next) {
    load(3, id, ROOT.MOCKAPI + path, function (json) {
      // 不能直接发送数字, 数字会当作响应码
      if (typeof json == 'number') {
        json = json.toString();
      }
      // 设置api响应头
      Object.keys(ROOT.API_RES_HEADERS).forEach((key) => {
        res.set(key, ROOT.API_RES_HEADERS[key]);
      });
      res.send(json);
    }, req);
  };
}

// 页面代理
function wrapPage(index, list) {
  return function (req, res, next) {
    let conf = list[index];
    let render = function (json) {
      if (VIEW_EXT.toLowerCase() === '.ftl') {
        // ftl 模板, 需要 node fmpp 处理
        // 如果不包裹一层，传递的时候 data 上会多出 settings 等字段，见 /node-fmpp/index.js
        res.render(conf.p + VIEW_EXT, {
          json: json,
          fmppConfig: ROOT.FMPP
        });
      } else {
        res.render(conf.p + VIEW_EXT, json);
      }
    }
    if (ROOT.MODEL_SERVER) {
      getModelFromServer(conf.p, function (json) {
        render(json);
      }, req);
    } else {
      load(1, conf.i, ROOT.MOCKTPL + conf.p, function (json) {
        // if('resfmt' in res.req.query && res.req.query['resfmt'] === 'json'){
        //   if (typeof json == 'number') {
        //       json = json.toString();
        //   }
        //   res.set('Content-Type', 'application/json');
        //   res.send(json);
        //   return;
        // }
        render(json);
      }, req);
    }
  };
}

module.exports = {
  // 获取设置
  getting: function () {
    return ROOT;
  },
  // 设置接口
  setting: function (obj) {
    ROOT.PK = obj.projectKey;
    ROOT.ONLINE = obj.online;
    ROOT.VIEW = obj.view;
    ROOT.MOCKTPL = obj.mockTpl;
    ROOT.MOCKAPI = obj.mockApi;
    ROOT.FMPP = obj.fmpp;
    ROOT.API_RES_HEADERS = obj.apiResHeaders;
    ROOT.MODEL_SERVER = obj.modelServer;
    VIEW_EXT = obj.viewExt;
  },
  // 获取包装过的路由
  getRoutes: function (routes) {
    let retRoutes = {};
    Object.keys(routes || {}).forEach(function (i) {
      let item = routes[i];
      if (_.isObject(item)) {
        if (item.hasOwnProperty('list')) {
          // 页面
          let list = [];
          item.list.forEach(function (it) {
            list.push({i: it.id, p: it.path});
          });

          retRoutes[i] = {
            nei: true,
            data: item,
            handle: wrapPage(item.index, list)
          };
        } else {
          // api
          retRoutes[i] = {
            nei: true,
            data: item,
            handle: wrapApi(item.id, item.path)
          };
        }
      } else {
        retRoutes[i] = item;
      }
    });
    return retRoutes;
  }
};
<|endoftext|>


<commit_before><commit_msg>ORDER BY . ASC or DESC available on SortedTable<commit_after><|endoftext|>


<commit_before>'use strict';

var request = require('request');

module.exports = { comment: comment };

function opt( options, name, defaultValue ) {
	return options && options[name] !== undefined ?
		options[name] : defaultValue;
}

function comment( value, options, cb ) {

	var repo_slug = opt( options, 'repo_slug', process.env.TRAVIS_REPO_SLUG );
	var commit_hash = opt( options, 'commit_hash', process.env.TRAVIS_COMMIT );
	var token = opt( options, 'token', process.env.GITHUB_TOKEN );
	var userAgent = opt( options, 'user_agent', 'peanut-gallery' );
	var comment_on_issue = opt( options, 'comment_on_issue', false);

	commit_hash = (comment_on_issue ? comment_on_issue : commit_hash);
	var commentLocation = (comment_on_issue ? '/issues/' : '/commits/')

	var githubUrl = 'https://api.github.com/repos/' + repo_slug + commentLocation +
		commit_hash + '/comments';
	console.log('GithubUrl: ', githubUrl);
	console.log('Message: ', value);
	var requestOptions = {
			url: githubUrl,
			headers: {
				'Authorization': 'token ' + token,
				'User-Agent': userAgent
			},
			json: { 'body': value }
		};

	function handleResponse( error, response, body ) {

		if( error ) {
			return cb( error );
		}

		if( response.statusCode !== 201 ) {
			return cb( {
					statusCode: response.statusCode,
					body: body
				} );
		}

		cb( null, body );

	}

	request.post( requestOptions, handleResponse );

}
<commit_msg>Add pull request number<commit_after>'use strict';

var request = require('request');

module.exports = { comment: comment };

function opt( options, name, defaultValue ) {
	return options && options[name] !== undefined ?
		options[name] : defaultValue;
}

function comment( value, options, cb ) {

	var repo_slug = opt( options, 'repo_slug', process.env.TRAVIS_REPO_SLUG );
	var commit_hash = opt( options, 'commit_hash', process.env.TRAVIS_COMMIT );
	var token = opt( options, 'token', process.env.GITHUB_TOKEN );
	var userAgent = opt( options, 'user_agent', 'peanut-gallery' );
	var comment_on_issue = opt( options, 'comment_on_issue', false);

	var pull_request_id = process.env.TRAVIS_PULL_REQUEST;

	var commentLocation = (comment_on_issue ? '/issues/' : '/commits/')
	var commentLocationIdentifier = (comment_on_issue ? pull_request_id : commit_hash);

	var githubUrl = 'https://api.github.com/repos/' + repo_slug + commentLocation +
		commentLocationIdentifier + '/comments';
	console.log('GithubUrl: ', githubUrl);

	var requestOptions = {
			url: githubUrl,
			headers: {
				'Authorization': 'token ' + token,
				'User-Agent': userAgent
			},
			json: { 'body': value }
		};

	function handleResponse( error, response, body ) {

		if( error ) {
			return cb( error );
		}

		if( response.statusCode !== 201 ) {
			return cb( {
					statusCode: response.statusCode,
					body: body
				} );
		}

		cb( null, body );

	}

	request.post( requestOptions, handleResponse );

}
<|endoftext|>


<commit_before>'use strict';
var Sprite = require('./sprite');

function Compass(game, x, y) {
  // this.grid = grid;
  this.x = x;
  this.y = y;
  this.game = game;
  Sprite.call(this, game, x, y, 'compass');
  // this.phSprite.anchor.setTo(0);
  this.phSprite.scale.setTo(0.4, 0.4);

  this.arrow = this.game.add.sprite(this.x, this.y, 'compass_arrow');
  this.arrow.anchor.setTo(0.5, 1);

  // this.pointTo(4);

}

//inherit from Sprite
Compass.prototype = new Sprite();
Compass.prototype.constructor = Compass;

Compass.prototype.pointTo = function(dir) {
  var target = (dir % 8)/8*360;
  this.game.add.tween(this.arrow)
  .to({angle: target}, 500, Phaser.Easing.Back.Out)
  .start();
};

module.exports = Compass;<commit_msg>Added direction to compass object.<commit_after>'use strict';
var Sprite = require('./sprite');

/*
  A compass object

  has a 'direction' property (0 - 7, 0 - north, increases clockwise)
*/

function Compass(game, x, y) {
  // this.grid = grid;
  this.x = x;
  this.y = y;
  this.game = game;
  Sprite.call(this, game, x, y, 'compass');
  this.phSprite.scale.setTo(0.4, 0.4);

  this.arrow = this.game.add.sprite(this.x, this.y, 'compass_arrow');
  this.arrow.anchor.setTo(0.5, 1);

  this.direction = 0;

}

//inherit from Sprite
Compass.prototype = new Sprite();
Compass.prototype.constructor = Compass;

Compass.prototype.pointTo = function(dir) {
  this.direction = dir;

  var target = (dir % 8)/8*360;
  this.game.add.tween(this.arrow)
  .to({angle: target}, 500, Phaser.Easing.Back.Out)
  .start();
};

module.exports = Compass;<|endoftext|>


<commit_before>/**
 * Topological sort class.
 * Original by Marcel Klehr, contributed by Gustavo Henke.
 *
 * @class
 * @since   0.1.0
 * @see     https://github.com/marcelklehr/node-toposort
 * @author  Marcel Klehr <mklehr@gmx.net>
 *
 * @see     https://github.com/gustavohenke/node-toposort
 * @author  Gustavo Henke <gustavo@injoin.com.br>
 */
function Toposort() {
	"use strict";
	var self = this,
		edges = [];

	/**
	 * Adds dependency edges.
	 *
	 * @since   0.1.0
	 * @param   {String} item               An dependent name. Must be an string and not empty
	 * @param   {String[]|String} [deps]    An dependency or array of dependencies
	 * @returns {Toposort}                  The Toposort instance
	 */
	this.add = function( item, deps ) {
		if ( typeof item !== "string" || !item ) {
			throw new TypeError( "Dependent name must be given as a not empty string" );
		}

		deps = Array.isArray( deps ) ? deps.slice() : [ deps ];
		if ( deps.length ) {
			deps.forEach(function( dep ) {
				if ( typeof dep !== "string" || !dep ) {
					throw new TypeError( "Dependency name must be given as a not empty string" );
				}

				edges.push([ item, dep ]);
			});
		} else {
			edges.push([ item ]);
		}

		return self;
	};

	/**
	 * Runs the toposorting and return an ordered array of strings
	 *
	 * @since   0.1.0
	 * @returns {String[]}  The list of items topologically sorted.
	 */
	this.sort = function() {
		var nodes = [];
		var sorted = [];

		edges.forEach(function( edge ) {
			edge.forEach(function( n ) {
				if ( nodes.indexOf( n ) === -1 ) {
					nodes.push( n );
				}
			});
		});

		function visit( node, predecessors, i ) {
			predecessors = predecessors || [];

			if ( predecessors.indexOf( node ) > -1 ) {
				throw new Error( "Cyclic dependency found. '" + node + "' is dependent of itself." );
			}

			var index = nodes.indexOf( node );
			if ( index === -1 ) {
				return i;
			}

			nodes.splice( index, 1 );
			if ( predecessors.length === 0 ) {
				i--;
			}

			var predsCopy = predecessors.slice();
			predsCopy.push( node );

			edges.filter(function( e ) {
				return e[ 0 ] === node;
			}).forEach(function( e ) {
				i = visit( e[ 1 ], predsCopy, i );
			});

			sorted.unshift( node );
			return i;
		}

		for ( var i = 0; i < nodes.length; i++ ) {
			i = visit( nodes[ i ], null, i );
		}

		return sorted;
	};

}

if ( module && module.exports ) {
	module.exports = exports.Toposort = Toposort;
} else if ( window ) {
	window.Toposort = Toposort;
}<commit_msg>Fix repos URLs in the JSDoc<commit_after>/**
 * Topological sort class.
 * Original by Marcel Klehr, contributed by Gustavo Henke.
 *
 * @class
 * @since   0.1.0
 * @see     https://github.com/marcelklehr/toposort
 * @author  Marcel Klehr <mklehr@gmx.net>
 *
 * @see     https://github.com/gustavohenke/toposort
 * @author  Gustavo Henke <gustavo@injoin.com.br>
 */
function Toposort() {
	"use strict";
	var self = this,
		edges = [];

	/**
	 * Adds dependency edges.
	 *
	 * @since   0.1.0
	 * @param   {String} item               An dependent name. Must be an string and not empty
	 * @param   {String[]|String} [deps]    An dependency or array of dependencies
	 * @returns {Toposort}                  The Toposort instance
	 */
	this.add = function( item, deps ) {
		if ( typeof item !== "string" || !item ) {
			throw new TypeError( "Dependent name must be given as a not empty string" );
		}

		deps = Array.isArray( deps ) ? deps.slice() : [ deps ];
		if ( deps.length ) {
			deps.forEach(function( dep ) {
				if ( typeof dep !== "string" || !dep ) {
					throw new TypeError( "Dependency name must be given as a not empty string" );
				}

				edges.push([ item, dep ]);
			});
		} else {
			edges.push([ item ]);
		}

		return self;
	};

	/**
	 * Runs the toposorting and return an ordered array of strings
	 *
	 * @since   0.1.0
	 * @returns {String[]}  The list of items topologically sorted.
	 */
	this.sort = function() {
		var nodes = [];
		var sorted = [];

		edges.forEach(function( edge ) {
			edge.forEach(function( n ) {
				if ( nodes.indexOf( n ) === -1 ) {
					nodes.push( n );
				}
			});
		});

		function visit( node, predecessors, i ) {
			predecessors = predecessors || [];

			if ( predecessors.indexOf( node ) > -1 ) {
				throw new Error( "Cyclic dependency found. '" + node + "' is dependent of itself." );
			}

			var index = nodes.indexOf( node );
			if ( index === -1 ) {
				return i;
			}

			nodes.splice( index, 1 );
			if ( predecessors.length === 0 ) {
				i--;
			}

			var predsCopy = predecessors.slice();
			predsCopy.push( node );

			edges.filter(function( e ) {
				return e[ 0 ] === node;
			}).forEach(function( e ) {
				i = visit( e[ 1 ], predsCopy, i );
			});

			sorted.unshift( node );
			return i;
		}

		for ( var i = 0; i < nodes.length; i++ ) {
			i = visit( nodes[ i ], null, i );
		}

		return sorted;
	};

}

if ( module && module.exports ) {
	module.exports = exports.Toposort = Toposort;
} else if ( window ) {
	window.Toposort = Toposort;
}<|endoftext|>


<commit_before>var e = module.exports = {};
var bu;
var exec = require('child_process').exec;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.CAT;

};
e.requireCtx = require;

e.isCommand = false;
e.hidden = false;
e.usage = '';
e.info = '';

e.execute = async function (msg) {
    if (msg.author.id === bu.CAT_ID) {
        await bu.r.table('vars').get('restart').replace({
          varname: 'restart',
          varvalue: msg.channel.id
        }).run();
        await bu.send(msg.channel.id, 'Ah! You\'ve killed me! D:');
        exec('pm2 restart 0');
    }
};<commit_msg>changed restart<commit_after>var e = module.exports = {};
var bu;
var exec = require('child_process').exec;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.CAT;

};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = '';
e.info = '';

e.execute = async function (msg) {
    if (msg.author.id === bu.CAT_ID) {
        await bu.r.table('vars').get('restart').replace({
          varname: 'restart',
          varvalue: msg.channel.id
        }).run();
        await bu.send(msg.channel.id, 'Ah! You\'ve killed me! D:');
        process.exit();
    }
};<|endoftext|>