Compare commits

...

239 Commits

Author SHA1 Message Date
Timothy Warren 35ff14aba7 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-17 10:23:15 -05:00
Timothy Warren 84e7c45f10 Remove setup checks for removed directories
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-17 10:22:46 -05:00
Timothy Warren dcc5b928c7 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-17 10:21:27 -05:00
Timothy Warren 3f9500c74c Remove now unused image caching directories
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-17 10:19:20 -05:00
Timothy Warren 2e1f58afaf Update broken tests
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-17 10:05:14 -05:00
Timothy Warren 5a5421cee9 Use images directly from Kitsu for the rest of the views
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-17 09:59:27 -05:00
Timothy Warren f210b157ff Update user details page to pull images directly from Kitsu
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-17 08:58:59 -05:00
Timothy Warren 02d99c8a43 Update more views to use images directly from Kitsu
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-17 08:37:00 -05:00
Timothy Warren 5d90721197 Make all media images come directly from Kitsu API, including on the search pages
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-12 18:23:40 -05:00
Timothy Warren 7cd0c486bd Update more views to use direct kitsu urls for poster images 2022-01-12 18:00:00 -05:00
Timothy Warren 858910f6bd Fix more cover image stuff
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-10 17:19:28 -05:00
Timothy Warren 5204053506 Use kitsu urls for anime cover images
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-10 17:06:05 -05:00
Timothy Warren b1fecc7c95 Show images directly for anime detail pages, so cover images are pulled correctly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2022-01-10 16:56:10 -05:00
Timothy Warren 1b28f03506 Ensure that list items always have some sort of cover image
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-08 13:04:24 -05:00
Timothy Warren 962bb32f96 Remove unused placeholder images in public directory
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-07 20:27:33 -05:00
Timothy Warren 85e7b0f6bc Use image builder class to simplify createPlaceholderImage 2022-01-07 19:57:07 -05:00
Timothy Warren 1c721af0ba Create image builder class to simplify creating placeholder images 2022-01-07 19:53:31 -05:00
Timothy Warren 8c6706f7de Merge pull request 'Fix table sorting algorithm' (#41) from colwellkr/HummingBirdAnimeClient:fix-sort into develop
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
Reviewed-on: #41
2022-01-07 17:04:19 -05:00
Timothy Warren 42fa458058 Merge branch 'develop' into fix-sort
timw4mail/HummingBirdAnimeClient/pipeline/pr-develop Build started... Details
2022-01-07 17:02:19 -05:00
Kevin Colwell a0e6467f17 Removed whitespace from string.replace() call
timw4mail/HummingBirdAnimeClient/pipeline/pr-develop This commit looks good Details
2022-01-07 18:29:37 +00:00
Timothy Warren 73a69e42d3 Code style fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-07 12:33:01 -05:00
Timothy Warren 7415c3bbd6 Update Anilist GraphQL schema
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-06 12:56:46 -05:00
Timothy Warren c23001e7b1 Make Kitsu auth token error easier to recover from
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2022-01-06 12:50:26 -05:00
Kevin Colwell e6c0fe5161 Updated sorting algorithm
timw4mail/HummingBirdAnimeClient/pipeline/pr-develop This commit looks good Details
2022-01-02 22:48:20 +00:00
Kevin Colwell 6876a1d811 Update "TV Rating" to "Age Rating" and add additional classes for sorting. 2022-01-02 21:29:08 +00:00
Kevin Colwell 8a5ad3fc19 Update column headers for better consistency between pages 2022-01-02 21:16:34 +00:00
Timothy Warren ecf09b5641 Allow adding items to anime collection that are in the anime list
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-12-29 17:04:55 -05:00
Timothy Warren 90a18870fd Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-12-02 17:23:54 -05:00
Timothy Warren fe8b769cf2 Fix testsuite by disabling tests with outdated data
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-12-02 17:13:31 -05:00
Timothy Warren c012b049e1 Partially fix broken tests
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-12-02 17:08:11 -05:00
Timothy Warren f3d347f249 Update and streamline dependencies
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-12-02 16:28:57 -05:00
Timothy Warren 03daa07d84 Remove old CodeIgniter style sniffs, as it is not being used 2021-12-02 16:27:43 -05:00
Timothy Warren f9850be35c Lots of little code fixes, hides notices shown on PHP 8.1
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-12-02 16:06:34 -05:00
Timothy Warren 7b2b711133 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-19 12:28:46 -04:00
Timothy Warren 5528589230 sync Manga before Anime, as it's usually faster
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-18 12:20:03 -04:00
Timothy Warren 744c180d64 Update library creation mutation to remove now redundant userID argument
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-18 12:15:47 -04:00
Timothy Warren b7e4f6cd67 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-14 22:10:26 -04:00
Timothy Warren c317bee70d Update frontend dependencies, and update Kitsu graphql schema
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-14 22:09:50 -04:00
Timothy Warren 629f7f1045 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-13 17:05:26 -04:00
Timothy Warren f9eb3e137d Remove redundant updates on finishing a media item
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-08 22:55:54 -04:00
Timothy Warren 81cffb9f4c Update test snapshot for anime detail page with new info
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-08 19:33:25 -04:00
Timothy Warren 099c7af0d8 Show full Language names on streaming info for anime detail pages 2021-10-08 19:32:10 -04:00
Timothy Warren cfc6324971 Add airing date range to anime detail pages 2021-10-08 19:31:40 -04:00
Timothy Warren 1cb880f785 Filter out titles not in English or Japanese from media cards
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-08 18:28:30 -04:00
Timothy Warren 6c5dbe886b Show when a media item is already in the list when searching on the /add pages, resolves #38
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-08 12:16:59 -04:00
Timothy Warren 52d6cd797a Refactor media search rendering to be less redundant 2021-10-08 12:15:34 -04:00
Timothy Warren 1de4580ee7 Check user library when searching for new media 2021-10-08 12:06:08 -04:00
Timothy Warren 0518e3898b Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-07 21:56:35 -04:00
Timothy Warren 70d5519d22 Attempt to fix Jenkins build, again
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-10-07 21:51:29 -04:00
Timothy Warren 08932ba8d2 Make some anime detail pages more robust
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-10-07 21:45:17 -04:00
Timothy Warren c810b811a8 Sort libraryEvents so watch history is in correct order
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-07-30 09:37:06 -04:00
Timothy Warren cd7082b764 Update Kitsu GraphQL schema 2021-07-30 09:36:25 -04:00
Timothy Warren 4c47bbeeee Merge branch 'develop'
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-23 19:23:08 -04:00
Timothy Warren 684cf1f4d6 Fix build issue with phpstan
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-23 19:22:45 -04:00
Timothy Warren 60c4e05a66 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-23 19:01:21 -04:00
Timothy Warren 4293a501ed Update GraphQL reference schemas
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-23 19:00:44 -04:00
Timothy Warren 5346ce7d89 Drastically simplify setup for bundling js files
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-23 18:58:51 -04:00
Timothy Warren ff4e1ee471 Even less floating, please
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-21 20:20:39 -04:00
Timothy Warren 15b5640f9f We don't want our ratings to float...
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-21 20:09:03 -04:00
Timothy Warren 06250b64cb A few minor tweaks
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-21 19:35:22 -04:00
Timothy Warren 12c9fc1b2e Update filtering of MAL IDs for items to check to update
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-04-21 19:33:51 -04:00
Timothy Warren c624b51acd Fix null error on updating Anime or Manga
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-03-01 10:08:36 -05:00
Timothy Warren eb461e2f2e Coverage fix for Ion DI 2021-03-01 10:08:07 -05:00
Timothy Warren c37943edf3 Update Kitsu GraphQL schema 2021-03-01 10:06:12 -05:00
Timothy Warren 0b9fbbf042 Misc fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-26 14:42:07 -05:00
Timothy Warren f0eacc2f93 Replace Whoops with Tracy
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-23 17:08:36 -05:00
Timothy Warren 4524885a79 Remove redundant docblocks from Type classes 2021-02-23 17:08:16 -05:00
Timothy Warren 7990b3ad68 Test Type classes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-23 15:38:29 -05:00
Timothy Warren a08a45c3af Attempt to fix the build with the right extension
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-23 13:33:54 -05:00
Timothy Warren 52f02dc51e Use correct symfony polyfill
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-23 13:15:37 -05:00
Timothy Warren 160718eaa1 Fix broken tests
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-23 13:10:26 -05:00
Timothy Warren e25c68b97c Increase test coverage of Kitsu Transformer classes
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-23 13:00:30 -05:00
Timothy Warren 0a87f81768 Improve test coverage
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-23 12:00:22 -05:00
Timothy Warren 0f9dd61b6b Better handle update API errors
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-22 15:39:03 -05:00
Timothy Warren 5498383587 Update GraphQL schemas
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-22 15:38:29 -05:00
Timothy Warren 177eff911c Better catch api errors on incrementing progress 2021-02-22 15:37:35 -05:00
Timothy Warren a338a43e43 A few minor fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-18 12:48:59 -05:00
Timothy Warren 97df64bf7a Remove RoboFile
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-18 07:37:33 -05:00
Timothy Warren cd7f042acb Remove some invalid exception docblock tags 2021-02-18 07:22:10 -05:00
Timothy Warren c6677de543 Fix Content Security Policy
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-18 07:15:43 -05:00
Timothy Warren 55ff6b0213 Simplify search template functions
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-18 07:14:07 -05:00
Timothy Warren c24124a6ca Use str_contains over strpos
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-17 20:02:51 -05:00
Timothy Warren f4686544c4 Update js build dependencies
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-17 19:50:15 -05:00
Timothy Warren 472f13ee62 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-17 13:07:02 -05:00
Timothy Warren 49c6b633bf Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 15:07:25 -05:00
Timothy Warren ea14b30283 Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 15:05:17 -05:00
Timothy Warren 78426c8f82 Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 15:01:48 -05:00
Timothy Warren 34cdb09ea9 Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 14:58:33 -05:00
Timothy Warren 457501001f Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-16 14:57:43 -05:00
Timothy Warren e77cfcf029 Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-16 14:56:05 -05:00
Timothy Warren d3b45fbdc5 Make PHPStan errors CI failures
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-16 14:53:21 -05:00
Timothy Warren e8ddfd8b37 Resolve remaining PHPStan issues
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 14:43:51 -05:00
Timothy Warren db6e089d93 Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-16 12:21:52 -05:00
Timothy Warren c71ff7f38e Fix more PHPStan issues
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 19:17:39 -05:00
Timothy Warren c03bd4c040 Increase warning level of PHPStan
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 17:52:58 -05:00
Timothy Warren e59b1ffc57 Fix failing test
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 13:14:58 -05:00
Timothy Warren be74cfaceb Fix a bunch more phpstan errors
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 13:09:57 -05:00
Timothy Warren e7ec57eec5 Just go back to CheckStyle
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 12:00:44 -05:00
Timothy Warren 8a32fc4e27 Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:57:36 -05:00
Timothy Warren 26118e627f Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:54:22 -05:00
Timothy Warren aa3e73d184 Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:48:24 -05:00
Timothy Warren ede4ab437b Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:43:25 -05:00
Timothy Warren d23e6ef95f Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:40:25 -05:00
Timothy Warren 35d8db08b7 Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:27:20 -05:00
Timothy Warren 21f1ae0502 Try to report phpStan errors more directly
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 11:24:57 -05:00
Timothy Warren eb03679579 Solve more PHPStan issues
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 11:14:45 -05:00
Timothy Warren d60b1dd776 Fix code warnings for 'src/AnimeClient/AnimeClient.php'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 10:53:07 -05:00
Timothy Warren 05b6d29721 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 10:42:51 -05:00
Timothy Warren dc02627a34 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-12 10:40:03 -05:00
Timothy Warren 42004ed735 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 10:38:16 -05:00
Timothy Warren 486e7dc982 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 10:34:08 -05:00
Timothy Warren 0ad9a5094b Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 10:19:58 -05:00
Timothy Warren 39b1d02862 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 10:07:55 -05:00
Timothy Warren 442e59f6cf Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 10:04:13 -05:00
Timothy Warren 3964157212 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 09:59:29 -05:00
Timothy Warren 7a41a5d9a9 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 09:51:52 -05:00
Timothy Warren dabb6be291 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 09:42:54 -05:00
Timothy Warren 0c592b4932 Attempt to add PHPStan messages to CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-12 09:39:28 -05:00
Timothy Warren c100105fbc Yet more PHPStan fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-11 19:54:22 -05:00
Timothy Warren 103f95c07b A few more PHPStan fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-10 17:31:20 -05:00
Timothy Warren 9224751d2d Add a bumch of soundness checks suggested by PHPStan
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-10 17:17:51 -05:00
Timothy Warren ebd7f811ee Code style fixes
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-10 13:59:37 -05:00
Timothy Warren b7a2eafc0d Add tests for title uniqueness check
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-10 10:59:15 -05:00
Timothy Warren cb7a4c28e5 Handle null values better in title uniqueness check
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-08 17:03:04 -05:00
Timothy Warren ff5503af16 Fix the CI build?
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-07 08:54:20 -05:00
Timothy Warren eb2f4d95b6 Fix the CI build?
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-05 20:21:05 -05:00
Timothy Warren 86c7812f47 Fix the CI build?
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-05 20:16:55 -05:00
Timothy Warren f88b8edeed Fix the CI build?
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2021-02-05 20:05:33 -05:00
Timothy Warren 3841bba923 Fix the CI build?
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-05 17:49:19 -05:00
Timothy Warren 03505518d6 Update docs and CI
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-05 17:19:11 -05:00
Timothy Warren 0f6d8a8b0a Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-04 12:41:24 -05:00
Timothy Warren c3d74afd82 Fix snapshot test
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2021-02-04 12:35:01 -05:00
Timothy Warren d15544a182 Fix tests?
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-04 12:27:52 -05:00
Timothy Warren f7bf4c8eb9 Update PHP version in header comments
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-04 11:57:01 -05:00
Timothy Warren 4a51c6419f Fix tests
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-03 10:27:04 -05:00
Timothy Warren e13b11fe74 Merge pull request 'develop' (#37) from develop into master
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
Reviewed-on: #37
2021-02-03 10:07:11 -05:00
Timothy Warren bc50ab3e0c Update CI to PHP 8
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
timw4mail/HummingBirdAnimeClient/pipeline/pr-master Build started... Details
2021-02-03 09:52:18 -05:00
Timothy Warren 6b6c37f015 Move to PHP 8 2021-02-03 09:46:36 -05:00
Timothy Warren 461914a929 Cleanup some path related things
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2021-02-03 09:45:18 -05:00
Timothy Warren 530d6ef207 Merge pull request 'Update header year code' (#36) from colwellkr/HummingBirdAnimeClient:develop into develop
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
Reviewed-on: #36
2021-01-14 12:50:22 -05:00
Kevin Colwell 20c734b8ef Update header year code
timw4mail/HummingBirdAnimeClient/pipeline/pr-develop This commit looks good Details
2021-01-13 01:58:28 -05:00
Timothy Warren 6ca193934b Increase test coverage
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-11 15:37:55 -05:00
Timothy Warren dee4a2dad5 Fix tests for PHP8...?
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-11 14:26:54 -05:00
Timothy Warren 9d82154b2f Make sure to run tests for PHP8
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-12-11 10:26:24 -05:00
Timothy Warren 2a8e6aa3ed Fix anime collection error
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-11 10:15:24 -05:00
Timothy Warren 05c7fa1a3e Update dependencies 2020-12-11 10:14:59 -05:00
Timothy Warren 191ec11258 Bump version in header comments
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-10 17:06:50 -05:00
Timothy Warren 71ee0a324c A little more test coverage
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-10 17:04:45 -05:00
Timothy Warren 292d9bbaaf Refactor, increase test coverage
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-10 15:59:37 -05:00
Timothy Warren 0029dd2fb8 Update Kitsu GraphQL schema file
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-12-02 12:43:04 -05:00
Timothy Warren 2b01871e5b Add random anime and random manga pages 2020-12-02 12:42:47 -05:00
Timothy Warren b80f5a5316 Merge pull request 'All in GraphQL' (#34) from develop into master
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
Reviewed-on: #34
2020-12-01 10:07:48 -05:00
Timothy Warren f003905c0b Revert status mapping change
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-10-21 21:23:35 -04:00
Timothy Warren 9330bb628e Fix broken test
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 18:53:32 -04:00
Timothy Warren f0e3aa6fd7 Remove some more dead code 2020-10-21 18:52:12 -04:00
Timothy Warren c437696955 Merge remote-tracking branch 'origin/master' into develop
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 18:35:43 -04:00
Timothy Warren fd6624862f More cleanup, update changelog
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 18:12:22 -04:00
Timothy Warren d2a3b8ad37 More Kitsu GraphQL API cleanup, resolves #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 17:59:43 -04:00
Timothy Warren 155f44a424 Remove test for JsonAPI nonsense
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 17:07:50 -04:00
Timothy Warren 7fc58f1605 Use GraphQL to update thumbnails, refactor GraphQL pagination, merge Anime and Manga traits back into the Kitsu model 2020-10-21 17:06:50 -04:00
Timothy Warren 23d9fd0b40 Use GraphQL search endpoints, see #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 15:45:30 -04:00
Timothy Warren b28e1f13ee Remove old transformer classes
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-21 15:02:25 -04:00
Timothy Warren 0868fb43bc Add missing GraphQL query 2020-10-21 14:56:33 -04:00
Timothy Warren 5340c39466 More GraphQL conversion, test updates, see #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master There was a failure building this commit Details
2020-10-21 14:51:17 -04:00
Timothy Warren 1e3bfa7a0a Sync Kitsu and Anilist both via GraphQL, see #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-16 16:18:56 -04:00
Timothy Warren 22de5776a7 Fetch Manga List via GraphQL, see #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-16 13:28:35 -04:00
Timothy Warren dddef5add6 Fix Manga List Incrementing, start of GraphQL conversion
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-12 14:06:49 -04:00
Timothy Warren 30db156df7 Pull anime lists from GraphQL, see #33
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-09 16:18:45 -04:00
Timothy Warren 58669f023c Update dependency versions, add Amp base package as dependency 2020-10-09 16:16:23 -04:00
Timothy Warren 238a423806 Fix updating anime status when certain fields are empty
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-07 15:30:42 -04:00
Timothy Warren 24d6eaf0da Add back search query canceling for anime search
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-07 09:10:11 -04:00
Timothy Warren ecce90abd5 Fix hiding anime on completion
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-10-05 12:32:12 -04:00
Timothy Warren 913f9082ef Update GraphQL queries to match API changes
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-15 08:08:39 -04:00
Timothy Warren 7584d0a935 Map more external sites
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-10 15:36:34 -04:00
Timothy Warren fd2f3121bf Fix possible issue with hiding completed anime/manga
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-10 15:35:43 -04:00
Timothy Warren 249333b9fa Remove genres from manga list view
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-09 13:26:31 -04:00
Timothy Warren aca0f66db2 Update streaming logs, remove genres from anime list view
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-09 13:25:27 -04:00
Timothy Warren 66fca53dfc Only show total length of a series if the number is positive
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-09-09 10:24:55 -04:00
Timothy Warren 3b754ce634 Update GraphQL schema for Kitsu 2020-09-09 10:24:12 -04:00
Timothy Warren 44e1039067 Add limit to all relationships 2020-09-09 10:23:17 -04:00
Timothy Warren 78755f4019 Do not check session unless already logged in
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-28 14:27:14 -04:00
Timothy Warren ed9a3227ac Sort voice acting roles by character name
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-27 15:39:23 -04:00
Timothy Warren e2f29c6731 Get Person detail pages via GraphQL, resolves #27
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-27 15:01:00 -04:00
Timothy Warren 1b74df5269 More components, resolve #31
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-26 17:26:42 -04:00
Timothy Warren 9c01f3fbd0 Misc tweaks
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-26 15:25:31 -04:00
Timothy Warren 3714a93dcf Extract common methods for Anime and Manga models into a trait
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-26 15:24:49 -04:00
Timothy Warren 608cd4004e Fix Dispatcher test 2020-08-26 15:23:47 -04:00
Timothy Warren aace368b24 Move Kitsu class out of API namespace 2020-08-26 15:22:14 -04:00
Timothy Warren e20601053c Missing pieces of previous commit
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-25 16:06:00 -04:00
Timothy Warren 08176be4d0 Abort previous requests when search for anime or manga
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-25 16:02:15 -04:00
Timothy Warren 69991a126a Misc fixes and tweaks
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-25 15:11:08 -04:00
Timothy Warren 980e2726c5 Start of pulling library from GraphQL
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
timw4mail/HummingBirdAnimeClient/pipeline/pr-master This commit looks good Details
2020-08-25 13:22:38 -04:00
Timothy Warren 3f8c0432d2 Update History to use GraphQL, resolves #29,#30
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-24 19:17:41 -04:00
Timothy Warren ba276cc86e Update profile page to use GraphQL, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-24 15:20:07 -04:00
Timothy Warren 26a1c464a1 Some syncing cleanup
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-24 13:10:43 -04:00
Timothy Warren 40a340c67c Update some GraphQL queries
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-24 13:09:43 -04:00
Timothy Warren 7d6af5ad00 Get library entry via GraphQL, see #28 2020-08-24 13:07:47 -04:00
Timothy Warren a4cde0b28d Fix setup of console commands 2020-08-21 19:26:54 -04:00
Timothy Warren a692617101 Use components instead of duplicating html everywhere
timw4mail/HummingBirdAnimeClient/pipeline/head Build started... Details
2020-08-21 19:25:27 -04:00
Timothy Warren 41ca3a2d86 Fix tests
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-21 13:07:00 -04:00
Timothy Warren 386938c75f Create component system to help cut down on view duplication, see #31
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-08-21 12:30:01 -04:00
Timothy Warren 2f2260e0b4 Drastically reduce the amount of junk logging, and remove old logic from Character transformer
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-18 16:59:08 -04:00
Timothy Warren 1da68d8ec4 Use constants for the API names instead of literals
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-08-17 21:08:53 -04:00
Timothy Warren e06cc16890 Fix sync command
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 18:08:58 -04:00
Timothy Warren 09734515ca Full character page pulled from GraphQL API, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 16:36:55 -04:00
Timothy Warren 1725a106fa Get character details page from GraphQL, still need to do castings section, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-08-17 14:01:55 -04:00
Timothy Warren 4d4b7126e2 Fix manga description page
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 11:36:01 -04:00
Timothy Warren e2e845b9c0 Add streaming links back to anime description pages, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-08-17 10:45:17 -04:00
Timothy Warren 24def28cda Update Kitsu GraphQL schema reference
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 10:25:36 -04:00
Timothy Warren f3b7e58ee1 Update Kitsu GraphQL Mutations
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 10:24:17 -04:00
Timothy Warren e7e70a1541 Improve error logging
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-17 10:23:32 -04:00
Timothy Warren 65a8da755a Remove some naming redundancies
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-06 09:39:12 -04:00
Timothy Warren ca8a0edad0 Add missing change from previous commit
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-05 21:52:36 -04:00
Timothy Warren 9a6d0052d7 Add background check for session validity
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
This checks when the app is made visible -- like the tab is switched to,
if the current session is still valid. If the session is not still
valid, the page is reloaded so that the session expiration is apparent.

Resolves #25
2020-08-05 21:46:14 -04:00
Timothy Warren 6a82944473 slugs in person urls, refactor AnilistTrait to match KitsuTrait
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-08-05 20:57:01 -04:00
Timothy Warren f328420869 Create and delete media items via GraphQL, see #28
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-05 13:30:24 -04:00
Timothy Warren 3965f137e1 Move GraphQL queries up a level
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-08-04 14:25:18 -04:00
Timothy Warren ff6fcffca8 Bump version
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-04 09:30:21 -04:00
Timothy Warren 4c1c78577e Get manga updates working correctly with GraphQL, see #28
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-08-04 09:20:28 -04:00
Timothy Warren d66b589a75 Add some previously missing GraphQL fields
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-08-03 14:36:14 -04:00
Timothy Warren 20f318114b Actually fix anime episode incrementing
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-07-31 20:00:11 -04:00
Timothy Warren 56032728eb Fix json loading overlay for anime updates
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-07-31 19:32:13 -04:00
Timothy Warren 722518579f Increment chapter/episode counts via GraphQL
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-07-31 19:03:27 -04:00
Timothy Warren eab8a1cd4b Add GraphQL schema reference for Kitsu 2020-07-31 18:59:16 -04:00
Timothy Warren 2fa83b5f1d Add GraphQL schema reference for Anilist 2020-07-31 18:58:49 -04:00
Timothy Warren 48b031e190 Refactor/streamline View layer
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-07-31 16:22:32 -04:00
Timothy Warren a8110d4a90 Groundwork for some upcoming API updates
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-07-30 15:16:24 -04:00
Timothy Warren 455b551683 Actually fix the character API call, previous commit added authentication to GraphQL calls
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-07-30 10:02:44 -04:00
Timothy Warren de6faf2224 Fix issue retrieving some characters
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-07-30 09:58:36 -04:00
Timothy Warren 10cc9dece2 Skip broken test
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-07-29 22:08:54 -04:00
Timothy Warren 8fe2114816 Small fixes for anime detail pages
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-07-29 20:06:59 -04:00
Timothy Warren 83de995951 Get Manga details from GraphQL, See #27
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details
2020-07-29 17:51:58 -04:00
Timothy Warren 766e3cd71c Fetch anime details by id for the collection
timw4mail/HummingBirdAnimeClient/pipeline/head Build started... Details
2020-07-29 16:25:57 -04:00
Timothy Warren 375b8f9bcb Anime detail page cleanup 2020-07-29 15:49:16 -04:00
Timothy Warren dcd138cba6 get anime staff from GraphQL, see #27 2020-07-29 14:04:03 -04:00
Timothy Warren 50f3c394c5 Re-add characters to anime details page, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-07-29 11:00:54 -04:00
Timothy Warren 0ed7a0de2e Run local phpunit from robo 2020-07-29 11:00:06 -04:00
Timothy Warren bb878d905f Use GraphQL request for anime detail pages, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details
2020-07-28 17:46:18 -04:00
Timothy Warren 3bb3d2a5cf Prepare for Kitsu GraphQL
timw4mail/HummingBirdAnimeClient/pipeline/head There was a failure building this commit Details
2020-07-28 16:11:13 -04:00
406 changed files with 40066 additions and 18545 deletions

4
.gitignore vendored
View File

@ -148,4 +148,6 @@ public/images/people/**
public/mal_mappings.json
.phpunit.result.cache
.is-dev
.is-dev
tmp

View File

@ -4,13 +4,14 @@ install:
- composer install --ignore-platform-reqs
php:
- 7.4
- 8.0
- 8.1
- nightly
script:
- mkdir -p build/logs
- php vendor/bin/phpunit -c build
matrix:
allow_failures:
- php: nightly
#matrix:
# allow_failures:
# - php: nightly

View File

@ -1,5 +1,14 @@
# Changelog
## Version 5.2
* Updated PHP requirement to 8
* Updated to support PHP 8.1
## Version 5.1
* Added session check, so when coming back to a page, if the session is expired, the page will refresh.
* Updated logging config so that much fewer, much smaller files are generated.
* Updated Kitsu integration to use GraphQL API, reducing a lot of internal complexity.
## Version 5
* Updated PHP requirement to 7.4
* Added anime watching history view

13
Jenkinsfile vendored
View File

@ -10,27 +10,29 @@ pipeline {
sh 'php composer.phar install --ignore-platform-reqs'
}
}
stage('PHP 7.4') {
stage('PHP 8') {
agent {
docker {
image 'php:7.4-alpine'
image 'php:8-cli-alpine'
args '-u root --privileged'
}
}
steps {
sh 'apk add --no-cache git'
sh 'apk add --no-cache git icu-dev'
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
sh 'php ./vendor/bin/phpunit --colors=never'
}
}
stage('Latest PHP') {
agent {
docker {
image 'php:alpine'
image 'php:cli-alpine'
args '-u root --privileged'
}
}
steps {
sh 'apk add --no-cache git'
sh 'apk add --no-cache git icu-dev'
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
sh 'php ./vendor/bin/phpunit --colors=never'
}
}
@ -43,6 +45,7 @@ pipeline {
cloverReportDir: '',
cloverReportFileName: 'build/logs/clover.xml',
])
junit 'build/logs/junit.xml'
}
}
}

View File

@ -3,7 +3,7 @@
Update your anime/manga list on Kitsu.io and Anilist
[![Build Status](https://travis-ci.com/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.com/github/timw4mail/HummingBirdAnimeClient)
[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshomepage.net/job/timw4mail/HummingBirdAnimeClient/develop)
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshome.page/job/timw4mail/job/HummingBirdAnimeClient/job/develop/)
[[Hosted Example](https://list.timshomepage.net)]
@ -31,7 +31,7 @@ Update your anime/manga list on Kitsu.io and Anilist
### Requirements
* PHP 7.4+
* PHP 8
* PDO SQLite or PDO PostgreSQL (For collection tab)
* GD extension for caching images

View File

@ -1,316 +0,0 @@
<?php declare(strict_types=1);
use Robo\Tasks;
if ( ! function_exists('glob_recursive'))
{
// Does not support flag GLOB_BRACE
function glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
{
$files = array_merge($files, glob_recursive($dir.'/'.basename($pattern), $flags));
}
return $files;
}
}
/**
* This is project's console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends Tasks {
/**
* Directories used by analysis tools
*
* @var array
*/
protected $taskDirs = [
'build/logs',
'build/pdepend',
'build/phpdox',
];
/**
* Directories to remove with the clean task
*
* @var array
*/
protected $cleanDirs = [
'coverage',
'docs',
'phpdoc',
'build/logs',
'build/phpdox',
'build/pdepend'
];
/**
* Do static analysis tasks
*/
public function analyze(): void
{
$this->prepare();
$this->lint();
$this->phploc(TRUE);
$this->phpcs(TRUE);
$this->phpmd(TRUE);
$this->dependencyReport();
$this->phpcpdReport();
}
/**
* Run all tests, generate coverage, generate docs, generate code statistics
*/
public function build(): void
{
$this->analyze();
$this->coverage();
$this->docs();
}
/**
* Cleanup temporary files
*/
public function clean(): void
{
$cleanFiles = [
'build/humbug.json',
'build/humbug-log.txt',
];
array_map(static function ($file) {
@unlink($file);
}, $cleanFiles);
// So the task doesn't complain,
// make any 'missing' dirs to cleanup
array_map(static function ($dir) {
if ( ! is_dir($dir))
{
`mkdir -p {$dir}`;
}
}, $this->cleanDirs);
$this->_cleanDir($this->cleanDirs);
$this->_deleteDir($this->cleanDirs);
}
/**
* Run unit tests and generate coverage reports
*/
public function coverage(): void
{
$this->_run(['phpdbg -qrr -- vendor/bin/phpunit -c build']);
}
/**
* Generate documentation with phpdox
*/
public function docs(): void
{
$cmd_parts = [
'vendor/bin/phpdox',
];
$this->_run($cmd_parts, ' && ');
}
/**
* Verify that source files are valid
*/
public function lint(): void
{
$files = $this->getAllSourceFiles();
$chunks = array_chunk($files, (int)shell_exec('getconf _NPROCESSORS_ONLN'));
foreach($chunks as $chunk)
{
$this->parallelLint($chunk);
}
}
/**
* Run the phpcs tool
*
* @param bool $report - if true, generates reports instead of direct output
*/
public function phpcs($report = FALSE): void
{
$report_cmd_parts = [
'vendor/bin/phpcs',
'--standard=./build/phpcs.xml',
'--report-checkstyle=./build/logs/phpcs.xml',
];
$normal_cmd_parts = [
'vendor/bin/phpcs',
'--standard=./build/phpcs.xml',
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
public function phpmd($report = FALSE): void
{
$report_cmd_parts = [
'vendor/bin/phpmd',
'./src',
'xml',
'cleancode,codesize,controversial,design,naming,unusedcode',
'--exclude ParallelAPIRequest',
'--reportfile ./build/logs/phpmd.xml'
];
$normal_cmd_parts = [
'vendor/bin/phpmd',
'./src',
'ansi',
'cleancode,codesize,controversial,design,naming,unusedcode',
'--exclude ParallelAPIRequest'
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
/**
* Run the phploc tool
*
* @param bool $report - if true, generates reports instead of direct output
*/
public function phploc($report = FALSE): void
{
// Command for generating reports
$report_cmd_parts = [
'vendor/bin/phploc',
'--count-tests',
'--log-csv=build/logs/phploc.csv',
'--log-xml=build/logs/phploc.xml',
'src',
'tests'
];
// Command for generating direct output
$normal_cmd_parts = [
'vendor/bin/phploc',
'--count-tests',
'src',
'tests'
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
/**
* Create temporary directories
*/
public function prepare(): void
{
array_map([$this, '_mkdir'], $this->taskDirs);
}
/**
* Lint php files and run unit tests
*/
public function test(): void
{
$this->lint();
$this->_run(['phpunit']);
}
/**
* Create pdepend reports
*/
protected function dependencyReport(): void
{
$cmd_parts = [
'vendor/bin/pdepend',
'--jdepend-xml=build/logs/jdepend.xml',
'--jdepend-chart=build/pdepend/dependencies.svg',
'--overview-pyramid=build/pdepend/overview-pyramid.svg',
'src'
];
$this->_run($cmd_parts);
}
/**
* Get the total list of source files, including tests
*
* @return array
*/
protected function getAllSourceFiles(): array
{
$files = array_merge(
glob_recursive('build/*.php'),
glob_recursive('src/*.php'),
glob_recursive('src/**/*.php'),
glob_recursive('tests/*.php'),
glob_recursive('tests/**/*.php'),
glob('*.php')
);
$files = array_filter($files, static function(string $value) {
return strpos($value, '__snapshots__') === FALSE;
});
sort($files);
return $files;
}
/**
* Run php's linter in one parallel task for the passed chunk
*
* @param array $chunk
*/
protected function parallelLint(array $chunk): void
{
$task = $this->taskParallelExec()
->timeout(5)
->printed(FALSE);
foreach($chunk as $file)
{
$task = $task->process("php -l {$file}");
}
$task->run();
}
/**
* Generate copy paste detector report
*/
protected function phpcpdReport(): void
{
$cmd_parts = [
'vendor/bin/phpcpd',
'--log-pmd build/logs/pmd-cpd.xml',
'src'
];
$this->_run($cmd_parts);
}
/**
* Shortcut for joining an array of command arguments
* and then running it
*
* @param array $cmd_parts - command arguments
* @param string $join_on - what to join the command arguments with
*/
protected function _run(array $cmd_parts, $join_on = ' '): void
{
$this->taskExec(implode($join_on, $cmd_parts))->run();
}
}

View File

@ -2,31 +2,32 @@
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2017 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://github.com/timw4mail/HummingBirdAnimeClient
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
use function Aviat\AnimeClient\loadToml;
use function Aviat\AnimeClient\loadConfig;
// ----------------------------------------------------------------------------
// Lower level configuration
//
// You shouldn't generally need to change anything below this line
// ----------------------------------------------------------------------------
$APP_DIR = realpath(__DIR__ . '/../');
$ROOT_DIR = realpath("{$APP_DIR}/../");
$APP_DIR = dirname(__DIR__);
$ROOT_DIR = dirname($APP_DIR);
$tomlConfig = loadToml(__DIR__);
$tomlConfig = loadConfig(__DIR__);
return array_merge($tomlConfig, [
'root' => $ROOT_DIR,
'asset_dir' => "{$ROOT_DIR}/public",
'base_config_dir' => __DIR__,
'config_dir' => "{$APP_DIR}/config",

View File

@ -2,15 +2,15 @@
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2018 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
@ -28,6 +28,17 @@ use const Aviat\AnimeClient\{
// Maps paths to controllers and methods
// -------------------------------------------------------------------------
$routes = [
// ---------------------------------------------------------------------
// AJAX Routes
// ---------------------------------------------------------------------
'cache_purge' => [
'path' => '/cache_purge',
'action' => 'clearCache',
],
'heartbeat' => [
'path' => '/heartbeat',
'action' => 'heartbeat',
],
// ---------------------------------------------------------------------
// Anime List Routes
// ---------------------------------------------------------------------
@ -40,6 +51,10 @@ $routes = [
'action' => 'add',
'verb' => 'post',
],
'anime.random' => [
'path' => '/anime/details/random',
'action' => 'random',
],
'anime.details' => [
'path' => '/anime/details/{id}',
'action' => 'details',
@ -73,6 +88,10 @@ $routes = [
'action' => 'delete',
'verb' => 'post',
],
'manga.random' => [
'path' => '/manga/details/random',
'action' => 'random',
],
'manga.details' => [
'path' => '/manga/details/{id}',
'action' => 'details',
@ -175,9 +194,9 @@ $routes = [
]
],
'person' => [
'path' => '/people/{id}',
'path' => '/people/{slug}',
'tokens' => [
'id' => SLUG_PATTERN
'slug' => SLUG_PATTERN,
]
],
'default_user_info' => [
@ -215,10 +234,6 @@ $routes = [
'file' => '[a-z0-9\-]+\.[a-z]{3,4}'
]
],
'cache_purge' => [
'path' => '/cache_purge',
'action' => 'clearCache',
],
'settings' => [
'path' => '/settings',
],
@ -240,6 +255,13 @@ $routes = [
'path' => '/logout',
'action' => 'logout',
],
'history' => [
'controller' => 'history',
'path' => '/history/{type}',
'tokens' => [
'type' => SLUG_PATTERN
]
],
'increment' => [
'path' => '/{controller}/increment',
'action' => 'increment',
@ -273,19 +295,12 @@ $routes = [
],
],
'list' => [
'path' => '/{controller}/{type}{/view}',
'path' => '/{controller}/{status}{/view}',
'tokens' => [
'type' => ALPHA_SLUG_PATTERN,
'status' => ALPHA_SLUG_PATTERN,
'view' => ALPHA_SLUG_PATTERN,
],
],
'history' => [
'controller' => 'history',
'path' => '/history/{type}',
'tokens' => [
'type' => SLUG_PATTERN
]
],
'index_redirect' => [
'path' => '/',
'action' => 'redirectToDefaultRoute',

View File

@ -4,13 +4,13 @@
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.4
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2020 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
@ -19,20 +19,27 @@ namespace Aviat\AnimeClient;
use Aura\Html\HelperLocatorFactory;
use Aura\Router\RouterContainer;
use Aura\Session\SessionFactory;
use Aviat\AnimeClient\API\{
Anilist,
Kitsu,
Kitsu\KitsuRequestBuilder
};
use Aviat\AnimeClient\API\{Anilist, Kitsu};
use Aviat\AnimeClient\Component;
use Aviat\AnimeClient\Model;
use Aviat\Banker\Teller;
use Aviat\Ion\Config;
use Aviat\Ion\Di\Container;
use Aviat\Ion\Di\ContainerInterface;
use Psr\SimpleCache\CacheInterface;
use Laminas\Diactoros\{Response, ServerRequestFactory};
use Laminas\Diactoros\ServerRequestFactory;
use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Psr\SimpleCache\CacheInterface;
use function Aviat\Ion\_dir;
if ( ! defined('HB_APP_DIR'))
{
define('HB_APP_DIR', __DIR__);
define('ROOT_DIR', dirname(HB_APP_DIR));
define('TEMPLATE_DIR', _dir(HB_APP_DIR, 'templates'));
}
// -----------------------------------------------------------------------------
// Setup DI container
@ -43,26 +50,28 @@ return static function (array $configArray = []): Container {
// -------------------------------------------------------------------------
// Logging
// -------------------------------------------------------------------------
$LOG_DIR = _dir(HB_APP_DIR, 'logs');
$appLogger = new Logger('animeclient');
$appLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
$anilistRequestLogger = new Logger('anilist-request');
$anilistRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/anilist_request.log', Logger::NOTICE));
$kitsuRequestLogger = new Logger('kitsu-request');
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
$appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING));
$container->setLogger($appLogger);
$container->setLogger($anilistRequestLogger, 'anilist-request');
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
foreach (['anilist-request', 'kitsu-request', 'kitsu-graphql'] as $channel)
{
$logger = new Logger($channel);
$handler = new RotatingFileHandler(_dir($LOG_DIR, "{$channel}.log"), 2, Logger::WARNING);
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);
$container->setLogger($logger, $channel);
}
// -------------------------------------------------------------------------
// Injected Objects
// -------------------------------------------------------------------------
// Create Config Object
$container->set('config', fn () => new Config($configArray));
$container->set('config', static fn () => new Config($configArray));
// Create Cache Object
$container->set('cache', static function(ContainerInterface $container): CacheInterface {
@ -72,49 +81,71 @@ return static function (array $configArray = []): Container {
});
// Create Aura Router Object
$container->set('aura-router', fn() => new RouterContainer);
$container->set('aura-router', static fn() => new RouterContainer);
// Create Html helper Object
// Create Html helpers
$container->set('html-helper', static function(ContainerInterface $container) {
$htmlHelper = (new HelperLocatorFactory)->newInstance();
$htmlHelper->set('menu', static function() use ($container) {
$menuHelper = new Helper\Menu();
$menuHelper->setContainer($container);
return $menuHelper;
});
$htmlHelper->set('field', static function() use ($container) {
$formHelper = new Helper\Form();
$formHelper->setContainer($container);
return $formHelper;
});
$htmlHelper->set('picture', static function() use ($container) {
$pictureHelper = new Helper\Picture();
$pictureHelper->setContainer($container);
return $pictureHelper;
});
$helpers = [
'menu' => Helper\Menu::class,
'field' => Helper\Form::class,
'picture' => Helper\Picture::class,
];
foreach ($helpers as $name => $class)
{
$htmlHelper->set($name, static function() use ($class, $container) {
$helper = new $class;
$helper->setContainer($container);
return $helper;
});
}
return $htmlHelper;
});
// Create Request/Response Objects
$container->set('request', fn () => ServerRequestFactory::fromGlobals(
$_SERVER,
// Create Component helpers
$container->set('component-helper', static function (ContainerInterface $container) {
$helper = (new HelperLocatorFactory)->newInstance();
$components = [
'animeCover' => Component\AnimeCover::class,
'mangaCover' => Component\MangaCover::class,
'character' => Component\Character::class,
'media' => Component\Media::class,
'tabs' => Component\Tabs::class,
'verticalTabs' => Component\VerticalTabs::class,
];
foreach ($components as $name => $componentClass)
{
$helper->set($name, static function () use ($container, $componentClass) {
$helper = new $componentClass;
$helper->setContainer($container);
return $helper;
});
}
return $helper;
});
// Create Request Object
$container->set('request', static fn () => ServerRequestFactory::fromGlobals(
$GLOBALS['_SERVER'],
$_GET,
$_POST,
$_COOKIE,
$_FILES
));
$container->set('response', fn () => new Response);
// Create session Object
$container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE));
$container->set('session', static fn () => (new SessionFactory())->newInstance($_COOKIE));
// Miscellaneous helper methods
$container->set('util', fn ($container) => new Util($container));
$container->set('util', static fn ($container) => new Util($container));
// Models
$container->set('kitsu-model', static function(ContainerInterface $container): Kitsu\Model {
$requestBuilder = new KitsuRequestBuilder($container);
$requestBuilder = new Kitsu\RequestBuilder($container);
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
$listItem = new Kitsu\ListItem();
@ -130,7 +161,7 @@ return static function (array $configArray = []): Container {
return $model;
});
$container->set('anilist-model', static function(ContainerInterface $container): Anilist\Model {
$requestBuilder = new Anilist\AnilistRequestBuilder();
$requestBuilder = new Anilist\RequestBuilder($container);
$requestBuilder->setLogger($container->getLogger('anilist-request'));
$listItem = new Anilist\ListItem();
@ -143,10 +174,10 @@ return static function (array $configArray = []): Container {
return $model;
});
$container->set('anime-model', fn ($container) => new Model\Anime($container));
$container->set('manga-model', fn ($container) => new Model\Manga($container));
$container->set('anime-collection-model', fn ($container) => new Model\AnimeCollection($container));
$container->set('manga-collection-model', fn ($container) => new Model\MangaCollection($container));
$container->set('anime-model', static fn ($container) => new Model\Anime($container));
$container->set('manga-model', static fn ($container) => new Model\Manga($container));
$container->set('anime-collection-model', static fn ($container) => new Model\AnimeCollection($container));
$container->set('manga-collection-model', static fn ($container) => new Model\MangaCollection($container));
$container->set('settings-model', static function($container) {
$model = new Model\Settings($container->get('config'));
$model->setContainer($container);
@ -154,13 +185,13 @@ return static function (array $configArray = []): Container {
});
// Miscellaneous Classes
$container->set('auth', fn ($container) => new Kitsu\Auth($container));
$container->set('url-generator', fn ($container) => new UrlGenerator($container));
$container->set('auth', static fn ($container) => new Kitsu\Auth($container));
$container->set('url-generator', static fn ($container) => new UrlGenerator($container));
// -------------------------------------------------------------------------
// Dispatcher
// -------------------------------------------------------------------------
$container->set('dispatcher', fn ($container) => new Dispatcher($container));
$container->set('dispatcher', static fn ($container) => new Dispatcher($container));
return $container;
};

View File

@ -1,12 +1,12 @@
<article
class="media"
data-kitsu-id="<?= $item['id'] ?>"
data-mal-id="<?= $item['mal_id'] ?>"
data-kitsu-id="<?= $item['id'] ?>"
data-mal-id="<?= $item['mal_id'] ?>"
>
<?php if ($auth->isAuthenticated()): ?>
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
<?php endif ?>
<?= $helper->picture("images/anime/{$item['anime']['id']}.webp") ?>
<?= $helper->img($item['anime']['cover_image'], ['width' => 220, 'loading' => 'lazy']) ?>
<div class="name">
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
@ -18,7 +18,7 @@
</a>
</div>
<div class="table">
<?php if ($item['private'] || $item['rewatching']): ?>
<?php if (isset($item['private']) || isset($item['rewatching'])): ?>
<div class="row">
<?php foreach (['private', 'rewatching'] as $attr): ?>
<?php if ($item[$attr]): ?>
@ -31,13 +31,13 @@
<?php if ($item['rewatched'] > 0): ?>
<div class="row">
<?php if ($item['rewatched'] == 1): ?>
<div>Rewatched once</div>
<div>Rewatched once</div>
<?php elseif ($item['rewatched'] == 2): ?>
<div>Rewatched twice</div>
<div>Rewatched twice</div>
<?php elseif ($item['rewatched'] == 3): ?>
<div>Rewatched thrice</div>
<div>Rewatched thrice</div>
<?php else: ?>
<div>Rewatched <?= $item['rewatched'] ?> times</div>
<div>Rewatched <?= $item['rewatched'] ?> times</div>
<?php endif ?>
</div>
<?php endif ?>

View File

@ -0,0 +1,6 @@
<article class="<?= $className ?>">
<div class="name">
<a href="<?= $link ?>"><?= $name ?></a>
</div>
<a href="<?= $link ?>"><?= $picture ?></a>
</article>

View File

@ -0,0 +1,68 @@
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
<?php if ($auth->isAuthenticated()): ?>
<div class="edit-buttons" hidden>
<button class="plus-one-chapter">+1 Chapter</button>
</div>
<?php endif ?>
<?= $helper->img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?>
<div class="name">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $escape->html($item['manga']['title']) ?>
<?php foreach($item['manga']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
<div class="table">
<?php if ($auth->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed"
title="Edit information about this manga"
href="<?= $url->generate('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">
Edit
</a>
</span>
</div>
<?php endif ?>
<div class="row">
<div><?= $item['manga']['type'] ?></div>
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
</div>
<?php if ($item['rereading']): ?>
<div class="row">
<?php foreach(['rereading'] as $attr): ?>
<?php if($item[$attr]): ?>
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
<?php endif ?>
<?php endforeach ?>
</div>
<?php endif ?>
<?php if ($item['reread'] > 0): ?>
<div class="row">
<?php if ($item['reread'] == 1): ?>
<div>Reread once</div>
<?php elseif ($item['reread'] == 2): ?>
<div>Reread twice</div>
<?php elseif ($item['reread'] == 3): ?>
<div>Reread thrice</div>
<?php else: ?>
<div>Reread <?= $item['reread'] ?> times</div>
<?php endif ?>
</div>
<?php endif ?>
<div class="row">
<div class="chapter_completion">
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
</div>
</div>
</div>
</article>

12
app/templates/media.php Normal file
View File

@ -0,0 +1,12 @@
<article class="<?= $className ?>">
<a href="<?= $link ?>"><?= $picture ?></a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>

View File

@ -0,0 +1,5 @@
<section class="<?= $className ?>">
<?php foreach ($data as $tabName => $tabData): ?>
<?= $callback($tabData, $tabName) ?>
<?php endforeach ?>
</section>

32
app/templates/tabs.php Normal file
View File

@ -0,0 +1,32 @@
<div class="tabs">
<?php $i = 0; foreach ($data as $tabName => $tabData): ?>
<?php if ( ! empty($tabData)): ?>
<?php $id = "{$name}-{$i}"; ?>
<input
role='tab'
aria-controls="_<?= $id ?>"
type="radio"
name="<?= $name ?>"
id="<?= $id ?>"
<?= ($i === 0) ? 'checked="checked"' : '' ?>
/>
<label for="<?= $id ?>"><?= ucfirst($tabName) ?></label>
<?php if ($hasSectionWrapper): ?>
<div class="content full-height">
<?php endif ?>
<section
id="_<?= $id ?>"
role="tabpanel"
class="<?= $className ?>"
>
<?= $callback($tabData, $tabName) ?>
</section>
<?php if ($hasSectionWrapper): ?>
</div>
<?php endif ?>
<?php endif ?>
<?php $i++; endforeach ?>
</div>

View File

@ -0,0 +1,25 @@
<div class="vertical-tabs">
<?php $i = 0; ?>
<?php foreach ($data as $tabName => $tabData): ?>
<?php $id = "{$name}-{$i}" ?>
<div class="tab">
<input
type="radio"
role='tab'
aria-controls="_<?= $id ?>"
name="<?= $name ?>"
id="<?= $id ?>"
<?= $i === 0 ? 'checked="checked"' : '' ?>
/>
<label for="<?= $id ?>"><?= $tabName ?></label>
<section
id='_<?= $id ?>'
role="tabpanel"
class="<?= $className ?>"
>
<?= $callback($tabData, $tabName) ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
</div>

View File

@ -20,7 +20,7 @@
<section class="media-wrap">
<?php foreach($items as $item): ?>
<?php if ($item['private'] && ! $auth->isAuthenticated()) continue; ?>
<?php include __DIR__ . '/cover-item.php' ?>
<?= $component->animeCover($item) ?>
<?php endforeach ?>
</section>
</section>

View File

@ -1,8 +1,12 @@
<?php use function Aviat\AnimeClient\getLocalImg; ?>
<?php
use Aviat\AnimeClient\Kitsu;
?>
<main class="details fixed">
<section class="flex">
<section class="flex" unselectable>
<aside class="info">
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
<?= $helper->img($data['cover_image'], ['width' => '390']) ?>
<br />
@ -11,20 +15,40 @@
<td class="align-right">Airing Status</td>
<td><?= $data['status'] ?></td>
</tr>
<?php if ( ! empty($data['airDate'])): ?>
<tr>
<td>Original Airing</td>
<td><?= $data['airDate'] ?></td>
</tr>
<?php endif ?>
<tr>
<td>Show Type</td>
<td><?= $data['show_type'] ?></td>
<td><?= (strlen($data['show_type']) > 3) ? ucfirst(strtolower($data['show_type'])) : $data['show_type'] ?></td>
</tr>
<tr>
<td>Episode Count</td>
<td><?= $data['episode_count'] ?? '-' ?></td>
</tr>
<?php if ( ! empty($data['episode_length'])): ?>
<?php if ($data['episode_count'] !== 1): ?>
<tr>
<td>Episode Length</td>
<td><?= $data['episode_length'] ?> minutes</td>
<td>Episode Count</td>
<td><?= $data['episode_count'] ?? '-' ?></td>
</tr>
<?php endif ?>
<?php if (( ! empty($data['episode_length'])) && $data['episode_count'] !== 1): ?>
<tr>
<td>Episode Length</td>
<td><?= Kitsu::friendlyTime($data['episode_length']) ?></td>
</tr>
<?php endif ?>
<?php if (isset($data['total_length'], $data['episode_count']) && $data['total_length'] > 0): ?>
<tr>
<td>Total Length</td>
<td><?= Kitsu::friendlyTime($data['total_length']) ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['age_rating'])): ?>
<tr>
<td>Age Rating</td>
@ -32,6 +56,18 @@
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -40,11 +76,13 @@
</tr>
</table>
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>
@ -90,8 +128,8 @@
&nbsp;&nbsp;<?= $link['meta']['name'] ?>
<?php endif ?>
</td>
<td><?= implode(', ', $link['subs']) ?></td>
<td><?= implode(', ', $link['dubs']) ?></td>
<td><?= implode(', ', array_map(fn ($sub) => Locale::getDisplayLanguage($sub, 'en'), $link['subs'])) ?></td>
<td><?= implode(', ', array_map(fn ($dub) => Locale::getDisplayLanguage($dub, 'en'), $link['dubs'])) ?></td>
</tr>
<?php endforeach ?>
</tbody>
@ -99,81 +137,69 @@
<?php endif ?>
<?php if ( ! empty($data['trailer_id'])): ?>
<div class="responsive-iframe">
<h4>Trailer</h4>
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen
></iframe>
<h4>Trailer</h4>
<iframe
width="560"
height="315"
role='img'
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
allow="autoplay; encrypted-media"
allowfullscreen
tabindex='0'
title="<?= $data['title'] ?> trailer video"
></iframe>
</div>
<?php endif ?>
</article>
</section>
<?php if (count($data['characters']) > 0): ?>
<section>
<h2>Characters</h2>
<section>
<h2>Characters</h2>
<div class="tabs">
<?php $i = 0 ?>
<?php foreach ($data['characters'] as $role => $list): ?>
<input
type="radio" name="character-types"
id="character-types-<?= $i ?>" <?= ($i === 0) ? 'checked' : '' ?> />
<label for="character-types-<?= $i ?>"><?= ucfirst($role) ?></label>
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($list as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name">
<?= $helper->a($link, $char['name']); ?>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$id}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endforeach ?>
</div>
</section>
<?= $component->tabs('character-types', $data['characters'], static function ($characterList, $role)
use ($component, $url, $helper) {
$rendered = [];
foreach ($characterList as $id => $character):
if (empty($character['image']))
{
continue;
}
$rendered[] = $component->character(
$character['name'],
$url->generate('character', ['slug' => $character['slug']]),
$helper->img($character['image']),
(strtolower($role) !== 'main') ? 'small-character' : 'character'
);
endforeach;
return implode('', array_map('mb_trim', $rendered));
}) ?>
</section>
<?php endif ?>
<?php if (count($data['staff']) > 0): ?>
<section>
<h2>Staff</h2>
<section>
<h2>Staff</h2>
<div class="vertical-tabs">
<?php $i = 0; ?>
<?php foreach ($data['staff'] as $role => $people): ?>
<div class="tab">
<input type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<section class='content media-wrap flex flex-wrap flex-justify-start'>
<?php foreach ($people as $pid => $person): ?>
<article class='character small-person'>
<?php $link = $url->generate('person', ['id' => $person['id']]) ?>
<div class="name">
<a href="<?= $link ?>">
<?= $person['name'] ?>
</a>
</div>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($person['image']['original'] ?? NULL)) ?>
</a>
</article>
<?php endforeach ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
</div>
</section>
<?= $component->verticalTabs('staff-role', $data['staff'], static function ($staffList)
use ($component, $url, $helper) {
$rendered = [];
foreach ($staffList as $id => $person):
if (empty($person['image']))
{
continue;
}
$rendered[] = $component->character(
$person['name'],
$url->generate('person', ['slug' => $person['slug']]),
$helper->img($person['image']),
'character small-person',
);
endforeach;
return implode('', array_map('mb_trim', $rendered));
}) ?>
</section>
<?php endif ?>
</main>

View File

@ -16,7 +16,7 @@
<tbody>
<tr>
<td rowspan="9">
<?= $helper->picture("images/anime/{$item['anime']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
<?= $helper->img($item['anime']['cover_image']) ?>
</td>
</tr>
<tr>
@ -32,7 +32,7 @@
<td>
<select name="watching_status" id="watching_status">
<?php foreach($statuses as $status_key => $status_title): ?>
<option <?php if($item['watching_status'] === $status_key): ?>selected="selected"<?php endif ?>
<option <?php if(strtolower($item['watching_status']) === $status_key): ?>selected="selected"<?php endif ?>
value="<?= $status_key ?>"><?= $status_title ?></option>
<?php endforeach ?>
</select>

View File

@ -1,4 +1,4 @@
<?php use function Aviat\AnimeClient\col_not_empty; ?>
<?php use function Aviat\AnimeClient\colNotEmpty; ?>
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
@ -15,7 +15,7 @@
<h3>There's nothing here!</h3>
<?php else: ?>
<?php
$hasNotes = col_not_empty($items, 'notes');
$hasNotes = colNotEmpty($items, 'notes');
?>
<table class='media-wrap'>
<thead>
@ -25,13 +25,12 @@
<?php endif ?>
<th>Title</th>
<th>Airing Status</th>
<th>Score</th>
<th class='numeric'>Score</th>
<th>Type</th>
<th>Progress</th>
<th>Rated</th>
<th class='numeric'>Progress</th>
<th class='rating'>Age Rating</th>
<th>Attributes</th>
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
<th>Genres</th>
</tr>
</thead>
<tbody>
@ -103,10 +102,6 @@
</ul>
</td>
<?php if ($hasNotes): ?><td><p><?= $escape->html($item['notes']) ?></p></td><?php endif ?>
<td class="align-left">
<?php sort($item['anime']->genres) ?>
<?= implode(', ', $item['anime']->genres) ?>
</td>
</tr>
<?php endforeach ?>
</tbody>

View File

@ -1,13 +1,13 @@
<?php
use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\Kitsu;
?>
<main class="character-page details fixed">
<section class="flex flex-no-wrap">
<aside>
<?= $helper->picture("images/characters/{$data['id']}-original.webp") ?>
<?= $helper->img($data['image']) ?>
</aside>
<div>
<h2 class="toph"><?= $data['name'] ?></h2>
@ -33,63 +33,20 @@ use Aviat\AnimeClient\API\Kitsu;
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
<h3>Media</h3>
<div class="tabs">
<?php if ( ! empty($data['media']['anime'])): ?>
<input checked="checked" type="radio" id="media-anime" name="media-tabs" />
<label for="media-anime">Anime</label>
<section class="media-wrap content">
<?php foreach ($data['media']['anime'] as $id => $anime): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
$titles = Kitsu::filterTitles($anime['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$id}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php endif ?>
<?= $component->tabs('character-media', $data['media'], static function ($media, $mediaType) use ($url, $component, $helper) {
$rendered = [];
foreach ($media as $id => $item)
{
$rendered[] = $component->media(
array_merge([$item['title']], $item['titles']),
$url->generate("{$mediaType}.details", ['id' => $item['slug']]),
$helper->img(Kitsu::getPosterImage($item), ['width' => 220, 'loading' => 'lazy']),
);
}
<?php if ( ! empty($data['media']['manga'])): ?>
<input type="radio" id="media-manga" name="media-tabs" />
<label for="media-manga">Manga</label>
<section class="media-wrap content">
<?php foreach ($data['media']['manga'] as $id => $manga): ?>
<article class="media">
<?php
$link = $url->generate('manga.details', ['id' => $manga['attributes']['slug']]);
$titles = Kitsu::filterTitles($manga['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/manga/{$id}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php endif ?>
</div>
return implode('', array_map('mb_trim', $rendered));
}, 'media-wrap content') ?>
<?php endif ?>
<section>
@ -118,7 +75,7 @@ use Aviat\AnimeClient\API\Kitsu;
$link = $url->generate('person', ['id' => $c['person']['id']]);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($c['person']['image'], TRUE)) ?>
<?= $helper->img($c['person']['image']) ?>
<div class="name">
<?= $c['person']['name'] ?>
</div>
@ -134,7 +91,7 @@ use Aviat\AnimeClient\API\Kitsu;
$titles = Kitsu::filterTitles($series['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
<?= $helper->img(Kitsu::getPosterImage($series['attributes'])) ?>
</a>
<div class="name">
<a href="<?= $link ?>">
@ -158,66 +115,47 @@ use Aviat\AnimeClient\API\Kitsu;
<?php if ( ! empty($vas)): ?>
<h4>Voice Actors</h4>
<div class="tabs">
<?php $i = 0; ?>
<?= $component->tabs('character-vas', $vas, static function ($casting) use ($url, $component, $helper) {
$castings = [];
foreach ($casting as $id => $c):
$person = $component->character(
$c['person']['name'],
$url->generate('person', ['slug' => $c['person']['slug']]),
$helper->img($c['person']['image']['original']['url']),
);
$medias = array_map(fn ($series) => $component->media(
array_merge([$series['title']], $series['titles']),
$url->generate('anime.details', ['id' => $series['slug']]),
$helper->img(Kitsu::getPosterImage($series)),
), $c['series']);
$media = implode('', array_map('mb_trim', $medias));
<?php foreach ($vas as $language => $casting): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="character-va<?= $i ?>"
name="character-vas"
/>
<label for="character-va<?= $i ?>"><?= $language ?></label>
<section class="content">
<table class="borderless max-table">
<tr>
<th>Cast Member</th>
<th>Series</th>
</tr>
<?php foreach ($casting as $cid => $c): ?>
<tr>
<td>
<article class="character">
<?php
$link = $url->generate('person', ['id' => $c['person']['id']]);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($c['person']['image'])) ?>
<div class="name">
<?= $c['person']['name'] ?>
</div>
</a>
</article>
</td>
<td width="75%">
<section class="align-left media-wrap-flex">
<?php foreach ($c['series'] as $series): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $series['attributes']['slug']]);
$titles = Kitsu::filterTitles($series['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
</td>
</tr>
<?php endforeach ?>
</table>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>
$castings[] = <<<HTML
<tr>
<td>{$person}</td>
<td width="75%">
<section class="align-left media-wrap-flex">
{$media}
</section>
</td>
</tr>
HTML;
endforeach;
$languages = implode('', array_map('mb_trim', $castings));
return <<<HTML
<table class="borderless max-table">
<thead>
<tr>
<th>Cast Member</th>
<th>Series</th>
</tr>
</thead>
<tbody>{$languages}</tbody>
</table>
HTML;
}, 'content') ?>
<?php endif ?>
<?php endif ?>
</section>

View File

@ -9,7 +9,7 @@
<div class="cssload-inner cssload-two"></div>
<div class="cssload-inner cssload-three"></div>
</div>
<label for="search">Search for <?= $collection_type ?> by name:&nbsp;&nbsp;&nbsp;&nbsp;<input type="search" id="search" name="search" /></label>
<label for="search-anime-collection">Search for <?= $collection_type ?> by name:&nbsp;&nbsp;&nbsp;&nbsp;<input type="search" id="search-anime-collection" name="search" /></label>
<section id="series-list" class="media-wrap">
</section>
</section>
@ -19,7 +19,7 @@
<tr>
<td class="align-right"><label for="media_id">Media</label></td>
<td class='align-left'>
<?php include '_media-select-list.php' ?>
<?php include 'media-select-list.php' ?>
</td>
</tr>
<tr>

View File

@ -1,3 +1,4 @@
<?php use function Aviat\AnimeClient\renderTemplate; ?>
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
@ -8,30 +9,20 @@
<br />
<label>Filter: <input type='text' class='media-filter' /></label>
<br />
<div class="tabs">
<?php $i = 0; ?>
<?php foreach ($sections as $name => $items): ?>
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<section class="media-wrap">
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/cover-item.php'; ?>
<?php endforeach ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
<!-- All Tab -->
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class='content full-height'>
<section class="media-wrap">
<?php foreach ($all as $item): ?>
<?php include __DIR__ . '/cover-item.php'; ?>
<?php endforeach ?>
</section>
</div>
</div>
<?= $component->tabs('collection-tab', $sections, static function ($items) use ($auth, $collection_type, $helper, $url, $component) {
$rendered = [];
foreach ($items as $item)
{
$rendered[] = renderTemplate(__DIR__ . '/cover-item.php', [
'auth' => $auth,
'collection_type' => $collection_type,
'helper' => $helper,
'item' => $item,
'url' => $url,
]);
}
return implode('', array_map('mb_trim', $rendered));
}, 'media-wrap', true) ?>
<?php endif ?>
</main>

View File

@ -1,3 +1,4 @@
<?php use function Aviat\AnimeClient\renderTemplate ?>
<?php if ($auth->isAuthenticated()): ?>
<main>
<h2>Edit Anime Collection Item</h2>
@ -24,7 +25,7 @@
<tr>
<td class="align-right"><label for="media_id">Media</label></td>
<td class="align-left">
<?php include '_media-select-list.php' ?>
<?php include 'media-select-list.php' ?>
</td>
</tr>
<tr>

View File

@ -1,44 +0,0 @@
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class="content full-height">
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?><td>&nbsp;</td><?php endif ?>
<th>Title</th>
<th>Media</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<th>Notes</th>
<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach ($all as $item): ?>
<?php $editLink = $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]); ?>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>
<a class="bracketed" href="<?= $editLink ?>">Edit</a>
</td>
<?php endif ?>
<td class="align-left">
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
<?= $item['title'] ?>
</a>
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
</td>
<td><?= implode(', ', $item['media']) ?></td>
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
<td><?= $item['episode_length'] ?></td>
<td><?= $item['show_type'] ?></td>
<td><?= $item['age_rating'] ?></td>
<td class="align-left"><?= nl2br($item['notes'], TRUE) ?></td>
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -11,6 +11,9 @@
</a>
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
</td>
<?php if ($hasMedia): ?>
<td><?= implode(', ', $item['media']) ?></td>
<?php endif ?>
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
<td><?= $item['episode_length'] ?></td>
<td><?= $item['show_type'] ?></td>

View File

@ -1,4 +1,4 @@
<?php use function Aviat\AnimeClient\col_not_empty; ?>
<?php use function Aviat\AnimeClient\{colNotEmpty, renderTemplate}; ?>
<main>
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
@ -9,38 +9,48 @@
<br />
<label>Filter: <input type='text' class='media-filter' /></label>
<br />
<?php $i = 0; ?>
<div class="tabs">
<?php foreach ($sections as $name => $items): ?>
<?php $hasNotes = col_not_empty($items, 'notes') ?>
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?><td>&nbsp;</td><?php endif ?>
<th>Title</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<?php if ($hasNotes): ?><th>Notes</th><?php endif ?>
<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<?php include 'list-item.php' ?>
<?php endforeach ?>
</tbody>
</table>
</div>
<?php $i++ ?>
<?php endforeach ?>
<!-- All -->
<?php include 'list-all.php' ?>
</div>
<?= $component->tabs('collection-tab', $sections, static function ($items, $section) use ($auth, $helper, $url, $collection_type) {
$hasNotes = colNotEmpty($items, 'notes');
$hasMedia = $section === 'All';
$firstTh = ($auth->isAuthenticated()) ? '<td>&nbsp;</td>' : '';
$mediaTh = ($hasMedia) ? '<th>Media</th>' : '';
$noteTh = ($hasNotes) ? '<th>Notes</th>' : '';
$rendered = [];
foreach ($items as $item)
{
$rendered[] = renderTemplate(__DIR__ . '/list-item.php', [
'auth' => $auth,
'collection_type' => $collection_type,
'hasMedia' => $hasMedia,
'hasNotes' => $hasNotes,
'helper' => $helper,
'item' => $item,
'url' => $url,
]);
}
$rows = implode('', array_map('mb_trim', $rendered));
return <<<HTML
<table class="full-width media-wrap">
<thead>
<tr>
{$firstTh}
<th>Title</th>
{$mediaTh}
<th class='numeric'>Episode Count</th>
<th class='numeric'>Episode Length</th>
<th>Show Type</th>
<th class='rating'>Age Rating</th>
{$noteTh}
<th>Genres</th>
</tr>
</thead>
<tbody>{$rows}</tbody>
</table>
HTML;
}) ?>
<?php endif ?>
</main>
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>

View File

@ -11,12 +11,6 @@
</div>
</section>
<script nomodule="nomodule" src="https://polyfill.io/v3/polyfill.min.js?features=es5%2CObject.assign"></script>
<?php if ($auth->isAuthenticated()): ?>
<script nomodule='nomodule' async="async" defer="defer" src="<?= $urlGenerator->assetUrl('js/scripts.min.js') ?>"></script>
<script type="module" src="<?= $urlGenerator->assetUrl('es/scripts.js') ?>"></script>
<?php else: ?>
<script nomodule="nomodule" async="async" defer="defer" src="<?= $urlGenerator->assetUrl('js/anon.min.js') ?>"></script>
<script type="module" src="<?= $urlGenerator->assetUrl('es/anon.js') ?>"></script>
<?php endif ?>
<script async="async" defer="defer" src="<?= $urlGenerator->assetUrl('js/scripts.min.js') ?>"></script>
</body>
</html>

View File

@ -39,5 +39,4 @@
}
}
?>
</header>

View File

@ -7,11 +7,9 @@
<article class="flex flex-no-wrap flex-justify-start">
<section class="flex-self-center history-img">
<a href="<?= $item['url'] ?>">
<?= $helper->picture(
<?= $helper->img(
$item['coverImg'],
'jpg',
['width' => '110px', 'height' => '156px'],
['width' => '110px', 'height' => '156px']
) ?>
</a>
</section>

View File

@ -5,13 +5,13 @@ namespace Aviat\AnimeClient;
$whose = $config->get('whose_list') . "'s ";
$lastSegment = $urlGenerator->lastSegment();
$extraSegment = $lastSegment === 'list' ? '/list' : '';
$hasAnime = stripos($_SERVER['REQUEST_URI'], 'anime') !== FALSE;
$hasManga = stripos($_SERVER['REQUEST_URI'], 'manga') !== FALSE;
$hasAnime = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'anime');
$hasManga = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'manga');
?>
<div id="main-nav" class="flex flex-align-end flex-wrap">
<span class="flex-no-wrap grow-1">
<?php if(strpos($route_path, 'collection') === FALSE): ?>
<?php if( ! str_contains($route_path, 'collection')): ?>
<?= $helper->a(
$urlGenerator->defaultUrl($url_type),
$whose . ucfirst($url_type) . ' List',
@ -84,7 +84,7 @@ $hasManga = stripos($_SERVER['REQUEST_URI'], 'manga') !== FALSE;
<?php if ($container->get('util')->isViewPage() && ($hasAnime || $hasManga)): ?>
<nav>
<?= $helper->menu($menu_name) ?>
<?php if (stripos($_SERVER['REQUEST_URI'], 'history') === FALSE): ?>
<?php if (stripos($GLOBALS['_SERVER']['REQUEST_URI'], 'history') === FALSE): ?>
<br />
<ul>
<?php $currentView = Util::eq('list', $lastSegment) ? 'list' : 'cover' ?>

View File

@ -19,80 +19,7 @@
<h2><?= $escape->html($name) ?></h2>
<section class="media-wrap">
<?php foreach($items as $item): ?>
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
<?php if ($auth->isAuthenticated()): ?>
<div class="edit-buttons" hidden>
<button class="plus-one-chapter">+1 Chapter</button>
<?php /* <button class="plus-one-volume">+1 Volume</button> */ ?>
</div>
<?php endif ?>
<?= $helper->picture("images/manga/{$item['manga']['id']}.webp") ?>
<div class="name">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $escape->html($item['manga']['title']) ?>
<?php foreach($item['manga']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
<div class="table">
<?php if ($auth->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed"
title="Edit information about this manga"
href="<?= $url->generate('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">
Edit
</a>
</span>
</div>
<?php endif ?>
<div class="row">
<div><?= $item['manga']['type'] ?></div>
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
</div>
<?php if ($item['rereading']): ?>
<div class="row">
<?php foreach(['rereading'] as $attr): ?>
<?php if($item[$attr]): ?>
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
<?php endif ?>
<?php endforeach ?>
</div>
<?php endif ?>
<?php if ($item['reread'] > 0): ?>
<div class="row">
<?php if ($item['reread'] == 1): ?>
<div>Reread once</div>
<?php elseif ($item['reread'] == 2): ?>
<div>Reread twice</div>
<?php elseif ($item['reread'] == 3): ?>
<div>Reread thrice</div>
<?php else: ?>
<div>Reread <?= $item['reread'] ?> times</div>
<?php endif ?>
</div>
<?php endif ?>
<div class="row">
<div class="chapter_completion">
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
</div>
<?php /* </div>
<div class="row"> */ ?>
<div class="volume_completion">
Volumes: <span class="volume_count"><?= $item['volumes']['total'] ?></span>
</div>
</div>
</div>
</article>
<?= $component->mangaCover($item, $name) ?>
<?php endforeach ?>
</section>
</section>

View File

@ -1,23 +1,51 @@
<main class="details fixed">
<section class="flex flex-no-wrap">
<aside class="info">
<?= $helper->picture("images/manga/{$data['id']}-original.webp", 'jpg', ['class' => 'cover']) ?>
<?= $helper->img($data['cover_image'], ['class' => 'cover', 'width' => '350']) ?>
<br />
<table class="media-details">
<tr>
<td>Manga Type</td>
<td><?= ucfirst($data['manga_type']) ?></td>
<td class="align-right">Publishing Status</td>
<td><?= $data['status'] ?></td>
</tr>
<tr>
<td>Manga Type</td>
<td><?= ucfirst(strtolower($data['manga_type'])) ?></td>
</tr>
<?php if ( ! empty($data['volume_count'])): ?>
<tr>
<td>Volume Count</td>
<td><?= $data['volume_count'] ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['chapter_count'])): ?>
<tr>
<td>Chapter Count</td>
<td><?= $data['chapter_count'] ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['age_rating'])): ?>
<tr>
<td>Age Rating</td>
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -29,8 +57,8 @@
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<?php foreach ($data['titles'] as $title): ?>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>
@ -44,61 +72,34 @@
<?php if (count($data['characters']) > 0): ?>
<h2>Characters</h2>
<div class="tabs">
<?php $i = 0 ?>
<?php foreach ($data['characters'] as $role => $list): ?>
<input
type="radio" name="character-role-tabs"
id="character-tabs<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="character-tabs<?= $i ?>"><?= ucfirst($role) ?></label>
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($list as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name">
<?= $helper->a($link, $char['name']); ?>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$id}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>
<?= $component->tabs('manga-characters', $data['characters'], static function($list, $role) use ($component, $helper, $url) {
$rendered = [];
foreach ($list as $id => $char)
{
$rendered[] = $component->character(
$char['name'],
$url->generate('character', ['slug' => $char['slug']]),
$helper->img($char['image'], ['loading' => 'lazy']),
($role !== 'main') ? 'small-character' : 'character'
);
}
return implode('', array_map('mb_trim', $rendered));
}) ?>
<?php endif ?>
<?php if (count($data['staff']) > 0): ?>
<h2>Staff</h2>
<div class="vertical-tabs">
<?php $i = 0 ?>
<?php foreach ($data['staff'] as $role => $people): ?>
<div class="tab">
<input
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<section class='content media-wrap flex flex-wrap flex-justify-start'>
<?php foreach ($people as $pid => $person): ?>
<article class='character person'>
<?php $link = $url->generate('person', ['id' => $pid]) ?>
<div class="name">
<a href="<?= $link ?>">
<?= $person['name'] ?>
</a>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/people/{$pid}.webp") ?>
</a>
</article>
<?php endforeach ?>
</section>
</div>
<?php $i++ ?>
<?php endforeach ?>
</div>
<?= $component->verticalTabs('manga-staff', $data['staff'],
fn($people) => implode('', array_map(
fn ($person) => $component->character(
$person['name'],
$url->generate('person', ['slug' => $person['slug']]),
$helper->img($person['image']),
),
$people
))
) ?>
<?php endif ?>
</main>

View File

@ -18,7 +18,7 @@
<tbody>
<tr>
<td rowspan="9">
<?= $helper->picture("images/manga/{$item['manga']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
<?= $helper->img($item['manga']['image']) ?>
</td>
</tr>
<tr>
@ -46,13 +46,6 @@
value="<?= $item['chapters']['read'] ?>"/> / <?= $item['chapters']['total'] ?>
</td>
</tr>
<tr>
<td><label for="volumes_read">Volumes Read</label></td>
<td>
<?php /*<input type="number" disabled="disabled" min="0" name="volumes_read" id="volumes_read" value="" /> */ ?>
- / <?= $item['volumes']['total'] ?>
</td>
</tr>
<tr>
<td><label for="rereading_flag">Rereading?</label></td>
<td>

View File

@ -20,12 +20,10 @@
<td>&nbsp;</td>
<?php endif ?>
<th>Title</th>
<th>Rating</th>
<th>Completed Chapters</th>
<th># of Volumes</th>
<th class='numeric'>Score</th>
<th class='numeric'>Completed Chapters</th>
<th>Attributes</th>
<th>Type</th>
<th>Genres</th>
</tr>
</thead>
<tbody>
@ -50,7 +48,6 @@
</td>
<td><?= $item['user_rating'] ?> / 10</td>
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>
<td><?= $item['volumes']['total'] ?></td>
<td>
<ul>
<?php if ($item['reread'] == 1): ?>
@ -70,9 +67,6 @@
</ul>
</td>
<td><?= $item['manga']['type'] ?></td>
<td class="align-left">
<?= implode(', ', $item['manga']['genres']) ?>
</td>
</tr>
<?php endforeach ?>
</tbody>

View File

@ -1,67 +0,0 @@
<?php
use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\Kitsu;
?>
<h3>Voice Acting Roles</h3>
<div class="tabs">
<?php $i = 0; ?>
<?php foreach($data['characters'] as $role => $characterList): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" name="character-type-tabs" id="character-type-<?= $i ?>" />
<label for="character-type-<?= $i ?>"><h5><?= ucfirst($role) ?></h5></label>
<section class="content">
<table class="borderless max-table">
<tr>
<th>Character</th>
<th>Series</th>
</tr>
<?php foreach ($characterList as $cid => $character): ?>
<tr>
<td>
<article class="character">
<?php
$link = $url->generate('character', ['slug' => $character['character']['slug']]);
?>
<a href="<?= $link ?>">
<?php $imgPath = ($character['character']['image'] === NULL)
? 'images/characters/empty.png'
: getLocalImg($character['character']['image']['original']);
echo $helper->picture($imgPath);
?>
<div class="name">
<?= $character['character']['canonicalName'] ?>
</div>
</a>
</article>
</td>
<td>
<section class="align-left media-wrap">
<?php foreach ($character['media'] as $sid => $series): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $series['slug']]);
$titles = Kitsu::filterTitles($series);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$sid}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
</td>
</tr>
<?php endforeach; ?>
</table>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>

View File

@ -1,21 +1,25 @@
<?php
use Aviat\AnimeClient\API\Kitsu;
?>
<main class="details fixed">
<section class="flex flex-no-wrap">
<div>
<?= $helper->picture("images/people/{$data['id']}-original.webp", 'jpg', ['class' => 'cover' ]) ?>
<?= $helper->img($data['image'], ['class' => 'cover' ]) ?>
</div>
<div>
<h2 class="toph"><?= $data['name'] ?></h2>
<?php foreach ($data['names'] as $name): ?>
<h3><?= $name ?></h3>
<?php endforeach ?>
<br />
<hr />
<div class="description">
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
</div>
</div>
</section>
<?php if ( ! empty($data['staff'])): ?>
<section>
<h3>Castings</h3>
<div class="vertical-tabs">
<?php $i = 0 ?>
<?php foreach ($data['staff'] as $role => $entries): ?>
@ -24,31 +28,17 @@ use Aviat\AnimeClient\API\Kitsu;
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<?php foreach ($entries as $type => $casting): ?>
<?php if ($type === 'characters') continue; ?>
<?php if ( ! (empty($entries['manga']) || empty($entries['anime']))): ?>
<?php if (isset($entries['manga'], $entries['anime'])): ?>
<h4><?= ucfirst($type) ?></h4>
<?php endif ?>
<section class="content">
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($casting as $sid => $series): ?>
<article class="media">
<?php
$mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime';
$link = $url->generate("{$mediaType}.details", ['id' => $series['slug']]);
$titles = Kitsu::filterTitles($series);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/{$type}/{$sid}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
<?= $component->media(
$series['titles'],
$url->generate("{$mediaType}.details", ['id' => $series['slug']]),
$helper->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
) ?>
<?php endforeach; ?>
</section>
<?php endforeach ?>
@ -59,9 +49,53 @@ use Aviat\AnimeClient\API\Kitsu;
</section>
<?php endif ?>
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
<?php if ( ! empty($data['characters'])): ?>
<section>
<?php include 'character-mapping.php' ?>
<h3>Voice Acting Roles</h3>
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $url) {
$voiceRoles = [];
foreach ($characterList as $cid => $item):
$character = $component->character(
$item['character']['canonicalName'],
$url->generate('character', ['slug' => $item['character']['slug']]),
$helper->img($item['character']['image'], ['loading' => 'lazy']),
);
$medias = [];
foreach ($item['media'] as $sid => $series)
{
$medias[] = $component->media(
$series['titles'],
$url->generate('anime.details', ['id' => $series['slug']]),
$helper->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
);
}
$media = implode('', array_map('mb_trim', $medias));
$voiceRoles[] = <<<HTML
<tr>
<td>{$character}</td>
<td>
<section class="align-left media-wrap">{$media}</section>
</td>
</tr>
HTML;
endforeach;
$roles = implode('', array_map('mb_trim', $voiceRoles));
return <<<HTML
<table class="borderless max-table">
<thead>
<tr>
<th>Character</th>
<th>Series</th>
</tr>
</thead>
<tbody>{$roles}</tbody>
</table>
HTML;
}) ?>
</section>
<?php endif ?>
</main>

View File

@ -28,9 +28,7 @@ $nestedPrefix = 'config';
/>
<label for="settings-tab<?= $i ?>"><h3><?= $sectionMapping[$section] ?></h3></label>
<section class="content">
<?php require __DIR__ . '/_form.php' ?>
<?php if ($section === 'anilist'): ?>
<hr />
<?php $auth = $anilistModel->checkAuth(); ?>
<?php if (array_key_exists('errors', $auth)): ?>
<p class="static-message error">Not Authorized.</p>
@ -43,11 +41,15 @@ $nestedPrefix = 'config';
<p class="static-message info">
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
</p>
<?php require __DIR__ . '/_form.php' ?>
<?= $helper->a(
$url->generate('anilist-redirect'),
'Update Access Token'
) ?>
$url->generate('anilist-redirect'),
'Update Access Token',
['class' => 'bracketed user-btn']
) ?>
<?php endif ?>
<?php else: ?>
<?php require __DIR__ . '/_form.php' ?>
<?php endif ?>
</section>
<?php $i++; ?>

View File

@ -1,5 +1,5 @@
<?php
use Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\Kitsu;
?>
<main class="user-page details">
<h2 class="toph">
@ -16,7 +16,7 @@ use Aviat\AnimeClient\API\Kitsu;
<section class="flex flex-no-wrap">
<aside class="info">
<center>
<?= $helper->img($urlGenerator->assetUrl($data['avatar']), ['alt' => '']); ?>
<?= $helper->img($data['avatar'], ['alt' => '']); ?>
</center>
<br />
<table class="media-details">
@ -36,7 +36,7 @@ use Aviat\AnimeClient\API\Kitsu;
$character = $data['waifu']['character'];
echo $helper->a(
$url->generate('character', ['slug' => $character['slug']]),
$character['canonicalName']
$character['names']['canonical']
);
?>
</td>
@ -57,79 +57,43 @@ use Aviat\AnimeClient\API\Kitsu;
<article>
<?php if ( ! empty($data['favorites'])): ?>
<h3>Favorites</h3>
<div class="tabs">
<?php $i = 0 ?>
<?php if ( ! empty($data['favorites']['characters'])): ?>
<input type="radio" name="user-favorites" id="user-fav-chars" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-chars">Characters</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['characters'] as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name"><?= $helper->a($link, $char['canonicalName']); ?></div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$char['id']}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
<?php if ( ! empty($data['favorites']['anime'])): ?>
<input type="radio" name="user-favorites" id="user-fav-anime" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-anime">Anime</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['anime'] as $anime): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $anime['slug']]);
$titles = Kitsu::filterTitles($anime);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$anime['id']}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
<?php if ( ! empty($data['favorites']['manga'])): ?>
<input type="radio" name="user-favorites" id="user-fav-manga" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-manga">Manga</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['manga'] as $manga): ?>
<article class="media">
<?php
$link = $url->generate('manga.details', ['id' => $manga['slug']]);
$titles = Kitsu::filterTitles($manga);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/manga/{$manga['id']}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
</div>
<?= $component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($component, $helper, $url) {
$rendered = [];
if ($type === 'character')
{
uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
}
else
{
uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
}
foreach ($items as $id => $item)
{
if ($type === 'character')
{
$rendered[] = $component->character(
$item['names']['canonical'],
$url->generate('character', ['slug' => $item['slug']]),
$helper->img($item['image']['original']['url'])
);
}
else
{
$rendered[] = $component->media(
array_merge(
[$item['titles']['canonical']],
Kitsu::getFilteredTitles($item['titles']),
),
$url->generate("{$type}.details", ['id' => $item['slug']]),
$helper->img(Kitsu::getPosterImage($item), ['width' => 220]),
);
}
}
return implode('', array_map('mb_trim', $rendered));
}, 'content full-width media-wrap') ?>
<?php endif ?>
</article>
</section>

View File

@ -1,28 +0,0 @@
<documentation title="Closing comments instead of PHP closing tag">
<standard>
<![CDATA[
The PHP closing tag on a PHP document ?> is optional to the PHP parser. However, if used, any whitespace following the closing tag, whether introduced by the developer, user, or an FTP application, can cause unwanted output, PHP errors, or if the latter are suppressed, blank pages. For this reason, all PHP files should OMIT the closing PHP tag, and instead use a comment block to mark the end of file and it's location relative to the application root. This allows you to still identify a file as being complete and not truncated.
]]>
</standard>
<code_comparison>
<code title="Examples of valid closing comments">
<![CDATA[
<?php
echo "Here's my code!";
/* End of file myfile.php */
/* Location: ./system/modules/mymodule/myfile.php */
]]>
</code>
<code title="Examples of invalid closing comments">
<![CDATA[
<?php
echo "Here's my code!";
?>
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,7 +0,0 @@
<documentation title="Unicode (UTF-8) encoding without BOM">
<standard>
<![CDATA[
Files should be saved with Unicode (UTF-8) encoding. The BOM should not be used. Unlike UTF-16 and UTF-32, there's no byte order to indicate in a UTF-8 encoded file, and the BOM can have a negative side effect in PHP of sending output, preventing the application from being able to set its own headers. Unix line endings should be used (LF).
]]>
</standard>
</documentation>

View File

@ -1,31 +0,0 @@
<documentation title="Constructor Names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
]]>
</standard>
<code_comparison>
<code title="Examples of valid constructor name">
<![CDATA[
class Super_class
{
function Super_class()
{
echo 'Some code here !';
}
}
]]>
</code>
<code title="Examples of invalid constructor name">
<![CDATA[
class Super_class
{
function __constructor()
{
echo 'Some code here !';
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,21 +0,0 @@
<documentation title="Class names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
]]>
</standard>
<code_comparison>
<code title="Examples of valid class names">
<![CDATA[
class Super_class
]]>
</code>
<code title="Examples of invalid class names">
<![CDATA[
class SuperClass // words not separated with underscores and words next to the first one start with an upper case
class superclass // words not separated with underscores
class Super_Class // words next to the first one start with an upper case
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,21 +0,0 @@
<documentation title="File names">
<standard>
<![CDATA[
To be able to find which class is contained in a file, file names should be case-insensitively equal to class names. Some operating systems and tools are case-insensitive, though other are. So, file names should be in lower case to avoid any trouble.
]]>
</standard>
<code_comparison>
<code title="Examples of valid file names">
<![CDATA[
super_class.php
]]>
</code>
<code title="Examples of invalid file names">
<![CDATA[
superclass.php // words not separated with underscores
SuperClass.php // not in lower case and words not separated with underscores
Super_class.php // not in lower case
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,27 +0,0 @@
<documentation title="Function and Method Names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
Methods and variables that are only accessed internally by your class, such as utility and helper functions that your public methods use for code abstraction, should be prefixed with an underscore.
]]>
</standard>
<code_comparison>
<code title="Examples of valid method names">
<![CDATA[
function get_file_properties() // descriptive, underscore separator, and all lowercase letters
private function _get_file_properties()
]]>
</code>
<code title="Examples of invalid method names">
<![CDATA[
function fileproperties() // not descriptive and needs underscore separator
function fileProperties() // not descriptive and uses CamelCase
function getfileproperties() // Better! But still missing underscore separator
function getFileProperties() // uses CamelCase
function get_the_file_properties_from_the_file() // wordy
private function get_the_file_properties() // not prefixed with an underscor, though private
function _get_the_file_properties() // prefixed with an underscor, though public
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,31 +0,0 @@
<documentation title="Variable names">
<standard>
<![CDATA[
Namely, variables should contain only lowercase letters, use underscore separators, and be reasonably named to indicate their purpose and contents. Very short, non-word variables should only be used as iterators in for() loops.
Methods and variables that are only accessed internally by your class, such as utility and helper functions that your public methods use for code abstraction, should be prefixed with an underscore.
]]>
</standard>
<code_comparison>
<code title="Examples of valid variable names">
<![CDATA[
for ($j = 0; $j < 10; $j++)
$str
$buffer
$group_id
$last_city
private $_internal_data;
]]>
</code>
<code title="Examples of invalid variable names">
<![CDATA[
$j = 'foo'; // single letter variables should only be used in for() loops
$Str // contains uppercase letters
$bufferedText // uses CamelCasing, and could be shortened without losing semantic meaning
$groupid // multiple words, needs underscore separator
$name_of_last_city_used // too long
private $internal_data; // not prefixed with an underscor, though private
$_public_attribute; // prefixed with an underscor, though public
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,40 +0,0 @@
<documentation title="Strict comparison operators">
<standard>
<![CDATA[
Some PHP functions return FALSE on failure, but may also have a valid return value of "" or 0, which would evaluate to FALSE in loose comparisons. Be explicit by comparing the variable type when using these return values in conditionals to ensure the return value is indeed what you expect, and not a value that has an equivalent loose-type evaluation.
Use the same stringency in returning and checking your own variables. Use === and !== as necessary.
]]>
</standard>
<code_comparison>
<code title="Valid strict comparison">
<![CDATA[
if (strpos($str, 'foo') === FALSE) {
echo 'Do something.';
}
function build_string($str = "")
{
if ($str === "") {
echo 'Buid string.';
}
}
]]>
</code>
<code title="Invalid loose comparison">
<![CDATA[
// If 'foo' is at the beginning of the string, strpos will return a 0,
// resulting in this conditional evaluating as TRUE
if (strpos($str, 'foo') == FALSE) {
echo 'Do something.';
}
function build_string($str = "")
{
if ($str == "") { // uh-oh! What if FALSE or the integer 0 is passed as an argument?
echo 'Buid string.';
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,28 +0,0 @@
<documentation title="Double-quoted strings">
<standard>
<![CDATA[
Always use single quoted strings unless you need variables parsed, and in cases where you do need variables parsed, use braces to prevent greedy token parsing. You may also use double-quoted strings if the string contains single quotes, so you do not have to use escape characters.
]]>
</standard>
<code_comparison>
<code title="Examples of invalid double-quoted strings">
<![CDATA[
"My String" // no variable parsing, so no use for double quotes
"My string $foo" // needs braces
'SELECT foo FROM bar WHERE baz = \'bag\'' // ugly
'\r\n' // it isn't wrong, but it won't be interpreted as a new line feed
]]>
</code>
<code title="Examples of valid double-quoted strings">
<![CDATA[
'My String'
"My string {$foo}" // variables in strings may be enclosed with braces in 2 ways
"My string ${foo}"
"My string {$foo['bar']}" // variables in strings may be an array entry
"My string {$foo->bar}" // variables in strings may be an object attribute
"SELECT foo FROM bar WHERE baz = 'bag'"
"\n" // not specified in Code Igniter coding standard, but it should be allowed
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,187 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Commenting_InlineCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Commenting_InlineCommentSniff.
*
* Ensure the use of single line comments within code (i.e //)
* and blank lines between large comment blocks and code.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Commenting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class InlineCommentSniff implements Sniff
{
/**
* @var int Limit defining long comments.
* Long comments count $longCommentLimit or more lines.
*/
public $longCommentLimit = 5;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_COMMENT
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// keep testing only if it's about the first comment of the block
$previousCommentPtr = $phpcsFile->findPrevious($tokens[$stackPtr]['code'], $stackPtr - 1);
if ($tokens[$previousCommentPtr]['line'] !== $tokens[$stackPtr]['line'] - 1) {
if (TRUE !== $this->_checkCommentStyle($phpcsFile, $stackPtr)) {
return;
}
$commentLines = $this->_getCommentBlock($phpcsFile, $stackPtr);
if (count($commentLines) >= $this->longCommentLimit) {
$this->_checkBlankLinesAroundLongComment($phpcsFile, $commentLines);
}
}
}//end process()
/**
* Add error to $phpcsFile, if comment pointed by $stackPtr doesn't start
* with '//'.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* that has to be a comment.
*
* @return bool TRUE if the content of the token pointed by $stackPtr starts
* with //, FALSE if an error was added to $phpcsFile.
*/
private function _checkCommentStyle(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content'][0] === '#') {
$error = 'Perl-style comments are not allowed; use "// Comment" or DocBlock comments instead';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
} else if (substr($tokens[$stackPtr]['content'], 0, 2) === '/*'
|| $tokens[$stackPtr]['content'][0] === '*'
) {
$error = 'Multi lines comments are not allowed; use "// Comment" DocBlock comments instead';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
} else if (substr($tokens[$stackPtr]['content'], 0, 2) !== '//') {
$error = 'Use single line or DocBlock comments within code';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
}
return TRUE;
}//_checkCommentStyle()
/**
* Gather into an array all comment lines to which $stackPtr belongs.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr Pointer to the first comment line.
*
* @return type array Pointers to tokens making up the comment block.
*/
private function _getCommentBlock(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$commentLines = array($stackPtr);
$nextComment = $stackPtr;
$lastLine = $tokens[$stackPtr]['line'];
while (($nextComment = $phpcsFile->findNext($tokens[$stackPtr]['code'], ($nextComment + 1), null, false)) !== false) {
if (($tokens[$nextComment]['line'] - 1) !== $lastLine) {
// Not part of the block.
break;
}
$lastLine = $tokens[$nextComment]['line'];
$commentLines[] = $nextComment;
}
return $commentLines;
}//_getCommentBlock()
/**
* Add errors to $phpcsFile, if $commentLines isn't enclosed with blank lines.
*
* @param File $phpcsFile The current file being scanned.
* @param array $commentLines Lines of the comment block being checked.
*
* @return bool TRUE if $commentLines is enclosed with at least a blank line
* before and after, FALSE otherwise.
*/
private function _checkBlankLinesAroundLongComment(File $phpcsFile, array $commentLines)
{
$hasBlankLinesAround = TRUE;
$tokens = $phpcsFile->getTokens();
// check blank line before the long comment
$firstCommentPtr = reset($commentLines);
$firstPreviousSpacePtr = $firstCommentPtr - 1;
while (T_WHITESPACE === $tokens[$firstPreviousSpacePtr]['code'] && $firstPreviousSpacePtr > 0) {
$firstPreviousSpacePtr--;
}
if ($tokens[$firstPreviousSpacePtr]['line'] >= $tokens[$firstCommentPtr]['line'] - 1) {
$error = "Please add a blank line before comments counting more than {$this->longCommentLimit} lines.";
$phpcsFile->addError($error, $firstCommentPtr, 'LongCommentWithoutSpacing');
$hasBlankLinesAround = FALSE;
}
// check blank line after the long comment
$lastCommentPtr = end($commentLines);
$lastNextSpacePtr = $lastCommentPtr + 1;
while (T_WHITESPACE === $tokens[$lastNextSpacePtr]['code'] && $lastNextSpacePtr < count($tokens)) {
$lastNextSpacePtr++;
}
if ($tokens[$lastNextSpacePtr]['line'] <= $tokens[$lastCommentPtr]['line'] + 1) {
$error = "Please add a blank line after comments counting more than {$this->longCommentLimit} lines.";
$phpcsFile->addError($error, $lastCommentPtr, 'LongCommentWithoutSpacing');
$hasBlankLinesAround = FALSE;
}
return $hasBlankLinesAround;
}//end _checkBlanksAroundLongComment()
}//end class
?>

View File

@ -1,98 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ByteOrderMarkSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ByteOrderMarkSniff.
*
* Ensures that no BOM appears at the beginning of file.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ByteOrderMarkSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array( T_OPEN_TAG );
}//end register()
/**
* List of supported BOM definitions.
*
* Use encoding names as keys and hex BOM representations as values.
*
* @return array
*/
protected function getBomDefinitions()
{
return array(
'UTF-8' => 'efbbbf',
'UTF-16 (BE)' => 'feff',
'UTF-16 (LE)' => 'fffe',
'UTF-32 (BE)' => '0000feff',
'UTF-32 (LE)' => 'fffe0000'
);
}//end getBomDefinitions()
/**
* Process tokens.
*
* Actually, only proceed when we're at index 0, this should be the only case
* that will contain BOM. Then check if BOM definition matches what
* we've found as file's inline HTML. Inline HTML could be longer than just BOM
* so make sure you test as much as needed.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr )
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$tokens = $phpcsFile->getTokens();
$fileStartString = $tokens[0]['content'];
foreach ($this->getBomDefinitions() as $bomName => $expectedBomHex) {
$bomByteLength = strlen($expectedBomHex) / 2;
$fileStartHex = bin2hex(substr($fileStartString, 0, $bomByteLength));
if ($fileStartHex === $expectedBomHex) {
$error = "File contains a $bomName byte order mark (BOM).";
$phpcsFile->addError($error, $stackPtr, 123);
break;
}
}
}//end process()
}

View File

@ -1,222 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_Utf8EncodingSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_Utf8EncodingSniff.
*
* Ensures that PHP files are encoded with Unicode (UTF-8) encoding.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class Utf8EncodingSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$file_path = $phpcsFile->getFilename();
$file_name = basename($file_path);
$file_content = file_get_contents($file_path);
if (false === mb_check_encoding($file_content, 'UTF-8')) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding.';
$phpcsFile->addError($error, 0);
}
if ( ! self::_checkUtf8W3c($file_content)) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding, but it did not successfully pass the W3C test.';
$phpcsFile->addError($error, 0);
}
if ( ! self::_checkUtf8Rfc3629($file_content)) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding, but it did not meet RFC3629 requirements.';
$phpcsFile->addError($error, 0);
}
}//end process()
/**
* Checks that the string $content contains only valid UTF-8 chars
* using W3C's method.
* Returns true if $content contains only UTF-8 chars, false otherwise.
*
* @param string $content String to check.
*
* @return bool true if $content contains only UTF-8 chars, false otherwise.
*
* @see http://w3.org/International/questions/qa-forms-utf-8.html
*/
private static function _checkUtf8W3c($content)
{
$content_chunks=self::mb_chunk_split($content, 4096, '');
foreach($content_chunks as $content_chunk)
{
$preg_result= preg_match(
'%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs',
$content_chunk
);
if($preg_result!==1)
{
return false;
}
}
return true;
}//end _checkUtf8W3c()
/**
* Checks that the string $content contains only valid UTF-8 chars
* using the method described in RFC 3629.
* Returns true if $content contains only UTF-8 chars, false otherwise.
*
* @param string $content String to check.
*
* @return bool true if $content contains only UTF-8 chars, false otherwise.
*
* @see http://www.php.net/manual/en/function.mb-detect-encoding.php#85294
*/
private static function _checkUtf8Rfc3629($content)
{
$len = strlen($content);
for ($i = 0; $i < $len; $i++) {
$c = ord($content[$i]);
if ($c > 128) {
if (($c >= 254)) {
return false;
} elseif ($c >= 252) {
$bits=6;
} elseif ($c >= 248) {
$bits=5;
} elseif ($c >= 240) {
$bytes = 4;
} elseif ($c >= 224) {
$bytes = 3;
} elseif ($c >= 192) {
$bytes = 2;
} else {
return false;
} if (($i + $bytes) > $len) {
return false;
} while ($bytes > 1) {
$i++;
$b = ord($content[$i]);
if ($b < 128 || $b > 191) {
return false;
}
$bytes--;
}
}
}
return true;
}//_checkUtf8Rfc3629()
/**
* Splits a string to chunks of given size
* This helps to avoid segmentation fault errors when large text is given
* Returns array of strings after splitting
*
* @param string $str String to split.
* @param int $len number of characters per chunk
*
* @return array string array after splitting
*
* @see http://php.net/manual/en/function.chunk-split.php
*/
private static function mb_chunk_split($str, $len, $glue)
{
if (empty($str)) return false;
$array = self::mbStringToArray ($str);
$n = -1;
$new = Array();
foreach ($array as $char) {
$n++;
if ($n < $len) $new []= $char;
elseif ($n == $len) {
$new []= $glue . $char;
$n = 0;
}
}
return $new;
}//mb_chunk_split
/**
* Supporting function for mb_chunk_split
*
* @param string $str
*
* @return array
*
* @see http://php.net/manual/en/function.chunk-split.php
*/
private static function mbStringToArray ($str)
{
if (empty($str)) return false;
$len = mb_strlen($str);
$array = array();
for ($i = 0; $i < $len; $i++) {
$array[] = mb_substr($str, $i, 1);
}
return $array;
}
}//end class
?>

View File

@ -1,81 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Operators_StrictComparisonOperatorSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Operators_StrictComparisonOperatorSniff.
*
* Ensures that only strict comparison operators are used instead of
* equal and not equal operators.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Operators;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class StrictComparisonOperatorSniff implements Sniff
{
private static $_replacements = array(
T_IS_EQUAL => '===',
T_IS_NOT_EQUAL => '!==',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_IS_EQUAL,
T_IS_NOT_EQUAL,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$operator_token = $tokens[$stackPtr];
$operator_string = $operator_token['content'];
$operator_code = $operator_token['code'];
$error_message = '"==" and "!=" are prohibited; use "'
. self::$_replacements[$operator_code] . '" instead of "'
. $operator_string . '".';
$phpcsFile->addError($error_message, $stackPtr, 'NonStrictComparisonUsed');
}//end process()
}//end class
?>

View File

@ -1,465 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Strings;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use Exception;
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* Ensures that double-quoted strings are used only to parse variables,
* to avoid escape characters before single quotes or for chars that need
* to be interpreted like \r, \n or \t.
* If a double-quoted string contain both single and double quotes
* but no variable, then a warning is raised to encourage the use of
* single-quoted strings.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class VariableUsageSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
/*
return array(
T_DOUBLE_QUOTED_STRING,
T_CONSTANT_ENCAPSED_STRING,
);
*/
return array();
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$string = $tokens[$stackPtr]['content'];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$openDblQtStr = substr($string, 0, 1);
if (0 === strcmp($openDblQtStr, '"')) {
$this->processDoubleQuotedString($phpcsFile, $stackPtr, $string);
} else if (0 === strcmp($openDblQtStr, "'")) {
$this->processSingleQuotedString($phpcsFile, $stackPtr, $string);
}
}//end process()
/**
* Processes this test, when the token encountered is a double-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $dblQtString The double-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processDoubleQuotedString (File $phpcsFile, $stackPtr, $dblQtString)
{
$variableFound = FALSE;
$strTokens = token_get_all('<?php '.$dblQtString);
$strPtr = 1; // skip php opening tag added by ourselves
$requireDblQuotes = FALSE;
while ($strPtr < count($strTokens)) {
$strToken = $strTokens[$strPtr];
if (is_array($strToken)) {
if (in_array($strToken[0], array(T_DOLLAR_OPEN_CURLY_BRACES, T_CURLY_OPEN))) {
$strPtr++;
try {
$this->_parseVariable($strTokens, $strPtr);
} catch (Exception $err) {
$error = 'There is no variable, object nor array between curly braces. Please use the escape char for $ or {.';
$phpcsFile->addError($error, $stackPtr, 234);
}
$variableFound = TRUE;
if ('}' !== $strTokens[$strPtr]) {
$error = 'There is no matching closing curly brace.';
$phpcsFile->addError($error, $stackPtr, 345);
}
// don't move forward, since it will be done in the main loop
// $strPtr++;
} else if (T_VARIABLE === $strToken[0]) {
$variableFound = TRUE;
$error = "Variable {$strToken[1]} in double-quoted strings should be enclosed with curly braces. Please consider {{$strToken[1]}}";
$phpcsFile->addError($error, $stackPtr, 456);
}
}
$strPtr++;
}
return $variableFound;
}//end processDoubleQuotedString()
/**
* Processes this test, when the token encountered is a single-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $sglQtString The single-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processSingleQuotedString (File $phpcsFile, $stackPtr, $sglQtString)
{
$variableFound = FALSE;
$strTokens = token_get_all('<?php '.$sglQtString);
$strPtr = 1; // skip php opening tag added by ourselves
while ($strPtr < count($strTokens)) {
$strToken = $strTokens[$strPtr];
if (is_array($strToken)) {
if (T_VARIABLE === $strToken[0]) {
$error = "Variables like {$strToken[1]} should be in double-quoted strings only.";
$phpcsFile->addError($error, $stackPtr);
}
}
$strPtr++;
}
return $variableFound;
}//end processSingleQuotedString()
/**
* Grammar rule to parse the use of a variable. Please notice that it
* doesn't manage the leading $.
*
* _parseVariable ::= <variable>
* | <variable>_parseObjectAttribute()
* | <variable>_parseArrayIndexes()
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return array The attribute name associated to index 'var', an array with
* indexes 'obj' and 'attr' or an array with indexes 'arr' and 'idx'.
*/
private function _parseVariable ($strTokens, &$strPtr)
{
if ( ! in_array($strTokens[$strPtr][0], array(T_VARIABLE, T_STRING_VARNAME))) {
throw new Exception ('Expected variable name.');
}
$var = $strTokens[$strPtr][1];
$strPtr++;
$startStrPtr = $strPtr;
try {
$attr = $this->_parseObjectAttribute($strTokens, $strPtr);
return array ('obj' => $var, 'attr' => $attr);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
try {
$idx = $this->_parseArrayIndexes($strTokens, $strPtr);
return array ('arr' => $var, 'idx' => $idx);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
return array ('var' => $var);
}//end _parseVariable()
/**
* Grammar rule to parse the use of an object attribute.
*
* _parseObjectAttribute ::= -><attribute>
* | -><attribute>_parseObjectAttribute()
* | -><attribute>_parseArrayIndexes()
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return mixed The attribute name as a string, an array with indexes
* 'obj' and 'attr' or an array with indexes 'arr' and 'idx'.
*/
private function _parseObjectAttribute ($strTokens, &$strPtr)
{
if (T_OBJECT_OPERATOR !== $strTokens[$strPtr][0]) {
throw new Exception ('Expected ->.');
}
$strPtr++;
if (T_STRING !== $strTokens[$strPtr][0]) {
throw new Exception ('Expected an object attribute.');
}
$attr = $strTokens[$strPtr][1];
$strPtr++;
$startStrPtr = $strPtr;
try {
$sub_attr = $this->_parseObjectAttribute($strTokens, $strPtr);
return array ('obj' => $attr, 'attr' => $sub_attr);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
try {
$idx = $this->_parseArrayIndexes($strTokens, $strPtr);
return array ('arr' => $attr, 'idx' => $idx);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
return $attr;
}//end _parseObjectAttribute()
/**
* Grammar rule to parse the use of one or more array indexes.
*
* _parseArrayIndexes ::= _parseArrayIndex()+
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return array Indexes in the same order as in the string.
*/
private function _parseArrayIndexes ($strTokens, &$strPtr)
{
$indexes = array($this->_parseArrayIndex($strTokens, $strPtr));
try {
while (1) {
$startStrPtr = $strPtr;
$indexes [] = $this->_parseArrayIndex($strTokens, $strPtr);
}
} catch (Exception $err) {
if (0 !== ($strPtr - $startStrPtr)) {
throw $err;
}
return $indexes;
}
}//end _parseArrayIndexes()
/**
* Grammar rule to parse the use of array index.
*
* _parseArrayIndex ::= [<index>]
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return string Index between the 2 square brackets
*/
private function _parseArrayIndex ($strTokens, &$strPtr)
{
if ('[' !== $strTokens[$strPtr]) {
throw new Exception ('Expected [.');
}
$strPtr++;
if (! in_array($strTokens[$strPtr][0], array(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER))) {
throw new Exception ('Expected an array index.');
}
$index = $strTokens[$strPtr][1];
$strPtr++;
if (']' !== $strTokens[$strPtr]) {
throw new Exception ('Expected ].');
}
$strPtr++;
return $index;
}//end _parseArrayIndex()
}//end class
/**
* CodeIgniter_Sniffs_Strings_VariableUsageSniff.
*
* Ensures that variables parsed in double-quoted strings are enclosed with
* braces to prevent greedy token parsing.
* Single-quoted strings don't parse variables, so there is no risk of greedy
* token parsing.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class DoubleQuoteUsageSniff extends VariableUsageSniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_DOUBLE_QUOTED_STRING,
T_CONSTANT_ENCAPSED_STRING,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// no variable are in the string from here
$tokens = $phpcsFile->getTokens();
$qtString = $tokens[$stackPtr]['content'];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$open_qt_str = substr($qtString, 0, 1);
// clean the enclosing quotes
$qtString = substr($qtString, 1, strlen($qtString) - 1 - 1);
if (0 === strcmp($open_qt_str, '"')) {
$this->processDoubleQuotedString($phpcsFile, $stackPtr, $qtString);
} else if (0 === strcmp($open_qt_str, "'")) {
$this->processSingleQuotedString($phpcsFile, $stackPtr, $qtString);
}
}//end process()
/**
* Processes this test, when the token encountered is a double-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $qtString The double-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processDoubleQuotedString (File $phpcsFile, $stackPtr, $qtString)
{
// so there should be at least a single quote or a special char
// if there are the 2 kinds of quote and no special char, then add a warning
$has_variable = parent::processDoubleQuotedString($phpcsFile, $stackPtr, '"'.$qtString.'"');
$has_specific_sequence = $this->_hasSpecificSequence($qtString);
$dbl_qt_at = strpos($qtString, '"');
$smpl_qt_at = strpos($qtString, "'");
if (false === $has_variable && false === $has_specific_sequence
&& false === $smpl_qt_at
) {
$error = 'Single-quoted strings should be used unless it contains variables, special chars like \n or single quotes.';
$phpcsFile->addError($error, $stackPtr, 111);
} else if (false !== $smpl_qt_at && false !== $dbl_qt_at
&& false === $has_variable && false === $has_specific_sequence
) {
$warning = 'It is encouraged to use a single-quoted string, since it doesn\'t contain any variable nor special char though it mixes single and double quotes.';
$phpcsFile->addWarning($warning, $stackPtr, 222);
}
}//end processDoubleQuotedString()
/**
* Processes this test, when the token encountered is a single-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $qtString The single-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processSingleQuotedString (File $phpcsFile, $stackPtr, $qtString)
{
// if there is single quotes without additional double quotes,
// then user is allowed to use double quote to avoid having to
// escape single quotes. Don't add the warning, if an error was
// already added, because a variable was found in a single-quoted
// string.
$has_variable = parent::processSingleQuotedString($phpcsFile, $stackPtr, "'".$qtString."'");
$dbl_qt_at = strpos($qtString, '"');
$smpl_qt_at = strpos($qtString, "'");
if (false === $has_variable && false !== $smpl_qt_at && false === $dbl_qt_at) {
$warning = 'You may also use double-quoted strings if the string contains single quotes, so you do not have to use escape characters.';
$phpcsFile->addWarning($warning, $stackPtr, 333);
}
}//end processSingleQuotedString()
/**
* Return TRUE, if a sequence of chars that is parsed in a specific way
* in double-quoted strings is found, FALSE otherwise.
*
* @param string $string String in which sequence of special chars will
* be researched.
*
* @return TRUE, if a sequence of chars that is parsed in a specific way
* in double-quoted strings is found, FALSE otherwise.
*
* @link http://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.double
*/
private function _hasSpecificSequence($string)
{
$hasSpecificSequence = FALSE;
$specialMeaningStrs = array('\n', '\r', '\t', '\v', '\f');
foreach ($specialMeaningStrs as $splStr) {
if (FALSE !== strpos($string, $splStr)) {
$hasSpecificSequence = TRUE;
}
}
$specialMeaningPtrns = array('\[0-7]{1,3}', '\x[0-9A-Fa-f]{1,2}');
foreach ($specialMeaningPtrns as $splPtrn) {
if (1 === preg_match("/{$splPtrn}/", $string)) {
$hasSpecificSequence = TRUE;
}
}
return $hasSpecificSequence;
}//end _hasSpecificSequence()
}//end class
?>

View File

@ -1,87 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowSpaceIndentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas ERNEST
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowSpaceIndentSniff.
*
* Ensures the use of tabs for indentation.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas ERNEST
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class DisallowSpaceIndentSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
'CSS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_WHITESPACE);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile All the tokens found in the document.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Make sure this is whitespace used for indentation.
$line = $tokens[$stackPtr]['line'];
if ($stackPtr > 0 && $tokens[($stackPtr - 1)]['line'] === $line) {
return;
}
if (strpos($tokens[$stackPtr]['content'], " ") !== false) {
$error = 'Tabs must be used to indent lines; spaces are not allowed for code indentation';
$phpcsFile->addError($error, $stackPtr, 'SpacesUsedForIndentation');
}
}//end process()
}//end class
?>

View File

@ -1,95 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowWitheSpaceAroundPhpTagsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowWitheSpaceAroundPhpTagsSniff.
*
* Ensures that no whitespace precedes the opening PHP tag
* or follows the closing PHP tag.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class DisallowWitheSpaceAroundPhpTagsSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
T_CLOSE_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$php_tag_token = $tokens[$stackPtr];
$php_tag_code = $php_tag_token['code'];
if (T_OPEN_TAG === $php_tag_code) {
// opening php tag should be the first token.
// any whitespace beofre an opening php tag is tokenized
// as T_INLINE_HTML, so no need to check the content of the token.
$isFirst = 0 === $stackPtr;
if ( ! $isFirst) {
$error = 'Any char before the opening PHP tag is prohibited. Please remove newline or indentation before the opening PHP tag.';
$phpcsFile->addError($error, $stackPtr);
}
} else {
// if (T_CLOSE_TAG === $php_tag_code)
// closing php tag should be the last token
// and it must not contain any whitespace.
$php_tag_string = $php_tag_token['content'];
$isLast = count($tokens) - 1 === $stackPtr;
// both of the two closing php tags contains 2 chars exactly.
$containsEndTagOnly = strlen($php_tag_string) > 2;
if ( ! $isLast || ! $containsEndTagOnly ) {
$error = 'Any char after the closing PHP tag is prohibited. Please removes newline or spaces after the closing PHP tag.';
$phpcsFile->addError($error, $stackPtr);
}
}
}//end process()
}//end class
?>

View File

@ -1,82 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_ElseOnNewLineSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_ElseOnNewLineSniff.
*
* Ensures that control structures else and elseif stand on new lines.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ElseOnNewLineSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_ELSE,
T_ELSEIF,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$else_token = $tokens[$stackPtr];
$previous_non_blank_token_ptr = $phpcsFile->findPrevious(array(T_WHITESPACE), $stackPtr - 1, null, true);
if (false === $previous_non_blank_token_ptr) {
// else is no preceded with any symbol, but it is not the responsibility of this sniff.
return;
}
$previous_non_blank_token = $tokens[$previous_non_blank_token_ptr];
if ($previous_non_blank_token['line'] === $else_token['line']) {
$error = '"' . $else_token['content'] . '" should be on a new line.';
$phpcsFile->addError($error, $stackPtr, 123423);
}
}//end process()
}//end class
?>

View File

@ -1,75 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_LogicalNotSpacingSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_LogicalNotSpacingSniff.
*
* Ensures that at exactly a space precedes and follows the logical operator !.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class LogicalNotSpacingSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_BOOLEAN_NOT,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$operator_token = $tokens[$stackPtr];
$previous_token = $tokens[$stackPtr - 1];
$next_token = $tokens[$stackPtr + 1];
if (T_WHITESPACE !== $previous_token['code'] || T_WHITESPACE !== $next_token['code']) {
$error = 'Logical operator ! should always be preceded and followed with a whitespace.';
$phpcsFile->addError($error, $stackPtr, 'badNot');
}
}//end process()
}//end class
?>

View File

@ -1,104 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_AbstractClosingCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_AbstractClosingCommentSniff.
*
* Defines some methods used by
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff
* and CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class AbstractClosingCommentSniff implements Sniff
{
/**
* As an abstract class, this sniff is not associated to any token.
*/
public function register()
{
return array();
}
/**
* As an abstract class, this sniff is not dedicated to process a token.
*/
public function process(File $phpcsFile, $stackPtr)
{
$error = __CLASS__.'::'.__METHOD__.' is abstract. Please develop this method in a child class.';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Returns the comment without its delimiter(s) as well as leading
* and traling whitespaces.
*
* It removes the first #, the two first / (i.e. //) or the first /*
* and last \*\/. If a comment starts with /**, then the last * will remain
* as well as whitespaces between this star and the comment content.
*
* @param string $comment Comment containing either comment delimiter(s) and
* trailing or leading whitspaces to clean.
*
* @return string Comment without comment delimiter(s) and whitespaces.
*/
protected static function _getCommentContent ($comment)
{
if (self::_stringStartsWith($comment, '#')) {
$comment = substr($comment, 1);
} else if (self::_stringStartsWith($comment, '//')) {
$comment = substr($comment, 2);
} else if (self::_stringStartsWith($comment, '/*')) {
$comment = substr($comment, 2, strlen($comment) - 2 - 2);
}
$comment = trim($comment);
return $comment;
}//_getCommentContent()
/**
* Binary safe string comparison between $needle and
* the beginning of $haystack. Returns true if $haystack starts with
* $needle, false otherwise.
*
* @param string $haystack The string to search in.
* @param string $needle The string to search for.
*
* @return bool true if $haystack starts with $needle, false otherwise.
*/
protected static function _stringStartsWith ($haystack, $needle)
{
$startsWith = false;
if (strlen($needle) <= strlen($haystack)) {
$haystackBeginning = substr($haystack, 0, strlen($needle));
if (0 === strcmp($haystackBeginning, $needle)) {
$startsWith = true;
}
}
return $startsWith;
}//_stringStartsWith()
}//end class
?>

View File

@ -1,109 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff.
*
* Ensures that a comment containing the file name is available at the end of file.
* Only other comments and whitespaces are allowed to follow this specific comment.
*
* It may be all kind of comment like multi-line and inline C-style comments as
* well as PERL-style comments. Any number of white may separate comment delimiters
* from comment content. However, content has to be equal to template
* "End of file <file_name>". Comparison between content and template is
* case-sensitive.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
class ClosingFileCommentSniff extends AbstractClosingCommentSniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$fullFilename = $phpcsFile->getFilename();
$filename = basename($fullFilename);
$commentTemplate = "End of file $filename";
$tokens = $phpcsFile->getTokens();
$currentToken = count($tokens) - 1;
$hasClosingFileComment = false;
$isNotAWhitespaceOrAComment = false;
while ($currentToken >= 0
&& ! $isNotAWhitespaceOrAComment
&& ! $hasClosingFileComment
) {
$token = $tokens[$currentToken];
$tokenCode = $token['code'];
if (T_COMMENT === $tokenCode) {
$commentString = self::_getCommentContent($token['content']);
if (0 === strcmp($commentString, $commentTemplate)) {
$hasClosingFileComment = true;
}
} else if (T_WHITESPACE === $tokenCode) {
// Whitespaces are allowed between the closing file comment,
// other comments and end of file
} else {
$isNotAWhitespaceOrAComment = true;
}
$currentToken--;
}
if ( ! $hasClosingFileComment) {
$error = 'No comment block marks the end of file instead of the closing PHP tag. Please add a comment block containing only "' . $commentTemplate . '".';
$phpcsFile->addError($error, $currentToken);
}
}//end process()
}//end class
?>

View File

@ -1,182 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* Ensures that a comment containing the file location exists at the end of file.
* Only other comments and whitespaces are allowed between this comment and
* the end of file.
*
* It may be all kind of comment like multi-line and inline C-style comments as
* well as PERL-style comments. Any number of white may separate comment delimiters
* from comment content. However, content has to be equal to template
* "Location: <file_path_relative_to_application_root>".
* Comparison between content and template is case-sensitive.
*
* There are several ways to configure the application root. In order of priority :
* - Configuration variable ci_application_root.
* - Rule property applicationRoot.
* - Default value '/application/'
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Common;
class ClosingLocationCommentSniff extends AbstractClosingCommentSniff
{
public $applicationRoot = '/application/';
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$filePath = $phpcsFile->getFilename();
$tokens = $phpcsFile->getTokens();
// removes the application root from the beginning of the file path
$locationPath = self::_getLocationPath($filePath, $this->_getAppRoot());
// add an error, if application root doesn't exist in current file path
if (false === $locationPath) {
$error = 'Unable to find "' . $this->_getAppRoot() . '" in file path "' . $filePath . '". Please set your project\'s application root.';
$phpcsFile->addError($error, count($tokens) - 1);
return;
}
// generates the expected comment
$commentTemplate = "Location: $locationPath";
$currentToken = count($tokens) - 1;
$hasClosingLocationComment = false;
$isNotAWhitespaceOrAComment = false;
while ($currentToken >= 0
&& ! $isNotAWhitespaceOrAComment
&& ! $hasClosingLocationComment
) {
$token = $tokens[$currentToken];
$tokenCode = $token['code'];
if (T_COMMENT === $tokenCode) {
$commentString = self::_getCommentContent($token['content']);
if (0 === strcmp($commentString, $commentTemplate)) {
$hasClosingLocationComment = true;
}
} else if (T_WHITESPACE === $tokenCode) {
// Whitespaces are allowed between the closing file comment,
//other comments and end of file
} else {
$isNotAWhitespaceOrAComment = true;
}
$currentToken--;
}
if ( ! $hasClosingLocationComment) {
$error = 'No comment block marks the end of file instead of the closing PHP tag. Please add a comment block containing only "' . $commentTemplate . '".';
$phpcsFile->addError($error, $currentToken);
}
}//end process()
/**
* Returns the relative path from $appRoot to $filePath, or false if
* $appRoot cannot be found in $filePath, because $appRoot is not a parent
* of $filePath.
*
* @param string $filePath Full path to the file being proceed.
* @param string $appRoot Partial or full path to the CodeIgniter
* application root of the file being proceed. It must not contain the
* full path to the application root, but at least the name of the
* application root. Parent directory of the application root are allowed
* but not mandatory.
*
* @return string|bool The relative path from $appRoot to $filePath, or
* false if $appRoot cannot be found in $filePath.
*/
private static function _getLocationPath ($filePath, $appRoot)
{
// removes the path to application root
// from the beginning of the file path
$appRootAt = strpos($filePath, $appRoot);
if (false === $appRootAt) {
return false;
}
$localPath = substr($filePath, $appRootAt + strlen($appRoot));
// ensures the location path to be a relative path starting with "./".
if ( ! self::_stringStartsWith($localPath, './')) {
$localPath = './' . $localPath;
} else if ( ! self::_stringStartsWith($localPath, '.')
&& self::_stringStartsWith($localPath, '/')
) {
$localPath = '.' . $localPath;
}
return $localPath;
}//end _getLocationPath()
/**
* Returns the application root that should be used first.
*
* There are several ways to configure the application root.
* In order of priority :
* - Configuration variable ci_application_root.
* - Rule property applicationRoot.
* - Default value '/application/'
*
* @return string Path to your project application root.
*/
private function _getAppRoot()
{
$appRoot = Common::getConfigData('ci_application_root');
if (null === $appRoot) {
$appRoot = $this->applicationRoot;
}
return $appRoot;
}//end _getAppRoot()
}//end class
?>

View File

@ -1,142 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ConstructorNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
/**
* CodeIgniter_Sniffs_NamingConventions_ConstructorNameSniff.
*
* Favor PHP 4 constructor syntax, which uses "function ClassName()".
* Avoid PHP 5 constructor syntax, which uses "function __construct()".
*
* @todo Try to avoid overly long and verbose names.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class ConstructorNameSniff extends AbstractScopeSniff
{
public $php5Constructors = '1';
/**
* Constructs the test with the tokens it wishes to listen for.
*
* @return void
*/
public function __construct()
{
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}//end __construct()
/**
* Processes this test when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $currScope A pointer to the start of the scope.
*
* @return void
*/
protected function processTokenWithinScope(
File $phpcsFile,
$stackPtr,
$currScope
) {
$methodName = $phpcsFile->getDeclarationName($stackPtr);
$className = $phpcsFile->getDeclarationName($currScope);
$isPhp4Constructor = strcasecmp($methodName, $className) === 0;
$isPhp5Constructor = strcasecmp($methodName, '__construct') === 0;
if ($this->php5Constructors != '0') {
if ($isPhp4Constructor) {
$error = "PHP4 style constructors are not allowed; use \"__construct\" instead";
$phpcsFile->addError($error, $stackPtr);
}
} else {
if ($isPhp5Constructor) {
$error = "PHP5 style constructors are not allowed; use \"$className\" instead";
$phpcsFile->addError($error, $stackPtr);
}
}
if ( ! $isPhp4Constructor && ! $isPhp5Constructor ) {
return;
}
$tokens = $phpcsFile->getTokens();
$parentClassName = $phpcsFile->findExtendedClassName($currScope);
$wrongConstructor = '';
// prepares the error message and wrong constructor
if ($this->php5Constructors != '0') {
$error = 'PHP4 style calls to parent constructors are not allowed.';
$error = "$error Please use \"parent::__construct\" instead.";
if (false !== $parentClassName) {
$wrongConstructor = $parentClassName;
}
// Else $wrongConstructor will be empty
// and the test expression will always be false.
// It doesn't check that no parent method should be called
// when no parent class is defined.
} else {
$error = 'PHP5 style calls to parent constructors are not allowed.';
if (false !== $parentClassName) {
$error = "$error Please use \"parent::$parentClassName\" instead.";
}
$wrongConstructor = '__construct';
}
// looks for the use of a wrong constructor.
$endFunctionIndex = $tokens[$stackPtr]['scope_closer'];
$doubleColonIndex = $phpcsFile->findNext(
array(T_DOUBLE_COLON),
$stackPtr,
$endFunctionIndex
);
while ($doubleColonIndex) {
if ($tokens[($doubleColonIndex + 1)]['code'] === T_STRING
&& $tokens[($doubleColonIndex + 1)]['content'] === $wrongConstructor
) {
$phpcsFile->addError($error, ($doubleColonIndex + 1));
}
$doubleColonIndex = $phpcsFile->findNext(
array(T_DOUBLE_COLON),
$doubleColonIndex + 1,
$endFunctionIndex
);
}
}//end processTokenWithinScope()
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
// TODO: Implement processTokenOutsideScope() method.
}
}//end class
?>

View File

@ -1,84 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidClassNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidClassNameSniff.
*
* Ensures that class and interface names have their first letter uppercase
* and that words are separated with an underscore, and not CamelCased.
*
* @todo Try to avoid overly long and verbose names in using property rule and
* configuration variable to set limits. Have a look at
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ValidClassNameSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// get the class name
$className = trim($phpcsFile->getDeclarationName($stackPtr));
// compute the expected class name
// [^_] means "something different from _", but not "nothing or something different from _"
$lcClassNameChunk = preg_replace('/([^_])([A-Z])/', '${1}_${2}', $className);
$expectedClassName
= strtoupper($className[0]) . strtolower(substr($lcClassNameChunk,1));
// ensures that the current class name
// and the expected class name are identical
if (0 !== strcmp($className, $expectedClassName)) {
$error = 'Class names should always have their first letter uppercase. Multiple words should be separated with an underscore, and not CamelCased. Please consider ' . $expectedClassName . ' instead of ' . $className . '.';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View File

@ -1,84 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidFileNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidFileNameSniff.
*
* Tests that the file name matchs the name of the class that it contains in lower case.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ValidFileNameSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// computes the expected filename based on the name of the class or interface that it contains.
$decNamePtr = $phpcsFile->findNext(T_STRING, $stackPtr);
$decName = $tokens[$decNamePtr]['content'];
$expectedFileName = strtolower($decName);
// extracts filename without extension from its path.
$fullPath = $phpcsFile->getFilename();
$fileNameAndExt = basename($fullPath);
$fileName = substr($fileNameAndExt, 0, strrpos($fileNameAndExt, '.'));
if ($expectedFileName !== $fileName) {
$errorTemplate = 'Filename "%s" doesn\'t match the name of the %s that it contains "%s" in lower case. "%s" was expected.';
$errorMessage = sprintf(
$errorTemplate,
$fileName,
strtolower($tokens[$stackPtr]['content']), // class or interface
$decName,
$expectedFileName
);
$phpcsFile->addError($errorMessage, 0);
}
}//end process()
}//end class
?>

View File

@ -1,161 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* Ensures that class methods and functions areentirely lowercased and that
* words are separated with an underscore, and not CamelCased.
* Ensures that private class methods are prefixed with an underscore and that
* all other methods are not prefixed with an underscored.
* Ensures that names longer than 50 chars are prohibited. Likewise names longer
* than 35 chars raise a warning.
*
* @todo Use a rule property or a configuration variable to allow users to set
* their own maximum lengths for function and method names. Have a look at
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff and application root.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstactScopeSniff;
use PHP_CodeSniffer\Files\File;
class ValidMethodNameSniff extends AbstractScopeSniff
{
/**
* A list of all PHP magic methods.
*
* @var array
*/
protected static $magicMethods = array(
'construct',
'destruct',
'call',
'callStatic',
'get',
'set',
'isset',
'unset',
'sleep',
'wakeup',
'toString',
'set_state',
'clone',
);
/**
* Defines which token(s) in which scope(s) will be proceed.
*/
public function __construct()
{
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}//end __construct()
/**
* Processes the tokens within the scope.
*
* @param File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
* @param int $currScope The position of the current scope.
*
* @return void
*/
protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
{
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null) {
// Ignore closures.
return;
}
$className = $phpcsFile->getDeclarationName($currScope);
// Is this a magic method i.e. is prefixed with "__".
if (0 === strcmp(substr($methodName, 0, 2), '__')) {
$magicPart = substr($methodName, 2);
if (in_array($magicPart, self::$magicMethods) === false) {
$error = "Method name \"$className::$methodName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
$phpcsFile->addError($error, $stackPtr);
}
return;
}
// PHP4 constructors are allowed to break our rules.
if ($methodName === $className) {
return;
}
// PHP4 destructors are allowed to break our rules.
if ($methodName === '_'.$className) {
return;
}
if (0 !== strcmp($methodName, strtolower($methodName))) {
$uscrdMethodName = preg_replace('/([A-Z])/', '_${1}', $methodName);
$expectedMethodName = strtolower($uscrdMethodName);
$error = "Class methods should be entirely lowercased. Please consider \"$expectedMethodName\" instead of \"$methodName\".";
$phpcsFile->addError($error, $stackPtr);
return;
}
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
$scope = $methodProps['scope'];
$scopeSpecified = $methodProps['scope_specified'];
// If it's a private method, it must have an underscore on the front.
if ($scope === 'private' && $methodName{0} !== '_') {
$error = "Private method name \"$className::$methodName\" must be prefixed with an underscore";
$phpcsFile->addError($error, $stackPtr);
return;
}
// If it's not a private method, it must not have an underscore on the front.
if ($scope !== 'private' && $methodName{0} === '_') {
if (true === $scopeSpecified) {
$error = "Public method name \"$className::$methodName\" must not be prefixed with an underscore";
} else {
$error = ucfirst($scope)." method name \"$className::$methodName\" must not be prefixed with an underscore";
}
$phpcsFile->addError($error, $stackPtr);
return;
}
// If name is too verbose,
// then either an error or a warning is displayed.
$error_limit = 50;
$warning_limit = 35;
if (strlen($methodName) > $error_limit) {
$error = "Overly long and verbose names are prohibited. Please find a name shorter than $error_limit chars.";
$phpcsFile->addError($error, $stackPtr);
return;
} else if (strlen($methodName) > $warning_limit) {
$warning = "Try to avoid overly long and verbose names in finding a name shorter than $warning_limit chars.";
$phpcsFile->addWarning($warning, $stackPtr);
}
}//end processTokenWithinScope()
}//end class
?>

View File

@ -1,562 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* Ensures that variable names contain only lowercase letters,
* use underscore separators.
* Ensures that class attribute names are prefixed with an underscore,
* only when they are private.
* Ensure that variable names are longer than 3 chars except those declared
* in for loops.
*
* @todo Try to avoid overly long and verbose names in using property rule and
* configuration variable to set limits. Have a look at
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
* @todo Use a property rule or a configuration variable to allow users to set
* minimum variable name length. Have a look at
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff and application root.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
use PHP_CodeSniffer\Files\File;
class ValidVariableNameSniff extends AbstractVariableSniff
{
/**
* Processes class member variables.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
// get variable name and properties
$tokens = $phpcsFile->getTokens();
$varTk = $tokens[$stackPtr];
$varName = substr($varTk['content'], 1);
$varProps = $phpcsFile->getMemberProperties($stackPtr);
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName) ) {
return;
}
if ( ! $this->checkVisibilityPrefix($phpcsFile, $stackPtr, $varName, $varProps)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
}//end processMemberVar()
/**
* Processes normal variables.
*
* @param File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
// get variable name
$tokens = $phpcsFile->getTokens();
$varTk = $tokens[$stackPtr];
$varName = substr($varTk['content'], 1);
// skip the current object variable, i.e. $this
if (0 === strcmp($varName, 'this')) {
return;
}
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
}//end processVariable()
/**
* Processes variables in double quoted strings.
*
* @param File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariableInString(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$stringTk = $tokens[$stackPtr];
$stringString = $stringTk['content'];
$varAt = self::_getVariablePosition($stringString, 0);
while (false !== $varAt) {
// get variable name
$matches = array();
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', substr($stringString, $varAt), $matches);
$varName = $matches[1];
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
// prepare checking next variable
$varAt = self::_getVariablePosition($stringString, $varAt + 1);
}
}//end processVariableInString()
/**
* Checks that the variable name is all in lower case, else it add an error
* to $phpcsFile. Returns true if variable name is all in lower case, false
* otherwise.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return bool true if variable name is all in lower case, false otherwise.
*/
protected function checkLowerCase(File $phpcsFile, $stackPtr, $varName)
{
$isInLowerCase = true;
if (0 !== strcmp($varName, strtolower($varName))) {
// get the expected variable name
$varNameWithUnderscores = preg_replace('/([A-Z])/', '_${1}', $varName);
$expectedVarName = strtolower(ltrim($varNameWithUnderscores, '_'));
// adapts the error message to the error case
if (strlen($varNameWithUnderscores) > strlen($varName)) {
$error = 'Variables should not use CamelCasing or start with a Capital.';
} else {
$error = 'Variables should be entirely lowercased.';
}
$error = $error . 'Please consider "' . $expectedVarName
. '" instead of "' . $varName . '".';
// adds the error and changes return value
$phpcsFile->addError($error, $stackPtr);
$isInLowerCase = false;
}
return $isInLowerCase;
}//end checkLowerCase()
/**
* Checks that an underscore is used at the beginning of a variable only if
* it is about a private variable. If it isn't a private variable, then it
* must not be prefixed with an underscore. Returns true if $varName is
* properly prefixed according to the variable visibility provided in
* $varProps, false otherwise.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
* @param array $varProps Member variable properties like
* its visibility.
*
* @return bool true if variable name is prefixed with an underscore only
* when it is about a private variable, false otherwise.
*/
protected function checkVisibilityPrefix(File $phpcsFile, $stackPtr, $varName, $varProps)
{
$isVisibilityPrefixRight = true;
$scope = $varProps['scope'];
// If it's a private variable, it must have an underscore on the front.
if ($scope === 'private' && $varName{0} !== '_') {
$error = "Private variable name \"$varName\" must be prefixed with an underscore";
$phpcsFile->addError($error, $stackPtr);
$isVisibilityPrefixRight = false;
} else if ($scope !== 'private' && $varName{0} === '_') {
// If it's not a private variable,
// then it must not start with an underscore.
if (isset ($scopeSpecified) && true === $scopeSpecified) {
$error = "Public variable name \"$varName\" must not be prefixed with an underscore";
} else {
$error = ucfirst($scope) . " variable name \"$varName\" must not be prefixed with an underscore";
}
$phpcsFile->addError($error, $stackPtr);
$isVisibilityPrefixRight = false;
}
return $isVisibilityPrefixRight;
}//end checkVisibilityPrefix()
/**
* Checks that variable name length is not too short. Returns true, if it
* meets minimum length requirement, false otherwise.
*
* A variable name is too short if it is shorter than the minimal
* length and it isn't in the list of allowed short names nor declared in a
* for loop (in which it would be nested).
* The minimal length is defined in the function. It is 3 chars now.
* The list of allowed short names is defined in the function.
* It is case-sensitive. It contains only 'ci' now.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return bool false if variable name $varName is shorter than the minimal
* length and it isn't in the list of allowed short names nor declared in a
* for loop (in which it would be nested), otherwise true.
*/
protected function checkLength(File $phpcsFile, $stackPtr, $varName)
{
$minLength = 3;
$allowedShortName = array('ci');
$isLengthRight = true;
// cleans variable name
$varName = ltrim($varName, '_');
if (strlen($varName) <= $minLength) {
// skips adding an error, if it is a specific variable name
if (in_array($varName, $allowedShortName)) {
return $isLengthRight;
}
// skips adding an error, if the variable is in a for loop
if (false !== self::_isInForLoop($phpcsFile, $stackPtr, $varName)) {
return $isLengthRight;
}
// adds the error message finally
$error = 'Very short'
. (
$minLength > 0 ?
' (i.e. less than ' . ($minLength + 1) . ' chars)'
: ''
)
. ', non-word variables like "' . $varName
. '" should only be used as iterators in for() loops.';
$phpcsFile->addError($error, $stackPtr);
$isLengthRight = false;
}
return $isLengthRight;
}//end checkLength()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in a for loop, otherwise false.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the head of a for loop, otherwise false.
*/
private static function _isInForLoop(File $phpcsFile, $stackPtr, $varName)
{
$keepLookingFromPtr = $stackPtr;
while (false !== $keepLookingFromPtr) {
// looks if it is in (head or body) of a for loop
$forPtr = self::_isInForLoopHead($phpcsFile, $keepLookingFromPtr);
if (false === $forPtr) {
$forPtr = self::_isInForLoopBody($phpcsFile, $keepLookingFromPtr);
}
// checks if it is declared in here and prepares next step
if (false !== $forPtr) {
if (false !== self::_isDeclaredInForLoop($phpcsFile, $forPtr, $varName)) {
return $forPtr;
}
$keepLookingFromPtr = $forPtr;
} else {
$keepLookingFromPtr = false;
}
}
return false;
}//end _isInForLoop()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in the head of a for loop, otherwise false.
* The head is the code placed between parenthesis next to the key word
* 'for' : for (<loop_head>) {<loop_body>}.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the head of a for loop, otherwise false.
*/
private static function _isInForLoopHead(File $phpcsFile, $stackPtr)
{
$isInForLoop = false;
$tokens = $phpcsFile->getTokens();
$currentTk = $tokens[$stackPtr];
if (array_key_exists('nested_parenthesis', $currentTk)) {
$nestedParenthesis = $currentTk['nested_parenthesis'];
foreach ( $nestedParenthesis as $openParPtr => $closeParPtr) {
$nonWhitspacePtr = $phpcsFile->findPrevious(
array(T_WHITESPACE),
$openParPtr - 1,
null,
true,
null,
true
);
if (false !== $nonWhitspacePtr) {
$isFor = T_FOR === $tokens[$nonWhitspacePtr]['code'];
if ($isFor) {
$isInForLoop = $nonWhitspacePtr;
break;
}
}
}
}
return $isInForLoop;
}//end _isInForLoopHead()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in the body of a for loop, otherwise false.
* The body are the instructions placed after parenthesis of a 'for'
* declaration, enclosed with curly brackets usually.
* 'for' : for (<loop_head>) {<loop_body>}.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the body of a for loop, otherwise false.
*/
private static function _isInForLoopBody(File $phpcsFile, $stackPtr)
{
$isInForLoop = false;
$tokens = $phpcsFile->getTokens();
// get englobing hierarchy
$parentPtrAndCode = $tokens[$stackPtr]['conditions'];
krsort($parentPtrAndCode);
// looks for a for loop having a body not enclosed with curly brackets,
// which involves that its body contains only one instruction.
if (is_array($parentPtrAndCode) && ! empty($parentPtrAndCode)) {
$parentCode = reset($parentPtrAndCode);
$parentPtr = key($parentPtrAndCode);
$openBracketPtr = $tokens[$parentPtr]['scope_opener'];
} else {
$parentCode = 0;
$parentPtr = 0;
$openBracketPtr = 0;
}
$openResearchScopePtr = $stackPtr;
// recursive search, since a for statement may englobe other inline
// control statement or may be near to function calls, etc...
while (false !== $openResearchScopePtr) {
$closeParPtr = $phpcsFile->findPrevious(
array(T_CLOSE_PARENTHESIS),
$openResearchScopePtr,
null,
false,
null,
true
);
// is there a closing parenthesis with a control statement before
// the previous instruction ?
if (false !== $closeParPtr) {
// is there no opening curly bracket specific to
// set of instructions, between the closing parenthesis
// and the current token ?
if ($openBracketPtr < $closeParPtr) {
// starts the search from the token before the closing
// parenthesis, if it isn't a for statement
$openResearchScopePtr = $closeParPtr - 1;
// is this parenthesis associated with a for statement ?
$closeParenthesisTk = $tokens[$closeParPtr];
if (array_key_exists('parenthesis_owner', $closeParenthesisTk)) {
$mayBeForPtr = $closeParenthesisTk['parenthesis_owner'];
$mayBeForTk = $tokens[$mayBeForPtr];
if (T_FOR === $mayBeForTk['code']) {
return $mayBeForPtr;
}
}
} else {
// if it is about a for loop, don't go further
// and detect it after one more loop execution, do it now
if (T_FOR === $parentCode) {
return $parentPtr;
}
// starts the search from the token before the one
// englobing the current statement
$openResearchScopePtr = $parentPtr - 1;
// re-initialize variables about the englobing structure
if (is_array($parentPtrAndCode)) {
$parentCode = next($parentPtrAndCode);
$parentPtr = key($parentPtrAndCode);
$openBracketPtr = $tokens[$parentPtr]['scope_opener'];
}
}
} else {
$openResearchScopePtr = false;
}
}
// looks for a for loop having a body enclosed with curly brackets
foreach ($parentPtrAndCode as $parentPtr => $parentCode) {
if (T_FOR === $parentCode) {
return $parentPtr;
}
}
return false;
}//end _isInForLoopBody()
/**
* Returns true if a variable declared in the head of the for loop pointed
* by $forPtr in file $phpcsFile has the name $varName.
*
* @param File $phpcsFile The current file being processed.
* @param int $forPtr The position of the 'for' token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return int|bool true if a variable declared in the head of the for loop
* pointed by $forPtr in file $phpcsFile has the name $varName.
*/
private static function _isDeclaredInForLoop(File $phpcsFile, $forPtr, $varName)
{
$isDeclaredInFor = false;
$tokens = $phpcsFile->getTokens();
$forVarPtrs = self::_getVarDeclaredInFor($phpcsFile, $forPtr);
foreach ($forVarPtrs as $forVarPtr) {
$forVarTk = $tokens[$forVarPtr];
// get variable name
$matches = array();
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', $forVarTk['content'], $matches);
$forVarName = $matches[1];
if (0 === strcmp($forVarName, $varName)) {
$isDeclaredInFor = $forVarPtr;
break;
}
}
return $isDeclaredInFor;
}//end _isDeclaredInForLoop()
/**
* Returns list of pointers to variables declared in for loop associated to
* $forPtr in file $phpcsFile.
*
* All pointers in the result list are pointing to token with code
* T_VARIABLE. An exception is raised, if $forPtr doesn't point a token with
* code T_FOR.
*
* @param File $phpcsFile The current file being processed.
* @param int $forPtr The position of the current token
* in the stack passed in $tokens.
*
* @return array List of pointers to variables declared in for loop $forPtr.
*/
private static function _getVarDeclaredInFor(File $phpcsFile, $forPtr)
{
$tokens = $phpcsFile->getTokens();
$forTk = $tokens[$forPtr];
if (T_FOR !== $forTk['code']) {
throw new PHP_CodeSniffer_Exception('$forPtr must be of type T_FOR');
}
$openParPtr = $forTk['parenthesis_opener'];
$openParenthesisTk = $tokens[$openParPtr];
$endOfDeclPtr = $phpcsFile->findNext(array(T_SEMICOLON), $openParPtr);
$forVarPtrs = array();
$varPtr = $phpcsFile->findNext(
array(T_VARIABLE),
$openParPtr + 1,
$endOfDeclPtr
);
while (false !== $varPtr) {
$forVarPtrs [] = $varPtr;
$varPtr = $phpcsFile->findNext(
array(T_VARIABLE),
$varPtr + 1,
$endOfDeclPtr
);
}
return $forVarPtrs;
}//end _getVarDeclaredInFor()
/**
* Returns the position of first occurrence of a PHP variable starting with
* $ in $haystack from $offset.
*
* @param string $haystack The string to search in.
* @param int $offset The optional offset parameter allows you to
* specify which character in haystack to start
* searching. The returned position is still
* relative to the beginning of haystack.
*
* @return mixed The position as an integer
* or the boolean false, if no variable is found.
*/
private static function _getVariablePosition($haystack, $offset = 0)
{
$var_starts_at = strpos($haystack, '$', $offset);
$is_a_var = false;
while (false !== $var_starts_at && ! $is_a_var) {
// makes sure that $ is used for a variable and not as a symbol,
// if $ is protected with the escape char, then it is a symbol.
if (0 !== strcmp($haystack[$var_starts_at - 1], '\\')) {
if (0 === strcmp($haystack[$var_starts_at + 1], '{')) {
// there is an opening brace in the right place
// so it looks for the closing brace in the right place
$hsChunk2 = substr($haystack, $var_starts_at + 2);
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\}/', $hsChunk2)) {
$is_a_var = true;
}
} else {
$hsChunk1 = substr($haystack, $var_starts_at + 1);
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff]/', $hsChunk1)) {
// $ is used for a variable and not as a symbol,
// since what follows $ matchs the definition of
// a variable label for PHP.
$is_a_var = true;
}
}
}
// update $var_starts_at for the next variable
// only if no variable was found, since it is returned otherwise.
if ( ! $is_a_var) {
$var_starts_at = strpos($haystack, '$', $var_starts_at + 1);
}
}
if ($is_a_var) {
return $var_starts_at;
} else {
return false;
}
}//end _getVariablePosition()
}//end class
?>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ruleset name="CodeIgniter">
<description>CodeIgniter coding standard as described at http://codeigniter.com/user_guide/general/styleguide.html.</description>
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<!-- PHP files should OMIT the closing PHP tag -->
<rule ref="Zend.Files.ClosingTag"/>
<!-- Always use full PHP opening tags -->
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<!-- Constants should always be fully uppercase -->
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<!-- TRUE, FALSE, and NULL keywords should always be fully uppercase -->
<rule ref="Generic.PHP.UpperCaseConstant"/>
<!-- One statement per line -->
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<!-- Classes and functions should be commented -->
<rule ref="PEAR.Commenting.ClassComment"/>
<rule ref="PEAR.Commenting.FunctionComment"/>
<!-- <rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>-->
<!-- Use warnings for docblock comments for files and variables, since nothing is cleary explained -->
<rule ref="PEAR.Commenting.FileComment">
<properties>
<property name="error" value="false"/>
</properties>
</rule>
<rule ref="Squiz.Commenting.VariableComment">
<properties>
<property name="error" value="false"/>
</properties>
</rule>
<!-- Use Allman style indenting. With the exception of Class declarations,
braces are always placed on a line by themselves, and indented at the same level as the control statement that "owns" them. -->
<rule ref="Generic.Functions.OpeningFunctionBraceBsdAllman"/>
<rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<!-- @todo Please see PHP_CodeSniffer_Standards_CodeIgniter_CodeIgniterCodingStandard for more details about what there is to do -->
</ruleset>

View File

@ -3,13 +3,13 @@
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.4
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2020 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/

View File

@ -1,29 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
stopOnFailure="false"
bootstrap="../tests/bootstrap.php"
beStrictAboutTestsThatDoNotTestAnything="true"
>
<filter>
<whitelist>
<directory suffix=".php">../src</directory>
</whitelist>
</filter>
<testsuites>
<testsuite name="AnimeClient">
<directory>../tests</directory>
</testsuite>
</testsuites>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" stopOnFailure="false" bootstrap="../tests/bootstrap.php" beStrictAboutTestsThatDoNotTestAnything="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">../src</directory>
</include>
<report>
<clover outputFile="logs/clover.xml"/>
<html outputDirectory="../coverage"/>
</report>
</coverage>
<testsuites>
<testsuite name="AnimeClient">
<directory>../tests/AnimeClient</directory>
</testsuite>
<testsuite name="Ion">
<directory>../tests/Ion</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-html" target="../coverage"/>
<log type="coverage-clover" target="logs/clover.xml"/>
<junit outputFile="logs/junit.xml"/>
</logging>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0" />
<server name="HTTP_HOST" value="localhost" />
<server name="SERVER_NAME" value="localhost" />
<server name="REQUEST_URI" value="/" />
<server name="REQUEST_METHOD" value="GET" />
</php>
</phpunit>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0"/>
<server name="HTTP_HOST" value="localhost"/>
<server name="SERVER_NAME" value="localhost"/>
<server name="REQUEST_URI" value="/"/>
<server name="REQUEST_METHOD" value="GET"/>
</php>
</phpunit>

View File

@ -2,6 +2,7 @@
declare(strict_types=1);
$file_patterns = [
'app/appConf/*.php',
'app/bootstrap.php',
'migrations/*.php',
'src/**/*.php',
@ -16,7 +17,7 @@ if ( ! function_exists('glob_recursive'))
{
// Does not support flag GLOB_BRACE
function glob_recursive($pattern, $flags = 0)
function glob_recursive(string $pattern, int $flags = 0): array
{
$files = glob($pattern, $flags);
@ -57,17 +58,21 @@ function get_text_to_replace(array $tokens): string
return $output;
}
function get_tokens($source): array
function get_tokens(string $source): array
{
return token_get_all($source);
}
function replace_files(array $files, $template)
function replace_files(array $files, string $template): void
{
print_r($files);
foreach ($files as $file)
{
$source = file_get_contents($file);
if ($source === FALSE)
{
continue;
}
if (stripos($source, 'namespace') === FALSE)
{

View File

@ -23,60 +23,51 @@
"autoload-dev": {
"psr-4": {
"Aviat\\AnimeClient\\Tests\\": "tests/AnimeClient",
"Aviat\\Ion\\Tests\\": "tests/Ion",
"CodeIgniter\\": "build/CodeIgniter/"
"Aviat\\Ion\\Tests\\": "tests/Ion"
}
},
"config": {
"lock": false,
"platform": {
"php": "7.4"
"php": "8"
}
},
"require": {
"amphp/http-client": "^4.2.2",
"amphp/amp": "^2.5.0",
"amphp/http-client": "^4.5.0",
"aura/html": "^2.5.0",
"aura/router": "^3.1.0",
"aura/session": "^2.1.0",
"aviat/banker": "^3.1.1",
"aviat/banker": "^3.0.0 || ^4.0.0",
"aviat/query": "^3.0.0",
"danielstjules/stringy": "^3.1.0",
"ext-dom": "*",
"ext-iconv": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-gd": "*",
"ext-pdo": "*",
"laminas/laminas-diactoros": "^2.2.3",
"laminas/laminas-httphandlerrunner": "^1.1.0",
"laminas/laminas-diactoros": "^2.5.0",
"laminas/laminas-httphandlerrunner": "^2.1.0",
"maximebf/consolekit": "^1.0.3",
"monolog/monolog": "^2.0.2",
"php": ">=7.4",
"psr/container": "^1.0.0",
"php": ">= 8.0.0",
"psr/http-message": "^1.0.1",
"psr/log": "^1.1.3",
"psr/log": "*",
"robmorgan/phinx": "^0.12.4",
"symfony/polyfill-mbstring": "^1.0.0",
"symfony/polyfill-util": "^1.0.0",
"tracy/tracy": "^2.8.0",
"yosymfony/toml": "^1.0.4"
},
"require-dev": {
"consolidation/robo": "^2.0.0",
"filp/whoops": "^2.1",
"pdepend/pdepend": "^2.",
"phploc/phploc": "^5.0.0",
"phpmd/phpmd": "^2.8.2",
"phpstan/phpstan": "^0.12.19",
"phpunit/phpunit": "^8.5.2",
"phpstan/phpstan": "^1.2.0",
"phpunit/phpunit": "^9.5.0",
"roave/security-advisories": "dev-master",
"robmorgan/phinx": "^0.10.6",
"sebastian/phpcpd": "^4.1.0",
"spatie/phpunit-snapshot-assertions": "^4.1.0",
"squizlabs/php_codesniffer": "^3.5.4",
"symfony/var-dumper": "^5.0.7",
"theseer/phpdox": "^0.12.0"
"spatie/phpunit-snapshot-assertions": "^4.1.0"
},
"scripts": {
"build": "vendor/bin/robo build",
"build:css": "cd public && npm run build:css && cd ..",
"build:js": "cd public && npm run build:js && cd ..",
"clean": "vendor/bin/robo clean",
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
"phpstan": "phpstan analyse -c phpstan.neon",
"watch:css": "cd public && npm run watch:css",
@ -85,9 +76,7 @@
"test-update": "vendor/bin/phpunit -c build --no-coverage -d --update-snapshots"
},
"scripts-descriptions": {
"build": "Generate the api docs",
"build:css": "Generate browser css",
"clean": "Remove documentation generation files and folders",
"coverage": "Generate a test coverage report",
"phpstan": "Run PHP Static analysis",
"test": "Run the unit tests"

View File

@ -7,7 +7,10 @@ require_once __DIR__ . '/vendor/autoload.php';
use Aviat\AnimeClient\Command;
use ConsoleKit\Console;
$_SERVER['HTTP_HOST'] = 'localhost';
$GLOBALS['_SERVER']['HTTP_HOST'] = 'localhost';
const APP_DIR = __DIR__ . '/app';
const TEMPLATE_DIR = APP_DIR . '/templates';
// -----------------------------------------------------------------------------
// Start console script
@ -23,7 +26,7 @@ try
'sync:lists' => Command\SyncLists::class
]))->run();
}
catch (\Exception $e)
catch (Throwable)
{
}

View File

@ -1,68 +0,0 @@
import compiler from '@ampproject/rollup-plugin-closure-compiler';
const plugins = [
compiler({
assumeFunctionWrapper: true,
compilationLevel: 'WHITESPACE_ONLY', //'ADVANCED',
createSourceMap: true,
env: 'BROWSER',
languageIn: 'ECMASCRIPT_2018',
languageOut: 'ES3'
})
];
const defaultOutput = {
format: 'iife',
sourcemap: true,
}
const nonModules = [{
input: './js/anon.js',
output: {
...defaultOutput,
file: '../public/js/anon.min.js',
sourcemapFile: '../public/js/anon.min.js.map',
},
plugins,
}, {
input: './js/index.js',
output: {
...defaultOutput,
file: '../public/js/scripts.min.js',
sourcemapFile: '../public/js/scripts.min.js.map',
},
plugins,
}, {
input: './js/base/sort-tables.js',
output: {
...defaultOutput,
file: '../public/js/tables.min.js',
sourcemapFile: '../public/js/tables.min.js.map',
},
plugins,
}];
const moduleOutput = {
format: 'es',
sourcemap: false,
}
let modules = [{
input: './js/anon.js',
output: {
...moduleOutput,
file: '../public/es/anon.js',
},
}, {
input: './js/index.js',
output: {
...moduleOutput,
file: '../public/es/scripts.js',
},
}];
// Return the config array for rollup
export default [
...nonModules,
...modules,
];

View File

@ -163,7 +163,7 @@ CSS Tabs
/* text-align: center; */
}
.tabs .content {
.tabs .content, .single-tab {
display: none;
max-height: 950px;
border: 1px solid #e5e5e5;
@ -175,7 +175,14 @@ CSS Tabs
overflow: auto;
}
.tabs .content.full-height {
.single-tab {
display: block;
border: 1px solid #e5e5e5;
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
margin-top: 1.5em;
}
.tabs .content.full-height, .single-tab.full-height {
max-height: none;
}

View File

@ -147,7 +147,8 @@ button:active {
.tabs > [type="radio"]:checked + label,
.tabs > [type="radio"]:checked + label + .content,
.vertical-tabs [type="radio"]:checked + label,
.vertical-tabs [type="radio"]:checked ~ .content {
.vertical-tabs [type="radio"]:checked ~ .content,
.single-tab {
/* border-color: #333; */
border: 0;
background: #666;

View File

@ -94,6 +94,7 @@ a:hover, a:active {
iframe {
display: block;
margin: 0 auto;
border: 0;
}
/* -----------------------------------------------------------------------------
@ -627,6 +628,14 @@ picture.cover {
background-size: contain;
background-repeat: no-repeat;
}
/* There are two .name elements, just darken them both in this case! */
.media.search.disabled .name {
background-color: #000;
background-color: rgba(0, 0, 0, 0.75);
background-size: cover;
background-size: contain;
background-repeat: no-repeat;
}
.media.search > .row {
z-index: 6;

View File

@ -9,7 +9,7 @@ const matches = (elm, selector) => {
return i > -1;
}
export const AnimeClient = {
const AnimeClient = {
/**
* Placeholder function
*/
@ -18,8 +18,8 @@ export const AnimeClient = {
* DOM selector
*
* @param {string} selector - The dom selector string
* @param {object} [context]
* @return {[HTMLElement]} - array of dom elements
* @param {Element} [context]
* @return array of dom elements
*/
$(selector, context = null) {
if (typeof selector !== 'string') {
@ -60,7 +60,7 @@ export const AnimeClient = {
/**
* Hide the selected element
*
* @param {string|Element} sel - the selector of the element to hide
* @param {string|Element|Element[]} sel - the selector of the element to hide
* @return {void}
*/
hide (sel) {
@ -77,7 +77,7 @@ export const AnimeClient = {
/**
* UnHide the selected element
*
* @param {string|Element} sel - the selector of the element to hide
* @param {string|Element|Element[]} sel - the selector of the element to hide
* @return {void}
*/
show (sel) {
@ -116,9 +116,9 @@ export const AnimeClient = {
/**
* Finds the closest parent element matching the passed selector
*
* @param {HTMLElement} current - the current HTMLElement
* @param {Element} current - the current Element
* @param {string} parentSelector - selector for the parent element
* @return {HTMLElement|null} - the parent element
* @return {Element|null} - the parent element
*/
closestParent (current, parentSelector) {
if (Element.prototype.closest !== undefined) {
@ -204,9 +204,9 @@ function delegateEvent(sel, target, event, listener) {
/**
* Add an event listener
*
* @param {string|HTMLElement} sel - the parent selector to bind to
* @param {string|Element} sel - the parent selector to bind to
* @param {string} event - event name(s) to bind
* @param {string|HTMLElement|function} target - the element to directly bind the event to
* @param {string|Element|function} target - the element to directly bind the event to
* @param {function} [listener] - event listener callback
* @return {void}
*/
@ -261,7 +261,7 @@ function ajaxSerialize(data) {
*
* @param {string} url - the url to request
* @param {Object} config - the configuration object
* @return {void}
* @return {XMLHttpRequest}
*/
AnimeClient.ajax = (url, config) => {
// Set some sane defaults
@ -322,6 +322,8 @@ AnimeClient.ajax = (url, config) => {
} else {
request.send(config.data);
}
return request
};
/**
@ -330,6 +332,7 @@ AnimeClient.ajax = (url, config) => {
* @param {string} url
* @param {object|function} data
* @param {function} [callback]
* @return {XMLHttpRequest}
*/
AnimeClient.get = (url, data, callback = null) => {
if (callback === null) {

View File

@ -1,30 +1,55 @@
import _ from './anime-client.js'
import { renderAnimeSearchResults } from './template-helpers.js'
import { renderSearchResults } from './template-helpers.js'
const search = (query) => {
const search = (query, isCollection = false) => {
// Show the loader
_.show('.cssload-loader');
// Do the api search
_.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
return _.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
searchResults = JSON.parse(searchResults);
// Hide the loader
_.hide('.cssload-loader');
// Show the results
_.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults.data);
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults, isCollection);
});
};
// Anime list search
if (_.hasElement('.anime #search')) {
let prevRequest = null;
_.on('#search', 'input', _.throttle(250, (e) => {
const query = encodeURIComponent(e.target.value);
if (query === '') {
return;
}
search(query);
if (prevRequest !== null) {
prevRequest.abort();
}
prevRequest = search(query);
}));
}
// Anime collection search
if (_.hasElement('#search-anime-collection')) {
let prevRequest = null;
_.on('#search-anime-collection', 'input', _.throttle(250, (e) => {
const query = encodeURIComponent(e.target.value);
if (query === '') {
return;
}
if (prevRequest !== null) {
prevRequest.abort();
}
prevRequest = search(query, true);
}));
}
@ -47,12 +72,12 @@ _.on('body.anime.list', 'click', '.plus-one', (e) => {
// If the episode count is 0, and incremented,
// change status to currently watching
if (isNaN(watchedCount) || watchedCount === 0) {
data.data.status = 'current';
data.data.status = 'CURRENT';
}
// If you increment at the last episode, mark as completed
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
data.data.status = 'completed';
data.data.status = 'COMPLETED';
}
_.show('#loading-shadow');
@ -65,14 +90,14 @@ _.on('body.anime.list', 'click', '.plus-one', (e) => {
success: (res) => {
const resData = JSON.parse(res);
if (resData.errors) {
if (resData.error) {
_.hide('#loading-shadow');
_.showMessage('error', `Failed to update ${title}. `);
_.scrollToTop();
return;
}
if (resData.data.attributes.status === 'completed') {
if (resData.data.libraryEntry.update.libraryEntry.status === 'COMPLETED') {
_.hide(parentSel);
}

View File

@ -1,227 +0,0 @@
/*
* classList.js: Cross-browser full element.classList implementation.
* 2014-07-23
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
if ("document" in self) {
// Full polyfill for browsers with no classList support
if (!("classList" in document.createElement("_"))) {
(function(view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList",
protoProp = "prototype",
elemCtrProto = view.Element[protoProp],
objCtr = Object,
strTrim = String[protoProp].trim || function() {
return this.replace(/^\s+|\s+$/g, "");
},
arrIndexOf = Array[protoProp].indexOf || function(item) {
var
i = 0,
len = this.length;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
,
DOMEx = function(type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
},
checkTokenAndGetIndex = function(classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR", "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR", "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
},
ClassList = function(elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
i = 0,
len = classes.length;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function() {
elem.setAttribute("class", this.toString());
};
},
classListProto = ClassList[protoProp] = [],
classListGetter = function() {
return new ClassList(this);
};
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function(i) {
return this[i] || null;
};
classListProto.contains = function(token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function() {
var
tokens = arguments,
i = 0,
l = tokens.length,
token, updated = false;
do {
token = tokens[i] + "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function() {
var
tokens = arguments,
i = 0,
l = tokens.length,
token, updated = false,
index;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (index !== -1) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function(token, force) {
token += "";
var
result = this.contains(token),
method = result ?
force !== true && "remove" :
force !== false && "add";
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.toString = function() {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter,
enumerable: true,
configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
} else {
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function() {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function(method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function(token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function(token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
testElement = null;
}());
}
}

View File

@ -6,9 +6,22 @@ const LightTableSorter = (() => {
const sort = (a, b) => {
let textA = text(a);
let textB = text(b);
const n = parseInt(textA, 10);
if (n) {
textA = n;
console.log("Comparing " + textA + " and " + textB)
if(th.classList.contains("numeric")){
let arrayA = textA.replace('episodes: ','').replace('-',0).split("/");
let arrayB = textB.replace('episodes: ','').replace('-',0).split("/");
if(arrayA.length > 1) {
textA = parseInt(arrayA[0],10) / parseInt(arrayA[1],10);
textB = parseInt(arrayB[0],10) / parseInt(arrayB[1],10);
}
else{
textA = parseInt(arrayA[0],10);
textB = parseInt(arrayB[0],10);
}
}
else if (parseInt(textA, 10)) {
textA = parseInt(textA, 10);
textB = parseInt(textB, 10);
}
if (textA > textB) {
@ -59,6 +72,7 @@ const LightTableSorter = (() => {
for (let i = 0, len = ths.length; i < len; i++) {
let th = ths[i];
th.classList.add('sorting');
th.classList.add('testing');
results.push(th.onclick = onClickEvent);
}
return results;

View File

@ -16,7 +16,7 @@ _.on('.media-filter', 'input', filterMedia);
/**
* Hide the html element attached to the event
*
* @param event
* @param {MouseEvent} event
* @return void
*/
function hide (event) {
@ -26,7 +26,7 @@ function hide (event) {
/**
* Confirm deletion of an item
*
* @param event
* @param {MouseEvent} event
* @return void
*/
function confirmDelete (event) {
@ -52,7 +52,7 @@ function clearAPICache () {
/**
* Scroll to the accordion/vertical tab section just opened
*
* @param event
* @param {InputEvent} event
* @return void
*/
function scrollToSection (event) {
@ -70,7 +70,7 @@ function scrollToSection (event) {
/**
* Filter an anime or manga list
*
* @param event
* @param {InputEvent} event
* @return void
*/
function filterMedia (event) {

View File

@ -1,4 +1,5 @@
import './anon.js';
import './sw.js';
import './events.js';
import './session-check.js';
import './anime.js';
import './manga.js';

View File

@ -1,23 +1,29 @@
import _ from './anime-client.js'
import { renderMangaSearchResults } from './template-helpers.js'
import { renderSearchResults } from './template-helpers.js'
const search = (query) => {
_.show('.cssload-loader');
_.get(_.url('/manga/search'), { query }, (searchResults, status) => {
return _.get(_.url('/manga/search'), { query }, (searchResults, status) => {
searchResults = JSON.parse(searchResults);
_.hide('.cssload-loader');
_.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults.data);
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('manga', searchResults);
});
};
if (_.hasElement('.manga #search')) {
let prevRequest = null
_.on('#search', 'input', _.throttle(250, (e) => {
let query = encodeURIComponent(e.target.value);
if (query === '') {
return;
}
search(query);
if (prevRequest !== null) {
prevRequest.abort();
}
prevRequest = search(query);
}));
}
@ -48,12 +54,12 @@ _.on('.manga.list', 'click', '.edit-buttons button', (e) => {
// If the episode count is 0, and incremented,
// change status to currently reading
if (isNaN(completed) || completed === 0) {
data.data.status = 'current';
data.data.status = 'CURRENT';
}
// If you increment at the last chapter, mark as completed
if ((!isNaN(completed)) && (completed + 1) === total) {
data.data.status = 'completed';
data.data.status = 'COMPLETED';
}
// Update the total count
@ -66,14 +72,22 @@ _.on('.manga.list', 'click', '.edit-buttons button', (e) => {
dataType: 'json',
type: 'POST',
mimeType: 'application/json',
success: () => {
if (data.data.status === 'completed') {
success: (res) => {
const resData = JSON.parse(res)
if (resData.error) {
_.hide('#loading-shadow');
_.showMessage('error', `Failed to update ${mangaName}. `);
_.scrollToTop();
return;
}
if (String(data.data.status).toUpperCase() === 'COMPLETED') {
_.hide(parentSel);
}
_.hide('#loading-shadow');
_.$(`.${type}s_read`, parentSel)[ 0 ].textContent = completed;
_.$(`.${type}s_read`, parentSel)[ 0 ].textContent = String(completed);
_.showMessage('success', `Successfully updated ${mangaName}`);
_.scrollToTop();
},

View File

@ -0,0 +1,40 @@
import _ from './anime-client.js';
(() => {
let hidden = null;
let visibilityChange = null;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
function handleVisibilityChange() {
// Check the user's session to see if they are currently logged-in
// when the page becomes visible
if ( ! document[hidden]) {
_.get('/heartbeat', (beat) => {
const status = JSON.parse(beat)
// If the session is expired, immediately reload so that
// you can't attempt to do an action that requires authentication
if (status.hasAuth !== true) {
document.removeEventListener(visibilityChange, handleVisibilityChange, false);
location.reload();
}
});
}
}
if (hidden === null) {
console.info('Page visibility API not supported, JS session check will not work');
} else {
document.addEventListener(visibilityChange, handleVisibilityChange, false);
}
})();

View File

@ -1,10 +1,8 @@
import './events.js';
// Start the service worker, if you can
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('Service worker registered', reg.scope);
}).catch(error => {
console.error('Failed to register service worker', error);
});
}
}

View File

@ -7,62 +7,58 @@ _.on('main', 'change', '.big-check', (e) => {
document.getElementById(`mal_${id}`).checked = true;
});
export function renderAnimeSearchResults (data) {
const results = [];
/**
* On search results with an existing library entry, this shows that fact, with an edit link for the existing
* library entry
*
* @param {'anime'|'manga'} type
* @param {Object} item
* @param isCollection
* @returns {String}
*/
function renderEditLink (type, item, isCollection = false) {
if (isCollection || item.libraryEntry === null) {
return '';
}
data.forEach(x => {
const item = x.attributes;
const titles = item.titles.join('<br />');
results.push(`
<article class="media search">
<div class="name">
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
<label for="${item.slug}">
<picture width="220">
<source srcset="/public/images/anime/${x.id}.webp" type="image/webp" />
<source srcset="/public/images/anime/${x.id}.jpg" type="image/jpeg" />
<img src="/public/images/anime/${x.id}.jpg" alt="" width="220" />
</picture>
<span class="name">
${item.canonicalTitle}<br />
<small>${titles}</small>
</span>
</label>
</div>
<div class="table">
<div class="row">
<span class="edit">
<a class="bracketed" href="/anime/details/${item.slug}">Info Page</a>
</span>
</div>
</div>
</article>
`);
});
return results.join('');
return `
<div class="row">
<span class="edit"><big>[ Already in List ]</big></span>
</div>
<div class="row">
<span class="edit">
<a class="bracketed" href="/${type}/edit/${item.libraryEntry.id}/${item.libraryEntry.status}">Edit</a>
</span>
</div>
<div class="row"><span class="edit">&nbsp;</span></div>
`
}
export function renderMangaSearchResults (data) {
const results = [];
data.forEach(x => {
const item = x.attributes;
/**
* Show the search results for a media item
*
* @param {'anime'|'manga'} type
* @param {Object} data
* @param {boolean} isCollection
* @returns {String}
*/
export function renderSearchResults (type, data, isCollection = false) {
return data.map(item => {
const titles = item.titles.join('<br />');
let disabled = item.libraryEntry !== null ? 'disabled' : '';
const editLink = renderEditLink(type, item, isCollection);
results.push(`
<article class="media search">
if (isCollection) {
disabled = '';
}
return `
<article class="media search ${disabled}">
<div class="name">
<input type="radio" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" ${disabled} />
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" ${disabled} />
<label for="${item.slug}">
<picture width="220">
<source srcset="/public/images/manga/${x.id}.webp" type="image/webp" />
<source srcset="/public/images/manga/${x.id}.jpg" type="image/jpeg" />
<img src="/public/images/manga/${x.id}.jpg" alt="" width="220" />
</picture>
<img src="${item.coverImage}" alt="" width="220" />
<span class="name">
${item.canonicalTitle}<br />
<small>${titles}</small>
@ -70,15 +66,14 @@ export function renderMangaSearchResults (data) {
</label>
</div>
<div class="table">
${editLink}
<div class="row">
<span class="edit">
<a class="bracketed" href="/manga/details/${item.slug}">Info Page</a>
<a class="bracketed" href="/${type}/details/${item.slug}">Info Page</a>
</span>
</div>
</div>
</article>
`);
});
return results.join('');
`;
}).join('');
}

View File

@ -3,19 +3,19 @@
"scripts": {
"build": "npm run build:css && npm run build:js",
"build:css": "node ./css.js",
"build:js": "rollup -c ./build-js.js",
"build:js": "spack",
"watch:css": "watch 'npm run build:css' --filter=./cssfilter.js",
"watch:js": "watch 'npm run build:js' ./js",
"watch": "concurrently \"npm:watch:css\" \"npm:watch:js\" --kill-others"
},
"devDependencies": {
"@ampproject/rollup-plugin-closure-compiler": "^0.25.2",
"concurrently": "^5.1.0",
"cssnano": "^4.1.10",
"postcss": "^7.0.27",
"postcss-import": "^12.0.1",
"@swc/cli": "^0.1.39",
"@swc/core": "^1.2.54",
"concurrently": "^6.0.2",
"cssnano": "^5.0.1",
"postcss": "^8.2.6",
"postcss-import": "^14.0.0",
"postcss-preset-env": "^6.7.0",
"rollup": "^2.4.0",
"watch": "^1.0.2"
}
}

View File

@ -0,0 +1,19 @@
module.exports = {
entry: {
'scripts.min': __dirname + '/js/index.js',
'tables.min': __dirname + '/js/base/sort-tables.js',
},
output: {
path: '../public/js',
},
options: {
jsc: {
target: 'es3',
loose: true,
},
minify: true,
module: {
type: 'es6'
}
}
}

View File

@ -1,33 +0,0 @@
<?php
$verb = strtolower($_SERVER['REQUEST_METHOD']);
// Send request method if nothing else is specified
if (empty($_GET))
{
echo $verb;
}
else if (isset($_GET['data']))
{
switch($verb)
{
case "get":
$var =& $_GET;
break;
case "post":
$var =& $_POST;
break;
default:
parse_str(file_get_contents('php://input'), $var);
break;
}
header('Content-type: application/json');
echo json_encode($var);
}
else if (isset($_GET['bad']))
{
http_response_code('401');
}

View File

@ -1,44 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hummingbird AnimeClient Front-end Testsuite</title>
<link rel="stylesheet" href='lib/mocha.css'>
</head>
<body>
<section id="parentTest">
<article class="child">
<div class="grandChild"></div>
</article>
</section>
<div id="mocha">
<ul id="mocha-stats">
<li class="progress"></li>
<li class="passes"></li>
<li class="failures"></li>
<li class="duration"></li>
</ul>
<ul id="mocha-report"></ul>
</div>
<script src='../js/base/class-list.js'></script>
<script src='lib/testBundle.js'></script>
<script>
var expect = chai.expect;
mocha.setup('tdd');
</script>
<!-- include source files here... -->
<script src='../js/anime-client.js'></script>
<!-- include test files here... -->
<script src='tests/AnimeClient.js'></script>
<script src='tests/ajax.js'></script>
<script>
mocha.checkLeaks();
mocha.globals(['AnimeClient']);
mocha.run();
</script>
</body>
</html>

View File

@ -1,305 +0,0 @@
@charset "utf-8";
body {
margin:0;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 60px 50px;
}
#mocha ul,
#mocha li {
margin: 0;
padding: 0;
}
#mocha ul {
list-style: none;
}
#mocha h1,
#mocha h2 {
margin: 0;
}
#mocha h1 {
margin-top: 15px;
font-size: 1em;
font-weight: 200;
}
#mocha h1 a {
text-decoration: none;
color: inherit;
}
#mocha h1 a:hover {
text-decoration: underline;
}
#mocha .suite .suite h1 {
margin-top: 0;
font-size: .8em;
}
#mocha .hidden {
display: none;
}
#mocha h2 {
font-size: 12px;
font-weight: normal;
cursor: pointer;
}
#mocha .suite {
margin-left: 15px;
}
#mocha .test {
margin-left: 15px;
overflow: hidden;
}
#mocha .test.pending:hover h2::after {
content: '(pending)';
font-family: arial, sans-serif;
}
#mocha .test.pass.medium .duration {
background: #c09853;
}
#mocha .test.pass.slow .duration {
background: #b94a48;
}
#mocha .test.pass::before {
content: '✓';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #00d6b2;
}
#mocha .test.pass .duration {
font-size: 9px;
margin-left: 5px;
padding: 2px 5px;
color: #fff;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#mocha .test.pass.fast .duration {
display: none;
}
#mocha .test.pending {
color: #0b97c4;
}
#mocha .test.pending::before {
content: '◦';
color: #0b97c4;
}
#mocha .test.fail {
color: #c00;
}
#mocha .test.fail pre {
color: black;
}
#mocha .test.fail::before {
content: '✖';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #c00;
}
#mocha .test pre.error {
color: #c00;
max-height: 300px;
overflow: auto;
}
#mocha .test .html-error {
overflow: auto;
color: black;
line-height: 1.5;
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
max-width: 85%; /*(1)*/
max-width: calc(100% - 42px); /*(2)*/
max-height: 300px;
word-wrap: break-word;
border-bottom-color: #ddd;
-webkit-border-radius: 3px;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-border-radius: 3px;
-moz-box-shadow: 0 1px 3px #eee;
border-radius: 3px;
}
#mocha .test .html-error pre.error {
border: none;
-webkit-border-radius: none;
-webkit-box-shadow: none;
-moz-border-radius: none;
-moz-box-shadow: none;
padding: 0;
margin: 0;
margin-top: 18px;
max-height: none;
}
/**
* (1): approximate for browsers not supporting calc
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
* ^^ seriously
*/
#mocha .test pre {
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
max-width: 85%; /*(1)*/
max-width: calc(100% - 42px); /*(2)*/
word-wrap: break-word;
border-bottom-color: #ddd;
-webkit-border-radius: 3px;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-border-radius: 3px;
-moz-box-shadow: 0 1px 3px #eee;
border-radius: 3px;
}
#mocha .test h2 {
position: relative;
}
#mocha .test a.replay {
position: absolute;
top: 3px;
right: 0;
text-decoration: none;
vertical-align: middle;
display: block;
width: 15px;
height: 15px;
line-height: 15px;
text-align: center;
background: #eee;
font-size: 15px;
-moz-border-radius: 15px;
border-radius: 15px;
-webkit-transition: opacity 200ms;
-moz-transition: opacity 200ms;
transition: opacity 200ms;
opacity: 0.3;
color: #888;
}
#mocha .test:hover a.replay {
opacity: 1;
}
#mocha-report.pass .test.fail {
display: none;
}
#mocha-report.fail .test.pass {
display: none;
}
#mocha-report.pending .test.pass,
#mocha-report.pending .test.fail {
display: none;
}
#mocha-report.pending .test.pass.pending {
display: block;
}
#mocha-error {
color: #c00;
font-size: 1.5em;
font-weight: 100;
letter-spacing: 1px;
}
#mocha-stats {
position: fixed;
top: 15px;
right: 10px;
font-size: 12px;
margin: 0;
color: #888;
z-index: 1;
}
#mocha-stats .progress {
float: right;
padding-top: 0;
}
#mocha-stats em {
color: black;
}
#mocha-stats a {
text-decoration: none;
color: inherit;
}
#mocha-stats a:hover {
border-bottom: 1px solid #eee;
}
#mocha-stats li {
display: inline-block;
margin: 0 5px;
list-style: none;
padding-top: 11px;
}
#mocha-stats canvas {
width: 40px;
height: 40px;
}
#mocha code .comment { color: #ddd; }
#mocha code .init { color: #2f6fad; }
#mocha code .string { color: #5890ad; }
#mocha code .keyword { color: #8a6343; }
#mocha code .number { color: #2f6fad; }
@media screen and (max-device-width: 480px) {
#mocha {
margin: 60px 0px;
}
#mocha #stats {
position: absolute;
}
}

View File

@ -1,312 +0,0 @@
(function e$$0(m,g,c){function l(e,a){if(!g[e]){if(!m[e]){var h="function"==typeof require&&require;if(!a&&h)return h(e,!0);if(b)return b(e,!0);h=Error("Cannot find module '"+e+"'");throw h.code="MODULE_NOT_FOUND",h;}h=g[e]={exports:{}};m[e][0].call(h.exports,function(a){var b=m[e][1][a];return l(b?b:a)},h,h.exports,e$$0,m,g,c)}return g[e].exports}for(var b="function"==typeof require&&require,e=0;e<c.length;e++)l(c[e]);return l})({1:[function(f,m,g){g=f("_process");m.exports=g.env.COV?f("./lib-cov/mocha"):
f("./lib/mocha")},{"./lib-cov/mocha":void 0,"./lib/mocha":14,_process:51}],2:[function(f,m,g){m.exports=function(c){return function(){}}},{}],3:[function(f,m,g){function c(e){return"[object Array]"===b.call(e)}function l(){}g.EventEmitter=l;var b=Object.prototype.toString;l.prototype.on=function(b,k){this.$events||(this.$events={});this.$events[b]?c(this.$events[b])?this.$events[b].push(k):this.$events[b]=[this.$events[b],k]:this.$events[b]=k;return this};l.prototype.addListener=l.prototype.on;l.prototype.once=
function(b,k){function a(){h.removeListener(b,a);k.apply(this,arguments)}var h=this;a.listener=k;this.on(b,a);return this};l.prototype.removeListener=function(b,k){if(this.$events&&this.$events[b]){var a=this.$events[b];if(c(a)){for(var h=-1,d=0,l=a.length;d<l;d++)if(a[d]===k||a[d].listener&&a[d].listener===k){h=d;break}if(0>h)return this;a.splice(h,1);a.length||delete this.$events[b]}else(a===k||a.listener&&a.listener===k)&&delete this.$events[b]}return this};l.prototype.removeAllListeners=function(b){if(void 0===
b)return this.$events={},this;this.$events&&this.$events[b]&&(this.$events[b]=null);return this};l.prototype.listeners=function(b){this.$events||(this.$events={});this.$events[b]||(this.$events[b]=[]);c(this.$events[b])||(this.$events[b]=[this.$events[b]]);return this.$events[b]};l.prototype.emit=function(b){if(!this.$events)return!1;var k=this.$events[b];if(!k)return!1;var a=Array.prototype.slice.call(arguments,1);if("function"===typeof k)k.apply(this,a);else if(c(k))for(var k=k.slice(),h=0,d=k.length;h<
d;h++)k[h].apply(this,a);else return!1;return!0}},{}],4:[function(f,m,g){function c(){this.percent=0;this.size(0);this.fontSize(11);this.font("helvetica, arial, sans-serif")}m.exports=c;c.prototype.size=function(c){this._size=c;return this};c.prototype.text=function(c){this._text=c;return this};c.prototype.fontSize=function(c){this._fontSize=c;return this};c.prototype.font=function(c){this._font=c;return this};c.prototype.update=function(c){this.percent=c;return this};c.prototype.draw=function(c){try{var b=
Math.min(this.percent,100),e=this._size,k=e/2,a=k-1,h=this._fontSize;c.font=h+"px "+this._font;var d=b/100*Math.PI*2;c.clearRect(0,0,e,e);c.strokeStyle="#9f9f9f";c.beginPath();c.arc(k,k,a,0,d,!1);c.stroke();c.strokeStyle="#eee";c.beginPath();c.arc(k,k,a-1,0,d,!0);c.stroke();var t=this._text||(b|0)+"%",f=c.measureText(t).width;c.fillText(t,k-f/2+1,k+h/2-1)}catch(g){}return this}},{}],5:[function(f,m,g){(function(c){g.isatty=function(){return!0};g.getWindowSize=function(){return"innerHeight"in c?[c.innerHeight,
c.innerWidth]:[640,480]}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{}],6:[function(f,m,g){function c(){}m.exports=c;c.prototype.runnable=function(c){if(!arguments.length)return this._runnable;this.test=this._runnable=c;return this};c.prototype.timeout=function(c){if(!arguments.length)return this.runnable().timeout();this.runnable().timeout(c);return this};c.prototype.enableTimeouts=function(c){this.runnable().enableTimeouts(c);
return this};c.prototype.slow=function(c){this.runnable().slow(c);return this};c.prototype.skip=function(){this.runnable().skip();return this};c.prototype.retries=function(c){if(!arguments.length)return this.runnable().retries();this.runnable().retries(c);return this};c.prototype.inspect=function(){return JSON.stringify(this,function(c,b){return"runnable"===c||"test"===c?void 0:b},2)}},{}],7:[function(f,m,g){function c(b,c){l.call(this,b,c);this.type="hook"}var l=f("./runnable");f=f("./utils").inherits;
m.exports=c;f(c,l);c.prototype.error=function(b){if(!arguments.length)return b=this._error,this._error=null,b;this._error=b}},{"./runnable":35,"./utils":39}],8:[function(f,m,g){var c=f("../suite"),l=f("../test"),b=f("escape-string-regexp");m.exports=function(e){var k=[e];e.on("pre-require",function(a,h,d){var t=f("./common")(k,a);a.before=t.before;a.after=t.after;a.beforeEach=t.beforeEach;a.afterEach=t.afterEach;a.run=d.options.delay&&t.runWithSuite(e);a.describe=a.context=function(a,d){var b=c.create(k[0],
a);b.file=h;k.unshift(b);d.call(b);k.shift();return b};a.xdescribe=a.xcontext=a.describe.skip=function(a,d){var b=c.create(k[0],a);b.pending=!0;k.unshift(b);d.call(b);k.shift()};a.describe.only=function(b,h){var c=a.describe(b,h);d.grep(c.fullTitle());return c};var g=a.it=a.specify=function(a,d){var b=k[0];b.pending&&(d=null);var c=new l(a,d);c.file=h;b.addTest(c);return c};a.it.only=function(a,h){var c=g(a,h),e="^"+b(c.fullTitle())+"$";d.grep(new RegExp(e));return c};a.xit=a.xspecify=a.it.skip=function(d){a.it(d)};
a.it.retries=function(d){a.retries(d)}})}},{"../suite":37,"../test":38,"./common":9,"escape-string-regexp":68}],9:[function(f,m,g){m.exports=function(c,l){return{runWithSuite:function(b){return function(){b.run()}},before:function(b,e){c[0].beforeAll(b,e)},after:function(b,e){c[0].afterAll(b,e)},beforeEach:function(b,e){c[0].beforeEach(b,e)},afterEach:function(b,e){c[0].afterEach(b,e)},test:{skip:function(b){l.test(b)},retries:function(b){l.retries(b)}}}}},{}],10:[function(f,m,g){var c=f("../suite"),
l=f("../test");m.exports=function(b){function e(a,b){var d,t;for(t in a)if("function"===typeof a[t])switch(d=a[t],t){case "before":k[0].beforeAll(d);break;case "after":k[0].afterAll(d);break;case "beforeEach":k[0].beforeEach(d);break;case "afterEach":k[0].afterEach(d);break;default:d=new l(t,d),d.file=b,k[0].addTest(d)}else d=c.create(k[0],t),k.unshift(d),e(a[t],b),k.shift()}var k=[b];b.on("require",e)}},{"../suite":37,"../test":38}],11:[function(f,m,g){g.bdd=f("./bdd");g.tdd=f("./tdd");g.qunit=f("./qunit");
g.exports=f("./exports")},{"./bdd":8,"./exports":10,"./qunit":12,"./tdd":13}],12:[function(f,m,g){var c=f("../suite"),l=f("../test"),b=f("escape-string-regexp");m.exports=function(e){var k=[e];e.on("pre-require",function(a,h,d){var t=f("./common")(k,a);a.before=t.before;a.after=t.after;a.beforeEach=t.beforeEach;a.afterEach=t.afterEach;a.run=d.options.delay&&t.runWithSuite(e);a.suite=function(a){1<k.length&&k.shift();a=c.create(k[0],a);a.file=h;k.unshift(a);return a};a.suite.only=function(b,h){var c=
a.suite(b,h);d.grep(c.fullTitle())};a.test=function(a,d){var b=new l(a,d);b.file=h;k[0].addTest(b);return b};a.test.only=function(h,c){var e=a.test(h,c),e="^"+b(e.fullTitle())+"$";d.grep(new RegExp(e))};a.test.skip=t.test.skip;a.test.retries=t.test.retries})}},{"../suite":37,"../test":38,"./common":9,"escape-string-regexp":68}],13:[function(f,m,g){var c=f("../suite"),l=f("../test"),b=f("escape-string-regexp");m.exports=function(e){var k=[e];e.on("pre-require",function(a,h,d){var t=f("./common")(k,
a);a.setup=t.beforeEach;a.teardown=t.afterEach;a.suiteSetup=t.before;a.suiteTeardown=t.after;a.run=d.options.delay&&t.runWithSuite(e);a.suite=function(a,b){var d=c.create(k[0],a);d.file=h;k.unshift(d);b.call(d);k.shift();return d};a.suite.skip=function(a,d){var b=c.create(k[0],a);b.pending=!0;k.unshift(b);d.call(b);k.shift()};a.suite.only=function(b,h){var c=a.suite(b,h);d.grep(c.fullTitle())};a.test=function(a,b){var d=k[0];d.pending&&(b=null);var c=new l(a,b);c.file=h;d.addTest(c);return c};a.test.only=
function(c,h){var e=a.test(c,h),e="^"+b(e.fullTitle())+"$";d.grep(new RegExp(e))};a.test.skip=t.test.skip;a.test.retries=t.test.retries})}},{"../suite":37,"../test":38,"./common":9,"escape-string-regexp":68}],14:[function(f,m,g){(function(c,l,b){function e(a){a=a||{};this.files=[];this.options=a;a.grep&&this.grep(new RegExp(a.grep));a.fgrep&&this.grep(a.fgrep);this.suite=new g.Suite("",new g.Context);this.ui(a.ui);this.bail(a.bail);this.reporter(a.reporter,a.reporterOptions);"undefined"!==typeof a.timeout&&
null!==a.timeout&&this.timeout(a.timeout);"undefined"!==typeof a.retries&&null!==a.retries&&this.retries(a.retries);this.useColors(a.useColors);null!==a.enableTimeouts&&this.enableTimeouts(a.enableTimeouts);a.slow&&this.slow(a.slow);this.suite.on("pre-require",function(a){g.afterEach=a.afterEach||a.teardown;g.after=a.after||a.suiteTeardown;g.beforeEach=a.beforeEach||a.setup;g.before=a.before||a.suiteSetup;g.describe=a.describe||a.suite;g.it=a.it||a.test;g.setup=a.setup||a.beforeEach;g.suiteSetup=
a.suiteSetup||a.before;g.suiteTeardown=a.suiteTeardown||a.after;g.suite=a.suite||a.describe;g.teardown=a.teardown||a.afterEach;g.test=a.test||a.it;g.run=a.run})}var k=f("escape-string-regexp"),a=f("path"),h=f("./reporters"),d=f("./utils");g=m.exports=e;c.browser||(c=c.cwd(),m.paths.push(c,a.join(c,"node_modules")));g.utils=d;g.interfaces=f("./interfaces");g.reporters=h;g.Runnable=f("./runnable");g.Context=f("./context");g.Runner=f("./runner");g.Suite=f("./suite");g.Hook=f("./hook");g.Test=f("./test");
e.prototype.bail=function(a){arguments.length||(a=!0);this.suite.bail(a);return this};e.prototype.addFile=function(a){this.files.push(a);return this};e.prototype.reporter=function(a,b){if("function"===typeof a)this._reporter=a;else{a=a||"spec";var d;h[a]&&(d=h[a]);if(!d)try{d=f(a)}catch(c){-1!==c.message.indexOf("Cannot find module")?console.warn('"'+a+'" reporter not found'):console.warn('"'+a+'" reporter blew up with error:\n'+c.stack)}d||"teamcity"!==a||console.warn("The Teamcity reporter was moved to a package named mocha-teamcity-reporter (https://npmjs.org/package/mocha-teamcity-reporter).");
if(!d)throw Error('invalid reporter "'+a+'"');this._reporter=d}this.options.reporterOptions=b;return this};e.prototype.ui=function(a){a=a||"bdd";this._ui=g.interfaces[a];if(!this._ui)try{this._ui=f(a)}catch(d){throw Error('invalid interface "'+a+'"');}this._ui=this._ui(this.suite);return this};e.prototype.loadFiles=function(d){var b=this,c=this.suite;this.files.forEach(function(d){d=a.resolve(d);c.emit("pre-require",l,d,b);c.emit("require",f(d),d,b);c.emit("post-require",l,d,b)});d&&d()};e.prototype._growl=
function(d,c){var h=f("growl");d.on("end",function(){var e=c.stats;e.failures?h(e.failures+" of "+d.total+" tests failed",{name:"mocha",title:"Failed",image:a.join(b,"../images","error.png")}):h(e.passes+" tests passed in "+e.duration+"ms",{name:"mocha",title:"Passed",image:a.join(b,"../images","ok.png")})})};e.prototype.grep=function(a){this.options.grep="string"===typeof a?new RegExp(k(a)):a;return this};e.prototype.invert=function(){this.options.invert=!0;return this};e.prototype.ignoreLeaks=function(a){this.options.ignoreLeaks=
!!a;return this};e.prototype.checkLeaks=function(){this.options.ignoreLeaks=!1;return this};e.prototype.fullTrace=function(){this.options.fullStackTrace=!0;return this};e.prototype.growl=function(){this.options.growl=!0;return this};e.prototype.globals=function(a){this.options.globals=(this.options.globals||[]).concat(a);return this};e.prototype.useColors=function(a){void 0!==a&&(this.options.useColors=a);return this};e.prototype.useInlineDiffs=function(a){this.options.useInlineDiffs=void 0!==a&&
a;return this};e.prototype.timeout=function(a){this.suite.timeout(a);return this};e.prototype.retries=function(a){this.suite.retries(a);return this};e.prototype.slow=function(a){this.suite.slow(a);return this};e.prototype.enableTimeouts=function(a){this.suite.enableTimeouts(arguments.length&&void 0!==a?a:!0);return this};e.prototype.asyncOnly=function(){this.options.asyncOnly=!0;return this};e.prototype.noHighlighting=function(){this.options.noHighlighting=!0;return this};e.prototype.allowUncaught=
function(){this.options.allowUncaught=!0;return this};e.prototype.delay=function(){this.options.delay=!0;return this};e.prototype.run=function(a){this.files.length&&this.loadFiles();var d=this.suite,b=this.options;b.files=this.files;var d=new g.Runner(d,b.delay),c=new this._reporter(d,b);d.ignoreLeaks=!1!==b.ignoreLeaks;d.fullStackTrace=b.fullStackTrace;d.asyncOnly=b.asyncOnly;d.allowUncaught=b.allowUncaught;b.grep&&d.grep(b.grep,b.invert);b.globals&&d.globals(b.globals);b.growl&&this._growl(d,c);
void 0!==b.useColors&&(g.reporters.Base.useColors=b.useColors);g.reporters.Base.inlineDiffs=b.useInlineDiffs;return d.run(function(d){c.done?c.done(d,a):a&&a(d)})}}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{},"/lib")},{"./context":6,"./hook":7,"./interfaces":11,"./reporters":22,"./runnable":35,"./runner":36,"./suite":37,"./test":38,"./utils":39,_process:51,"escape-string-regexp":68,growl:69,path:41}],15:[function(f,
m,g){function c(b){if(b=/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(b)){var c=parseFloat(b[1]);switch((b[2]||"ms").toLowerCase()){case "years":case "year":case "y":return 315576E5*c;case "days":case "day":case "d":return 864E5*c;case "hours":case "hour":case "h":return 36E5*c;case "minutes":case "minute":case "m":return 6E4*c;case "seconds":case "second":case "s":return 1E3*c;case "ms":return c}}}function l(b,c,k){if(!(b<c))return b<1.5*c?Math.floor(b/c)+" "+
k:Math.ceil(b/c)+" "+k+"s"}m.exports=function(b,e){e=e||{};return"string"===typeof b?c(b):e["long"]?l(b,864E5,"day")||l(b,36E5,"hour")||l(b,6E4,"minute")||l(b,1E3,"second")||b+" ms":864E5<=b?Math.round(b/864E5)+"d":36E5<=b?Math.round(b/36E5)+"h":6E4<=b?Math.round(b/6E4)+"m":1E3<=b?Math.round(b/1E3)+"s":b+"ms"}},{}],16:[function(f,m,g){m.exports=function(c){this.message=c}},{}],17:[function(f,m,g){(function(c,l){function b(a){var d=this.stats={suites:0,tests:0,passes:0,pending:0,failures:0},b=this.failures=
[];a&&(this.runner=a,a.stats=d,a.on("start",function(){d.start=new D}),a.on("suite",function(a){d.suites=d.suites||0;a.root||d.suites++}),a.on("test end",function(){d.tests=d.tests||0;d.tests++}),a.on("pass",function(a){d.passes=d.passes||0;a.duration>a.slow()?a.speed="slow":a.duration>a.slow()/2?a.speed="medium":a.speed="fast";d.passes++}),a.on("fail",function(a,c){d.failures=d.failures||0;d.failures++;a.err=c;b.push(a)}),a.on("end",function(){d.end=new D;d.duration=new D-d.start}),a.on("pending",
function(){d.pending++}))}function e(d,b){var c=a(d,"WordsWithSpace",b),h=c.split("\n");if(4<h.length)var n=String(h.length).length,c=h.map(function(a,d){var b=++d,b=String(b);return Array(n-b.length+1).join(" ")+b+" | "+a}).join("\n");c="\n"+w("diff removed","actual")+" "+w("diff added","expected")+"\n\n"+c+"\n";return c=c.replace(/^/gm," ")}function k(a,b){var c=r.createPatch("string",a.actual,a.expected).split("\n").splice(4);return"\n "+d("diff added","+ expected")+" "+d("diff removed",
"- actual")+"\n\n"+c.map(function(a){b&&(a=h(a));return"+"===a[0]?" "+d("diff added",a):"-"===a[0]?" "+d("diff removed",a):a.match(/\@\@/)||a.match(/\\ No newline/)?null:" "+a}).filter(function(a){return"undefined"!==typeof a&&null!==a}).join("\n")}function a(a,b,c){var e=c?h(a.actual):a.actual;a=c?h(a.expected):a.expected;return r["diff"+b](e,a).map(function(a){return a.added?d("diff added",a.value):a.removed?d("diff removed",a.value):a.value}).join("")}function h(a){return a.replace(/\t/g,
"<tab>").replace(/\r/g,"<CR>").replace(/\n/g,"<LF>\n")}function d(a,d){return d.split("\n").map(function(d){return w(a,d)}).join("\n")}var t=f("tty"),r=f("diff"),q=f("../ms"),x=f("../utils"),u=c.browser?null:f("supports-color");g=m.exports=b;var D=l.Date,E=t.isatty(1)&&t.isatty(2);g.useColors=!c.browser&&(u||void 0!==c.env.MOCHA_COLORS);g.inlineDiffs=!1;g.colors={pass:90,fail:31,"bright pass":92,"bright fail":91,"bright yellow":93,pending:36,suite:0,"error title":0,"error message":31,"error stack":90,
checkmark:32,fast:90,medium:33,slow:31,green:32,light:90,"diff gutter":90,"diff added":32,"diff removed":31};g.symbols={ok:"\u2713",err:"\u2716",dot:"\u2024"};"win32"===c.platform&&(g.symbols.ok="\u221a",g.symbols.err="\u00d7",g.symbols.dot=".");var w=g.color=function(a,d){return g.useColors?"\u001b["+g.colors[a]+"m"+d+"\u001b[0m":String(d)};g.window={width:75};E&&(g.window.width=c.stdout.getWindowSize?c.stdout.getWindowSize(1)[0]:t.getWindowSize()[1]);g.cursor={hide:function(){E&&c.stdout.write("\u001b[?25l")},
show:function(){E&&c.stdout.write("\u001b[?25h")},deleteLine:function(){E&&c.stdout.write("\u001b[2K")},beginningOfLine:function(){E&&c.stdout.write("\u001b[0G")},CR:function(){E?(g.cursor.deleteLine(),g.cursor.beginningOfLine()):c.stdout.write("\r")}};g.list=function(a){console.log();a.forEach(function(a,d){var b=w("error title"," %s) %s:\n")+w("error message"," %s")+w("error stack","\n%s\n"),c,h=a.err,l;l=h.message?h.message:"function"===typeof h.inspect?h.inspect()+"":"";var f=h.stack||l,
t=f.indexOf(l),r=h.actual,C=h.expected,H=!0;-1===t?c=l:(t+=l.length,c=f.slice(0,t),f=f.slice(t+1));h.uncaught&&(c="Uncaught "+c);if(H=!1!==h.showDiff)H=A.call(r)===A.call(C);H&&void 0!==C&&(H=!1,x.isString(r)&&x.isString(C)||(h.actual=x.stringify(r),h.expected=x.stringify(C)),b=w("error title"," %s) %s:\n%s")+w("error stack","\n%s\n"),l=l.match(/^([^:]+): expected/),c="\n "+w("error message",l?l[1]:c),c=g.inlineDiffs?c+e(h,H):c+k(h,H));f=f.replace(/^/gm," ");console.log(b,d+1,a.fullTitle(),
c,f)})};b.prototype.epilogue=function(){var a=this.stats,d;console.log();d=w("bright pass"," ")+w("green"," %d passing")+w("light"," (%s)");console.log(d,a.passes||0,q(a.duration));a.pending&&(d=w("pending"," ")+w("pending"," %d pending"),console.log(d,a.pending));a.failures&&(d=w("fail"," %d failing"),console.log(d,a.failures),b.list(this.failures),console.log());console.log()};var A=Object.prototype.toString}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?
self:"undefined"!==typeof window?window:{})},{"../ms":15,"../utils":39,_process:51,diff:67,"supports-color":41,tty:5}],18:[function(f,m,g){var c=f("./base"),l=f("../utils");m.exports=function(b){function e(){return Array(k).join(" ")}c.call(this,b);var k=2;b.on("suite",function(a){a.root||(++k,console.log('%s<section class="suite">',e()),++k,console.log("%s<h1>%s</h1>",e(),l.escape(a.title)),console.log("%s<dl>",e()))});b.on("suite end",function(a){a.root||(console.log("%s</dl>",e()),--k,console.log("%s</section>",
e()),--k)});b.on("pass",function(a){console.log("%s <dt>%s</dt>",e(),l.escape(a.title));a=l.escape(l.clean(a.body));console.log("%s <dd><pre><code>%s</code></pre></dd>",e(),a)});b.on("fail",function(a,b){console.log('%s <dt class="error">%s</dt>',e(),l.escape(a.title));var d=l.escape(l.clean(a.fn.body));console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>',e(),d);console.log('%s <dd class="error">%s</dd>',e(),l.escape(b))})}},{"../utils":39,"./base":17}],19:[function(f,m,g){(function(c){function l(a){b.call(this,
a);var h=this,d=.75*b.window.width|0,e=-1;a.on("start",function(){c.stdout.write("\n")});a.on("pending",function(){0===++e%d&&c.stdout.write("\n ");c.stdout.write(k("pending",b.symbols.dot))});a.on("pass",function(a){0===++e%d&&c.stdout.write("\n ");"slow"===a.speed?c.stdout.write(k("bright yellow",b.symbols.dot)):c.stdout.write(k(a.speed,b.symbols.dot))});a.on("fail",function(){0===++e%d&&c.stdout.write("\n ");c.stdout.write(k("fail",b.symbols.dot))});a.on("end",function(){console.log();h.epilogue()})}
var b=f("./base"),e=f("../utils").inherits,k=b.color;m.exports=l;e(l,b)}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],20:[function(f,m,g){(function(c,l){function b(a){return 75<=a?"high":50<=a?"medium":25<=a?"low":"terrible"}var e=f("./json-cov"),k=f("fs").readFileSync,a=f("path").join;m.exports=function(h){var d=f("jade"),g=a(l,"/templates/coverage.jade"),r=k(g,"utf8"),m=d.compile(r,{filename:g}),x=this;e.call(this,h,!1);h.on("end",function(){c.stdout.write(m({cov:x.cov,coverageClass:b}))})}}).call(this,
f("_process"),"/lib/reporters")},{"./json-cov":23,_process:51,fs:41,jade:41,path:41}],21:[function(f,m,g){(function(c){function l(b){g.call(this,b);var c=this,l=this.stats,f=e(E),y=f.getElementsByTagName("li"),z=y[1].getElementsByTagName("em")[0],n=y[1].getElementsByTagName("a")[0],p=y[2].getElementsByTagName("em")[0],G=y[2].getElementsByTagName("a")[0],J=y[3].getElementsByTagName("em")[0],y=f.getElementsByTagName("canvas")[0],v=e('<ul id="mocha-report"></ul>'),m=[v],C,H,I=document.getElementById("mocha");
if(y.getContext){var L=window.devicePixelRatio||1;y.style.width=y.width;y.style.height=y.height;y.width*=L;y.height*=L;H=y.getContext("2d");H.scale(L,L);C=new q}I?(d(n,"click",function(){a();var d=/pass/.test(v.className)?"":" pass";v.className=v.className.replace(/fail|pass/g,"")+d;v.className.trim()&&k("test pass")}),d(G,"click",function(){a();var d=/fail/.test(v.className)?"":" fail";v.className=v.className.replace(/fail|pass/g,"")+d;v.className.trim()&&k("test fail")}),I.appendChild(f),I.appendChild(v),
C&&C.size(40),b.on("suite",function(a){if(!a.root){var d=c.suiteURL(a);a=e('<li class="suite"><h1><a href="%s">%s</a></h1></li>',d,u(a.title));m[0].appendChild(a);m.unshift(document.createElement("ul"));a.appendChild(m[0])}}),b.on("suite end",function(a){a.root||m.shift()}),b.on("fail",function(a){"hook"!==a.type&&"test"!==a.type||b.emit("test end",a)}),b.on("test end",function(a){var b=l.tests/this.total*100|0;C&&C.update(b).draw(H);b=new D-l.start;h(z,l.passes);h(p,l.failures);h(J,(b/1E3).toFixed(2));
if("passed"===a.state)b=c.testURL(a),b=e('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="%s" class="replay">\u2023</a></h2></li>',a.speed,a.title,a.duration,b);else if(a.pending)b=e('<li class="test pass pending"><h2>%e</h2></li>',a.title);else{var b=e('<li class="test fail"><h2>%e <a href="%e" class="replay">\u2023</a></h2></li>',a.title,c.testURL(a)),n,k=a.err.toString();"[object Error]"===k&&(k=a.err.message);a.err.stack?(n=a.err.stack.indexOf(a.err.message),n=-1===
n?a.err.stack:a.err.stack.substr(a.err.message.length+n)):a.err.sourceURL&&void 0!==a.err.line&&(n="\n("+a.err.sourceURL+":"+a.err.line+")");n=n||"";a.err.htmlMessage&&n?b.appendChild(e('<div class="html-error">%s\n<pre class="error">%e</pre></div>',a.err.htmlMessage,n)):a.err.htmlMessage?b.appendChild(e('<div class="html-error">%s</div>',a.err.htmlMessage)):b.appendChild(e('<pre class="error">%e%e</pre>',k,n))}if(!a.pending){n=b.getElementsByTagName("h2")[0];d(n,"click",function(){I.style.display=
"none"===I.style.display?"block":"none"});var I=e("<pre><code>%e</code></pre>",r.clean(a.body));b.appendChild(I);I.style.display="none"}m[0]&&m[0].appendChild(b)})):document.body.appendChild(e('<div id="mocha-error">%s</div>',"#mocha div missing, add it to your document"))}function b(a){var d=window.location.search;d&&(d=d.replace(/[?&]grep=[^&\s]*/g,"").replace(/^&/,"?"));return window.location.pathname+(d?d+"&":"?")+"grep="+encodeURIComponent(x(a))}function e(a){var d=arguments,b=document.createElement("div"),
c=1;b.innerHTML=a.replace(/%([se])/g,function(a,b){switch(b){case "s":return String(d[c++]);case "e":return u(d[c++])}});return b.firstChild}function k(a){for(var d=document.getElementsByClassName("suite"),b=0;b<d.length;b++)d[b].getElementsByClassName(a).length||(d[b].className+=" hidden")}function a(){for(var a=document.getElementsByClassName("suite hidden"),d=0;d<a.length;++d)a[d].className=a[d].className.replace("suite hidden","suite")}function h(a,d){a.textContent?a.textContent=d:a.innerText=
d}function d(a,d,b){a.addEventListener?a.addEventListener(d,b,!1):a.attachEvent("on"+d,b)}var g=f("./base"),r=f("../utils"),q=f("../browser/progress"),x=f("escape-string-regexp"),u=r.escape,D=c.Date;m.exports=l;var E='<ul id="mocha-stats"><li class="progress"><canvas width="40" height="40"></canvas></li><li class="passes"><a href="javascript:void(0);">passes:</a> <em>0</em></li><li class="failures"><a href="javascript:void(0);">failures:</a> <em>0</em></li><li class="duration">duration: <em>0</em>s</li></ul>';
l.prototype.suiteURL=function(a){return b(a.fullTitle())};l.prototype.testURL=function(a){return b(a.fullTitle())}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"../browser/progress":4,"../utils":39,"./base":17,"escape-string-regexp":68}],22:[function(f,m,g){g.Base=g.base=f("./base");g.Dot=g.dot=f("./dot");g.Doc=g.doc=f("./doc");g.TAP=g.tap=f("./tap");g.JSON=g.json=f("./json");g.HTML=g.html=f("./html");g.List=g.list=f("./list");
g.Min=g.min=f("./min");g.Spec=g.spec=f("./spec");g.Nyan=g.nyan=f("./nyan");g.XUnit=g.xunit=f("./xunit");g.Markdown=g.markdown=f("./markdown");g.Progress=g.progress=f("./progress");g.Landing=g.landing=f("./landing");g.JSONCov=g["json-cov"]=f("./json-cov");g.HTMLCov=g["html-cov"]=f("./html-cov");g.JSONStream=g["json-stream"]=f("./json-stream")},{"./base":17,"./doc":18,"./dot":19,"./html":21,"./html-cov":20,"./json":25,"./json-cov":23,"./json-stream":24,"./landing":26,"./list":27,"./markdown":28,"./min":29,
"./nyan":30,"./progress":31,"./spec":32,"./tap":33,"./xunit":34}],23:[function(f,m,g){(function(c,l){function b(a){var d={instrumentation:"node-jscoverage",sloc:0,hits:0,misses:0,coverage:0,files:[]},b;for(b in a)if(Object.prototype.hasOwnProperty.call(a,b)){var c=e(b,a[b]);d.files.push(c);d.hits+=c.hits;d.misses+=c.misses;d.sloc+=c.sloc}d.files.sort(function(a,d){return a.filename.localeCompare(d.filename)});0<d.sloc&&(d.coverage=d.hits/d.sloc*100);return d}function e(a,d){var b={filename:a,coverage:0,
hits:0,misses:0,sloc:0,source:{}};d.source.forEach(function(a,c){c++;0===d[c]?(b.misses++,b.sloc++):void 0!==d[c]&&(b.hits++,b.sloc++);b.source[c]={source:a,coverage:void 0===d[c]?"":d[c]}});b.coverage=b.hits/b.sloc*100;return b}function k(a){return{duration:a.duration,currentRetry:a.currentRetry(),fullTitle:a.fullTitle(),title:a.title}}var a=f("./base");m.exports=function(h,d){a.call(this,h);d=1===arguments.length||d;var e=this,f=[],g=[],m=[];h.on("test end",function(a){f.push(a)});h.on("pass",function(a){m.push(a)});
h.on("fail",function(a){g.push(a)});h.on("end",function(){var a=e.cov=b(l._$jscoverage||{});a.stats=e.stats;a.tests=f.map(k);a.failures=g.map(k);a.passes=m.map(k);d&&c.stdout.write(JSON.stringify(a,null,2))})}}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./base":17,_process:51}],24:[function(f,m,g){(function(c){function l(b){return{title:b.title,fullTitle:b.fullTitle(),duration:b.duration,currentRetry:b.currentRetry()}}
var b=f("./base");m.exports=function(e){b.call(this,e);var k=this,a=e.total;e.on("start",function(){console.log(JSON.stringify(["start",{total:a}]))});e.on("pass",function(a){console.log(JSON.stringify(["pass",l(a)]))});e.on("fail",function(a,d){a=l(a);a.err=d.message;a.stack=d.stack||null;console.log(JSON.stringify(["fail",a]))});e.on("end",function(){c.stdout.write(JSON.stringify(["end",k.stats]))})}}).call(this,f("_process"))},{"./base":17,_process:51}],25:[function(f,m,g){(function(c){function l(c){return{title:c.title,
fullTitle:c.fullTitle(),duration:c.duration,currentRetry:c.currentRetry(),err:b(c.err||{})}}function b(b){var a={};Object.getOwnPropertyNames(b).forEach(function(c){a[c]=b[c]},b);return a}var e=f("./base");m.exports=function(b){e.call(this,b);var a=this,h=[],d=[],f=[],g=[];b.on("test end",function(a){h.push(a)});b.on("pass",function(a){g.push(a)});b.on("fail",function(a){f.push(a)});b.on("pending",function(a){d.push(a)});b.on("end",function(){var e={stats:a.stats,tests:h.map(l),pending:d.map(l),failures:f.map(l),
passes:g.map(l)};b.testResults=e;c.stdout.write(JSON.stringify(e,null,2))})}}).call(this,f("_process"))},{"./base":17,_process:51}],26:[function(f,m,g){(function(c){function l(h){function d(){var b=Array(l).join("-");return" "+a("runway",b)}b.call(this,h);var e=this,l=.75*b.window.width|0,f=h.total,g=c.stdout,m=a("plane","\u2708"),D=-1,E=0;h.on("start",function(){g.write("\n\n\n ");k.hide()});h.on("test end",function(b){var c=-1===D?l*++E/f|0:D;"failed"===b.state&&(m=a("plane crash","\u2708"),D=
c);g.write("\u001b["+(l+1)+"D\u001b[2A");g.write(d());g.write("\n ");g.write(a("runway",Array(c).join("\u22c5")));g.write(m);g.write(a("runway",Array(l-c).join("\u22c5")+"\n"));g.write(d());g.write("\u001b[0m")});h.on("end",function(){k.show();console.log();e.epilogue()})}var b=f("./base"),e=f("../utils").inherits,k=b.cursor,a=b.color;m.exports=l;b.colors.plane=0;b.colors["plane crash"]=31;b.colors.runway=90;e(l,b)}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],27:[function(f,
m,g){(function(c){function l(h){b.call(this,h);var d=0;h.on("start",function(){console.log()});h.on("test",function(a){c.stdout.write(k("pass"," "+a.fullTitle()+": "))});h.on("pending",function(a){var b=k("checkmark"," -")+k("pending"," %s");console.log(b,a.fullTitle())});h.on("pass",function(d){var c=k("checkmark"," "+b.symbols.dot)+k("pass"," %s: ")+k(d.speed,"%dms");a.CR();console.log(c,d.fullTitle(),d.duration)});h.on("fail",function(b){a.CR();console.log(k("fail"," %d) %s"),++d,b.fullTitle())});
h.on("end",this.epilogue.bind(this))}var b=f("./base"),e=f("../utils").inherits,k=b.color,a=b.cursor;m.exports=l;e(l,b)}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],28:[function(f,m,g){(function(c){var l=f("./base"),b=f("../utils");m.exports=function(e){function k(a,b){var d=b,c="$"+a.title;b=b[c]=b[c]||{suite:a};a.suites.forEach(function(a){k(a,b)});return d}function a(d,c){++c;var h="",e,k;for(k in d)"suite"!==k&&("$"!==k&&(e=" - ["+k.substring(1)+"]",e+="(#"+b.slug(d[k].suite.fullTitle())+
")\n",h+=Array(c).join(" ")+e),h+=a(d[k],c));return h}function h(b){b=k(b,{});return a(b,0)}l.call(this,e);var d=0,f="";h(e.suite);e.on("suite",function(a){++d;var c=b.slug(a.fullTitle()),c=f+='<a name="'+c+'"></a>\n';a=a.title;a=Array(d).join("#")+" "+a;f=c+(a+"\n")});e.on("suite end",function(){--d});e.on("pass",function(a){var d=b.clean(a.body);f+=a.title+".\n";f+="\n```js\n";f+=d+"\n";f+="```\n\n"});e.on("end",function(){c.stdout.write("# TOC\n");c.stdout.write(h(e.suite));c.stdout.write(f)})}}).call(this,
f("_process"))},{"../utils":39,"./base":17,_process:51}],29:[function(f,m,g){(function(c){function l(e){b.call(this,e);e.on("start",function(){c.stdout.write("\u001b[2J");c.stdout.write("\u001b[1;3H")});e.on("end",this.epilogue.bind(this))}var b=f("./base"),e=f("../utils").inherits;m.exports=l;e(l,b)}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],30:[function(f,m,g){(function(c){function l(a){e.call(this,a);var c=this,d=.75*e.window.width|0,k=this.nyanCatWidth=11;this.colorIndex=
0;this.numberOfLines=4;this.rainbowColors=c.generateColors();this.scoreboardWidth=5;this.tick=0;this.trajectories=[[],[],[],[]];this.trajectoryWidthMax=d-k;a.on("start",function(){e.cursor.hide();c.draw()});a.on("pending",function(){c.draw()});a.on("pass",function(){c.draw()});a.on("fail",function(){c.draw()});a.on("end",function(){e.cursor.show();for(var a=0;a<c.numberOfLines;a++)b("\n");c.epilogue()})}function b(a){c.stdout.write(a)}var e=f("./base"),k=f("../utils").inherits;m.exports=l;k(l,e);
l.prototype.draw=function(){this.appendRainbow();this.drawScoreboard();this.drawRainbow();this.drawNyanCat();this.tick=!this.tick};l.prototype.drawScoreboard=function(){function a(a,c){b(" ");b(e.color(a,c));b("\n")}var c=this.stats;a("green",c.passes);a("fail",c.failures);a("pending",c.pending);b("\n");this.cursorUp(this.numberOfLines)};l.prototype.appendRainbow=function(){for(var a=this.rainbowify(this.tick?"_":"-"),b=0;b<this.numberOfLines;b++){var d=this.trajectories[b];d.length>=this.trajectoryWidthMax&&
d.shift();d.push(a)}};l.prototype.drawRainbow=function(){var a=this;this.trajectories.forEach(function(c){b("\u001b["+a.scoreboardWidth+"C");b(c.join(""));b("\n")});this.cursorUp(this.numberOfLines)};l.prototype.drawNyanCat=function(){var a="\u001b["+(this.scoreboardWidth+this.trajectories[0].length)+"C",c="";b(a);b("_,------,");b("\n");b(a);c=this.tick?" ":" ";b("_|"+c+"/\\_/\\ ");b("\n");b(a);c=this.tick?"_":"__";b((this.tick?"~":"^")+"|"+c+this.face()+" ");b("\n");b(a);c=this.tick?" ":" ";
b(c+'"" "" ');b("\n");this.cursorUp(this.numberOfLines)};l.prototype.face=function(){var a=this.stats;return a.failures?"( x .x)":a.pending?"( o .o)":a.passes?"( ^ .^)":"( - .-)"};l.prototype.cursorUp=function(a){b("\u001b["+a+"A")};l.prototype.cursorDown=function(a){b("\u001b["+a+"B")};l.prototype.generateColors=function(){for(var a=[],b=0;42>b;b++){var d=Math.floor(Math.PI/3),c=1/6*b;a.push(36*Math.floor(3*Math.sin(c)+3)+6*Math.floor(3*Math.sin(c+2*d)+3)+Math.floor(3*Math.sin(c+4*d)+3)+16)}return a};
l.prototype.rainbowify=function(a){if(!e.useColors)return a;var b=this.rainbowColors[this.colorIndex%this.rainbowColors.length];this.colorIndex+=1;return"\u001b[38;5;"+b+"m"+a+"\u001b[0m"}}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],31:[function(f,m,g){(function(c){function l(e,d){b.call(this,e);var f=this,l=.5*b.window.width|0,g=e.total,m=0,u=-1;d=d||{};d.open=d.open||"[";d.complete=d.complete||"\u25ac";d.incomplete=d.incomplete||b.symbols.dot;d.close=d.close||"]";d.verbose=
!1;e.on("start",function(){console.log();a.hide()});e.on("test end",function(){m++;var b=m/g*l|0,e=l-b;if(b!==u||d.verbose)u=b,a.CR(),c.stdout.write("\u001b[J"),c.stdout.write(k("progress"," "+d.open)),c.stdout.write(Array(b).join(d.complete)),c.stdout.write(Array(e).join(d.incomplete)),c.stdout.write(k("progress",d.close)),d.verbose&&c.stdout.write(k("progress"," "+m+" of "+g))});e.on("end",function(){a.show();console.log();f.epilogue()})}var b=f("./base"),e=f("../utils").inherits,k=b.color,a=b.cursor;
m.exports=l;b.colors.progress=90;e(l,b)}).call(this,f("_process"))},{"../utils":39,"./base":17,_process:51}],32:[function(f,m,g){function c(c){function a(){return Array(h).join(" ")}l.call(this,c);var h=0,d=0;c.on("start",function(){console.log()});c.on("suite",function(d){++h;console.log(b("suite","%s%s"),a(),d.title)});c.on("suite end",function(){--h;1===h&&console.log()});c.on("pending",function(d){var c=a()+b("pending"," - %s");console.log(c,d.title)});c.on("pass",function(d){var c;"fast"===
d.speed?(c=a()+b("checkmark"," "+l.symbols.ok)+b("pass"," %s"),e.CR(),console.log(c,d.title)):(c=a()+b("checkmark"," "+l.symbols.ok)+b("pass"," %s")+b(d.speed," (%dms)"),e.CR(),console.log(c,d.title,d.duration))});c.on("fail",function(c){e.CR();console.log(a()+b("fail"," %d) %s"),++d,c.title)});c.on("end",this.epilogue.bind(this))}var l=f("./base");f=f("../utils").inherits;var b=l.color,e=l.cursor;m.exports=c;f(c,l)},{"../utils":39,"./base":17}],33:[function(f,m,g){var c=f("./base");m.exports=
function(f){c.call(this,f);var b=1,e=0,k=0;f.on("start",function(){var a=f.grepTotal(f.suite);console.log("%d..%d",1,a)});f.on("test end",function(){++b});f.on("pending",function(a){console.log("ok %d %s # SKIP -",b,a.fullTitle().replace(/#/g,""))});f.on("pass",function(a){e++;console.log("ok %d %s",b,a.fullTitle().replace(/#/g,""))});f.on("fail",function(a,c){k++;console.log("not ok %d %s",b,a.fullTitle().replace(/#/g,""));c.stack&&console.log(c.stack.replace(/^/gm," "))});f.on("end",function(){console.log("# tests "+
(e+k));console.log("# pass "+e);console.log("# fail "+k)})}},{"./base":17}],34:[function(f,m,g){(function(c,l){function b(b,d){a.call(this,b);var c=this.stats,h=[],k=this;if(d.reporterOptions&&d.reporterOptions.output){if(!g.createWriteStream)throw Error("file output not supported in browser");q.sync(x.dirname(d.reporterOptions.output));k.fileStream=g.createWriteStream(d.reporterOptions.output)}b.on("pending",function(a){h.push(a)});b.on("pass",function(a){h.push(a)});b.on("fail",function(a){h.push(a)});
b.on("end",function(){k.write(e("testsuite",{name:"Mocha Tests",tests:c.tests,failures:c.failures,errors:c.failures,skipped:c.tests-c.failures-c.passes,timestamp:(new u).toUTCString(),time:c.duration/1E3||0},!1));h.forEach(function(a){k.test(a)});k.write("</testsuite>")})}function e(a,b,d,c){d=d?"/>":">";var e=[],h;for(h in b)Object.prototype.hasOwnProperty.call(b,h)&&e.push(h+'="'+r(b[h])+'"');b="<"+a+(e.length?" "+e.join(" "):"")+d;c&&(b+=c+"</"+a+d);return b}function k(a){return"<![CDATA["+r(a)+
"]]\x3e"}var a=f("./base"),h=f("../utils"),d=h.inherits,g=f("fs"),r=h.escape,q=f("mkdirp"),x=f("path"),u=l.Date;m.exports=b;d(b,a);b.prototype.done=function(a,b){this.fileStream?this.fileStream.end(function(){b(a)}):b(a)};b.prototype.write=function(a){this.fileStream?this.fileStream.write(a+"\n"):"object"===typeof c&&c.stdout?c.stdout.write(a+"\n"):console.log(a)};b.prototype.test=function(a){var b={classname:a.parent.fullTitle(),name:a.title,time:a.duration/1E3||0};"failed"===a.state?(a=a.err,this.write(e("testcase",
b,!1,e("failure",{},!1,k(r(a.message)+"\n"+a.stack))))):a.pending?this.write(e("testcase",b,!1,e("skipped",{},!0))):this.write(e("testcase",b,!0))}}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"../utils":39,"./base":17,_process:51,fs:41,mkdirp:70,path:41}],35:[function(f,m,g){(function(c){function l(a,b){this.title=a;this.async=(this.fn=b)&&b.length;this.sync=!this.async;this._timeout=2E3;this._slow=75;this._enableTimeouts=
!0;this.timedOut=!1;this._trace=Error("done() called multiple times");this._retries=-1;this._currentRetry=0}var b=f("events").EventEmitter,e=f("./pending"),k=f("debug")("mocha:runnable"),a=f("./ms"),h=f("./utils"),d=h.inherits,g=c.Date,r=c.setTimeout,q=c.clearTimeout,x=Object.prototype.toString;m.exports=l;d(l,b);l.prototype.timeout=function(b){if(!arguments.length)return this._timeout;0===b&&(this._enableTimeouts=!1);"string"===typeof b&&(b=a(b));k("timeout %d",b);this._timeout=b;this.timer&&this.resetTimeout();
return this};l.prototype.slow=function(b){if(!arguments.length)return this._slow;"string"===typeof b&&(b=a(b));k("timeout %d",b);this._slow=b;return this};l.prototype.enableTimeouts=function(a){if(!arguments.length)return this._enableTimeouts;k("enableTimeouts %s",a);this._enableTimeouts=a;return this};l.prototype.skip=function(){throw new e;};l.prototype.retries=function(a){if(!arguments.length)return this._retries;this._retries=a};l.prototype.currentRetry=function(a){if(!arguments.length)return this._currentRetry;
this._currentRetry=a};l.prototype.fullTitle=function(){return this.parent.fullTitle()+" "+this.title};l.prototype.clearTimeout=function(){q(this.timer)};l.prototype.inspect=function(){return JSON.stringify(this,function(a,b){if("_"!==a[0])return"parent"===a?"#<Suite>":"ctx"===a?"#<Context>":b},2)};l.prototype.resetTimeout=function(){var a=this,b=this.timeout()||1E9;this._enableTimeouts&&(this.clearTimeout(),this.timer=r(function(){a._enableTimeouts&&(a.callback(Error("timeout of "+b+"ms exceeded. Ensure the done() callback is being called in this test.")),
a.timedOut=!0)},b))};l.prototype.globals=function(a){if(!arguments.length)return this._allowedGlobals;this._allowedGlobals=a};l.prototype.run=function(a){function b(d){var c=e.timeout();e.timedOut||(l?(d=d||e._trace,z||(z=!0,e.emit("error",d||Error("done() called multiple times; stacktrace may be inaccurate")))):(e.clearTimeout(),e.duration=new g-k,l=!0,!d&&e.duration>c&&e._enableTimeouts&&(d=Error("timeout of "+c+"ms exceeded. Ensure the done() callback is being called in this test.")),a(d)))}function d(a){if((a=
a.call(f))&&"function"===typeof a.then)e.resetTimeout(),a.then(function(){b();return null},function(a){b(a||Error("Promise rejected with no or falsy reason"))});else{if(e.asyncOnly)return b(Error("--async-only option in use without declaring `done()` or returning a promise"));b()}}function c(a){a.call(f,function(a){if(a instanceof Error||"[object Error]"===x.call(a))return b(a);if(a)return"[object Object]"===Object.prototype.toString.call(a)?b(Error("done() invoked with non-Error: "+JSON.stringify(a))):
b(Error("done() invoked with non-Error: "+a));b()})}var e=this,k=new g,f=this.ctx,l,z;f&&f.runnable&&f.runnable(this);this.callback=b;if(this.async){this.resetTimeout();if(this.allowUncaught)return c(this.fn);try{c(this.fn)}catch(n){b(h.getError(n))}}else if(this.allowUncaught)d(this.fn),b();else try{this.pending?b():d(this.fn)}catch(n){b(h.getError(n))}}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./ms":15,"./pending":16,
"./utils":39,debug:2,events:3}],36:[function(f,m,g){(function(c,l){function b(b,d){var c=this;this._globals=[];this._abort=!1;this._delay=d;this.suite=b;this.started=!1;this.total=b.total();this.failures=0;this.on("test end",function(a){c.checkGlobals(a)});this.on("hook end",function(a){c.checkGlobals(a)});this._defaultGrep=/.*/;this.grep(this._defaultGrep);this.globals(this.globalProps().concat(a()))}function e(a){function b(a){for(var d=0;d<a.length;d++)delete a[d].fn}y(a._beforeAll)&&b(a._beforeAll);
y(a._beforeEach)&&b(a._beforeEach);y(a._afterAll)&&b(a._afterAll);y(a._afterEach)&&b(a._afterEach);for(var d=0;d<a.tests.length;d++)delete a.tests[d].fn}function k(a,b){return u(b,function(b){return/^d+/.test(b)||l.navigator&&/^getInterface/.test(b)||l.navigator&&/^\d+/.test(b)||/^mocha-/.test(b)?!1:!u(a,function(a){return~a.indexOf("*")?0===b.indexOf(a.split("*")[0]):b===a}).length&&(!l.navigator||"onerror"!==b)})}function a(){if("object"===typeof c&&"string"===typeof c.version){var a=c.version.split(".");
if(2315>g.reduce(a,function(a,b){return a<<8|b}))return["errno"]}return[]}var h=f("events").EventEmitter,d=f("./pending"),g=f("./utils"),r=g.inherits,q=f("debug")("mocha:runner"),x=f("./runnable"),u=g.filter,D=g.indexOf,E=g.keys,w=g.stackTraceFilter(),A=g.stringify,F=g.type,B=g.undefinedError,y=g.isArray,z="setTimeout clearTimeout setInterval clearInterval XMLHttpRequest Date setImmediate clearImmediate".split(" ");m.exports=b;b.immediately=l.setImmediate||c.nextTick;r(b,h);b.prototype.grep=function(a,
b){q("grep %s",a);this._grep=a;this._invert=b;this.total=this.grepTotal(this.suite);return this};b.prototype.grepTotal=function(a){var b=this,d=0;a.eachTest(function(a){a=b._grep.test(a.fullTitle());b._invert&&(a=!a);a&&d++});return d};b.prototype.globalProps=function(){for(var a=E(l),b=0;b<z.length;++b)~D(a,z[b])||a.push(z[b]);return a};b.prototype.globals=function(a){if(!arguments.length)return this._globals;q("globals %j",a);this._globals=this._globals.concat(a);return this};b.prototype.checkGlobals=
function(a){if(!this.ignoreLeaks){var b=this._globals,d=this.globalProps();a&&(b=b.concat(a._allowedGlobals||[]));this.prevGlobalsLength!==d.length&&(this.prevGlobalsLength=d.length,b=k(b,d),this._globals=this._globals.concat(b),1<b.length?this.fail(a,Error("global leaks detected: "+b.join(", ")+"")):b.length&&this.fail(a,Error("global leak detected: "+b[0])))}};b.prototype.fail=function(a,b){++this.failures;a.state="failed";b instanceof Error||b&&"string"===typeof b.message||(b=Error("the "+F(b)+
" "+A(b)+" was thrown, throw an Error :)"));b.stack=this.fullStackTrace||!b.stack?b.stack:w(b.stack);this.emit("fail",a,b)};b.prototype.failHook=function(a,b){a.ctx&&a.ctx.currentTest&&(a.originalTitle=a.originalTitle||a.title,a.title=a.originalTitle+' for "'+a.ctx.currentTest.title+'"');this.fail(a,b);this.suite.bail()&&this.emit("end")};b.prototype.hook=function(a,c){function e(a){var b=k[a];if(!b)return c();f.currentRunnable=b;b.ctx.currentTest=f.test;f.emit("hook",b);if(!b.listeners("error").length)b.on("error",
function(a){f.failHook(b,a)});b.run(function(I){var k=b.error();k&&f.fail(f.test,k);if(I)if(I instanceof d)h.pending=!0;else return f.failHook(b,I),c(I);f.emit("hook end",b);delete b.ctx.currentTest;e(++a)})}var h=this.suite,k=h["_"+a],f=this;b.immediately(function(){e(0)})};b.prototype.hooks=function(a,b,d){function c(C){e.suite=C;if(!C)return e.suite=h,d();e.hook(a,function(a){if(a){var C=e.suite;e.suite=h;return d(a,C)}c(b.pop())})}var e=this,h=this.suite;c(b.pop())};b.prototype.hookUp=function(a,
b){var d=[this.suite].concat(this.parents()).reverse();this.hooks(a,d,b)};b.prototype.hookDown=function(a,b){var d=[this.suite].concat(this.parents());this.hooks(a,d,b)};b.prototype.parents=function(){for(var a=this.suite,b=[];a.parent;)a=a.parent,b.push(a);return b};b.prototype.runTest=function(a){var b=this,d=this.test;this.asyncOnly&&(d.asyncOnly=!0);if(this.allowUncaught)return d.allowUncaught=!0,d.run(a);try{d.on("error",function(a){b.fail(d,a)}),d.run(a)}catch(c){a(c)}};b.prototype.runTests=
function(a,c){function e(a,b,d){var C=k.suite;k.suite=d?b.parent:b;k.suite?k.hookUp("afterEach",function(a,d){k.suite=C;if(a)return e(a,d,!0);c(b)}):(k.suite=C,c(b))}function h(H,I){function l(a){return a.pending||a.parent&&l(a.parent)}if(k.failures&&a._bail||k._abort)return c();if(H)return e(H,I,!0);C=f.shift();if(!C)return c();var g=k._grep.test(C.fullTitle());k._invert&&(g=!g);if(g){if(C.pending||l(C.parent))return k.emit("pending",C),k.emit("test end",C),h();k.emit("test",k.test=C);k.hookDown("beforeEach",
function(b,c){if(a.pending)return k.emit("pending",C),k.emit("test end",C),h();if(b)return e(b,c,!1);k.currentRunnable=k.test;k.runTest(function(a){C=k.test;if(a){var b=C.currentRetry();if(a instanceof d)C.pending=!0,k.emit("pending",C);else{if(b<C.retries())return a=C.clone(),a.currentRetry(b+1),f.unshift(a),k.hookUp("afterEach",h);k.fail(C,a)}k.emit("test end",C);return a instanceof d?h():k.hookUp("afterEach",h)}C.state="passed";k.emit("pass",C);k.emit("test end",C);k.hookUp("afterEach",h)})})}else k._grep!==
k._defaultGrep?b.immediately(h):h()}var k=this,f=a.tests.slice(),C;this.next=h;this.hookErr=e;h()};b.prototype.runSuite=function(a,d){function c(d){if(d)return d===a?e():e(d);if(k._abort)return e();var C=a.suites[h++];if(!C)return e();k._grep!==k._defaultGrep?b.immediately(function(){k.runSuite(C,c)}):k.runSuite(C,c)}function e(b){k.suite=a;k.nextSuite=c;H?d(b):(H=!0,delete k.test,k.hook("afterAll",function(){k.emit("suite end",a);d(b)}))}var h=0,k=this,C=this.grepTotal(a),H=!1;q("run suite %s",a.fullTitle());
if(!C||k.failures&&a._bail)return d();this.emit("suite",this.suite=a);this.nextSuite=c;this.hook("beforeAll",function(b){if(b)return e();k.runTests(a,c)})};b.prototype.uncaught=function(a){a?q("uncaught exception %s",a!==function(){return this}.call(a)?a:a.message||a):(q("uncaught undefined exception"),a=B());a.uncaught=!0;var b=this.currentRunnable;if(b){if(b.clearTimeout(),!b.state)if(this.fail(b,a),"test"===b.type)this.emit("test end",b),this.hookUp("afterEach",this.next);else{if("hook"===b.type){var d=
this.suite;return-1<b.fullTitle().indexOf("after each")?this.hookErr(a,d,!0):-1<b.fullTitle().indexOf("before each")?this.hookErr(a,d,!1):this.nextSuite(d)}this.emit("end")}}else b=new x("Uncaught error outside test suite"),b.parent=this.suite,this.started?this.fail(b,a):(this.emit("start"),this.fail(b,a),this.emit("end"))};b.prototype.run=function(a){function b(a){h.uncaught(a)}function d(){h.started=!0;h.emit("start");h.runSuite(k,function(){q("finished running");h.emit("end")})}var h=this,k=this.suite;
a=a||function(){};q("start");this.on("suite end",e);this.on("end",function(){q("end");c.removeListener("uncaughtException",b);a(h.failures)});c.on("uncaughtException",b);this._delay?(this.emit("waiting",k),k.once("run",d)):d();return this};b.prototype.abort=function(){q("aborting");this._abort=!0;return this}}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./pending":16,"./runnable":35,"./utils":39,_process:51,debug:2,
events:3}],37:[function(f,m,g){function c(a,b){function c(){}this.title=a;c.prototype=b;this.ctx=new c;this.suites=[];this.tests=[];this.pending=!1;this._beforeEach=[];this._beforeAll=[];this._afterEach=[];this._afterAll=[];this.root=!a;this._timeout=2E3;this._enableTimeouts=!0;this._slow=75;this._bail=!1;this._retries=-1;this.delayed=!1}var l=f("events").EventEmitter,b=f("./hook"),e=f("./utils"),k=e.inherits,a=f("debug")("mocha:suite"),h=f("./ms");g=m.exports=c;g.create=function(a,b){var e=new c(b,
a.ctx);e.parent=a;a.pending&&(e.pending=!0);e.fullTitle();a.addSuite(e);return e};k(c,l);c.prototype.clone=function(){var b=new c(this.title);a("clone");b.ctx=this.ctx;b.timeout(this.timeout());b.retries(this.retries());b.enableTimeouts(this.enableTimeouts());b.slow(this.slow());b.bail(this.bail());return b};c.prototype.timeout=function(b){if(!arguments.length)return this._timeout;"0"===b.toString()&&(this._enableTimeouts=!1);"string"===typeof b&&(b=h(b));a("timeout %d",b);this._timeout=parseInt(b,
10);return this};c.prototype.retries=function(b){if(!arguments.length)return this._retries;a("retries %d",b);this._retries=parseInt(b,10)||0;return this};c.prototype.enableTimeouts=function(b){if(!arguments.length)return this._enableTimeouts;a("enableTimeouts %s",b);this._enableTimeouts=b;return this};c.prototype.slow=function(b){if(!arguments.length)return this._slow;"string"===typeof b&&(b=h(b));a("slow %d",b);this._slow=b;return this};c.prototype.bail=function(b){if(!arguments.length)return this._bail;
a("bail %s",b);this._bail=b;return this};c.prototype.beforeAll=function(a,c){if(this.pending)return this;"function"===typeof a&&(c=a,a=c.name);var e=new b('"before all" hook'+(a?": "+a:""),c);e.parent=this;e.timeout(this.timeout());e.retries(this.retries());e.enableTimeouts(this.enableTimeouts());e.slow(this.slow());e.ctx=this.ctx;this._beforeAll.push(e);this.emit("beforeAll",e);return this};c.prototype.afterAll=function(a,c){if(this.pending)return this;"function"===typeof a&&(c=a,a=c.name);var e=
new b('"after all" hook'+(a?": "+a:""),c);e.parent=this;e.timeout(this.timeout());e.retries(this.retries());e.enableTimeouts(this.enableTimeouts());e.slow(this.slow());e.ctx=this.ctx;this._afterAll.push(e);this.emit("afterAll",e);return this};c.prototype.beforeEach=function(a,c){if(this.pending)return this;"function"===typeof a&&(c=a,a=c.name);var e=new b('"before each" hook'+(a?": "+a:""),c);e.parent=this;e.timeout(this.timeout());e.retries(this.retries());e.enableTimeouts(this.enableTimeouts());
e.slow(this.slow());e.ctx=this.ctx;this._beforeEach.push(e);this.emit("beforeEach",e);return this};c.prototype.afterEach=function(a,c){if(this.pending)return this;"function"===typeof a&&(c=a,a=c.name);var e=new b('"after each" hook'+(a?": "+a:""),c);e.parent=this;e.timeout(this.timeout());e.retries(this.retries());e.enableTimeouts(this.enableTimeouts());e.slow(this.slow());e.ctx=this.ctx;this._afterEach.push(e);this.emit("afterEach",e);return this};c.prototype.addSuite=function(a){a.parent=this;a.timeout(this.timeout());
a.retries(this.retries());a.enableTimeouts(this.enableTimeouts());a.slow(this.slow());a.bail(this.bail());this.suites.push(a);this.emit("suite",a);return this};c.prototype.addTest=function(a){a.parent=this;a.timeout(this.timeout());a.retries(this.retries());a.enableTimeouts(this.enableTimeouts());a.slow(this.slow());a.ctx=this.ctx;this.tests.push(a);this.emit("test",a);return this};c.prototype.fullTitle=function(){if(this.parent){var a=this.parent.fullTitle();if(a)return a+" "+this.title}return this.title};
c.prototype.total=function(){return e.reduce(this.suites,function(a,b){return a+b.total()},0)+this.tests.length};c.prototype.eachTest=function(a){e.forEach(this.tests,a);e.forEach(this.suites,function(b){b.eachTest(a)});return this};c.prototype.run=function(){this.root&&this.emit("run")}},{"./hook":7,"./ms":15,"./utils":39,debug:2,events:3}],38:[function(f,m,g){function c(b,c){l.call(this,b,c);this.pending=!c;this.type="test";this.body=(c||"").toString()}var l=f("./runnable");f=f("./utils").inherits;
m.exports=c;f(c,l);c.prototype.clone=function(){var b=new c(this.title,this.fn);b.timeout(this.timeout());b.slow(this.slow());b.enableTimeouts(this.enableTimeouts());b.retries(this.retries());b.currentRetry(this.currentRetry());b.globals(this.globals());b.parent=this.parent;b.file=this.file;b.ctx=this.ctx;return b}},{"./runnable":35,"./utils":39}],39:[function(f,m,g){(function(c,l){function b(a){return!~D.indexOf(a)}function e(a,b){b=b||g.type(a);switch(b){case "function":return"[Function]";case "object":return"{}";
case "array":return"[]";default:return a.toString()}}function k(a,b,c){function d(a,b){return Array(b).join(a)}function e(a){switch(g.type(a)){case "null":case "undefined":a="["+a+"]";break;case "array":case "object":a=k(a,b,c+1);break;case "boolean":case "regexp":case "number":a=0===a&&-Infinity===1/a?"-0":a.toString();break;case "date":a="[Date: "+(isNaN(a.getTime())?a.toString():a.toISOString())+"]";break;case "buffer":a=a.toJSON();a=a.data&&a.type?a.data:a;a="[Buffer: "+k(a,2,c+1)+"]";break;default:a=
"[Function]"===a||"[Circular]"===a?a:JSON.stringify(a)}return a}if("undefined"===typeof b)return e(a);c=c||1;var h=b*c,f=E(a)?"[":"{",l=E(a)?"]":"}",G=a.length||g.keys(a).length,t;for(t in a)a.hasOwnProperty(t)&&(--G,f+="\n "+d(" ",h)+(E(a)?"":'"'+t+'": ')+e(a[t])+(G?",":""));return f+(1!==f.length?"\n"+d(" ",--h)+l:l)}var a=f("path").basename,h=f("debug")("mocha:watch"),d=f("fs").existsSync||f("path").existsSync,t=f("glob"),m=f("path").join,q=f("fs").readdirSync,x=f("fs").statSync,u=f("fs").watchFile,
D=["node_modules",".git"];g.inherits=f("util").inherits;g.escape=function(a){return String(a).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")};g.forEach=function(a,b,c){for(var d=0,e=a.length;d<e;d++)b.call(c,a[d],d)};g.isString=function(a){return"string"===typeof a};g.map=function(a,b,c){for(var d=[],e=0,h=a.length;e<h;e++)d.push(b.call(c,a[e],e,a));return d};g.indexOf=function(a,b,c){c=c||0;for(var d=a.length;c<d;c++)if(a[c]===b)return c;return-1};g.reduce=
function(a,b,c){for(var d=0,e=a.length;d<e;d++)c=b(c,a[d],d,a);return c};g.filter=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++){var h=a[d];b(h,d,a)&&c.push(h)}return c};g.keys="function"===typeof Object.keys?Object.keys:function(a){var b=[],c=Object.prototype.hasOwnProperty,d;for(d in a)c.call(a,d)&&b.push(d);return b};g.watch=function(a,b){var c={interval:100};a.forEach(function(a){h("file %s",a);u(a,c,function(c,d){d.mtime<c.mtime&&b(a)})})};var E="function"===typeof Array.isArray?Array.isArray:
function(a){return"[object Array]"===Object.prototype.toString.call(a)};g.isArray=E;"undefined"!==typeof l&&l.prototype&&(l.prototype.toJSON=l.prototype.toJSON||function(){return Array.prototype.slice.call(this,0)});g.files=function(a,c,d){d=d||[];c=c||["js"];var e=new RegExp("\\.("+c.join("|")+")$");q(a).filter(b).forEach(function(b){b=m(a,b);x(b).isDirectory()?g.files(b,c,d):b.match(e)&&d.push(b)});return d};g.slug=function(a){return a.toLowerCase().replace(/ +/g,"-").replace(/[^-\w]/g,"")};g.clean=
function(a){a=a.replace(/\r\n?|[\n\u2028\u2029]/g,"\n").replace(/^\uFEFF/,"").replace(/^function *\(.*\)\s*\{|\(.*\) *=> *\{?/,"").replace(/\s+\}$/,"");var b=a.match(/^\n?( *)/)[1].length,c=a.match(/^\n?(\t*)/)[1].length;a=a.replace(new RegExp("^\n?"+(c?"\t":" ")+"{"+(c?c:b)+"}","gm"),"");return g.trim(a)};g.trim=function(a){return a.replace(/^\s+|\s+$/g,"")};g.parseQuery=function(a){return g.reduce(a.replace("?","").split("&"),function(a,b){var c=b.indexOf("="),d=b.slice(0,c),c=b.slice(++c);a[d]=
decodeURIComponent(c);return a},{})};g.highlightTags=function(a){a=document.getElementById("mocha").getElementsByTagName(a);for(var b=0,c=a.length;b<c;++b)a[b].innerHTML=a[b].innerHTML.replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\/\/(.*)/gm,'<span class="comment">//$1</span>').replace(/('.*?')/gm,'<span class="string">$1</span>').replace(/(\d+\.\d+)/gm,'<span class="number">$1</span>').replace(/(\d+)/gm,'<span class="number">$1</span>').replace(/\bnew[ \t]+(\w+)/gm,'<span class="keyword">new</span> <span class="init">$1</span>').replace(/\b(function|new|throw|return|var|if|else)\b/gm,
'<span class="keyword">$1</span>')};g.type=function(a){return void 0===a?"undefined":null===a?"null":"undefined"!==typeof l&&l.isBuffer(a)?"buffer":Object.prototype.toString.call(a).replace(/^\[.+\s(.+?)\]$/,"$1").toLowerCase()};g.stringify=function(a){var b=g.type(a);if(!~g.indexOf(["object","array","function"],b)){if("buffer"!==b)return k(a);a=a.toJSON();return k(a.data&&a.type?a.data:a,2).replace(/,(\n|$)/g,"$1")}for(var c in a)if(Object.prototype.hasOwnProperty.call(a,c))return k(g.canonicalize(a),
2).replace(/,(\n|$)/g,"$1");return e(a,b)};g.isBuffer=function(a){return"undefined"!==typeof l&&l.isBuffer(a)};g.canonicalize=function(a,b){function c(a,d){b.push(a);d();b.pop()}var d,h,k=g.type(a);b=b||[];if(-1!==g.indexOf(b,a))return"[Circular]";switch(k){case "undefined":case "buffer":case "null":d=a;break;case "array":c(a,function(){d=g.map(a,function(a){return g.canonicalize(a,b)})});break;case "function":for(h in a){d={};break}if(!d){d=e(a,k);break}case "object":d=d||{};c(a,function(){g.forEach(g.keys(a).sort(),
function(c){d[c]=g.canonicalize(a[c],b)})});break;case "date":case "number":case "regexp":case "boolean":d=a;break;default:d=a+""}return d};g.lookupFiles=function A(b,c,e){var h=[],k=new RegExp("\\.("+c.join("|")+")$");if(!d(b))if(d(b+".js"))b+=".js";else{h=t.sync(b);if(!h.length)throw Error("cannot resolve path (or pattern) '"+b+"'");return h}try{if(x(b).isFile())return b}catch(f){return}q(b).forEach(function(d){d=m(b,d);try{var f=x(d);if(f.isDirectory()){e&&(h=h.concat(A(d,c,e)));return}}catch(l){return}f.isFile()&&
k.test(d)&&"."!==a(d)[0]&&h.push(d)});return h};g.undefinedError=function(){return Error("Caught undefined error, did you throw without specifying what?")};g.getError=function(a){return a||g.undefinedError()};g.stackTraceFilter=function(){var a="undefined"===typeof document?{node:!0}:{browser:!0},b=a.node?c.cwd()+"/":("undefined"===typeof location?window.location:location).href.replace(/\/[^\/]*$/,"/");return function(c){c=c.split("\n");c=g.reduce(c,function(c,d){if(~d.indexOf("node_modules/mocha/")||
~d.indexOf("components/mochajs/")||~d.indexOf("components/mocha/")||~d.indexOf("/mocha.js"))return c;var e;if(e=a.node)e=~d.indexOf("(timers.js:")||~d.indexOf("(events.js:")||~d.indexOf("(node.js:")||~d.indexOf("(module.js:")||~d.indexOf("GeneratorFunctionPrototype.next (native)")||!1;if(e)return c;c.push(d.replace(b,""));return c},[]);return c.join("\n")}}}).call(this,f("_process"),f("buffer").Buffer)},{_process:51,buffer:43,debug:2,fs:41,glob:41,path:41,util:66}],40:[function(f,m,g){(function(c){function l(c){if(!(this instanceof
l))return new l(c);c=c||{};b.call(this,c);this.label=void 0!==c.label?c.label:"stdout"}var b=f("stream").Writable,e=f("util").inherits;m.exports=l;e(l,b);l.prototype._write=function(b,a,e){b=b.toString?b.toString():b;!1===this.label?console.log(b):console.log(this.label+":",b);c.nextTick(e)}}).call(this,f("_process"))},{_process:51,stream:63,util:66}],41:[function(f,m,g){},{}],42:[function(f,m,g){arguments[4][41][0].apply(g,arguments)},{dup:41}],43:[function(f,m,g){function c(a){if(!(this instanceof
c))return 1<arguments.length?new c(a,arguments[1]):new c(a);this.length=0;this.parent=void 0;if("number"===typeof a){var b;b=e(this,0>a?0:k(a)|0);if(!c.TYPED_ARRAY_SUPPORT)for(var d=0;d<a;d++)b[d]=0;return b}if("string"===typeof a){b=this;d=1<arguments.length?arguments[1]:"utf8";if("string"!==typeof d||""===d)d="utf8";var f=h(a,d)|0;b=e(b,f);b.write(a,d);return b}return l(this,a)}function l(a,d){if(c.isBuffer(d)){var h=a,f=k(d.length)|0,h=e(h,f);d.copy(h,0,0,f);return h}if(p(d)){for(var h=a,f=k(d.length)|
0,h=e(h,f),l=0;l<f;l+=1)h[l]=d[l]&255;return h}if(null==d)throw new TypeError("must start with number, buffer, array or string");if("undefined"!==typeof ArrayBuffer){if(d.buffer instanceof ArrayBuffer)return b(a,d);if(d instanceof ArrayBuffer)return h=a,c.TYPED_ARRAY_SUPPORT?(d.byteLength,h=c._augment(new Uint8Array(d))):h=b(h,new Uint8Array(d)),h}if(d.length){h=a;f=k(d.length)|0;h=e(h,f);for(l=0;l<f;l+=1)h[l]=d[l]&255;return h}h=a;l=0;"Buffer"===d.type&&p(d.data)&&(f=d.data,l=k(f.length)|0);for(var h=
e(h,l),g=0;g<l;g+=1)h[g]=f[g]&255;return h}function b(a,b){var c=k(b.length)|0;a=e(a,c);for(var d=0;d<c;d+=1)a[d]=b[d]&255;return a}function e(a,b){c.TYPED_ARRAY_SUPPORT?a=c._augment(new Uint8Array(b)):(a.length=b,a._isBuffer=!0);0!==b&&b<=c.poolSize>>>1&&(a.parent=G);return a}function k(a){if(a>=(c.TYPED_ARRAY_SUPPORT?2147483647:1073741823))throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+(c.TYPED_ARRAY_SUPPORT?2147483647:1073741823).toString(16)+" bytes");return a|
0}function a(b,d){if(!(this instanceof a))return new a(b,d);var e=new c(b,d);delete e.parent;return e}function h(a,b){"string"!==typeof a&&(a=""+a);var c=a.length;if(0===c)return 0;for(var d=!1;;)switch(b){case "ascii":case "binary":case "raw":case "raws":return c;case "utf8":case "utf-8":return F(a).length;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":return 2*c;case "hex":return c>>>1;case "base64":return z.toByteArray(A(a)).length;default:if(d)return F(a).length;b=(""+b).toLowerCase();
d=!0}}function d(a,b,c){var d=!1;b|=0;c=void 0===c||Infinity===c?this.length:c|0;a||(a="utf8");0>b&&(b=0);c>this.length&&(c=this.length);if(c<=b)return"";for(;;)switch(a){case "hex":a=b;b=c;c=this.length;if(!a||0>a)a=0;if(!b||0>b||b>c)b=c;d="";for(c=a;c<b;c++)a=d,d=this[c],d=16>d?"0"+d.toString(16):d.toString(16),d=a+d;return d;case "utf8":case "utf-8":return t(this,b,c);case "ascii":a="";for(c=Math.min(this.length,c);b<c;b++)a+=String.fromCharCode(this[b]&127);return a;case "binary":a="";for(c=Math.min(this.length,
c);b<c;b++)a+=String.fromCharCode(this[b]);return a;case "base64":return b=0===b&&c===this.length?z.fromByteArray(this):z.fromByteArray(this.slice(b,c)),b;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":b=this.slice(b,c);c="";for(a=0;a<b.length;a+=2)c+=String.fromCharCode(b[a]+256*b[a+1]);return c;default:if(d)throw new TypeError("Unknown encoding: "+a);a=(a+"").toLowerCase();d=!0}}function t(a,b,c){c=Math.min(a.length,c);for(var d=[];b<c;){var e=a[b],h=null,k=239<e?4:223<e?3:191<e?2:1;if(b+
k<=c){var f,l,g;switch(k){case 1:128>e&&(h=e);break;case 2:f=a[b+1];128===(f&192)&&(e=(e&31)<<6|f&63,127<e&&(h=e));break;case 3:f=a[b+1];l=a[b+2];128===(f&192)&&128===(l&192)&&(e=(e&15)<<12|(f&63)<<6|l&63,2047<e&&(55296>e||57343<e)&&(h=e));break;case 4:f=a[b+1],l=a[b+2],g=a[b+3],128===(f&192)&&128===(l&192)&&128===(g&192)&&(e=(e&15)<<18|(f&63)<<12|(l&63)<<6|g&63,65535<e&&1114112>e&&(h=e))}}null===h?(h=65533,k=1):65535<h&&(h-=65536,d.push(h>>>10&1023|55296),h=56320|h&1023);d.push(h);b+=k}a=d.length;
if(a<=J)d=String.fromCharCode.apply(String,d);else{c="";for(b=0;b<a;)c+=String.fromCharCode.apply(String,d.slice(b,b+=J));d=c}return d}function r(a,b,c){if(0!==a%1||0>a)throw new RangeError("offset is not uint");if(a+b>c)throw new RangeError("Trying to access beyond buffer length");}function q(a,b,d,e,h,k){if(!c.isBuffer(a))throw new TypeError("buffer must be a Buffer instance");if(b>h||b<k)throw new RangeError("value is out of bounds");if(d+e>a.length)throw new RangeError("index out of range");}
function x(a,b,c,d){0>b&&(b=65535+b+1);for(var e=0,h=Math.min(a.length-c,2);e<h;e++)a[c+e]=(b&255<<8*(d?e:1-e))>>>8*(d?e:1-e)}function u(a,b,c,d){0>b&&(b=4294967295+b+1);for(var e=0,h=Math.min(a.length-c,4);e<h;e++)a[c+e]=b>>>8*(d?e:3-e)&255}function D(a,b,c,d,e,h){if(b>e||b<h)throw new RangeError("value is out of bounds");if(c+d>a.length)throw new RangeError("index out of range");if(0>c)throw new RangeError("index out of range");}function E(a,b,c,d,e){e||D(a,b,c,4,3.4028234663852886E38,-3.4028234663852886E38);
n.write(a,b,c,d,23,4);return c+4}function w(a,b,c,d,e){e||D(a,b,c,8,1.7976931348623157E308,-1.7976931348623157E308);n.write(a,b,c,d,52,8);return c+8}function A(a){a=a.trim?a.trim():a.replace(/^\s+|\s+$/g,"");a=a.replace(K,"");if(2>a.length)return"";for(;0!==a.length%4;)a+="=";return a}function F(a,b){b=b||Infinity;for(var c,d=a.length,e=null,h=[],k=0;k<d;k++){c=a.charCodeAt(k);if(55295<c&&57344>c){if(!e){if(56319<c){-1<(b-=3)&&h.push(239,191,189);continue}else if(k+1===d){-1<(b-=3)&&h.push(239,191,
189);continue}e=c;continue}if(56320>c){-1<(b-=3)&&h.push(239,191,189);e=c;continue}c=e-55296<<10|c-56320|65536}else e&&-1<(b-=3)&&h.push(239,191,189);e=null;if(128>c){if(0>--b)break;h.push(c)}else if(2048>c){if(0>(b-=2))break;h.push(c>>6|192,c&63|128)}else if(65536>c){if(0>(b-=3))break;h.push(c>>12|224,c>>6&63|128,c&63|128)}else if(1114112>c){if(0>(b-=4))break;h.push(c>>18|240,c>>12&63|128,c>>6&63|128,c&63|128)}else throw Error("Invalid code point");}return h}function B(a){for(var b=[],c=0;c<a.length;c++)b.push(a.charCodeAt(c)&
255);return b}function y(a,b,c,d){for(var e=0;e<d&&!(e+c>=b.length||e>=a.length);e++)b[e+c]=a[e];return e}var z=f("base64-js"),n=f("ieee754"),p=f("is-array");g.Buffer=c;g.SlowBuffer=a;g.INSPECT_MAX_BYTES=50;c.poolSize=8192;var G={};c.TYPED_ARRAY_SUPPORT=function(){function a(){}try{var b=new Uint8Array(1);b.foo=function(){return 42};b.constructor=a;return 42===b.foo()&&b.constructor===a&&"function"===typeof b.subarray&&0===b.subarray(1,1).byteLength}catch(c){return!1}}();c.isBuffer=function(a){return!(null==
a||!a._isBuffer)};c.compare=function(a,b){if(!c.isBuffer(a)||!c.isBuffer(b))throw new TypeError("Arguments must be Buffers");if(a===b)return 0;for(var d=a.length,e=b.length,h=0,k=Math.min(d,e);h<k&&a[h]===b[h];)++h;h!==k&&(d=a[h],e=b[h]);return d<e?-1:e<d?1:0};c.isEncoding=function(a){switch(String(a).toLowerCase()){case "hex":case "utf8":case "utf-8":case "ascii":case "binary":case "base64":case "raw":case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":return!0;default:return!1}};c.concat=function(a,
b){if(!p(a))throw new TypeError("list argument must be an Array of Buffers.");if(0===a.length)return new c(0);var d;if(void 0===b)for(d=b=0;d<a.length;d++)b+=a[d].length;var e=new c(b),h=0;for(d=0;d<a.length;d++){var k=a[d];k.copy(e,h);h+=k.length}return e};c.byteLength=h;c.prototype.length=void 0;c.prototype.parent=void 0;c.prototype.toString=function(){var a=this.length|0;return 0===a?"":0===arguments.length?t(this,0,a):d.apply(this,arguments)};c.prototype.equals=function(a){if(!c.isBuffer(a))throw new TypeError("Argument must be a Buffer");
return this===a?!0:0===c.compare(this,a)};c.prototype.inspect=function(){var a="",b=g.INSPECT_MAX_BYTES;0<this.length&&(a=this.toString("hex",0,b).match(/.{2}/g).join(" "),this.length>b&&(a+=" ... "));return"<Buffer "+a+">"};c.prototype.compare=function(a){if(!c.isBuffer(a))throw new TypeError("Argument must be a Buffer");return this===a?0:c.compare(this,a)};c.prototype.indexOf=function(a,b){function d(a,b,c){for(var e=-1,h=0;c+h<a.length;h++)if(a[c+h]===b[-1===e?0:h-e]){if(-1===e&&(e=h),h-e+1===
b.length)return c+e}else e=-1;return-1}2147483647<b?b=2147483647:-2147483648>b&&(b=-2147483648);b>>=0;if(0===this.length||b>=this.length)return-1;0>b&&(b=Math.max(this.length+b,0));if("string"===typeof a)return 0===a.length?-1:String.prototype.indexOf.call(this,a,b);if(c.isBuffer(a))return d(this,a,b);if("number"===typeof a)return c.TYPED_ARRAY_SUPPORT&&"function"===Uint8Array.prototype.indexOf?Uint8Array.prototype.indexOf.call(this,a,b):d(this,[a],b);throw new TypeError("val must be string, number or Buffer");
};c.prototype.get=function(a){console.log(".get() is deprecated. Access using array indexes instead.");return this.readUInt8(a)};c.prototype.set=function(a,b){console.log(".set() is deprecated. Access using array indexes instead.");return this.writeUInt8(a,b)};c.prototype.write=function(a,b,c,d){if(void 0===b)d="utf8",c=this.length,b=0;else if(void 0===c&&"string"===typeof b)d=b,c=this.length,b=0;else if(isFinite(b))b|=0,isFinite(c)?(c|=0,void 0===d&&(d="utf8")):(d=c,c=void 0);else{var e=d;d=b;b=
c|0;c=e}e=this.length-b;if(void 0===c||c>e)c=e;if(0<a.length&&(0>c||0>b)||b>this.length)throw new RangeError("attempt to write outside buffer bounds");d||(d="utf8");for(e=!1;;)switch(d){case "hex":b=Number(b)||0;d=this.length-b;c?(c=Number(c),c>d&&(c=d)):c=d;d=a.length;if(0!==d%2)throw Error("Invalid hex string");c>d/2&&(c=d/2);for(d=0;d<c;d++){e=parseInt(a.substr(2*d,2),16);if(isNaN(e))throw Error("Invalid hex string");this[b+d]=e}return d;case "utf8":case "utf-8":return y(F(a,this.length-b),this,
b,c);case "ascii":return y(B(a),this,b,c);case "binary":return y(B(a),this,b,c);case "base64":return y(z.toByteArray(A(a)),this,b,c);case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":d=this.length-b;for(var h=void 0,h=e=void 0,k=[],f=0;f<a.length&&!(0>(d-=2));f++)h=a.charCodeAt(f),e=h>>8,h%=256,k.push(h),k.push(e);return y(k,this,b,c);default:if(e)throw new TypeError("Unknown encoding: "+d);d=(""+d).toLowerCase();e=!0}};c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||
this,0)}};var J=4096;c.prototype.slice=function(a,b){var d=this.length;a=~~a;b=void 0===b?d:~~b;0>a?(a+=d,0>a&&(a=0)):a>d&&(a=d);0>b?(b+=d,0>b&&(b=0)):b>d&&(b=d);b<a&&(b=a);if(c.TYPED_ARRAY_SUPPORT)d=c._augment(this.subarray(a,b));else for(var e=b-a,d=new c(e,void 0),h=0;h<e;h++)d[h]=this[h+a];d.length&&(d.parent=this.parent||this);return d};c.prototype.readUIntLE=function(a,b,c){a|=0;b|=0;c||r(a,b,this.length);c=this[a];for(var d=1,e=0;++e<b&&(d*=256);)c+=this[a+e]*d;return c};c.prototype.readUIntBE=
function(a,b,c){a|=0;b|=0;c||r(a,b,this.length);c=this[a+--b];for(var d=1;0<b&&(d*=256);)c+=this[a+--b]*d;return c};c.prototype.readUInt8=function(a,b){b||r(a,1,this.length);return this[a]};c.prototype.readUInt16LE=function(a,b){b||r(a,2,this.length);return this[a]|this[a+1]<<8};c.prototype.readUInt16BE=function(a,b){b||r(a,2,this.length);return this[a]<<8|this[a+1]};c.prototype.readUInt32LE=function(a,b){b||r(a,4,this.length);return(this[a]|this[a+1]<<8|this[a+2]<<16)+16777216*this[a+3]};c.prototype.readUInt32BE=
function(a,b){b||r(a,4,this.length);return 16777216*this[a]+(this[a+1]<<16|this[a+2]<<8|this[a+3])};c.prototype.readIntLE=function(a,b,c){a|=0;b|=0;c||r(a,b,this.length);c=this[a];for(var d=1,e=0;++e<b&&(d*=256);)c+=this[a+e]*d;c>=128*d&&(c-=Math.pow(2,8*b));return c};c.prototype.readIntBE=function(a,b,c){a|=0;b|=0;c||r(a,b,this.length);c=b;for(var d=1,e=this[a+--c];0<c&&(d*=256);)e+=this[a+--c]*d;e>=128*d&&(e-=Math.pow(2,8*b));return e};c.prototype.readInt8=function(a,b){b||r(a,1,this.length);return this[a]&
128?-1*(255-this[a]+1):this[a]};c.prototype.readInt16LE=function(a,b){b||r(a,2,this.length);var c=this[a]|this[a+1]<<8;return c&32768?c|4294901760:c};c.prototype.readInt16BE=function(a,b){b||r(a,2,this.length);var c=this[a+1]|this[a]<<8;return c&32768?c|4294901760:c};c.prototype.readInt32LE=function(a,b){b||r(a,4,this.length);return this[a]|this[a+1]<<8|this[a+2]<<16|this[a+3]<<24};c.prototype.readInt32BE=function(a,b){b||r(a,4,this.length);return this[a]<<24|this[a+1]<<16|this[a+2]<<8|this[a+3]};
c.prototype.readFloatLE=function(a,b){b||r(a,4,this.length);return n.read(this,a,!0,23,4)};c.prototype.readFloatBE=function(a,b){b||r(a,4,this.length);return n.read(this,a,!1,23,4)};c.prototype.readDoubleLE=function(a,b){b||r(a,8,this.length);return n.read(this,a,!0,52,8)};c.prototype.readDoubleBE=function(a,b){b||r(a,8,this.length);return n.read(this,a,!1,52,8)};c.prototype.writeUIntLE=function(a,b,c,d){a=+a;b|=0;c|=0;d||q(this,a,b,c,Math.pow(2,8*c),0);d=1;var e=0;for(this[b]=a&255;++e<c&&(d*=256);)this[b+
e]=a/d&255;return b+c};c.prototype.writeUIntBE=function(a,b,c,d){a=+a;b|=0;c|=0;d||q(this,a,b,c,Math.pow(2,8*c),0);d=c-1;var e=1;for(this[b+d]=a&255;0<=--d&&(e*=256);)this[b+d]=a/e&255;return b+c};c.prototype.writeUInt8=function(a,b,d){a=+a;b|=0;d||q(this,a,b,1,255,0);c.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));this[b]=a;return b+1};c.prototype.writeUInt16LE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,2,65535,0);c.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8):x(this,a,b,!0);return b+2};c.prototype.writeUInt16BE=
function(a,b,d){a=+a;b|=0;d||q(this,a,b,2,65535,0);c.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=a):x(this,a,b,!1);return b+2};c.prototype.writeUInt32LE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,4,4294967295,0);c.TYPED_ARRAY_SUPPORT?(this[b+3]=a>>>24,this[b+2]=a>>>16,this[b+1]=a>>>8,this[b]=a):u(this,a,b,!0);return b+4};c.prototype.writeUInt32BE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,4,4294967295,0);c.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=a):u(this,a,b,!1);
return b+4};c.prototype.writeIntLE=function(a,b,c,d){a=+a;b|=0;d||(d=Math.pow(2,8*c-1),q(this,a,b,c,d-1,-d));d=0;var e=1,h=0>a?1:0;for(this[b]=a&255;++d<c&&(e*=256);)this[b+d]=(a/e>>0)-h&255;return b+c};c.prototype.writeIntBE=function(a,b,c,d){a=+a;b|=0;d||(d=Math.pow(2,8*c-1),q(this,a,b,c,d-1,-d));d=c-1;var e=1,h=0>a?1:0;for(this[b+d]=a&255;0<=--d&&(e*=256);)this[b+d]=(a/e>>0)-h&255;return b+c};c.prototype.writeInt8=function(a,b,d){a=+a;b|=0;d||q(this,a,b,1,127,-128);c.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));
0>a&&(a=255+a+1);this[b]=a;return b+1};c.prototype.writeInt16LE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,2,32767,-32768);c.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8):x(this,a,b,!0);return b+2};c.prototype.writeInt16BE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,2,32767,-32768);c.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=a):x(this,a,b,!1);return b+2};c.prototype.writeInt32LE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,4,2147483647,-2147483648);c.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8,this[b+
2]=a>>>16,this[b+3]=a>>>24):u(this,a,b,!0);return b+4};c.prototype.writeInt32BE=function(a,b,d){a=+a;b|=0;d||q(this,a,b,4,2147483647,-2147483648);0>a&&(a=4294967295+a+1);c.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=a):u(this,a,b,!1);return b+4};c.prototype.writeFloatLE=function(a,b,c){return E(this,a,b,!0,c)};c.prototype.writeFloatBE=function(a,b,c){return E(this,a,b,!1,c)};c.prototype.writeDoubleLE=function(a,b,c){return w(this,a,b,!0,c)};c.prototype.writeDoubleBE=
function(a,b,c){return w(this,a,b,!1,c)};c.prototype.copy=function(a,b,d,e){d||(d=0);e||0===e||(e=this.length);b>=a.length&&(b=a.length);b||(b=0);0<e&&e<d&&(e=d);if(e===d||0===a.length||0===this.length)return 0;if(0>b)throw new RangeError("targetStart out of bounds");if(0>d||d>=this.length)throw new RangeError("sourceStart out of bounds");if(0>e)throw new RangeError("sourceEnd out of bounds");e>this.length&&(e=this.length);a.length-b<e-d&&(e=a.length-b+d);var h=e-d;if(this===a&&d<b&&b<e)for(e=h-1;0<=
e;e--)a[e+b]=this[e+d];else if(1E3>h||!c.TYPED_ARRAY_SUPPORT)for(e=0;e<h;e++)a[e+b]=this[e+d];else a._set(this.subarray(d,d+h),b);return h};c.prototype.fill=function(a,b,c){a||(a=0);b||(b=0);c||(c=this.length);if(c<b)throw new RangeError("end < start");if(c!==b&&0!==this.length){if(0>b||b>=this.length)throw new RangeError("start out of bounds");if(0>c||c>this.length)throw new RangeError("end out of bounds");if("number"===typeof a)for(;b<c;b++)this[b]=a;else{a=F(a.toString());for(var d=a.length;b<
c;b++)this[b]=a[b%d]}return this}};c.prototype.toArrayBuffer=function(){if("undefined"!==typeof Uint8Array){if(c.TYPED_ARRAY_SUPPORT)return(new c(this)).buffer;for(var a=new Uint8Array(this.length),b=0,d=a.length;b<d;b+=1)a[b]=this[b];return a.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser");};var v=c.prototype;c._augment=function(a){a.constructor=c;a._isBuffer=!0;a._set=a.set;a.get=v.get;a.set=v.set;a.write=v.write;a.toString=v.toString;a.toLocaleString=v.toString;
a.toJSON=v.toJSON;a.equals=v.equals;a.compare=v.compare;a.indexOf=v.indexOf;a.copy=v.copy;a.slice=v.slice;a.readUIntLE=v.readUIntLE;a.readUIntBE=v.readUIntBE;a.readUInt8=v.readUInt8;a.readUInt16LE=v.readUInt16LE;a.readUInt16BE=v.readUInt16BE;a.readUInt32LE=v.readUInt32LE;a.readUInt32BE=v.readUInt32BE;a.readIntLE=v.readIntLE;a.readIntBE=v.readIntBE;a.readInt8=v.readInt8;a.readInt16LE=v.readInt16LE;a.readInt16BE=v.readInt16BE;a.readInt32LE=v.readInt32LE;a.readInt32BE=v.readInt32BE;a.readFloatLE=v.readFloatLE;
a.readFloatBE=v.readFloatBE;a.readDoubleLE=v.readDoubleLE;a.readDoubleBE=v.readDoubleBE;a.writeUInt8=v.writeUInt8;a.writeUIntLE=v.writeUIntLE;a.writeUIntBE=v.writeUIntBE;a.writeUInt16LE=v.writeUInt16LE;a.writeUInt16BE=v.writeUInt16BE;a.writeUInt32LE=v.writeUInt32LE;a.writeUInt32BE=v.writeUInt32BE;a.writeIntLE=v.writeIntLE;a.writeIntBE=v.writeIntBE;a.writeInt8=v.writeInt8;a.writeInt16LE=v.writeInt16LE;a.writeInt16BE=v.writeInt16BE;a.writeInt32LE=v.writeInt32LE;a.writeInt32BE=v.writeInt32BE;a.writeFloatLE=
v.writeFloatLE;a.writeFloatBE=v.writeFloatBE;a.writeDoubleLE=v.writeDoubleLE;a.writeDoubleBE=v.writeDoubleBE;a.fill=v.fill;a.inspect=v.inspect;a.toArrayBuffer=v.toArrayBuffer;return a};var K=/[^+\/0-9A-Za-z-_]/g},{"base64-js":44,ieee754:45,"is-array":46}],44:[function(f,m,g){(function(c){function f(b){b=b.charCodeAt(0);if(43===b||45===b)return 62;if(47===b||95===b)return 63;if(48>b)return-1;if(58>b)return b-48+52;if(91>b)return b-65;if(123>b)return b-97+26}var b="undefined"!==typeof Uint8Array?Uint8Array:
Array;c.toByteArray=function(c){function k(a){m[q++]=a}var a,h,d,g,m;if(0<c.length%4)throw Error("Invalid string. Length must be a multiple of 4");a=c.length;g="="===c.charAt(a-2)?2:"="===c.charAt(a-1)?1:0;m=new b(3*c.length/4-g);h=0<g?c.length-4:c.length;var q=0;for(a=0;a<h;a+=4)d=f(c.charAt(a))<<18|f(c.charAt(a+1))<<12|f(c.charAt(a+2))<<6|f(c.charAt(a+3)),k((d&16711680)>>16),k((d&65280)>>8),k(d&255);2===g?(d=f(c.charAt(a))<<2|f(c.charAt(a+1))>>4,k(d&255)):1===g&&(d=f(c.charAt(a))<<10|f(c.charAt(a+
1))<<4|f(c.charAt(a+2))>>2,k(d>>8&255),k(d&255));return m};c.fromByteArray=function(b){var c,a=b.length%3,h="",d,f;c=0;for(f=b.length-a;c<f;c+=3)d=(b[c]<<16)+(b[c+1]<<8)+b[c+2],d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>18&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>12&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>6&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d&
63),h+=d;switch(a){case 1:d=b[b.length-1];h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>2);h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d<<4&63);h+="==";break;case 2:d=(b[b.length-2]<<8)+b[b.length-1],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>10),h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>4&63),h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d<<
2&63),h+="="}return h}})("undefined"===typeof g?this.base64js={}:g)},{}],45:[function(f,m,g){g.read=function(c,f,b,e,k){var a;a=8*k-e-1;var h=(1<<a)-1,d=h>>1,g=-7;k=b?k-1:0;var m=b?-1:1,q=c[f+k];k+=m;b=q&(1<<-g)-1;q>>=-g;for(g+=a;0<g;b=256*b+c[f+k],k+=m,g-=8);a=b&(1<<-g)-1;b>>=-g;for(g+=e;0<g;a=256*a+c[f+k],k+=m,g-=8);if(0===b)b=1-d;else{if(b===h)return a?NaN:Infinity*(q?-1:1);a+=Math.pow(2,e);b-=d}return(q?-1:1)*a*Math.pow(2,b-e)};g.write=function(c,f,b,e,k,a){var h,d=8*a-k-1,g=(1<<d)-1,m=g>>1,q=
23===k?Math.pow(2,-24)-Math.pow(2,-77):0;a=e?0:a-1;var x=e?1:-1,u=0>f||0===f&&0>1/f?1:0;f=Math.abs(f);isNaN(f)||Infinity===f?(f=isNaN(f)?1:0,e=g):(e=Math.floor(Math.log(f)/Math.LN2),1>f*(h=Math.pow(2,-e))&&(e--,h*=2),f=1<=e+m?f+q/h:f+q*Math.pow(2,1-m),2<=f*h&&(e++,h/=2),e+m>=g?(f=0,e=g):1<=e+m?(f=(f*h-1)*Math.pow(2,k),e+=m):(f=f*Math.pow(2,m-1)*Math.pow(2,k),e=0));for(;8<=k;c[b+a]=f&255,a+=x,f/=256,k-=8);e=e<<k|f;for(d+=k;0<d;c[b+a]=e&255,a+=x,e/=256,d-=8);c[b+a-x]|=128*u}},{}],46:[function(f,m,g){var c=
Object.prototype.toString;m.exports=Array.isArray||function(f){return!!f&&"[object Array]"==c.call(f)}},{}],47:[function(f,m,g){function c(){this._events=this._events||{};this._maxListeners=this._maxListeners||void 0}function l(b){return"function"===typeof b}function b(b){return"object"===typeof b&&null!==b}m.exports=c;c.EventEmitter=c;c.prototype._events=void 0;c.prototype._maxListeners=void 0;c.defaultMaxListeners=10;c.prototype.setMaxListeners=function(b){if("number"!==typeof b||0>b||isNaN(b))throw TypeError("n must be a positive number");
this._maxListeners=b;return this};c.prototype.emit=function(c){var f,a,h,d;this._events||(this._events={});if("error"===c&&(!this._events.error||b(this._events.error)&&!this._events.error.length)){f=arguments[1];if(f instanceof Error)throw f;throw TypeError('Uncaught, unspecified "error" event.');}a=this._events[c];if(void 0===a)return!1;if(l(a))switch(arguments.length){case 1:a.call(this);break;case 2:a.call(this,arguments[1]);break;case 3:a.call(this,arguments[1],arguments[2]);break;default:f=arguments.length;
h=Array(f-1);for(d=1;d<f;d++)h[d-1]=arguments[d];a.apply(this,h)}else if(b(a)){f=arguments.length;h=Array(f-1);for(d=1;d<f;d++)h[d-1]=arguments[d];a=a.slice();f=a.length;for(d=0;d<f;d++)a[d].apply(this,h)}return!0};c.prototype.addListener=function(e,f){var a;if(!l(f))throw TypeError("listener must be a function");this._events||(this._events={});this._events.newListener&&this.emit("newListener",e,l(f.listener)?f.listener:f);this._events[e]?b(this._events[e])?this._events[e].push(f):this._events[e]=
[this._events[e],f]:this._events[e]=f;b(this._events[e])&&!this._events[e].warned&&(a=void 0!==this._maxListeners?this._maxListeners:c.defaultMaxListeners)&&0<a&&this._events[e].length>a&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"===typeof console.trace&&console.trace());return this};c.prototype.on=c.prototype.addListener;c.prototype.once=
function(b,c){function a(){this.removeListener(b,a);h||(h=!0,c.apply(this,arguments))}if(!l(c))throw TypeError("listener must be a function");var h=!1;a.listener=c;this.on(b,a);return this};c.prototype.removeListener=function(c,f){var a,h,d;if(!l(f))throw TypeError("listener must be a function");if(!this._events||!this._events[c])return this;a=this._events[c];d=a.length;h=-1;if(a===f||l(a.listener)&&a.listener===f)delete this._events[c],this._events.removeListener&&this.emit("removeListener",c,f);
else if(b(a)){for(;0<d--;)if(a[d]===f||a[d].listener&&a[d].listener===f){h=d;break}if(0>h)return this;1===a.length?(a.length=0,delete this._events[c]):a.splice(h,1);this._events.removeListener&&this.emit("removeListener",c,f)}return this};c.prototype.removeAllListeners=function(b){var c;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[b]&&delete this._events[b],this;if(0===arguments.length){for(c in this._events)"removeListener"!==
c&&this.removeAllListeners(c);this.removeAllListeners("removeListener");this._events={};return this}c=this._events[b];if(l(c))this.removeListener(b,c);else for(;c.length;)this.removeListener(b,c[c.length-1]);delete this._events[b];return this};c.prototype.listeners=function(b){return this._events&&this._events[b]?l(this._events[b])?[this._events[b]]:this._events[b].slice():[]};c.listenerCount=function(b,c){return b._events&&b._events[c]?l(b._events[c])?1:b._events[c].length:0}},{}],48:[function(f,
m,g){m.exports="function"===typeof Object.create?function(c,f){c.super_=f;c.prototype=Object.create(f.prototype,{constructor:{value:c,enumerable:!1,writable:!0,configurable:!0}})}:function(c,f){c.super_=f;var b=function(){};b.prototype=f.prototype;c.prototype=new b;c.prototype.constructor=c}},{}],49:[function(f,m,g){m.exports=Array.isArray||function(c){return"[object Array]"==Object.prototype.toString.call(c)}},{}],50:[function(f,m,g){g.endianness=function(){return"LE"};g.hostname=function(){return"undefined"!==
typeof location?location.hostname:""};g.loadavg=function(){return[]};g.uptime=function(){return 0};g.freemem=function(){return Number.MAX_VALUE};g.totalmem=function(){return Number.MAX_VALUE};g.cpus=function(){return[]};g.type=function(){return"Browser"};g.release=function(){return"undefined"!==typeof navigator?navigator.appVersion:""};g.networkInterfaces=g.getNetworkInterfaces=function(){return{}};g.arch=function(){return"javascript"};g.platform=function(){return"browser"};g.tmpdir=g.tmpDir=function(){return"/tmp"};
g.EOL="\n"},{}],51:[function(f,m,g){function c(){a=!1;h.length?k=h.concat(k):d=-1;k.length&&l()}function l(){if(!a){var b=setTimeout(c);a=!0;for(var e=k.length;e;){h=k;for(k=[];++d<e;)h&&h[d].run();d=-1;e=k.length}h=null;a=!1;clearTimeout(b)}}function b(a,b){this.fun=a;this.array=b}function e(){}f=m.exports={};var k=[],a=!1,h,d=-1;f.nextTick=function(c){var d=Array(arguments.length-1);if(1<arguments.length)for(var e=1;e<arguments.length;e++)d[e-1]=arguments[e];k.push(new b(c,d));1!==k.length||a||
setTimeout(l,0)};b.prototype.run=function(){this.fun.apply(null,this.array)};f.title="browser";f.browser=!0;f.env={};f.argv=[];f.version="";f.versions={};f.on=e;f.addListener=e;f.once=e;f.off=e;f.removeListener=e;f.removeAllListeners=e;f.emit=e;f.binding=function(a){throw Error("process.binding is not supported");};f.cwd=function(){return"/"};f.chdir=function(a){throw Error("process.chdir is not supported");};f.umask=function(){return 0}},{}],52:[function(f,m,g){m.exports=f("./lib/_stream_duplex.js")},
{"./lib/_stream_duplex.js":53}],53:[function(f,m,g){(function(c){function g(c){if(!(this instanceof g))return new g(c);a.call(this,c);h.call(this,c);c&&!1===c.readable&&(this.readable=!1);c&&!1===c.writable&&(this.writable=!1);this.allowHalfOpen=!0;c&&!1===c.allowHalfOpen&&(this.allowHalfOpen=!1);this.once("end",b)}function b(){this.allowHalfOpen||this._writableState.ended||c.nextTick(this.end.bind(this))}m.exports=g;var e=Object.keys||function(a){var b=[],c;for(c in a)b.push(c);return b},k=f("core-util-is");
k.inherits=f("inherits");var a=f("./_stream_readable"),h=f("./_stream_writable");k.inherits(g,a);(function(a,b){for(var c=0,e=a.length;c<e;c++)b(a[c],c)})(e(h.prototype),function(a){g.prototype[a]||(g.prototype[a]=h.prototype[a])})}).call(this,f("_process"))},{"./_stream_readable":55,"./_stream_writable":57,_process:51,"core-util-is":58,inherits:48}],54:[function(f,m,g){function c(b){if(!(this instanceof c))return new c(b);l.call(this,b)}m.exports=c;var l=f("./_stream_transform");m=f("core-util-is");
m.inherits=f("inherits");m.inherits(c,l);c.prototype._transform=function(b,c,f){f(null,b)}},{"./_stream_transform":56,"core-util-is":58,inherits:48}],55:[function(f,m,g){(function(c){function g(a,b){var c=f("./_stream_duplex");a=a||{};var d=a.highWaterMark,e=a.objectMode?16:16384;this.highWaterMark=d||0===d?d:e;this.highWaterMark=~~this.highWaterMark;this.buffer=[];this.length=0;this.pipes=null;this.pipesCount=0;this.flowing=null;this.reading=this.endEmitted=this.ended=!1;this.sync=!0;this.readableListening=
this.emittedReadable=this.needReadable=!1;this.objectMode=!!a.objectMode;b instanceof c&&(this.objectMode=this.objectMode||!!a.readableObjectMode);this.defaultEncoding=a.defaultEncoding||"utf8";this.ranOut=!1;this.awaitDrain=0;this.readingMore=!1;this.encoding=this.decoder=null;a.encoding&&(y||(y=f("string_decoder/").StringDecoder),this.decoder=new y(a.encoding),this.encoding=a.encoding)}function b(a){f("./_stream_duplex");if(!(this instanceof b))return new b(a);this._readableState=new g(a,this);
this.readable=!0;F.call(this)}function e(b,c,e,h,f){var k;k=e;var g=null;B.isBuffer(k)||B.isString(k)||B.isNullOrUndefined(k)||c.objectMode||(g=new TypeError("Invalid non-string/buffer chunk"));(k=g)?b.emit("error",k):B.isNullOrUndefined(e)?(c.reading=!1,c.ended||(c.decoder&&!c.ended&&(e=c.decoder.end())&&e.length&&(c.buffer.push(e),c.length+=c.objectMode?1:e.length),c.ended=!0,a(b))):c.objectMode||e&&0<e.length?c.ended&&!f?(e=Error("stream.push() after EOF"),b.emit("error",e)):c.endEmitted&&f?(e=
Error("stream.unshift() after end event"),b.emit("error",e)):(!c.decoder||f||h||(e=c.decoder.write(e)),f||(c.reading=!1),c.flowing&&0===c.length&&!c.sync?(b.emit("data",e),b.read(0)):(c.length+=c.objectMode?1:e.length,f?c.buffer.unshift(e):c.buffer.push(e),c.needReadable&&a(b)),d(b,c)):f||(c.reading=!1);return!c.ended&&(c.needReadable||c.length<c.highWaterMark||0===c.length)}function k(a,b){if(0===b.length&&b.ended)return 0;if(b.objectMode)return 0===a?0:1;if(isNaN(a)||B.isNull(a))return b.flowing&&
b.buffer.length?b.buffer[0].length:b.length;if(0>=a)return 0;if(a>b.highWaterMark){var c=a;if(8388608<=c)c=8388608;else{c--;for(var d=1;32>d;d<<=1)c|=c>>d;c++}b.highWaterMark=c}if(a>b.length){if(b.ended)return b.length;b.needReadable=!0;return 0}return a}function a(a){var b=a._readableState;b.needReadable=!1;b.emittedReadable||(z("emitReadable",b.flowing),b.emittedReadable=!0,b.sync?c.nextTick(function(){h(a)}):h(a))}function h(a){z("emit readable");a.emit("readable");q(a)}function d(a,b){b.readingMore||
(b.readingMore=!0,c.nextTick(function(){for(var c=b.length;!b.reading&&!b.flowing&&!b.ended&&b.length<b.highWaterMark&&(z("maybeReadMore read 0"),a.read(0),c!==b.length);)c=b.length;b.readingMore=!1}))}function t(a){return function(){var b=a._readableState;z("pipeOnDrain",b.awaitDrain);b.awaitDrain&&b.awaitDrain--;0===b.awaitDrain&&A.listenerCount(a,"data")&&(b.flowing=!0,q(a))}}function r(a,b){b.resumeScheduled||(b.resumeScheduled=!0,c.nextTick(function(){b.resumeScheduled=!1;a.emit("resume");q(a);
b.flowing&&!b.reading&&a.read(0)}))}function q(a){var b=a._readableState;z("flow",b.flowing);if(b.flowing){do var c=a.read();while(null!==c&&b.flowing)}}function x(a,b){var c=b.buffer,d=b.length,e=!!b.decoder,h=!!b.objectMode;if(0===c.length)return null;if(0===d)d=null;else if(h)d=c.shift();else if(!a||a>=d)d=e?c.join(""):w.concat(c,d),c.length=0;else if(a<c[0].length)h=c[0],d=h.slice(0,a),c[0]=h.slice(a);else if(a===c[0].length)d=c.shift();else for(var d=e?"":new w(a),f=0,k=0,g=c.length;k<g&&f<a;k++){var h=
c[0],l=Math.min(a-f,h.length);e?d+=h.slice(0,l):h.copy(d,f,0,l);l<h.length?c[0]=h.slice(l):c.shift();f+=l}return d}function u(a){var b=a._readableState;if(0<b.length)throw Error("endReadable called on non-empty stream");b.endEmitted||(b.ended=!0,c.nextTick(function(){b.endEmitted||0!==b.length||(b.endEmitted=!0,a.readable=!1,a.emit("end"))}))}function D(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)}m.exports=b;var E=f("isarray"),w=f("buffer").Buffer;b.ReadableState=g;var A=f("events").EventEmitter;
A.listenerCount||(A.listenerCount=function(a,b){return a.listeners(b).length});var F=f("stream"),B=f("core-util-is");B.inherits=f("inherits");var y,z=f("util"),z=z&&z.debuglog?z.debuglog("stream"):function(){};B.inherits(b,F);b.prototype.push=function(a,b){var c=this._readableState;B.isString(a)&&!c.objectMode&&(b=b||c.defaultEncoding,b!==c.encoding&&(a=new w(a,b),b=""));return e(this,c,a,b,!1)};b.prototype.unshift=function(a){return e(this,this._readableState,a,"",!0)};b.prototype.setEncoding=function(a){y||
(y=f("string_decoder/").StringDecoder);this._readableState.decoder=new y(a);this._readableState.encoding=a;return this};b.prototype.read=function(b){z("read",b);var c=this._readableState,d=b;if(!B.isNumber(b)||0<b)c.emittedReadable=!1;if(0===b&&c.needReadable&&(c.length>=c.highWaterMark||c.ended))return z("read: emitReadable",c.length,c.ended),0===c.length&&c.ended?u(this):a(this),null;b=k(b,c);if(0===b&&c.ended)return 0===c.length&&u(this),null;var e=c.needReadable;z("need readable",e);if(0===c.length||
c.length-b<c.highWaterMark)e=!0,z("length less than watermark",e);if(c.ended||c.reading)e=!1,z("reading or ended",e);e&&(z("do read"),c.reading=!0,c.sync=!0,0===c.length&&(c.needReadable=!0),this._read(c.highWaterMark),c.sync=!1);e&&!c.reading&&(b=k(d,c));e=0<b?x(b,c):null;B.isNull(e)&&(c.needReadable=!0,b=0);c.length-=b;0!==c.length||c.ended||(c.needReadable=!0);d!==b&&c.ended&&0===c.length&&u(this);B.isNull(e)||this.emit("data",e);return e};b.prototype._read=function(a){this.emit("error",Error("not implemented"))};
b.prototype.pipe=function(a,b){function d(a){z("onunpipe");a===y&&h()}function e(){z("onend");a.end()}function h(){z("cleanup");a.removeListener("close",g);a.removeListener("finish",l);a.removeListener("drain",B);a.removeListener("error",k);a.removeListener("unpipe",d);y.removeListener("end",e);y.removeListener("end",h);y.removeListener("data",f);!r.awaitDrain||a._writableState&&!a._writableState.needDrain||B()}function f(b){z("ondata");!1===a.write(b)&&(z("false write response, pause",y._readableState.awaitDrain),
y._readableState.awaitDrain++,y.pause())}function k(b){z("onerror",b);m();a.removeListener("error",k);0===A.listenerCount(a,"error")&&a.emit("error",b)}function g(){a.removeListener("finish",l);m()}function l(){z("onfinish");a.removeListener("close",g);m()}function m(){z("unpipe");y.unpipe(a)}var y=this,r=this._readableState;switch(r.pipesCount){case 0:r.pipes=a;break;case 1:r.pipes=[r.pipes,a];break;default:r.pipes.push(a)}r.pipesCount+=1;z("pipe count=%d opts=%j",r.pipesCount,b);var u=b&&!1===b.end||
a===c.stdout||a===c.stderr?h:e;if(r.endEmitted)c.nextTick(u);else y.once("end",u);a.on("unpipe",d);var B=t(y);a.on("drain",B);y.on("data",f);if(a._events&&a._events.error)E(a._events.error)?a._events.error.unshift(k):a._events.error=[k,a._events.error];else a.on("error",k);a.once("close",g);a.once("finish",l);a.emit("pipe",y);r.flowing||(z("pipe resume"),y.resume());return a};b.prototype.unpipe=function(a){var b=this._readableState;if(0===b.pipesCount)return this;if(1===b.pipesCount){if(a&&a!==b.pipes)return this;
a||(a=b.pipes);b.pipes=null;b.pipesCount=0;b.flowing=!1;a&&a.emit("unpipe",this);return this}if(!a){a=b.pipes;var c=b.pipesCount;b.pipes=null;b.pipesCount=0;b.flowing=!1;for(var d=0;d<c;d++)a[d].emit("unpipe",this);return this}a:{for(var d=b.pipes,c=0,e=d.length;c<e;c++)if(d[c]===a){d=c;break a}d=-1}if(-1===d)return this;b.pipes.splice(d,1);--b.pipesCount;1===b.pipesCount&&(b.pipes=b.pipes[0]);a.emit("unpipe",this);return this};b.prototype.on=function(b,d){var e=F.prototype.on.call(this,b,d);"data"===
b&&!1!==this._readableState.flowing&&this.resume();if("readable"===b&&this.readable){var h=this._readableState;if(!h.readableListening)if(h.readableListening=!0,h.emittedReadable=!1,h.needReadable=!0,h.reading)h.length&&a(this,h);else{var f=this;c.nextTick(function(){z("readable nexttick read 0");f.read(0)})}}return e};b.prototype.addListener=b.prototype.on;b.prototype.resume=function(){var a=this._readableState;a.flowing||(z("resume"),a.flowing=!0,a.reading||(z("resume read 0"),this.read(0)),r(this,
a));return this};b.prototype.pause=function(){z("call pause flowing=%j",this._readableState.flowing);!1!==this._readableState.flowing&&(z("pause"),this._readableState.flowing=!1,this.emit("pause"));return this};b.prototype.wrap=function(a){var b=this._readableState,c=!1,d=this;a.on("end",function(){z("wrapped end");if(b.decoder&&!b.ended){var a=b.decoder.end();a&&a.length&&d.push(a)}d.push(null)});a.on("data",function(e){z("wrapped data");b.decoder&&(e=b.decoder.write(e));e&&(b.objectMode||e.length)&&
!d.push(e)&&(c=!0,a.pause())});for(var e in a)B.isFunction(a[e])&&B.isUndefined(this[e])&&(this[e]=function(b){return function(){return a[b].apply(a,arguments)}}(e));D(["error","close","destroy","pause","resume"],function(b){a.on(b,d.emit.bind(d,b))});d._read=function(b){z("wrapped _read",b);c&&(c=!1,a.resume())};return d};b._fromList=x}).call(this,f("_process"))},{"./_stream_duplex":53,_process:51,buffer:43,"core-util-is":58,events:47,inherits:48,isarray:49,stream:63,"string_decoder/":64,util:42}],
56:[function(f,m,g){function c(a,b){this.afterTransform=function(a,c){var e;e=b._transformState;e.transforming=!1;var f=e.writecb;f?(e.writechunk=null,e.writecb=null,k.isNullOrUndefined(c)||b.push(c),f&&f(a),e=b._readableState,e.reading=!1,(e.needReadable||e.length<e.highWaterMark)&&b._read(e.highWaterMark),e=void 0):e=b.emit("error",Error("no writecb in Transform class"));return e};this.transforming=this.needTransform=!1;this.writechunk=this.writecb=null}function l(a){if(!(this instanceof l))return new l(a);
e.call(this,a);this._transformState=new c(a,this);var h=this;this._readableState.needReadable=!0;this._readableState.sync=!1;this.once("prefinish",function(){k.isFunction(this._flush)?this._flush(function(a){b(h,a)}):b(h)})}function b(a,b){if(b)return a.emit("error",b);var c=a._transformState;if(a._writableState.length)throw Error("calling transform done when ws.length != 0");if(c.transforming)throw Error("calling transform done when still transforming");return a.push(null)}m.exports=l;var e=f("./_stream_duplex"),
k=f("core-util-is");k.inherits=f("inherits");k.inherits(l,e);l.prototype.push=function(a,b){this._transformState.needTransform=!1;return e.prototype.push.call(this,a,b)};l.prototype._transform=function(a,b,c){throw Error("not implemented");};l.prototype._write=function(a,b,c){var e=this._transformState;e.writecb=c;e.writechunk=a;e.writeencoding=b;e.transforming||(a=this._readableState,(e.needTransform||a.needReadable||a.length<a.highWaterMark)&&this._read(a.highWaterMark))};l.prototype._read=function(a){a=
this._transformState;k.isNull(a.writechunk)||!a.writecb||a.transforming?a.needTransform=!0:(a.transforming=!0,this._transform(a.writechunk,a.writeencoding,a.afterTransform))}},{"./_stream_duplex":53,"core-util-is":58,inherits:48}],57:[function(f,m,g){(function(c){function g(a,b,c){this.chunk=a;this.encoding=b;this.callback=c}function b(a,b){var c=f("./_stream_duplex");a=a||{};var d=a.highWaterMark,e=a.objectMode?16:16384;this.highWaterMark=d||0===d?d:e;this.objectMode=!!a.objectMode;b instanceof c&&
(this.objectMode=this.objectMode||!!a.writableObjectMode);this.highWaterMark=~~this.highWaterMark;this.finished=this.ended=this.ending=this.needDrain=!1;this.decodeStrings=!1!==a.decodeStrings;this.defaultEncoding=a.defaultEncoding||"utf8";this.length=0;this.writing=!1;this.corked=0;this.sync=!0;this.bufferProcessing=!1;this.onwrite=function(a){q(b,a)};this.writecb=null;this.writelen=0;this.buffer=[];this.pendingcb=0;this.errorEmitted=this.prefinished=!1}function e(a){var c=f("./_stream_duplex");
if(!(this instanceof e||this instanceof c))return new e(a);this._writableState=new b(a,this);this.writable=!0;A.call(this)}function k(a,b,d){var e=Error("write after end");a.emit("error",e);c.nextTick(function(){d(e)})}function a(a,b,d,e){var h=!0;if(!(w.isBuffer(d)||w.isString(d)||w.isNullOrUndefined(d)||b.objectMode)){var f=new TypeError("Invalid non-string/buffer chunk");a.emit("error",f);c.nextTick(function(){e(f)});h=!1}return h}function h(a,b,c,e,h){var f=e;!b.objectMode&&!1!==b.decodeStrings&&
w.isString(c)&&(c=new E(c,f));w.isBuffer(c)&&(e="buffer");f=b.objectMode?1:c.length;b.length+=f;var k=b.length<b.highWaterMark;k||(b.needDrain=!0);b.writing||b.corked?b.buffer.push(new g(c,e,h)):d(a,b,!1,f,c,e,h);return k}function d(a,b,c,d,e,h,f){b.writelen=d;b.writecb=f;b.writing=!0;b.sync=!0;c?a._writev(e,b.onwrite):a._write(e,h,b.onwrite);b.sync=!1}function t(a,b,d,e,h){d?c.nextTick(function(){b.pendingcb--;h(e)}):(b.pendingcb--,h(e));a._writableState.errorEmitted=!0;a.emit("error",e)}function r(a){a.writing=
!1;a.writecb=null;a.length-=a.writelen;a.writelen=0}function q(a,b){var d=a._writableState,e=d.sync,h=d.writecb;r(d);if(b)t(a,d,e,b,h);else{var f=d.ending&&0===d.length&&!d.finished&&!d.writing;f||d.corked||d.bufferProcessing||!d.buffer.length||u(a,d);e?c.nextTick(function(){x(a,d,f,h)}):x(a,d,f,h)}}function x(a,b,c,d){!c&&0===b.length&&b.needDrain&&(b.needDrain=!1,a.emit("drain"));b.pendingcb--;d();D(a,b)}function u(a,b){b.bufferProcessing=!0;if(a._writev&&1<b.buffer.length){for(var c=[],e=0;e<b.buffer.length;e++)c.push(b.buffer[e].callback);
b.pendingcb++;d(a,b,!0,b.length,b.buffer,"",function(a){for(var d=0;d<c.length;d++)b.pendingcb--,c[d](a)});b.buffer=[]}else{for(e=0;e<b.buffer.length;e++){var h=b.buffer[e],f=h.chunk;d(a,b,!1,b.objectMode?1:f.length,f,h.encoding,h.callback);if(b.writing){e++;break}}e<b.buffer.length?b.buffer=b.buffer.slice(e):b.buffer.length=0}b.bufferProcessing=!1}function D(a,b){var c=b.ending&&0===b.length&&!b.finished&&!b.writing;c&&(0===b.pendingcb?(b.prefinished||(b.prefinished=!0,a.emit("prefinish")),b.finished=
!0,a.emit("finish")):b.prefinished||(b.prefinished=!0,a.emit("prefinish")));return c}m.exports=e;var E=f("buffer").Buffer;e.WritableState=b;var w=f("core-util-is");w.inherits=f("inherits");var A=f("stream");w.inherits(e,A);e.prototype.pipe=function(){this.emit("error",Error("Cannot pipe. Not readable."))};e.prototype.write=function(b,c,d){var e=this._writableState,f=!1;w.isFunction(c)&&(d=c,c=null);w.isBuffer(b)?c="buffer":c||(c=e.defaultEncoding);w.isFunction(d)||(d=function(){});e.ended?k(this,
e,d):a(this,e,b,d)&&(e.pendingcb++,f=h(this,e,b,c,d));return f};e.prototype.cork=function(){this._writableState.corked++};e.prototype.uncork=function(){var a=this._writableState;a.corked&&(a.corked--,a.writing||a.corked||a.finished||a.bufferProcessing||!a.buffer.length||u(this,a))};e.prototype._write=function(a,b,c){c(Error("not implemented"))};e.prototype._writev=null;e.prototype.end=function(a,b,d){var e=this._writableState;w.isFunction(a)?(d=a,b=a=null):w.isFunction(b)&&(d=b,b=null);w.isNullOrUndefined(a)||
this.write(a,b);e.corked&&(e.corked=1,this.uncork());if(!e.ending&&!e.finished){a=d;e.ending=!0;D(this,e);if(a)if(e.finished)c.nextTick(a);else this.once("finish",a);e.ended=!0}}}).call(this,f("_process"))},{"./_stream_duplex":53,_process:51,buffer:43,"core-util-is":58,inherits:48,stream:63}],58:[function(f,m,g){(function(c){function f(b){return"object"===typeof b&&null!==b}g.isArray=function(b){return Array.isArray(b)};g.isBoolean=function(b){return"boolean"===typeof b};g.isNull=function(b){return null===
b};g.isNullOrUndefined=function(b){return null==b};g.isNumber=function(b){return"number"===typeof b};g.isString=function(b){return"string"===typeof b};g.isSymbol=function(b){return"symbol"===typeof b};g.isUndefined=function(b){return void 0===b};g.isRegExp=function(b){return f(b)&&"[object RegExp]"===Object.prototype.toString.call(b)};g.isObject=f;g.isDate=function(b){return f(b)&&"[object Date]"===Object.prototype.toString.call(b)};g.isError=function(b){return f(b)&&("[object Error]"===Object.prototype.toString.call(b)||
b instanceof Error)};g.isFunction=function(b){return"function"===typeof b};g.isPrimitive=function(b){return null===b||"boolean"===typeof b||"number"===typeof b||"string"===typeof b||"symbol"===typeof b||"undefined"===typeof b};g.isBuffer=function(b){return c.isBuffer(b)}}).call(this,f("buffer").Buffer)},{buffer:43}],59:[function(f,m,g){m.exports=f("./lib/_stream_passthrough.js")},{"./lib/_stream_passthrough.js":54}],60:[function(f,m,g){g=m.exports=f("./lib/_stream_readable.js");g.Stream=f("stream");
g.Readable=g;g.Writable=f("./lib/_stream_writable.js");g.Duplex=f("./lib/_stream_duplex.js");g.Transform=f("./lib/_stream_transform.js");g.PassThrough=f("./lib/_stream_passthrough.js")},{"./lib/_stream_duplex.js":53,"./lib/_stream_passthrough.js":54,"./lib/_stream_readable.js":55,"./lib/_stream_transform.js":56,"./lib/_stream_writable.js":57,stream:63}],61:[function(f,m,g){m.exports=f("./lib/_stream_transform.js")},{"./lib/_stream_transform.js":56}],62:[function(f,m,g){m.exports=f("./lib/_stream_writable.js")},
{"./lib/_stream_writable.js":57}],63:[function(f,m,g){function c(){l.call(this)}m.exports=c;var l=f("events").EventEmitter;f("inherits")(c,l);c.Readable=f("readable-stream/readable.js");c.Writable=f("readable-stream/writable.js");c.Duplex=f("readable-stream/duplex.js");c.Transform=f("readable-stream/transform.js");c.PassThrough=f("readable-stream/passthrough.js");c.Stream=c;c.prototype.pipe=function(b,c){function f(a){b.writable&&!1===b.write(a)&&q.pause&&q.pause()}function a(){q.readable&&q.resume&&
q.resume()}function h(){x||(x=!0,b.end())}function d(){x||(x=!0,"function"===typeof b.destroy&&b.destroy())}function g(a){m();if(0===l.listenerCount(this,"error"))throw a;}function m(){q.removeListener("data",f);b.removeListener("drain",a);q.removeListener("end",h);q.removeListener("close",d);q.removeListener("error",g);b.removeListener("error",g);q.removeListener("end",m);q.removeListener("close",m);b.removeListener("close",m)}var q=this;q.on("data",f);b.on("drain",a);b._isStdio||c&&!1===c.end||
(q.on("end",h),q.on("close",d));var x=!1;q.on("error",g);b.on("error",g);q.on("end",m);q.on("close",m);b.on("close",m);b.emit("pipe",q);return b}},{events:47,inherits:48,"readable-stream/duplex.js":52,"readable-stream/passthrough.js":59,"readable-stream/readable.js":60,"readable-stream/transform.js":61,"readable-stream/writable.js":62}],64:[function(f,m,g){function c(a){return a.toString(this.encoding)}function l(a){this.charLength=(this.charReceived=a.length%2)?2:0}function b(a){this.charLength=
(this.charReceived=a.length%3)?3:0}var e=f("buffer").Buffer,k=e.isEncoding||function(a){switch(a&&a.toLowerCase()){case "hex":case "utf8":case "utf-8":case "ascii":case "binary":case "base64":case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":case "raw":return!0;default:return!1}};f=g.StringDecoder=function(a){this.encoding=(a||"utf8").toLowerCase().replace(/[-_]/,"");if(a&&!k(a))throw Error("Unknown encoding: "+a);switch(this.encoding){case "utf8":this.surrogateSize=3;break;case "ucs2":case "utf16le":this.surrogateSize=
2;this.detectIncompleteChar=l;break;case "base64":this.surrogateSize=3;this.detectIncompleteChar=b;break;default:this.write=c;return}this.charBuffer=new e(6);this.charLength=this.charReceived=0};f.prototype.write=function(a){for(var b="";this.charLength;){b=a.length>=this.charLength-this.charReceived?this.charLength-this.charReceived:a.length;a.copy(this.charBuffer,this.charReceived,0,b);this.charReceived+=b;if(this.charReceived<this.charLength)return"";a=a.slice(b,a.length);var b=this.charBuffer.slice(0,
this.charLength).toString(this.encoding),c=b.charCodeAt(b.length-1);if(55296<=c&&56319>=c)this.charLength+=this.surrogateSize,b="";else{this.charReceived=this.charLength=0;if(0===a.length)return b;break}}this.detectIncompleteChar(a);var e=a.length;this.charLength&&(a.copy(this.charBuffer,0,a.length-this.charReceived,e),e-=this.charReceived);b+=a.toString(this.encoding,0,e);e=b.length-1;c=b.charCodeAt(e);return 55296<=c&&56319>=c?(c=this.surrogateSize,this.charLength+=c,this.charReceived+=c,this.charBuffer.copy(this.charBuffer,
c,0,c),a.copy(this.charBuffer,0,0,c),b.substring(0,e)):b};f.prototype.detectIncompleteChar=function(a){for(var b=3<=a.length?3:a.length;0<b;b--){var c=a[a.length-b];if(1==b&&6==c>>5){this.charLength=2;break}if(2>=b&&14==c>>4){this.charLength=3;break}if(3>=b&&30==c>>3){this.charLength=4;break}}this.charReceived=b};f.prototype.end=function(a){var b="";a&&a.length&&(b=this.write(a));this.charReceived&&(a=this.encoding,b+=this.charBuffer.slice(0,this.charReceived).toString(a));return b}},{buffer:43}],
65:[function(f,m,g){m.exports=function(c){return c&&"object"===typeof c&&"function"===typeof c.copy&&"function"===typeof c.fill&&"function"===typeof c.readUInt8}},{}],66:[function(f,m,g){(function(c,l){function b(a,b){var c={seen:[],stylize:k};3<=arguments.length&&(c.depth=arguments[2]);4<=arguments.length&&(c.colors=arguments[3]);D(b)?c.showHidden=b:b&&g._extend(c,b);A(c.showHidden)&&(c.showHidden=!1);A(c.depth)&&(c.depth=2);A(c.colors)&&(c.colors=!1);A(c.customInspect)&&(c.customInspect=!0);c.colors&&
(c.stylize=e);return h(c,a,c.depth)}function e(a,c){var d=b.styles[c];return d?"\u001b["+b.colors[d][0]+"m"+a+"\u001b["+b.colors[d][1]+"m":a}function k(a,b){return a}function a(a){var b={};a.forEach(function(a,c){b[a]=!0});return b}function h(b,c,e){if(b.customInspect&&c&&n(c.inspect)&&c.inspect!==g.inspect&&(!c.constructor||c.constructor.prototype!==c)){var f=c.inspect(e,b);w(f)||(f=h(b,f,e));return f}if(f=d(b,c))return f;var k=Object.keys(c),l=a(k);b.showHidden&&(k=Object.getOwnPropertyNames(c));
if(z(c)&&(0<=k.indexOf("message")||0<=k.indexOf("description")))return m(c);if(0===k.length){if(n(c))return b.stylize("[Function"+(c.name?": "+c.name:"")+"]","special");if(F(c))return b.stylize(RegExp.prototype.toString.call(c),"regexp");if(y(c))return b.stylize(Date.prototype.toString.call(c),"date");if(z(c))return m(c)}var f="",G=!1,v=["{","}"];u(c)&&(G=!0,v=["[","]"]);n(c)&&(f=" [Function"+(c.name?": "+c.name:"")+"]");F(c)&&(f=" "+RegExp.prototype.toString.call(c));y(c)&&(f=" "+Date.prototype.toUTCString.call(c));
z(c)&&(f=" "+m(c));if(0===k.length&&(!G||0==c.length))return v[0]+f+v[1];if(0>e)return F(c)?b.stylize(RegExp.prototype.toString.call(c),"regexp"):b.stylize("[Object]","special");b.seen.push(c);k=G?r(b,c,e,l,k):k.map(function(a){return q(b,c,e,l,a,G)});b.seen.pop();return x(k,f,v)}function d(a,b){if(A(b))return a.stylize("undefined","undefined");if(w(b)){var c="'"+JSON.stringify(b).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return a.stylize(c,"string")}if(E(b))return a.stylize(""+
b,"number");if(D(b))return a.stylize(""+b,"boolean");if(null===b)return a.stylize("null","null")}function m(a){return"["+Error.prototype.toString.call(a)+"]"}function r(a,b,c,d,e){for(var h=[],f=0,k=b.length;f<k;++f)Object.prototype.hasOwnProperty.call(b,String(f))?h.push(q(a,b,c,d,String(f),!0)):h.push("");e.forEach(function(e){e.match(/^\d+$/)||h.push(q(a,b,c,d,e,!0))});return h}function q(a,b,c,d,e,f){var k,g;b=Object.getOwnPropertyDescriptor(b,e)||{value:b[e]};b.get?g=b.set?a.stylize("[Getter/Setter]",
"special"):a.stylize("[Getter]","special"):b.set&&(g=a.stylize("[Setter]","special"));Object.prototype.hasOwnProperty.call(d,e)||(k="["+e+"]");g||(0>a.seen.indexOf(b.value)?(g=null===c?h(a,b.value,null):h(a,b.value,c-1),-1<g.indexOf("\n")&&(g=f?g.split("\n").map(function(a){return" "+a}).join("\n").substr(2):"\n"+g.split("\n").map(function(a){return" "+a}).join("\n"))):g=a.stylize("[Circular]","special"));if(A(k)){if(f&&e.match(/^\d+$/))return g;k=JSON.stringify(""+e);k.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?
(k=k.substr(1,k.length-2),k=a.stylize(k,"name")):(k=k.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),k=a.stylize(k,"string"))}return k+": "+g}function x(a,b,c){var d=0;return 60<a.reduce(function(a,b){d++;0<=b.indexOf("\n")&&d++;return a+b.replace(/\u001b\[\d\d?m/g,"").length+1},0)?c[0]+(""===b?"":b+"\n ")+" "+a.join(",\n ")+" "+c[1]:c[0]+b+" "+a.join(", ")+" "+c[1]}function u(a){return Array.isArray(a)}function D(a){return"boolean"===typeof a}function E(a){return"number"===typeof a}
function w(a){return"string"===typeof a}function A(a){return void 0===a}function F(a){return B(a)&&"[object RegExp]"===Object.prototype.toString.call(a)}function B(a){return"object"===typeof a&&null!==a}function y(a){return B(a)&&"[object Date]"===Object.prototype.toString.call(a)}function z(a){return B(a)&&("[object Error]"===Object.prototype.toString.call(a)||a instanceof Error)}function n(a){return"function"===typeof a}function p(a){return 10>a?"0"+a.toString(10):a.toString(10)}function G(){var a=
new Date,b=[p(a.getHours()),p(a.getMinutes()),p(a.getSeconds())].join(":");return[a.getDate(),C[a.getMonth()],b].join(" ")}var J=/%[sdj%]/g;g.format=function(a){if(!w(a)){for(var c=[],d=0;d<arguments.length;d++)c.push(b(arguments[d]));return c.join(" ")}for(var d=1,e=arguments,h=e.length,c=String(a).replace(J,function(a){if("%%"===a)return"%";if(d>=h)return a;switch(a){case "%s":return String(e[d++]);case "%d":return Number(e[d++]);case "%j":try{return JSON.stringify(e[d++])}catch(b){return"[Circular]"}default:return a}}),
f=e[d];d<h;f=e[++d])c=null!==f&&B(f)?c+(" "+b(f)):c+(" "+f);return c};g.deprecate=function(a,b){if(A(l.process))return function(){return g.deprecate(a,b).apply(this,arguments)};if(!0===c.noDeprecation)return a;var d=!1;return function(){if(!d){if(c.throwDeprecation)throw Error(b);c.traceDeprecation?console.trace(b):console.error(b);d=!0}return a.apply(this,arguments)}};var v={},K;g.debuglog=function(a){A(K)&&(K=c.env.NODE_DEBUG||"");a=a.toUpperCase();if(!v[a])if((new RegExp("\\b"+a+"\\b","i")).test(K)){var b=
c.pid;v[a]=function(){var c=g.format.apply(g,arguments);console.error("%s %d: %s",a,b,c)}}else v[a]=function(){};return v[a]};g.inspect=b;b.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]};b.styles={special:"cyan",number:"yellow","boolean":"yellow",undefined:"grey","null":"bold",string:"green",date:"magenta",regexp:"red"};g.isArray=u;g.isBoolean=D;g.isNull=
function(a){return null===a};g.isNullOrUndefined=function(a){return null==a};g.isNumber=E;g.isString=w;g.isSymbol=function(a){return"symbol"===typeof a};g.isUndefined=A;g.isRegExp=F;g.isObject=B;g.isDate=y;g.isError=z;g.isFunction=n;g.isPrimitive=function(a){return null===a||"boolean"===typeof a||"number"===typeof a||"string"===typeof a||"symbol"===typeof a||"undefined"===typeof a};g.isBuffer=f("./support/isBuffer");var C="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ");g.log=function(){console.log("%s - %s",
G(),g.format.apply(g,arguments))};g.inherits=f("inherits");g._extend=function(a,b){if(!b||!B(b))return a;for(var c=Object.keys(b),d=c.length;d--;)a[c[d]]=b[c[d]];return a}}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./support/isBuffer":65,_process:51,inherits:48}],67:[function(f,m,g){(function(c,f){function b(a,b,c){if(Array.prototype.map)return Array.prototype.map.call(a,b,c);for(var d=Array(a.length),e=0,f=
a.length;e<f;e++)d[e]=b.call(c,a[e],e,a);return d}function e(a){for(var b=[],c=0;c<a.length;c++)a[c]&&b.push(a[c]);return b}function k(a){a=a.replace(/&/g,"&amp;");a=a.replace(/</g,"&lt;");a=a.replace(/>/g,"&gt;");return a=a.replace(/"/g,"&quot;")}function a(b,c,d){c=c||[];d=d||[];var e;for(e=0;e<c.length;e+=1)if(c[e]===b)return d[e];var f;if("[object Array]"===g.call(b)){c.push(b);f=Array(b.length);d.push(f);for(e=0;e<b.length;e+=1)f[e]=a(b[e],c,d);c.pop();d.pop()}else if("object"===typeof b&&null!==
b){c.push(b);f={};d.push(f);var h=[],k;for(k in b)h.push(k);h.sort();for(e=0;e<h.length;e+=1)k=h[e],f[k]=a(b[k],c,d);c.pop();d.pop()}else f=b;return f}function h(a,c,d,e){for(var f=0,h=a.length,k=0,g=0;f<h;f++){var l=a[f];if(l.removed)l.value=d.slice(g,g+l.count).join(""),g+=l.count,f&&a[f-1].added&&(l=a[f-1],a[f-1]=a[f],a[f]=l);else{if(!l.added&&e){var m=c.slice(k,k+l.count),m=b(m,function(a,b){var c=d[g+b];return c.length>a.length?c:a});l.value=m.join("")}else l.value=c.slice(k,k+l.count).join("");
k+=l.count;l.added||(g+=l.count)}}return a}function d(a){this.ignoreWhitespace=a}var g=Object.prototype.toString;d.prototype={diff:function(a,b,c){function d(a){return c?(setTimeout(function(){c(f,a)},0),!0):a}function e(){for(var c=-1*t;c<=t;c+=2){var G;G=r[c-1];var n=r[c+1],u=(n?n.newPos:0)-c;G&&(r[c-1]=f);var D=G&&G.newPos+1<g,u=n&&0<=u&&u<m;if(D||u){!D||u&&G.newPos<n.newPos?(G={newPos:n.newPos,components:n.components.slice(0)},k.pushComponent(G.components,f,!0)):(G.newPos++,k.pushComponent(G.components,
!0,f));u=k.extractCommon(G,b,a,c);if(G.newPos+1>=g&&u+1>=m)return d(h(G.components,b,a,k.useLongestToken));r[c]=G}else r[c]=f}t++}var k=this;if(b===a)return d([{value:b}]);if(!b)return d([{value:a,removed:!0}]);if(!a)return d([{value:b,added:!0}]);b=this.tokenize(b);a=this.tokenize(a);var g=b.length,m=a.length,t=1,u=g+m,r=[{newPos:-1,components:[]}],D=this.extractCommon(r[0],b,a,0);if(r[0].newPos+1>=g&&D+1>=m)return d([{value:b.join("")}]);if(c)(function M(){setTimeout(function(){if(t>u)return c();
e()||M()},0)})();else for(;t<=u;)if(D=e())return D},pushComponent:function(a,b,c){var d=a[a.length-1];d&&d.added===b&&d.removed===c?a[a.length-1]={count:d.count+1,added:b,removed:c}:a.push({count:1,added:b,removed:c})},extractCommon:function(a,b,c,d){var e=b.length,f=c.length,h=a.newPos;d=h-d;for(var k=0;h+1<e&&d+1<f&&this.equals(b[h+1],c[d+1]);)h++,d++,k++;k&&a.components.push({count:k});a.newPos=h;return d},equals:function(a,b){var c=/\S/;return a===b||this.ignoreWhitespace&&!c.test(a)&&!c.test(b)},
tokenize:function(a){return a.split("")}};var r=new d,q=new d(!0),x=new d;q.tokenize=x.tokenize=function(a){return e(a.split(/(\s+|\b)/))};var u=new d(!0);u.tokenize=function(a){return e(a.split(/([{}:;,]|\s+)/))};var D=new d,E=new d;E.ignoreTrim=!0;D.tokenize=E.tokenize=function(a){var b=[];a=a.split(/^/m);for(var c=0;c<a.length;c++){var d=a[c],e=a[c-1],e=e&&e[e.length-1];"\n"===d&&"\r"===e?b[b.length-1]=b[b.length-1].slice(0,-1)+"\r\n":(this.ignoreTrim&&(d=d.trim(),c<a.length-1&&(d+="\n")),b.push(d))}return b};
var w=new d;w.tokenize=function(a){var b=[];a=a.split(/(\n|\r\n)/);a[a.length-1]||a.pop();for(var c=0;c<a.length;c++){var d=a[c];c%2?b[b.length-1]+=d:b.push(d)}return b};var A=new d;A.tokenize=function(a){return e(a.split(/(\S.+?[.!?])(?=\s+|$)/))};var F=new d;F.useLongestToken=!0;F.tokenize=D.tokenize;F.equals=function(a,b){return D.equals(a.replace(/,([\r\n])/g,"$1"),b.replace(/,([\r\n])/g,"$1"))};var B={Diff:d,diffChars:function(a,b,c){return r.diff(a,b,c)},diffWords:function(a,b,c){return q.diff(a,
b,c)},diffWordsWithSpace:function(a,b,c){return x.diff(a,b,c)},diffLines:function(a,b,c){return D.diff(a,b,c)},diffTrimmedLines:function(a,b,c){return E.diff(a,b,c)},diffSentences:function(a,b,c){return A.diff(a,b,c)},diffCss:function(a,b,c){return u.diff(a,b,c)},diffJson:function(b,c,d){return F.diff("string"===typeof b?b:JSON.stringify(a(b),f," "),"string"===typeof c?c:JSON.stringify(a(c),f," "),d)},createTwoFilesPatch:function(a,c,d,e,f,h){function k(a){return b(a,function(a){return" "+a})}function g(a,
b,c){var d=m[m.length-2],e=b===m.length-2;b=b===m.length-3&&c.added!==d.added;/\n$/.test(c.value)||!e&&!b||a.push("\\ No newline at end of file")}var l=[];a==c&&l.push("Index: "+a);l.push("===================================================================");l.push("--- "+a+("undefined"===typeof f?"":"\t"+f));l.push("+++ "+c+("undefined"===typeof h?"":"\t"+h));var m=w.diff(d,e);m.push({value:"",lines:[]});c=a=0;d=[];f=e=1;for(h=0;h<m.length;h++){var t=m[h],u=t.lines||t.value.replace(/\n$/,"").split("\n");
t.lines=u;if(t.added||t.removed){if(!a){var r=m[h-1];a=e;c=f;r&&(d=k(r.lines.slice(-4)),a-=d.length,c-=d.length)}d.push.apply(d,b(u,function(a){return(t.added?"+":"-")+a}));g(d,h,t);t.added?f+=u.length:e+=u.length}else a&&(8>=u.length&&h<m.length-2?d.push.apply(d,k(u)):(r=Math.min(u.length,4),l.push("@@ -"+a+","+(e-a+r)+" +"+c+","+(f-c+r)+" @@"),l.push.apply(l,d),l.push.apply(l,k(u.slice(0,r))),4>=u.length&&g(l,h,t),c=a=0,d=[])),e+=u.length,f+=u.length}return l.join("\n")+"\n"},createPatch:function(a,
b,c,d,e){return B.createTwoFilesPatch(a,a,b,c,d,e)},applyPatch:function(a,b){for(var c=b.split("\n"),d=[],e=0,f=!1,h=!1;e<c.length&&!/^@@/.test(c[e]);)e++;for(;e<c.length;e++)if("@"===c[e][0]){var k=c[e].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);d.unshift({start:k[3],oldlength:+k[2],removed:[],newlength:k[4],added:[]})}else"+"===c[e][0]?d[0].added.push(c[e].substr(1)):"-"===c[e][0]?d[0].removed.push(c[e].substr(1)):" "===c[e][0]?(d[0].added.push(c[e].substr(1)),d[0].removed.push(c[e].substr(1))):
"\\"===c[e][0]&&("+"===c[e-1][0]?f=!0:"-"===c[e-1][0]&&(h=!0));c=a.split("\n");for(e=d.length-1;0<=e;e--){for(var k=d[e],g=0;g<k.oldlength;g++)if(c[k.start-1+g]!==k.removed[g])return!1;Array.prototype.splice.apply(c,[k.start-1,k.oldlength].concat(k.added))}if(f)for(;!c[c.length-1];)c.pop();else h&&c.push("");return c.join("\n")},convertChangesToXML:function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];d.added?b.push("<ins>"):d.removed&&b.push("<del>");b.push(k(d.value));d.added?b.push("</ins>"):
d.removed&&b.push("</del>")}return b.join("")},convertChangesToDMP:function(a){for(var b=[],c,d,e=0;e<a.length;e++)c=a[e],d=c.added?1:c.removed?-1:0,b.push([d,c.value]);return b},canonicalize:a};"undefined"!==typeof m&&m.exports?m.exports=B:"function"===typeof define&&define.amd?define([],function(){return B}):"undefined"===typeof c.JsDiff&&(c.JsDiff=B)})(this)},{}],68:[function(f,m,g){var c=/[|\\{}()[\]^$+*?.]/g;m.exports=function(f){if("string"!==typeof f)throw new TypeError("Expected a string");
return f.replace(c,"\\$&")}},{}],69:[function(f,m,g){(function(c){function l(b){for(var d=c.env.PATH.split(":"),e,f=0,h=d.length;f<h;++f)if(e=k.join(d[f],b),a(e))return e}var b=f("child_process").exec,e=f("fs"),k=f("path"),a=e.existsSync||k.existsSync,e=f("os"),h=JSON.stringify,d;switch(e.type()){case "Darwin":d=l("terminal-notifier")?{type:"Darwin-NotificationCenter",pkg:"terminal-notifier",msg:"-message",title:"-title",subtitle:"-subtitle",priority:{cmd:"-execute",range:[]}}:{type:"Darwin-Growl",
pkg:"growlnotify",msg:"-m",sticky:"--sticky",priority:{cmd:"--priority",range:[-2,-1,0,1,2,"Very Low","Moderate","Normal","High","Emergency"]}};break;case "Linux":d={type:"Linux",pkg:"notify-send",msg:"",sticky:"-t 0",icon:"-i",priority:{cmd:"-u",range:["low","normal","critical"]}};break;case "Windows_NT":d={type:"Windows",pkg:"growlnotify",msg:"",sticky:"/s:true",title:"/t:",icon:"/i:",priority:{cmd:"/p:",range:[-2,-1,0,1,2]}}}g=m.exports=function(a,c,e){var f,g;c=c||{};e=e||function(){};if(!d)return e(Error("growl not supported on this platform"));
g=[d.pkg];if(f=c.image)switch(d.type){case "Darwin-Growl":var l,m=k.extname(f).substr(1);l=(l=(l=(l=l||"icns"==m&&"iconpath"||/^[A-Z]/.test(f)&&"appIcon")||/^png|gif|jpe?g$/.test(m)&&"image")||m&&(f=m)&&"icon")||"icon";g.push("--"+l,h(f));break;case "Linux":g.push(d.icon,h(f));c.sticky||g.push("--hint=int:transient:1");break;case "Windows":g.push(d.icon+h(f))}c.sticky&&g.push(d.sticky);c.priority&&(f=c.priority+"",d.priority.range.indexOf(f),~d.priority.range.indexOf(f)&&g.push(d.priority,c.priority));
c.name&&"Darwin-Growl"===d.type&&g.push("--name",c.name);switch(d.type){case "Darwin-Growl":g.push(d.msg);g.push(h(a));c.title&&g.push(h(c.title));break;case "Darwin-NotificationCenter":g.push(d.msg);g.push(h(a));c.title&&(g.push(d.title),g.push(h(c.title)));c.subtitle&&(g.push(d.subtitle),g.push(h(c.subtitle)));break;case "Darwin-Growl":g.push(d.msg);g.push(h(a));c.title&&g.push(h(c.title));break;case "Linux":c.title&&(g.push(h(c.title)),g.push(d.msg));g.push(h(a));break;case "Windows":g.push(h(a)),
c.title&&g.push(d.title+h(c.title))}b(g.join(" "),e)};g.version="1.4.1"}).call(this,f("_process"))},{_process:51,child_process:41,fs:41,os:50,path:41}],70:[function(f,m,g){(function(c){function g(f,a,h,d){"function"===typeof a?(h=a,a={}):a&&"object"===typeof a||(a={mode:a});var m=a.mode,r=a.fs||e;void 0===m&&(m=511&~c.umask());d||(d=null);var q=h||function(){};f=b.resolve(f);r.mkdir(f,m,function(c){if(!c)return d=d||f,q(null,d);switch(c.code){case "ENOENT":g(b.dirname(f),a,function(b,c){b?q(b,c):
g(f,a,q,c)});break;default:r.stat(f,function(a,b){a||!b.isDirectory()?q(c,d):q(null,d)})}})}var b=f("path"),e=f("fs");m.exports=g.mkdirp=g.mkdirP=g;g.sync=function a(f,d,g){d&&"object"===typeof d||(d={mode:d});var l=d.mode,m=d.fs||e;void 0===l&&(l=511&~c.umask());g||(g=null);f=b.resolve(f);try{m.mkdirSync(f,l),g=g||f}catch(u){switch(u.code){case "ENOENT":g=a(b.dirname(f),d,g);a(f,d,g);break;default:var x;try{x=m.statSync(f)}catch(D){throw u;}if(!x.isDirectory())throw u;}}return g}}).call(this,f("_process"))},
{_process:51,fs:41,path:41}],71:[function(f,m,g){(function(c,g){function b(){for(var c=(new a).getTime();r.length&&100>(new a).getTime()-c;)r.shift()();q=r.length?h(b,0):null}c.stdout=f("browser-stdout")();var e=f("../"),k=new e({reporter:"html"}),a=g.Date,h=g.setTimeout,d=[],m=g.onerror;c.removeListener=function(a,b){if("uncaughtException"==a){g.onerror=m?m:function(){};var c=e.utils.indexOf(d,b);-1!=c&&d.splice(c,1)}};c.on=function(a,b){"uncaughtException"==a&&(g.onerror=function(a,c,d){b(Error(a+
" ("+c+":"+d+")"));return!k.allowUncaught},d.push(b))};k.suite.removeAllListeners("pre-require");var r=[],q;e.Runner.immediately=function(a){r.push(a);q||(q=h(b,0))};k.throwError=function(a){e.utils.forEach(d,function(b){b(a)});throw a;};k.ui=function(a){e.prototype.ui.call(this,a);this.suite.emit("pre-require",g,null,this);return this};k.setup=function(a){"string"==typeof a&&(a={ui:a});for(var b in a)this[b](a[b]);return this};k.run=function(a){var b=k.options;k.globals("location");var c=e.utils.parseQuery(g.location.search||
"");c.grep&&k.grep(new RegExp(c.grep));c.fgrep&&k.grep(c.fgrep);c.invert&&k.invert();return e.prototype.run.call(k,function(c){var d=g.document;d&&d.getElementById("mocha")&&!0!==b.noHighlighting&&e.utils.highlightTags("code");a&&a(c)})};e.process=c;g.Mocha=e;g.mocha=k}).call(this,f("_process"),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"../":1,_process:51,"browser-stdout":40}]},{},[71]);(function(){function f(m){var g=f.modules[m];if(!g)throw Error('failed to require "'+m+'"');"exports"in g||"function"!==typeof g.definition||(g.client=g.component=!0,g.definition.call(this,g.exports={},g),delete g.definition);return g.exports}f.loader="component";f.helper={};f.helper.semVerSort=function(f,g){for(var c=f.version.split("."),l=g.version.split("."),b=0;b<c.length;++b){var e=parseInt(c[b],10),k=parseInt(l[b],10);if(e===k){e=c[b].substr((""+e).length);k=l[b].substr((""+k).length);if(""===
e&&""!==k)return 1;if(""!==e&&""===k)return-1;if(""!==e&&""!==k)return e>k?1:-1}else return e>k?1:-1}return 0};f.latest=function(m,g){function c(a){throw Error('failed to find latest module of "'+a+'"');}var l=/(.*)~(.*)@v?(\d+\.\d+\.\d+[^\/]*)$/;/(.*)~(.*)/.test(m)||c(m);for(var b=Object.keys(f.modules),e=[],k=[],a=0;a<b.length;a++){var h=b[a];if((new RegExp(m+"@")).test(h)){var d=h.substr(m.length+1);null!=l.exec(h)?e.push({version:d,name:h}):k.push({version:d,name:h})}}0===e.concat(k).length&&
c(m);if(0<e.length)return l=e.sort(f.helper.semVerSort).pop().name,!0===g?l:f(l);l=k.sort(function(a,b){return a.name>b.name})[0].name;return!0===g?l:f(l)};f.modules={};f.register=function(m,g){f.modules[m]={definition:g}};f.define=function(m,g){f.modules[m]={exports:g}};f.register("chaijs~assertion-error@1.0.0",function(f,g){function c(){function b(b,a){Object.keys(a).forEach(function(f){~c.indexOf(f)||(b[f]=a[f])})}var c=[].slice.call(arguments);return function(){for(var c=[].slice.call(arguments),
a=0,e={};a<c.length;a++)b(e,c[a]);return e}}function l(b,e,f){var a=c("name","message","stack","constructor","toJSON")(e||{});this.message=b||"Unspecified AssertionError";this.showDiff=!1;for(var h in a)this[h]=a[h];(f=f||arguments.callee)&&Error.captureStackTrace&&Error.captureStackTrace(this,f)}g.exports=l;l.prototype=Object.create(Error.prototype);l.prototype.name="AssertionError";l.prototype.constructor=l;l.prototype.toJSON=function(b){var e=c("constructor","toJSON","stack")({name:this.name},
this);!1!==b&&this.stack&&(e.stack=this.stack);return e}});f.register("chaijs~type-detect@0.1.1",function(f,g){function c(c){var f=Object.prototype.toString.call(c);return b[f]?b[f]:null===c?"null":void 0===c?"undefined":c===Object(c)?"object":typeof c}function l(){this.tests={}}f=g.exports=c;var b={"[object Array]":"array","[object RegExp]":"regexp","[object Function]":"function","[object Arguments]":"arguments","[object Date]":"date"};f.Library=l;l.prototype.of=c;l.prototype.define=function(b,c){if(1===
arguments.length)return this.tests[b];this.tests[b]=c;return this};l.prototype.test=function(b,f){if(f===c(b))return!0;var a=this.tests[f];if(a&&"regexp"===c(a))return a.test(b);if(a&&"function"===c(a))return a(b);throw new ReferenceError('Type test "'+f+'" not defined or invalid.');}});f.register("chaijs~deep-eql@0.1.3",function(m,g){function c(b,f,g){return l(b,f)?!0:"date"===a(b)?(g="date"!==a(f)?!1:l(b.getTime(),f.getTime()),g):"regexp"===a(b)?(g="regexp"!==a(f)?!1:l(b.toString(),f.toString()),
g):h.isBuffer(b)?(g=h.isBuffer(f)?e(b,f):!1,g):"arguments"===a(b)?("arguments"!==a(f)?g=!1:(b=[].slice.call(b),f=[].slice.call(f),g=c(b,f,g)),g):a(b)!==a(f)?!1:"object"!==a(b)&&"object"!==a(f)&&"array"!==a(b)&&"array"!==a(f)?l(b,f):k(b,f,g)}function l(a,b){return a===b?0!==a||1/a===1/b:a!==a&&b!==b}function b(a){var b=[],c;for(c in a)b.push(c);return b}function e(a,b){if(a.length!==b.length)return!1;for(var c=0,e=!0;c<a.length;c++)if(a[c]!==b[c]){e=!1;break}return e}function k(a,f,h){if(null===a||
void 0===a||null===f||void 0===f||a.prototype!==f.prototype)return!1;var g;if(h)for(g=0;g<h.length;g++){if(h[g][0]===a&&h[g][1]===f||h[g][0]===f&&h[g][1]===a)return!0}else h=[];try{var k=b(a),l=b(f)}catch(m){return!1}k.sort();l.sort();if(!e(k,l))return!1;h.push([a,f]);for(g=k.length-1;0<=g;g--)if(l=k[g],!c(a[l],f[l],h))return!1;return!0}var a=f("chaijs~type-detect@0.1.1"),h;try{h=f("buffer").Buffer}catch(d){h={isBuffer:function(){return!1}}}g.exports=c});f.register("chai",function(m,g){g.exports=
f("chai/lib/chai.js")});f.register("chai/lib/chai.js",function(m,g){var c=[];m=g.exports={};m.version="2.1.0";m.AssertionError=f("chaijs~assertion-error@1.0.0");var l=f("chai/lib/chai/utils/index.js");m.use=function(b){~c.indexOf(b)||(b(this,l),c.push(b));return this};m.util=l;var b=f("chai/lib/chai/config.js");m.config=b;b=f("chai/lib/chai/assertion.js");m.use(b);b=f("chai/lib/chai/core/assertions.js");m.use(b);b=f("chai/lib/chai/interface/expect.js");m.use(b);b=f("chai/lib/chai/interface/should.js");
m.use(b);b=f("chai/lib/chai/interface/assert.js");m.use(b)});f.register("chai/lib/chai/assertion.js",function(m,g){var c=f("chai/lib/chai/config.js");g.exports=function(f,b){function e(b,c,e){a(this,"ssfi",e||arguments.callee);a(this,"object",b);a(this,"message",c)}var g=f.AssertionError,a=b.flag;f.Assertion=e;Object.defineProperty(e,"includeStack",{get:function(){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead.");return c.includeStack},set:function(a){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead.");
c.includeStack=a}});Object.defineProperty(e,"showDiff",{get:function(){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead.");return c.showDiff},set:function(a){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead.");c.showDiff=a}});e.addProperty=function(a,c){b.addProperty(this.prototype,a,c)};e.addMethod=function(a,c){b.addMethod(this.prototype,a,c)};e.addChainableMethod=function(a,c,e){b.addChainableMethod(this.prototype,a,c,e)};e.overwriteProperty=
function(a,c){b.overwriteProperty(this.prototype,a,c)};e.overwriteMethod=function(a,c){b.overwriteMethod(this.prototype,a,c)};e.overwriteChainableMethod=function(a,c,e){b.overwriteChainableMethod(this.prototype,a,c,e)};e.prototype.assert=function(e,d,f,l,m,x){var u=b.test(this,arguments);!0!==x&&(x=!1);!0!==c.showDiff&&(x=!1);if(!u)throw d=b.getMessage(this,arguments),u=b.getActual(this,arguments),new g(d,{actual:u,expected:l,showDiff:x},c.includeStack?this.assert:a(this,"ssfi"));};Object.defineProperty(e.prototype,
"_obj",{get:function(){return a(this,"object")},set:function(b){a(this,"object",b)}})}});f.register("chai/lib/chai/config.js",function(f,g){g.exports={includeStack:!1,showDiff:!0,truncateThreshold:40}});f.register("chai/lib/chai/core/assertions.js",function(f,g){g.exports=function(c,f){function b(a,b){b&&p(this,"message",b);a=a.toLowerCase();var c=p(this,"object"),d=~["a","e","i","o","u"].indexOf(a.charAt(0))?"an ":"a ";this.assert(a===f.type(c),"expected #{this} to be "+d+a,"expected #{this} not to be "+
d+a)}function e(){p(this,"contains",!0)}function g(a,b){b&&p(this,"message",b);var c=p(this,"object"),d=!1;if("array"===f.type(c)&&"object"===f.type(a))for(var e in c){if(f.eql(c[e],a)){d=!0;break}}else if("object"===f.type(a)){if(!p(this,"negate")){for(var h in a)(new n(c)).property(h,a[h]);return}d={};for(h in a)d[h]=c[h];d=f.eql(d,a)}else d=c&&~c.indexOf(a);this.assert(d,"expected #{this} to include "+f.inspect(a),"expected #{this} to not include "+f.inspect(a))}function a(){var a=p(this,"object"),
a=Object.prototype.toString.call(a);this.assert("[object Arguments]"===a,"expected #{this} to be arguments but got "+a,"expected #{this} to not be arguments")}function h(a,b){b&&p(this,"message",b);var c=p(this,"object");if(p(this,"deep"))return this.eql(a);this.assert(a===c,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",a,this._obj,!0)}function d(a,b){b&&p(this,"message",b);this.assert(f.eql(a,p(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",
a,this._obj,!0)}function m(a,b){b&&p(this,"message",b);var c=p(this,"object");p(this,"doLength")?((new n(c,b)).to.have.property("length"),c=c.length,this.assert(c>a,"expected #{this} to have a length above #{exp} but got #{act}","expected #{this} to not have a length above #{exp}",a,c)):this.assert(c>a,"expected #{this} to be above "+a,"expected #{this} to be at most "+a)}function r(a,b){b&&p(this,"message",b);var c=p(this,"object");p(this,"doLength")?((new n(c,b)).to.have.property("length"),c=c.length,
this.assert(c>=a,"expected #{this} to have a length at least #{exp} but got #{act}","expected #{this} to have a length below #{exp}",a,c)):this.assert(c>=a,"expected #{this} to be at least "+a,"expected #{this} to be below "+a)}function q(a,b){b&&p(this,"message",b);var c=p(this,"object");p(this,"doLength")?((new n(c,b)).to.have.property("length"),c=c.length,this.assert(c<a,"expected #{this} to have a length below #{exp} but got #{act}","expected #{this} to not have a length below #{exp}",a,c)):this.assert(c<
a,"expected #{this} to be below "+a,"expected #{this} to be at least "+a)}function x(a,b){b&&p(this,"message",b);var c=p(this,"object");p(this,"doLength")?((new n(c,b)).to.have.property("length"),c=c.length,this.assert(c<=a,"expected #{this} to have a length at most #{exp} but got #{act}","expected #{this} to have a length above #{exp}",a,c)):this.assert(c<=a,"expected #{this} to be at most "+a,"expected #{this} to be above "+a)}function u(a,b){b&&p(this,"message",b);var c=f.getName(a);this.assert(p(this,
"object")instanceof a,"expected #{this} to be an instance of "+c,"expected #{this} to not be an instance of "+c)}function D(a,b){b&&p(this,"message",b);var c=p(this,"object");this.assert(c.hasOwnProperty(a),"expected #{this} to have own property "+f.inspect(a),"expected #{this} to not have own property "+f.inspect(a))}function E(a,b){b&&p(this,"message",b);var c=p(this,"object");(new n(c,b)).to.have.property("length");c=c.length;this.assert(c==a,"expected #{this} to have a length of #{exp} but got #{act}",
"expected #{this} to not have a length of #{act}",a,c)}function w(a){var b=p(this,"object"),c,d=!0;switch(f.type(a)){case "array":if(1<arguments.length)throw Error("keys must be given single argument of Array|Object|String, or multiple String arguments");break;case "object":if(1<arguments.length)throw Error("keys must be given single argument of Array|Object|String, or multiple String arguments");a=Object.keys(a);break;default:a=Array.prototype.slice.call(arguments)}if(!a.length)throw Error("keys required");
var e=Object.keys(b),b=a,h=a.length,g=p(this,"any"),k=p(this,"all");g||k||(k=!0);g&&(d=0<b.filter(function(a){return~e.indexOf(a)}).length);k&&(d=a.every(function(a){return~e.indexOf(a)}),p(this,"negate")||p(this,"contains")||(d=d&&a.length==e.length));if(1<h){a=a.map(function(a){return f.inspect(a)});var m=a.pop();k&&(c=a.join(", ")+", and "+m);g&&(c=a.join(", ")+", or "+m)}else c=f.inspect(a[0]);c=(1<h?"keys ":"key ")+c;c=(p(this,"contains")?"contain ":"have ")+c;this.assert(d,"expected #{this} to "+
c,"expected #{this} to not "+c,b.slice(0).sort(),e.sort(),!0)}function A(a,b,c){c&&p(this,"message",c);var d=p(this,"object");(new n(d,c)).is.a("function");var e=!1,h=null,g=null,k=null;0===arguments.length?a=b=null:a&&(a instanceof RegExp||"string"===typeof a)?(b=a,a=null):a&&a instanceof Error?(h=a,b=a=null):"function"===typeof a?(g=a.prototype.name||a.name,"Error"===g&&a!==Error&&(g=(new a).name)):a=null;try{d()}catch(m){if(h)return this.assert(m===h,"expected #{this} to throw #{exp} but #{act} was thrown",
"expected #{this} to not throw #{exp}",h instanceof Error?h.toString():h,m instanceof Error?m.toString():m),p(this,"object",m),this;if(a&&(this.assert(m instanceof a,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp} but #{act} was thrown",g,m instanceof Error?m.toString():m),!b))return p(this,"object",m),this;e="object"===f.type(m)&&"message"in m?m.message:""+m;if(null!=e&&b&&b instanceof RegExp)return this.assert(b.exec(e),"expected #{this} to throw error matching #{exp} but got #{act}",
"expected #{this} to throw error not matching #{exp}",b,e),p(this,"object",m),this;if(null!=e&&b&&"string"===typeof b)return this.assert(~e.indexOf(b),"expected #{this} to throw error including #{exp} but got #{act}","expected #{this} to throw error not including #{act}",b,e),p(this,"object",m),this;e=!0;k=m}d="";g=null!==g?g:h?"#{exp}":"an error";e&&(d=" but #{act} was thrown");this.assert(!0===e,"expected #{this} to throw "+g+d,"expected #{this} to not throw "+g+d,h instanceof Error?h.toString():
h,k instanceof Error?k.toString():k);p(this,"object",k)}function F(a,b,c){return a.every(function(a){return c?b.some(function(b){return c(a,b)}):-1!==b.indexOf(a)})}function B(a,b,c){c&&p(this,"message",c);var d=p(this,"object");(new n(a,c)).to.have.property(b);(new n(d)).is.a("function");c=a[b];d();this.assert(c!==a[b],"expected ."+b+" to change","expected ."+b+" to not change")}function y(a,b,c){c&&p(this,"message",c);var d=p(this,"object");(new n(a,c)).to.have.property(b);(new n(d)).is.a("function");
c=a[b];d();this.assert(0<a[b]-c,"expected ."+b+" to increase","expected ."+b+" to not increase")}function z(a,b,c){c&&p(this,"message",c);var d=p(this,"object");(new n(a,c)).to.have.property(b);(new n(d)).is.a("function");c=a[b];d();this.assert(0>a[b]-c,"expected ."+b+" to decrease","expected ."+b+" to not decrease")}var n=c.Assertion,p=f.flag;"to be been is and has have with that which at of same".split(" ").forEach(function(a){n.addProperty(a,function(){return this})});n.addProperty("not",function(){p(this,
"negate",!0)});n.addProperty("deep",function(){p(this,"deep",!0)});n.addProperty("any",function(){p(this,"any",!0);p(this,"all",!1)});n.addProperty("all",function(){p(this,"all",!0);p(this,"any",!1)});n.addChainableMethod("an",b);n.addChainableMethod("a",b);n.addChainableMethod("include",g,e);n.addChainableMethod("contain",g,e);n.addChainableMethod("contains",g,e);n.addChainableMethod("includes",g,e);n.addProperty("ok",function(){this.assert(p(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")});
n.addProperty("true",function(){this.assert(!0===p(this,"object"),"expected #{this} to be true","expected #{this} to be false",this.negate?!1:!0)});n.addProperty("false",function(){this.assert(!1===p(this,"object"),"expected #{this} to be false","expected #{this} to be true",this.negate?!0:!1)});n.addProperty("null",function(){this.assert(null===p(this,"object"),"expected #{this} to be null","expected #{this} not to be null")});n.addProperty("undefined",function(){this.assert(void 0===p(this,"object"),
"expected #{this} to be undefined","expected #{this} not to be undefined")});n.addProperty("exist",function(){this.assert(null!=p(this,"object"),"expected #{this} to exist","expected #{this} to not exist")});n.addProperty("empty",function(){var a=p(this,"object"),b=a;Array.isArray(a)||"string"===typeof object?b=a.length:"object"===typeof a&&(b=Object.keys(a).length);this.assert(!b,"expected #{this} to be empty","expected #{this} not to be empty")});n.addProperty("arguments",a);n.addProperty("Arguments",
a);n.addMethod("equal",h);n.addMethod("equals",h);n.addMethod("eq",h);n.addMethod("eql",d);n.addMethod("eqls",d);n.addMethod("above",m);n.addMethod("gt",m);n.addMethod("greaterThan",m);n.addMethod("least",r);n.addMethod("gte",r);n.addMethod("below",q);n.addMethod("lt",q);n.addMethod("lessThan",q);n.addMethod("most",x);n.addMethod("lte",x);n.addMethod("within",function(a,b,c){c&&p(this,"message",c);var d=p(this,"object"),e=a+".."+b;p(this,"doLength")?((new n(d,c)).to.have.property("length"),c=d.length,
this.assert(c>=a&&c<=b,"expected #{this} to have a length within "+e,"expected #{this} to not have a length within "+e)):this.assert(d>=a&&d<=b,"expected #{this} to be within "+e,"expected #{this} to not be within "+e)});n.addMethod("instanceof",u);n.addMethod("instanceOf",u);n.addMethod("property",function(a,b,c){c&&p(this,"message",c);var d=!!p(this,"deep"),e=d?"deep property ":"property ",h=p(this,"negate"),g=p(this,"object"),k=d?f.getPathInfo(a,g):null,m=d?k.exists:f.hasProperty(a,g),d=d?k.value:
g[a];if(h&&void 0!==b){if(void 0===d)throw Error((null!=c?c+": ":"")+f.inspect(g)+" has no "+e+f.inspect(a));}else this.assert(m,"expected #{this} to have a "+e+f.inspect(a),"expected #{this} to not have "+e+f.inspect(a));void 0!==b&&this.assert(b===d,"expected #{this} to have a "+e+f.inspect(a)+" of #{exp}, but got #{act}","expected #{this} to not have a "+e+f.inspect(a)+" of #{act}",b,d);p(this,"object",d)});n.addMethod("ownProperty",D);n.addMethod("haveOwnProperty",D);n.addChainableMethod("length",
E,function(){p(this,"doLength",!0)});n.addMethod("lengthOf",E);n.addMethod("match",function(a,b){b&&p(this,"message",b);var c=p(this,"object");this.assert(a.exec(c),"expected #{this} to match "+a,"expected #{this} not to match "+a)});n.addMethod("string",function(a,b){b&&p(this,"message",b);var c=p(this,"object");(new n(c,b)).is.a("string");this.assert(~c.indexOf(a),"expected #{this} to contain "+f.inspect(a),"expected #{this} to not contain "+f.inspect(a))});n.addMethod("keys",w);n.addMethod("key",
w);n.addMethod("throw",A);n.addMethod("throws",A);n.addMethod("Throw",A);n.addMethod("respondTo",function(a,b){b&&p(this,"message",b);var c=p(this,"object"),d=p(this,"itself"),c="function"!==f.type(c)||d?c[a]:c.prototype[a];this.assert("function"===typeof c,"expected #{this} to respond to "+f.inspect(a),"expected #{this} to not respond to "+f.inspect(a))});n.addProperty("itself",function(){p(this,"itself",!0)});n.addMethod("satisfy",function(a,b){b&&p(this,"message",b);var c=p(this,"object"),c=a(c);
this.assert(c,"expected #{this} to satisfy "+f.objDisplay(a),"expected #{this} to not satisfy"+f.objDisplay(a),this.negate?!1:!0,c)});n.addMethod("closeTo",function(a,b,c){c&&p(this,"message",c);var d=p(this,"object");(new n(d,c)).is.a("number");if("number"!==f.type(a)||"number"!==f.type(b))throw Error("the arguments to closeTo must be numbers");this.assert(Math.abs(d-a)<=b,"expected #{this} to be close to "+a+" +/- "+b,"expected #{this} not to be close to "+a+" +/- "+b)});n.addMethod("members",function(a,
b){b&&p(this,"message",b);var c=p(this,"object");(new n(c)).to.be.an("array");(new n(a)).to.be.an("array");var d=p(this,"deep")?f.eql:void 0;if(p(this,"contains"))return this.assert(F(a,c,d),"expected #{this} to be a superset of #{act}","expected #{this} to not be a superset of #{act}",c,a);this.assert(F(c,a,d)&&F(a,c,d),"expected #{this} to have the same members as #{act}","expected #{this} to not have the same members as #{act}",c,a)});n.addChainableMethod("change",B);n.addChainableMethod("changes",
B);n.addChainableMethod("increase",y);n.addChainableMethod("increases",y);n.addChainableMethod("decrease",z);n.addChainableMethod("decreases",z)}});f.register("chai/lib/chai/interface/assert.js",function(f,g){g.exports=function(c,f){var b=c.Assertion,e=f.flag,g=c.assert=function(a,e){(new b(null,null,c.assert)).assert(a,e,"[ negation message unavailable ]")};g.fail=function(a,b,d,e){throw new c.AssertionError(d||"assert.fail()",{actual:a,expected:b,operator:e},g.fail);};g.ok=function(a,c){(new b(a,
c)).is.ok};g.notOk=function(a,c){(new b(a,c)).is.not.ok};g.equal=function(a,c,d){d=new b(a,d,g.equal);d.assert(c==e(d,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",c,a)};g.notEqual=function(a,c,d){d=new b(a,d,g.notEqual);d.assert(c!=e(d,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",c,a)};g.strictEqual=function(a,c,d){(new b(a,d)).to.equal(c)};g.notStrictEqual=function(a,c,d){(new b(a,d)).to.not.equal(c)};g.deepEqual=function(a,
c,d){(new b(a,d)).to.eql(c)};g.notDeepEqual=function(a,c,d){(new b(a,d)).to.not.eql(c)};g.isAbove=function(a,c,d){(new b(a,d)).to.be.above(c)};g.isBelow=function(a,c,d){(new b(a,d)).to.be.below(c)};g.isTrue=function(a,c){(new b(a,c)).is["true"]};g.isFalse=function(a,c){(new b(a,c)).is["false"]};g.isNull=function(a,c){(new b(a,c)).to.equal(null)};g.isNotNull=function(a,c){(new b(a,c)).to.not.equal(null)};g.isUndefined=function(a,c){(new b(a,c)).to.equal(void 0)};g.isDefined=function(a,c){(new b(a,
c)).to.not.equal(void 0)};g.isFunction=function(a,c){(new b(a,c)).to.be.a("function")};g.isNotFunction=function(a,c){(new b(a,c)).to.not.be.a("function")};g.isObject=function(a,c){(new b(a,c)).to.be.a("object")};g.isNotObject=function(a,c){(new b(a,c)).to.not.be.a("object")};g.isArray=function(a,c){(new b(a,c)).to.be.an("array")};g.isNotArray=function(a,c){(new b(a,c)).to.not.be.an("array")};g.isString=function(a,c){(new b(a,c)).to.be.a("string")};g.isNotString=function(a,c){(new b(a,c)).to.not.be.a("string")};
g.isNumber=function(a,c){(new b(a,c)).to.be.a("number")};g.isNotNumber=function(a,c){(new b(a,c)).to.not.be.a("number")};g.isBoolean=function(a,c){(new b(a,c)).to.be.a("boolean")};g.isNotBoolean=function(a,c){(new b(a,c)).to.not.be.a("boolean")};g.typeOf=function(a,c,d){(new b(a,d)).to.be.a(c)};g.notTypeOf=function(a,c,d){(new b(a,d)).to.not.be.a(c)};g.instanceOf=function(a,c,d){(new b(a,d)).to.be.instanceOf(c)};g.notInstanceOf=function(a,c,d){(new b(a,d)).to.not.be.instanceOf(c)};g.include=function(a,
c,d){(new b(a,d,g.include)).include(c)};g.notInclude=function(a,c,d){(new b(a,d,g.notInclude)).not.include(c)};g.match=function(a,c,d){(new b(a,d)).to.match(c)};g.notMatch=function(a,c,d){(new b(a,d)).to.not.match(c)};g.property=function(a,c,d){(new b(a,d)).to.have.property(c)};g.notProperty=function(a,c,d){(new b(a,d)).to.not.have.property(c)};g.deepProperty=function(a,c,d){(new b(a,d)).to.have.deep.property(c)};g.notDeepProperty=function(a,c,d){(new b(a,d)).to.not.have.deep.property(c)};g.propertyVal=
function(a,c,d,e){(new b(a,e)).to.have.property(c,d)};g.propertyNotVal=function(a,c,d,e){(new b(a,e)).to.not.have.property(c,d)};g.deepPropertyVal=function(a,c,d,e){(new b(a,e)).to.have.deep.property(c,d)};g.deepPropertyNotVal=function(a,c,d,e){(new b(a,e)).to.not.have.deep.property(c,d)};g.lengthOf=function(a,c,d){(new b(a,d)).to.have.length(c)};g.Throw=function(a,c,d,f){if("string"===typeof c||c instanceof RegExp)d=c,c=null;a=(new b(a,f)).to.Throw(c,d);return e(a,"object")};g.doesNotThrow=function(a,
c,d){"string"===typeof c&&(d=c,c=null);(new b(a,d)).to.not.Throw(c)};g.operator=function(a,c,d,g){if(!~"== === > >= < <= != !==".split(" ").indexOf(c))throw Error('Invalid operator "'+c+'"');g=new b(eval(a+c+d),g);g.assert(!0===e(g,"object"),"expected "+f.inspect(a)+" to be "+c+" "+f.inspect(d),"expected "+f.inspect(a)+" to not be "+c+" "+f.inspect(d))};g.closeTo=function(a,c,d,e){(new b(a,e)).to.be.closeTo(c,d)};g.sameMembers=function(a,c,d){(new b(a,d)).to.have.same.members(c)};g.sameDeepMembers=
function(a,c,d){(new b(a,d)).to.have.same.deep.members(c)};g.includeMembers=function(a,c,d){(new b(a,d)).to.include.members(c)};g.changes=function(a,c,d){(new b(a)).to.change(c,d)};g.doesNotChange=function(a,c,d){(new b(a)).to.not.change(c,d)};g.increases=function(a,c,d){(new b(a)).to.increase(c,d)};g.doesNotIncrease=function(a,c,d){(new b(a)).to.not.increase(c,d)};g.decreases=function(a,c,d){(new b(a)).to.decrease(c,d)};g.doesNotDecrease=function(a,c,d){(new b(a)).to.not.decrease(c,d)};g.ifError=
function(a,c){(new b(a,c)).to.not.be.ok};(function h(b,c){g[c]=g[b];return h})("Throw","throw")("Throw","throws")}});f.register("chai/lib/chai/interface/expect.js",function(f,g){g.exports=function(c,f){c.expect=function(b,e){return new c.Assertion(b,e)};c.expect.fail=function(b,e,f,a){throw new c.AssertionError(f||"expect.fail()",{actual:b,expected:e,operator:a},c.expect.fail);}}});f.register("chai/lib/chai/interface/should.js",function(f,g){g.exports=function(c,f){function b(){function b(){return this instanceof
String||this instanceof Number?new e(this.constructor(this),null,b):this instanceof Boolean?new e(1==this,null,b):new e(this,null,b)}Object.defineProperty(Object.prototype,"should",{set:function(a){Object.defineProperty(this,"should",{value:a,enumerable:!0,configurable:!0,writable:!0})},get:b,configurable:!0});var a={fail:function(b,d,e,f){throw new c.AssertionError(e||"should.fail()",{actual:b,expected:d,operator:f},a.fail);},equal:function(a,b,c){(new e(a,c)).to.equal(b)},Throw:function(a,b,c,f){(new e(a,
f)).to.Throw(b,c)},exist:function(a,b){(new e(a,b)).to.exist},not:{}};a.not.equal=function(a,b,c){(new e(a,c)).to.not.equal(b)};a.not.Throw=function(a,b,c,f){(new e(a,f)).to.not.Throw(b,c)};a.not.exist=function(a,b){(new e(a,b)).to.not.exist};a["throw"]=a.Throw;a.not["throw"]=a.not.Throw;return a}var e=c.Assertion;c.should=b;c.Should=b}});f.register("chai/lib/chai/utils/addChainableMethod.js",function(m,g){var c=f("chai/lib/chai/utils/transferFlags.js"),l=f("chai/lib/chai/utils/flag.js"),b=f("chai/lib/chai/config.js"),
e="__proto__"in Object,k=/^(?:length|name|arguments|caller)$/,a=Function.prototype.call,h=Function.prototype.apply;g.exports=function(d,f,g,m){"function"!==typeof m&&(m=function(){});var x={method:g,chainingBehavior:m};d.__methods||(d.__methods={});d.__methods[f]=x;Object.defineProperty(d,f,{get:function(){x.chainingBehavior.call(this);var f=function w(){l(this,"ssfi")&&!1===b.includeStack&&l(this,"ssfi",w);var a=x.method.apply(this,arguments);return void 0===a?this:a};if(e){var g=f.__proto__=Object.create(this);
g.call=a;g.apply=h}else Object.getOwnPropertyNames(d).forEach(function(a){if(!k.test(a)){var b=Object.getOwnPropertyDescriptor(d,a);Object.defineProperty(f,a,b)}});c(this,f);return f},configurable:!0})}});f.register("chai/lib/chai/utils/addMethod.js",function(m,g){var c=f("chai/lib/chai/config.js"),l=f("chai/lib/chai/utils/flag.js");g.exports=function(b,e,f){b[e]=function(){l(this,"ssfi")&&!1===c.includeStack&&l(this,"ssfi",b[e]);var a=f.apply(this,arguments);return void 0===a?this:a}}});f.register("chai/lib/chai/utils/addProperty.js",
function(f,g){g.exports=function(c,f,b){Object.defineProperty(c,f,{get:function(){var c=b.call(this);return void 0===c?this:c},configurable:!0})}});f.register("chai/lib/chai/utils/flag.js",function(f,g){g.exports=function(c,f,b){var e=c.__flags||(c.__flags=Object.create(null));if(3===arguments.length)e[f]=b;else return e[f]}});f.register("chai/lib/chai/utils/getActual.js",function(f,g){g.exports=function(c,f){return 4<f.length?f[4]:c._obj}});f.register("chai/lib/chai/utils/getEnumerableProperties.js",
function(f,g){g.exports=function(c){var f=[],b;for(b in c)f.push(b);return f}});f.register("chai/lib/chai/utils/getMessage.js",function(m,g){var c=f("chai/lib/chai/utils/flag.js"),l=f("chai/lib/chai/utils/getActual.js");f("chai/lib/chai/utils/inspect.js");var b=f("chai/lib/chai/utils/objDisplay.js");g.exports=function(e,f){var a=c(e,"negate"),g=c(e,"object"),d=f[3],m=l(e,f),a=a?f[2]:f[1],r=c(e,"message");"function"===typeof a&&(a=a());a=(a||"").replace(/#{this}/g,b(g)).replace(/#{act}/g,b(m)).replace(/#{exp}/g,
b(d));return r?r+": "+a:a}});f.register("chai/lib/chai/utils/getName.js",function(f,g){g.exports=function(c){return c.name?c.name:(c=/^\s?function ([^(]*)\(/.exec(c))&&c[1]?c[1]:""}});f.register("chai/lib/chai/utils/getPathValue.js",function(m,g){var c=f("chai/lib/chai/utils/getPathInfo.js");g.exports=function(f,b){return c(f,b).value}});f.register("chai/lib/chai/utils/getPathInfo.js",function(m,g){function c(b){return b.replace(/\[/g,".[").match(/(\\\.|[^.]+?)+/g).map(function(b){var a=/\[(\d+)\]$/.exec(b);
return a?{i:parseFloat(a[1])}:{p:b}})}function l(b,c,a){var f;a=void 0===a?b.length:a;for(var d=0;d<a;d++){var g=b[d];c?("undefined"!==typeof g.p?c=c[g.p]:"undefined"!==typeof g.i&&(c=c[g.i]),d==a-1&&(f=c)):f=void 0}return f}var b=f("chai/lib/chai/utils/hasProperty.js");g.exports=function(e,f){var a=c(e),g=a[a.length-1],a={parent:l(a,f,a.length-1),name:g.p||g.i,value:l(a,f)};a.exists=b(a.name,a.parent);return a}});f.register("chai/lib/chai/utils/hasProperty.js",function(m,g){var c=f("chai/lib/chai/utils/type.js"),
l={number:Number,string:String};g.exports=function(b,e){var f=c(e);if("null"===f||"undefined"===f)return!1;l[f]&&"object"!==typeof e&&(e=new l[f](e));return b in e}});f.register("chai/lib/chai/utils/getProperties.js",function(f,g){g.exports=function(c){function f(c){-1===b.indexOf(c)&&b.push(c)}var b=Object.getOwnPropertyNames(subject);for(c=Object.getPrototypeOf(subject);null!==c;)Object.getOwnPropertyNames(c).forEach(f),c=Object.getPrototypeOf(c);return b}});f.register("chai/lib/chai/utils/index.js",
function(m,g){m=g.exports={};m.test=f("chai/lib/chai/utils/test.js");m.type=f("chai/lib/chai/utils/type.js");m.getMessage=f("chai/lib/chai/utils/getMessage.js");m.getActual=f("chai/lib/chai/utils/getActual.js");m.inspect=f("chai/lib/chai/utils/inspect.js");m.objDisplay=f("chai/lib/chai/utils/objDisplay.js");m.flag=f("chai/lib/chai/utils/flag.js");m.transferFlags=f("chai/lib/chai/utils/transferFlags.js");m.eql=f("chaijs~deep-eql@0.1.3");m.getPathValue=f("chai/lib/chai/utils/getPathValue.js");m.getPathInfo=
f("chai/lib/chai/utils/getPathInfo.js");m.hasProperty=f("chai/lib/chai/utils/hasProperty.js");m.getName=f("chai/lib/chai/utils/getName.js");m.addProperty=f("chai/lib/chai/utils/addProperty.js");m.addMethod=f("chai/lib/chai/utils/addMethod.js");m.overwriteProperty=f("chai/lib/chai/utils/overwriteProperty.js");m.overwriteMethod=f("chai/lib/chai/utils/overwriteMethod.js");m.addChainableMethod=f("chai/lib/chai/utils/addChainableMethod.js");m.overwriteChainableMethod=f("chai/lib/chai/utils/overwriteChainableMethod.js")});
f.register("chai/lib/chai/utils/inspect.js",function(m,g){function c(f,g,E){if(g&&"function"===typeof g.inspect&&g.inspect!==m.inspect&&(!g.constructor||g.constructor.prototype!==g)){var w=g.inspect(E);"string"!==typeof w&&(w=c(f,w,E));return w}var A=l(f,g);if(A)return A;A="object"===typeof HTMLElement?g instanceof HTMLElement:g&&"object"===typeof g&&1===g.nodeType&&"string"===typeof g.nodeName;if(A){if("outerHTML"in g)return g.outerHTML;try{if(document.xmlVersion)return(new XMLSerializer).serializeToString(g);
w=document.createElementNS("http://www.w3.org/1999/xhtml","_");w.appendChild(g.cloneNode(!1));html=w.innerHTML.replace("><",">"+g.innerHTML+"<");w.innerHTML="";return html}catch(z){}}var F=x(g),A=f.showHidden?q(g):F;if(0===A.length||t(g)&&(1===A.length&&"stack"===A[0]||2===A.length&&"description"===A[0]&&"stack"===A[1])){if("function"===typeof g){var B=r(g);return f.stylize("[Function"+(B?": "+B:"")+"]","special")}if(h(g))return f.stylize(RegExp.prototype.toString.call(g),"regexp");if(d(g))return f.stylize(Date.prototype.toUTCString.call(g),
"date");if(t(g))return"["+Error.prototype.toString.call(g)+"]"}var B="",y=!1,w=["{","}"];a(g)&&(y=!0,w=["[","]"]);"function"===typeof g&&(B=r(g),B=" [Function"+(B?": "+B:"")+"]");h(g)&&(B=" "+RegExp.prototype.toString.call(g));d(g)&&(B=" "+Date.prototype.toUTCString.call(g));if(t(g))return"["+Error.prototype.toString.call(g)+"]";if(0===A.length&&(!y||0==g.length))return w[0]+B+w[1];if(0>E)return h(g)?f.stylize(RegExp.prototype.toString.call(g),"regexp"):f.stylize("[Object]","special");f.seen.push(g);
A=y?b(f,g,E,F,A):A.map(function(a){return e(f,g,E,F,a,y)});f.seen.pop();return k(A,B,w)}function l(a,b){switch(typeof b){case "undefined":return a.stylize("undefined","undefined");case "string":var c="'"+JSON.stringify(b).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return a.stylize(c,"string");case "number":return 0===b&&-Infinity===1/b?a.stylize("-0","number"):a.stylize(""+b,"number");case "boolean":return a.stylize(""+b,"boolean")}if(null===b)return a.stylize("null","null")}
function b(a,b,c,d,f){for(var g=[],h=0,k=b.length;h<k;++h)Object.prototype.hasOwnProperty.call(b,String(h))?g.push(e(a,b,c,d,String(h),!0)):g.push("");f.forEach(function(f){f.match(/^\d+$/)||g.push(e(a,b,c,d,f,!0))});return g}function e(a,b,d,e,f,g){var h,k;b.__lookupGetter__&&(b.__lookupGetter__(f)?k=b.__lookupSetter__(f)?a.stylize("[Getter/Setter]","special"):a.stylize("[Getter]","special"):b.__lookupSetter__(f)&&(k=a.stylize("[Setter]","special")));0>e.indexOf(f)&&(h="["+f+"]");k||(0>a.seen.indexOf(b[f])?
(k=null===d?c(a,b[f],null):c(a,b[f],d-1),-1<k.indexOf("\n")&&(k=g?k.split("\n").map(function(a){return" "+a}).join("\n").substr(2):"\n"+k.split("\n").map(function(a){return" "+a}).join("\n"))):k=a.stylize("[Circular]","special"));if("undefined"===typeof h){if(g&&f.match(/^\d+$/))return k;h=JSON.stringify(""+f);h.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(h=h.substr(1,h.length-2),h=a.stylize(h,"name")):(h=h.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),h=a.stylize(h,"string"))}return h+
": "+k}function k(a,b,c){var d=0;return 60<a.reduce(function(a,b){d++;0<=b.indexOf("\n")&&d++;return a+b.length+1},0)?c[0]+(""===b?"":b+"\n ")+" "+a.join(",\n ")+" "+c[1]:c[0]+b+" "+a.join(", ")+" "+c[1]}function a(a){return Array.isArray(a)||"object"===typeof a&&"[object Array]"===Object.prototype.toString.call(a)}function h(a){return"object"===typeof a&&"[object RegExp]"===Object.prototype.toString.call(a)}function d(a){return"object"===typeof a&&"[object Date]"===Object.prototype.toString.call(a)}
function t(a){return"object"===typeof a&&"[object Error]"===Object.prototype.toString.call(a)}var r=f("chai/lib/chai/utils/getName.js"),q=f("chai/lib/chai/utils/getProperties.js"),x=f("chai/lib/chai/utils/getEnumerableProperties.js");g.exports=function(a,b,d,e){return c({showHidden:b,seen:[],stylize:function(a){return a}},a,"undefined"===typeof d?2:d)}});f.register("chai/lib/chai/utils/objDisplay.js",function(m,g){var c=f("chai/lib/chai/utils/inspect.js"),l=f("chai/lib/chai/config.js");g.exports=
function(b){var e=c(b),f=Object.prototype.toString.call(b);if(l.truncateThreshold&&e.length>=l.truncateThreshold){if("[object Function]"===f)return b.name&&""!==b.name?"[Function: "+b.name+"]":"[Function]";if("[object Array]"===f)return"[ Array("+b.length+") ]";if("[object Object]"===f)return b=Object.keys(b),"{ Object ("+(2<b.length?b.splice(0,2).join(", ")+", ...":b.join(", "))+") }"}return e}});f.register("chai/lib/chai/utils/overwriteMethod.js",function(f,g){g.exports=function(c,f,b){var e=c[f],
g=function(){return this};e&&"function"===typeof e&&(g=e);c[f]=function(){var a=b(g).apply(this,arguments);return void 0===a?this:a}}});f.register("chai/lib/chai/utils/overwriteProperty.js",function(f,g){g.exports=function(c,f,b){var e=Object.getOwnPropertyDescriptor(c,f),g=function(){};e&&"function"===typeof e.get&&(g=e.get);Object.defineProperty(c,f,{get:function(){var a=b(g).call(this);return void 0===a?this:a},configurable:!0})}});f.register("chai/lib/chai/utils/overwriteChainableMethod.js",function(f,
g){g.exports=function(c,f,b,e){c=c.__methods[f];var g=c.chainingBehavior;c.chainingBehavior=function(){var a=e(g).call(this);return void 0===a?this:a};var a=c.method;c.method=function(){var c=b(a).apply(this,arguments);return void 0===c?this:c}}});f.register("chai/lib/chai/utils/test.js",function(m,g){var c=f("chai/lib/chai/utils/flag.js");g.exports=function(f,b){var e=c(f,"negate"),g=b[0];return e?!g:g}});f.register("chai/lib/chai/utils/transferFlags.js",function(f,g){g.exports=function(c,f,b){var e=
c.__flags||(c.__flags=Object.create(null));f.__flags||(f.__flags=Object.create(null));b=3===arguments.length?b:!0;for(var g in e)if(b||"object"!==g&&"ssfi"!==g&&"message"!=g)f.__flags[g]=e[g]}});f.register("chai/lib/chai/utils/type.js",function(f,g){var c={"[object Arguments]":"arguments","[object Array]":"array","[object Date]":"date","[object Function]":"function","[object Number]":"number","[object RegExp]":"regexp","[object String]":"string"};g.exports=function(f){var b=Object.prototype.toString.call(f);
return c[b]?c[b]:null===f?"null":void 0===f?"undefined":f===Object(f)?"object":typeof f}});"object"==typeof exports?module.exports=f("chai"):"function"==typeof define&&define.amd?define("chai",[],function(){return f("chai")}):(this||window).chai=f("chai")})();

View File

@ -1,59 +0,0 @@
var slice = Array.prototype.slice;
suite('AnimeClient methods exist', function () {
test("AnimeClient exists", function () {
expect(AnimeClient).to.be.ok;
});
['$', 'scrollToTop', 'showMessage', 'show', 'hide', 'closestParent', 'url', 'throttle', 'on', 'ajax', 'get'].forEach((method) => {
test("AnimeClient." + method + ' exists.', function () {
expect(AnimeClient[method]).to.be.ok;
});
});
});
suite('AnimeClient.$', function () {
test('$ returns an array', function () {
var matched = AnimeClient.$('div');
expect(matched).to.be.an('array');
});
test('$ returns same element as "getElementById"', function () {
var actual = AnimeClient.$('#mocha')[0];
var expected = document.getElementById('mocha');
expect(actual).to.equal(expected);
});
test('$ returns same elements as "getElementsByClassName"', function () {
var actual = AnimeClient.$('.progress');
var expected = slice.apply(document.getElementsByClassName('progress'));
expect(actual).to.deep.equal(expected);
});
test('$ returns same elements as "getElementsByTagName"', function () {
var actual = AnimeClient.$('ul');
var expected = slice.apply(document.getElementsByTagName('ul'));
expect(actual).to.deep.equal(expected);
});
});
suite('AnimeClient.url', function () {
test('url method has expected result', function () {
let expected = `//${document.location.host}/path`;
expect(AnimeClient.url('/path')).to.equal(expected);
});
});
suite('AnimeClient.closestParent', function () {
test('".grandChild" closest "section" is "#parentTest"', function () {
let sel = AnimeClient.$('.grandChild')[0];
let expected = document.getElementById('parentTest');
expect(AnimeClient.closestParent(sel, 'section')).to.equal(expected);
});
test('".child" closest "article" is "#parentTest"', function () {
let sel = AnimeClient.$('.child')[0];
let expected = document.getElementById('parentTest');
expect(AnimeClient.closestParent(sel, 'section')).to.equal(expected);
});
});

View File

@ -1,100 +0,0 @@
suite('AnimeClient.ajax', function () {
'use strict';
test('AnimeClient.get method', function (done) {
AnimeClient.get('ajax.php', function (res) {
expect(res).to.be.ok;
done();
});
});
test('GET', function (done) {
AnimeClient.ajax('ajax.php', {
success: function (res) {
expect(res).to.be.ok;
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('POST', function (done) {
AnimeClient.ajax('ajax.php', {
type: 'POST',
success: function (res) {
expect(res).to.be.ok;
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('PUT', function (done) {
AnimeClient.ajax('ajax.php', {
type: 'PUT',
success: function (res) {
expect(res).to.be.ok;
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('DELETE', function (done) {
AnimeClient.ajax('ajax.php', {
type: 'DELETE',
success: function (res) {
expect(res).to.be.ok;
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('POST with data', function (done) {
var expected = '{"foo":"data"}';
AnimeClient.ajax('ajax.php?data', {
data: {foo:'data'},
type: 'POST',
success: function (res) {
expect(res).to.be.equal(expected);
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('PUT with data', function (done) {
var expected = '{"bar":"data"}';
AnimeClient.ajax('ajax.php?data', {
data: {bar:'data'},
type: 'POST',
success: function (res) {
expect(res).to.be.equal(expected);
done();
},
error: function (err) {
expect.fail;
done();
}
});
});
test('Bad request', function (done) {
AnimeClient.ajax('ajax.php?bad', {
error: function (status) {
expect(status).to.be.equal(401);
done();
}
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -4,22 +4,20 @@
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.4
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2020 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient;
use Aviat\AnimeClient\Types\Config as ConfigType;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
use Tracy\Debugger;
use function Aviat\Ion\_dir;
setlocale(LC_CTYPE, 'en_US');
@ -27,12 +25,9 @@ setlocale(LC_CTYPE, 'en_US');
// Load composer autoloader
require_once __DIR__ . '/vendor/autoload.php';
if (file_exists('.is-dev'))
{
$whoops = new Run;
$whoops->pushHandler(new PrettyPageHandler);
$whoops->register();
}
Debugger::$strictMode = E_ALL & ~E_DEPRECATED; // all errors except deprecated notices
Debugger::$showBar = false;
Debugger::enable(Debugger::DEVELOPMENT, __DIR__ . '/app/logs');
// Define base directories
$APP_DIR = _dir(__DIR__, 'app');
@ -45,7 +40,7 @@ $CONF_DIR = _dir($APP_DIR, 'config');
$baseConfig = require "{$APPCONF_DIR}/base_config.php";
$di = require "{$APP_DIR}/bootstrap.php";
$config = loadToml($CONF_DIR);
$config = loadConfig($CONF_DIR);
$overrideFile = "{$CONF_DIR}/admin-override.toml";
$overrideConfig = file_exists($overrideFile)
@ -60,11 +55,11 @@ $checkedConfig = ConfigType::check($configArray);
// First look in app config, then PHP config, and at last
// resort, just set to UTC.
$timezone = ini_get('date.timezone');
if (array_key_exists('timezone', $checkedConfig) && ! empty($checkedConfig['timezone']))
if (is_array($checkedConfig) && array_key_exists('timezone', $checkedConfig) && ! empty($checkedConfig['timezone']))
{
date_default_timezone_set($checkedConfig['timezone']);
}
else if ($timezone !== '')
else if (is_string($timezone) && $timezone !== '')
{
date_default_timezone_set($timezone);
}

View File

@ -2,17 +2,21 @@ parameters:
checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false
inferPrivatePropertyTypeFromConstructor: true
level: 7
autoload_files:
- %rootDir%/../../../tests/mocks.php
level: 8
paths:
- app/appConf
- src
- ./console
- index.php
ignoreErrors:
- '#Access to an undefined property Aviat\\\Ion\\\Friend::\$[a-zA-Z0-9_]+#'
- '#Call to an undefined method Aviat\\\Ion\\\Friend::[a-zA-Z0-9_]+\(\)#'
- "#Offset 'fields' does not exist on array#"
- '#Function imagepalletetotruecolor not found#'
- '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#'
- '#Property Amp\\Artax\\Internal\\RequestCycle::\$[a-zA-Z0-9_]+#'
- '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#'
excludes_analyse:
- tests/mocks.php
- tests/mocks.php
- vendor
# These are objects that basically can return anything
universalObjectCratesClasses:
- Aviat\Ion\Friend
tmpDir: tmp

View File

@ -1,24 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"
>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
<testsuites>
<testsuite name="AnimeClient">
<directory>tests</directory>
</testsuite>
</testsuites>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0" />
<server name="HTTP_HOST" value="localhost" />
<server name="SERVER_NAME" value="localhost" />
<server name="REQUEST_URI" value="/" />
<server name="REQUEST_METHOD" value="GET" />
</php>
</phpunit>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" stopOnFailure="false" bootstrap="tests/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="AnimeClient">
<directory>tests/AnimeClient</directory>
</testsuite>
<testsuite name="Ion">
<directory>tests/Ion</directory>
</testsuite>
</testsuites>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0"/>
<server name="HTTP_HOST" value="localhost"/>
<server name="SERVER_NAME" value="localhost"/>
<server name="REQUEST_URI" value="/"/>
<server name="REQUEST_METHOD" value="GET"/>
</php>
</phpunit>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More