<commit_before>//-------------------------------------------
// Foxtrot FIGURES
/* template
'PatternName' :
	{
		'name' : 'Pattern Name',
		'urlpath' : '/pattern-name',
		'level' : 'Level',
		'startAlignment' : ['LOD'],
		'startFoot' : 'LF',
		'startDirection' : 'fwd',
		'follow' : [
		{
			'id' : "NextPattern"
		}
	    ]
	},
*/
var foxtrotFigures =
{
  'FeatherStep' :
    {
    'name' : 'Feather Step',
    'level' : 'Newcomer',
    'urlpath' : '/feather-step',
    'startAlignment' : ['LOD'],
    'startFoot' : 'RF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'ThreeStep'
    },
    {
      'id' : 'ReverseTurn'
    },
    {
      'id' : 'BasicWeave',
      'comment' : '1-4 Reverse Turn check and Basic Weave'
    },
    {
      'id' : 'ChangeOfDirection'
    },
    {
      'id' : 'ClosedTelemark'
    },
    {
      'eval' : 'MatchFigureName("Open Telemark")'
    },
    {
      'id' : 'HoverTelemark'
    },
    {
      'id' : 'ReverseWave'
    },
    {
      'id' : 'TopSpin',
      'comment' : 'check back after step 3 of feather finish'
    },
    {
      'id' : 'OutsideSwivel',
      'comment' : 'check back after step 3 of Feather Step'
    },
    {
      'id' : 'FallawayReverseAndSlipPivot'
    },
    {
      'id' : 'BounceFallawayWithWeaveEnding'
    }
      ]
    },
  'ThreeStep' :
    {
    'name' : 'Three Step',
    'urlpath' : '/three-step',
    'level' : 'Newcomer',
    'startAlignment' : ['LOD'],
    'startFoot' : 'LF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'NaturalTurn'
    },
    {
      'id' : 'NaturalWeave'
    },
    {
      'id' : 'NaturalTelemark'
    },
    {
      'id' : 'HoverCross'
    },
    {
      'id' : 'NaturalTwistTurn'
    },
    {
      'id' : 'CurvedFeatherToBackFeather'
    },
    {
      'id' : 'NaturalHoverTelemark'
    }
      ]
    },
  'NaturalTurn' :
    {
    'name' : 'Natural Turn',
    'urlpath' : '/natural-turn',
    'level' : 'Newcomer',
    'startAlignment' : ['LOD'],
    'startFoot' : 'RF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'FeatherStep'
    },
    {
      'id' : 'NaturalWeave',
      'comment' : 'at corner'
    },
    {
      'id' : 'ClosedImpetusAndFeatherFinish',
      'comment' :  'after step 3'
    },
    {
      'id' : 'NaturalWeave',
      'comment' : 'side of room'
    },
    {
      'id' : 'OpenImpetus',
      'comment' : 'after step 3'
    },
    {
      'id' : 'HoverFeather',
      'comment' : 'after step 5'
    },
    {
      'id' : 'CurvedFeatherToBackFeather',
      'comment' : 'at corner'
    }
      ]
    },
  'ReverseTurn' :
    {
    'name' : 'Reverse Turn (incorporating Feather Finish)',
    'urlpath' : '/reverse-turn',
    'level' : 'Newcomer',
    'startAlignment' : ['DC'],
    'startFoot' : 'LF',
    'startDirection' : 'fwd',
    'follow' : [
      {
        'id' : 'ThreeStep'
      },
      {
        'id' : 'ChangeOfDirection'
      },
      {
        'id' : 'BasicWeave',
        'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
      },
      {
        'id' : 'HoverTelemark'
      },
      {
        'id' : 'ReverseWave',
      },
      {
        'id' : 'TopSpin',
        'comment' : 'check back after step 6'
      },
      {
        'id' : 'OutsideSwivel',
        'comment' : 'check back after step 6'
      },
      {
        'id' : 'FallawayReverseAndSlipPivot',
        'comment' : 'at corner'
      }
      ]
    },
  'ClosedImpetusAndFeatherFinish' :
    {
      'name' : 'Closed Impetus and Feather Finish',
      'urlpath' : '/closed-impetus',
      'level' : 'Newcomer',
      'startAlignment' : ['BLOD'],
      'startFoot' : 'LF',
      'startDirection' : 'back',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ReverseTurn'
        },
        {
          'id' : 'BasicWeave',
          'comment' : '1-4 Reverse Turn check and Basic Weave'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'ClosedTelemark'
        },
        {
          'eval' : 'MatchFigureName("Open Telemark")'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave'
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 3 of feather finish'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 3 of Feather Step'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot'
        },
        {
          'id' : 'BounceFallawayWithWeaveEnding'
        }
      ]
    },
  'NaturalWeave' :
    {
      'name' : 'Natural Weave',
      'urlpath' : '/natural-weave',
      'level' : 'Bronze',
      'startAlignment' : ['LOD'],
      'startFoot' : 'RF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'BasicWeave',
          'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave',
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot',
          'comment' : 'at corner'
        }
      ]
    },
  'ChangeOfDirection' :
    {
      'name' : 'Change Of Direction',
      'level' : 'Bronze',
      'urlpath' : '/change-of-direction',
      'startAligment' : ['DW'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'FeatherStep'
        },
        {
          'id' : 'NaturalWeave',
          'comment' : 'at corner'
        },
        {
          'id' : 'NaturalWeave',
          'comment' : 'side of room'
        }
      ]
    },
  'BasicWeave' :
    {
      'name' : 'Basic Weave',
      'urlpath' : '/basic-weave',
      'level' : 'Bronze',
      'startAlignment' : ['BDW'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'BasicWeave',
          'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave',
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot',
          'comment' : 'at corner'
        }
      ]
    },
  'ClosedTelemark' :
    {
      'name' : 'Closed Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
        'id' : "NextPattern"
      }
      ]
    },
  'OpenTelemarkAndFeatherEnding' :
    {
      'name' : 'Open Telemark and Feather Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'TopSpin' :
    {
      'name' : 'Top Spin',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverFeather' :
    {
      'name' : 'Hover Feather',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverTelemark' :
    {
      'name' : 'Hover Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalTelemark' :
    {
      'name' : 'Natural Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverCross' :
    {
      'name' : 'Hover Cross',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OpenTelemarkNaturalTurnOutsideSwivelAndFeatherEnding' :
    {
      'name' : 'Open Telemark, Natural Turn, Outside Swivel and Feather Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OpenImpetus' :
    {
      'name' : 'Open Impetus',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'WeaveFromPP' :
    {
      'name' : 'Weave from PP',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'ReverseWave' :
    {
      'name' : 'Reverse Wave',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OutsideSwivel' :
    {
     //doesn't exist as a separate pattern in book, will have to extract it
      'name' : 'Outside Swivel',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalTwistTurn' :
    {
      'name' : 'Natural Twist Turn',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'CurvedFeatherToBackFeather' :
    {
      'name' : 'Curved Feather to Back Feather',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalZigZagFromPP' :
    {
      'name' : 'Natural Zig-Zag from PP',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'FallawayReverseAndSlipPivot' :
    {
      'name' : 'Fallaway Reverse and Slip Pivot',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalHoverTelemark' :
    {
      'name' : 'Natural Hover Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'BounceFallawayWithWeaveEnding' :
    {
      'name' : 'Bounce Fallaway with Weave Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    }
};

<commit_msg>Remove dup, verify precedes<commit_after>//-------------------------------------------
// Foxtrot FIGURES
/* template
'PatternName' :
	{
		'name' : 'Pattern Name',
		'urlpath' : '/pattern-name',
		'level' : 'Level',
		'startAlignment' : ['LOD'],
		'startFoot' : 'LF',
		'startDirection' : 'fwd',
		'follow' : [
		{
			'id' : "NextPattern"
		}
	    ]
	},
*/
var foxtrotFigures =
{
  'FeatherStep' :
    {
    'name' : 'Feather Step',
    'level' : 'Newcomer',
    'urlpath' : '/feather-step',
    'startAlignment' : ['LOD'],
    'startFoot' : 'RF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'ThreeStep'
    },
    {
      'id' : 'ReverseTurn',
      'comment' : 'just Reverse Turn with Feather finish, or 1-4 Reverse Turn check and Basic Weave'
    },
    {
      'id' : 'ChangeOfDirection'
    },
    {
      'id' : 'ClosedTelemark'
    },
    {
      'eval' : 'MatchFigureName("Open Telemark")'
    },
    {
      'id' : 'HoverTelemark'
    },
    {
      'id' : 'ReverseWave'
    },
    {
      'id' : 'TopSpin',
      'comment' : 'check back after step 3 of feather finish'
    },
    {
      'id' : 'OutsideSwivel',
      'comment' : 'check back after step 3 of Feather Step'
    },
    {
      'id' : 'FallawayReverseAndSlipPivot'
    },
    {
      'id' : 'BounceFallawayWithWeaveEnding'
    }
      ]
    },
  'ThreeStep' :
    {
    'name' : 'Three Step',
    'urlpath' : '/three-step',
    'level' : 'Newcomer',
    'startAlignment' : ['LOD'],
    'startFoot' : 'LF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'NaturalTurn'
    },
    {
      'id' : 'NaturalWeave'
    },
    {
      'id' : 'NaturalTelemark'
    },
    {
      'id' : 'HoverCross'
    },
    {
      'id' : 'NaturalTwistTurn'
    },
    {
      'id' : 'CurvedFeatherToBackFeather'
    },
    {
      'id' : 'NaturalHoverTelemark'
    }
      ]
    },
  'NaturalTurn' :
    {
    'name' : 'Natural Turn',
    'urlpath' : '/natural-turn',
    'level' : 'Newcomer',
    'startAlignment' : ['LOD'],
    'startFoot' : 'RF',
    'startDirection' : 'fwd',
    'follow' : [
    {
      'id' : 'FeatherStep'
    },
    {
      'id' : 'NaturalWeave',
      'comment' : 'at corner for Bronze, side of room for Silver and Gold'
    },
    {
      'id' : 'ClosedImpetusAndFeatherFinish',
      'comment' :  'after step 3'
    },
    {
      'id' : 'OpenImpetus',
      'comment' : 'after step 3'
    },
    {
      'id' : 'HoverFeather',
      'comment' : 'after step 5'
    },
    {
      'id' : 'CurvedFeatherToBackFeather',
      'comment' : 'at corner'
    }
      ]
    },
  'ReverseTurn' :
    {
    'name' : 'Reverse Turn',
    'urlpath' : '/reverse-turn',
    'level' : 'Newcomer',
    'startAlignment' : ['DC'],
    'startFoot' : 'LF',
    'startDirection' : 'fwd',
    'follow' : [
      {
        'id' : 'ThreeStep'
      },
      {
        'id' : 'ChangeOfDirection'
      },
      {
        'id' : 'BasicWeave',
        'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
      },
      {
        'id' : 'HoverTelemark'
      },
      {
        'id' : 'ReverseWave',
      },
      {
        'id' : 'TopSpin',
        'comment' : 'check back after step 6'
      },
      {
        'id' : 'OutsideSwivel',
        'comment' : 'check back after step 6'
      },
      {
        'id' : 'FallawayReverseAndSlipPivot',
        'comment' : 'at corner'
      }
      ]
    },
  'ClosedImpetusAndFeatherFinish' :
    {
      'name' : 'Closed Impetus and Feather Finish',
      'urlpath' : '/closed-impetus',
      'level' : 'Newcomer',
      'startAlignment' : ['BLOD'],
      'startFoot' : 'LF',
      'startDirection' : 'back',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ReverseTurn',
          'comment' : 'just Reverse Turn with Feather Finish, or 1-4 Reverse Turn check and Basic Weave'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'ClosedTelemark'
        },
        {
          'eval' : 'MatchFigureName("Open Telemark")'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave'
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 3 of feather finish'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 3 of Feather Step'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot'
        },
        {
          'id' : 'BounceFallawayWithWeaveEnding'
        }
      ]
    },
  'NaturalWeave' :
    {
      'name' : 'Natural Weave',
      'urlpath' : '/natural-weave',
      'level' : 'Bronze',
      'startAlignment' : ['LOD'],
      'startFoot' : 'RF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'BasicWeave',
          'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave',
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot',
          'comment' : 'at corner'
        }
      ]
    },
  'ChangeOfDirection' :
    {
      'name' : 'Change Of Direction',
      'level' : 'Bronze',
      'urlpath' : '/change-of-direction',
      'startAligment' : ['DW'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'FeatherStep'
        },
        {
          'id' : 'NaturalWeave',
          'comment' : 'at corner for Bronze; side of room for Silver and Gold'
        }
      ]
    },
  'BasicWeave' :
    {
      'name' : 'Basic Weave',
      'urlpath' : '/basic-weave',
      'level' : 'Bronze',
      'startAlignment' : ['BDW'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
        {
          'id' : 'ThreeStep'
        },
        {
          'id' : 'ChangeOfDirection'
        },
        {
          'id' : 'BasicWeave',
          'comment' : 'overturned 1-4 Reverse Turn, check, and Basic Weave'
        },
        {
          'id' : 'HoverTelemark'
        },
        {
          'id' : 'ReverseWave',
        },
        {
          'id' : 'TopSpin',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'OutsideSwivel',
          'comment' : 'check back after step 6'
        },
        {
          'id' : 'FallawayReverseAndSlipPivot',
          'comment' : 'at corner'
        }
      ]
    },
  'ClosedTelemark' :
    {
      'name' : 'Closed Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
        'id' : "NextPattern"
      }
      ]
    },
  'OpenTelemarkAndFeatherEnding' :
    {
      'name' : 'Open Telemark and Feather Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'TopSpin' :
    {
      'name' : 'Top Spin',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverFeather' :
    {
      'name' : 'Hover Feather',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverTelemark' :
    {
      'name' : 'Hover Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalTelemark' :
    {
      'name' : 'Natural Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'HoverCross' :
    {
      'name' : 'Hover Cross',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OpenTelemarkNaturalTurnOutsideSwivelAndFeatherEnding' :
    {
      'name' : 'Open Telemark, Natural Turn, Outside Swivel and Feather Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OpenImpetus' :
    {
      'name' : 'Open Impetus',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'WeaveFromPP' :
    {
      'name' : 'Weave from PP',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'ReverseWave' :
    {
      'name' : 'Reverse Wave',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'OutsideSwivel' :
    {
     //doesn't exist as a separate pattern in book, will have to extract it
      'name' : 'Outside Swivel',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalTwistTurn' :
    {
      'name' : 'Natural Twist Turn',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'CurvedFeatherToBackFeather' :
    {
      'name' : 'Curved Feather to Back Feather',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalZigZagFromPP' :
    {
      'name' : 'Natural Zig-Zag from PP',
      'urlpath' : '/pattern-name',
      'level' : 'Silver',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'FallawayReverseAndSlipPivot' :
    {
      'name' : 'Fallaway Reverse and Slip Pivot',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'NaturalHoverTelemark' :
    {
      'name' : 'Natural Hover Telemark',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    },
  'BounceFallawayWithWeaveEnding' :
    {
      'name' : 'Bounce Fallaway with Weave Ending',
      'urlpath' : '/pattern-name',
      'level' : 'Gold',
      'startAlignment' : ['LOD'],
      'startFoot' : 'LF',
      'startDirection' : 'fwd',
      'follow' : [
      {
      	'id' : "NextPattern"
      }
      ]
    }
};

<|endoftext|>


<commit_before>/*
@licstart  The following is the entire license notice for the
JavaScript code in this page.

Copyright (C) 2010  Goteo Foundation

The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.  The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.

As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.

@licend  The above is the entire license notice
for the JavaScript code in this page.
*/

$(function () {
    var filter;
    var newFilter;

    var form = $('#filter-form');
    var title = $('#form-title');
    var startdate = $('#form-startdate');
    var enddate = $('#form-enddate');
    var predefineddate = $('#form-predefineddata');
    var role = $('#form-role');
    var projects = $('#form-projects');
    var calls = $('#form-calls');
    var matchers = $('#form-matchers');
    var sdgs = $('#form-sdgs');
    var footprints = $('#form-footprints');
    var status = $('#form-status');
    var typeofdonor = $('#form-typeofdonor');
    var foundationdonor = $('#form-foundationdonor');
    var wallet = $('#form-wallet');
    var cert = $('#form-cert');
    var location = $('#form-project_location');

    function changeForm(role){

        if (role == "user" || role == "test") {
            startdate.hide();
            enddate.hide();
            predefineddate.hide();
            projects.hide();
            calls.hide();
            matchers.hide();
            sdgs.hide();
            footprints.hide();
            status.hide();
            status.val = '';
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.hide();
            location.val = '';
        }
        else if (role == "donor" || role == "no-donor") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.show();
            calls.show();
            matchers.show()
            footprints.show();
            sdgs.show();
            status.show();
            typeofdonor.show();
            foundationdonor.show();
            cert.show();
            wallet.show();
            location.show();
        }
        else if (role == "promoter") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.show();
            calls.show();
            matchers.show();
            sdgs.show();
            footprints.show();
            status.show();
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.show();
        }
        else if (role == "matcher") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.hide();
            calls.hide();
            footprints.hide();
            sdgs.hide();
            status.hide();
            status.val = '';
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.hide();
            location.val = '';
            matchers.show();
        }
    }

    function changeDates(dates){

        var today = new Date();

        if (dates == 0)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(7, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        }
        else if (dates == 1)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(30, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 2)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(365, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 3)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + today.getFullYear();
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 4)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + (today.getFullYear()-1);
            document.getElementById('autoform_enddate').value = '31/12/' + (today.getFullYear()-1);
        } else if (dates == 5)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + (today.getFullYear()-2);
            document.getElementById('autoform_enddate').value = '31/12/' + (today.getFullYear()-2);
        }
    }

    if (document.getElementById('autoform')){

        document.getElementById('autoform_role').onchange =
            function(){
                changeForm(this.value);
            };

        document.getElementById('autoform_predefineddata').onchange =
            function(){
                changeDates(this.value);
            };

        document.getElementById('autoform_role').onchange();
    }
});<commit_msg>fix view of no-donor in filter form<commit_after>/*
@licstart  The following is the entire license notice for the
JavaScript code in this page.

Copyright (C) 2010  Goteo Foundation

The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.  The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.

As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.

@licend  The above is the entire license notice
for the JavaScript code in this page.
*/

$(function () {
    var filter;
    var newFilter;

    var form = $('#filter-form');
    var title = $('#form-title');
    var startdate = $('#form-startdate');
    var enddate = $('#form-enddate');
    var predefineddate = $('#form-predefineddata');
    var role = $('#form-role');
    var projects = $('#form-projects');
    var calls = $('#form-calls');
    var matchers = $('#form-matchers');
    var sdgs = $('#form-sdgs');
    var footprints = $('#form-footprints');
    var status = $('#form-status');
    var typeofdonor = $('#form-typeofdonor');
    var foundationdonor = $('#form-foundationdonor');
    var wallet = $('#form-wallet');
    var cert = $('#form-cert');
    var location = $('#form-project_location');

    function changeForm(role){

        if (role == "user" || role == "test") {
            startdate.hide();
            enddate.hide();
            predefineddate.hide();
            projects.hide();
            calls.hide();
            matchers.hide();
            sdgs.hide();
            footprints.hide();
            status.hide();
            status.val = '';
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.hide();
            location.val = '';
        }
        else if (role == "donor") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.show();
            calls.show();
            matchers.show()
            footprints.show();
            sdgs.show();
            status.show();
            typeofdonor.show();
            foundationdonor.show();
            cert.show();
            wallet.show();
            location.show();
        }
        else if (role == "no-donor") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.hide();
            calls.hide();
            matchers.hide()
            footprints.hide();
            sdgs.hide();
            status.hide();
            typeofdonor.hide();
            foundationdonor.hide();
            cert.hide();
            wallet.hide();
            location.hide();
        }
        else if (role == "promoter") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.show();
            calls.show();
            matchers.show();
            sdgs.show();
            footprints.show();
            status.show();
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.show();
        }
        else if (role == "matcher") {
            startdate.show();
            enddate.show();
            predefineddate.show();
            projects.hide();
            calls.hide();
            footprints.hide();
            sdgs.hide();
            status.hide();
            status.val = '';
            typeofdonor.hide();
            typeofdonor.val = '';
            foundationdonor.hide();
            foundationdonor.val = '';
            cert.hide();
            cert.val = '';
            wallet.hide();
            wallet.val = '';
            location.hide();
            location.val = '';
            matchers.show();
        }
    }

    function changeDates(dates){

        var today = new Date();

        if (dates == 0)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(7, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        }
        else if (dates == 1)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(30, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 2)
        {
            document.getElementById('autoform_startdate').value = moment().subtract(365, 'days').format('DD/MM/YYYY');
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 3)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + today.getFullYear();
            document.getElementById('autoform_enddate').value = moment().format('DD/MM/YYYY');
        } else if (dates == 4)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + (today.getFullYear()-1);
            document.getElementById('autoform_enddate').value = '31/12/' + (today.getFullYear()-1);
        } else if (dates == 5)
        {
            document.getElementById('autoform_startdate').value = '01/01/' + (today.getFullYear()-2);
            document.getElementById('autoform_enddate').value = '31/12/' + (today.getFullYear()-2);
        }
    }

    if (document.getElementById('autoform')){

        document.getElementById('autoform_role').onchange =
            function(){
                changeForm(this.value);
            };

        document.getElementById('autoform_predefineddata').onchange =
            function(){
                changeDates(this.value);
            };

        document.getElementById('autoform_role').onchange();
    }
});<|endoftext|>


<commit_before>const chai = require('chai');
const expect = chai.expect;

const MockModule = require('../../../src/mocker/MockModule');

describe('./mocker/MockModule', () => {
    describe('check json-file.json', () => {
        let mockModule;

        before(() => {
            mockModule = new MockModule('json-file', require('../../data/fixtures/mock_modules/json-file'));
            // console.log(mockModule);
        });

        it('should return object', () => {
            expect(mockModule).to.be.an('object');
        });

        it('should contain some fields', () => {
            expect(mockModule).to.have.all.keys('name', 'module', 'config');
        });

        it('should return correct value', () => {
            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'json-file.json',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-promise.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-promise', require('../../data/fixtures/mock_modules/return-function-promise'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-promise',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-pure.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-pure', require('../../data/fixtures/mock_modules/return-function-pure'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-pure',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-with-param.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-with-param', require('../../data/fixtures/mock_modules/return-function-with-param'));

            return mockModule.getResult('name-a', 2, 'not-exist')
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-with-param',
                        desc: 'a=name-a,b=2',
                        age: 16
                    });
                });
        });
    });

    describe('check return-plain-object.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-plain-object', require('../../data/fixtures/mock_modules/return-plain-object'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-plain-object',
                        age: 16
                    });
                });
        });
    });

    describe('check return-promise.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-promise', require('../../data/fixtures/mock_modules/return-promise'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-promise',
                        age: 16
                    });
                });
        });
    });

    describe('check no-config', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('no-config', require('../../data/fixtures/mock_modules/no-config'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'no-config',
                        age: 16
                    });
                });
        });
    });

    describe('check exist-config', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('exist-config', require('../../data/fixtures/mock_modules/exist-config'), require('../../data/fixtures/mock_modules/exist-config/config'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'exist-config',
                        age: 16
                    });
                });
        });
    });
});

<commit_msg>refactor: MockModule 的测试用例<commit_after>const chai = require('chai');
const expect = chai.expect;

const MockModule = require('../../../src/mocker/MockModule');
const MockModuleConfig = require('../../../src/mocker/MockModuleConfig');

describe('./mocker/MockModule', () => {
    describe('check json-file.json', () => {
        let mockModule;

        before(() => {
            mockModule = new MockModule('json-file', require('../../data/fixtures/mock_modules/json-file'));
            // console.log(mockModule);
        });

        it('should return object', () => {
            expect(mockModule).to.be.an('object');
        });

        it('should contain some fields', () => {
            expect(mockModule).to.have.all.keys('name', 'module', 'config');
        });

        it('mockModule.config is instanceof MockModuleConfig ', () => {
            expect(mockModule.config).to.be.an.instanceof(MockModuleConfig);
        });

        it('should return correct value', () => {
            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'json-file.json',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-promise.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-promise', require('../../data/fixtures/mock_modules/return-function-promise'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-promise',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-pure.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-pure', require('../../data/fixtures/mock_modules/return-function-pure'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-pure',
                        age: 16
                    });
                });
        });
    });

    describe('check return-function-with-param.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-function-with-param', require('../../data/fixtures/mock_modules/return-function-with-param'));

            return mockModule.getResult('name-a', 2, 'not-exist')
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-function-with-param',
                        desc: 'a=name-a,b=2',
                        age: 16
                    });
                });
        });
    });

    describe('check return-plain-object.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-plain-object', require('../../data/fixtures/mock_modules/return-plain-object'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-plain-object',
                        age: 16
                    });
                });
        });
    });

    describe('check return-promise.js', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('return-promise', require('../../data/fixtures/mock_modules/return-promise'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'return-promise',
                        age: 16
                    });
                });
        });
    });

    describe('check no-config', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('no-config', require('../../data/fixtures/mock_modules/no-config'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'no-config',
                        age: 16
                    });
                });
        });
    });

    describe('check exist-config', () => {
        it('should return correct value', () => {
            let mockModule = new MockModule('exist-config', require('../../data/fixtures/mock_modules/exist-config'), require('../../data/fixtures/mock_modules/exist-config/config'));

            return mockModule.getResult()
                .then((data) => {
                    expect(data).to.eql({
                        name: 'exist-config',
                        age: 16
                    });
                });
        });
    });
});

<|endoftext|>


<commit_before>(function() {

	//为了开发简单快速，目前所有代码业务都写在这一个文件里~
	//
	//
	var isLoading;

	function extend(o, p) {
		for (var i in p) {
			o[i] = p[i];
		}
		return o;
	}

	function bind() {
		var hash = window.location.hash.slice(1);
		$('[node-type=nav_li]').removeClass('active');
		if (hash == 'home' || hash === '') {
			home_line();
		} else if (hash == 'public') {
			public_line();
		}
	}

	$('[node-type=nav_li]').click(function() {
		$('[data-toggle=collapse]').trigger('click');
	});

	var contentWrap = $('[node-type=time_line]');

	contentWrap.html('正在努力加载中...');

	function get_line(url, page, cb) {
		if (!isLoading) {
			isLoading = true;
			contentWrap.html('正在努力加载中...');
			$.ajax({
				url: url,
				data: {
					count: 4,
					feature: 1,
					page: page
				},
				dataType: 'json',
				success: function(json) {
					isLoading = false;
					cb(json);
				}
			});
		}
	}

	var regUrl = /(htt(p|ps):\/\/[A-Za-z\.\d\/\?\&\;\=\-\#_(\u4e00-\u9fa5)]+)/gi;

	function stripscript(s) {
		var pattern = new RegExp("[`~!#$^&*()=|{}':;',\\[\\].<>/?~！#￥……&*（）——|{}【】‘“'。、]");
		var rs = "";
		for (var i = 0; i < s.length; i++) {
			rs = rs + s.substr(i, 1).replace(pattern, ' ');
		}
		return rs;
	}

	function fixText(txt) {
		txt = txt.replace(regUrl, '');
		txt = txt.replace(/\[*.\]/g, '');
		txt = stripscript(txt);
		return txt;
	}

	function appendContent(items) {
		var html = '
';
		items.forEach(function(item) {
			html += '
' + item.user.name + ' 说: ' + fixText(item.text) + '<button node-type="add_one" class="btn btn-mini">加入播放</button>
';
		});
		html += '
';
		contentWrap.html(html);
	}

	function home_line() {
		$('[node-type=home_line]').parent().addClass('active');
		get_line('/home_list', 1, function(json) {
			appendContent(json.items);
		});
	}

	function public_line() {
		$('[node-type=public_line]').parent().addClass('active');
		get_line('/public_list', 1, function(json) {
			appendContent(json.items);
		});
	}

	bind();

	$(window).bind('hashchange', bind);

	$('[node-type=cn]').addClass('active');

	function events() {
		this.map = {};
	}

	extend(events.prototype, {
		trigger: function(name, args) {
			var cbs = this.map[name];
			if (cbs) {
				cbs.forEach(function(fn) {
					fn.apply(this, args);
				});
			}
		},
		on: function(name, cb) {
			if (this.map[name]) {
				this.map[name].push(cb);
			} else {
				this.map[name] = [cb];
			}
		}
	});

	//声音播放类
	//
	function weiboTspeech() {
		this.supportSpeech = (window.speechSynthesis && window.SpeechSynthesisUtterance);
		if (!this.supportSpeech) {
			alert('您当前的环境不支持微博听,请使用ios设备浏览本站');
		}
		this.speeks = [];
		this.options = {
			lang: 'zh-CN',
			volume: 44,
			rate: 0.5,
			pitch: 0.8
		};
		this.speech = new SpeechSynthesisUtterance();
		this.events = new events();
	}

	weiboTspeech.prototype = {
		constructor: weiboTspeech,
		play: function() {
			if (!speechSynthesis.speaking && this.speeks.length) {
				var msg = this.speeks.shift();
				this.speech.text = msg;
				speechSynthesis.speak(this.speech);
				this.events.trigger('play');
			}
		},
		add: function(msg) {
			this.speeks.push(msg);
			this.events.trigger('add');
			return this;
		},
		init: function() {
			this.set(this.options);
			this.bindEvents(this.speech);
			return this;
		},
		bindEvents: function(speech) {
			var self = this;
			speech.onstart = function() {
				self.events.trigger('start', self);
			};
			speech.onend = function() {
				self.play();
				self.events.trigger('end', self);
			};
			speech.onerror = function(e) {
				self.events.trigger('error', self);
			};
			speech.onpause = function() {
				self.events.trigger('pause', self);
			};
			speech.onresume = function() {
				self.events.trigger('resume', self);
			};
			return this;
		},
		set: function(options) {
			extend(this.speech,options);
			return this;
		},
		clear: function() {
			this.cancel();
			this.speaks = [];
		}
	};

	var myspeaker = new weiboTspeech();
	myspeaker.init();

	if (myspeaker.supportSpeech) {
		$('[node-type=start]').click(function() {
			if (myspeaker.speeks.length) {
				myspeaker.play();
			} else {
				alert('请向播放列表添加微博');
			}
		});
		$('[node-type=add_all]').click(function() {
			contentWrap.find('[node-type=text]').each(function(index,item) {
				var text = $(item).text();
				myspeaker.add(text);
			});
		});

		contentWrap.on('click','[node-type=add_one]',function(){
            		myspeaker.add($(this).prev().text());
        	});

		$('[node-type=cn]').click(function(){
			myspeaker.set({lang:'zh-CN'});
		});
		$('[node-type=hk]').click(function(){
			myspeaker.set({lang:'zh-HK'});
		});
		$('[node-type=tw]').click(function(){
			myspeaker.set({lang:'zh-TW'});
		});

		function showNum() {
			$('[node-type=count]').text(myspeaker.speeks.length);
		}

		myspeaker.events.on('add', showNum);
		myspeaker.events.on('end', showNum);
	}

})();

<commit_msg>修改count<commit_after>(function() {

	//为了开发简单快速，目前所有代码业务都写在这一个文件里~
	//
	//
	var isLoading;

	function extend(o, p) {
		for (var i in p) {
			o[i] = p[i];
		}
		return o;
	}

	function bind() {
		var hash = window.location.hash.slice(1);
		$('[node-type=nav_li]').removeClass('active');
		if (hash == 'home' || hash === '') {
			home_line();
		} else if (hash == 'public') {
			public_line();
		}
	}

	$('[node-type=nav_li]').click(function() {
		$('[data-toggle=collapse]').trigger('click');
	});

	var contentWrap = $('[node-type=time_line]');

	contentWrap.html('正在努力加载中...');

	function get_line(url, page, cb) {
		if (!isLoading) {
			isLoading = true;
			contentWrap.html('正在努力加载中...');
			$.ajax({
				url: url,
				data: {
					count: 20,
					feature: 1,
					page: page
				},
				dataType: 'json',
				success: function(json) {
					isLoading = false;
					cb(json);
				}
			});
		}
	}

	var regUrl = /(htt(p|ps):\/\/[A-Za-z\.\d\/\?\&\;\=\-\#_(\u4e00-\u9fa5)]+)/gi;

	function stripscript(s) {
		var pattern = new RegExp("[`~!#$^&*()=|{}':;',\\[\\].<>/?~！#￥……&*（）——|{}【】‘“'。、]");
		var rs = "";
		for (var i = 0; i < s.length; i++) {
			rs = rs + s.substr(i, 1).replace(pattern, ' ');
		}
		return rs;
	}

	function fixText(txt) {
		txt = txt.replace(regUrl, '');
		txt = txt.replace(/\[*.\]/g, '');
		txt = stripscript(txt);
		return txt;
	}

	function appendContent(items) {
		var html = '
';
		items.forEach(function(item) {
			html += '
' + item.user.name + ' 说: ' + fixText(item.text) + '<button node-type="add_one" class="btn btn-mini">加入播放</button>
';
		});
		html += '
';
		contentWrap.html(html);
	}

	function home_line() {
		$('[node-type=home_line]').parent().addClass('active');
		get_line('/home_list', 1, function(json) {
			appendContent(json.items);
		});
	}

	function public_line() {
		$('[node-type=public_line]').parent().addClass('active');
		get_line('/public_list', 1, function(json) {
			appendContent(json.items);
		});
	}

	bind();

	$(window).bind('hashchange', bind);

	$('[node-type=cn]').addClass('active');

	function events() {
		this.map = {};
	}

	extend(events.prototype, {
		trigger: function(name, args) {
			var cbs = this.map[name];
			if (cbs) {
				cbs.forEach(function(fn) {
					fn.apply(this, args);
				});
			}
		},
		on: function(name, cb) {
			if (this.map[name]) {
				this.map[name].push(cb);
			} else {
				this.map[name] = [cb];
			}
		}
	});

	//声音播放类
	//
	function weiboTspeech() {
		this.supportSpeech = (window.speechSynthesis && window.SpeechSynthesisUtterance);
		if (!this.supportSpeech) {
			alert('您当前的环境不支持微博听,请使用ios设备浏览本站');
		}
		this.speeks = [];
		this.options = {
			lang: 'zh-CN',
			volume: 44,
			rate: 0.5,
			pitch: 0.8
		};
		this.speech = new SpeechSynthesisUtterance();
		this.events = new events();
	}

	weiboTspeech.prototype = {
		constructor: weiboTspeech,
		play: function() {
			if (!speechSynthesis.speaking && this.speeks.length) {
				var msg = this.speeks.shift();
				this.speech.text = msg;
				speechSynthesis.speak(this.speech);
				this.events.trigger('play');
			}
		},
		add: function(msg) {
			this.speeks.push(msg);
			this.events.trigger('add');
			return this;
		},
		init: function() {
			this.set(this.options);
			this.bindEvents(this.speech);
			return this;
		},
		bindEvents: function(speech) {
			var self = this;
			speech.onstart = function() {
				self.events.trigger('start', self);
			};
			speech.onend = function() {
				self.play();
				self.events.trigger('end', self);
			};
			speech.onerror = function(e) {
				self.events.trigger('error', self);
			};
			speech.onpause = function() {
				self.events.trigger('pause', self);
			};
			speech.onresume = function() {
				self.events.trigger('resume', self);
			};
			return this;
		},
		set: function(options) {
			extend(this.speech,options);
			return this;
		},
		clear: function() {
			this.cancel();
			this.speaks = [];
		}
	};

	var myspeaker = new weiboTspeech();
	myspeaker.init();

	if (myspeaker.supportSpeech) {
		$('[node-type=start]').click(function() {
			if (myspeaker.speeks.length) {
				myspeaker.play();
			} else {
				alert('请向播放列表添加微博');
			}
		});
		$('[node-type=add_all]').click(function() {
			contentWrap.find('[node-type=text]').each(function(index,item) {
				var text = $(item).text();
				myspeaker.add(text);
			});
		});

		contentWrap.on('click','[node-type=add_one]',function(){
            		myspeaker.add($(this).prev().text());
        	});

		$('[node-type=cn]').click(function(){
			myspeaker.set({lang:'zh-CN'});
		});
		$('[node-type=hk]').click(function(){
			myspeaker.set({lang:'zh-HK'});
		});
		$('[node-type=tw]').click(function(){
			myspeaker.set({lang:'zh-TW'});
		});

		function showNum() {
			$('[node-type=count]').text(myspeaker.speeks.length);
		}

		myspeaker.events.on('add', showNum);
		myspeaker.events.on('end', showNum);
	}

})();

<|endoftext|>


<commit_before>var MemoryStore = require('../../../shared/store/memory_store'),
    should = require('chai').should();

describe('MemoryStore', function() {
  var store;

  beforeEach(function() {
    store = new MemoryStore();
  });

  it("should undefined for missing keys", function() {
    var value = store.get('foobar');
    should.equal(value, void 0);
  });

  it("should set and get a key + value", function() {
    var value;

    store.set('cached_value', 42);
    value = store.get('cached_value');
    should.equal(value, 42);

    store.set('cached_value', 'new value');
    value = store.get('cached_value');
    should.equal(value, 'new value');
  });

  it("should be able to clear a key", function() {
    var value;

    store.set('somethin', 'some value');
    store.clear('somethin');
    value = store.get('somethin');

    should.equal(value, void 0);
  });

  it("should be able to expire a key", function(done) {
    var value;

    store.set('will_expire', '1234', 0.01);
    value = store.get('will_expire');
    should.equal(value, '1234');

    setTimeout(function() {
      value = store.get('will_expire');
      should.equal(value, void 0);
      done();
    }, 11);
  });
});
<commit_msg>improve assertions<commit_after>var MemoryStore = require('../../../shared/store/memory_store'),
    should = require('chai').should();

describe('MemoryStore', function() {
  var store;

  beforeEach(function() {
    store = new MemoryStore();
  });

  it("should undefined for missing keys", function() {
    var value = store.get('foobar');
    should.not.exist(value);
  });

  it("should set and get a key + value", function() {
    store.set('cached_value', 42);
    store.get('cached_value').should.equal(42);

    store.set('cached_value', 'new value');
    store.get('cached_value').should.equal('new value');
  });

  it("should be able to clear a key", function() {
    var value;

    store.set('somethin', 'some value');
    store.clear('somethin');
    value = store.get('somethin');

    should.not.exist(value);
  });

  it("should be able to expire a key", function(done) {
    var value;

    store.set('will_expire', '1234', 0.01);
    value = store.get('will_expire');
    value.should.equal('1234');

    setTimeout(function() {
      value = store.get('will_expire');
      should.not.exist(value);
      done();
    }, 11);
  });
});
<|endoftext|>


<commit_before>
var Regularity = require('../lib/regularity.js');

describe("Regularity", function() {
    var regularity;

    beforeEach(function() {
        regularity = new Regularity();
    });

    it("is an object constructor", function() {
        expect(typeof regularity).toBe('object');
    });

    describe("return from done()", function() {
        it("is a function", function() {
            expect(typeof regularity.done().test).toBe('function');
        });

        it (" has correct source", function() {
            expect(regularity.done().source).toBe('(?:)');
        });
    });

    describe("escapes regexp special chars", function() {
        var chars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']',
          '{', '}' ];

        function testEscapedChar(c) {
            it ("escapes '" + c + "'", function() {
                var regex = regularity.append( c ).done();
                expect(regex.source).toBe("\\" + c);
            });
        }
        for(var i = 0; i < chars.length; i++ ) { testEscapedChar(chars[i]); }
    });

    describe("#startsWith", function(){
        it("matches positive", function(){
            var regex = regularity.startWith('a').done();
            expect(regex.test('abcde')).toBeTruthy();
        });

        it("matches negative", function(){
            var regex = regularity.startWith('a').done();
            expect(regex.test('edcba')).toBeFalsy();
        });
    });

    describe("#endWith", function(){
        it("matches positive", function(){
            var regex = regularity.endWith('a').done();
            expect(regex.test('edcba')).toBeTruthy();
        });

        it("matches negative", function(){
            var regex = regularity.endWith('a').done();
            expect(regex.test('abcde')).toBeFalsy();
        });
    });

    describe("#maybe", function(){
        it("matches with pattern", function(){
            var regex = regularity.maybe('a').done();
            expect(regex.test('aaaa')).toBeTruthy();
        });

        it("matches without pattern", function(){
            var regex = regularity.maybe('a').done();
            expect(regex.test('bbbb')).toBeTruthy();
        });
    });

    describe("#oneOf", function(){
        it("matches with one", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('addd')).toBeTruthy();
        });

        it("matches with other", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('dbb')).toBeTruthy();
        });

        it("does not match", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('bccddd')).toBeTruthy();
        });
    });

    describe("#between", function(){
        it("doesn't match under lower bound", function(){
            var regex = regularity.between([2,3], 'a').done();
            expect(regex.test('addd')).toBeFalsy();
        });
    });

});

// append: [Function],

// between: [Function],

// zeroOrMore: [Function],

// oneOrMore: [Function],

// atLeast: [Function],

// atMost: [Function],

// insensitive: [Function],

// global: [Function],

// multiLine: [Function],

// done: [Function],

// regexp: [Function] }

<commit_msg>Rewrite tests for 'done' into a more meaningful check.<commit_after>
var Regularity = require('../lib/regularity.js');

describe("Regularity", function() {
    var regularity;

    beforeEach(function() {
        regularity = new Regularity();
    });

    it("is an object constructor", function() {
        expect(typeof regularity).toBe('object');
    });

    describe("return from done()", function() {
        it("is a RegExp instance", function() {
            expect(regularity.done() instanceof RegExp).toBe(true);
        });

        it ("returns an empty regexp by default", function() {
            expect(regularity.done()).toEqual(new RegExp());
        });
    });

    describe("escapes regexp special chars", function() {
        var chars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']',
          '{', '}' ];

        function testEscapedChar(c) {
            it ("escapes '" + c + "'", function() {
                var regex = regularity.append( c ).done();
                expect(regex.source).toBe("\\" + c);
            });
        }
        for(var i = 0; i < chars.length; i++ ) { testEscapedChar(chars[i]); }
    });

    describe("#startsWith", function(){
        it("matches positive", function(){
            var regex = regularity.startWith('a').done();
            expect(regex.test('abcde')).toBeTruthy();
        });

        it("matches negative", function(){
            var regex = regularity.startWith('a').done();
            expect(regex.test('edcba')).toBeFalsy();
        });
    });

    describe("#endWith", function(){
        it("matches positive", function(){
            var regex = regularity.endWith('a').done();
            expect(regex.test('edcba')).toBeTruthy();
        });

        it("matches negative", function(){
            var regex = regularity.endWith('a').done();
            expect(regex.test('abcde')).toBeFalsy();
        });
    });

    describe("#maybe", function(){
        it("matches with pattern", function(){
            var regex = regularity.maybe('a').done();
            expect(regex.test('aaaa')).toBeTruthy();
        });

        it("matches without pattern", function(){
            var regex = regularity.maybe('a').done();
            expect(regex.test('bbbb')).toBeTruthy();
        });
    });

    describe("#oneOf", function(){
        it("matches with one", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('addd')).toBeTruthy();
        });

        it("matches with other", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('dbb')).toBeTruthy();
        });

        it("does not match", function(){
            var regex = regularity.oneOf(['a','bb','ccc']).done();
            expect(regex.test('bccddd')).toBeTruthy();
        });
    });

    describe("#between", function(){
        it("doesn't match under lower bound", function(){
            var regex = regularity.between([2,3], 'a').done();
            expect(regex.test('addd')).toBeFalsy();
        });
    });

});

// append: [Function],

// between: [Function],

// zeroOrMore: [Function],

// oneOrMore: [Function],

// atLeast: [Function],

// atMost: [Function],

// insensitive: [Function],

// global: [Function],

// multiLine: [Function],

// done: [Function],

// regexp: [Function] }

<|endoftext|>


<commit_before>'use strict'

const co = require('co')
const cli = require('heroku-cli-util')

function * run (context, heroku) {
  const host = require('../../lib/host')
  const fetcher = require('../../lib/fetcher')(heroku)
  const util = require('../../lib/util')
  const {app, args, flags} = context
  let db = yield fetcher.addon(app, args.database)
  let all = flags.all

  if (all && 'name' in flags) {
    throw new Error('cannot pass both --all and --name')
  }
  let cred = flags.name || 'default'
  if ((cred === 'default' || all) && 'force' in flags) {
    if (all) {
      throw new Error('Cannot force rotate all credentials: the default credential cannot be force rotated.')
    } else {
      throw new Error('Cannot force rotate the default credential.')
    }
  }
  if (util.starterPlan(db) && cred !== 'default') {
    throw new Error(`Only one default credential is supported for Hobby tier databases.`)
  }
  let attachments = yield heroku.get(`/addons/${db.name}/addon-attachments`)
  if (flags.name) {
    attachments = attachments.filter(a => a.namespace === `credential:${cred}`)
  }

  let warnings = []
  if (!flags.all) {
    warnings.push(`The password for the ${cred} credential will rotate.`)
  }
  if (flags.all || flags.force || cred === 'default') {
    warnings.push(`Connections will be reset and applications will be restarted.`)
  } else {
    warnings.push(`Connections older than 30 minutes will be reset, and a temporary rotation username will be used during the process.`)
  }
  if (attachments.length > 0) {
    warnings.push(`This command will affect the app${(attachments.length > 1) ? 's' : ''} ${[...new Set(attachments.map(c => cli.color.app(c.app.name)))].sort().join(', ')}.`)
  }

  yield cli.confirmApp(app, flags.confirm, `WARNING: Destructive Action
${warnings.join('\n')}`)

  let body = flags.force ? {host: host(db), force: true} : {host: host(db)}

  if (all) {
    yield cli.action(`Rotating all credentials on ${cli.color.addon(db.name)}`, co(function * () {
      yield heroku.post(`/postgres/v0/databases/${db.name}/credentials_rotation`,
                        body)
    }))
  } else {
    yield cli.action(`Rotating ${cred} on ${cli.color.addon(db.name)}`, co(function * () {
      yield heroku.post(`/postgres/v0/databases/${db.name}/credentials/${encodeURIComponent(cred)}/credentials_rotation`,
                        body)
    }))
  }
}

module.exports = {
  topic: 'pg',
  command: 'credentials:rotate',
  description: 'rotate the database credentials',
  needsApp: true,
  needsAuth: true,
  flags: [
    {name: 'name', char: 'n', description: 'which credentials to rotate (default credentials if not specified)', hasValue: true},
    {name: 'all', description: 'rotate all credentials', hasValue: false},
    {name: 'confirm', char: 'c', hasValue: true},
    {name: 'force', description: 'forces rotating the targeted credentials', hasValue: false}
  ],
  args: [{name: 'database', optional: true}],
  run: cli.command({preauth: true}, co.wrap(run))
}
<commit_msg>Avoid using 'in' to check for flags<commit_after>'use strict'

const co = require('co')
const cli = require('heroku-cli-util')

function * run (context, heroku) {
  const host = require('../../lib/host')
  const fetcher = require('../../lib/fetcher')(heroku)
  const util = require('../../lib/util')
  const {app, args, flags} = context
  let db = yield fetcher.addon(app, args.database)
  let all = flags.all

  if (all && flags.name !== undefined) {
    throw new Error('cannot pass both --all and --name')
  }
  let cred = flags.name || 'default'
  if ((cred === 'default' || all) && flags.force) {
    if (all) {
      throw new Error('Cannot force rotate all credentials: the default credential cannot be force rotated.')
    } else {
      throw new Error('Cannot force rotate the default credential.')
    }
  }
  if (util.starterPlan(db) && cred !== 'default') {
    throw new Error(`Only one default credential is supported for Hobby tier databases.`)
  }
  let attachments = yield heroku.get(`/addons/${db.name}/addon-attachments`)
  if (flags.name) {
    attachments = attachments.filter(a => a.namespace === `credential:${cred}`)
  }

  let warnings = []
  if (!flags.all) {
    warnings.push(`The password for the ${cred} credential will rotate.`)
  }
  if (flags.all || flags.force || cred === 'default') {
    warnings.push(`Connections will be reset and applications will be restarted.`)
  } else {
    warnings.push(`Connections older than 30 minutes will be reset, and a temporary rotation username will be used during the process.`)
  }
  if (attachments.length > 0) {
    warnings.push(`This command will affect the app${(attachments.length > 1) ? 's' : ''} ${[...new Set(attachments.map(c => cli.color.app(c.app.name)))].sort().join(', ')}.`)
  }

  yield cli.confirmApp(app, flags.confirm, `WARNING: Destructive Action
${warnings.join('\n')}`)

  let body = flags.force ? {host: host(db), force: true} : {host: host(db)}

  if (all) {
    yield cli.action(`Rotating all credentials on ${cli.color.addon(db.name)}`, co(function * () {
      yield heroku.post(`/postgres/v0/databases/${db.name}/credentials_rotation`,
                        body)
    }))
  } else {
    yield cli.action(`Rotating ${cred} on ${cli.color.addon(db.name)}`, co(function * () {
      yield heroku.post(`/postgres/v0/databases/${db.name}/credentials/${encodeURIComponent(cred)}/credentials_rotation`,
                        body)
    }))
  }
}

module.exports = {
  topic: 'pg',
  command: 'credentials:rotate',
  description: 'rotate the database credentials',
  needsApp: true,
  needsAuth: true,
  flags: [
    {name: 'name', char: 'n', description: 'which credentials to rotate (default credentials if not specified)', hasValue: true},
    {name: 'all', description: 'rotate all credentials', hasValue: false},
    {name: 'confirm', char: 'c', hasValue: true},
    {name: 'force', description: 'forces rotating the targeted credentials', hasValue: false}
  ],
  args: [{name: 'database', optional: true}],
  run: cli.command({preauth: true}, co.wrap(run))
}
<|endoftext|>


<commit_before>// register the directive with your app module
var app = angular.module('ddApp', ['ngAnimate', 'ui.bootstrap', 'rzModule']);

// function referenced by the drop target
app.controller('ddController', ['$scope', '$uibModal', '$log', '$timeout', '$http', function($scope, $uibModal, $log, $timeout, $http){

    $scope.z = 0;
    $scope.placedDropTiles = 0;
    $scope.startedScoring = false;
    $scope.startedTime = false;
    $scope.time = 0;

    $scope.sliderOptions = {
        floor: 0,
        ceil: 0,
        showSelectionBar: true,
        showTicksValues: true
    };

    $scope.tiles = {};

    $http.get("/api/runs/"+runId+"?populate=true").then(function(response){
        $scope.height = response.data.height;
        $scope.sliderOptions.ceil = $scope.height - 1;
        $scope.width = response.data.width;
        $scope.length = response.data.length;
        $scope.team = response.data.team;
        $scope.field = response.data.field;

        $scope.numberOfDropTiles = response.data.numberOfDropTiles;;
        $scope.rescuedVictims = response.data.rescuedVictims;

        for(var i = 0; i < response.data.tiles.length; i++){
            $scope.tiles[response.data.tiles[i].x + ',' +
                         response.data.tiles[i].y + ',' +
                         response.data.tiles[i].z] = response.data.tiles[i];
        }

        $scope.score = response.data.score;
        $scope.showedUp = response.data.showedUp;
        $scope.LoPs = response.data.LoPs;
        // Verified time by timekeeper
        $scope.minutes = response.data.time.minutes;;
        $scope.seconds = response.data.time.seconds;

        console.log($scope.tiles);
    }, function(response){
        console.log("Error: " + response.statusText);
    });

    $scope.range = function(n){
        arr = [];
        for (var i=0; i < n; i++) {
            arr.push(i);
        }
        return arr;
    }

    $scope.startScoring = function(){
        if($scope.numberOfDropTiles - $scope.placedDropTiles > 0) {
            alert("All checkpoints are not yet placed.");
            return;
        }
        // Start/stop scoring
        $scope.startedScoring = !$scope.startedScoring;
    }

    $scope.decrement = function(index){
        if($scope.LoPs[index])
            $scope.LoPs[index]--;
        else
            $scope.LoPs[index] = 0;
        if($scope.LoPs[index] < 0)
            $scope.LoPs[index] = 0;
    }
    $scope.increment = function(index){
        if($scope.LoPs[index])
            $scope.LoPs[index]++;
        else
            $scope.LoPs[index] = 1;
        if($scope.LoPs[index] >= 3)
            $timeout(function(){alert("The team *may* move to next drop tile now.");},20);
    }

    var tick = function() {
        $scope.time += 1000;
        if($scope.startedTime)
            $timeout(tick, 1000);
    }

    $scope.startTime = function(){
        // Start/stop timer
        $scope.startedTime = !$scope.startedTime;
        if($scope.startedTime){
            // Start the timer
            $timeout(tick, $scope.tickInterval);
        }
    }

    $scope.totalNumberOf = function(objects){
        return objects.gaps + objects.speedbumps + objects.obstacles + objects.intersections;
    }

    $scope.doScoring = function(x,y,z){
        var tile = $scope.tiles[x+','+y+','+z];
        // If this is not a created tile
        if(!tile)
            return;

        var total = $scope.totalNumberOf(tile.items);

        // If the run is not started, we can place drop pucks on this tile
        if(!$scope.startedScoring){
            // We can only place drop markers on tiles without scoring elements (rule 3.3.4)
            if(total > 0){
                alert("Place drop markers on tiles without scoring elements (rule 3.3.4)");
            }else{
                if($scope.numberOfDropTiles - $scope.placedDropTiles > 0) {
                    if(tile.scoredItems.dropTiles.length > 0){
                        tile.scoredItems.dropTiles = [];
                        $scope.placedDropTiles--;
                    }else{
                        tile.scoredItems.dropTiles = [];
                        for(var i = 0; i < tile.index.length; i++){
                            tile.scoredItems.dropTiles.push(false);
                        }
                        $scope.placedDropTiles++;
                    }
                }else if(tile.scoredItems.dropTiles.length > 0){
                    tile.scoredItems.dropTiles = [];
                    $scope.placedDropTiles--;
                }
            }

        // Match has started!
        }else{
            // Add the number of possible passes for drop tiles
            if(tile.scoredItems.dropTiles.length > 0) {
                total += tile.scoredItems.dropTiles.length;
            }

            if(total > 1){
                // Show modal
                $scope.open(x,y,z);
            }else if(total==1){
                if(tile.items.gaps>0)
                    tile.scoredItems.gaps[0] = !tile.scoredItems.gaps[0];
                else if(tile.items.speedbumps)
                    tile.scoredItems.speedbumps[0] = !tile.scoredItems.speedbumps[0];
                else if(tile.items.obstacles)
                    tile.scoredItems.obstacles[0] = !tile.scoredItems.obstacles[0];
                else if(tile.items.intersections)
                    tile.scoredItems.intersections[0] = !tile.scoredItems.intersections[0];
                else if(tile.scoredItems.dropTiles.length > 0)
                    tile.scoredItems.dropTiles[0] = !tile.scoredItems.dropTiles[0];
            }
        }
    }

    $scope.open = function(x,y,z) {
        var modalInstance = $uibModal.open({
            animation: true,
            templateUrl: '/templates/line_judge_modal.html',
            controller: 'ModalInstanceCtrl',
            size: 'sm',
            resolve: {
                tile: function () {
                    return $scope.tiles[x+','+y+','+z];
                }
            }
        });
    };

}]);

// Please note that $uibModalInstance represents a modal window (instance) dependency.
// It is not the same as the $uibModal service used above.

app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, tile) {
    $scope.tile = tile;
    $scope.ok = function () {
        $uibModalInstance.close();
    };

    $scope.cancel = function () {
        $uibModalInstance.dismiss('cancel');
    };
});

app.directive('tile', function() {
    return {
        scope: {
            tile: '='
        },
        restrict: 'E',
        templateUrl: '/templates/tile.html',
        link : function($scope, element, attrs){

            $scope.tileStatus = function(tile){
                // If this is a non-existent tile
                if(!tile)
                    return;
                var successfully = 0;
                var possible = 0;

                var count = function(list){
                    for(var i = 0; i < list.length; i++){
                        if(list[i])
                            successfully++;
                        possible++;
                    }
                }
                count(tile.scoredItems.gaps);
                count(tile.scoredItems.speedbumps);
                count(tile.scoredItems.intersections);
                count(tile.scoredItems.obstacles);
                if(tile.scoredItems.dropTiles.length > 0)
                    count(tile.scoredItems.dropTiles);

                if(possible > 0 && successfully == possible)
                    return "done";
                else if(successfully > 0)
                    return "halfdone";
                else if(possible > 0)
                    return "undone";
                else
                    return "";
            }

            $scope.rotateRamp = function(direction){
                switch(direction){
                case "bottom":
                    return "rot0";
                case "top":
                    return "rot180";
                case "left":
                    return "rot90";
                case "right":
                    return "rot270";
                }
            }

        }
    };
});

<commit_msg>Try to save scoring during round<commit_after>// register the directive with your app module
var app = angular.module('ddApp', ['ngAnimate', 'ui.bootstrap', 'rzModule']);

// function referenced by the drop target
app.controller('ddController', ['$scope', '$uibModal', '$log', '$timeout', '$http', function($scope, $uibModal, $log, $timeout, $http){

    $scope.z = 0;
    $scope.placedDropTiles = 0;
    $scope.startedScoring = false;
    $scope.startedTime = false;
    $scope.time = 0;

    $scope.sliderOptions = {
        floor: 0,
        ceil: 0,
        showSelectionBar: true,
        showTicksValues: true
    };

    $scope.tiles = {};

    $http.get("/api/runs/"+runId+"?populate=true").then(function(response){
        $scope.height = response.data.height;
        $scope.sliderOptions.ceil = $scope.height - 1;
        $scope.width = response.data.width;
        $scope.length = response.data.length;
        $scope.team = response.data.team;
        $scope.field = response.data.field;

        $scope.numberOfDropTiles = response.data.numberOfDropTiles;;
        $scope.rescuedVictims = response.data.rescuedVictims;

        for(var i = 0; i < response.data.tiles.length; i++){
            $scope.tiles[response.data.tiles[i].x + ',' +
                         response.data.tiles[i].y + ',' +
                         response.data.tiles[i].z] = response.data.tiles[i];
        }

        $scope.score = response.data.score;
        $scope.showedUp = response.data.showedUp;
        $scope.LoPs = response.data.LoPs;
        // Verified time by timekeeper
        $scope.minutes = response.data.time.minutes;;
        $scope.seconds = response.data.time.seconds;

        console.log($scope.tiles);
    }, function(response){
        console.log("Error: " + response.statusText);
    });

    $scope.range = function(n){
        arr = [];
        for (var i=0; i < n; i++) {
            arr.push(i);
        }
        return arr;
    }

    $scope.startScoring = function(){
        if($scope.numberOfDropTiles - $scope.placedDropTiles > 0) {
            alert("All checkpoints are not yet placed.");
            return;
        }
        // Start/stop scoring
        $scope.startedScoring = !$scope.startedScoring;
    }

    $scope.decrement = function(index){
        if($scope.LoPs[index])
            $scope.LoPs[index]--;
        else
            $scope.LoPs[index] = 0;
        if($scope.LoPs[index] < 0)
            $scope.LoPs[index] = 0;
    }
    $scope.increment = function(index){
        if($scope.LoPs[index])
            $scope.LoPs[index]++;
        else
            $scope.LoPs[index] = 1;
        if($scope.LoPs[index] >= 3)
            $timeout(function(){alert("The team *may* move to next drop tile now.");},20);
    }

    var tick = function() {
        $scope.time += 1000;
        if($scope.startedTime)
            $timeout(tick, 1000);
    }

    $scope.startTime = function(){
        // Start/stop timer
        $scope.startedTime = !$scope.startedTime;
        if($scope.startedTime){
            // Start the timer
            $timeout(tick, $scope.tickInterval);
        }
    }

    $scope.totalNumberOf = function(objects){
        return objects.gaps + objects.speedbumps + objects.obstacles + objects.intersections;
    }

    $scope.doScoring = function(x,y,z){
        var tile = $scope.tiles[x+','+y+','+z];
        // If this is not a created tile
        if(!tile)
            return;

        var total = $scope.totalNumberOf(tile.items);

        // If the run is not started, we can place drop pucks on this tile
        if(!$scope.startedScoring){
            // We can only place drop markers on tiles without scoring elements (rule 3.3.4)
            if(total > 0){
                alert("Place drop markers on tiles without scoring elements (rule 3.3.4)");
            }else{
                if($scope.numberOfDropTiles - $scope.placedDropTiles > 0) {
                    if(tile.scoredItems.dropTiles.length > 0){
                        tile.scoredItems.dropTiles = [];
                        $scope.placedDropTiles--;
                    }else{
                        tile.scoredItems.dropTiles = [];
                        for(var i = 0; i < tile.index.length; i++){
                            tile.scoredItems.dropTiles.push(false);
                        }
                        $scope.placedDropTiles++;
                    }
                }else if(tile.scoredItems.dropTiles.length > 0){
                    tile.scoredItems.dropTiles = [];
                    $scope.placedDropTiles--;
                }
                $http.post("/api/runs/"+runId+"/update", {tiles:[{tile}]}).then(function(response){
                    $scope.score = response.data.score;
                }, function(response){
                    console.log("Error: " + response.statusText);
                });
            }

        // Match has started!
        }else{
            // Add the number of possible passes for drop tiles
            if(tile.scoredItems.dropTiles.length > 0) {
                total += tile.scoredItems.dropTiles.length;
            }

            if(total == 0){
                return;
            }else if(total > 1){
                // Show modal
                $scope.open(x,y,z);
            }else if(total==1){
                if(tile.items.gaps>0)
                    tile.scoredItems.gaps[0] = !tile.scoredItems.gaps[0];
                else if(tile.items.speedbumps)
                    tile.scoredItems.speedbumps[0] = !tile.scoredItems.speedbumps[0];
                else if(tile.items.obstacles)
                    tile.scoredItems.obstacles[0] = !tile.scoredItems.obstacles[0];
                else if(tile.items.intersections)
                    tile.scoredItems.intersections[0] = !tile.scoredItems.intersections[0];
                else if(tile.scoredItems.dropTiles.length > 0)
                    tile.scoredItems.dropTiles[0] = !tile.scoredItems.dropTiles[0];
            }
            $http.post("/api/runs/"+runId+"/update", {tiles:[{tile}]}).then(function(response){
                $scope.score = response.data.score;
            }, function(response){
                console.log("Error: " + response.statusText);
            });

        }
    }

    $scope.open = function(x,y,z) {
        var modalInstance = $uibModal.open({
            animation: true,
            templateUrl: '/templates/line_judge_modal.html',
            controller: 'ModalInstanceCtrl',
            size: 'sm',
            resolve: {
                tile: function () {
                    return $scope.tiles[x+','+y+','+z];
                }
            }
        });
    };

}]);

// Please note that $uibModalInstance represents a modal window (instance) dependency.
// It is not the same as the $uibModal service used above.

app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, tile) {
    $scope.tile = tile;
    $scope.ok = function () {
        $uibModalInstance.close();
    };

    $scope.cancel = function () {
        $uibModalInstance.dismiss('cancel');
    };
});

app.directive('tile', function() {
    return {
        scope: {
            tile: '='
        },
        restrict: 'E',
        templateUrl: '/templates/tile.html',
        link : function($scope, element, attrs){

            $scope.tileStatus = function(tile){
                // If this is a non-existent tile
                if(!tile)
                    return;
                var successfully = 0;
                var possible = 0;

                var count = function(list){
                    for(var i = 0; i < list.length; i++){
                        if(list[i])
                            successfully++;
                        possible++;
                    }
                }
                count(tile.scoredItems.gaps);
                count(tile.scoredItems.speedbumps);
                count(tile.scoredItems.intersections);
                count(tile.scoredItems.obstacles);
                if(tile.scoredItems.dropTiles.length > 0)
                    count(tile.scoredItems.dropTiles);

                if(possible > 0 && successfully == possible)
                    return "done";
                else if(successfully > 0)
                    return "halfdone";
                else if(possible > 0)
                    return "undone";
                else
                    return "";
            }

            $scope.rotateRamp = function(direction){
                switch(direction){
                case "bottom":
                    return "rot0";
                case "top":
                    return "rot180";
                case "left":
                    return "rot90";
                case "right":
                    return "rot270";
                }
            }

        }
    };
});

<|endoftext|>


<commit_before>import { createMachine, interpret } from 'xstate'

// Promise实现
const promiseMachine = createMachine({
  id: 'promise',
  initial: 'pending',
  states: {
    pending: {
      on: {
        RESOLVE: { target: 'resolved' },
        REJECT: { target: 'rejected' }
      }
    },
    resolved: {
      type: 'final'
    },
    rejected: {
      type: 'final'
    }
  }
})

const promiseService = interpret(promiseMachine).onTransition(state => console.log(state.value))

promiseService.start()

promiseService.send({ type: 'RESOLVE' })
<commit_msg>fix: 调整promise<commit_after>import { createMachine, interpret, assign } from 'xstate'

// Promise实现
const promiseMachine = createMachine({
  id: 'promise',
  initial: 'pending',
  context: {
    test: 'xxx'
  },
  states: {
    pending: {
      on: {
        RESOLVE: {
          target: 'resolved',
          actions: 'resolvingAction'
        },
        REJECT: {
          target: 'rejected',
          actions: 'rejectingAction'
        }
      }
    },
    resolved: {
      type: 'final',
      invoke: {
        src: 'resolvedServicer'
      }
    },
    rejected: {
      type: 'final',
      invoke: {
        src: 'rejectedServicer'
      }
    }
  }
}, {
  actions: {
    resolvingAction: assign((context) => {
      console.log('【Promise Action】invoke resolving action!')
    }),
    rejectingAction: assign((context) => {
      console.log('【Promise Action】invoke rejecting action!')
    })
  },
  services: {
    resolvedServicer: () => () => {
      console.log('【Promise Service】invoke resolved service!')
    },
    rejectedServicer: () => (send) => {
      console.log('【Promise Service】invoke rejected service!')
    }
  }
})

const promiseService = interpret(promiseMachine).onTransition(state => console.log(`【Promise Transition】${state.value}`))

promiseService.start()

promiseService.send({ type: 'RESOLVE' }, 'resolve payload')

// Warning: Event "REJECT" was sent to stopped service "promise". This service has already reached its final state, and will not transition.
// promiseService.send({ type: 'REJECT' })

promiseService.stop()
<|endoftext|>