├── web ├── favicon.ico ├── vendor │ ├── jszip-utils.min.js │ └── jszip.min.js ├── shapeshift.css ├── index.html └── shapeshift.dart ├── .gitignore ├── lib ├── shapeshift_frontend.dart ├── shapeshift_cli.dart ├── src │ ├── frontend │ │ ├── null_tree_sanitizer.dart │ │ ├── html_writer_provider.dart │ │ ├── html_writer.dart │ │ ├── js_zip_package_reporter.dart │ │ └── js_zip_wrapper.dart │ ├── cli │ │ ├── directory_diff_writer_provider.dart │ │ └── directory_package_reporter.dart │ └── common │ │ ├── utils.dart │ │ ├── library_api_diff.dart │ │ ├── reporters │ │ ├── package_reporter.dart │ │ ├── methods_reporter.dart │ │ ├── library_reporter.dart │ │ ├── variables_reporter.dart │ │ ├── class_reporter.dart │ │ ├── classes_reporter.dart │ │ └── method_attributes_reporter.dart │ │ ├── api_differ.dart │ │ └── markdown_diff_writer.dart └── shapeshift_common.dart ├── pubspec.yaml ├── test ├── test_all_the_things.dart ├── methods_reporter_test.dart ├── class_reporter_test.dart ├── utils_test.dart ├── test_helpers.dart ├── variables_reporter_test.dart └── method_attributes_reporter_test.dart ├── CONTRIBUTING.md ├── bin └── shapeshift.dart ├── pubspec.lock ├── README.md └── LICENSE /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/dart-shapeshift/HEAD/web/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | packages 3 | .buildlog 4 | .project 5 | .pub/ 6 | .DS_Store 7 | *.dart.js 8 | *.js_ 9 | *.js.deps 10 | *.js.map 11 | 12 | diff* 13 | sdk-* 14 | -------------------------------------------------------------------------------- /lib/shapeshift_frontend.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift.frontend; 5 | 6 | export 'src/frontend/js_zip_wrapper.dart'; 7 | -------------------------------------------------------------------------------- /lib/shapeshift_cli.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_cli; 5 | 6 | export 'package:doc_coverage/doc_coverage_cli.dart'; 7 | export 'package:doc_coverage/doc_coverage_common.dart'; 8 | 9 | export 'src/cli/directory_package_reporter.dart'; 10 | export 'src/cli/directory_diff_writer_provider.dart'; 11 | -------------------------------------------------------------------------------- /lib/src/frontend/null_tree_sanitizer.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shaepshift.frontend.null_tree_sanitiver; 5 | 6 | import 'dart:html'; 7 | 8 | class _NullTreeSanitizer implements NodeTreeSanitizer { 9 | const _NullTreeSanitizer(); 10 | void sanitizeTree(Node node) {} 11 | } 12 | 13 | const nullTreeSanitizer = const _NullTreeSanitizer(); 14 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: shapeshift 2 | description: Understand how the shape of your Dart package is shifting. 3 | environment: 4 | sdk: '>=1.9.0 <2.0.0' 5 | dependencies: 6 | args: any 7 | diff_match_patch: ^0.2.1 8 | doc_coverage: ^0.1.5 9 | http: ^0.11.1+1 10 | json_diff: ^0.1.2 11 | markdown: ^0.7.1+2 12 | path: ^1.3.3 13 | pub_semver: ^1.2.1 14 | quiver: ^0.21.3+1 15 | route_hierarchical: ^0.6.2 16 | sdk_builds: ^0.1.0 17 | dev_dependencies: 18 | test: ^0.12.0 19 | transformers: 20 | - $dart2js: 21 | commandLineOptions: [--show-package-warnings] 22 | -------------------------------------------------------------------------------- /lib/src/frontend/html_writer_provider.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shaepshift.frontend.html_writer_provider; 5 | 6 | import '../../shapeshift_common.dart'; 7 | import 'package:doc_coverage/doc_coverage_common.dart'; 8 | 9 | import 'html_writer.dart'; 10 | 11 | class HtmlWriterProvider extends WriterProvider { 12 | HtmlWriter sink; 13 | HtmlWriterProvider(this.sink); 14 | 15 | MarkdownWriter writerFor(String _) => new MarkdownDiffWriter(() => sink, 16 | shouldClose: false, shouldWriteMetadata: false); 17 | } 18 | -------------------------------------------------------------------------------- /test/test_all_the_things.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import 'class_reporter_test.dart' as class_reporter_test; 4 | import 'method_attributes_reporter_test.dart' 5 | as method_attributes_reporter_test; 6 | import 'methods_reporter_test.dart' as methods_reporter_test; 7 | import 'utils_test.dart' as utils_test; 8 | import 'variables_reporter_test.dart' as variables_reporter_test; 9 | 10 | void main() { 11 | group('class_reporter', class_reporter_test.main); 12 | group('method_attributes_reporter', method_attributes_reporter_test.main); 13 | group('methods_reporter', methods_reporter_test.main); 14 | group('utils', utils_test.main); 15 | group('variables_reporter', variables_reporter_test.main); 16 | } 17 | -------------------------------------------------------------------------------- /lib/shapeshift_common.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common; 5 | 6 | export 'package:doc_coverage/doc_coverage_common.dart'; 7 | export 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | export 'src/common/api_differ.dart'; 10 | export 'src/common/library_api_diff.dart'; 11 | export 'src/common/markdown_diff_writer.dart'; 12 | export 'src/common/utils.dart'; 13 | 14 | export 'src/common/reporters/class_reporter.dart'; 15 | export 'src/common/reporters/library_reporter.dart'; 16 | export 'src/common/reporters/method_attributes_reporter.dart'; 17 | export 'src/common/reporters/methods_reporter.dart'; 18 | export 'src/common/reporters/package_reporter.dart'; 19 | export 'src/common/reporters/variables_reporter.dart'; 20 | -------------------------------------------------------------------------------- /lib/src/cli/directory_diff_writer_provider.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_cli.directory_diff_writer_provider; 5 | 6 | import 'package:doc_coverage/doc_coverage_cli.dart'; 7 | import 'package:doc_coverage/doc_coverage_common.dart'; 8 | import '../../shapeshift_common.dart'; 9 | 10 | /// A [WriterProvider] that provides a [MarkdownDiffWriter] that writes to a new 11 | /// Markdown file for each library. 12 | class DirectoryDiffWriterProvider extends DirectoryWriterProvider { 13 | /// Constructs a [DirectoryDiffWriterProvider] that will use [path] when creating 14 | /// new writers. 15 | DirectoryDiffWriterProvider(path) : super(path); 16 | 17 | MarkdownWriter writerCtor(fileTargetBuilder, {bool shouldClose: true}) => 18 | new MarkdownDiffWriter(fileTargetBuilder, shouldClose: shouldClose); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/common/utils.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.utils; 5 | 6 | RegExp oldStyle = 7 | new RegExp(r'"(name|outer|qualifiedName|return|superclass)":\s*"dart-'); 8 | RegExp oldStyleList = 9 | new RegExp(r'"implements":\s*\[(("dart-[^"]+"(,\s*)?)+)\]'); 10 | RegExp oldStyleLink = new RegExp(r'a( href="[^"]*")?>dart-'); 11 | 12 | String scrubHyphens(String json) { 13 | String _replaceAllNamesInAList(Match m) { 14 | var names = m[1].replaceAll(new RegExp(r'"dart-'), r'"dart:'); 15 | return '"implements":[$names]'; 16 | } 17 | 18 | return json 19 | .replaceAllMapped(oldStyle, (Match m) => '"${m[1]}":"dart:') 20 | .replaceAllMapped(oldStyleList, _replaceAllNamesInAList) 21 | .replaceAllMapped( 22 | oldStyleLink, (Match m) => 'a${m[1] == null ? '' : m[1]}>dart:'); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/common/library_api_diff.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.libarry_api_diff; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | import 'reporters/class_reporter.dart'; 10 | import 'reporters/library_reporter.dart'; 11 | 12 | class LibraryApiDiff { 13 | String libraryName; 14 | DiffNode lybrary; 15 | final List classes = new List(); 16 | 17 | LibraryApiDiff(this.libraryName, this.lybrary); 18 | 19 | bool get isUninitialized => libraryName == null; 20 | 21 | void report(MarkdownWriter writer) { 22 | writer.writeMetadata(libraryName); 23 | new LibraryReporter(lybrary, writer).report(); 24 | classes.forEach((diff) => new ClassReporter(diff, writer).report()); 25 | writer.close(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/frontend/html_writer.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift.frontend.html_writer; 5 | 6 | import 'dart:html'; 7 | 8 | import 'package:markdown/markdown.dart' hide Element, Node; 9 | 10 | import 'null_tree_sanitizer.dart'; 11 | 12 | class HtmlWriter extends StringSink { 13 | final Element el; 14 | 15 | HtmlWriter(this.el); 16 | 17 | void write(Object obj) { 18 | // I'm a little worried about this... I have to gunarantee _no_ nesting 19 | // here, like `write("* bullet"); write("* another bullet");`. 20 | el.setInnerHtml(el.innerHtml + markdownToHtml(obj.toString()), 21 | treeSanitizer: nullTreeSanitizer); 22 | } 23 | 24 | void writeAll(Iterable objects, [String separator = ""]) { 25 | // Don't use. 26 | } 27 | 28 | void writeCharCode(int charCode) { 29 | // Don't use. 30 | } 31 | 32 | void writeln([Object obj = '']) => write(obj.toString() + '\n'); 33 | } 34 | -------------------------------------------------------------------------------- /test/methods_reporter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Unit tests for Shapeshift's methods reporter. 5 | library methods_reporter_tests; 6 | 7 | import 'package:shapeshift/shapeshift_common.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | import 'test_helpers.dart'; 11 | 12 | void main() { 13 | ReadableStringSink io; 14 | Map v1, v2; 15 | 16 | expectIoContains(RegExp re) => expect(io.read(), matches(re)); 17 | 18 | setUp(() { 19 | io = new ReadableStringSink(); 20 | }); 21 | 22 | test('Shapeshift reports on new methods', () { 23 | v1 = classWithMethods({'foo': methodNamed('foo')}); 24 | v2 = classWithMethods( 25 | {'foo': methodNamed('foo'), 'bar': methodNamed('bar')}); 26 | 27 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 28 | expectIoContains(new RegExp(r'''New method \[bar\]\(.*\): 29 | 30 | ```dart 31 | ///

Send a data event to a stream.

32 | dart:core.String bar\(\) 33 | ```''')); 34 | }); 35 | } 36 | 37 | void diffAndReport(String v1, String v2, ReadableStringSink io) { 38 | DiffNode diff = diffApis(v1, v2); 39 | MarkdownDiffWriter w = new MarkdownDiffWriter(() => io, shouldClose: false); 40 | Function noop = (Map m, [String key]) {}; 41 | new MethodsReporter('methods', diff['methods']['methods'], w, noop).report(); 42 | } 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | 5 | Before we can use your code, you must sign the 6 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | 15 | Before you start working on a larger contribution, you should get in touch with 16 | us first through the issue tracker with your idea so that we can help out and 17 | possibly guide you. Coordinating up front makes it much easier to avoid 18 | frustration later on. 19 | 20 | ### Code reviews 21 | 22 | All submissions, including submissions by project members, require review. We 23 | use Github pull requests for this purpose. 24 | 25 | ### The small print 26 | 27 | Contributions made by corporations are covered by a different agreement than 28 | the one above, the Software Grant and Corporate Contributor License Agreement. 29 | -------------------------------------------------------------------------------- /bin/shapeshift.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:args/args.dart'; 7 | import 'package:path/path.dart' as path; 8 | 9 | import 'package:shapeshift/shapeshift_cli.dart'; 10 | import 'package:shapeshift/shapeshift_common.dart'; 11 | 12 | class Shapeshift { 13 | ArgResults args; 14 | 15 | void go(List arguments) { 16 | parseArgs(arguments); 17 | if (args.rest.isEmpty) { 18 | print('Usage: shapeshift.dart [options] '); 19 | exit(0); 20 | } 21 | String left = args.rest[0]; 22 | String right = args.rest[1]; 23 | String leftPath = path.join(args['base'], left); 24 | String rightPath = path.join(args['base'], right); 25 | if (args['subset'].isNotEmpty) { 26 | leftPath = path.join(leftPath, args['subset']); 27 | rightPath = path.join(rightPath, args['subset']); 28 | } 29 | 30 | WriterProvider writer = (args['out'] == null) 31 | ? new SingleSinkWriterProvider(stdout) 32 | : new DirectoryDiffWriterProvider(args['out']); 33 | 34 | var report = 35 | new DirectoryPackageReporter(leftPath, rightPath).calculateAllDiffs(); 36 | 37 | report.write(writer); 38 | } 39 | 40 | void parseArgs(List arguments) { 41 | var parser = new ArgParser(); 42 | parser.addOption('base', 43 | defaultsTo: '/Users/srawlins/code/dartlang.org/api-docs'); 44 | parser.addOption('subset', defaultsTo: ''); 45 | parser.addOption('out'); 46 | args = parser.parse(arguments); 47 | } 48 | } 49 | 50 | void main(List arguments) => new Shapeshift().go(arguments); 51 | -------------------------------------------------------------------------------- /web/vendor/jszip-utils.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.JSZipUtils=a():"undefined"!=typeof global?global.JSZipUtils=a():"undefined"!=typeof self&&(self.JSZipUtils=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g diff = new Map(); 22 | final Map libraryDiffs = 23 | new Map(); 24 | 25 | void add(String name, DiffNode node) { 26 | diff[name] = node; 27 | 28 | if (node.metadata['packageName'] != null) { 29 | // Here, file represents the API of a library. 30 | String libraryName = node.metadata['qualifiedName']; 31 | if (!libraryDiffs.containsKey(libraryName)) { 32 | libraryDiffs[libraryName] = new LibraryApiDiff(libraryName, node); 33 | } else if (libraryDiffs[libraryName].isUninitialized) { 34 | libraryDiffs[libraryName].libraryName = libraryName; 35 | libraryDiffs[libraryName].lybrary = node; 36 | } 37 | } else { 38 | // Here, file represents the API of a class... 39 | // or other library member(?). 40 | String libraryName = node.metadata['qualifiedName'].split('.')[0]; 41 | var lib = libraryDiffs.putIfAbsent(libraryName, () { 42 | return new LibraryApiDiff(null, null); 43 | }); 44 | lib.classes.add(node); 45 | } 46 | } 47 | 48 | void write(WriterProvider writer) { 49 | libraryDiffs.forEach((String name, LibraryApiDiff libraryDiff) { 50 | libraryDiff.report(writer.writerFor(name)); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/class_reporter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Unit tests for Shapeshift's class reporter. 5 | library class_reporter_tests; 6 | 7 | import 'package:shapeshift/shapeshift_common.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | import 'test_helpers.dart'; 11 | 12 | void main() { 13 | ReadableStringSink io; 14 | Map v1, v2; 15 | 16 | expectIoContains(RegExp re) => expect(io.read(), matches(re)); 17 | 18 | setUp(() { 19 | io = new ReadableStringSink(); 20 | }); 21 | 22 | test('Shapeshift reports on changed class superclass', () { 23 | v1 = baseClass; 24 | v2 = baseClass..['superclass'] = 'dart:core.String'; 25 | 26 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 27 | expectIoContains(new RegExp(r'''class \[Foo\]\(.*\) 28 | ---+ 29 | 30 | Foo's `superclass` changed: 31 | 32 | Was: dart:core.Object 33 | 34 | Now: \[dart:core.String\]\(.*\)''')); 35 | }); 36 | 37 | test('Shapeshift reports on new class annotations', () { 38 | v1 = baseClass; 39 | v2 = baseClass 40 | ..['annotations'] 41 | .add({'name': 'metadata.Unstable.Unstable-', 'parameters': []}); 42 | 43 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 44 | expectIoContains(new RegExp(r'''class \[Foo\]\(.*\) 45 | ---+ 46 | 47 | Foo has new annotations: 48 | 49 | \* `@Unstable-\(\)`''')); 50 | }); 51 | 52 | test('Shapeshift reports on new subclasses', () { 53 | v1 = baseClass; 54 | v2 = baseClass..['subclass'].add('foo.Bar'); 55 | 56 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 57 | expectIoContains(new RegExp(r'''class \[Foo\]\(.*\) 58 | ---+ 59 | 60 | Foo has new subclasses: 61 | 62 | \* \[foo.Bar\]\(.*\)''')); 63 | }); 64 | } 65 | 66 | void diffAndReport(String v1, String v2, ReadableStringSink io) { 67 | DiffNode diff = diffApis(v1, v2); 68 | MarkdownDiffWriter w = new MarkdownDiffWriter(() => io, shouldClose: false); 69 | new ClassReporter(diff, w).report(); 70 | } 71 | -------------------------------------------------------------------------------- /test/utils_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Unit tests for Shapeshift's utils. 5 | library utils_tests; 6 | 7 | import 'package:shapeshift/shapeshift_common.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | void main() { 11 | test('Shapeshift scrubHyphens doesn\'t change OK strings', () { 12 | String api = 'not necessarily json'; 13 | expect(scrubHyphens(api), equals(api)); 14 | }); 15 | 16 | test('Shapeshift scrubHyphens can scrub old-style names', () { 17 | String scrubbedApi = scrubHyphens('"name":"dart-foo.Foo'); 18 | String expected = '"name":"dart:foo.Foo'; 19 | expect(scrubbedApi, equals(expected)); 20 | }); 21 | 22 | test('Shapeshift scrubHyphens can scrub old-style qualifiedNames', () { 23 | String scrubbedApi = scrubHyphens('"qualifiedName":"dart-foo.Foo'); 24 | String expected = '"qualifiedName":"dart:foo.Foo'; 25 | expect(scrubbedApi, equals(expected)); 26 | }); 27 | 28 | test('Shapeshift scrubHyphens can scrub old-style outer fields', () { 29 | String scrubbedApi = scrubHyphens('"outer":"dart-foo.Foo'); 30 | String expected = '"outer":"dart:foo.Foo'; 31 | expect(scrubbedApi, equals(expected)); 32 | }); 33 | 34 | test('Shapeshift scrubHyphens can scrub old-style returns', () { 35 | String scrubbedApi = scrubHyphens('"return":"dart-foo.Foo'); 36 | String expected = '"return":"dart:foo.Foo'; 37 | expect(scrubbedApi, equals(expected)); 38 | }); 39 | 40 | test('Shapeshift scrubHyphens can scrub old-style superclasses', () { 41 | String scrubbedApi = scrubHyphens('"superclass":"dart-foo.Foo'); 42 | String expected = '"superclass":"dart:foo.Foo'; 43 | expect(scrubbedApi, equals(expected)); 44 | }); 45 | 46 | test('Shapeshift scrubHyphens can scrub old-style implements list', () { 47 | String scrubbedApi = 48 | scrubHyphens('"implements":["dart-foo.Foo", "dart-bar.Bar"]'); 49 | String expected = '"implements":["dart:foo.Foo", "dart:bar.Bar"]'; 50 | expect(scrubbedApi, equals(expected)); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/common/reporters/methods_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.method_reporter; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | import '../markdown_diff_writer.dart'; 10 | import 'method_attributes_reporter.dart'; 11 | 12 | class MethodsReporter { 13 | final DiffNode diff; 14 | final MarkdownDiffWriter io; 15 | final Function erase; 16 | 17 | String category; 18 | String parenthetical; 19 | 20 | MethodsReporter(_category, this.diff, this.io, this.erase, 21 | {this.parenthetical: ''}) { 22 | category = singularize(_category); 23 | if (parenthetical.isNotEmpty) parenthetical = ' _($parenthetical)_'; 24 | } 25 | 26 | void report() { 27 | diff.forEachAdded(reportEachAdded); 28 | erase(diff.added); 29 | 30 | diff.forEachRemoved(reportEachRemoved); 31 | erase(diff.removed); 32 | 33 | diff.forEach((method, attributes) { 34 | new MethodAttributesReporter(category, method, attributes, io, erase) 35 | .report(); 36 | }); 37 | } 38 | 39 | void reportEachAdded(String methodName, Map method) { 40 | String link = mdLinkToDartlang(method['qualifiedName'], methodName); 41 | bool includeType = category != 'constructor'; 42 | bool includeParens = category != 'setter' && category != 'getter'; 43 | io.writeln('New $category$parenthetical $link:\n'); 44 | io.writeCodeblockHr(methodSignature(method, 45 | includeReturn: includeType, includeParens: includeParens)); 46 | } 47 | 48 | void reportEachRemoved(String methodName, Map method) { 49 | if (methodName == '') methodName = diff.metadata['name']; 50 | bool includeType = category != 'constructor'; 51 | bool includeParens = category != 'setter' && category != 'getter'; 52 | io.writeln('Removed $category$parenthetical $methodName:\n'); 53 | io.writeCodeblockHr(methodSignature(method, 54 | includeComment: false, 55 | includeAnnotations: false, 56 | includeReturn: includeType, 57 | includeParens: includeParens)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/test_helpers.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Test helpers for Shapeshift's tests. 5 | library test_helpers; 6 | 7 | import 'dart:convert'; 8 | 9 | jsonFrom(Map obj) => new JsonEncoder().convert(obj); 10 | 11 | Map classWithVariables(Map variables) => 12 | baseClass..['variables'] = variables; 13 | 14 | Map classWithMethods(Map methods) => 15 | baseClass..['methods']['methods'] = methods; 16 | 17 | Map get baseClass => { 18 | 'name': 'Foo', 19 | 'qualifiedName': 'foo.Foo', 20 | 'comment': '', 21 | 'isAbstract': false, 22 | 'superclass': 'dart:core.Object', 23 | 'implements': [], 24 | 'subclass': [], 25 | 'variables': {}, 26 | 'methods': { 27 | 'setters': {}, 28 | 'getters': {}, 29 | 'constructors': {}, 30 | 'operators': {}, 31 | 'methods': {}, 32 | }, 33 | 'annotations': [], 34 | }; 35 | 36 | Map variableNamed(String name) => baseVariable 37 | ..['name'] = name 38 | ..['qualifiedName'] = 'foo.Foo.$name'; 39 | 40 | Map get baseVariable => { 41 | 'name': 'foo', 42 | 'qualifiedName': 'foo.Foo.foo', 43 | 'comment': '

Send a data event to a stream.

', 44 | 'final': false, 45 | 'static': false, 46 | 'constant': false, 47 | 'type': [{'outer': 'dart:core.String', 'inner': []},], 48 | 'annotations': [], 49 | }; 50 | 51 | Map methodNamed(String name) => baseMethod 52 | ..['name'] = name 53 | ..['qualifiedName'] = 'foo.Foo.$name'; 54 | 55 | Map get baseMethod => { 56 | 'name': 'foo', 57 | 'qualifiedName': 'foo.Foo.foo', 58 | 'comment': '

Send a data event to a stream.

', 59 | 'commentFrom': '', 60 | 'inheritedFrom': '', 61 | 'static': false, 62 | 'abstract': false, 63 | 'constant': false, 64 | 'return': [{'outer': 'dart:core.String', 'inner': []},], 65 | 'parameters': {}, 66 | 'annotations': [], 67 | }; 68 | 69 | Map get baseParameter => { 70 | 'name': 'p', 71 | 'optional': false, 72 | 'named': false, 73 | 'default': false, 74 | 'type': [{'outer': 'dart:core.String', 'inner': []}], 75 | }; 76 | -------------------------------------------------------------------------------- /lib/src/common/api_differ.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.api_differ; 5 | 6 | import 'package:json_diff/json_diff.dart'; 7 | import 'package:sdk_builds/sdk_builds.dart'; 8 | 9 | import 'utils.dart'; 10 | 11 | const _lastRevisionWithHyphens = 41515; 12 | 13 | class ApiDiffer { 14 | final String a, b; 15 | final bool includeComments; 16 | 17 | ApiDiffer(this.a, this.b, {this.includeComments: true}); 18 | 19 | DiffNode diff() { 20 | JsonDiffer differ = new JsonDiffer(a, b); 21 | differ.atomics..add('type')..add('return')..add('annotations[]'); 22 | differ.metadataToKeep..add('qualifiedName'); 23 | if (!includeComments) differ.ignored.add('comment'); 24 | differ.ensureIdentical(['name', 'qualifiedName']); 25 | return differ.diff() 26 | ..metadata['qualifiedName'] = differ.leftJson['qualifiedName'] 27 | ..metadata['name'] = differ.leftJson['name'] 28 | ..metadata['packageName'] = differ.leftJson['packageName']; 29 | } 30 | } 31 | 32 | DiffNode diffApis(String a, String b, {includeComments: true}) => 33 | new ApiDiffer(a, b, includeComments: includeComments).diff(); 34 | 35 | DiffNode diffSdkApis(String left, String right, VersionInfo leftRevision, 36 | VersionInfo rightRevision, {includeComments: true}) { 37 | // The dartdoc utility used to generate JSON for the Dart SDK with names of 38 | // libraries and library members that looked like "dart-core", 39 | // "dart-core.String", etc. After revision 41515 (Dart 1.8.0-dev.3.0), 40 | // dartdoc generated JSON with names that look instead like "dart:core", and 41 | // "dart:core.String". So when comparing a revision <= 41515 with a 42 | // revision > 41515, the diff will think that every single library and 43 | // library member was renamed. So we have to do this ugly munging. 44 | 45 | if (leftRevision is SvnVersionInfo && 46 | leftRevision.revision <= _lastRevisionWithHyphens) { 47 | if (rightRevision is GitVersionInfo || 48 | (rightRevision as SvnVersionInfo).revision > _lastRevisionWithHyphens) { 49 | left = scrubHyphens(left); 50 | } 51 | } 52 | 53 | return diffApis(left, right, includeComments: includeComments); 54 | } 55 | -------------------------------------------------------------------------------- /test/variables_reporter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Unit tests for Shapeshift's variables reporter. 5 | library variables_reporter_tests; 6 | 7 | import 'package:shapeshift/shapeshift_common.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | import 'test_helpers.dart'; 11 | 12 | void main() { 13 | ReadableStringSink io; 14 | Map v1, v2; 15 | 16 | expectIoContains(String re) => expect(io.read(), matches(new RegExp(re))); 17 | 18 | setUp(() { 19 | io = new ReadableStringSink(); 20 | }); 21 | 22 | test('Shapeshift reports on new variables', () { 23 | v1 = classWithVariables({'foo': variableNamed('foo')}); 24 | v2 = classWithVariables( 25 | {'foo': variableNamed('foo'), 'bar': variableNamed('bar')}); 26 | 27 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 28 | expectIoContains(r'New variables:\n' 29 | r'\n' 30 | r'```dart\n' 31 | r'dart:core.String bar;\n' 32 | r'```'); 33 | }); 34 | 35 | test('Shapeshift reports on removed variables', () { 36 | v1 = classWithVariables( 37 | {'foo': variableNamed('foo'), 'bar': variableNamed('bar')}); 38 | v2 = classWithVariables({'foo': variableNamed('foo')}); 39 | 40 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 41 | expectIoContains(r'Removed variables:\n' 42 | r'\n' 43 | r'```dart\n' 44 | r'dart:core.String bar;\n' 45 | r'```'); 46 | }); 47 | 48 | test('Shapeshift reports on variables with changed attributes', () { 49 | v1 = classWithVariables({'foo': variableNamed('foo')}); 50 | Map v2Variable = variableNamed('foo') 51 | ..['annotations'] 52 | .add({'name': 'metadata.Unstable.Unstable-', 'parameters': []}); 53 | v2 = classWithVariables({'foo': v2Variable}); 54 | 55 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 56 | expectIoContains(r"The \[foo\]\(.*\) variable has new annotations:\n" 57 | r'\n' 58 | r'\* `@Unstable-\(\)`'); 59 | }); 60 | } 61 | 62 | void diffAndReport(String v1, String v2, ReadableStringSink io) { 63 | DiffNode diff = diffApis(v1, v2); 64 | MarkdownDiffWriter w = new MarkdownDiffWriter(() => io, shouldClose: false); 65 | Function noop = (Map m, [String key]) {}; 66 | new VariablesReporter('variables', diff['variables'], w, noop).report(); 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/common/reporters/library_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.library_reporter; 5 | 6 | import 'package:json_diff/json_diff.dart'; 7 | 8 | import '../markdown_diff_writer.dart'; 9 | import 'methods_reporter.dart'; 10 | import 'classes_reporter.dart'; 11 | 12 | class LibraryReporter { 13 | static bool shouldErase = true; 14 | 15 | final DiffNode diff; 16 | final MarkdownDiffWriter io; 17 | String qualifiedName; 18 | 19 | LibraryReporter(this.diff, this.io) { 20 | qualifiedName = diff.metadata['qualifiedName']; 21 | } 22 | 23 | void report() { 24 | if (diff == null) return; 25 | 26 | io.bufferH1(qualifiedName); 27 | reportLibrary(); 28 | 29 | // After reporting, prune and print anything remaining. 30 | diff.prune(); 31 | diff.metadata.clear(); 32 | String diffString = diff.toString(); 33 | if (diffString.isNotEmpty) { 34 | print('ERROR: $qualifiedName has unresolved nodes:\n$diffString'); 35 | } 36 | } 37 | 38 | void reportLibrary() { 39 | if (diff.changed.containsKey('packageIntro')) { 40 | io.writeBad( 41 | 'TODO: The packageIntro changed, which is probably ' 42 | 'huge. Not including here yet.'); 43 | erase(diff.changed, 'packageIntro'); 44 | } 45 | 46 | // Iterate over the class categories ('classes', 'typedefs', 'errors'). 47 | diff.forEachOf('classes', (String classCategory, DiffNode classDiff) => 48 | new ClassesReporter(classCategory, classDiff, qualifiedName, io, erase) 49 | .report()); 50 | 51 | diff.changed.forEach((String key, List oldNew) { 52 | io.writeln("${diff.metadata['name']}'s `${key}` changed:\n"); 53 | io.writeWasNow((oldNew as List)[0], (oldNew as List)[1], 54 | blockquote: key == 'comment'); 55 | io.writeHr(); 56 | }); 57 | diff.changed.clear(); 58 | 59 | // TODO: report variables. 60 | 61 | diff.forEachOf('functions', _reportEachMethodCategory); 62 | } 63 | 64 | void _reportEachMethodCategory(String methodCategory, DiffNode diff) => 65 | new MethodsReporter(methodCategory, diff, io, erase).report(); 66 | 67 | void erase(Map m, [String key]) { 68 | if (!shouldErase) return; 69 | 70 | if (key == null) m.clear(); 71 | else m.remove(key); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/cli/directory_package_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_cli.directory_package_reporter; 5 | 6 | import 'dart:io'; 7 | 8 | import 'package:doc_coverage/doc_coverage_common.dart'; 9 | import 'package:path/path.dart' as path; 10 | 11 | import '../../shapeshift_common.dart'; 12 | 13 | /// A [PackageReporter] that calculates the diff between APIs found in two 14 | /// directories. 15 | /// 16 | /// The constructor takes the paths of the two directories, and a [WriterProvider]. 17 | /// After it has been constructed, a [DirectoryPackageReporter] can calculate 18 | /// the diffs of all the files in the two directories, recursively, with 19 | /// [calculateAllDiffs], and can report the diffs into the [WriterProvider] with 20 | /// [report]. 21 | class DirectoryPackageReporter extends PackageReporter { 22 | final String leftPath, rightPath; 23 | 24 | DirectoryPackageReporter(this.leftPath, this.rightPath); 25 | 26 | void _calculateDiff(PackageReport report, String fileName) { 27 | File left = new File(path.join(leftPath, fileName)); 28 | File right = new File(path.join(rightPath, fileName)); 29 | if (!left.existsSync()) { 30 | String associatedLibrary = associatedLibraryJsonPath(left.path); 31 | if (associatedLibrary != null) { 32 | // fileName not found in the left path, which will be noted in the 33 | // library JSON file. Don't worry about it. 34 | } else { 35 | print('Hmm... "${left.path} doesn\'t exist, which is weird.'); 36 | } 37 | return; 38 | } 39 | report.add( 40 | fileName, diffApis(left.readAsStringSync(), right.readAsStringSync())); 41 | } 42 | 43 | PackageReport calculateAllDiffs() { 44 | List rightRawLs = new Directory(rightPath).listSync(recursive: true); 45 | List rightLs = rightRawLs 46 | .where((FileSystemEntity f) => f is File) 47 | .map((File f) => f.path) 48 | .toList(); 49 | 50 | var report = new PackageReport(); 51 | 52 | rightLs.forEach((String file) { 53 | file = path.relative(file, from: rightPath); 54 | if (path.basename(file) == 'index.json' || 55 | path.basename(file) == 'library_list.json' || 56 | path.extension(file) != '.json') { 57 | print('Skipping $file'); 58 | return; 59 | } 60 | _calculateDiff(report, file); 61 | }); 62 | 63 | return report; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/frontend/js_zip_package_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | import 'dart:async'; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:sdk_builds/sdk_builds.dart'; 8 | 9 | import '../../shapeshift_common.dart'; 10 | import 'js_zip_wrapper.dart'; 11 | 12 | /// A [PackageReporter] that calculates the diff between APIs found in two 13 | /// directories. 14 | /// 15 | /// The constructor takes the paths of the two directories, and a [WriterProvider]. 16 | /// After it has been constructed, a [DirectoryPackageReporter] can calculate 17 | /// the diffs of all the files in the two directories, recursively, with 18 | /// [calculateAllDiffs], and can report the diffs into the [WriterProvider] with 19 | /// [report]. 20 | class JSZipPackageReporter extends PackageReporter { 21 | final JSZipWrapper leftZip, rightZip; 22 | final VersionInfo leftRevision, rightRevision; 23 | 24 | JSZipPackageReporter( 25 | this.leftZip, this.rightZip, this.leftRevision, this.rightRevision, 26 | {includeComments: true}) 27 | : super(includeComments: includeComments); 28 | 29 | void _calculateDiff(PackageReport report, String fileName) { 30 | // If fileName not found in the left Zip, it will be noted in the library 31 | // JSON file. 32 | if (!leftZip.hasFile(fileName)) return; 33 | report.add(fileName, diffSdkApis(leftZip.read(fileName), 34 | rightZip.read(fileName), leftRevision, rightRevision, 35 | includeComments: includeComments)); 36 | } 37 | 38 | /// This can be *slow*, so it is async and it inserts a small delay before 39 | /// processing each file to ensure UI – like a web page – can update. 40 | Future calculateDiffForLibrary(String library) async { 41 | var report = new PackageReport(); 42 | 43 | for (var fileName in rightZip.filesByLibrary[library]) { 44 | await _calculateDiff(report, fileName); 45 | await letHtmlUpdate(); 46 | } 47 | 48 | return report; 49 | } 50 | 51 | PackageReport calculateAllDiffs() { 52 | var report = new PackageReport(); 53 | 54 | for (var fileName in rightZip.filesByLibrary['dart-core']) { 55 | _calculateDiff(report, fileName); 56 | } 57 | 58 | return report; 59 | } 60 | } 61 | 62 | Future letHtmlUpdate() async { 63 | // inject a delay so that the html updates 64 | await new Future.delayed(const Duration(seconds: 0)); 65 | } 66 | -------------------------------------------------------------------------------- /test/method_attributes_reporter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | /// Unit tests for Shapeshift's method attributes reporter. 5 | library method_attributes_reporter_tests; 6 | 7 | import 'package:shapeshift/shapeshift_common.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | import 'test_helpers.dart'; 11 | 12 | void main() { 13 | ReadableStringSink io; 14 | Map v1, v2; 15 | 16 | expectIoContains(RegExp re) => expect(io.read(), matches(re)); 17 | 18 | setUp(() { 19 | io = new ReadableStringSink(); 20 | }); 21 | 22 | test('Shapeshift reports on changed method attributes', () { 23 | v1 = baseMethod; 24 | v2 = baseMethod..['abstract'] = true; 25 | 26 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 27 | expectIoContains(new RegExp( 28 | r'''The \[foo\]\(.*\) method's `abstract` changed: 29 | 30 | Was: `false` 31 | 32 | Now: `true`''')); 33 | }); 34 | 35 | test('Shapeshift reports on new method annotations', () { 36 | v1 = baseMethod; 37 | v2 = baseMethod..['annotations'].add({'name': 'foo.Bar', 'parameters': []}); 38 | 39 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 40 | expectIoContains(new RegExp( 41 | r'''The \[foo\]\(.*\) method has new annotations: 42 | 43 | \* `@Bar\(\)`''')); 44 | }); 45 | 46 | test('Shapeshift reports on removed method annotations', () { 47 | v1 = baseMethod..['annotations'].add({'name': 'foo.Bar', 'parameters': []}); 48 | v2 = baseMethod; 49 | 50 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 51 | expectIoContains(new RegExp( 52 | r'''The \[foo\]\(.*\) method has removed annotations: 53 | 54 | \* `@Bar\(\)`''')); 55 | }); 56 | 57 | test('Shapeshift reports on new method parameters', () { 58 | v1 = baseMethod; 59 | v2 = baseMethod..['parameters']['p'] = baseParameter; 60 | 61 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 62 | expectIoContains(new RegExp(r'''The \[foo\]\(.*\) method has new parameters: 63 | 64 | \* `dart:core.String p`''')); 65 | }); 66 | 67 | test('Shapeshift reports on new method parameters', () { 68 | v1 = baseMethod..['parameters']['p'] = baseParameter; 69 | v2 = baseMethod; 70 | 71 | diffAndReport(jsonFrom(v1), jsonFrom(v2), io); 72 | expectIoContains(new RegExp( 73 | r'''The \[foo\]\(.*\) method has removed parameters: 74 | 75 | \* `dart:core.String p`''')); 76 | }); 77 | } 78 | 79 | void diffAndReport(String v1, String v2, ReadableStringSink io) { 80 | DiffNode diff = diffApis(v1, v2); 81 | MarkdownDiffWriter w = new MarkdownDiffWriter(() => io, shouldClose: false); 82 | Function noop = (Map m, [String key]) {}; 83 | new MethodAttributesReporter('method', 'foo', diff, w, noop).report(); 84 | } 85 | -------------------------------------------------------------------------------- /web/shapeshift.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | background-color: #F8F8F8; 9 | color: rgb(72, 72, 72); 10 | font: 400 15px/19px 'Roboto', sans-serif; 11 | } 12 | 13 | strong { 14 | font-weight: 500; 15 | color: rgb(48, 48, 48); 16 | } 17 | 18 | #status { 19 | display: none; 20 | text-align: center; 21 | font-style: italic; 22 | font-size: 150%; 23 | margin-top: 2em; 24 | } 25 | 26 | #status.active { 27 | display: block; 28 | } 29 | 30 | #version-selection { 31 | background-color: #eee; 32 | border: solid 1px #ddd; 33 | display: table; 34 | } 35 | 36 | #version-selection .table-cell { 37 | display: table-cell; 38 | padding: 10px; 39 | } 40 | 41 | #version-selection .table-cell >div { 42 | padding: 2px 0; 43 | } 44 | 45 | .the-body { 46 | background-color: #FFF; 47 | border: solid 1px #CCC; 48 | border-top: 0; 49 | border-bottom: 0; 50 | box-shadow: 0 2px 8px #CCC; 51 | margin: 0 auto; 52 | max-width: 940px; 53 | height: auto !important; 54 | height: 100%; 55 | min-height: 100%; 56 | position: relative; 57 | } 58 | 59 | .container { 60 | border-top: solid 3px #CCC; 61 | padding: 15px 15px 80px 15px; 62 | } 63 | 64 | .the-header a { 65 | cursor: pointer; 66 | } 67 | 68 | .the-header.disabled a { 69 | cursor: wait; 70 | } 71 | 72 | .container:first-child { 73 | border-top: 0; 74 | } 75 | 76 | .container >h1 { 77 | color: #000; 78 | margin-top: 0; 79 | margin-bottom: 16px; 80 | font: 400 46px/52px 'Montserrat', sans-serif; 81 | } 82 | 83 | .container .notification { 84 | text-align: center; 85 | margin: 20px; 86 | font-size: 150%; 87 | font-style: italic; 88 | } 89 | 90 | table td { 91 | padding: 0; 92 | vertical-align: middle; 93 | } 94 | table td:first-child { 95 | padding-right: 30px; 96 | } 97 | 98 | /* #0084c5 */ 99 | a { 100 | color: #428bca; 101 | text-decoration: none; 102 | } 103 | 104 | a:hover, a:focus { 105 | color: #2a6496; 106 | text-decoration: underline; 107 | } 108 | 109 | pre { 110 | background-color: #ddd; 111 | border: solid 1px #999; 112 | box-shadow: 1px 1px 3px #999 inset; 113 | font-size: 13px; 114 | overflow: auto; 115 | padding: 5px; 116 | } 117 | 118 | p >code, 119 | li >code { 120 | background-color: #eee; 121 | border-bottom: solid 1px #ccc; 122 | white-space: nowrap; 123 | } 124 | 125 | footer { 126 | position: absolute; 127 | bottom: 0; 128 | text-align: right; 129 | display: block; 130 | width: 100%; 131 | border-top: solid 1px #CCC; 132 | padding: 4px 0; 133 | } 134 | 135 | footer p { 136 | margin: 0.3em 0.2em 0.3em 0; 137 | font-size: 14px; 138 | } 139 | 140 | .hidden { 141 | display: none; 142 | } 143 | 144 | .sup { 145 | font-size: 67%; 146 | font-weight: bold; 147 | vertical-align: super; 148 | } 149 | 150 | a .sup:hover { 151 | text-decoration: none; 152 | } 153 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Shapeshift 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |

Shapeshift

23 | 24 |
25 |

Understand how the shape of the Dart SDK is shifting.

26 | 27 |

Shapeshift executes against two versions of Dart docgen-generated 28 | APIs to expose how your API has changed. Shapeshift generates a set 29 | of markdown files that list API differences in your Dart package.

30 |
31 | 32 |
33 |
34 |
35 | 36 | 40 | 41 | 45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 |
59 | 69 |
70 | 71 | Fork me on GitHub 72 | 73 | 74 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | _discoveryapis_commons: 5 | description: _discoveryapis_commons 6 | source: hosted 7 | version: "0.1.1" 8 | analyzer: 9 | description: analyzer 10 | source: hosted 11 | version: "0.25.0+1" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.13.2" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | collection: 25 | description: collection 26 | source: hosted 27 | version: "1.1.1" 28 | crypto: 29 | description: crypto 30 | source: hosted 31 | version: "0.9.0" 32 | csslib: 33 | description: csslib 34 | source: hosted 35 | version: "0.12.0+1" 36 | diff_match_patch: 37 | description: diff_match_patch 38 | source: hosted 39 | version: "0.2.1" 40 | doc_coverage: 41 | description: doc_coverage 42 | source: hosted 43 | version: "0.1.5" 44 | glob: 45 | description: glob 46 | source: hosted 47 | version: "1.0.4" 48 | googleapis: 49 | description: googleapis 50 | source: hosted 51 | version: "0.11.0" 52 | html: 53 | description: html 54 | source: hosted 55 | version: "0.12.1+1" 56 | http: 57 | description: http 58 | source: hosted 59 | version: "0.11.2" 60 | http_multi_server: 61 | description: http_multi_server 62 | source: hosted 63 | version: "1.3.2" 64 | http_parser: 65 | description: http_parser 66 | source: hosted 67 | version: "0.0.2+7" 68 | json_diff: 69 | description: json_diff 70 | source: hosted 71 | version: "0.1.2" 72 | logging: 73 | description: logging 74 | source: hosted 75 | version: "0.11.1" 76 | markdown: 77 | description: markdown 78 | source: hosted 79 | version: "0.7.1+2" 80 | matcher: 81 | description: matcher 82 | source: hosted 83 | version: "0.12.0+1" 84 | mime: 85 | description: mime 86 | source: hosted 87 | version: "0.9.3" 88 | path: 89 | description: path 90 | source: hosted 91 | version: "1.3.5" 92 | pool: 93 | description: pool 94 | source: hosted 95 | version: "1.0.2" 96 | pub_semver: 97 | description: pub_semver 98 | source: hosted 99 | version: "1.2.1" 100 | quiver: 101 | description: quiver 102 | source: hosted 103 | version: "0.21.4" 104 | route: 105 | description: route 106 | source: hosted 107 | version: "0.4.6" 108 | route_hierarchical: 109 | description: route_hierarchical 110 | source: hosted 111 | version: "0.6.2" 112 | sdk_builds: 113 | description: sdk_builds 114 | source: hosted 115 | version: "0.1.0" 116 | shelf: 117 | description: shelf 118 | source: hosted 119 | version: "0.6.1+2" 120 | shelf_static: 121 | description: shelf_static 122 | source: hosted 123 | version: "0.2.2" 124 | shelf_web_socket: 125 | description: shelf_web_socket 126 | source: hosted 127 | version: "0.0.1+2" 128 | source_map_stack_trace: 129 | description: source_map_stack_trace 130 | source: hosted 131 | version: "1.0.4" 132 | source_maps: 133 | description: source_maps 134 | source: hosted 135 | version: "0.10.1" 136 | source_span: 137 | description: source_span 138 | source: hosted 139 | version: "1.1.2" 140 | stack_trace: 141 | description: stack_trace 142 | source: hosted 143 | version: "1.3.3" 144 | string_scanner: 145 | description: string_scanner 146 | source: hosted 147 | version: "0.1.3+1" 148 | test: 149 | description: test 150 | source: hosted 151 | version: "0.12.3+2" 152 | utf: 153 | description: utf 154 | source: hosted 155 | version: "0.9.0+2" 156 | watcher: 157 | description: watcher 158 | source: hosted 159 | version: "0.9.6" 160 | yaml: 161 | description: yaml 162 | source: hosted 163 | version: "2.1.3" 164 | -------------------------------------------------------------------------------- /lib/src/common/reporters/variables_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.variables_reporter; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | import '../markdown_diff_writer.dart'; 10 | 11 | class VariablesReporter { 12 | final String variableListPlural, variableList; 13 | final DiffNode variables; 14 | final MarkdownDiffWriter io; 15 | final Function erase; 16 | 17 | VariablesReporter(_variableListPlural, this.variables, this.io, this.erase) 18 | : this.variableListPlural = _variableListPlural, 19 | this.variableList = singularize(_variableListPlural); 20 | 21 | void report() { 22 | if (variables.hasAdded) { 23 | io.writeln('New $variableListPlural:\n'); 24 | io.writeCodeblockHr( 25 | variables.added.values.map(variableSignature).join('\n\n')); 26 | } 27 | erase(variables.added); 28 | 29 | if (variables.hasRemoved) { 30 | io.writeln('Removed $variableListPlural:\n'); 31 | io.writeCodeblockHr( 32 | variables.removed.values.map(variableSignature).join('\n\n')); 33 | } 34 | erase(variables.removed); 35 | 36 | if (variables.hasChanged) { 37 | variables.forEachChanged((k, v) { 38 | print('TODO: CHANGED: $k, $v'); 39 | }); 40 | } 41 | 42 | variables.forEach(reportEachVariable); 43 | } 44 | 45 | void reportEachVariable(String name, DiffNode variable) { 46 | if (!variable.metadata.containsKey('qualifiedName')) { 47 | // Magically renamed (I suspect docgen). 48 | if (variable.changed.containsKey('qualifiedName')) { 49 | var oldNew = variable.changed['qualifiedName']; 50 | io.writeBad( 51 | 'The `$name` variable\'s qualifiedName changed from ${oldNew[0]} to ${oldNew[1]}'); 52 | } else { 53 | io.writeBad('TODO: WHAT?'); 54 | } 55 | return; 56 | } 57 | 58 | var link = mdLinkToDartlang(variable.metadata['qualifiedName'], name); 59 | if (variable.hasChanged) { 60 | variable.forEachChanged((attribute, value) => 61 | reportChangedAttributes(attribute, value, link)); 62 | } 63 | erase(variable.changed); 64 | 65 | if (variable.node.isNotEmpty) variable.node.forEach( 66 | (String attributeName, DiffNode attribute) => 67 | reportDeepChange(name, variable, link, attributeName, attribute)); 68 | } 69 | 70 | void reportChangedAttributes(String attribute, List value, String link) { 71 | io.writeln('The $link $variableList\'s `$attribute` changed:\n'); 72 | if (attribute == 'type') { 73 | io.writeWasNow(simpleType(value[0]), simpleType(value[1]), link: true); 74 | } else { 75 | io.writeWasNow(value[0], value[1], blockquote: attribute == 'comment'); 76 | } 77 | io.writeHr(); 78 | } 79 | 80 | void reportDeepChange(String name, DiffNode variable, String link, 81 | String attributeName, DiffNode attribute) { 82 | if (attributeName == 'annotations') { 83 | if (attribute.hasAdded) { 84 | io.writeln('The $link $variableList has new annotations:\n'); 85 | attribute.added.forEach((_, property) => 86 | io.writeln(propertyListItem('annotations', property))); 87 | } 88 | erase(variables.added); 89 | if (attribute.hasChanged) { 90 | io.writeln('The $link $variableList\'s annotations have changed:\n'); 91 | attribute.forEachChanged((String idx, List ann) { 92 | io.writeWasNow( 93 | formattedAnnotation(ann[0]), formattedAnnotation(ann[1])); 94 | }); 95 | } 96 | erase(attribute.changed); 97 | io.writeHr(); 98 | } else { 99 | io.writeBad( 100 | 'TODO: The [$name](#) $variableList\'s `$attributeName` has changed:\n', 101 | attribute.toString(pretty: false)); 102 | } 103 | } 104 | 105 | // TODO: yanked from method_attributes_reporter; these need a superclass! 106 | String propertyListItem(String attributeName, Map property) { 107 | if (attributeName == 108 | 'annotations') return '* ${formattedAnnotation(property)}'; 109 | 110 | if (attributeName == 111 | 'parameters') return '* `${parameterSignature(property)}`'; 112 | 113 | return '* `$property`'; 114 | } 115 | } 116 | 117 | void reportVariables( 118 | DiffNode diff, String variableList, MarkdownWriter io, Function erase) { 119 | if (!diff.containsKey(variableList)) return; 120 | 121 | new VariablesReporter(variableList, diff[variableList], io, erase).report(); 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Shapeshift 2 | ========== 3 | 4 | Understand how the shape of your Dart package is shifting. 5 | 6 | Execute shapeshift against two versions of your Dart docgen-generated API to 7 | expose how your API has changed. Shapeshift generates a set of markdown files 8 | that list API differences in your Dart package. 9 | 10 | Shapeshift can be used as a command-line script, or as Dart library. 11 | 12 | The shapeshift.dart script 13 | ========================== 14 | 15 | The shapeshift package comes with a command-line tool, `bin/shapeshift.dart`. 16 | This tool can read JSON-formatted API files (generated by dartdoc), and output, 17 | in Markdown format, a list of differences. 18 | 19 | Basic example 20 | ------------- 21 | 22 | Assuming you have two versions of your docgen-generated docs at: 23 | 24 | ```bash 25 | /Code/my_package/docs/docs-v1.6.0/ 26 | /Code/my_package/docs/docs-v1.7.0/ 27 | ``` 28 | 29 | Then you can run the following command to generate reports of the API 30 | differences. 31 | 32 | ```bash 33 | dart bin/shapeshift.dart \ 34 | --base=/Code/my_package/docs \ 35 | --out=./diff-1.6.0_1.7.0 \ 36 | docs-v1.6.0 \ 37 | docs-v1.7.0 38 | ``` 39 | 40 | In this command, `--base` specifies the directory where your two documentation 41 | directories live. `--out` specifies a new or existing directory where you want 42 | the output markdown files to be written. The final two arguments are the 43 | directory with the _old_ and the directory with the _new_ documentation. 44 | 45 | This command will create the output directory, if it wasn't there, and write a 46 | markdown file for each library that was examined. For example, if a 47 | `dart-async.json` file was found, documenting the `dart:async` library, then 48 | the output will include a `dart:async.markdown` file. 49 | 50 | Each library's markdown file will then include all of the changes found for the 51 | library itself, and for every class within the library. 52 | 53 | The directory of markdown files works surprisingly well with 54 | [Jekyll](http://jekyllrb.com/) and Jekyll's basic template, but we're working 55 | on more streamlined output options. 56 | 57 | Options 58 | ------- 59 | 60 | The shapeshift Dart script accepts the following options: 61 | 62 | * `--base` is the file path that the two directories (old and new) have in 63 | common. 64 | * `--out` is an optional directory where the API changes can be written to. 65 | Shapeshift will write a separate Markdown file for each library it finds. 66 | Each Markdown file will include changes for all member classes of a library. 67 | If `--out` is not used, the differences, still in Markdown format, will be 68 | printed to stdout. 69 | 70 | Using the Dart library 71 | ====================== 72 | 73 | Shapeshift can also be used with other Dart code as a library. I would 74 | recommend importing `'package:shapeshift/shapeshift_common.dart'`. The 75 | [tests](test/shapeshift/) can be used as examples. 76 | 77 | `shapeshift_common` library example 78 | --------------------------------------------------- 79 | 80 | Assuming you have two versions of a class's API sitting on disk, say at 81 | `v1/foo.Foo.json`, and `v2/foo.Foo.json`, you may diff them as follows: 82 | 83 | ```dart 84 | import 'package:shapeshift/shapeshift_common.dart'; 85 | 86 | void main() { 87 | String v1 = new File('v1/foo.Foo.json')..readAsStringSync(); 88 | String v2 = new File('v2/foo.Foo.json')..readAsStringSync(); 89 | 90 | // Calculate the diff between the two APIs. 91 | DiffNode diff = diffApis(v1, v2); 92 | 93 | // ReadableStringSink is just a class that acts like a StringSink, but can be 94 | // read at any point. You can also use a File handle. 95 | ReadableStringSink io = new ReadableStringSink(); 96 | 97 | // The MarkdownWriter takes a callback that will instantiate the write 98 | // target, if it needs to be written to. 99 | MarkdownWriter writer = new MarkdownWriter(() => io, false); 100 | 101 | // Create a new ClassReporter for the diff, which can report to the writer. 102 | // And write! 103 | new ClassReporter(diff, writer).report(); 104 | 105 | // Now io contains the report, in Markdown format. 106 | print(io.read()); 107 | } 108 | ``` 109 | 110 | `shapeshift_common` API 111 | ----------------------- 112 | 113 | TODO: Publish shapeshift as a package, so that it's API is online. 114 | 115 | For now, the [tests](test/shapeshift/) serve as good examples of how to use the 116 | API. 117 | 118 | License 119 | ======= 120 | 121 | [Apache v2](LICENSE) 122 | 123 | Contributing 124 | ============ 125 | 126 | Contributions welcome! Please read the 127 | [contribution guidelines](CONTRIBUTING.md). 128 | 129 | Disclaimer 130 | ========== 131 | 132 | This is not an official Google product. 133 | -------------------------------------------------------------------------------- /lib/src/common/reporters/class_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.class_reporter; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | import '../markdown_diff_writer.dart'; 10 | import 'methods_reporter.dart'; 11 | import 'variables_reporter.dart'; 12 | 13 | class ClassReporter { 14 | static bool hideInherited = true; 15 | static bool shouldErase = true; 16 | static Function identityFormatter = (e, {link: null}) => e; 17 | 18 | final DiffNode diff; 19 | final MarkdownDiffWriter io; 20 | 21 | String name, qualifiedName; 22 | 23 | ClassReporter(this.diff, this.io) { 24 | name = diff.metadata['name']; 25 | qualifiedName = diff.metadata['qualifiedName']; 26 | } 27 | 28 | void report() { 29 | if (diff == null) return; 30 | 31 | // TODO: also Errors and Typedefs? 32 | io.bufferH2('class ${mdLinkToDartlang(qualifiedName, name)}'); 33 | reportClass(); 34 | 35 | // After reporting, prune and print anything remaining. 36 | diff.prune(); 37 | diff.metadata.clear(); 38 | String ds = diff.toString(); 39 | if (ds.isNotEmpty) { 40 | print('$qualifiedName HAS UNRESOLVED NODES:'); 41 | print(ds); 42 | } 43 | } 44 | 45 | void reportClass() { 46 | if (diff.containsKey('annotations')) { 47 | _reportList('annotations', formatter: formattedAnnotation); 48 | } 49 | 50 | _reportImmediateChanges(); 51 | 52 | if (diff.containsKey('subclass')) { 53 | _reportList('subclass', formatter: classFormatter); 54 | } 55 | 56 | _reportImplements(); 57 | 58 | // Iterate over the method categories. 59 | diff.forEachOf('methods', _reportEachMethodCategory); 60 | if (hideInherited) erase(diff.node, 'inheritedMethods'); 61 | else diff.forEachOf('inheritedMethods', _reportEachMethodCategory); 62 | 63 | reportVariables(diff, 'variables', io, erase); 64 | if (hideInherited) erase(diff.node, 'inheritedVariables'); 65 | else reportVariables(diff, 'inheritedVariables', io, erase); 66 | } 67 | 68 | void _reportImmediateChanges() { 69 | if (!diff.hasChanged) return; 70 | 71 | diff.forEachChanged((String key, List oldNew) { 72 | io.writeln("$name's `${key}` changed:\n"); 73 | io.writeWasNow(oldNew[0], oldNew[1], 74 | blockquote: key == 'comment', link: ['superclass'].contains(key)); 75 | io.writeHr(); 76 | }); 77 | diff.changed.clear(); 78 | } 79 | 80 | void _reportImplements() { 81 | if (!diff.containsKey('implements')) return; 82 | 83 | DiffNode implements = diff['implements']; 84 | if (implements.hasAdded) { 85 | String added = implements.added.values.map(mdLinkToDartlang).join(', '); 86 | io.writeln("$name now implements ${added}."); 87 | erase(implements.added); 88 | } 89 | if (implements.hasRemoved) { 90 | String removed = 91 | implements.removed.values.map(mdLinkToDartlang).join(', '); 92 | io.writeln("$name no longer implements ${removed}."); 93 | erase(implements.removed); 94 | } 95 | io.writeHr(); 96 | } 97 | 98 | void _reportEachMethodCategory(String methodCategory, DiffNode diff) => 99 | new MethodsReporter(methodCategory, diff, io, erase).report(); 100 | 101 | void _reportList(String key, {Function formatter: null}) { 102 | if (formatter == null) formatter = identityFormatter; 103 | 104 | if (diff[key].hasAdded) { 105 | io.writeln('$name has new ${pluralize(key)}:\n'); 106 | diff[key].forEachAdded((String idx, Object el) { 107 | el = formatter(el, link: true); 108 | io.writeln('* $el'); 109 | }); 110 | io.writeHr(); 111 | erase(diff[key].added); 112 | } 113 | 114 | if (diff[key].hasRemoved) { 115 | io.writeln('$name no longer has these ${pluralize(key)}:\n'); 116 | diff[key].forEachRemoved((String idx, Object el) { 117 | el = formatter(el, link: false); 118 | io.writeln('* $el'); 119 | }); 120 | io.writeHr(); 121 | erase(diff[key].removed); 122 | } 123 | 124 | if (diff[key].hasChanged) { 125 | io.writeln('$name has changed ${pluralize(key)}:\n'); 126 | diff[key].forEachChanged((String idx, List oldNew) { 127 | var theOld = oldNew[0]; 128 | var theNew = oldNew[1]; 129 | theOld = formatter(theOld, link: false); 130 | theNew = formatter(theNew, link: false); 131 | io.writeln('* $theOld is now $theNew.'); 132 | }); 133 | io.writeHr(); 134 | erase(diff[key].changed); 135 | } 136 | } 137 | 138 | void erase(Map m, [String key]) { 139 | if (!shouldErase) return; 140 | 141 | if (key == null) m.clear(); 142 | else m.remove(key); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/src/common/reporters/classes_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.classes_reporter; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart'; 8 | 9 | import '../markdown_diff_writer.dart'; 10 | 11 | class ClassesReporter { 12 | final String category; 13 | final DiffNode diff; 14 | final String libraryName; 15 | final MarkdownDiffWriter io; 16 | final Function erase; 17 | 18 | ClassesReporter( 19 | this.category, this.diff, this.libraryName, this.io, this.erase); 20 | 21 | void report() { 22 | reportRemovedClasses(); 23 | reportAddedClasses(); 24 | diff.forEach(reportEachClass); 25 | } 26 | 27 | void reportRemovedClasses() { 28 | if (!diff.hasRemoved) return; 29 | 30 | var cat = diff.removed.length == 1 ? category : pluralize(category); 31 | var names = diff.removed.values.map((klass) => klass['name']).join(', '); 32 | io.writeln('Removed $cat: $names.'); 33 | io.writeHr(); 34 | erase(diff.removed); 35 | } 36 | 37 | void reportAddedClasses() { 38 | if (!diff.hasAdded) return; 39 | 40 | var cat = diff.added.length == 1 ? category : pluralize(category); 41 | var names = diff.added.values 42 | .map((klass) => mdLinkToDartlang(klass['qualifiedName'], klass['name'])) 43 | .join(', '); 44 | io.writeln('New $cat: $names.'); 45 | io.writeHr(); 46 | erase(diff.added); 47 | } 48 | 49 | void reportEachClass(String klassName, DiffNode klass) { 50 | String qualifiedName = klass.metadata['qualifiedName']; 51 | reportAddedClassMembers(qualifiedName, klass); 52 | 53 | if (klass.hasChanged && 54 | klass.changed.containsKey('name') && 55 | klass.changed.containsKey('qualifiedName')) { 56 | // A "changed" class thing probably means a class was removed and 57 | // another was added, at the same index in the class thing list. 58 | // Awkward. 59 | var changed = klass.changed; 60 | String link = 61 | mdLinkToDartlang(changed['qualifiedName'][1], changed['name'][1]); 62 | io 63 | ..writeln('Removed $category: ${changed['name'][0]}.') 64 | ..writeHr() 65 | ..writeln('New $category: $link.') 66 | ..writeHr(); 67 | } 68 | 69 | if (klass.hasChanged) { 70 | klass.changed.forEach((String key, List oldNew) { 71 | String name = klass.metadata['name']; 72 | // If a class's name changed, then "name" won't be in the metadata, 73 | // so we'll grab it from the changed map. 74 | // TODO: make this better... 75 | if (name == null && klass.changed.containsKey('name')) { 76 | name = klass.changed['name'][0]; 77 | } 78 | if (qualifiedName == null && 79 | klass.changed.containsKey('qualifiedName')) { 80 | qualifiedName = klass.changed['qualifiedName'][0]; 81 | } 82 | qualifiedName = qualifiedName.replaceAll(new RegExp(r'.*\.'), ''); 83 | String classThingLink = mdLinkToDartlang(qualifiedName, qualifiedName); 84 | io 85 | ..writeln( 86 | '${libraryName}\'s $classThingLink $category `$key` changed:\n') 87 | ..writeWasNow(oldNew[0], oldNew[1], 88 | blockquote: ['comment', 'preview'].contains(key)) 89 | ..writeHr(); 90 | }); 91 | } 92 | erase(klass.changed); 93 | 94 | klass.forEachOf('parameters', (String name, DiffNode p) => 95 | reportChangedTypedefParameter(qualifiedName, name, p)); 96 | } 97 | 98 | void reportAddedClassMembers(String qualifiedName, DiffNode klass) { 99 | if (!klass.hasAdded) return; 100 | 101 | klass.added.forEach((String key, dynamic thing) { 102 | String name = qualifiedName.replaceAll(new RegExp(r'.*\.'), ''); 103 | String classThingLink = mdLinkToDartlang(qualifiedName, name); 104 | io.writeln( 105 | '${diff.metadata['name']}\'s $classThingLink $category has a new `$key`:\n'); 106 | if (key == 'preview') io.writeBlockquote(thing); 107 | else io.writeCodeblockHr(thing); 108 | io.writeHr(); 109 | }); 110 | erase(klass.added); 111 | } 112 | 113 | void reportChangedTypedefParameter( 114 | String qualifiedName, String name, DiffNode parameter) { 115 | if (!parameter.changed.containsKey('type')) return; 116 | 117 | String oldType = simpleType(parameter.changed['type'][0]); 118 | String newType = simpleType(parameter.changed['type'][1]); 119 | // This is so ugly because we are so deep, but an example would be: 120 | // The foo typedef's value parameter's type has changed from int to bool. 121 | io.writeln('The [$qualifiedName](#) $category\'s [$name](#) ' 122 | 'parameter\'s type has changed from `$oldType` to `$newType`'); 123 | io.writeHr(); 124 | erase(parameter.changed, 'type'); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/src/common/markdown_diff_writer.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.markdown_diff_writer; 5 | 6 | import 'package:diff_match_patch/diff_match_patch.dart'; 7 | import 'package:doc_coverage/doc_coverage_common.dart'; 8 | 9 | class MarkdownDiffWriter extends MarkdownWriter { 10 | MarkdownDiffWriter(openIo, {shouldClose: true, shouldWriteMetadata: true}) 11 | : super(openIo, 12 | shouldClose: shouldClose, shouldWriteMetadata: shouldWriteMetadata); 13 | 14 | void writeWasNow(Object theOld, Object theNew, 15 | {bool blockquote: false, bool link: false}) { 16 | String theOldStr = theOld.toString(); 17 | String theNewStr = theNew.toString(); 18 | if (blockquote) { 19 | if (theOldStr.isEmpty) { 20 | writeln('Was: _empty_'); 21 | } else { 22 | writeln('Was:\n'); 23 | writeBlockquote(highlightDeleted(theOldStr, theNewStr)); 24 | //writeBlockquote(theOldStr); 25 | } 26 | if (theNewStr.isEmpty) { 27 | writeln('Now: _empty_'); 28 | } else { 29 | writeln('Now:\n'); 30 | writeBlockquote(highlightInserted(theOldStr, theNewStr)); 31 | //writeBlockquote(theNewStr); 32 | } 33 | } else { 34 | if (theOldStr.isEmpty) { 35 | theOldStr = '_empty_'; 36 | } else if (link) { 37 | theOldStr = decoratedName(theOldStr); 38 | } else { 39 | theOldStr = '`$theOldStr`'; 40 | } 41 | if (theNewStr.isEmpty) { 42 | theNewStr = '_empty_'; 43 | } else if (link) { 44 | theNewStr = mdLinkToDartlang(theNewStr); 45 | } else { 46 | theNewStr = '`$theNewStr`'; 47 | } 48 | writeln('Was: $theOldStr\n'); 49 | writeln('Now: $theNewStr'); 50 | } 51 | } 52 | } 53 | 54 | String highlightInserted(String theOld, String theNew) { 55 | List diffResult = diffAndCleanup(theOld, theNew); 56 | String result = ''; 57 | String greenBg = 'background-color: rgba(153, 255, 153, 0.7)'; 58 | diffResult.forEach((Diff d) { 59 | if (d.operation == DIFF_EQUAL) { 60 | result += d.text; 61 | } else if (d.operation == DIFF_INSERT) { 62 | // TODO: more block element tags 63 | if (d.text.contains('
') || 64 | d.text.contains('

') || 65 | d.text.contains('

')) {
 66 |         result += '
' 68 | '${d.text}' 69 | '
'; 70 | } else { 71 | result += '${d.text}'; 72 | } 73 | } 74 | }); 75 | return result; 76 | } 77 | 78 | String highlightDeleted(String theOld, String theNew) { 79 | List diffResult = diffAndCleanup(theOld, theNew); 80 | String result = ''; 81 | String redBg = 'background-color: rgba(255, 153, 153, 0.7)'; 82 | diffResult.forEach((Diff d) { 83 | if (d.operation == DIFF_EQUAL) { 84 | result += d.text; 85 | } else if (d.operation == DIFF_DELETE) { 86 | // TODO: more block element tags 87 | if (d.text.contains('
') || 88 | d.text.contains('

') || 89 | d.text.contains('

')) {
 90 |         result +=
 91 |             '
${d.text}
'; 92 | } else { 93 | result += 94 | '${d.text}'; 95 | } 96 | } 97 | }); 98 | return result; 99 | } 100 | 101 | List diffAndCleanup(String theOld, String theNew) { 102 | List result = diff(theOld, theNew); 103 | cleanupSemantic(result); 104 | RegExp unclosedTag = new RegExp(r'<[^>]*$'); 105 | RegExp unopenedTag = new RegExp(r'^[^<]*>'); 106 | for (int i = 0; i < result.length; i++) { 107 | Diff d = result[i]; 108 | if (d.text.contains(unclosedTag)) { 109 | String dirtyDom = unclosedTag.stringMatch(d.text); 110 | removeFromDiff(d, unclosedTag); 111 | if (d.operation == DIFF_EQUAL) { 112 | prependToDiff(result[i + 1], dirtyDom); 113 | if (result[i + 2].operation != DIFF_EQUAL) { 114 | prependToDiff(result[i + 2], dirtyDom); 115 | } 116 | } else { 117 | if (result[i + 1].operation == DIFF_EQUAL) { 118 | prependToDiff(result[i + 1], dirtyDom); 119 | } else { 120 | // If dirtyDom in result[i+1] != dirtyDom, then we should pull the 121 | // unopened back instead of pushing the unclosed forward. 122 | if (unclosedTag.stringMatch(result[i + 1].text) == dirtyDom) { 123 | removeFromDiff(result[i + 1], unclosedTag); 124 | prependToDiff(result[i + 2], dirtyDom); 125 | } else { 126 | if (result[i + 2].text.contains(unopenedTag)) { 127 | String plus2DirtyDom = 128 | unopenedTag.stringMatch(result[i + 2].text); 129 | removeFromDiff(result[i + 2], unopenedTag); 130 | // Just kidding put it back! 131 | appendToDiff(d, dirtyDom); 132 | appendToDiff(d, plus2DirtyDom); 133 | appendToDiff(result[i + 1], plus2DirtyDom); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | return result; 142 | } 143 | 144 | void appendToDiff(Diff d, String suffix) { 145 | d.text = '${d.text}$suffix'; 146 | } 147 | 148 | void prependToDiff(Diff d, String prefix) { 149 | d.text = '$prefix${d.text}'; 150 | } 151 | 152 | void removeFromDiff(Diff d, Pattern p) { 153 | d.text = d.text.replaceAll(p, ''); 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/frontend/js_zip_wrapper.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shaepshift.frontend.js_zip_wrapper; 5 | 6 | import 'dart:async'; 7 | import 'dart:html'; 8 | import 'dart:js'; 9 | import 'dart:typed_data'; 10 | 11 | import 'package:path/path.dart' as p; 12 | import 'package:sdk_builds/sdk_builds.dart'; 13 | 14 | import 'js_zip_package_reporter.dart'; 15 | import 'html_writer.dart'; 16 | import 'html_writer_provider.dart'; 17 | 18 | String _libraryFor(String file) { 19 | var segments = p.split(file); 20 | var fileSegment = segments.last; 21 | assert(p.extension(fileSegment) == '.json'); 22 | var fileParts = fileSegment.split('.'); 23 | return fileParts.first; 24 | } 25 | 26 | class JSZipWrapper { 27 | final JsObject zip; 28 | final List files; 29 | 30 | factory JSZipWrapper(ByteBuffer data) { 31 | var zip = new JsObject(context['JSZip'], [data]); 32 | 33 | var files = new List.from( 34 | context['Object'].callMethod('keys', [zip['files']])); 35 | 36 | return new JSZipWrapper._(zip, files); 37 | } 38 | 39 | JSZipWrapper._(this.zip, this.files); 40 | 41 | Iterable get _jsonFiles => files.where((file) { 42 | return file.endsWith('.json') && 43 | !file.endsWith('index.json') && 44 | !file.endsWith('library_list.json'); 45 | }); 46 | 47 | Map> get filesByLibrary { 48 | var map = >{}; 49 | 50 | for (var file in _jsonFiles) { 51 | String lib = _libraryFor(file); 52 | var fileList = map.putIfAbsent(lib, () => []); 53 | fileList.add(file); 54 | } 55 | 56 | return map; 57 | } 58 | 59 | bool hasFile(String fileName) => files.contains(fileName); 60 | 61 | String read(String fileName) => 62 | zip.callMethod('file', [fileName]).callMethod('asText', []) as String; 63 | } 64 | 65 | Future getBinaryContent(String uri) { 66 | var completer = new Completer(); 67 | 68 | context['JSZipUtils'].callMethod('getBinaryContent', [ 69 | uri, 70 | (err, data) { 71 | if (err != null) { 72 | completer.completeError(err); 73 | } else { 74 | completer.complete(data); 75 | } 76 | } 77 | ]); 78 | 79 | return completer.future; 80 | } 81 | 82 | const String issuesUrl = 'https://github.com/google/dart-shapeshift/issues'; 83 | 84 | void compareZips(VersionInfo leftVersion, ByteBuffer leftData, 85 | VersionInfo rightVersion, ByteBuffer rightData, bool includeComments, 86 | DivElement diffContainer) { 87 | String leftV = leftVersion.version.toString(); 88 | String rightV = rightVersion.version.toString(); 89 | var header = new HeadingElement.h1()..text = 'Changes from $leftV to $rightV'; 90 | AnchorElement issuesLink = new AnchorElement(href: issuesUrl) 91 | ..text = 'GitHub'; 92 | var summaryText = new ParagraphElement() 93 | ..appendText('''The following report is the difference in the public API 94 | between the Dart $leftV SDK and the Dart $rightV SDK. The 95 | Shapeshift tool is still very new, and and issue reports at ''') 96 | ..append(issuesLink) 97 | ..appendText(' are highly appreciated!'); 98 | 99 | diffContainer.children.clear(); 100 | 101 | diffContainer..append(header)..append(summaryText); 102 | 103 | JSZipWrapper leftZip = new JSZipWrapper(leftData); 104 | JSZipWrapper rightZip = new JSZipWrapper(rightData); 105 | 106 | var reporter = new JSZipPackageReporter( 107 | leftZip, rightZip, leftVersion, rightVersion, 108 | includeComments: includeComments); 109 | 110 | var contentHost = new DivElement()..classes.add("the-host"); 111 | 112 | new _UIManager(contentHost, reporter); 113 | 114 | diffContainer.append(contentHost); 115 | } 116 | 117 | class _UIManager { 118 | static const _disabledClass = 'disabled'; 119 | bool get _working => _header.classes.contains(_disabledClass); 120 | 121 | final DivElement _host; 122 | final JSZipPackageReporter _reporter; 123 | final DivElement _header; 124 | 125 | DivElement _content; 126 | 127 | _UIManager(this._host, this._reporter) : this._header = new DivElement() 128 | ..classes.add('the-header') { 129 | var libraryNames = _reporter.rightZip.filesByLibrary.keys.toList(); 130 | libraryNames.sort(); 131 | 132 | for (var libText in libraryNames) { 133 | assert(libText.startsWith('dart-')); 134 | 135 | var link = new AnchorElement()..text = libText.substring(5); 136 | 137 | link.onClick.listen((_) { 138 | _writeContent(libText); 139 | }); 140 | 141 | _header.children.add(link); 142 | 143 | _header.appendHtml(r' '); 144 | } 145 | 146 | _host.append(_header); 147 | 148 | _content = new DivElement() 149 | ..classes.add('the-content'); 150 | 151 | _host.append(_content); 152 | 153 | _writeContent('dart-core'); 154 | } 155 | 156 | Future _writeContent(String library) async { 157 | if (_working) return; 158 | 159 | try { 160 | _header.classes.add(_disabledClass); 161 | _content.children.clear(); 162 | 163 | _content.children.add(_progressNotification('Loading $library ...')); 164 | 165 | await letHtmlUpdate(); 166 | 167 | var report = await _reporter.calculateDiffForLibrary(library); 168 | 169 | _content.children.clear(); 170 | var writer = new HtmlWriterProvider(new HtmlWriter(_content)); 171 | report.write(writer); 172 | 173 | if (_content.children.isEmpty) { 174 | _content.children.add(_progressNotification('No changes.')); 175 | } 176 | 177 | } finally { 178 | _header.classes.remove(_disabledClass); 179 | } 180 | } 181 | } 182 | 183 | DivElement _progressNotification(String things) =>new DivElement() 184 | ..classes.add('notification') 185 | ..text = things; 186 | -------------------------------------------------------------------------------- /web/shapeshift.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | import 'dart:async'; 5 | import 'dart:html'; 6 | import 'dart:typed_data'; 7 | 8 | import 'package:http/browser_client.dart' as http; 9 | import 'package:route_hierarchical/client.dart'; 10 | import 'package:sdk_builds/sdk_builds.dart'; 11 | import 'package:shapeshift/shapeshift_frontend.dart'; 12 | 13 | final Map _versionMaps = new Map(); 14 | 15 | final InputElement _includeCommentsCheck = querySelector('#include-comments'); 16 | final InputElement _goButton = querySelector('#get-diff'); 17 | final Element _statusElement = querySelector('#status'); 18 | 19 | final DivElement _diffContainer = querySelector('#diff-container'); 20 | 21 | final SelectElement _leftVersionSelect = querySelector('#left-version'); 22 | final OptGroupElement _leftVersionStableOptGroup = 23 | _leftVersionSelect.querySelector('.stable'); 24 | final OptGroupElement _leftVersionDevOptGroup = 25 | _leftVersionSelect.querySelector('.dev'); 26 | 27 | final SelectElement _rightVersionSelect = querySelector('#right-version'); 28 | final OptGroupElement _rightVersionStableOptGroup = 29 | _rightVersionSelect.querySelector('.stable'); 30 | final OptGroupElement _rightVersionDevOptGroup = 31 | _rightVersionSelect.querySelector('.dev'); 32 | 33 | final Router _router = new Router(useFragment: true); 34 | 35 | void main() { 36 | _goButton.onClick.listen(_goClicked); 37 | 38 | _startDownload(); 39 | 40 | _router.root 41 | ..addRoute( 42 | name: 'compare', 43 | path: '/compare', 44 | enter: (e) => _compareToNavigation(e.route)); 45 | 46 | _router.listen(); 47 | } 48 | 49 | void _addToSelects(VersionInfo rev) { 50 | var str = rev.version.toString(); 51 | 52 | var left = new OptionElement() 53 | ..text = str 54 | ..attributes['value'] = str; 55 | var right = new OptionElement() 56 | ..text = str 57 | ..attributes['value'] = rev.version.toString(); 58 | 59 | if (rev.channel == 'stable') { 60 | _leftVersionStableOptGroup.children.add(left); 61 | _rightVersionStableOptGroup.children.add(right); 62 | } else { 63 | _leftVersionDevOptGroup.children.add(left); 64 | _rightVersionDevOptGroup.children.add(right); 65 | } 66 | } 67 | 68 | _startDownload() async { 69 | await _populateVersionFiles('dev'); 70 | await _populateVersionFiles('stable'); 71 | 72 | _updateStatus(); 73 | _updateSelectors(); 74 | 75 | if (_router.activePath.isNotEmpty) { 76 | var active = _router.activePath.last; 77 | if (active.name == 'compare') { 78 | _router.reload(); 79 | } 80 | } 81 | } 82 | 83 | Future _populateVersionFiles(String channel) async { 84 | _updateStatus('$channel: getting list'); 85 | 86 | var client = new http.BrowserClient(); 87 | var dd = new DartDownloads(client: client); 88 | 89 | try { 90 | var list = await dd.getVersions(channel); 91 | 92 | for (var vi in list) { 93 | _versionMaps[vi.version.toString()] = vi; 94 | } 95 | } finally { 96 | dd.close(); 97 | } 98 | } 99 | 100 | void _updateSelectors() { 101 | _leftVersionSelect.disabled = false; 102 | _rightVersionSelect.disabled = false; 103 | _goButton.disabled = false; 104 | 105 | _leftVersionStableOptGroup.children.clear(); 106 | _leftVersionDevOptGroup.children.clear(); 107 | _rightVersionStableOptGroup.children.clear(); 108 | _rightVersionDevOptGroup.children.clear(); 109 | 110 | List sortedVersions = _versionMaps.values.toList()..sort(); 111 | 112 | (sortedVersions.reversed).forEach(_addToSelects); 113 | 114 | // Cannot use the newest version as the older version. 115 | //leftVersionSelect.children.first.attributes['disabled'] = 'disabled'; 116 | // Cannot use the oldest version as the newer version. 117 | //rightVersionSelect.children.last.attributes['disabled'] = 'disabled'; 118 | } 119 | 120 | void _updateStatus([String value]) { 121 | if (value == null || value.isEmpty) { 122 | _statusElement.classes.remove('active'); 123 | } else { 124 | _statusElement.classes.add('active'); 125 | _statusElement.setInnerHtml('$value'); 126 | } 127 | } 128 | 129 | Future _goClicked(Event event) async { 130 | var leftVersionString = 131 | _leftVersionSelect.selectedOptions[0].attributes['value']; 132 | var rightVersionString = 133 | _rightVersionSelect.selectedOptions[0].attributes['value']; 134 | 135 | var params = { 136 | 'leftVersion': leftVersionString, 137 | 'rightVersion': rightVersionString 138 | }; 139 | 140 | if (_includeCommentsCheck.checked) { 141 | params['includeComments'] = 'true'; 142 | } 143 | 144 | await _router.go('compare', {}, queryParameters: params); 145 | } 146 | 147 | Future _compareToNavigation(Route route) async { 148 | var leftVersionString = route.queryParameters['leftVersion']; 149 | var rightVersionString = route.queryParameters['rightVersion']; 150 | 151 | var includeComments = route.queryParameters.containsKey('includeComments'); 152 | 153 | await _compareValues(leftVersionString, rightVersionString, includeComments); 154 | } 155 | 156 | Future _compareValues( 157 | String leftValue, String rightValue, bool includeComments) async { 158 | try { 159 | if (_goButton.disabled) { 160 | print('Waiting on navigation...'); 161 | return; 162 | } 163 | _goButton.disabled = true; 164 | _diffContainer.children.clear(); 165 | 166 | _select(_leftVersionSelect, leftValue); 167 | _select(_rightVersionSelect, rightValue); 168 | 169 | VersionInfo left = _versionMaps[leftValue]; 170 | VersionInfo right = _versionMaps[rightValue]; 171 | 172 | // TODO: update selection, right? 173 | 174 | if (left == right) { 175 | _updateStatus('Cannot compare the same version - ${left.version}'); 176 | return; 177 | } 178 | 179 | try { 180 | await _compareVersions(left, right, includeComments); 181 | _updateStatus(); 182 | } catch (e, stack) { 183 | _printError(e, stack, 'Error comparing versions.'); 184 | } 185 | } finally { 186 | _goButton.disabled = false; 187 | } 188 | } 189 | 190 | Future _compareVersions( 191 | VersionInfo left, VersionInfo right, bool includeComments) async { 192 | var leftData = await _getData(left); 193 | var rightData = await _getData(right); 194 | 195 | _updateStatus('Calculating diff'); 196 | compareZips( 197 | left, leftData, right, rightData, includeComments, _diffContainer); 198 | } 199 | 200 | Future _getData(VersionInfo info) async { 201 | Uri docsUri; 202 | 203 | var client = new http.BrowserClient(); 204 | var dd = new DartDownloads(client: client); 205 | try { 206 | docsUri = await dd.getDownloadLink( 207 | info.channel, info.revisionPath, 'api-docs/dart-api-docs.zip'); 208 | } finally { 209 | dd.close(); 210 | } 211 | 212 | _updateStatus('Downloading docs: ${info.channel} ${info.revisionPath}'); 213 | return await getBinaryContent(docsUri.toString()); 214 | } 215 | 216 | void _printError(e, stack, String message) { 217 | print(message); 218 | print(e); 219 | print(stack); 220 | _updateStatus('$message See console output.'); 221 | } 222 | 223 | void _select(SelectElement element, String valueAttrValue) { 224 | var option = element.options.firstWhere((OptionElement oe) { 225 | return oe.attributes['value'] == valueAttrValue; 226 | }, orElse: () => null); 227 | 228 | if (option == null) return; 229 | 230 | if (element.selectedOptions.contains(option)) return; 231 | 232 | var index = element.options.indexOf(option); 233 | element.selectedIndex = index; 234 | } 235 | -------------------------------------------------------------------------------- /lib/src/common/reporters/method_attributes_reporter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0, found in the LICENSE file. 3 | 4 | library shapeshift_common.method_attribute_reporter; 5 | 6 | import 'package:doc_coverage/doc_coverage_common.dart'; 7 | import 'package:json_diff/json_diff.dart' show DiffNode; 8 | 9 | import '../markdown_diff_writer.dart'; 10 | 11 | /// Reporter for changes in a method's attributes. 12 | /// 13 | /// This reporter can iterate over attributes with their own added, deleted, or 14 | /// changed properties, and iterate over shallow attributes (such as `return`) 15 | /// to list changes. 16 | /// 17 | /// Definitions that might help: 18 | /// 19 | /// * There are 5 method categories: constructor, method, getter, setter, and 20 | /// operator. 21 | /// * Each method has attributes, such as "parameters" and "annotations" 22 | /// * Each method attribute, or member thereof, might have properties. 23 | /// E.g. Each member of the "parameters" attribute (each parameter) has 24 | /// properties such as "name", and "type". 25 | /// 26 | /// It is unexpected to find any new or removed attributes themselves (a method 27 | /// should never be missing the "parameters" attribute, or suddenly gain the 28 | /// "annotations" attribute). 29 | class MethodAttributesReporter { 30 | final String category; 31 | final String method; 32 | final DiffNode attributes; 33 | final MarkdownDiffWriter io; 34 | final Function erase; 35 | 36 | String link; 37 | bool shouldHr; 38 | 39 | MethodAttributesReporter( 40 | this.category, this.method, this.attributes, this.io, this.erase) { 41 | link = mdLinkToDartlang(attributes.metadata['qualifiedName'], method); 42 | shouldHr = false; 43 | } 44 | 45 | void report() { 46 | attributes.forEach(reportEach); 47 | attributes.forEachChanged(reportEachChanged); 48 | erase(attributes.changed); 49 | } 50 | 51 | void reportEach(String attributeName, DiffNode attribute) { 52 | reportRemovedProperties(attributeName, attribute); 53 | reportAddedProperties(attributeName, attribute); 54 | reportChangedProperties(attributeName, attribute); 55 | 56 | if (shouldHr) io.writeHr(); 57 | 58 | attribute.node.forEach((propertyName, property) { 59 | reportEachProperty(attributeName, propertyName, property); 60 | }); 61 | } 62 | 63 | void reportEachChanged(String key, List oldNew) { 64 | // We don't care about commentFrom changing. 65 | if (key == 'commentFrom') return; 66 | 67 | io.writeln('The $link $category\'s `${key}` changed:\n'); 68 | if (key == 'return') { 69 | io.writeWasNow(simpleType(oldNew[0]), simpleType(oldNew[1])); 70 | } else { 71 | io.writeWasNow(oldNew[0], oldNew[1], blockquote: key == 'comment'); 72 | } 73 | io.writeHr(); 74 | } 75 | 76 | void reportRemovedProperties(String attributeName, DiffNode attribute) { 77 | if (!attribute.hasRemoved) return; 78 | 79 | io.writeln('The $link $category has removed $attributeName:\n'); 80 | shouldHr = true; 81 | 82 | attribute.forEachRemoved( 83 | (_, property) => io.writeln(propertyListItem(attributeName, property))); 84 | 85 | io.writeln(''); 86 | erase(attribute.removed); 87 | } 88 | 89 | void reportAddedProperties(String attributeName, DiffNode attribute) { 90 | if (!attribute.hasAdded) return; 91 | 92 | if (shouldHr) { 93 | // TODO: get this font-weight up. 94 | io.writeln('and new $attributeName:\n'); 95 | } else { 96 | io.writeln('The $link $category has new $attributeName:\n'); 97 | } 98 | 99 | shouldHr = true; 100 | 101 | attribute.forEachAdded( 102 | (_, property) => io.writeln(propertyListItem(attributeName, property))); 103 | 104 | erase(attribute.added); 105 | } 106 | 107 | void reportChangedProperties(String attributeName, DiffNode attribute) { 108 | if (!attribute.hasChanged) return; 109 | 110 | if (shouldHr) { 111 | // TODO: get this font weight up. 112 | io.writeln('and changed $attributeName:\n'); 113 | } else { 114 | io.writeln('The $link $category has changed $attributeName:\n'); 115 | } 116 | 117 | shouldHr = true; 118 | 119 | attribute.forEachChanged((_, property) { 120 | if (attributeName == 'annotations') { 121 | io.writeln( 122 | '* ${formattedAnnotation(property[0])} is now ${formattedAnnotation(property[1])}.'); 123 | } else { 124 | io.writeln('* `${property[0]}` is now `${property[1]}`.'); 125 | } 126 | }); 127 | 128 | erase(attribute.changed); 129 | } 130 | 131 | String propertyListItem(String attributeName, Map property) { 132 | if (attributeName == 'annotations') { 133 | return '* ${formattedAnnotation(property)}'; 134 | } 135 | 136 | if (attributeName == 'parameters') { 137 | return '* `${parameterSignature(property)}`'; 138 | } 139 | 140 | return '* `$property`'; 141 | } 142 | 143 | void reportEachProperty( 144 | String attributeName, String propertyName, DiffNode property) { 145 | String methodQualifiedName = attributes.metadata['qualifiedName']; 146 | String methodLink = mdLinkToDartlang(methodQualifiedName, method); 147 | String propertyLink = 148 | mdLinkToDartlang('$methodQualifiedName,$propertyName', propertyName); 149 | String firstPart = 150 | 'The $methodLink $category\'s $propertyLink ${singularize(attributeName)}'; 151 | property.forEachChanged((key, oldNew) { 152 | if (key == 'type') { 153 | io.writeln('$firstPart\'s $key ${changedType(oldNew)}'); 154 | } else if (key == 'default') { 155 | String change = (oldNew[0]) 156 | ? 'no longer has a default value (was ${property.changed['value'][0]}).' 157 | : 'now has a default value (${property.changed['value'][1]}).'; 158 | io.writeln('$firstPart $change'); 159 | } else { 160 | if (key == 'value' && property.changed.containsKey('default')) { 161 | // We will report the changed 'value' when reporting the changed 162 | // 'default'. 163 | return; 164 | } 165 | io.writeln( 166 | '$firstPart\'s $key changed from `$key: ${oldNew[0]}` to `$key: ${oldNew[1]}`'); 167 | } 168 | io.writeHr(); 169 | }); 170 | erase(property.changed); 171 | 172 | if (property.containsKey('type')) { 173 | String key = 'type'; 174 | List oldNew = property[key]['0'].changed['outer']; 175 | // This is so ugly because we are so deep, but an example would be: 176 | // The foo method's value parameter's type has changed from int to bool. 177 | io.writeln('The [$method](#) $category\'s [$propertyName](#) ' 178 | '${singularize(attributeName)}\'s $key has changed from ' 179 | '`${oldNew[0]}` to `${oldNew[1]}`'); 180 | io.writeHr(); 181 | erase(property.node, 'type'); 182 | } 183 | 184 | if (property.containsKey('functionDeclaration')) { 185 | DiffNode declaration = property.node['functionDeclaration']; 186 | if (declaration.changed.containsKey('return')) { 187 | List> oldNew = declaration.changed['return']; 188 | // This is so ugly because we are so deep, but an example would be: 189 | // The foo method's callback parameter's return type has changed from 190 | // Object to String. 191 | io.writeln('The [$method](#) $category\'s [$propertyName](#) ' 192 | '${singularize(attributeName)}\'s return type ${changedType(oldNew)}'); 193 | io.writeHr(); 194 | erase(declaration.changed, 'return'); 195 | } 196 | } 197 | } 198 | } 199 | 200 | String changedType(List oldNew) { 201 | String oldType = simpleType(oldNew[0]); 202 | String newType = simpleType(oldNew[1]); 203 | return 'changed from `$oldType` to `$newType`'; 204 | } 205 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /web/vendor/jszip.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZip - A Javascript class for generating and reading zip files 4 | 5 | 6 | (c) 2009-2014 Stuart Knightley 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. 8 | 9 | JSZip uses the library pako released under the MIT license : 10 | https://github.com/nodeca/pako/blob/master/LICENSE 11 | */ 12 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.lengtha)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.compressionOptions=null,c.comment=null,c.unixPermissions=null,c.dosPermissions=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a,b){return e.deflateRaw(a,{level:b.level||-1})},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;gc;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a0?a.substring(0,b):""},x=function(a){return"/"!=a.slice(-1)&&(a+="/"),a},y=function(a,b){return b="undefined"!=typeof b?b:!1,a=x(a),this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},z=function(a,b,c){var f,g=new j;return a._data instanceof j?(g.uncompressedSize=a._data.uncompressedSize,g.crc32=a._data.crc32,0===g.uncompressedSize||a.dir?(b=i.STORE,g.compressedContent="",g.crc32=0):a._data.compressionMethod===b.magic?g.compressedContent=a._data.getCompressedContent():(f=a._data.getContent(),g.compressedContent=b.compress(d.transformTo(b.compressInputType,f),c))):(f=p(a),(!f||0===f.length||a.dir)&&(b=i.STORE,f=""),g.uncompressedSize=f.length,g.crc32=e(f),g.compressedContent=b.compress(d.transformTo(b.compressInputType,f),c)),g.compressedSize=g.compressedContent.length,g.compressionMethod=b.magic,g},A=function(a,b){var c=a;return a||(c=b?16893:33204),(65535&c)<<16},B=function(a){return 63&(a||0)},C=function(a,b,c,g,h){var i,j,k,m,n=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),o=b.comment||"",p=d.transformTo("string",l.utf8encode(o)),q=n.length!==b.name.length,r=p.length!==o.length,t=b.options,u="",v="",w="";k=b._initialMetadata.dir!==b.dir?b.dir:t.dir,m=b._initialMetadata.date!==b.date?b.date:t.date;var x=0,y=0;k&&(x|=16),"UNIX"===h?(y=798,x|=A(b.unixPermissions,k)):(y=20,x|=B(b.dosPermissions,k)),i=m.getHours(),i<<=6,i|=m.getMinutes(),i<<=5,i|=m.getSeconds()/2,j=m.getFullYear()-1980,j<<=4,j|=m.getMonth()+1,j<<=5,j|=m.getDate(),q&&(v=s(1,1)+s(e(n),4)+n,u+="up"+s(v.length,2)+v),r&&(w=s(1,1)+s(this.crc32(p),4)+p,u+="uc"+s(w.length,2)+w);var z="";z+="\n\x00",z+=q||r?"\x00\b":"\x00\x00",z+=c.compressionMethod,z+=s(i,2),z+=s(j,2),z+=s(c.crc32,4),z+=s(c.compressedSize,4),z+=s(c.uncompressedSize,4),z+=s(n.length,2),z+=s(u.length,2);var C=f.LOCAL_FILE_HEADER+z+n+u,D=f.CENTRAL_FILE_HEADER+s(y,2)+z+s(p.length,2)+"\x00\x00\x00\x00"+s(x,4)+s(g,4)+n+u+p;return{fileRecord:C,dirRecord:D,compressedObject:c}},D={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=y.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;cg&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;cb?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a>8;this.dir=16&this.externalFileAttributes?!0:!1,a===h&&(this.dosPermissions=63&this.externalFileAttributes),a===i&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileName.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead=hb&&(a.ins_h=(a.ins_h<=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<=hb&&(a.ins_h=(a.ins_h<4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<q&&(p+=B[f++]<>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<q&&(p+=B[f++]<>>=w,q-=w),15>q&&(p+=B[f++]<>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<q&&(p+=B[f++]<q&&(p+=B[f++]<k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whaven;){if(0===i)break a;i--,m+=e[g++]<>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<n;){if(0===i)break a;i--,m+=e[g++]<>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.haven;){if(0===i)break a;i--,m+=e[g++]<>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1); 14 | break}for(c.back=0;Ab=c.lencode[m&(1<>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<n;){if(0===i)break a;i--,m+=e[g++]<=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++hh?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++jj){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)}); --------------------------------------------------------------------------------