From fb959c4fb5c2d7938cae117897453db80f290609 Mon Sep 17 00:00:00 2001 From: jamesliauw Date: Sat, 2 Jun 2018 11:40:18 +0800 Subject: [PATCH 01/10] feat: init w4 --- .babelrc | 3 + .gitignore | 12 + LICENSE | 21 - README.md | 71 +- a1/README.md | 3285 ----- a1/assets/above-the-fold-1.png | Bin 126268 -> 0 bytes a1/assets/above-the-fold-2.png | Bin 109158 -> 0 bytes a1/assets/brackets-angular-snippets.yaml | 226 - a1/assets/gde.png | Bin 61980 -> 0 bytes a1/assets/modularity-1.png | Bin 302935 -> 0 bytes a1/assets/modularity-2.png | Bin 34181 -> 0 bytes a1/assets/ng-clean-code-banner.png | Bin 56340 -> 0 bytes .../angular.controller.sublime-snippet | 27 - .../angular.directive.sublime-snippet | 40 - .../angular.factory.sublime-snippet | 27 - .../angular.filter.sublime-snippet | 23 - .../angular.module.sublime-snippet | 13 - .../angular.service.sublime-snippet | 24 - a1/assets/testing-tools.png | Bin 95536 -> 0 bytes .../angular.controller.snip | 22 - .../angular.directive.snip | 35 - .../vim-angular-snippets/angular.factory.snip | 22 - .../vim-angular-snippets/angular.filter.snip | 19 - .../vim-angular-snippets/angular.module.snip | 10 - .../vim-angular-snippets/angular.service.snip | 19 - .../javascript_angular.controller.snippets | 22 - .../javascript_angular.directive.snippets | 35 - .../javascript_angular.factory.snippets | 22 - .../javascript_angular.filter.snippets | 19 - .../javascript_angular.module.snippets | 10 - .../javascript_angular.service.snippets | 19 - a1/assets/vscode-snippets/javascript.json | 124 - a1/assets/vscode-snippets/typescript.json | 64 - .../angular.app.webstorm-live-template.xml | 33 - .../angular.config.webstorm-live-template.xml | 34 - ...ular.controller.webstorm-live-template.xml | 35 - ...gular.directive.webstorm-live-template.xml | 37 - ...angular.factory.webstorm-live-template.xml | 36 - .../angular.filter.webstorm-live-template.xml | 34 - .../angular.module.webstorm-live-template.xml | 31 - .../angular.route.webstorm-live-template.xml | 35 - .../angular.run.webstorm-live-template.xml | 34 - ...angular.service.webstorm-live-template.xml | 36 - .../angular.state.webstorm-live-template.xml | 36 - .../webstorm-angular-live-templates.xml | 361 - a1/i18n/README.md | 41 - a1/i18n/de-DE.md | 3098 ----- a1/i18n/es-ES.md | 2838 ---- a1/i18n/fr-FR.md | 3185 ----- a1/i18n/it-IT.md | 3233 ----- a1/i18n/ja-JP.md | 3104 ----- a1/i18n/ko-KR.md | 3255 ----- a1/i18n/mk-MK.md | 2929 ----- a1/i18n/pt-BR.md | 2516 ---- a1/i18n/ru-RU.md | 2774 ---- a1/i18n/tr-TR.md | 3233 ----- a1/i18n/zh-CN.md | 3102 ----- a2/README.md | 423 - a2/notes.md | 632 - package-lock.json | 10726 ++++++++++++++++ package.json | 42 + postcss.config.js | 5 + src/assets/readme.txt | 1 + src/css/a.css | 27 + src/images/edit.png | Bin 0 -> 3037 bytes src/index.html | 13 + src/index.js | 13 + src/index2.html | 10 + src/js/index.js | 14 + src/js/index2.js | 1 + src/js/test.js | 3 + src/less/test.less | 4 + src/sass/test.scss | 5 + webpack.config.js | 44 + webpack.plguins.js | 50 + webpack.rules.js | 71 + 76 files changed, 11048 insertions(+), 39300 deletions(-) create mode 100644 .babelrc create mode 100644 .gitignore delete mode 100644 LICENSE delete mode 100644 a1/README.md delete mode 100644 a1/assets/above-the-fold-1.png delete mode 100644 a1/assets/above-the-fold-2.png delete mode 100644 a1/assets/brackets-angular-snippets.yaml delete mode 100644 a1/assets/gde.png delete mode 100644 a1/assets/modularity-1.png delete mode 100644 a1/assets/modularity-2.png delete mode 100644 a1/assets/ng-clean-code-banner.png delete mode 100644 a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet delete mode 100644 a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet delete mode 100644 a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet delete mode 100644 a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet delete mode 100644 a1/assets/sublime-angular-snippets/angular.module.sublime-snippet delete mode 100644 a1/assets/sublime-angular-snippets/angular.service.sublime-snippet delete mode 100644 a1/assets/testing-tools.png delete mode 100644 a1/assets/vim-angular-snippets/angular.controller.snip delete mode 100644 a1/assets/vim-angular-snippets/angular.directive.snip delete mode 100644 a1/assets/vim-angular-snippets/angular.factory.snip delete mode 100644 a1/assets/vim-angular-snippets/angular.filter.snip delete mode 100644 a1/assets/vim-angular-snippets/angular.module.snip delete mode 100644 a1/assets/vim-angular-snippets/angular.service.snip delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets delete mode 100644 a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets delete mode 100644 a1/assets/vscode-snippets/javascript.json delete mode 100644 a1/assets/vscode-snippets/typescript.json delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml delete mode 100644 a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml delete mode 100644 a1/i18n/README.md delete mode 100644 a1/i18n/de-DE.md delete mode 100644 a1/i18n/es-ES.md delete mode 100644 a1/i18n/fr-FR.md delete mode 100644 a1/i18n/it-IT.md delete mode 100644 a1/i18n/ja-JP.md delete mode 100644 a1/i18n/ko-KR.md delete mode 100644 a1/i18n/mk-MK.md delete mode 100644 a1/i18n/pt-BR.md delete mode 100644 a1/i18n/ru-RU.md delete mode 100644 a1/i18n/tr-TR.md delete mode 100644 a1/i18n/zh-CN.md delete mode 100644 a2/README.md delete mode 100644 a2/notes.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 src/assets/readme.txt create mode 100644 src/css/a.css create mode 100644 src/images/edit.png create mode 100644 src/index.html create mode 100644 src/index.js create mode 100644 src/index2.html create mode 100644 src/js/index.js create mode 100644 src/js/index2.js create mode 100644 src/js/test.js create mode 100644 src/less/test.less create mode 100644 src/sass/test.scss create mode 100644 webpack.config.js create mode 100644 webpack.plguins.js create mode 100644 webpack.rules.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..181cce65 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["env"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4331e8bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 83192343..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2016 John Papa - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index f8d72e76..ceb47bf5 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,4 @@ -# Angular Style Guide - -## Versions -There are multiple versions of Angular, and thus there are multiple versions of the guide. Choose your guide appropriately. - -### Angular 1 Style Guide -[The Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). - -### Angular 2 Style Guide -[The **D R A F T** Angular 2 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). - -## Angular Team Endorsed -Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. - -## Purpose -*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* - -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. - -The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. - ->If you like this guide, check out my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at Pluralsight which is a companion to this guide. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My most excellent friend Ward has helped influence the ultimate evolution of these guides. - -## Contributing -Open an issue first to discuss potential changes/additions. If you have questions with the guide, feel free to leave them as issues in the repository. If you find a typo, create a pull request. The idea is to keep the content up to date and use github’s native feature to help tell the story with issues and PR’s, which are all searchable via google. Why? Because odds are if you have a question, someone else does too! You can learn more here at about how to contribute. - -*By contributing to this repository you are agreeing to make your content available subject to the license of this repository.* - -### Process - 1. Discuss the changes in a GitHub issue. - 2. Open a Pull Request, reference the issue, and explain the change and why it adds value. - 3. The Pull Request will be evaluated and either merged or declined. - -## License - -_tldr; Use this guide. Attributions are appreciated._ - -### Copyright - -Copyright (c) 2014-2016 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Back to top](#angular-style-guide)** +# webpack4.x_Demo +基于webpack4.6.0的多页应用配置,包括常见的插件使用与配置。 +要点都有注释! +如有帮助,请赐我一颗小星星。 diff --git a/a1/README.md b/a1/README.md deleted file mode 100644 index a4275686..00000000 --- a/a1/README.md +++ /dev/null @@ -1,3285 +0,0 @@ -# Angular 1 Style Guide - -## Angular Team Endorsed -Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. - -## Purpose -*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* - -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. - -The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. - ->If you like this guide, check out my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at Pluralsight which is a companion to this guide. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) to get a sense for his approach and how it compares. - -Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. - -## See the Styles in a Sample App -While this guide explains the *what*, *why* and *how*, I find it helpful to see them in practice. This guide is accompanied by a sample application that follows these styles and patterns. You can find the [sample application (named modular) here](https://github.com/johnpapa/ng-demos) in the `modular` folder. Feel free to grab it, clone it, or fork it. [Instructions on running it are in its readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -##Translations -[Translations of this Angular style guide](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n) are maintained by the community and can be found here. - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises](#resolving-promises) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - -## Single Responsibility - -### Rule of 1 -###### [Style [Y001](#style-y001)] - - - Define 1 component per file, recommended to be less than 500 lines of code. - - *Why?*: One component per file promotes easier unit testing and mocking. - - *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - - *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - - The following example defines the `app` module and its dependencies, defines a controller, and defines a factory all in the same file. - - ```javascript - /* avoid */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - The same components are now separated into their own files. - - ```javascript - /* recommended */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommended */ - - // some.controller.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - - // some.factory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Back to top](#table-of-contents)** - -### Small Functions -###### [Style [Y002](#style-y002)] - - - Define small functions, no more than 75 LOC (less is better). - - *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. - - *Why?*: Small functions promote reuse. - - *Why?*: Small functions are easier to read. - - *Why?*: Small functions are easier to maintain. - - *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - -**[Back to top](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - - Wrap Angular components in an Immediately Invoked Function Expression (IIFE). - - *Why?*: An IIFE removes variables from the global scope. This helps prevent variables and function declarations from living longer than expected in the global scope, which also helps avoid variable collisions. - - *Why?*: When your code is minified and bundled into a single file for deployment to a production server, you could have collisions of variables and many global variables. An IIFE protects you against both of these by providing variable scope for each file. - - ```javascript - /* avoid */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger function is added as a global variable - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage function is added as a global variable - function storage() { } - ``` - - ```javascript - /** - * recommended - * - * no globals are left behind - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Note: For brevity only, the rest of the examples in this guide may omit the IIFE syntax. - - - Note: IIFE's prevent test code from reaching private members like regular expressions or helper functions which are often good to unit test directly on their own. However you can test these through accessible members or by exposing them through their own component. For example placing helper functions, regular expressions or constants in their own factory or constant. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [Y020](#style-y020)] - - - Use unique naming conventions with separators for sub-modules. - - *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. - -### Definitions (aka Setters) -###### [Style [Y021](#style-y021)] - - - Declare modules without a variable using the setter syntax. - - *Why?*: With 1 component per file, there is rarely a need to introduce a variable for the module. - - ```javascript - /* avoid */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Instead use the simple setter syntax. - - ```javascript - /* recommended */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - When using a module, avoid using a variable and instead use chaining with the getter syntax. - - *Why?*: This produces more readable code and avoids variable collisions or leaks. - - ```javascript - /* avoid */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-y023)] - - - Only set once and get for all other instances. - - *Why?*: A module should only be created once, then retrieved from that point and after. - - ```javascript - /* recommended */ - - // to set a module - angular.module('app', []); - - // to get a module - angular.module('app'); - ``` - -### Named vs Anonymous Functions -###### [Style [Y024](#style-y024)] - - - Use named functions instead of passing an anonymous function in as a callback. - - *Why?*: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Back to top](#table-of-contents)** - -## Controllers - -### controllerAs View Syntax -###### [Style [Y030](#style-y030)] - - - Use the [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) syntax over the `classic controller with $scope` syntax. - - *Why?*: Controllers are constructed, "newed" up, and provide a single new instance, and the `controllerAs` syntax is closer to that of a JavaScript constructor than the `classic $scope syntax`. - - *Why?*: It promotes the use of binding to a "dotted" object in the View (e.g. `customer.name` instead of `name`), which is more contextual, easier to read, and avoids any reference issues that may occur without "dotting". - - *Why?*: Helps avoid using `$parent` calls in Views with nested controllers. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax -###### [Style [Y031](#style-y031)] - - - Use the `controllerAs` syntax over the `classic controller with $scope` syntax. - - - The `controllerAs` syntax uses `this` inside controllers which gets bound to `$scope` - - *Why?*: `controllerAs` is syntactic sugar over `$scope`. You can still bind to the View and still access `$scope` methods. - - *Why?*: Helps avoid the temptation of using `$scope` methods inside a controller when it may otherwise be better to avoid them or move the method to a factory, and reference them from the controller. Consider using `$scope` in a controller only when needed. For example when publishing and subscribing events using [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), or [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on). - - ```javascript - /* avoid */ - function CustomerController($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs with vm -###### [Style [Y032](#style-y032)] - - - Use a capture variable for `this` when using the `controllerAs` syntax. Choose a consistent variable name such as `vm`, which stands for ViewModel. - - *Why?*: The `this` keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of `this` avoids encountering this problem. - - ```javascript - /* avoid */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function CustomerController() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Note: You can avoid any [jshint](http://jshint.com/) warnings by placing the comment above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Note: When creating watches in a controller using `controller as`, you can watch the `vm.*` member using the following syntax. (Create watches with caution as they add more load to the digest cycle.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - - Note: When working with larger codebases, using a more descriptive name can help ease cognitive overhead & searchability. Avoid overly verbose names that are cumbersome to type. - - ```html - - - ``` - - ```html - - - ``` - -### Bindable Members Up Top -###### [Style [Y033](#style-y033)] - - - Place bindable members at the top of the controller, alphabetized, and not spread through the controller code. - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. - - *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. - - ```javascript - /* avoid */ - function SessionsController() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. - - ```javascript - /* avoid */ - function SessionsController(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - -### Function Declarations to Hide Implementation Details -###### [Style [Y034](#style-y034)] - - - Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code/). - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.) - - *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). - - *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. - - *Why?*: Order is critical with function expressions - - ```javascript - /** - * avoid - * Using function expressions. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Notice that the important stuff is scattered in the preceding example. In the example below, notice that the important stuff is up top. For example, the members bound to the controller such as `vm.avengers` and `vm.title`. The implementation details are down below. This is just easier to read. - - ```javascript - /* - * recommend - * Using function declarations - * and bindable members up top. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Defer Controller Logic to Services -###### [Style [Y035](#style-y035)] - - - Defer logic in a controller by delegating to services and factories. - - *Why?*: Logic may be reused by multiple controllers when placed within a service and exposed via a function. - - *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the controller can be easily mocked. - - *Why?*: Removes dependencies and hides implementation details from the controller. - - *Why?*: Keeps the controller slim, trim, and focused. - - ```javascript - - /* avoid */ - function OrderController($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recommended */ - function OrderController(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showError); - }; - } - ``` - -### Keep Controllers Focused -###### [Style [Y037](#style-y037)] - - - Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view. - - *Why?*: Reusing controllers with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. - -### Assigning Controllers -###### [Style [Y038](#style-y038)] - - - When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes. - - Note: If a View is loaded via another means besides a route, then use the `ng-controller="Avengers as vm"` syntax. - - *Why?*: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), that view is always associated with the same controller. - - ```javascript - /* avoid - when using with a route and dynamic pairing is desired */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommended */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - Services are instantiated with the `new` keyword, use `this` for public methods and variables. Since these are so similar to factories, use a factory instead for consistency. - - Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). This means that there is only one instance of a given service per injector. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Back to top](#table-of-contents)** - -## Factories - -### Single Responsibility -###### [Style [Y050](#style-y050)] - - - Factories should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a factory begins to exceed that singular purpose, a new factory should be created. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Factories are singletons and return an object that contains the members of the service. - - Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). - -### Accessible Members Up Top -###### [Style [Y052](#style-y052)] - - - Expose the callable members of the service (its interface) at the top, using a technique derived from the [Revealing Module Pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). - - *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. - - *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. - - ```javascript - /* avoid */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommended */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Function Declarations to Hide Implementation Details -###### [Style [Y053](#style-y053)] - - - Use function declarations to hide implementation details. Keep your accessible members of the factory up top. Point those to function declarations that appears later in the file. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Why?*: Placing accessible members at the top makes it easy to read and helps you instantly identify which functions of the factory you can access externally. - - *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). - - *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. - - *Why?*: Order is critical with function expressions - - ```javascript - /** - * avoid - * Using function expressions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommended - * Using function declarations - * and accessible members up top. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [Y060](#style-y060)] - - - Refactor logic for making data operations and interacting with data to a factory. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. - - *Why?*: The controller's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the controller be simpler and more focused on the view. - - *Why?*: This makes it easier to test (mock or real) the data calls when testing a controller that uses a data service. - - *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a controller), also making it easier to change the implementation. - - ```javascript - /* recommended */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. - - ```javascript - /* recommended */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['dataservice', 'logger']; - - function AvengersController(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Return a Promise from Data Calls -###### [Style [Y061](#style-y061)] - - - When calling a data service that returns a promise such as `$http`, return a promise in your calling function too. - - *Why?*: You can chain the promises together and take further action after the data call completes and resolves or rejects the promise. - - ```javascript - /* recommended */ - - activate(); - - function activate() { - /** - * Step 1 - * Ask the getAvengers function for the - * avenger data and wait for the promise - */ - return getAvengers().then(function() { - /** - * Step 4 - * Perform an action on resolve of final promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Ask the data service for the data and wait - * for the promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * set the data and resolve the promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Back to top](#table-of-contents)** - -## Directives -### Limit 1 Per File -###### [Style [Y070](#style-y070)] - - - Create one directive per file. Name the file for the directive. - - *Why?*: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module. - - *Why?*: One directive per file is easy to maintain. - - > Note: "**Best Practice**: Directives should clean up after themselves. You can use `element.on('$destroy', ...)` or `scope.$on('$destroy', ...)` to run a clean-up function when the directive is removed" ... from the Angular documentation. - - ```javascript - /* avoid */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order directive that is specific to the order module */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales directive that can be used anywhere across the sales app */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner directive that can be used anywhere across apps */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* implementation details */ - } - - function salesCustomerInfo() { - /* implementation details */ - } - - function sharedSpinner() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* calendar-range.directive.js */ - - /** - * @desc order directive that is specific to the order module at a company named Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* customer-info.directive.js */ - - /** - * @desc sales directive that can be used anywhere across the sales app at a company named Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* spinner.directive.js */ - - /** - * @desc spinner directive that can be used anywhere across apps at a company named Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* implementation details */ - } - ``` - - Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. - -### Manipulate DOM in a Directive -###### [Style [Y072](#style-y072)] - - - When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow. - - *Why?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) - -### Provide a Unique Directive Prefix -###### [Style [Y073](#style-y073)] - - - Provide a short, unique and descriptive directive prefix such as `acmeSalesCustomerInfo` which would be declared in HTML as `acme-sales-customer-info`. - - *Why?*: The unique short prefix identifies the directive's context and origin. For example a prefix of `cc-` may indicate that the directive is part of a CodeCamper app while `acme-` may indicate a directive for the Acme company. - - Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). - -### Restrict to Elements and Attributes -###### [Style [Y074](#style-y074)] - - - When creating a directive that makes sense as a stand-alone element, allow restrict `E` (custom element) and optionally restrict `A` (custom attribute). Generally, if it could be its own control, `E` is appropriate. General guideline is allow `EA` but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element. - - *Why?*: It makes sense. - - *Why?*: While we can allow the directive to be used as a class, if the directive is truly acting as an element it makes more sense as an element or at least as an attribute. - - Note: EA is the default for Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* avoid */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommended */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives and ControllerAs -###### [Style [Y075](#style-y075)] - - - Use `controller as` syntax with a directive to be consistent with using `controller as` with view and controller pairings. - - *Why?*: It makes sense and it's not difficult. - - Note: The directive below demonstrates some of the ways you can use scope inside of link and directive controllers, using controllerAs. I in-lined the template just to keep it all in one place. - - Note: Regarding dependency injection, see [Manually Identify Dependencies](#manual-annotating-for-dependency-injection). - - Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - // note: This would be 'ExampleController' (the exported controller name, as string) - // if referring to a defined controller in its separate file. - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injecting $scope just for comparison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Note: You can also name the controller when you inject it into the link function and access directive attributes as properties of the controller. - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - Use `bindToController = true` when using `controller as` syntax with a directive when you want to bind the outer scope to the directive's controller's scope. - - *Why?*: It makes it easy to bind outer scope to the directive's controller scope. - - Note: `bindToController` was introduced in Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Back to top](#table-of-contents)** - -## Resolving Promises -### Controller Activation Promises -###### [Style [Y080](#style-y080)] - - - Resolve start-up logic for a controller in an `activate` function. - - *Why?*: Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller. - - *Why?*: The controller `activate` makes it convenient to re-use the logic for a refresh for the controller/View, keeps the logic together, gets the user to the View faster, makes animations easy on the `ng-view` or `ui-view`, and feels snappier to the user. - - Note: If you need to conditionally cancel the route before you start using the controller, use a [route resolve](#style-y081) instead. - - ```javascript - /* avoid */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Route Resolve Promises -###### [Style [Y081](#style-y081)] - - - When a controller depends on a promise to be resolved before the controller is activated, resolve those dependencies in the `$routeProvider` before the controller logic is executed. If you need to conditionally cancel a route before the controller is activated, use a route resolver. - - - Use a route resolve when you want to decide to cancel the route before ever transitioning to the View. - - *Why?*: A controller may require data before it loads. That data may come from a promise via a custom factory or [$http](https://docs.angularjs.org/api/ng/service/$http). Using a [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) allows the promise to resolve before the controller logic executes, so it might take action based on that data from the promise. - - *Why?*: The code executes after the route and in the controller’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via `ng-view` or `ui-view`) - - Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the [controller `activate` technique](#style-y080) instead. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('AvengersController', AvengersController); - - function AvengersController(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection. - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Note: The code example's dependency on `movieService` is not minification safe on its own. For details on how to make this code minification safe, see the sections on [dependency injection](#manual-annotating-for-dependency-injection) and on [minification and annotation](#minification-and-annotation). - -**[Back to top](#table-of-contents)** - -### Handling Exceptions with Promises -###### [Style [Y082](#style-y082)] - - - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. - - - Always handle exceptions in services/factories. - - *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. - - *Why?*: To avoid swallowing errors and misinforming the user. - - Note: Consider putting any exception handling in a function in a shared module and service. - - ```javascript - /* avoid */ - - function getCustomer(id) { - return $http.get('/api/customer/' + id) - .then(getCustomerComplete) - .catch(getCustomerFailed); - - function getCustomerComplete(data, status, headers, config) { - return data.data; - } - - function getCustomerFailed(e) { - var newMessage = 'XHR Failed for getCustomer' - if (e.data && e.data.description) { - newMessage = newMessage + '\n' + e.data.description; - } - e.data.description = newMessage; - logger.error(newMessage); - // *** - // Notice there is no return of the rejected promise - // *** - } - } - - /* recommended */ - function getCustomer(id) { - return $http.get('/api/customer/' + id) - .then(getCustomerComplete) - .catch(getCustomerFailed); - - function getCustomerComplete(data, status, headers, config) { - return data.data; - } - - function getCustomerFailed(e) { - var newMessage = 'XHR Failed for getCustomer' - if (e.data && e.data.description) { - newMessage = newMessage + '\n' + e.data.description; - } - e.data.description = newMessage; - logger.error(newMessage); - return $q.reject(e); - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### UnSafe from Minification -###### [Style [Y090](#style-y090)] - - - Avoid using the shortcut syntax of declaring dependencies without using a minification-safe approach. - - *Why?*: The parameters to the component (e.g. controller, factory, etc) will be converted to mangled variables. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. - - ```javascript - /* avoid - not minification-safe*/ - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController(common, dataservice) { - } - ``` - - This code may produce mangled variables when minified and thus cause runtime errors. - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('DashboardController', d);function d(a, b) { } - ``` - -### Manually Identify Dependencies -###### [Style [Y091](#style-y091)] - - - Use `$inject` to manually identify your dependencies for Angular components. - - *Why?*: This technique mirrors the technique used by [`ng-annotate`](https://github.com/olov/ng-annotate), which I recommend for automating the creation of minification safe dependencies. If `ng-annotate` detects injection has already been made, it will not duplicate it. - - *Why?*: This safeguards your dependencies from being vulnerable to minification issues when parameters may be mangled. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. - - *Why?*: Avoid creating in-line dependencies as long lists can be difficult to read in the array. Also it can be confusing that the array is a series of strings while the last item is the component's function. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('DashboardController', DashboardController); - - DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function DashboardController($location, $routeParams, common, dataservice) { - } - ``` - - Note: When your function is below a return statement the `$inject` may be unreachable (this may happen in a directive). You can solve this by moving the Controller outside of the directive. - - ```javascript - /* avoid */ - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommended */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Manually Identify Route Resolver Dependencies -###### [Style [Y092](#style-y092)] - - - Use `$inject` to manually identify your route resolver dependencies for Angular components. - - *Why?*: This technique breaks out the anonymous function for the route resolver, making it easier to read. - - *Why?*: An `$inject` statement can easily precede the resolver to handle making any dependencies minification safe. - - ```javascript - /* recommended */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Use [ng-annotate](//github.com/olov/ng-annotate) for [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) and comment functions that need automated dependency injection using `/* @ngInject */` - - *Why?*: This safeguards your code from any dependencies that may not be using minification-safe practices. - - *Why?*: [`ng-min`](https://github.com/btford/ngmin) is deprecated - - >I prefer Gulp as I feel it is easier to write, to read, and to debug. - - The following code is not using minification safe dependencies. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - ``` - - When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - - AvengersController.$inject = ['storage', 'avengerService']; - ``` - - Note: If `ng-annotate` detects injection has already been made (e.g. `@ngInject` was detected), it will not duplicate the `$inject` code. - - Note: When using a route resolver you can prefix the resolver's function with `/* @ngInject */` and it will produce properly annotated code, keeping any injected dependencies minification safe. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note: Starting from Angular 1.3 you can use the [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) directive's `ngStrictDi` parameter to detect any potentially missing minification safe dependencies. When present the injector will be created in "strict-di" mode causing the application to fail to invoke functions which do not use explicit function annotation (these may not be minification safe). Debugging info will be logged to the console to help track down the offending code. I prefer to only use `ng-strict-di` for debugging purposes only. - `` - -### Use Gulp or Grunt for ng-annotate -###### [Style [Y101](#style-y101)] - - - Use [gulp-ng-annotate](https://www.npmjs.com/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.com/package/grunt-ng-annotate) in an automated build task. Inject `/* @ngInject */` prior to any function that has dependencies. - - *Why?*: ng-annotate will catch most dependencies, but it sometimes requires hints using the `/* @ngInject */` syntax. - - The following code is an example of a gulp task using ngAnnotate - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Back to top](#table-of-contents)** - -## Exception Handling - -### decorators -###### [Style [Y110](#style-y110)] - - - Use a [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), at config time using the [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) service, on the [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) service to perform custom actions when exceptions occur. - - *Why?*: Provides a consistent way to handle uncaught Angular exceptions for development-time or run-time. - - Note: Another option is to override the service instead of using a decorator. This is a fine option, but if you want to keep the default behavior and extend it a decorator is recommended. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Could add the error to a service's collection, - * add errors to $rootScope, log errors to remote web server, - * or log locally. Or throw hard. It is entirely up to you. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Catchers -###### [Style [Y111](#style-y111)] - - - Create a factory that exposes an interface to catch and gracefully handle exceptions. - - *Why?*: Provides a consistent way to catch exceptions that may be thrown in your code (e.g. during XHR calls or promise failures). - - Note: The exception catcher is good for catching and reacting to specific exceptions from calls that you know may throw one. For example, when making an XHR call to retrieve data from a remote web service and you want to catch any exceptions from that service and react uniquely. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Errors -###### [Style [Y112](#style-y112)] - - - Handle and log all routing errors using [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Why?*: Provides a consistent way to handle all routing errors. - - *Why?*: Potentially provides a better user experience if a routing error occurs and you route them to a friendly screen with more details or recovery options. - - ```javascript - /* recommended */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionally log using a custom service or $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - - /** - * On routing error, go to another route/state. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [Y120](#style-y120)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: - * the file name (`avengers.controller.js`) - * the registered component name with Angular (`AvengersController`) - - *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. - - *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - -### Feature File Names -###### [Style [Y121](#style-y121)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for any automated tasks. - - ```javascript - /** - * common options - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommended - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Note: Another common convention is naming controller files without the word `controller` in the file name such as `avengers.js` instead of `avengers.controller.js`. All other conventions still hold using a suffix of the type. Controllers are the most common type of component so this just saves typing and is still easily identifiable. I recommend you choose 1 convention and be consistent for your team. My preference is `avengers.controller.js` identifying the `AvengersController`. - - ```javascript - /** - * recommended - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test File Names -###### [Style [Y122](#style-y122)] - - - Name test specifications similar to the component they test with a suffix of `spec`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - - ```javascript - /** - * recommended - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller Names -###### [Style [Y123](#style-y123)] - - - Use consistent names for all controllers named after their feature. Use UpperCamelCase for controllers, as they are constructors. - - *Why?*: Provides a consistent way to quickly identify and reference controllers. - - *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Controller Name Suffix -###### [Style [Y124](#style-y124)] - - - Append the controller name with the suffix `Controller`. - - *Why?*: The `Controller` suffix is more commonly used and is more explicitly descriptive. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Factory and Service Names -###### [Style [Y125](#style-y125)] - - - Use consistent names for all factories and services named after their feature. Use camel-casing for services and factories. Avoid prefixing factories and services with `$`. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). - - *Why?*: Provides a consistent way to quickly identify and reference factories. - - *Why?*: Avoids name collisions with built-in factories and services that use the `$` prefix. - - *Why?*: Clear service names such as `logger` do not require a suffix. - - *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `avengersService`. - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - - ```javascript - /** - * recommended - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // customer.service.js - angular - .module - .service('customerService', customerService); - - function customerService() { } - ``` - -### Directive Component Names -###### [Style [Y126](#style-y126)] - - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - - *Why?*: Provides a consistent way to quickly identify and reference components. - - ```javascript - /** - * recommended - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - When there are multiple modules, the main module file is named `app.module.js` while other dependent modules are named after what they represent. For example, an admin module is named `admin.module.js`. The respective registered module names would be `app` and `admin`. - - *Why?*: Provides consistency for multiple module apps, and for expanding to large applications. - - *Why?*: Provides easy way to use task automation to load all module definitions first, then all other angular files (for bundling). - -### Configuration -###### [Style [Y128](#style-y128)] - - - Separate configuration for a module into its own file named after the module. A configuration file for the main `app` module is named `app.config.js` (or simply `config.js`). A configuration for a module named `admin.module.js` is named `admin.config.js`. - - *Why?*: Separates configuration from module definition, components, and active code. - - *Why?*: Provides an identifiable place to set configuration for a module. - -### Routes -###### [Style [Y129](#style-y129)] - - - Separate route configuration into its own file. Examples might be `app.route.js` for the main module and `admin.route.js` for the `admin` module. Even in smaller apps I prefer this separation from the rest of the configuration. - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. - - *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? - - When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines - - 1. `L`ocating our code is easy - 2. `I`dentify code at a glance - 3. `F`lat structure as long as we can - 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - Make locating your code intuitive, simple and fast. - - *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - When you look at a file you should instantly know what it contains and represents. - - *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. - -### Flat -###### [Style [Y143](#style-y143)] - - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. - - *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - Be DRY, but don't go nuts and sacrifice readability. - - *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [Y150](#style-y150)] - - - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each controller, service, module, view is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app (`bower_components`, `scripts`, `lib`). - - Note: Find more details and reasoning behind the structure at [this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and controller may act as the container for the app, navigation, menus, content areas, and other regions. - - *Why?*: Organizes all layout in a single place re-used throughout the application. - -### Folders-by-Feature Structure -###### [Style [Y152](#style-y152)] - - - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. - - *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. - - *Why?*: The LIFT guidelines are all covered. - - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. - - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. - - ```javascript - /** - * recommended - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Note: Do not structure your app using folders-by-type. This requires moving to multiple folders when working on a feature and gets unwieldy quickly as the app grows to 5, 10 or 25+ views and controllers (and other features), which makes it more difficult than folder-by-feature to locate files. - - ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [Y160](#style-y160)] - - - Create small modules that encapsulate one responsibility. - - *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. - -### Create an App Module -###### [Style [Y161](#style-y161)] - - - Create an application root module whose role is to pull together all of the modules and features of your application. Name this for your application. - - *Why?*: Angular encourages modularity and separation patterns. Creating an application root module whose role is to tie your other modules together provides a very straightforward way to add or remove modules from your application. - -### Keep the App Module Thin -###### [Style [Y162](#style-y162)] - - - Only put logic for pulling together the app in the application module. Leave features in their own modules. - - *Why?*: Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. - - *Why?*: The app module becomes a manifest that describes which modules help define the application. - -### Feature Areas are Modules -###### [Style [Y163](#style-y163)] - - - Create modules that represent feature areas, such as layout, reusable and shared services, dashboards, and app specific features (e.g. customers, admin, sales). - - *Why?*: Self contained modules can be added to the application with little or no friction. - - *Why?*: Sprints or iterations can focus on feature areas and turn them on at the end of the sprint or iteration. - - *Why?*: Separating feature areas into modules makes it easier to test the modules in isolation and reuse code. - -### Reusable Blocks are Modules -###### [Style [Y164](#style-y164)] - - - Create modules that represent reusable application blocks for common services such as exception handling, logging, diagnostics, security, and local data stashing. - - *Why?*: These types of features are needed in many applications, so by keeping them separated in their own modules they can be application generic and be reused across applications. - -### Module Dependencies -###### [Style [Y165](#style-y165)] - - - The application root module depends on the app specific feature modules and any shared or reusable modules. - - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Why?*: The main app module contains a quickly identifiable manifest of the application's features. - - *Why?*: Each feature area contains a manifest of what it depends on, so it can be pulled in as a dependency in other applications and still work. - - *Why?*: Intra-App features such as shared data services become easy to locate and share from within `app.core` (choose your favorite name for this module). - - Note: This is a strategy for consistency. There are many good options here. Choose one that is consistent, follows Angular's dependency rules, and is easy to maintain and scale. - - > My structures vary slightly between projects but they all follow these guidelines for structure and modularity. The implementation may vary depending on the features and the team. In other words, don't get hung up on an exact like-for-like structure but do justify your structure using consistency, maintainability, and efficiency in mind. - - > In a small app, you can also consider putting all the shared dependencies in the app module where the feature modules have no direct dependencies. This makes it easier to maintain the smaller application, but makes it harder to reuse modules outside of this application. - -**[Back to top](#table-of-contents)** - -## Startup Logic - -### Configuration -###### [Style [Y170](#style-y170)] - - - Inject code into [module configuration](https://docs.angularjs.org/guide/module#module-loading-dependencies) that must be configured before running the angular app. Ideal candidates include providers and constants. - - *Why?*: This makes it easier to have less places for configuration. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run Blocks -###### [Style [Y171](#style-y171)] - - - Any code that needs to run when an application starts should be declared in a factory, exposed via a function, and injected into the [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Why?*: Code directly in a run block can be difficult to test. Placing in a factory makes it easier to abstract and mock. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document and $window -###### [Style [Y180](#style-y180)] - - - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) and [`$window`](https://docs.angularjs.org/api/ng/service/$window) instead of `document` and `window`. - - *Why?*: These services are wrapped by Angular and more easily testable than using document and window in tests. This helps you avoid having to mock document and window yourself. - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) and [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) instead of `setTimeout` and `setInterval` . - - *Why?*: These services are wrapped by Angular and more easily testable and handle Angular's digest cycle thus keeping data binding in sync. - -**[Back to top](#table-of-contents)** - -## Testing -Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information. - -### Write Tests with Stories -###### [Style [Y190](#style-y190)] - - - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. - - *Why?*: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success. - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Testing Library -###### [Style [Y191](#style-y191)] - - - Use [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) for unit testing. - - *Why?*: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features. - - Note: When using Mocha, also consider choosing an assert library such as [Chai](http://chaijs.com). I prefer Mocha. - -### Test Runner -###### [Style [Y192](#style-y192)] - - - Use [Karma](http://karma-runner.github.io) as a test runner. - - *Why?*: Karma is easy to configure to run once or automatically when you change your code. - - *Why?*: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp. - - *Why?*: Some IDE's are beginning to integrate with Karma, such as [WebStorm](http://www.jetbrains.com/webstorm/) and [Visual Studio](https://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Why?*: Karma works well with task automation leaders such as [Grunt](http://gruntjs.com/) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://gulpjs.com/). When using Gulp, use [Karma](https://github.com/karma-runner/karma) directly and not with a plugin as the API can be called directly. - - ```javascript - /* recommended */ - - // Gulp example with Karma directly - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### Stubbing and Spying -###### [Style [Y193](#style-y193)] - - - Use [Sinon](http://sinonjs.org/) for stubbing and spying. - - *Why?*: Sinon works well with both Jasmine and Mocha and extends the stubbing and spying features they offer. - - *Why?*: Sinon makes it easier to toggle between Jasmine and Mocha, if you want to try both. - - *Why?*: Sinon has descriptive messages when tests fail the assertions. - -### Headless Browser -###### [Style [Y194](#style-y194)] - - - Use [PhantomJS](http://phantomjs.org/) to run your tests on a server. - - *Why?*: PhantomJS is a headless browser that helps run your tests without needing a "visual" browser. So you do not have to install Chrome, Safari, IE, or other browsers on your server. - - Note: You should still test on all browsers in your environment, as appropriate for your target audience. - -### Code Analysis -###### [Style [Y195](#style-y195)] - - - Run JSHint on your tests. - - *Why?*: Tests are code. JSHint can help identify code quality issues that may cause the test to work improperly. - -### Alleviate Globals for JSHint Rules on Tests -###### [Style [Y196](#style-y196)] - - - Relax the rules on your test code to allow for common globals such as `describe` and `expect`. Relax the rules for expressions, as Mocha uses these. - - *Why?*: Your tests are code and require the same attention and code quality rules as all of your production code. However, global variables used by the testing framework, for example, can be relaxed by including this in your test specs. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Or you can add the following to your JSHint Options file. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Organizing Tests -###### [Style [Y197](#style-y197)] - - - Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate `tests` folder. - - *Why?*: Unit tests have a direct correlation to a specific component and file in source code. - - *Why?*: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage. - - *Why?*: When you update source code it is easier to go update the tests at the same time. - - *Why?*: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source. - - *Why?*: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations. - - *Why?*: Separating specs so they are not in a distributed build is easy with grunt or gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Back to top](#table-of-contents)** - -## Animations - -### Usage -###### [Style [Y210](#style-y210)] - - - Use subtle [animations with Angular](https://docs.angularjs.org/guide/animations) to transition between states for views and primary visual elements. Include the [ngAnimate module](https://docs.angularjs.org/api/ngAnimate). The 3 keys are subtle, smooth, seamless. - - *Why?*: Subtle animations can improve User Experience when used appropriately. - - *Why?*: Subtle animations can improve perceived performance as views transition. - -### Sub Second -###### [Style [Y211](#style-y211)] - - - Use short durations for animations. I generally start with 300ms and adjust until appropriate. - - *Why?*: Long animations can have the reverse effect on User Experience and perceived performance by giving the appearance of a slow application. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Use [animate.css](http://daneden.github.io/animate.css/) for conventional animations. - - *Why?*: The animations that animate.css provides are fast, smooth, and easy to add to your application. - - *Why?*: Provides consistency in your animations. - - *Why?*: animate.css is widely used and tested. - - Note: See this [great post by Matias Niemelä on Angular animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Back to top](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-y220)] - - - If planning to produce documentation, use [`jsDoc`](http://usejsdoc.org/) syntax to document function names, description, params and returns. Use `@namespace` and `@memberOf` to match your app structure. - - *Why?*: You can generate (and regenerate) documentation from your code, instead of writing it from scratch. - - *Why?*: Provides consistency using a common industry tool. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Back to top](#table-of-contents)** - -## JS Hint - -### Use an Options File -###### [Style [Y230](#style-y230)] - - - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://jshint.com/docs/) for details on the options. - - *Why?*: Provides a first alert prior to committing any code to source control. - - *Why?*: Provides consistency across your team. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Back to top](#table-of-contents)** - -## JSCS - -### Use an Options File -###### [Style [Y235](#style-y235)] - - - Use JSCS for checking your coding styles your JavaScript and be sure to customize the JSCS options file and include in source control. See the [JSCS docs](http://jscs.info/) for details on the options. - - *Why?*: Provides a first alert prior to committing any code to source control. - - *Why?*: Provides consistency across your team. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "jsDoc": { - "checkAnnotations": true, - "checkParamNames": true, - "requireParamTypes": true, - "checkReturnTypes": true, - "checkTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Back to top](#table-of-contents)** - -## Constants - -### Vendor Globals -###### [Style [Y240](#style-y240)] - - - Create an Angular Constant for vendor libraries' global variables. - - *Why?*: Provides a way to inject vendor libraries that otherwise are globals. This improves code testability by allowing you to more easily know what the dependencies of your components are (avoids leaky abstractions). It also allows you to mock these dependencies, where it makes sense. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Use constants for values that do not change and do not come from another service. When constants are used only for a module that may be reused in multiple applications, place constants in a file per module named after the module. Until this is required, keep constants in the main module in a `constants.js` file. - - *Why?*: A value that may change, even infrequently, should be retrieved from a service so you do not have to change the source code. For example, a url for a data service could be placed in a constants but a better place would be to load it from a web service. - - *Why?*: Constants can be injected into any angular component, including providers. - - *Why?*: When an application is separated into modules that may be reused in other applications, each stand-alone module should be able to operate on its own including any dependent constants. - - ```javascript - // Constants used by the entire app - angular - .module('app.core') - .constant('moment', moment); - - // Constants used only by the sales module - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular snippets that follow these styles and guidelines. - - - Download the [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) - - Place it in your Packages folder - - Restart Sublime - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Angular file templates that follow these styles and guidelines can be found at [SideWaffle](http://www.sidewaffle.com) - - - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) - - Run the vsix file - - Restart Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Angular live templates that follow these styles and guidelines. - - - Download the [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) - - Place it in your [templates folder](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) - - Restart WebStorm - - In a JavaScript file type these commands followed by a `TAB`: - - ```javascript - // These are full file snippets containing an IIFE - ngapp // creates an Angular module setter - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngfilter // creates an Angular filter - ngservice // creates an Angular service - - // These are partial snippets intended to be chained - ngconfig // defines a configuration phase function - ngmodule // creates an Angular module getter - ngroute // defines an Angular ngRoute 'when' definition - ngrun // defines a run phase function - ngstate // creates an Angular UI Router state definition - ``` - - *Individual templates are also available for download within the [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true) folder* - -### Atom -###### [Style [Y253](#style-y253)] - - - Angular snippets that follow these styles and guidelines. - ``` - apm install angularjs-styleguide-snippets - ``` - or - - Open Atom, then open the Package Manager (Packages -> Settings View -> Install Packages/Themes) - - Search for the package 'angularjs-styleguide-snippets' - - Click 'Install' to install the package - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular snippets that follow these styles and guidelines. - - Download the [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) - - Brackets Extension manager ( File > Extension manager ) - - Install ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Click the light bulb in brackets' right gutter - - Click `Settings` and then `Import` - - Choose the file and select to skip or override - - Click `Start Import` - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngapp // creates an Angular module setter - ngservice // creates an Angular service - ngfilter // creates an Angular filter - - // These are partial snippets intended to chained - ngmodule // creates an Angular module getter - ngstate // creates an Angular UI Router state definition - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngwhen // defines an Angular ngRoute 'when' definition - ngtranslate // uses $translate service with its promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - vim snippets that follow these styles and guidelines. - - - Download the [vim Angular snippets](assets/vim-angular-snippets?raw=true) - - set [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - copy snippets to snippet directory - - - vim UltiSnips snippets that follow these styles and guidelines. - - - Download the [vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) - - set [UltiSnips](https://github.com/SirVer/ultisnips) - - copy snippets to UltiSnips directory - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio Code - -###### [Style [Y256](#style-y256)] - - - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - - - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) - - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ``` - -**[Back to top](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -You can use the [HotTowel yeoman generator](http://jpapa.me/yohottowel) to create an app that serves as a starting point for Angular that follows this style guide. - -1. Install generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Create a new folder and change directory to it - - ``` - mkdir myapp - cd myapp - ``` - -3. Run the generator - - ``` - yo hottowel helloWorld - ``` - -**[Back to top](#table-of-contents)** - -## Routing -Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. - -###### [Style [Y270](#style-y270)] - - - Use the [AngularUI Router](http://angular-ui.github.io/ui-router/) for client-side routing. - - *Why?*: UI Router offers all the features of the Angular router plus a few additional ones including nested routes and states. - - *Why?*: The syntax is quite similar to the Angular router and is easy to migrate to UI Router. - - - Note: You can use a provider such as the `routerHelperProvider` shown below to help configure states across files, during the run phase. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - Define routes for views in the module where they exist. Each module should contain the routes for the views in the module. - - *Why?*: Each module should be able to stand on its own. - - *Why?*: When removing a module or adding a module, the app will only contain routes that point to existing views. - - *Why?*: This makes it easy to enable or disable portions of an application without concern over orphaned routes. - -**[Back to top](#table-of-contents)** - -## Task Automation -Use [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) for creating automated tasks. Gulp leans to code over configuration while Grunt leans to configuration over code. I personally prefer Gulp as I feel it is easier to read and write, but both are excellent. - -> Learn more about gulp and patterns for task automation in my [Gulp Pluralsight course](http://jpapa.me/gulpps) - -###### [Style [Y400](#style-y400)] - - - Use task automation to list module definition files `*.module.js` before all other application JavaScript files. - - *Why?*: Angular needs the module definitions to be registered before they are used. - - *Why?*: Naming modules with a specific pattern such as `*.module.js` makes it easy to grab them with a glob and list them first. - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Back to top](#table-of-contents)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - Avoid using filters for scanning all properties of a complex object graph. Use filters for select properties. - - *Why?*: Filters can easily be abused and negatively affect performance if not used wisely, for example when a filter hits a large and deep object graph. - -**[Back to top](#table-of-contents)** - -## Angular docs -For anything else, API reference, check the [Angular documentation](//docs.angularjs.org/api). - -**[Back to top](#table-of-contents)** diff --git a/a1/assets/above-the-fold-1.png b/a1/assets/above-the-fold-1.png deleted file mode 100644 index 71b977803f7df282926fa2e6c659f4d354cf40f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126268 zcmZ_01yGb>+XgBnh;%GScS(v!H%qrP2uOE#N{O&^cXvv6iFCKr(w)-HdGY)H|D2hB z&I~iVv+u(0`^0@;^$?;cFM*0ofc)y!D^w{-G38gU;P76(g7ray1zwRKwUB!Sa|9

|zM?g4SWkx* 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Ðu1|KdR*!R`uGI=mT7;~ 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%0AH5$zm$yvAl{n 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%0D%^ty6X%(x`%0gB1FvQ4u9p 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 diff --git a/a1/assets/modularity-1.png b/a1/assets/modularity-1.png deleted file mode 100644 index 4ba0af469e49a9bd78e99abcfcea0da9d7f25110..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302935 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

vx07R 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

3ugQWh%wr(;c^DLIL(+c;&vspmMoK9#v z<+$#&Ht-I;3@40gX;>~pnB{@tVbm#N1 zF(fJy37zM@pVRTA0`At7>PRHp!jhA|I>qz&oG7tl)zVr-N%X?*MZpC4Vm0KX-*zlA z<#cxVkP#f3Ba(G5ow(rqEL*{2)OXEZj7i~FY?<6ppUyK_xX7ruU=|t!vSrz$%W<$c zl;y8bT-Je*q8!ebEO>ddi|RT62qR+Xt>WLitln&Zu8UTwiTATkDV5FoO&*IWKNkq&(u>y}ooR@Xp68JpIDShcHjO-L84Q`j?F^he?D+FZ zrregCf9l1O;zzle|Fwb5C<~D>(Vk#WOs?AbIQFZ=CqXb31~B8E)c=8C*16SCdw zTlh@(6UTiC;DbF~U*K2*eP6uBw1fq4AV;=RYGqZow!CE_Pc z6C7!YE>Bi7IDuz+exc~A?gS>#bM!o0ix6;FB#VvENMGM+=7eC?q;VN+zraWsy9=?5U$pZb!Fr_S8_`r6_u#7u3|I&+E7T6#X*H2U)_*Ct$2-kXb{~5Dviiw{j2H zO;08HWiQ3U*=1Z_?g4{r?F!^AGsrpQWU)RlAGC31GQ&F0F<@ApA8;TLKEzS_=KmW> zNbTFN)B8Iwj<)QA-&BWg@JSMbfOPH@27{Wknc$N#YD!6v(N||pc=fU5GOjM7IwL?N z$2*410LWQ)K>EkvHxTh~QHp2j24fBZ#BzB48HR(6@{VpQrCKBwj)j@USF58yIp5dk zvSv>IQ3;D;r~OezB*xeQN9>z=b#e1fgZWUVXxGQgFOMV@aPd!`A#|`%Qw5N z4LpX&Mhf6JB*XtJ~}Bn~dnBtn5)^{skI6hgi4dk>Cg zg@HCEszky@OcDyqzW!lhQ(_AIGqc{+bMRm3K9bi>*{cl&EKYC{3i-8Ip{LDCJFeTN z9M~7UV#R)X@wOctV9Es$9q&r~&LdYc^cJrE^~zk;h+@*cwN0abnmJsrUohZbd+(4;AipNHS&n4x}q$7K1;}k($uVxHDN@OzP zB+%G|#QuuW0LKAD>4lAU2+j+`LjB|3z3};FOF>dOOX_|&?|zBLqR>{2neT_9R$tMP zx#?pBAiG66f4HSMR=tf1PYt=>&lcUddYFx;*&OY5$vkQ=e2X&c9`~@Y39iHuZj0MX z`ff?|IvNZ~1gjm$^AO6#&M2d#mc1q|q|6p$@=r`~6WS1FbG(#dmGP2>n+vRJ(sw?u zv*oCpiX5;g`1ZB*x+|C`8 zs*MK7nF_@^{su&ga-`xnnP;chGPBv_S40zlcco;D*4IM)up1OEX)q&)EKp3~g!w~J zpznST+J^~bWkRCza@u0}`*#1(fK;=%%E-4x+)k8Q!bGxss9POthc%H&fJ7~ABv9xZ zB0vye--Q6>WxW%NfgeN|FYqznDo#~(dpo{+`rO)~hUpU%`DQso2H@O+! zcz0A7zw<|UqM0oKtLPQgVBf%p-L*{Ss)Nu~Dg5;9Y!Y6o8-h6D5 z&{mcDOym0e1VMEP^A^DM7mzesr}bl|PDM~a=vT9h$2`e@v^P~gYR3O)Z>)+y8}hcf z#6R5^wxXB(x!oAj0oYV7SQM-Q*n5bK9o1TG4Uk=0E&%A(o%TLHkXd)u)ogsC^}37k z!cKl_$yxf^q~{OL;z|+{hXm|lPrM!!XvgpOv$~Q4FPdUjapOw9?zAX1%yt(H>J`8* z#Ho3wiq&&%5ggEF*yxx4;pdb(NZey&q#CGy9ZaQ^qv&|3V>J3(O5t#@B#U=xY4L<2 zR~3N3Es=CkPsyP%Xrqr-C$CwhHw#Wn_h&ohgTnp?XpE%)V@gI~^!VL_+XLjRk-nm5PK*ep1 zliyXS7yC&q&+1kro4cf5QZ!isd`K|<6rs>(ueZ2>FP8|a&Q!{+v}CY{{)N)HiFc}s z8L5$*Ld=p~jyeXtKg9M~X4T-xc7MZi@`xIIoe9CKF3dG4Fv+iLCI@lPH`NG5L9$Kl z&2SCtY73zY_HSPYq4(^DxJpf)YFI31tjB8-HXgG66zt$^Gb0PWzq%qT7AL#OVHfeJ z+CSlNvK<{%1)l;ODF7OtEdJX5>?WCuu|^4K;+xg#v`tOPwOEiwX}VE);rZ>`QE$xt zIee?sj!dOxR(eDL)Y&^U$aDSUIDOf+tNdh3Iz-@h6v~(HZew%n^eURyfAjc9FJzu+ zRgUy11T0isSr`e5{$VuuU>cf00KB1iD$OIS49NXOXT8(K%1tmQQbvD_Is)Lnj^}=d zViN>#?rLd$nvZ)EZ}4i)2iQUvJS#nKA zTl$Rr%I(0pT%2U-bnWk6is?V5rf^D4kEi3REEoWH3 z!)$<-?0HOo0`tZLLi7W9caJWYf(yhYb@ugiy7|PX?S!^{rQUM`UP0*3UcE_DC}$mt z1L?LzH6fP>j6&5yA#+iUn14DBGE@%5S_7ozlEOe&MC+fXBF0z&IJ5m)dp--RrT|1- zMU?hK0IopX764IRMk1EezkM`9*QweDdkBahn@`g=3eIin3NagS%rh(h8A890-{ha` zV1iW~%GB=nkz#gwP-Q>shgl<>c+9l;alD}ZLbah+=W#tFK>#3VgMCNs^z7t7{mU#Z z&0U-;KTa8;0Jb2faV80tL5<1%owM6Xu^G@nLLP~?w%c0oZypqX-*ZU)?FnOtgEd|x z5fmK}@%r69v?&{A*}UJ2#!;+mbkI%&H#8cH4SJU|ZvnmqwWbs(g1*xx(uviJxqOMo zF%vUoTXjfebo8R>piP4y-ShqWiclp`El~61|>q6 zFph<9a`(N5|F*RKNQ-fm0J6plyxK=aZ^nWWaRPAb5%9|ET}LX!HbchUcfYqp{GR=L znlMp0vJo`sfwu`kib$qwnjI~60f-R?zzs@BTK}LHmgk)59bKK^MG!;lwL`-?Gx~lI z5uAGVrSM@FHJzw0IjpF1wPHsws0Cn&X|gRNYo2(q;yie6bgl|1H2r#1kYjqCkW0d$ z`xtVR3gD+kV%ylokzAD(ZO;dXioGmWKxq}cCrQXdMyJhF|CTB!@DHCRx%?VQqZlUO zNSUG+<2tXArYb!LPGv5=6Nyjy;ali>brPiURLj@ZaU+<#9VGHl1VA7Ml&x*BX-zFM z)HDx@l(OgbXB_R>MS=MG+s{%Oo+4=GeyGask%D&|9CBXNFA?>e8Uky4ke?Ti0^76Q zpoj6w4*II#MMHjSb6in#2eMdqt3{Bl_yuQGaD4kRZpPU5LhSaseITk0yL}fjuclZp zCisDJ)TYa;C1}X_Sb{SBASx4y;#uElwov3V17sWpu>hE+2f-=Ry>U%H=USJG0&0)o zu44E=bnPg%6K7+Q;b5XWK$e@CB(z(Qqw@9d$#AX(-7u&Lvc7eAe;hp{E3xskm&Yd@0q$-$B z3L`&m00vlGgoJa|e*yV#h1FTc@5Pl74JHH?mU|DT8+R8{_Z=w?iWz5X*Tr-8_apgb z!9vLjXfsrQ;27F_+BXK_qWGC7Iu^h}fkxe^X=|vW(u7WlK#&@^%k(u3pX>*-$P7S- zk{J*eGDm$l{)JtV@xJX_@nijC1GPuUL(@ukQeN0}D9t2W@j43)tT4g%A3SZZsK1$J zPs6&qv9IQ8{6}PfNMVbVl!)^MuO1r-4w@Md9wQ>|`xj)eC_kaZx4+5_; z!$&m<{u%geqqDzh*a!>b$!-YS9jC8tLW_Ob#RZ;Hm%$v?77hbuDT|ezdF3+Ox@EhX zE#FhRUpQ&4Er>{oJZ$Yc8rRZ%P4|)7#3d;)tM|xfTU+F>!hN$$Ltl(dnq@ z`enCz2g6h)ofe==>hX_O(0 zq>dPM;K7bjhj?usi9=X^lhgwfwQ_rTyr>4Dor%?Yk$SChO;)2DTOsv0G(=@Oub9eu zXHM1C!*iKKDMNS5*${0=AF)z3H#oDqmiZQqd5bS*n$oaS?7J(ijn-;nnR|=h#UwgG zE+#{eGd?D4-{BZS7q|nCN~2ptQ+SB5&I=z3(t}S2o=LGXVo@CK1LvIR1`S4^PNxmN zhU~J}@5wMkPWo1RgdY4}HrOUG>XGF)vVME`Ar;S}xEsQr+V_hPAW>@@3BbKc3t88i zdv8CQ_aSlzZXO5Vo~{#3>mwGXcMrRI?N$q>)O}nd;GC{p`QosPSd9xycdcucE`QuM zYQJ3RH9!GWKsM1qB`&K=jqfJOoB4XV`2rjzKj9nS*o)Jv=PUx`)=L8nTk6I~+&Fd= z4vH@&nS#;=xqg!-PwKnITnrX9yuFfsDGQoeiRh`yDG|< zg@q@%w-ACADc_fCxuXxVA6QUmv><9C@>m9orfeoCFW-!s3hg$Hjs{&rFPASV zq}X93B79oI+6v&Qq5&##_hsnB{3))-dW}$btRcws1ZAg#lV=n*{aw&lQ z^A|b@CO=XmZN`R96&}FRJp8Rf%--cZX@)aL=WUFw#l_utcrb4W`o&%zdPZkcH zMj+@S^AY)PWyEmVAT}^AqkMOCTF9wsKPjNd8On6V zO&O~HkjeY`6xNMkVK{G7G~bi~VnPL2W%f651z@@7Y3h**1g7ie@W?B(W@>xgzOb0{bvE&%3~j4f}cQISOmFB$yX@SYI%VvwK2q}My+y{=*W}So3=w(JeP51 zbt3Gm6ej&M z+4+itmz(b&=dt9PC)z63(QZreltbW|`0F&^c{h|k)*G^l(;ZLYHru*|>^vl)jX2JV zuOTOO)7#jH0QUrvg6CmVmh`Ts>Ah?NS)r)fzal8wfaN=+jb;o0+`cqODk-|byzlZN zA5l-NU6(jpl*9?>7Bedt0N!+jomhxlQE$j9AjVD4h=IfupjaSmb3Xt-^H6m3Q{ zPbbH&6s8m5I}wwmg4&7X+w{HVeTgjc=voS>qeD5Po8SR5Cf!)B8(jzK>+}s0PT=9C z1zET7h^i0I>pvd^!F};!c_t<>>l_A`Rxch(cCIO{&Myc`vR`uNsF4OqBP_ufzj4bA z!X`}M%z4#^P_1zabu2;r?W?vacVd3mn(HX6dEmA+oxg1x8O4pSo>8U*0oK00#CK|+ zl`K7Qnk%BO1aCf4&z-4j8D*=jtCK6lSCZr}6?22am3O-z?2wlUQ-T9PDN}FjsSP>^ zvjXNpdMebREz?=%rWc5#hs-wyHYC05R_nQY?+3TLuoj&@J!lvb(1q6r|7g>n| zqR1&J065ovWT+;{7zw-<9wQGz2xms}{*TaSh~nD-f|45^dPMQBSCBd+-KduynG{Sr zBtBd1KJQ}{~QER3z0~-Swn_~ z@vO|JDi$=7EjX8+UC7_ksKkwnKr5M$2ruYTl^3M4+)8o%Kt6QKLk#NcyKh%LDfZKJ z)DX*cSHgfQ8)HM@mJCyt&QAKW0m8{?1t%VqGOH&;2YA~YI+1|>8N%QE`iE7{vhPOe zDwK6=8Ar^f{=V^z=CseV~;1B z*llL5k;N|R8#R6OVa3STp~Na8Tz`TgJjK{;V(3Ch&VegpvO-l%-eI1+p4~?6C@j7d z(TIogh>Rd)c1^dYTB2KdE#iM=@jvQQ@RLw&vygu*)i*WicOPL6Ge!!NBr*nwR$@Nf z)lxS&(@7AheW|4NJ=RAcs$bI1YQC6b8Ex)M-_Bmq=WEWnlZj88%KL6~;L)XE>+(7C z=K7RD$3+m6@3<_|0OjFTVE9eL<2@9aYj1Qjy#S-(uQ1cczd7%Vq!aU_lnXzcGjZe` z$&%iuy$C$iYe7hcY17YBlhoTI!nzRg%mn7!bu zcY1S;I?e@3nl38c=+;xQQ_?2ijVNFo(;Qh?iluKX(E&LL1yL z*&wFdgr_hzCQJ!+G^y#y%bv(}eB5`w)8vM`l@4M~*&bUU?hHl_6T#+!pe+ZNZP7%{ zThPaISL0J-;95?rFd}ogr!+zz$yB^I0g?Mqx0(7_AN9&poCF+Vj7BMY#B zB`by;pSWDvQ?~%JGbNTnRS#k+DYz7HY`}%v@&gGHnnD&!j}vixE2$J(*|YthSpX0* z)Z_b1MmRa0AXpZ;!k@p`k^Sv?j^qSlYB{Zct-S^42^UY70^LU2NAT$}BDnUcl*6sq|A|&_OTts zQm(w!!Z;U>eC@h|rMjcn(+Jzh%}*s-g#QS+An% zBeORmg`q5qCCcaAeJAPGG2c36Q_CuJ=mt_fF+m|K+8UM_@)s{8vmCKLX=A+Rmk-H< za@h5GCYGOYv63aibBrLOp)9m^0ZAxMr+@eHaPIo2kaX12rXmq&C(+8;@(MfVV|Nym z{U;pQ0-!I2Sw{jIMXofa5&HujsKQ^{W4z4^RW5K%l+cnf10-}bnHxqTQXzh^%4asm z81EannStTAc;u2&XchGgeeincDJEpmRJr^$LgyZ-DWz2t3sSLvT0>xz(cx2S6B>|= z6p&~y4f*x$?28c#LQSe5X>sp1Na#1c01%xMXv_s1Asa3r&*nQOAdxZZEKq~16l?q7 z77CTbG4jEJEHZqG)-O0X1 zCh|`WP8^)ua1G4x3b^a>UZG zwr;x@e9ov73;!zr&g-?!Lic8>xqglc=V@H|nReRxwpncnBQ5vKD!jA2T_Fu0N0Kty zCfms=$n61fs>R-5$m4w&6yp_Sp25q83h_wOhLlk1n@@2mVl*4FY`+8Ri4=Uw^6m<6 zU3L~@^AOI0OuQ-$r1xg`Hc)##`~shI41qh=M(8esAi9d@ykY2BAblbY_*_Eol z$lIR~Zrn0Z^?D^j{_W6=brRoAK`Hy#+*5ve$>FtA+!C6vmUkekb`P<22kp@}nsN#(zdQ;6QX*#km zh|8is$^xm|INZME47ZoH=}f-V*9kXiNZ7PRU>gu?B&V;#l`2PJ*Bj0UO& zzskr3MDeOvM7T5I$VDNkQ}k8!5m}EKUtvBMms#ITP)qlBWQ4zls(hdK-|0O3y~H5k z3Oa+7I0qis5}p`0iTy><1a%hDE5AUR*|(R$GJys7|x9CVVP3=3$YH z0WfJC-C8Af+wer=J!KmLaC4S+kTCgZ+@yQ;fQsMuaOaME=ss0$XZx*_(NiEPwFCqYA^Jx=)qH?oCN9AXzk3?q=;o@~A~0{F{?(2>hS zx*+?@0fbdR0TLtfk}vJA-2SDT9m+oPAghA4lut-Se1kBh-=I&Z&>6YznaCtSKQZt| z>=|c82T-7eC{}(;8Y|nUTq5t52pd0==GfhJ0v_HdUm5A;tdu*Z#9|Lk~Qz)0m8(^!Jp@k%ev4z;q1aDA`aUyN+wGq zGP;&G_@PV_;@K%FUMs`rf@R2+o3(zJjV|P@0<5M}TO=kfu9Io0l}2L|bX!Xuje^up zm6~MFZlv)I(sDk|>Ge(7O^tFr9yd$By#$|#)Fwmsc{*jH9`E!I)%+vT88-sV=`ZMm zUn~q^hn0p}t8QLm-6Vang+FFZ$O{#c=ttp>x+!#U{c(_o0qwY(3yf>V0od){0MQFi z7v>t?vVW>hqOyQNBPu7}Y!81%9Ysv_9l@8=!negK#kC(Q@9ca%HeJ^2r+Z4*iW@R( z7rtI_7qb{>X)@a{C%pc~Iv+96v@VdT&tX)Tz#-7uMb zzU|*Iz(-^vA}hk%E3F2{mT<{0p%-_WE?y!82VvQ5%4z&Dv9fG^iG7d^Y)>{34>(Nu z0(l%ihN>Of=)ztchV<{e^l#rIpv9gzhv4$_PS;*{r(4nR`#jC7yZHGZSr_a-%BFrB zZ0_90%#e)A0e<$Ki-i+tF)KCs72r?%0;2FQ-|k_ia$wrH9|rBCmgiLS22cAgq2C+_ z;S={EXNacGZ=if7(1aQeBG_j6)U@=szP+5Kynp{c0F6L$zmv9$i+n-i_;0q`xN&iUUJ&0_-?4dQ$+Cxlt9C0u)hwC1WBp!;>U=H0uK@=bg)bw8jSxs zFRfVuIx6!0u)&xkEUH4j#-MSTaUej*0VeQdNusRC_o^f44nnDq0HF9mU{;j=?xnxw zt+Nf-;>^3?!Nr&NxC-rfc+VG=DaYjMbAez26#D4!9-$qbBpF2Qmc~Vuc1vbZXy#%3 z)MQYv5^L(P4F7%xMu@#eys2awt?j-np*l;eL_8|We_Z!|tiavi@XV2KIRY3U7GmSizVeuX=XoE%8?A|+@e2;uZle4(Gb&9d_Z_n%Z$+scp4qKBQSkBx%4 z?iLL>t5SGU)zJfC{3)izkA+K{3){)Q01ZE3p=#rkI9Njvlu3>jByB6&RSiBlP31-84%E*4OqQi=EPAO zPg{#C-?a&xR?^0==pYv_U%+n);msh6WNm>GVjfy7iUK0?L z>-W|?8gx}CLAbB0m!~WRs;o^zbP%<*nRr5d)#m1igN}n-*|c{jeH#Vbn;gfpJZiuL zY>J*eJ1XOVLzPP1HW9M5sjA<)ljcxw40XM86Lw)Lg^w4a7+k+?^Dv{Gk?9zn5De4# zOP6dM_0tGJ+-~2TwR@-;dsXqrh5x>*knREx`7F8);9P2)u`mYRMv0>?u*)$|WGUvU z2BW;Vw2C9$7029a4s4amJ#n5NN}*DCZrssQV4vS`wOvd_K_S|RU%#-NwF>j^R1$Pl z@>}kCc=w8yCRPIAvA}%j_A?;by96_$E`E=Dd(pPbdJBxYL-+Dp_ zE-@lNIr9+!3#Wl000S+IIPVUy*bl%sNyYsU{qS3e7JyozG4aJ`v`zi@Dxi z1GOfP>B&F+&Z7OnYwOM*h^9d>6?RkV(S6q%t1y|;E=!5GKqW8-(7%dVle%*MzEMvS z3Ko9l<}Cma=AWX9C|aC&G|jgy9*6?u-$^$7)jL(ouC6nETQ2Gju=ok5CVU`#ft>$t>{K#pJke$5bx?#%~1(R<+q@={%^9AFd&9Hj!0LEzVt97ZK{FFemI{M{zcMgdDw_;eYA2ioQ;e9B1k zL(zBSq!}z5jfqkC#th@t0Zi;sNe-fBCgJCpKSyF4suFC_2K=&k)c%;vzJ@Hx0dbr0 zCx@nD%3Yr`_Y~$h&*4@CUOxr6F$3?I0}RM=Xtcp0oo*KQ>Rbf~;3U5lOjk-(0UQda z%b_yu8y}5ZK3yP;v*tYGh*6}!QTelhhi!v~x%M#Dq%pFW4n94OdS?dnc#SATcW=$Y zPjb+xh?Ml7=F4VhQBny?R1q!Pip}4fw;U0B`aly4@X-qNAihoqXA^*`7zSGo4X-S+ zhQhzOtupDo0nL8DD>i9zB7^g0I>Rub1JoMwxYRtlTxGykx<QO<(t5)A|3u6+-VQd^t1Th^f(%0`Uv$={+Alu}dz^3?70W_fE5;99{ z9yK(PdKqUr*$~6QDTlyPmkMq*=@V{^D;H_Lj}rYSN@!ggrW-_k^IwV@d@Gy<%b|azfVskIoZV4 z6PwLEfTX9H(H32Dn0AtGs1`$Zg z02mEUU47tU3&`*R4A(QA!RwD;-nB-a0VbuVkGU@J!T}d(VCdaxUjSgl(9GxtFq}39 zEQzi?$%(lkfclr8MI9KyQB3ARN$bu%9GU(vU!S+{-pry#tkcee?hdX$G77LCg~J1c zSVWkH4`@{I36#xQntlB!dvqPD$XA-<$Fu`=rla;b?cp2jUoyEA*dSqArjJ?fXW)DD z_=Nqh!yfI=qa4a;U#YNX=Q$84!E1QDSciHHx(x)%651rvh@2QqaK;Yq)*Lm3sJ}7AM%N?60GkdMg6jG~qf2;J0*OBr~IR4jAU( zL&Wz~0Gf)>YlM5RJbj6MEopgZD2?19>VIPF}44jb_E3wP&jUVJJ3a}*}k0U;NO_{hB<2S7_BS5+Cu z0sy2%EYqzd8b~c7qmjkyt33;>=UJQB+Bn#g&|D^dae+zVPV5<-8lW1* zLJ);~D?&*Rn^O!Vby;D47}mZpp$RNxA;P4Tm+Wzbh-o4c^wdkj_S`1pX;Pewc0wL| zu(hb5N-491NI$A; z?^Mu6sI!>Lm&e{ZK|3aX9HCR7hO{Qu(6-RR>pA7}g!2tHYH%3WE&($s0G0{R>Nutj zMHZ2aPD@xQbYjpOVZny}BQi|@{U0aX)bct`!x}}9Xci+ag3dlyj#*{XTY?68$}ItB zg>W1+j?>lz8w5hKlG|CmCC$6=Ab;J+av5l&(e9IH?9Htf*x)AF&~c&$S}j$-)s?gr zn?24kQ5bqg6xt3o#GbqxagJHjMl{M|3BKdd{an6)`9cmbji@6mr1CcJG;`=vd6r(s z$y%!0e~ysyTIK+0;PgZQ&y z>cnCQI`wh_zp{p-xwSkSBZH2{tBfRc8UT$d^V|*xSBF*csNWd-Qd1u0l;-@*sCQzy znr8gVW4vHDZ}_Q}0_8LY2ANk=XxmJT4O*p;w_Z+i3Q|jEobH)l47WHypTNPSm>n3K z42(pbb;RAjJsgj!=@a-n)T_ae_y9)5^L^n$s8;ZQ0o@#c!FuTd86EIK8X21Njy67l zc=x|Qb^wE3jxZ$+K&Was|FJ9+yZfQ@&T2^$KC7yuhxM0#u=)emTGurE8ic2++5K3iVHuAM$R zaR1p+U*?6oQ9aFW1K%WPq;l%Ffe>B77T#MS`F)@9u_LSFd=b(N?-+w`lBS9FQ~~1R zcXZg}Yv8%806uhBR+XP4z?$YJLOXnl4$fBCkB`?ruToy?x+~bWTBT?=Z{7p^Mw68T zi9t+S6YwwsB$}KuFX4PqD)a+HXXs8UcLP3dadz2;(E1!3L5r8Ya0U&IN%3185)|MW z+5qrQtMdrbiT<1L9?Of%9Eh}u?l+1blzt*i$kav4EWWi+>t9(Xyf1XhWV3etPQ;$S zGR=wm0A@RAo;YMuI~iaRW&mp(u8G$ZqoKLIvjtvjwt*M@s)~2F^5J||KkP}af}O(s z8#-UNs^n<23COc-H{ioBoF9efi=e4H0-bc2gwPDCA&_VdkZ68!#f3HX1aysYwl(&( zd9-n5l62+#nDgawnL|}6DKL$kwzIT(1_01>zsmN?7hSQaEaz<&jpl*Kj3wbeCIK17 zxkF{kykd2AjRU|@gB5j$xOC-#$<+t`;~oG;AEx5x(ez0&_L4jybHoM*FZtya_$tN* zrpx*C8e@_JIu4$$To^}ANK#Ln%63Z77}9exGs~j|QKOB{5vhKsYn^j-4xoqeK{!bJ z+@Uf*sqUV3QWFQnt9Ey8)z&$vV|KYVcd|d>n+WSY>gA@|;_HUFwF<8&9<0cC_Bc{* zRVTO>c@L>>(%MiIC$=cdplP;>=1B>&d8p)N`~mnIfE$mw6E%gSpri?y{sai27STK^ zVI?MLiw(3K$F0YD2sj^ zFhh&1R4RMWtdLsTC>ywo`hXpr%mm~-EC)c-#2K748tPi*$u$}wIs{ICoIPLcN@p(dTs>r?h9JZ2+KvHi_D`Z^gSCaiY&MAMyn8`T3mn zMPV#pDh8r#Y@BK=!Ib6b7YQXpXbcQ;niWR4b{ikGloO_5zz3P|Y_w{eTmh0SK#O%i z8i*Z3`$eWpVKghG+9|CAsX11vs1P!S6^d;LFeL&Vq)uCg0i1;nX?(|QK<6T+F|5~Y zAZR$MpAXBGAGWl9sSYuvzQ660H^Jk5DmxPIJ@q5EbP$%m2oe$BEvP&LduEhah5($}C z^|4_pgc**u{GrEM6N33n;4JH$3kN;yG?sb*D4oi%m}ke??95)@q=}?;PGh+N5Sd0} zMdRYA)6l&+oy9A(qlyF1GA1MynUDAY1|}p`qL8pYc)*1K3_d#`L)Un1tw$oV67P=Q zn)BY3$Mx6kK~c2I2Btci7ieEN2c7~l_`C&P2*eO@!An4fOkX~vNr3LP<*NNAdz70v z>fcV%5?%wS{8v9&wSV%xC0igIt@=LzHhc;_?srtt;jd=kod(#?s&XO<2wPySI{uj6 zHECI+OPm(J_bvw|tnp3Of=wrM_-);Fzkm6-@5+lZ>g=2Ey?28>lJej&%9x*Ubg*j) zQCZTAtg^Q(TCJ30?CD^m>Hy~~0^hZI0l!-SG6-}VZGRra8}aA{bzfgyvDYz)+d#`y z{sr9N-SqC$ey>B4_uhkV0Q{CYt9%Z~p<$Ini+U?;A0HldV27elw8c!hw-~HlCebaQtUKe1#5~`r*%Haw>kl$YEW9h#W=s?8Xk$ zZXRZhD#?>B69DZ^M1Kq2aC3~J1vS*kI^ znk%59%;C-5<{aZA3rHdU1oM9Q9yzy_R!5ooS1?~GFfNKRNr7MvRG^#!2Rd*6^nK?i zupiBp82qw;Q6=Y}4~=%o93}cghpv8`-3<@)M}y5z`i%B2nkCoYzel^^F>av$G297vEJCj}H8KnST$wFeOvdFPQAyCiPBxRsy z*4I(LT5|%mysvvF3J=oimaQRhA(b+LM?vQY5!?~#@YFk!(wTwm=!D>7Is-r;bCrzD zLEOpnu5y`|+VqyVZxOHJWq=w*s%Wz*tg}fBA_ykYT9DIJ30iU#Da{m3W^IgXD+mru zSfw@_mK+#4Z}^(vT5Q=2b1eQT1Zn|8@*8lV5DS@T6kD=uWz&86vX?t)BovYiTF+OVegN>4k_|SV36j*D&Bn z3Y9lmN33$fzYLhN4wE5=rnC^pooPTGKosh)XSETXc2bt^v;mMo=07bM78lZ&F(sb7 z$#-cT=!Ct_X@~4k2hju41|WMvkSOTNf|*o1dZ@F)+GU)5vO+Fuyp@ZJpAAET@I1&V zZw$H(Vp>rGd}=n(G#VhRmBuTk3ON`w(Lc?Cix!`1K%b!HK_5(^>KaLrk1n63Btk)x z*Z?L-j0gBJXQHvZv_^jxsJom}F}fJPO->J%IlWRSuL=58CPAanBUj{9k9_*H8S}i7 z#<(1aZnBZO80PM(PTg>73mB#Wp;N)rNE%xu7?}B4%){8!Wo9)tIqtN@s;n_BDy5PJ zhX6B@!=A%k7@-YLSLX1)vdTuDMe}MyvDWvgQYb7N&tc{a-nkiT3Lh;s?-p}qpW9EP zNvEHO%tERHE;>Fi6gsuKiWg$EFX}k<)bDyZ;6k4TVhFt8v&M*PsNY)N^-&;zf9dpV ze5@RM1V+SCIPdI49~l6+a9SAn#E?VJEv*Y*1B3eoV(bvmj#bnL{w9rm2|ZtZH)mgZ zY1BS(5n!mRf77Lw{q8sC?T>$&ad!O{ndVp0u3+8UUl6p9LJfQz%QF#OCgV=to8}^N=}fn6*QEl@7%Ow4}z+ z^2LXg_OvD@GxCepO;>4Xg+DR_ zRxio|VJ$CcK{$n7Pjh_WnceUHq`;yJJMP{RG@MrPpk)XthyHu2lpyx{n=xYca(ge{W&OCnLq)o2@%DMuukO!$vqgp=+(k;6* zfgIWtV!*75lrf#E&yf+!F{ry5?gj;@O6qc#9mp&X_t zS;eK-W=37Q7jxy~*^9OT957Si6xuo3WZep*)p6zWMHXdGp!XIzfj!6v()eJ*(6X$^ zE@2#(_OS^?06gSCR%mkp1X-pvf)wO{jMf4)5H1cTLViES01qB!(q^Z1Si{bN9kf-O zwKjbBgLV~?_qVgb&^Y;#2x9`JSlI%f@-Tu0>fGp}Loe;C(Y9hCO92} zG7cs^-ieBH!UkiIhcObJJvwN!;(1-iCRk2hANzrPvp_t;~(wgJ=bzzZ}loEeEUFaS|2_`l%Piv^2C zhQh8%c0jSI|0`T;Eb5UTQl5d*|Iicb z`oRBDmoKqq|94-$V}Ix8C+#y=h8*De&SKeq@5ih5I$>VRivFaHShQB zzY?AmSxwx)4wDx>1?^(^*U~{nUEl?Kw*zzLepkX`heO_7_6{n)#eV0CKukC8LE@@rgl|&ou z1Z`K&-u!pnehOdVF%fKmv0Pq`My`DNQEgU|&`n>7zw-L6bD8VgiG29ofk=QHesXLv ze_ZZ6?)UG|yMAT=r!UJ0*gEr4QlGiRK-}av@AG~_G|vKzlx3dTNgiaTF$@n1AM7Qs zzDJE~zdqe`@$(hGvG;k8hBY1D3A5`;UQ=j?qhZ$%?qe6tRK-ra{kznD2NL=h9_qXL zs6+1q%V7ml(E8^})5 z)7scoIaYKDJSNT<7HnEd*J9MY^iXOW8U96?YW(y26U8{}kHHvDo3DdBydQ~ef5zn`=Swph@F z3cIKaU5h8n28*+&PUr4d_0`7C&W4Ten$>Jq7Zd z!lcP>iWW`LPbUeEq49{|J;G;goC0gad@0OV2=Ggxv=rkjj5?qCN$RIf#%mM$gK#-o zyA0ehiI^eiK))#K?(Fx}>oHu=xQH;$rGl#(%2BI;i`1|`igwo6ZF-PaSLJ}!{mPgq zb9%9sRv5gni|V2XGJ}}xNP|O+htxHtCcDDiE}$AK=U1BFr<1%oN@ZCmIZei+w7sS$ znQMSG`c&}AcmCbH zC8A4ih{z#!#xawSy?Y2flnMlyo`eGO(@?-n@V)oYz+}&oYQ6l6?H1xsl6Pm7KiQ+l ze9AvLKIZMT%O8l|`%YoZ|7}-pTY}!s-^JgASWm{s$8Cxz5{eKfP+pPJ1ezxC8Fv|f zPV)bj!;~R|x_?o7y%?gdw6hNUKrwzv+x680{?)FB5BTWr+_O*aeSWL#4xiawHnX-< z;@;=BitUu)-Eqi$TMski_5?V%8csy6-GgQ^nXmh~ToCX!|HxEh1NcDX2%nsnz4XILC!}wVUx&PJ2C#gMISdQ_y`}peaq5e%Yo~F=vI9F|dA z-xC7#keQ{Q{Ix=Wg%h)(gBJ6soZ{fDQ(mm4=GX!pkX@`CG|Jvg&xwvnXgo+18khXc zyl?ybg^n;Z>Y~GwC$KsO6_N@7D06yULGau-K+WE(KPM|CIZ>nl&?qO{yDkB_<_53M zyGsUSk#PTzlgjqmO@+1?o?{fs?VeVG=uIwas509(brw>76O*zv(uHw|4*ui&(ck^1 zalUWHkNIM!QUb126961(s+2H6ahg2))K2Zty;1=#;A6$9#{%%knQb1XkdJOG$}y=x z!?>dUpu8iqufZx>QfO+eVcM~RW>$^y#E^gR+)(Yb3x? z^Djna#!zq6OvqDzsjmva+QF#OMwyAp9#y`7q%|dnqgfp0N?_2lr%~M zbbwUFeqD|+y;a>89M4!q53J7p4zAtz@BY4DK?mK#`sDToLTk=Ux=4NE4O^J9NDEU3 zUvipXEadDSn!I^Mx6`Tdp6t3Vbba({vgg8n6+GbDah~nmv+u3@UffmIaqijm$OC^p zsLb8hcYohqaM$l$A9vq%@b}I``#!tx#jdOl`~9%ry0X~w@qV7$b?>gPd$QiTu`ZS12mld?2zc+)RAyF|udeQ@_P$v|R}}5ch+NWWNur*mCq3y|OAqoK&RSX{t`TRN zDldUbx!oPe?3DZ@&e@JAjwUL3 zCXot{H0D;bB^auT;L5_J>~}ny#0@3?;dHN%*$se&bi7EH8qo7wox>4mJy)}X_}Q8hpp2} zp+ew^#h?;&ZVI2@oP1yb>e^cn9On0|5VCURsYjb9NqD?EeoW#P$xS6sIIn|90wUx$ z8V88z?e27n+A$}R1$fA_MQo)tiE8Qh3{z>apXe_zRK}_irIL>;Y}QRybh$!W@l5ht zO~6DE_>o16XA#y$aNMnJx>vj63ftUPZv#97`1i%kVd<2NB5*0uYzg11U(r)Ghd}8# z+GJyBz|F(tm3#2PFk&8d%lZ0y3)~+Igk$w!pmXQrulrNmJ1@n98+UHIJ3iA*Y8dS_ zmqU$7xme29)A*pTvYNRYvLTm^Xe@ysUmIa02|WsEAeV?GG&&YwD-;m%8yf7kYQh@b z7$qDK;+d`JV6+l)s|n^p2a!l75P6(p@-4r(yH8qL*AHXs#}Q1x)jGN{X66b(k;)dsEya%*r? zT{5o0ZneZ4m&=Xz9IE8(Ks%gU5hiDXCAS5=Tyi&i@YPM6iK-%DP3Mk;og}s-#!(3* zOs);pa`n**_XT9eU2EMPCIg3dV6wn8bQeodm=5OUv^ub9z#`bP8BRpFfM^2GLAr%M zKvLuz=iLKCTwO( zD_*#`;QA9|u-FY-sh^jaOU?YY3q)jDP)I)_>)h(T+qL-eM%6zCXd+j+V}pcYM$4xJ zXtIQ`uRSv0Bo}O6$2U$^tD^xKrazu23g)L^>F99}$4T=Vk;F zF)u<53~nk9VBC}I;WPCX*lvOSp4Z!t^nY!)z&d^4JbF!OgxMpETQu&I>ngpYixML} zIEiygT=f1uaktme|K%<4(YC;c|Ci^%jr)CvYf{TZO>xD0I*9e%P;0p*016_CC@kWd zL$Y_f@wBoiKYj-FgpFNaE7-M>Ny2T;TOJljl=#epqW95dDB8!;_?lxe^z^pda9WNpLbc_+U?KP*lgL=>^E8>Qehhi^! zy#z9oNkW-MmK?aDqP7i>xbDI9imhc^IW=Wg!GjP{tbmJY!zxD=R827;G=;9h&I+NW z5UAR6=n|K69j4A208<{YBFX)nL{u{l$fHSpD6c~EUAWsMi~vnSqf7sO|QjkbZfn$S>oD0?<>%b(E-DBfq<8 z+``;Me)|coZtl0hg{|DB`UdqPM+aC)$As{09GA+V){e`U1YG+&U~d2f#aslSgSb2R z%?J<#NJ%u*6MAnx01Ua7(%tAkrl=2c|0)2gF4`P)`$YtW2m5=iyCcps2C-0pi@-I> zy(5@QUQNxgPvTs+5@E;mtW)FxjL&mWO86a#8XE$@=xyei2=k)5sn)syFuHIhNq`cG zp(3}lO7KDehWnj|7tnnMcGGid6X4vun)m-p6(QHE1XL*6h+|&x=>QDrUm)PgpnnUv z(05+ib~u0O_uqiNL~j9afrrup61!d@@?90r{2-}+((_pl=Rm(?+xY0AY^ATXw}7|6 zVYPrpQa;^B3uxj}$Hc9fu&M1*jpkuLO6XUGQOvsm|L&6%QTG&+cp3)AN+#{%SSzAL z9`VtdD3o<|8s(%i6wM?D0HUx|67P$!imY%-1;wb|y@>GiTYPG=7_t7rUKb@~P2pat zyZq?aaJO)Y{38tPyYRVeZelXYU8ZOkIaJ5RR5%cA02QRkDNDrlO!O2^vmu&h(v){A zvk^1{+5nGw5gbi45Q#!FS*5#bdyqwtkFE#W2mn&^OlXs_q_q5&JD=@@_KU*4_qCf_4cY!7|%a<7UDHckpT@6s)eB zCT@oc=Gc>|DXm+5~DmoL2kdv2yD%WNe zALkSydlmLKoU1k64He!v9jWz(l+6RD-phjd9=0gxgz{$79r zawl3LYQ;R@QD-yZc$xglWkAe>MO46ZfFB!7^y<-8i6I)R>)yoVe`W$ugzKo-;YDcw z3rr$AThJ!KX6@Ka;Lj|=t4yb@v%3QjED1nVjzr-a^57?R+b$iGZcS{L76ItmxF29F z2;k7YP+gbeJ~eB?g{T8}hh{NQIDVNnXK`7QD}~&QN&s<7MBV5B42xpFMgapbj$#Wb zTc_>Ktp2wF@+czCuGbPIq6E06kbTlKNyci(quKg<0QV%)%PrH8NLp`u(z&ImZoB5Q ztzin2w`w_nCb4rGagpfgXv1Zu-kQX&Ap?aP6YGkfuX9IH-19l^2ZimGkZz+GRsG!n znrT-zT{(&F)^V>iCU6149?SSA7sWmT+|a)mLY^HhNyl1fX)cG=rA!l==MsWM?xcX#u)xBE!t3HSOt zEv<*KBCU%=R{&y&eW8~`JtHW*I=~`y9Vvqsj)r}&>*n+K7VsAE7C7VYOl~Low^!00MOBe-XO(8auG7#| zfR`p(yqriMwEQ6HQ$m0u*E4D0`2F%PpLAOva+`3J^SZbtaFfZg3co_65P=t>wyTYC zCSU><3arSdS(#o!e?Xx|T`GSB&DG3IulJb*09*lL z2rn|f4I-pazG(ZJBq{d#!6#JJVXQ0=v0|aUkx6-@wE(ijaD{1-z$p_}tzN`nYzW*C z$We!@!}5%Pir~7HSe0L70-x+#fD#4B4&2B&9y^fuVSmz5JkpYb8U_u?E4!wQd)`ZIC zDsB}Cz>TD1@#yCg-hlfwnoJeKZUr`im^ATdf)K||fFyBimo5rf$VH=AW;c}hO)A>P z9L$|X^loY*09G&_x?vAVd{e)3r5c_AaRhGTu#IW~>DKb-S-9(}y|6L>hniqKbiwM# z!#>Hvh{;H)lVha4(hTqs$9<;+J4@o6VhK4=aTmKN#}AqWSQL21s4n62LUyl@tXpS2 zAVy!zp6_Z0eCYtxt49N)4kiZoH;fB@f)(x~w1wYY0Jw(NjeD8Aob`D-3;>4MFBziE zB>PTTO7rL@C*TrN(P1>w13cB%OCG8;4SdLETG<2N%|M}Gu_-@-_O#0 zS>6KP0v|mKcyQxBUUN~~6Rg6w!rEw&n*pvO)A*o^`mKpkIFZ=5N)Di4R zEiTzCB7?KDDW?_S&LLeWy;UcKaqT8^6+fEx`Qi)Y|tx5+aZFRWH)76D?)(})! zqh2Mq6t33po#ZOCvBt`Nx@hMw;0jb2Ku4t!HyKG3Di+0;%GF)SkdppWkKf_nbc;}c_y2%9{`b@TTi)eF5kO)BU7qZVsQr;83AQ4fe5;$vO@`wVU>G4L%Gc` z8(d@_mvK-94cLH4(1}>G5pI`6EaF@~l1V_7B-dB%A&;5a8Qe=?Mc^7nO=)MqB--sd zyX(s&Um=<=TpA&dUdOPB0NTU>Ty!6m&uUoYF^RG({5D$ zHoO1V!k74PTEK%F_wl-Iu<9qbgD`yQ%JKqw6)DFw2&NXdTN!m1LDB?F)c6(jEUsK; zk|QvHRciGcTSw{G?SBPl-^shU6B{;zU;fhkLV5CqXdo-m3Q_5tCn&0j=0X~G8@XJp z;2NVSFyS;(%#J()lrz)Qme0&Pa3iHzYjC!!lmsLTtXPjT5nh;{XJwm-Agkw!fC2;F zB~5T=2(_7Ka@U9(Kr+sRn!!gEGno`+XK)S4qDfN4L{u&UYt$)=OV1jSWkPdQk)I~c zsrf~F|4iPkpr_E$SaI|I+gRaXGOIB|1|5wH7cQe$K*&qjBMnTh>m@eAl~{#c;=*-O zmn`g+9Ii-CIJR5~MH-ozMoWdL7ZT%?c&IcN1R^ZJaF8&UtP{nJN#H{oA;(~t64>ER z-{n4}h%eW!-mnQoHU(}hQCHb`fyfsHfQS(~*CO@K<31w8FU-xm?Ke0)4WmI3P6C`Q zaGRQ)o2IQfSP}|fN(f%q7G&pKspLK((bp8`$<1T)B9t2-Fl7o&nWg0v*D~f@iX_xp z!sR7{jzya5%$y%K2V%Ayn}rdyhK9`MwPeJmYn;N(u)85Q(;?mX~b~ z?TcDOc@=qt)^m^2hA@NO4C$_~BLB?Wxw9)aN}Ckz#T5$3Ah2f{Et?``Y-Yp7NiAY0 zK#?f~f13aWnh5#q?g#EoUBeu87b7Fh{gB5MB#4rvEC59i>ns3XOpF62@Vjc{eyC+& ztt5a{x)w|Pu9jdf6*mWos=(eU+^dK+ory>w;EC%Wlq6C#&r8yA$@8 ze>Y*XENlStR<1Q2D#J+|Nm1BZT4UDNg9{za$#&m@)%J^@kJ>l>aoRHE>ohp-BL91KD%V_niuz4`GAO!l*O+bzjj@=0?Q6sK zsV5V*N`L?QYm@fVSLbaSqq*UStCN587Vs9>%K{$U*vm2pzFh<8M(f1Qpot061{3os zLbEP07}vuroP69-v79fOeF3)+0SIyxxYtSGMG=;WR<)Oa;}s%PK!IzAs}Jl4Cg;+N z43hS2wX!6}11qU3S=Ci1=>OJ!9b00uon``;MtoDQ59%nlAHymDx&Vu?LsIGGpbl=P z&5Amc7cmRgWn5!IUEc1_*QSc3Np*oi!1OHCf5AeqLUtTdarDlw053xH43YmvoOX0RQQ0^p*+%O%>kokYS_!tQC93ppm|bD#a>0RKVG-@E@cgTfX5SlF*W3=H``b%HzGCYNC7DVCI*Jo{H4(+s{khZze zC3`EPJ}m6P?r{Zt_TB;yxdlA9albC0o1EZEkxC=NiFhcJR5Y7 zkwSXX8NfU!Zirj3BQb;68)(tQG@LqV(y_}!Tk}TuFUWV-W7y^Dz^ZJ(XDHX9Y;WbN zF4T3h;jZ%Mwhzh*u==eD(6;@z`G+RKs~fN%SVi7Uu4(EO!#0MWn@6}Wt{nh6+eS;9 zlUlVQ$a60{Y|riX|5~pAO2V3{6AjDTc6B&*b52cl!sE9}3nu;oom>jd;0_FIaPv7^ zXNMV6m8_D--kOXs82bYZ6AUCsp~H1YGNLPebSGv$Y5)&^|s(P0)Ww_@To8` zIueA_6@bA=%z+r1ygLSlXu&&bOH8+xH3<4 zmMpdes957dH~@q1t04=cJXj7}xCvmeK>)^Hsq)#d*D-#(w}7|6N7w?e^b~^J2_fTR zq36loPn5%BrwEhe?w*7_`%JqXA4%{8_X;LOm5 zH8J`$H?TXL_A7VoN!*LS^pyeYgUEOF!kk^dxM0_25XCMpP}~zcg_*iK1g@SU)asto zLfOM`zlBZkpWXrw+5#TjxL;R*j&TiU#l*y{&CoH5xsgR+5YoUyoq{f&$F36{*x8i* zAIDq6gIgV^eD^-a?QtD+xo2|K*gF28%Guev-TvOaE;@3nKD(W_dBjZvjh*N1{Clt5 zZh!lheYVcozOAy%?R&P)*!mV6QC{MxJoPqg5a!}e+h&Vx8FARozr!kCM=N1zz;G)LWl(9YOyB5Tg?i zqfG!t8N3ktBGA4l3wTye^Hk-gwNsPMeS<7?rbS&Fmwm3#qhFPfOSG1I4{!)T(4<& zkTugiTZ_63+6i?zv~pn2%&ml8KzjuS3z^&WUbSlv7jLGhD$#;;ECMCnzpoID<5<_8 zVb(gj=V(5+pe%LaG=6=maPIlI`fRLfb3bcBU9KC7GYM*$N@O5^@Kcq&xZ>ev&IGLX z&D+>HYvVra0$Jmq?4?76X~c0s#$4FhxQ@wv`)OfXUHh7F0@)OvCW@V}Q1=BjUv3rH zSjX0Ijg2^^&}&VSSzXEC2l;KjFsX-9P^-_z`k=rDC|5^OkT978O7cB-A6N#udqYiK zofg6Iv`hTa+Y_J|W7J{d6)dX-ek4T(1zLg8pLW2qqMYSCJbNH|AZ{=qew#)R{5G71 zxla)vQB2(^^H5Om2%bTj|By^L``NnKUy}vtoU+ljj7jJn@8o>*BOf`UK@)|`LnYG)Q5sV&qN<^!2% z2_LK37XwKt`dS`%bEY<&oJ7OvX-|cwhMY5NG}>2E8;mbzF$zNf_Vs>W7BoLt*%b-n zn<5l&w!LtK&t4OIrmbiQ!%7N1$G4_0*^@v{o~FlU){}Mtt5TaG%@NH9H?U$Z3|t1B z!%S6)2Unrs6oSULpd-`!%bB9Ibv-f6q)T0is!7fX;0|1J9;(z(Bfl@t%ZW z%09|5KLhrcSU-76w>>BQLtrTR(A@ruKIpMc4DgVUI|aC4h0AQL5a`oW5=22INC9Ka z6c8-d@FnH&KBPO^v%JIvW>4f=>hlp-;8dFI4$vA0I8K=93fS2&Ls017sGo$`Q6s<; z5@|M~Ia!LB_9O(d5oN#twDB_veWWa4teohj19rqpn7(!Yef8XJ!2>ZiyO5WbzXTW6 znNv`VYsfD&(3rwn7LT|Qqn_e4=jE~Z(IIt*8_opX8v9vx9sN?HzLDbt7$fjr+1jV&J_C9+|k?q&G7>7*u6JqhhlOd0MCtU$0}Yk=_+ zCXKGGptdt07XOEOW7Q~jW}saw)!281i)b{-+9ryq*nRE+!I{V{b$HFJFV*vlHYXMn zy9SX`3rn8W-#(5$OMznm&dtscIQ~7T9wGdp#l2B@?BBt;(FWXWPT*SUK9Dbb6@gQ4 zi+}jmsfvUoRtnsano}a%`BYP25~5o7wBRzo%t}eAp!Cj=Dp82WRuU^YOWvngC~-?m z1km9Msb5imL+%e5gHyvOC*bAtffb7V5&P^eek&0h-3N)fg8?o|{BHv?&Z8gyLoT-d zf_@-G1P|FnMWJqelo)>LPYu5uoCfN+b^#nZc}zA#y!`L*K7FB244j_m-v>6-#=37) zk1uMvg&jWIv%dQuO&=M^1iWkJAA{Gq^|(2f+$$}rZ8a7vQYXHTp&w0{Q zQ&8;$3Xrk1ZY9D?4n6OO3jC{>bqtzE7tZZd-FH4EO~2ojkg@p;X2-Xlo+I97bM@-$ zJiq_cAA>M~{Vj*mqFr107Ikwg>3z4#Z4)S}9Ll|VVY=q`gnIC3VWQDHDY>iq zs?faFpxC%7Yj_E%6{SO1EFIec0G}sE<>hqtUG09otN>l69y6*Zcq8u~@)KM(7=Ec2 zep)1sR{7Glw*da56!rXzeY5bBrXoLeI9^gaaM_Q|c>8H8?d|t8!ixAGsONjC9NXGy z-?nX`d$DjqKY_>q_n$UFW+RrN&5H3%>!J59pvj&#a|bUnyE|Nfw^__l?37oF|Vqo_=pe_7#j32AmZuvMBYY8sl2+X#u3vi%D+{XAt2@ zc4M3wP$Km`#(9hc6{{ESJP+S24{y4^HBALh9o7I#4{nj6!BaOOpahb2>>`>y4L!=^ z?62bvRd&LRIdN}-na|zlp?a#-3OH?pw9^D9!+N%@DH4pbUz}00VLG95!@%i6<_;;S z*Y5_4P!5?Kj`Cxv49VZG0_DEFu<^Tpj)jXyOYzAbWtNLzE{Ohnb)8+0E4*ptwrIm0 zXFm9ZWy?!_`xXrUx>E9z_Au9zx49(oLGdR#&m`XweBV-nvxVkjmI9f3MLqmg7A0)t zN0$qBF{~WyqZ#CZiz%(qwIT+S`~yPv7-nZmH#42nSxza}ooB}7eG;8F^i5G@1e;xB zSr3PO?)yVttbu-`@bePq<6a&Dj5K;rQdwEnX6bg`J3)%A)GK6der$KcmlnKmp3>eZ zHAsXxxskQ0W_($u16v<*n?kjVk-8#5=kiIIFe96oU$#D-YhG|R>#N(QxgOoi3O){V zTbWK8ElZ%!b&xcdSjR=?^FWZw6wZKUzOa4am2rgt$ThV|1vND3V=4Z)mAEys63fbD zX)0G&0^s($z%rSTBxJS!a0D~k#+F<>%_)t5Rb1k0dS{(+bXk-9rTYy^v{SbK^Dh%n zCJU$!EK&!17J|lUxUJvqb0_uf2)q^z4Pf$t=5rBG^UW~s#Ta8RhTD3|SgH!?4;=XK ztd!8DPY3_1)$6}%b-6x9a{7-SCaV|Wr9-timVd%VZCqLp(EEn>_bUiGm_O2^x!zf0 z8bOYB>s0NY2$DjHYnwBg;9dg^FTB@R!erBoe0qX$UkD~>0KLux2O#2H)14#8;} ztQW2zf%gbHnC_W6)s>wNiV4avWV`Eq@JRiS;lFEB=qJvO)o_t7GN?C{LlxxkZaIiy zDa6=|pC-;?lI@%MHWCMt{fmb|aQxeJyn3aj+jF%06h-J+bc?j=X=X_MizPwM06 zK7yq9q05V>s9#Pk-)27DNd>>VZ*GAV6*>(qe$KzUAG_fB-F=~}qVV}|Elb{_w*}1= zh2}&K(5)mQnCjJA&z(z`ZyXCB_aWx>9Jo1h8lqc!CYY91)Hmq(@b~2&_JLw@KU#3J ztEJL{@dy<-li%g@h6k6WF1Fte;jY4LQY_>4@#oBY9&MyAltaGf`WDZB4s<@UG)7&>V z;xugky_CMF$o-~c?mYYcpg6s*)x6eyx0m31sH%^`iBBB2n!ZYSSgnh|lYwY)ba8$^ z4&u(;$As2(@}hxrZQ)u`RdipI?GdnzN8(;R7FxEd$ zn`~>kxy}6l1j`WQ8Z^O~L=WelLXO}&R8IG4#;TMCjXA0n%@E>2zXGZt zKu^sEP$4AaD~}c9&0wt@qC&r7Lzq|&FE*NfP9BxpIa52(osbhfHsLr7$wPM}gW(t3 zGwNFNnO)C0Orr7W<#ovwb=QZTw|qAg^r;*qK6PlRt`6bqD~(`0fLD(0m_93KVm`8j zZ6~s;sP0+VF-kt?;cqZrmcEx#4TkbK&=EVbRu@2TD#<+)L8UaW_!6ROnA_;!Z?Un} zCT5B!0qARKwO3yeFrOCQw9fV7G*<)%xcwEJ#ccbf)^nqB<`SFMmg;2K284Gf0FJ%Q zxApvt{zLK)?7r%f{w=%m|GDjL$f)!j(7^)zA||weGxJu21O5SR}~ zNfCf+f^lQI=z!P@xABdWR!1_U>uyyA9$=aL?ZNzid}}8(s2PlyGyt-qui>ZkQPpox z7{(SZ^OhtyM}-MGPWnXtw1^tCHxu|HJ^Up-byaM1E;ybu;@^Q9VH*r*<;&>RdUXuT zE-4U5PUu>m09m|y{GfubU?DJCUwDMOPNxf+B7%k(qwFOjBk$(=5dyx?N}i@=>5z#b z+A}z#TdDVG*-*%i=w6L{n-yb!V8g98Lk~~UqVoiYFa?`A9#IUGz?zltt)7xhb=B2h zs4Cr7?s6!J-erY_s6=!vnUcI{j;n%VwtNF%{~1-+lUd5s_*S$gNoWM4T9v?9eSkcU zjzXO&y3o+Pgs*G7^!Nhn>i3|-r$BYORj_i^L zqUTYNbp?)MqiXXq8$U`#Y2e4%=Ut>^1&(U3rn3+3n@V7KC@wqF?OiOG_wr;+;)i(A zS%Ot;@Gu9Eis0^nL@4*KKc~gLiK3xhWu)jdcS=S7Q(ew>p0HTN*-qz}V5N3jr{2Ox+R#_}^fCu++UaDWb#q<>gcG zvewQ-qTe{dlM0fBW6A%+I|-bC;LIrxIA{#Ub`;i=Ov?Oj0!DSY6WT_;Hhg*c z7Vt2VInGOE{t-Tb$ ze=H{)vPqO#uV-aNWHeo1jawDo^nC+OZ(l#+eCnWpb1TrU?RLJyB42)9j#u%pfk!j+ zY`=A8JFDbMg;}P_Tm#vGMs~X57DS5``0}55BNF~aqn^7aW)q{K6!h_o-?(l+upw}Q ziC2gYuMVQt+tT}b^L{Sp4fI>N04}|9W%ZIqZ71s{>OCSKY1PQG$!X99SddTj{ z68`#O_5zns0^qC%V)d=og}fv(EG!EEN@{)(wX=vGTSxJ|B>$T4 zPe7~1NP0lt%W7XCg;Ba7Ech|L1;>*souex_xMVNCV^iP;+*7NdLOWO?O~UEVdVg`q zZm#JJx%uUV+e~GIkCwQ^t@VZ{v}SB~%a-|VTTkZDy~33se1ksv@-=z)5=&2LoCsxQ zZebO`@-~G2x?;iv^r|HW%8tF?h8KdCyiXi=mJ0pa(o4BmeC&G-Uc^!f;GlgkKzr9G zEcD8g9@){xg}Bt0(!#k-JX21f?%H;W? zTLNqg{qOi`f8*La-iSa8JZ4g2f01LVFXt*mD{p_n_}A?~Ue)!^9M~ zrwDux#l1mdJlXF0PkHQw7DFtxp(~(C{4GwIJny5HuY3-GUeH1|{^?3#pP~odYbc9% z$NN^|Qs(#t%oGkSfG>em?pd`2Uw@l^DdHuceg#Tq1^O(vskaV1!C(NonAk3ADo4uO zwseYmOB`$A@G+sQY?Bd#`%lwO*qmkX=3_H!GI{^#E9iJTw}Bi9jS0V$<%)=LEwZHj z4@~TW>zBw>g7K4z?u%f=(kF7zL_Tr>B=)AMvw)dHPX`>obTyUj7L`**M^48@dxd;c zlV$9Ks}@q7I#a~RFS-UwZjZId)h+d-chgTl>%b@D&x?gSCV13rUB5qFMf9H8(hFM~ z&=C*d^9fM3|1(qj8z-}XAUnEmN#}vZ@iS=<2FYe!1&Kb*`#BoU$2u=?7c7Py(ZuB; zPpr&_Dqt7Jxz_Gx?N*UG;v^}M&*^M#MjWL#avcwW6K!+h1mnI|hhSOC-ebiIo3iRtz78IK$>Xk#%lmoR}tC(GIqOtiXh{YvRr|Z~|+i zo_1?h|JGU0Mdm2(g?z|;r!DTkvAAprsjpmNuHyavMh7J`4|-qhTrZ(mY~iLf0c{NP zU6u{c6y;3|iJB&pKo{g3T+X#g>XP=$Ex^Y?tiyqc z4!Gp;*mDuGD3NpOo0)8aYEfaoMeVF(iVn--(*oyXA}e5`rR=FOtT*whTZWcen|@1S zr*T4>3`8b(K&X)-VQfV0s4Efa_l*6xzT?Nnl^whbMzZ3ia)(uh zYyoJL#W^!Y+@n8kddFu~jXU8Lim2dB+IG~)u%+C2knas5edzRzZ&potU7_O zgwQvO5qQ4TvySDs@~zukHzX_rflbR?@+_Sms8KL>V~m^1nVuQ0RGhE|Z4dN?0B8OM zJo$J1!mCzlcc5__{*|fkv`iK4RN|@t$$1)Uk_d}>c&)2 zu|LPVeGr3f1wV#HFIVGA*Cu@e1({1nm)-c6*cNk6cEj?PcJ1=iA*0HY`%D_?){k1+ zJgE<*xfe%pB4T^7W0R$uj7}OEH`hnC@UmMirBg^ISu)k%J~jYhp1Ktk-B@p){c`+^ zx%t$_Va1l!+d$soP+2-2O;Pm?+G;*lnf+Q1!sS)whjH?+sa-?v%w z=xrieR!!oCO%!wRVE~a@_Eu{uJX@x6z?SFN`z~l=;(s={-8PMN>+(n^2&X~npZNs% z`CY~dohOC~q0RJ^#I1tb#&2n!EB*n1e+#D4*miP3Qd!AtbpaQ< zk+u~Ns7Oa)Loa;4z|~4Dn9s;%(ZM083Z8LrNi8i635bC@jG`}JOjW_9Z5bY4d5O*&PVJ28VE(7%c9G5?-{1;AlLK6cKOEqx|NrzW<)_zHf4TDyc z#rnH%PK9(C)wJgqv#`0zfiHCm-dA5B;@gy5S;iGD8g zm*1={9_pkAl|!L)@YO$5$x>Tfj&W>Vi`V1+EUcEz6QztdXu&wLg(>lB)OrmCFol#{ zLeYK|S^%6T(sej;U4ravCiLI(CE3V_B+tJTN@uUndOf?u<7}&2{kxZbTYt7mgi8z! zid_SY5ggA%gqDp)N|vF>(G*e{P=SUN-5>#@;o{=@IBIt{%J6cah^CA7-A#3I(iiG? zpua`-VcN|k%Ryx`o6#_Q=HjsR-iYB~oZH+R?P;<>_=5@q)WMA|N#&|4U^Ta)3; z%MrjvJAdTuR5QKT-LFAx0)p|C=V;jh3)oUH^@PS0oyNX4H&)cWr`jC8RnuV@f;x+k z8#6h3>=YMj8(bEq)kA`>V1$YA?lNOhhAP*Ng+$`{-oBwS_j*&+)0L|`byEDk^@BR4 zx`MKZRu{g9V?l3wZ&U#ke8Mqh0?^f*Ej%JThl`jLUMN7*TIHe!1Ind0tY)8&iy!jf zF%kNAq;t_FnHQm2wd|8GV|Ca@%M^m4%Y!{zo#vZ%rd{);Sn~lBEV-E6J0vl*qDkA1&Hi@ z5N*Eb(oJt-BD%B|r*%G)lP8fVn|MS9f{H*@S{#0!Df)EI2(?BGKMRO%LbHm%?ORGN(HJM zzv9}ktFSGk?EOZEhLuc!wC~f4a3p~-w7oQfB!I-WVtOcUexKilob+i#}=WYnQ7F6>=mlsS#KUq9{@d9-uFpO7$b zJ)|-6$dAa?N+HB*aF01a!Poi?J%2ksnNCU4#7lZfh{&#i8^2qIu7kwdevIq`vC$XU zA95o$o7UZN^N^2co_qnCB^Tks*-f(hp2siF2@{aeYSDYbFDsaKAV#4gLyzjs7cH)U z&=WkAT}pwLdTWQj^Xjj+(-s$p?kT55jsBZwGoc=PrzqaLnEIgS_@$+EU*TVni* z%XG6#Yyqd0IqTg#v*kz>Zp&I(Z1CqbjWpd*9Eu(=3|+y1$XC{0k69vEy}{(56jQj3H= zXjVH&SWSV{U5qW}7TNbjWEQV?5iTHkpp$StXjVOOO3yY%3DSh+4N?F1$WdtLa@Fm# z_kX%<4@938CNw$nNI%Ke#Y!>nXn7I;MyA_iKFD%`{dy!sVdLrc-E@Aqy0i43ZzVmg zz2wTH-49{}_SDM!2#$4e=KYM(lEOZ|qjCO~!(r>33Ns#|?>SCJL1;zUtkiQM{yihc zQwI~}Y*-=K~RUA|IW2azVOd& zw9|{z-FW5iV$-aH;{7x;Y#b*X>boes-g<(4rc-^hTDyYg6Wa=BRm3l^nGX6bbp%x! z_v3HC0%3EO&YAE@-f&T!D9O%o+z5$Q{JMDIS?jNNjETEIUdgLQt`sxTQ->Z;c}Nj{ zPfq*c7lO+0!2x2N-`u4l@_f)X_=V+wGs+7~XzZ1q=OHRQ4hGP(qU+kJ^d5J}>($Mz zhK10oZ|oxDs=W`VnFVtVl$~i!ecQPNK}BoaEpsDxK}?`JU31j|JJ7`E$1A!x;Wtrt zebwB+PmJ9X0LydL7U`f2hU(EKjOBTAV1$5mIsp{2cRwOs%k`m(M+RdoGJ=aavbA@b zWM??X$q(j7(apmQd4f{b(uvSW9PM`4_BR0^f{1tGgDG(oz2hh{k`A?Ljrbjn0S(3cn543;=dQ&`u= zgXdWIRi&CuK)*2!(!tyn?8vmfH2ipukWcVOenEmws>R0YWbwaD^L=i}1|3ky*cAWP zE}j>1p)!+A2g|>P(Bp;o-W#~Z=?HAq*z~6Jl5@*h1l>*}twtMCVqKfWu9UBt532w{ z&M+@D_SCJb0)FgP@mZfH{^yRxyxVrNhu#Cc$*@noHa({vUEI zL^RRb_#AV%8uP*m2Ku!hZ~l^ofq`3;l}Jo{c1pRm4z8XR!3Ry8Np-0`?u$}Win>G< z%=<}S?^ssTmuYNwhdS%b2gKxXmcZSDsFerlCt#v2ze?~K`g$&yS5K{40~f2=w-{7{ z)~cKv0igKpZd2T?)~oj|y`r7y^AfuC#c-a-X~_*K3qbJ{W6k>K?|0g{<)If=zA$na zY-;J&UHcwKWq)cCDh=AYL6V){^$nkA)AV5@BGqeFn+O=<((C&q{4VY)nK0-M*A6AU zUq*2V02jm7xsxtek2Z35PJ`N^*LUu$>>5sVZG0M}!WMD8S6->UrROJ2Utb$)EF95M zhDPVGRR`5yFEH9^1LPD~g6-DIn@!E-B}Ud}=X8e`1Ymj<`qQqqSCa!oB8t8K zCGuVu{TN~P^bJK7s7>+DnuXAo=t%|6>jI6=WwtDxtRjw)M*>m!sc7k72p@HyO=X0C zD#}6TcJ2^tgQ?vnn>Q4~R?pLAA&A!U(L!kP5bd!ocWPBd=p=8!NJiQuSrhV#MAbFy z<6YRGzGceHmMnG&D(Thm{n6x8Pdus1=`;8FnDBdlfvCod27`4SupZH;M#c$KY+lzC zUN)N+$jB??=(qu;y)Cv#U+5dh9G{dnnUdk=%zu`)+oK=ywR_VVDx*KLCqaXsa_6vP zOMs`P=Cu6!^2c6Am#0wddXS#~*=15LN&WWgqR_eUnmLKZUbbkc_uaJxqx^8kXbN46 zI1+dpG}TPuH;9voPv-^C$9vctRM?P83R1pUGlWW(Un6|43A8(WgO^CLqOTR$Y%PxL z5`=YU3qaU6nfJ7YY2|{w@~20Y;GI^7;HzHh+g%CY#q%`ETBb$HEL*y}Jqvpw-y&;U z4U+5D%P6ugae8PZBwTO#j5gmb2CQaveaP{~6iw>-7J=eAF3F(|ISKIwbc>McaT~h) zvp7V~JQy|yr(cGG9|V|*EgkABgcM9A5;(T8K?pPDCpn~eh|TSmgfR|tEksW7`cK#Y z4;6~Xs1#()%lE@PRA~Q&kqwGIX-LruF9Mf?0SGP#qs%8ub!@qdl9ZBcU^PVG@VhdW zb?8UP3jUjkn{SukM;nTbL$%pfoZoWR!=jdek#la2VI5DJM@6n0Nx8EYC+f`*k^DG+jUI}FeN94h5};3=>Yp>I>EKVF4RIlyfra@;8HgHC z395;%NLQckG9BPLQ3*aFoY8^&&gg*8p$!c{ZA{4-liB+P_d7J5-aB70=T=fPrVxtZF!;gT9%(=Xz5BnH4eo1LNwjQ;b4uR|KW3@w2$Qd zoSR6{!7bK>O%X+|Hs-#S(@4_8XL39vNOuBJXY*sq8GOtH{MB>`Tom9Wfa2SL3}$}x zl9lc0FyseXtJMGLfp>g@@8sF$ya-x=Ph*9QaLR9RM&6!-*9(oZf52#T88>ajCN+*! z$IWKh9v7%2h7)r)13A1I&q~TXZ@>>sXbmdOe!mKC*}?QzAIp$h0Gg+xm80~Ed_Xug z5Oh-R&~+gqcM=AOZXO(AMR~z|RZ(0nV(%PwM+JnW-2TtNyaf@y2Jz5@Kgb3R^u3xQ z)^XGPXbZE6qF&zvgvqSipi}#weZesxwyWh}jFX!@W{QlYOjP%}pnvGCCtwGeZgUMVRnS3_TxEL^5_0pZ}ii8JCCtzcf z+YSA?83A~W6MRQ-LC>po$*S+;MN#3P6Nuttun8cxs}8~CbX4kCUTv9pGO2($tgX0M z=8on032H(2JO!&WVND1~sA^PCt+IQ;$to1*ieuSX+P5Cvu4YW2J|uT~1H?4EU35HG zL?w?e#o2p5B#K7wrl?FXtsTl`hjyb0NQH;5X=DtCR7SxrW8%^!Uiuq84-ZPx2VJEWdckQh!d7J(ilhlD z3qJ@&{)?>MGUqS z*WsM=)kXdNwH~x~JnJp?j?+~qRu3ZgLbwWdKdE4ZT~yyLslzhW<)+l)wFX^K?a+-Q zgrk_zbi@V3XB_$1 zFM6cqT6}0yJ%Rq`BTZCEwC9+^KX86u77?`kfC(y_Z@M-R^wMb(L}YqQ)+_<{u4Q1r zP!JA2!kKw*^$T?=Q`ILtyhwo|xRPAVFx=w|(%P?-+OaaVJIOuqDsP}6Miy$d$_&3% z!nX}LieONdt+EmY^Ep3~i0}Ensrh?^m9M|8cEjfcc9k5k=l`;P4$2J4_ukdw$e*>g zMbSAc0z+ECEkvr7(z5!FPDc&JyiwANC`Dmx){0hpC>A``CGf46d-v^C^z0!1zZTE` zWXNrCW=@WlC_B%lR4M!jNOhd z8h9TGCHM1iu3bYRE5jPLb%lZ&|Lp5To1|rsUZ_~J*Z}uJ@J79EL7ovgX$m5Nw_R4G zu*e9XAff?N<->1cqetJrQZUM87-t(HyprGQL;o)7Lvg9=t2^{(Vx+#&cgJY+X~`=Q z=>~|Y%n(^9oh#H(&K8m);dDo5pzVLto?WWtCOJ^`5EMZwz!u+BZWJ!}Ou!61*b9n{ z!6lSe0h2YG3m}s?&rSsQUIW+3Ma67^9Q6VZ^{R3vsm7l+~*nx znuz-M!OZ(=Kb_sra3LEj7%Le2%7I*^mpTOAivgzX4D@fY)ShU12ATFY-E+BK~{HX7@lK?j>%~|D$6(&;;#HtN0?WTDeMW^UB~?p zPIb6dx8B&GwcZoa4zq?y36q8=C7i4r_zT>z7zoY?x$n%Kt`8bE1I6g~X7Ga-0`#Z( z@;vu0&AW^WxF=YUY3RCAY7th(!{#3M>#QVbkr=k9PE})ixFr}zDU?d#5|DUc1WME&0WJ1>#6Vfim zIPdX>cY}mqX1&X@lcCw5^|6-G2gctm)KqBBnItJg_{$FWi|xO&W=@iTtG#zdo;z#d z$yY~xhwY5@x`3W@hvDzx2Wc3M?@}A$VZ>G~+kRFnIpPu=p&k7!)y!+5<8&SDV+X{f zq91Wtwu2T8<2iykfF_?m+O-2zgPo;o86_}Gi+b6k&4|xsz5@JQi8u}Nm@An-8krgw zMA|ZC44bs^0t_ev855l=9|5vA%u7KE?%F@yc}0dES2|Ko;#4-1A9lYV%Ss||biZxs z!EvD|mDYpRuKr>)E6u)8aT^eri8oKB;E7hrJS;3L1cf^3<>04*Nq{rI{gqz6c#F2m z6yp-0b>tY|i9YN9s;xz>YFTBf>8RZX`>j7rV!%~i5}cl9un;wQLqG9qEyVk+9TAgg3`BJW&C9<*gW6b8yMC3Sc(J1zGh13ZQa1BXwa1R1)aLdSv9er;|JoDn`B;q ztZU<Iy4+@q9;#q3&}7DQLXK#F@pF+_U2p6eKc&;1CC{jZDMM`oME|c zn6S_dw~z%|l}Y;C^UTUvCNMy%&Q(`r0aU=ZH@(Vq{1hAWVz^)4z`m(?Bg?hj@&;KH zDh0|B6ms42>ptp3ElisLbnz$l;eI2?%gHL!tUV7f2qN2bA)f#72>;;{>Y?%aMYi(d zSymAkW#Fg7UDPB`X5ggXTt+e;fcH2F&;wc3=&|cG4uSElpWe?~v`sK)nMjPiGI+9y zuor8Fq6W~~XN>PGfn&5Vfwloln)rJS0V zlfp&Ns(2&B;0eWuMVa|Je{mVH2yB~eo`jV)uW@;sjH;aQn~JJ^xBtl3J%r%*=K{i3X-;x^q~BNb5XhNWd_D7Dxqdd!>U?hg&x_`SOQFB zNfhhASp~4*bdF)K3=YIqhScwK`$bEJl@Nn(U^Dk2axC7T$rDUak2Bo<(b3>!uYcwz z-Md11ztqgkFC<{frswD?x*QEHhVmIS%amr8)SS{b?}_;<^#A|U`5OaFTTIDs1mMh~>=c_iiy(#P zobA!7?~D60coEA%$0>k@i0wGxB;N&pel=joDb=MFs3ljW75Dai_d$!95Cio0;`rtA z05H)OU>UmivX1V3U!|RyW&|BB~l(`SLgntw)a#gR~d*_AvzH6RZu7J zQ6}`dGAbo2#bfJ4W^v5IxT->r2COQbg?*G8ds zzg)^%_>Ro@rLAP-5QZSvqw%-jwR=2f4v&!_(?EM4iryWhJOmRy2GeIWZMb6l<>ke; zwq-a2qqU5;E*6q$%HqGT#y^(i#!)LkXc<|SxFE414DLK8lv5Pd;H4V64zMn68bVcnkh`x9GvY7j=dKHWS<~E zrR&!cdax8i+Z2*Fz=8rv^g4|q&E;x5>PoL;rte;;WXI>J;A)QA=U&**a}Z}Wf@wd$ z$TU=3-7!YE1IO2a8wm?t@+BwM6xw`DVD zfYxdsSF$17+S)JbyM;ltLysS!3q& zk~lL_3*O^hr}lS$j=c~Gf&ciQTEqOPm(gUhRJxUwM@jG3i6Abn-ev$fiQ>|FuM&q2 z+FVV94z>zrA?|9 zNwvJ*H?q3i%vbH@uG5G(DQw{HBvg!)cfB2Pu8|_xMC&5-7Y)a?XsOB~ zfvd3>cA9o>WjG183Dh0+H-r%-HB$a#dvxGcb*0mXVev0kB5`bNW9I|awULgbbZvz(U3<*v)Y>KQb9ZPtMWVsF)Mc%HJidb|RA6zD=dz&vTzOqg zq@=*=(k6GB3+;`-IIyX2kcGr`^R$BA>^K80hNCsG`Bn~NH_SXIdl*ODDVDU+D$ro6 zi3!GKwo{AhSE7Arr_AGbCI6=vdLR;7BSK|+$~X2Ahfcom_k1<=l~)J8H?h4Y^>i8~ z(S^oKi%-NxqbQ4)0+B0pFNxUBcijGJ&3VmQn?y}Xv;Yu4rwsArww#`3#WqCzpc>mU zvI(k`;ixR*FzR{cr@4GCbOXIlOI_6T2wPD^9H;6%&3D-`CwkTaxu*2Z7)zlE!1-E| zI89S@UB&?wzJJSr-lzAM-2Ntg@0UG-7OA+wPP0ahsY6+Fg*K=5b%vgG(*_>xt5Ib! zZ#V0j7rMgP919zk?|m#0n{o1QY+De2=KqZp$X)6lug8jNh+z0V;jw1$e%k29cI!tt zB#JdV#Z0}IpjX-4QCW>zu=8`)Mh9C){aQ-Vqx)c0 zi=@k)TL_aZH-Ba`$(zT<7MZ{)V>@xiLKq*;*sw3nydJ#+u0y4kgFl7{h54xYo{H)c-q6?x1DnCRFxL^|STy&w4 zh@W>7nNPgQZf~vrZGN0p5#Tye6g;(CtdU?5mYlWP)`^X)sjZ(N6v>z9>oxCuoTWRp zit5J;j4}%ANwtM|GatBgJG+L~wa>aj-LmX6Lcq*=1)%U`Gu1%?|CV=t!D>lXGGXfZz z>~FgF`e{V1Q;`pOe?z0{~4Ucuazgv-Sf>}BFVqLfGUAAq# zi~zdNja~P~ZQyQ@Gv%`g$(&m04y?!aqzqdJD*6`+LKAou*iUNF@Grp_sha&-5bH z^IwlynAgpqm^q-Qc18VF|39YQ!LQP=Ya6a6+jf&}V`{?GWZSlFO}6ctjGb-U*5n#=0lF_+0SD;Hp~A;JHEib0 z5U`Utj0hXO;PCtxjO&N@GR~l$w>&()o&1%#7isKz*+HchD6mbHYc;C*8k$3Opzo$K zlfGV4-Un<~d=X>FxT<-q?8&Gy0=?V4)^ZKRela){{4Z5VQW>m016~}^@E;i&&|TUH z@GwH+JP4@tWwUmaricQs!-Lisqmdr$`(euhvT1PurvG zm{FXjzs%Dbo9*XI)V_Bh8qc+bb}~*N{qTBhM*-1#do!V2*4|d1Vbi~5E(UiXcOSwS zg9|Hrvbgn}D-Bhpo9qj6r%*;$4&ZwD+4dTsLdowy8r98TQ5|>-#CVDv#28W*#_F`S z8ko;8Yeg@?x^s0&g4dZ4ef&I?xwvOoaf9%QF!=PK&>YKW!KJS5EMB^pJB^OL>r{xM)4QjB3Oq9msl4yhc*(vO*Cb!=fJPO zF*YloJ998zKkqCO@_+3QSywvz%kQsgv8wm^Uj>ybJ#V2g^nvQ(#WTCBx+IPl^FDw` z0kyd)8rB46_cWVgWtz02>KDpC^)=JM>fJuOYPukPITDfnH1OJI+R?Cfp_*3xhdvAP>wM-@D02JV;o+J z_{2)VC!(&S#<>vJHQ?I<$1gbP)#|?;Yo*#WO8WNHa(PYNAg(i|FLy^_T~D{}K%g@> zgFYfOySs9j@XF^b>i_bM5`B%nW~Q4GT=}zc)lRq&GpZb0f&8L_-&l z-jtT-2%v}8*W0bknSVlGyq9ehbK6J@cWW>NH0(Mfbu`cLjE$v8DuO7PlE@$35=@bL zY0*oq?NmvhW10&5sHKxBHlg)n`$}lvREKF)yv)0dTfaB;BBS9_%&AtEPFb`9xROwq zDEa)M4PQNPY0xX=D%d{F`^h%tjh~6Qu}|aEi$8~_nRm~kI~s<`?Nq08@H#7qU1KW< z4ILv{(Lq;M6)+*N5`i}on?7hJ%*!bZ`@Ipa+J+aM*?Nrt_#X0zEy;+^l@Q#rD3uXh z4Sd$pdy5Z*~<$>_9vta}( z6c2F$89^9zL$2P2OHOjLLWCHXT0}i_pCJbJ|6B}*EdkCrYZzmnGv$*4JAtGwK}I?} zUp2s>bF#p~q}2xeN*zbpi2^n7nNVOLP6FvgcBd1^g@0iNptX!;t|}Vr`cbf$jKpp{ zH{KSHU$wY8J&`h;$f+Jbf1B>eGG6KsTQ;FANk#TD^uy+T^yGI3RdwAQG26gNjQLhg zZgC9e{U3<&8JG%ZWow&5wvp~NqFl_m} zc7{A2ZsCiK5j!hn#1|WZTZ=3~*Y+VVzQQ`I$_#_rwZX>*-S*6SiNTGm*+2M!wq8$s z3|qV|bGV>Iilc3KQnIt5)zL!TcJgnJsv}0T3idg0Uf7KF1hYI6_AGkFuNqDa3kF_W z@x5KG%yt8?RSy&zJAuvGN=Y_nX?xk2$O> zw=@7z>Ppx@25n)t;tIB*WgzTErw;I4{c@ySDL#a~uikW5T>p657317o6FWf{;id#* z*^JfO@T#_rgBD#@oZOeHme?DYUvb#qP+UG{-v+8OGk%{{{HASD<{fp4xB2s3y6}4> z`M03&)6CSJFa|<9&Nr|DkhJI_42j+0gHaE;pGg-F7jJ^z{tpaU&R|@ezA^-S5NgPu z9CY7y9RLi|Ll4u#fA*^4?g#zhLNny(KD5k&-mlr;0GH-fW0WRM`n^u1lg2GUN1Vh3 zu*`aQ|FUZ6ez){GLI;$}FLUW&kDgD!WURr_TzpkgQJ2Pt30ns6CSvp+Ei1!ix$cLW z(6d|6`-c5m{lkoQ#QC9@E}6YC+@tOT<*rO`S^zV+K7@KgKoj9Rmli;s@RUIV0Sr~( zuhb_K;;f5JDLDiu>c=0xZp&SpkP3bqK%pj*RIlbM5$-zw%sja7 z5j1(&UW%@djt!kIsS7Vrij$2!W|+rjZCKj4^7ls0HyIut^D0CnlfCuW^#K$hML`Q$ ze|3&LzOPW#6YHfgltoDfQO$}Cw{*I`>@mCyt3zL~qX3eA#3G?gtCxolSNn2!o=_bl zfNrmbFG&13q_GA5?8fIox0`(NA@10?=-An^XVuu=Hr~9c;UT6ci#mWKpMkagWs2QV z-EtrO_PB;ekVT1+C!;8hL{n5CwJLPK_RahCGUR?Qr;x#m+(ox!s|EM`Yr$#;L8fMmqUg?tCqh1yD(vH&yyd;^NmoCEh`E~Vjhy^W>7g8)Yf6Vj7X!LGHE5= zZz_7G%Ojm58%GIO|6@Hv@o3UM~n9bOi|E z``CvP27GEs^r&ND20XWTsg10RhvhB`-RAkiZO)-ze+Ef;d9~mYa(9~MAESIuusHw> zoa_AZh(>hLRCEwF?=hG0WTuW+*Yd(6+6~%%G+4$bh_hzeQMTqvzZ%<-!Hrr6hx*f6 z+Y3B=@%{kh74?a9YBBLPr~%`3j!PU7V#cl_{~eufNFTUI^j1$eDL@5}k<5HKm)Z^S z_1~Q8XqlP#L3V8UDAYZ|oRDMzIqWup;#~V~KGV-+e(2SlotDZWz89^F3IB#D7f^h` zK!hS%5A?3&j7Wd?9qL2ttj+@ zNUR#|9>0-?JnP8KU&a^@HGnaVZ1*ET&)|lZe&s6y%?eg8w|4&I|B)4-vNN$-Ch-`} z5FoyOdlWT80}*F7(&+Y@!<#0~JWtrngx2{0b)$zi*U3E@b9^EoJH(a&Ka-o?A|hm; z0OmtxjT4@G4lWBKky<6~jp{~-Z`9}2_+tOukVY91bH*7at`DzTGoIr6&ve zjObGMvkV#kD?{!Vi*~Gafa?^x!;BdL=V`x(&QT6m3=?jVs594gdkc4i*H0V-0Wozf zrWKt!CMw$Pe%WAszJVR{LbA1v-kX=Q73!1#C7gHP4qsltQW6t&TrCLQH4!U z8I*kukQm_8pj2J2z+eaEeIy~SsET4nHs`xstbn~^G40uIW2n`|1N@(l7eMDLu$2KT zHYmXNDF!~wv_{{21sZ8t5+%_@Y! zG5`$**>zRl|0>@1%wlk3%{{ewq(;JvcYgc$0#anmZobD2`O|)2t4OfAWmR@l$Rqn} zoTuV&gqpM{JeVTxl6q*S`bKHn;aoQW<=_vF1aM1T9k>W%nUzk4IS?94Lu3c3SHtx( zt$)!ng2J^?6nDQm^Eg)b_I@wUFRa?^AYhc|?^XL& z>)*DqzMl1?dc%f2#49^gxd^;SKbMLUHGN0Wds`12%mPf|lt_A2|4p9tTu6#aSoLza z!_aSqPx#Bz@yCPw5BWG-#ML>N7oeS}*V(m)$3cps_ay*|Us&0(S5h#bpUsg8{zt_r z_pJ?D+1MC26|mo%9Xb0qG0owrXDtrAb-{%`U@59{IYD^i>#!hyN|I>;i0kv^H z+h14qIC-DaneLYuG90735^8?n#b7$);jZ8stO6_U%~I(#{L_s;^LkVd(ypN{pp7ky^C!U^cw%_kZjuGu-ydNzP4%5Lq&J8+{ zc4==dTj_2?DYLR@S1kqXcRZqc9@uT{e#AHnHe0sRC>`J(d&X0|idAeDqg!lx+8WIb z^HVE>X7_Mk@fEEHvW&2=!2WbpV%^j+QZzoq%5Gfg<+dc@+y;=CSvIV7J)K3r4XHod z{&L866VUqJ|D(HWy@H8dC)Vv}82=ZmXy!F^GTPgE1(aGfOsX94QHN890>UwHp*d6g z1!!H)tiB{Ns1c~(Z%U)m`O~JHPNEe}Hc(^=J|q|6Re+|ojsq(oeJ8*CaF@W{`T8G4 zO*uX=s_xem-R2f*3mH6t+?&hCs~%hc?fm+05?PPZO?US)qSY;%shA13=Pu2cF?k7F zkIqB3PN_?rvF|k3B{0azAA1NnZXwqx?}M1PgEGNx%p?*Oh_z#*=phY06>29*w|h@R zX}u&-kUkCvai*orB-9VQTN@~CbL>~*44th?vy_>5BTp0o$I$trG%xzlw^jc}a8DS$>} zO8VsFKrzPgh148>0T%2rHS`GkBpdAw>;+5UVhXFzHe$#;P%JjgvDYx>h73sub}1GnXKHaGzMY3T8+J-p>hfQQBId z%a_HvROL#ig!}j2D&ziM3XMf#lbB%a=3YyW>>i2!@BQsE7e`f^)eKQw*oB@x=#6uG zd1n0X2vTzZ_2;$ESi( zCUhIc94HL^?{g&?V6bN$p12CtCUnvz0jdbDET#L$|F?1YSL=5%5_*yFBO$(D8;r)| ziJ$7_SP$V#DfD@$wuZA8uS`C8i3EY|;Z30>c*^?6ULmS?>lB9`(_xdMicMXH-6TTt@Ja@Dp(MHy z>mk>x5A$aNX$q(xb~eJ?rK0ZL#b$qaj5dgIFNX4gA!i`x-pE2P(??r-!dq;PM=L3| zMjBBr!(Zsd7VG%o{+7AFuxZmF<=1|ZT!NMrK7c9~U49Urx7GJ59N0(g&-E~0_WOVj z0S@p^40Pd@i!2eXl2lT&^*+otYK(X62#5hAS52wt>+BaL#tLB$0SljDDwZ3q4mZVr4o zxc`6ceBg#GgceABRkN1v(m08HuiKFKE*ar!?h70ySAh-fZK4K5FDh4E_r(t~@ZDZ| zQPt(?4jm>}k8l+p11kaBh2q$^>tN`px;qkUxb?001izyNU$;Y%$>-e%9tYH`=)U}z z$^H{Gi>5E({1;JqS1poP_O5G6^Qb{o;L4czMof)eru9E&~INka~cP4N~ zld1x^TsV^-ocV`VZ#zD(z`t@7K`b2TO@c2Q7may9z?WdMEE4^uG`;Mm2f05ssScQ) z35#ZN75%~D#B>gREVWSmVM-ozBqanK6@f$QT7%#(Lp|cRRpySq7j1sM zp$R=%ciGdMr^xw$J4^o@xB5UuJWQaIQW`2NZ)W2}V|w^#0+b`XWg8*6J{F7K z@U5X`kz_`*iPA{^o6GaHZ}oDh`smlev8??|W~5z7RhL z&0{Wi@bDO6rXr17JA-*>XbjLG#v4fUutSS(&6+uG-F{oAY0U;I-%2bjlhcznrKZ1g z;;FFfZT{EW0K2n8mdBbgSLC9uM5eq*yymDx+OF?Sf68aT&en>GPTXFB1g?~=ovYyU zXN3h)eg1Wi-^~#Z0jwrFuZ>-x-h>c}fXp5i^PP{=^qj1(?vI&)ZxL=caaE4w2Ec+c zH~!_bt{hn|KPU9tGTJODV>SN}w)&Lv6Sm;Yw*r4wdPdJnee2;-M~F-kt@ecxvb$)XRD_C7;! z>l&KF;CD6jX@_k4puscrr*q`iLSLNSuI54j2XyFtt|G%C&hnF?bxn!LZe})fe%H)p ztWC!`g$)qW4L?8H;Va!jTpE!*)>cm#AKSUyLVFhV{B^ce+z3yl-@MU5_hsQ16M0wl zkdOrx zurR0_iY0`iwXbQce{KN8k7Sladrx84y-{!O@V9$jUhV6jUD!2?g>PJJ2U1y2n=%kC zLXaQXZmrte(9RjH%%eAN9n$t(UAF!yRcF9%3&nwL28V7VM!A5~)&&?<3?=3WWN`{m zYD(GLTyT3=LplOG&qXS?Wc8^A=+$y%wThxx-wxK1+BJEfk6-@tLaG_T_b@9%{0(3@;bSA`yaC$8D?*#xic8qeWV{iwq9Y>i|HNEE`c`eDY>ktQX zph{@K=N$}D?Vgr{CE@V{xhXcKu#uwA&#bVv%5o=(keF2I)EKS(*O-varQ|!T3IdjO zI7lDK_-5mfx0lSmn*B+ND{C!~B39`MJJqq0Lc^d(?T{u~cu@aMdCRH5Bfo@N#z`J$ z5YONsdmXmxDXJ2qUfv&Zs5G^W0I2Sn*r+B_d+By-u+Bpf|GPG~V&xA4`huRq`K$OF zj7S>D?pi)vQZbv>CvOF~7R|BB0Fj+%c9)95Mx;{_WZwIY@@o&>54zTpH`S@mpyAq&vG@1znf2>c zLR+0vS0`tr7xNeNM~|U31bSjdS$}gsU?i+)9EzYMAec#Dg!)31FGdV|oBdPdm*+3p z7Ig`;Re*kIBZbL>z@jaR@Tw&WNo*@>iq<(lFAluq5-4|^PC8sPM0Xx#8v*S1h;!&y z#%6dHilNDGc!eBoDvyLHNz(Pv+YMGg6EEI69K8f*&8&=CjfioTj_FE0;^T1~*~Kdx z-*tss4Dsbw$J<#_u=243Vnw27=xvn%OzA?1Tbj#va@$K@SvB|7sBOq+uJpVv$NPSyHFC9y@M*+kJi$Wa zS2a@GJ!~%6_1O&jy{!i&IEL4dkzTYOz<>M1hk&7e<@KcT5sXx;9?->R3qdWuU(vw^Jx6x^}z zX429N#a3-I7cMay_y)solP2;@(%UvLnts6~OOzrJ~A&Ed!`yL-PE>&+5yiLQ0BNVuu*5v5e}bJW z$_C?b@NH2HxLko*osUg6TLwcy)a3i{*B+iZ!A2>{^95)>d~q1sz=;~`l)#d_5-VQK zh*k#!5(mn6H}814xvpj)cueosV;C(o0yr&v!IZ^JYI$FVdA)BQ$#=tMGl*^>QzOGW zb&Heq_`768aB5pR=l`~SUWJ>P|0q6fQ@zcpLukH(XhAdq&+-O4k#SC@^pzpA+i!wT z-Qq@`e`0>z_!2)?@uwN_YihRoB4oH>9h9C7-9_juFp8wP$})Lq>8P*7Dr-+z|7x{P zw91wtrFoPa;fF`mac}TkQ)Kx-W>xII3fL8fG0n%+An8b>IB-$NS^Xw zWmfyjc+nQcyCK=W`lvAb9KH{-Tz;h$Rng+tsw$%hK(5~p2Dw4y|L`GLhZp6+tk-1_QuyKX5M&13UFgFW3ZOSa@WE0>RzUA zY?}2Fc7xsz!&TJVgN5ng$%2D6N-Epx_6 zwm6>QaJ=m^C$OV93?FCi=YXDG_Qm&Pm+9emSy?+%+H3(ZZ_0M!8;fLR8~&*_n%? z*!yt7^JS}%0$YUaY5#y|d3zqr9a0AYHCaBIVI79V*99iZt}@Sd@8fmK+!t{#ClHVdHN!(#nJX=^5BPP-O06F1NTuo;r% zzW^hZRQI3cyTR3fV9`wfxsGX^Ikmht@X(?i;i{8Om`l9NK}DbNuG+3&Bdr0OOSb-N zT|@2KN6KpR+Jf>XkuiQ=<>f0MylP-PMt|VmBkY2`yB!fRC1F|tnr$nG0l)(f2*#~o z5L7&M&(*r@uHAH#D4TN8rXP!Y_KRqUfILu6e96HMOz66+(`2(5tv#jeUXj^L5tfK< z^N^OSG5}U40GIuQ`MH>wh_>)^_9Su|t2i$*HV|S9oXPoQ)6l8?$H-zS_R`Ii1m##3 z^HjZr&dldx8AcPX*X=4Sb{RV{U{oCI@yUJ199)Tiq@`Gvv$%)Kbd34MDvTyQE6Mzm zTX8uFtnm7laXpAD>X%~fdx{J%zrW!mo6bZfn227Fd$D*Ax;_L+^8PHuFKn{FB*N>Z83J`Iw|))G2*(J2W9>e^cM4)?iXjb+u-CJ{An$=;{-KZz1Ui= zSwn*$&OEJ{@yBmQK}cCrRh+vQ(n*S3kP1G>jXW1#9lOTTmN9kA2&4W z;FJ#HKD&KP9p0p)g-uo`C@P0|HwR@$K%AZ|(62V8p2o)3h|P+?r&aH|sa&tZk=V-! zO@`&cAcQ+!eyRszDQz!GgPZ^ux-3>?&f?JaKG5cpRi1al3@HDnOEYA=q2TaGLrh$; z4YtyZz#E~+m`2#{-*Dl)x;v&3UTW)J2rdnAelSco_VCZPb$B{Tq!#R^$Y+W?%XqFc zfbH&ZBdEde-7)#i`9+x(P-u(GYnP0JU0+3L+u2`EYxkk+7 zk`)qOa@Rw_Sbnd~YGL1eId2qjMOX{+{UDLwxa)z}#?ixvcRe{phQO|S>TA!H#j=IW zaqywDzoU(|TsC#IkxCKKuFHtJ11<2nNV-{p7X|%m^GRvOr@f4k_Z>d zPk6XY*Tv$@e!)L|rR(_l&kgzUCS0m(5vDX_gwx@s+RlpfA7rAO^;ILg^{W zcLs*EDpSz?TkvBNkN*aq5DRxBNJeLGtSeDy*SDX2rzp+@u7kEgQR&48XbrbU#l*0> z^PcA!)mVWgTH(mmfh>YHaZr-jPp>Yz^66NQ&dzN9I1nz?(PvsZAiCV<6a0R49~&+Z zY@9cF@9IZo`|%{)b-c_q% zx)vQl*JK$wMznU57spqE5qYQ?O!B!9rS*HMjZsK$04rcHng{Kk$wxx32Tyu|Q(N`A zL}uZJhsZ0;YhStd(2JEf^>q~molpdv+{=6~`>?g8acuZTn80%((4w8%zS3QYK;PQg zO#@qT)P5xh_%Yc-WYxM2JXrNS7Y#EFb`o5Ly+jlUaNss9sm$y7Q=1}qWOBd;_7pm~ zW?m!W4qvskEabKd_r*fP_lIh>1XN5<*T8<&%>kW?v7NycQ%KMof4lAr?*P*46V~bC{=J>oFfv>+s;oDD)H;${Z7 z)(C+#T6{suz~xZUqH5AUYV?SHWn?jprKV03Sy$)JwJD4f(+O^bm;>HdzCbs1c?@6k zkiTPXl*u{~@@>&v)S;-oX*Y9g5ZBX}14M3|l?on)D; zVS?d_q`u`{o}sLt9yoJxjc}Hn7pV1N&v2B{bqdR-{$KWskpUdddHP_9g*$7ZaAy73 z)5PY&2`VBwQ) zy-3y4<+^(>wAYIT%5>slw&25L9PkUQDg8eC8cijU#Zqm5TJH$$KX#L+#9P<35n~#O z6N+x5PQ~7G_7J|joBndz)0FalQY$Dvp2iUGpolgroo%;K{);!E zwHFkOc)s3HDADx|G=j^|M{N+}vdqx4bDPaZIGV6Ky&J~xw-umEZ12N&x0#ScLn95dFjKi;iVg}9R{j!RT`^lr(IbA7qkGV!v;5ZD)5D=Gl(Yzqm-Q&(^K z+4i-eUt<(OkMk7>Py+NO>nj@!{ODIidG#rwEPjZUhqU&tdqUNQI>faJ1!7s0f`d}q zzW#B(FO_1=W!x4~NqGD)V~Z>mU1$AP$e;}i^X}75bJG6lO`a7l%UkX(W z>GJS&n+~qZY;w2o?h6`|6tIk>_=Ay5is$Z&@H(CW3PZkJ!2;OW=ZsLi8i6@T+_jwG z&olp(4tKGW1GLwctK9Hvw6Sah%57ft_(WlIj>cw?R&j_e>s$2l0QHtieMhtEv!2L= zzy{%u=lG$U$JT4T`E8Qv`oMVKP`XwE5pWCJNB;K=HKm4u7@#NJh~L!)O~=%74n?RQ?Am` z+9@)=8)p+Sd2RLfH3#i8CArm^?!NOliGo2ZshLM|1nG3YKj7bHe4Q~+I7q$oi;6}r zg?J#ZBypyDO*>I!-4eaiC>*5BJ{{)T1}wTH$)%%Za&1Nxz514n4ZT>tfF z9Pkvy+95F=&_;{I?Go}6O! zMg*OyKx6v*Z6}75wQew=aP~78V&R0F1FzmUc=_cREHb+}T0(&5{(fn`$CY1-Ya`d@ z!Y42N9mh+Pi~!41e~0Z;`AS3y2mUggJhv4Jdcp=(cEc8+#9(KakI;t3sn2Z9->Kn(<)wAVxfp)~q4-4+w}Fh{D~c^(MyYgOBXxe!ngEy@?1Ida1p+ zGk8Uk{dPOmvYs^R8L+y%FFO2B767KSBymNm_e)b0u(0HOgkJ%nXyEyOn)|LoK<-LM zNM4MUdD*zzkB%c~@`H|w1z)n@yx#`0&v(SAVSY@kTgwY<9?dt~&MT$cIoi6!#6?%RZM5(QIR333zYwtJh^rMj`+` zACTnDmNDz>S>L+?xMT_0r#jyUKK)%4U*E7=_tted9iZbr_w%+@o!wBK?clT&2n|O> z?+CM6_mG=CwzaS`6-ypVkR77OHeLdWHL|pbdHLv0U2VMR{h_Y#bFomiNMka zTw8jxfsgxso-qGcTHmFj+*NC>#%`0juc_`x+iuLg)yhJ&Jz-dg=!h*Z?ZmbpLo)3#4m)zfiRj}*_%rdt4%ip>}O7X$AZ>xPff8oOE${ftwu#uq&i3@&Uf zK*fI0+ogI>J%Wz$y{n$oeG2tc-e+IrO6h=f4qOKQC0bw)XF~PnP6)(Z_y&#asDU=` z7o*)FC8qChIsSx=9kGSMwgc9mPe7L7?e=~_UO<3G5ae$N7Ir>84NHs=PpcVW-!-Jn zW@7+8wO8Z5_qXZ|RHE6zqv;SJ>BxX|bCB3o?qVdk7(C~-V$tsuVre5haam?p- zx4;r1AN`v-Cw~gnL(~h;auNexv~_xG3v|!yau5;U=q}H&E;E9|pleyTt;>k5Pa&)u z5O0ZwI)adJ=lxj@>m))Nq%@qT31KmLVX-lH3LFyKGt6$*ITD%V<{L4lXOo zk18^<@8mUP4B!TO2ncivpNSLz=!eG2Y6`}PTgx`;v%(1+PP66KX8eWBu5zX|^g^)t z(x33&4W|9xj4FXph-Y?j>w6Si%^kMzO0y6;xT=-(p8Op59(R~G(Q|e&^LezX+)-JU znRSF{k;^7ZFf7X6z~=#i61zLtpJ_SX$FDYHN7wtb6f%KQnwyN~H;{M(9p+doDrv8# z6Pnm8rfNaBzzddocFaGPiqSFF=XA%9B>&N)nVmKX?S=ZiZ>X`HMcegaAKK&_(RV(hi2SKLpZ6~GdzZsv?}&=}0e2If+%PAbR4o}b zx}le<-<6|Z02tF3m$0c02y8Xd?iz%`={oXGKwt7vJj~2-^3gIow0f z2#vOZmyb8a?dh7XK%TmQkHwZi()jHY96uynDd^cPZP!N>qzeNar2zpr)DNb3uYt>=Q}DS zxLuIHO z{5~paj|=FhDn+5w_=b94{+$UL@Q?8>D5@##G&=*={&W|}#BgmOE8>%2v4QQ#mT=Gw z6Uc&VobkKerCF$v-mXZ@#fx3Kk;p3>&hx*ZMyhp47N$~Tr@ZyZ1R0AR4}m)wpM$C!L)WN zF)fK(a$lJPIE5Y51~-k46lC{6+}XY@#^n(v$@KLUH2@5lMe|R5)nDqt9w^1x4`6GO z$+~~2B6}lXE8GWee--GV`BN%#j7tY~v!-Gh`-6)vb9B%@dU*`$SOcZ8;oD$YxW`wr zLhku1U+4*??3=7)BUap+fJ^S`j*zM(lP0)^E0 zyET|L5=G*M143C3H!{0)eFLd$>Q*C_t>9218bk?5^{+?sO%l-;dtV3-5h2h`l1Eq$ z`*gTGFl@w%ql-jy)G59mop$Yf|$V&Y*Md{;jSZ>U== z{Z?!!?TQWg*AmiI3HgqYR3fmPng zZECo1hC{91=GduNRt))evZ7|utMTT9z`wdZ?VH|k`0+)?@xk(|OYQvu?E>ejl|E2* z6$jTl_Wd}e5Vv9RIIqKErYoUoNZ_DghJPW7;Ht$x?EkzoEXw?QpA0Cc2$vGqs+@Bu z@$f>~vaET-W%|GRPv{~L;Y+%9CcmT8_6Vz+MlHdbQIBA52JCsW zSx+vh9=6h})NlROFR3DiH9s>J(TO0gbuN@=*T_PBrkU{9zUOs}CBs6=?H5OX2W%Q1 zp+z&X5@bu?-Q3cWjhI8;1#xsfAg94xpE3*KmDtW>!{%A5tQ!6O>vX&p8l(z}WQ3^0 z|CRpjRN*Xi_4TuAYX-J{zdgMC`5#w6pu9AMQ0vW)AASTtI?Tcn3M(-OIz$)f>cr`_ zIDe;UJulOovxWRzZRm-%M;S#7cZU~{EVnzfEr$XjUIe~k+2&^3qME7wTACiXsMySs3 zyE_W>oOeni?tL@Yj}$}>A_g8#+1U4AX;t+jcp!g#4iWR?e%_W~P0Wp*}~*t8gw+ms^A%x6?Q_$a;u>tb1tF_}i1j zx^JZ`!1%;(5-lyQAx#W?JoIVUhVF6gB4{#<`&+AMqGW8TAWazhvJxqW&d`s!A>;;e z{W&75jxhcE*M0|h1qs)A*s%rGS4t52TQzyQRU z1v~Kn1|V9!wf&`JUTiv&5f|yb4??+}9ve9GyNIFX|IwTbOUBR02tJPubJ6|sLMmiC zkVIt!e5(eneUf%L3%ml}($T-#T3y6j&FtU7nG(KdTjw_tp-@4Nc&Me0y|W4aX#WGj zZ)D|Y{j}+G;D_{Ir2GG7f83iOXBn9v%0bEQ!X_6<+3ueV$=4G90b7iIk=7sS*T|t9 z$w`zQJc)q%o1hZu^{x??;cFDsRMha!Mxq0~u(Qk@FL7%Kx}bPNw?wUqLSlYJ!PkbvE%X1b+Zyj?W4=ZK|mm9)ol*c zNVlOGEN=>bSszJ9KyZwrfFoWebXYNI6&}a@~n^ah%TyXQ18m(Q9 ze9(EZ8mQZfkT%sGMR!W9gpk{%3?$pxMmGFS@kY*^<$Ogk0DEfC(f$?Y=%R2-&p8d|4LW_hp z5C)dbJY?UE=^b2Dpzy13wgNbh$1RzTpNHjcJ z4dbJoa`^ogQn>-4D1$a@(e-BbVKnp0%utdam~~qNfipkcc{EPbC%? zab=brD)-td$@(NP#~F4%)q|FQnH-b)_}f35G8*EUt7-a+E3 z2I1^Ho@asnMs;}(V2Gv(zwDaAxnmVqVg%s5;BZcc19AgHwevW4$x(w=ickvYEy}@- zX5lkfNMWFMQSArXe@t3$bA#JmoS_)2G&;*mt3nBZ4QLR&Sq%I_`j*|MRIQPz`_>R2 z5X0%uXhuOq?_er5N8LBrQN{W9^ST`*Ke>Q&k9dh}kMEy~*4aG%!FxXVq zc~0_nk|th`E2(DJ7p#(FSI%9&k#balJ;z9I*~((r*)U$H>86VtzSiwcjefsSDupp4 zMJJeRR&rHBn06;DiYx6d;1>@Ww{P)15NN`bVH}7|;H@#_Y-05r)}d)1cL~D-bV2ZI zJv5_%3?ErHiBMjyMmFAujbtBAF>)pHVCy<|&R8&C5i9@uCgVLJ4tb?CWN&G~w>vKY z#IFGld~i_+#GiPIbS7XBS%E9TD_|ld*>ajIB2hs8O*=^#OA=DKgE>zK9k&M9>`HB< z)yppr>uEi&92NH3gsqBN=}mW6-4@i-(Q{PJ!vS3hS0?9vy8kH~4w5opLmh!JQDD!C z6nK;-91+=Fw;^s>kq;X<3h&K+SY;bb^JH6n`Xvm~BIb)DDi~i~TQ_5MSLfFCK$%k?nR>I> z{fOSkwzc`}>CTJDLZPsv9!*zIu%kG$c1%f6FL37!c(}4`N4aT`dGD9S}XxTVS z$Nl45!Iy{&Ajd=4U;sc^K2N#AuOp%PYPL#=b}Zo>h-%!=i&+9f#Ry-Yll5`FmN-)> zU+5%1@`4FpGsDr+0>q!6CsJ~8R}}(%FH53##xf;ZVb?DzV zVDl={2$c1qqF2MRFVtXgurlqZga6sRA8V~|FoiT7a(jWIpH>So+Q8wh&)((khn zI){s5=Tv`?SjPK$&lB+yc(@c2^dMy$8M(Q|wjr=w9;&Uz0n)|Bt@Z?VG|IlAKAx`D z*h)TY+nM~##`C;ure0t7b;vMf{XBve$YkNL2fOa3H-FE$>HbmP*5N(+w-|5JL;@wl zWA?aPnPQZ7aS@`V>P>LoM2~Y72IhHgPJTmg6I?(qbzEAHoRm<#LO#HzxR}a5zqfsd zwwfm0e?2l*VvAe4k-3kR(jO zV$dwGT4R!vkHeut$>!?G{c`8PMHv+jFBk{5B-_Dwi5%4|8TzMNAigaxJ*u@{5Q1Q) zq=oKu^Jp`Ujlk?Kc$eVYSJBJfDb9T-ad=F0{M-s5Cv|JtQ*QX z14;s{ySXp*Yqwuv!JIu1miwP*8+x-!)aVmct)R9w`V0 z?3Gw_A$Rl}(>1*D^f=**r9|EZYefIP+v9HI_iY9pB8e0D5jywpQ2G}=G|Ruj|ML8V zd}m3?6z3y4QeORzVtoL&#`1QP48$X|3hwuQ!gV?TF zL|ReeS0LNaDLx@wm^8Ox5ZQ4d+05%bz|`Ha@8!EaY-C_@!HzFX0tZVw1hgy!*cR3&@!!)30S<*uBMIO6Gu2*Oq2CAEJ~t&D zbltS#2Ag8;DMFvC2^?zT+d8DJ43CubrAQ8mJ)gRE0n6QlAvCEjUOq=VDKjN0MCpxo zM2m5~PyG7Fg4dvOYuz>OEoObJ|6#Fp9Fg~vk`k7+79$Qm8gQ!PF{GQ<=E||EnQ;J} zbHB+=mE-NPZS2UFG4+x1s~KQ|JpPHvy7x`%1(xaGX;pjJ zOTeY-8pjQ;uIyNa-(I#lQz~Fhwir_yXYZ_!`#Bzs9=}8qF)#%Y6cSn}&91-w!fQ&c zN7@VwY}oO07)K%;xB^KKDM>9JlKDiL3+g8uYpu0kk*c3>>`ko z^t>5M6Z2G|%IS?-V=X`YK9jo6Ot-imB=0luFFVYdpVKNm{YuE4+n_ce+zDytU&i+6 z7dy(L8or9qeitnFaPf!c4d|O%gEWJ~0Lm1*c#Q%eqHjcT zo%2K-^>@ce>LJ(7pm7mj(CBseFP=iwpOSvGLkbAR-G$aN$8WEVG@nx4q`glspj*%+ zuE+$GQn!iekR^qGX!rh-I2aG|b&lf|Ie9^G-Jn!c&4t33pIzU5?|DysU)b|CWGfR5 zy2cSe!J7a15x_xaWnb{3kl@_5behU_)|pD-a{`1<%b8sdfAiQ#C%S9$?{>&LkMj;z zyW>&Nwv!FPi3R`2A(-8-EbbaPd0E+7z7P7kmm=f*?Fe0_p87l-6L?LXbOg66LQ0{&zKqtCPdK%4Cj}qgU-X-Wk5QgYRdFd{v-#mVXoaOa`ljXz0v)LZxX1h0jAoY4W@E+ zW7m$e8|xObZN8{2hb>#XKYnl=*LADyRz*S!3o%i83HMx72!4!y}8u0ul_7|7;ck0+xdi3|) z(xU3Xtgq+n>|hm_u4_;O=ngN~;bixF+j)=HwjhIHi_c+BN3LS_^gd3S9sS3| zA%zhMWc{#@X_Xeeup};-IEYv?_Y@<~Hi~+C&MKp@zOIUbA=?VVg_>>+$7+NK z+&swdGj1YKrf&W__lI;xzWadK8B!HH3al5dy$rHscz)tsV|N&shkk3NK`N)m;h5Y% z3GA&VdIfZv2G1Xe^k8w_&yOj&JPP9hUGSL zCcfJ!Md2V)*?BOR1OIZHsE`gWL4E;;K8hA|Q|d`A;mn6_)P0xlAqic8!9QK(%q_UM zBN2?vSFul>bm1h`!})I%B9sWuv}m)fc6cV(OISG5pX8(=3P&nzZ1Cr7P)rk{Ihgm) z8<+kn6ulS6pUoPKX|}c)j^Mi~lDF&zoBL*hOk3{+Hz-AF+4v;*s{Lg*z%wbk5R3*V z(|LI;!d508?2K$L7^89+T<;?eVW@82s;medNE;L2Z>ltmW7NpSyF7Pn`;tt(O2&=Q z=DV%+>7b|e4GcWDr`QjU|4NPsnLxe#Xos6I=SG=r4x7xHd9uy*RWeT1Q@xu!I5IWTIeTnVLI|aFq3QycESNS44hGa+)4cWiz(cBs zb+ZmG;dF3&!Wa$0?8c3>rTALRJKuELO23)cvqYLXDvomNhxE1i=GZ>!mLYAzSMnf9 zfnlqgi#Buqhc3{h`uHS|A9Xz1SD;isR!W=P@|pa4X0SOjX~yxsCOPzzli6A7=UN$S zxr!9(TRjv-RwD7vK2RSu(yrT9^{sE}i0d^Cp7=W(4R5w*&bHUP7&c3JTB{#A&Nhp$ z!UTbv+x2TFvrmd;qz23Uadn$Kvu@2-A;JUG6*y)vu09U0&2{2N^!}`CQ8L zB&zU0$q`N7j#)juj!;iON_e{S2H)Kb5l0U1*(p05jOk8*^@?+emCb|7t9G$hm5O6} zHmCLNNPjzo3Lsp6=$(wQIWb!GNn^ndNo!#nfRkT27K9+I9l#enc}hW%BzR*A#;9X0 zupcLaakL%Jv}Y@NPcNi={qbD*$I^}qan&aO5kllUe)~O)U=--mv3^Kf%Jz7V zQuDgoh?`wMEmnu#>K^mV%eZMN#0;;AKg0V+RW$&AlYDV@Uo zAT+>J)-zeHBk&SWtA1dJniU>$iUG^ko8LrCj0OK15goZ^1cM3Vqvm%8c{e#Ch$=OW zc0EY-6!gU^YIY_Gl1H5p)o!i@{iEuFQx>rr(s7Q&EW!YOFjT*|0%+J*N<(GcEKK7# zs7+`!IoLdfMKZeq+YdI#ZxEivR<-Ubm$PX5mYGJ*tE)JlH4I$Ww0#_&hg+7qvy>y7 z4IPp7-@We~AODe* zsaD==6lTh`6}ajSb#r}!^JU(QztHD|q51I_aUVkh4LX>uj@Ycm;P+$vF#xD9m~fD~ z84ltM8{G#*8rl125r;dd!c4l8#kvi!=z&9%!1r)a1!1wY`K9C5B&ma%qOIkAyw-() zHB&kMI1gzQqw<9dlxo`?Ok-CiCqPfzqLCV;{ zM0nd?SQ6*DOU-j;Fm?nJ_Hti6RJ_}|Ox;m2hdyYNqoDd9yUVzypJxkw(G-?A5z}ZXH6a4wH_TX^z zbT%o5>FleU4!_ zN+N!J-)S$>Ai;{67WUn_jM5P67Rs!Ra~-oM#HK%?2$aEfzERnHbKJ-fqN`wQqU55Z z!lQ*eu3xj5`wn!DaWN4yM`?nz7{dIBof@t^l41Z?#ST~?<&&NbL`K(8H&l;Qt3mvq zVm%X)7-$M>>IRwYvyUoFLb64pnds+rxHbK>nf=R?eMo3tU19yML!FwHwv}csd;P0U z^CII>Mdz>px&D4i&Zmr-R1lGaAEwuuOWJP!E^OLpCxxMPj9&EUf=o^Oqh2y^UW)FKGlLSE%1(pH@DW|WGwa~-Gk*13HhT( z{_5EkThxLbjfjOiXyuNDWh~}4p4!nawU|E`J&-Mgh-N@|H#RtZUyRlf}hB=>r=}R)eBgJ-ro#yv?#O5#R>(D&e!a zL)5QuH=TULE1%u&1wK_Mrz}dA5%}G+u3SRMl9kTIlT9f7nl%n}IN_b-$f;-ts0P{I z-xW=6`ki|efD`YpSTKf*a@uzG?}t#+^O=~-RlVB3TA*}5)U{?32(6TJI*d5XapIxu5=-+1g?A1?;4vkd_~yXd8Incflo~3vu*@f`m937c*UD?Wa}e?$Da*3Gy#! zP2{jDPHWJAurm91z`J>l_<~cmm&YJn<`did{G1?yoR`F;sZ56}o?Z)`gri2Atv>ym zELvD+STO>)XU@c<39zW74B8zW`5o`kXH>R=$sUp^SZ_uh8&KiAwe@5N((?Gs4Y_rZ z!nAS$_~W*wwyQ}vU97|1f-+d9dqd+lp!}ED5l4{tfG+%g7(1D?#q!CVd;|Fd$WEhV z5OFxvd)b>m1+7EjO*REfhd6&GQI6m+nK^Lut%akaZ8Ey1(}1SK7Y`jJX!3WQX0MnSnKIkPgh2fm&6G+|)}8-i ziz3>90NPRxbP+~+UyQK?he%vZOkkA)McgR()6X+Ntx`*P%&;Ze#3fBk3M=C9;#@H&u-$$SFH^J(NcX$F!q-Oj+f2CL*>Xf)jER+|h%x=Ub8wK>h>HHTfBo6Py9rEK|Y@fb@0Zc+;dHWR9#&|SH84u7()y(6(9gKucK)_}Q+d?l1 z#NiLu+qpiQG|}hqRXisUavlOSeINhet&I_11`c0*ANy+q?Rp*=pN|497Oj(53Q5Ek znHnO)MPEpgv+5@0Cbu;=?T?GQ8rUvm80ZY-5QTfCz2EJi)T&VD^UjI^H@GXJS1s?V zoPJ2>gB75AQ#)J8W6Wt2PH1wI=0hHS|AQ)fuCnK8Q%g6%n%Lm%iDRzxLC3I8V`pW4 zzMAk?>zvJ(;}d{AoVX?Gvfl4ZaQ(d>(H$~aNVIdk?_*aaI?z4-z>AG{WQHKX2$SNZ zkSgqY;dArmz7AzWJ?iyzE1H>E46Uk$BRyLF(Vgsd+_Wbf*2TG!NtM~v=Qnj_#a>km zC+F)MuR%Q>O->{XKaFEiHhjI`%i|HvVH)65!DJYTK%hTJRtJZd+1q&7!1q?0ZwS#K z8%sA-(~tk_!REil5`1D{)DN2!Bg#uipU$DVo-y(Kl1HU(aelw1}TG7&kw|t@5j6t`y(pDuFv_arU1FK1=SYzsdMcMqmbu0vv!RVFO|T8GZdk(-o>9%L z6H&`@zYz-LV8IpjY#!&`LQTZ3%RE}zb*M;0Anr^QUG*%gNeE&+`@`-f)7l-OibSlq zuJ)mx#I{)9H@7xGM}J6$*<%_B7lI{831r>}q4bmDz_X@ryrlKKwtq_R#E$p0G8o^O z!r7M8XBo!oHdz}<35;tE;!J@aXHD$~HilqdWHBxm*aSGy$y;L9$H5%GvtpT*$XeM1 zU>L4RqZAt+&0;Qj8r2w59sW#qT8DD-jbMqc=mFIZrUB9hc`{YsV4kuBa|Hy{k>Uq) z>Ef|iXM+}?g>cer6{x~hGd+G-4JyYwE6{E8jlUf5V|X%_6mv>NsbQx{K;r@a#Iz@> zBoKazyF?H}=0V5kiXk7~)Yuf{&8a%}q-MOq^~=;s_aUJpEr4c}|BiFEt4$~s4`&Yr z&B#DUz2sV;66{qoRjM!A3j+x_1|)-QYdP|D5gh7|-fB5POFn>DgM(<(ngU02WxXo% zZhG(EDeyquhM7>$vOrNxa^U!|>kV`3OitzAa6z%G>e9iUC^DC$>RzZ_b`b;CcQ>zH zKWQJa-iv__`yZHrLt!*sST0mD{=) zQ?Kegh!>h@3njU7*P3M9{JbM-FH247`5LW${o`QR${!tUU-Z9aHeBSO79|oj?bS25 zXEg2yXUrX{9lcXqD>nnhKxi?Qt*&z9;2>sg_f4vac7stdBNL}=qH}rBL7dGEC!$N6 za4wqxM#WjNmRgh6IJ@G8ZVqE*>J+sr$HZLw&_K_;JT9&BXPh%kJ?IB-cuE-YI}i#Q zgm?^NS~|~@Hfs4$`j=|Oy91cnNF(OkjTM_6J5zI$A@jtBTy_%W$QT#aeF?0J`?Q_w z<@71fDoR)v3SW^@(BO{ha(HAY_aZZ4(17Wq(nJb!vGb72xRDA?D3Yu0Z?Ce{*)ah@ z7##3^=Jjl&WbonydFJL25&Fj0Xg&svpB}hHeyl1~i2|v;r zz$Of#tZ?FU<6b#gul7fTDSI(NVlV&-z$OoanZslP33-|TVSXAm&+P~Sl3hG-;r^k3 z#yh-gGC!sR@epZG$r4FL6|o#*aY;BuY6%ix>TBY3wi*2Cxo2u@V8!ssvLQJ)~5ouSs25Gr?GxiIk?)dxcZz`y;at5Xit zaQEt>r%z9l84=+ykC*1#aB@ld&NLX3;H#}SQ5N;*?N_jkufVtIa*R6_s)>9PDY(8R z=Cy;o9zT>P?ZRJhGS+8Y#E>wRx`CnrSMG72F_#&4?-0KyH3U#nm7Ff7jGqfvLcO>ABPEL5b#VH6kSFLx%b*+rI^Fu%7VI zSVf76>{c!+SR`n6U~aJ^KtOW_HVIuW`LO9veha2)Cux(v!M>SZq)J-ASe-5`BUr#0wUtcvahrR!) zQZ1*_*OB2AZ*Lu5Goc=LA>ZG8OA+DQ2N@HDTeid?Ppyrve$mEGR}A z;+Coi;b+5Je^4^IhGbTox=vA2lHY~#t2o(0Sr|OyL{DgD26ao-9%C;CiQ!t-4Q$Q+ot@e214AC+|WtuMT z3JHJ-X$QWA*aq0KdzaXwKxy~C6Fn( z*)QzDDRn$FX14$-UqlhYa=oegZjr2F|m-Od~nt(%#AR6Q?vWa7=Tq5u3OqMPbWN6 zH;dHG__JSbs*Sn4A=@g6t3)qDLjc6gM{^hH_ReFMo)Cn;CxqUNah4|@J&*%n#y99v z414_3IMH>MIHy@cSa_vgq1*dkt=dJA5nlirtsugTJC<@SI~YdCwufL)jcDjyVxD4 zXF%jAbSVSNEru;2moA2nGruOZ{SVy{zWqNNC83(qh)|iQ;~1vOU|e<07CNR)4fgqD zC$>LIEq`yYHzqsxlmuPX9`kN>E+HpRhe#5W>DxyV`+G>uqee-}P?XMh0dYY};BmR> zn_hQu9|e+2CEzm*+tLCSml!o4-9Lt_K?nk*s5zo8R%TZ^(HdWX-{t=qzy2Am!ND3y zNA5S}zrQovFI|Mv`y$^%8O_&S@z+e%Y*Ze1=YBLqfzDsL=lXd2Z16Y?C4~M05i3Tz zH*xeB4|5z_o3%|PyiR=h=c1covZf2b;37i0e8Glxuv<8c)os>l9k?mNZh3M=&wiFG z4ayY~ihBsfhDi11tGef=KU-%=2d!(QlY`G(BzaKlki`Y}20L6PzdeZxNLW>+ z@$;;51^;gOSrLt009uR`@7+GM8^YH!?e~Bx%anT6^-i_&Ma#FIS{id=!>f#_e#rN4 znFXIeGNtJ4qw`YzW6eCuh0Y^6DvyO={qC|bqI$Wm5v^qfss#-JlNz zRG(Ogt-DvmHP-{TC&x}dncb6N6J|%T>(GidSr`ow9aIOXQRDCxg()MD2@nqBTnD{9 zBmh}JYYHnWD1M$fXKLr`yE|ctE7ln|aOU!1yXoDz|8xHR!ARw_54Q=N_LLHAe?gQQ zypBIDq~F!qeBq&jo#d0X8}Y|@A$YO&8iwaOgX+B+Oc-Ftfr9{#1gFv%z$j;`x2<7Uay3+;FTc;h zDKt};18imZ%oUl>Tt66C&*j<-nwj#?^Ks_>alyPT|3q*WQLgf57^7-8}S4 zzy)2(y7*C({q$lW_aPE%s0X5M*s|AjNdH61O9sBWkI7#w9g+)v$CXU9{@086pdcyE z&^HoQ&|#_Jg>9XmeP{1l02ZlYOh)YJWwY|rPtT!ZB{b52L?dlCLHp>i92cK!g%E;R zLOc*q7ly~`JV5OkoBkxAf<;xS2nYyx0r>ne zs1uD*$K3}=Q=YUIqNI^CzJk%{<-4NTJfT53q7`@YQSu}Zf`Mblj?o^<3cGumm5`X^ zz$ie6ebk~V!H+EnkBcCY$!fD5qZStGCkYTH6{?vlP6kMWiHU|d67Y8J{bco==ujbT zX&?45gHhWt59gEORO@LSTmbA>HDe-!;YJW^&r=;6i=2-$n^tYItj)TC_e;>-SbOjh zK?@t@=@VbXH?50HBt(68gSF@KyK-U6>yDC;POuje&>gBTP56fCf;*FChrjFV0LH~< z6?D(p^=ds;=yZ!j6EUKSIZMuxIiT+gVy?mN;~=<&&I8g9PZ@2`2zQnoOfi{7Z}hUz z1+kUMlud>pn^%L}-%~}D`N=$;>z0diH?h5E#jms7aFn^O~b9S9s<|-V?Bp$u}SZK(=`3&yX?K z`=znn+TDm8lk_0@p8JNHq&jwImB`E*x^_$;H9s?e&gJns>i<#ifDa2pNPh&Xtd@qd zL*8-jy0as#ZT_M)S~{a%Xp_}7gQ(#|HnX!a#)`Nd9vP}u#b*E5Q3?D#f7Y7#6lfpi z454laPjG*+qt}83nK@B&dqK*00g^T zln@^i7GW1_kb+zh7mu}@)-+Bl(nZRuI9A^9vJ5xC)KIzKM6SG{d)0~qdo5Y;dPfK0O>epA&weIt{{ z{Yb__&Uy-l2ucj6d6N(ev=pJ!HI4n{`nt@~kjc1vg_W}+NEI;W5zE<((k#eLIlMwL zIDxi1$=X8qEF)g_BiVBzfoEn)x@yNc=2{x!8#cX1KABC(H znF-u%1l5=Z{m>NfrFh)9iIYM^^=?Wgu4XfHfD?eW=`?nD=J1tO0S1!tt_Kn}PMS*O zQETPV=@Sm97OagpcN+S$vZU#iskV&D&AW*fG{Slom$%kO_n-`nz_%t=rW!-4na^4G12} zt@)+fL(z};`?6qa2|LugvR(EFKUTCt25LvnOd+fZoSf{;Z+M_nVm~7IAJ22&HZxyI z^c|nl74j~ev6~oVB}HB8*JFh2&;Sb$NBmj_=f`S&soc%>IsA?xw?*U?bMC|f@ydh) zW`L-6>~n4mwi&yu8Eabrw&W}-qsxvBGk#QEv}AuRXZNNKh){7*NAw|Ey_-Xj{A74i@QR)ZGGci8%Iuaq=0+NhrEt zO#~DrLs!!xw)Ewjn={NkQedPV&<~pur3W0y%LK>knqp3n+$l^<&l5m3sDi^R5}i0F zIeMyh(l!DO|NPzXxjP%Lqnmr`CudERChwN|P1~epx)7a0-Ac{IFe6QP%N6Z&{ir6>;? z`5hU&)vnK)@0}mh-tS$P-tdG_fMx$EDuW@*+wGYs?E$$@&ApC;k>KO4yK8w-gQ``_P z5%5rOTcl+dp$eB`J>^^zSHwiKXXmRqKEm+0l|dF}V`m7@dR-6TL>vfH8=WG2ijUsQ z-FF-X4WY@=;gi&l)XZDIMc^CWD331ScCUz0X;a!w`t0e+&SSAOaK#mH+;sfv?zr}> zUN$2BWj#a!fy=rJnpINosdA~m5-j%sIA`>ZS98&D+-*&j4d38mcPc+)GT8p#TNcq z-l$zjQb~$V&4k>S-iyL=Sr zy@&CH;0GfDjql$fL(u}0RiK@VezhFO#gMbK8%H-%2-`r)Zpt5#xmj1hkz+#stB-RO zi*w@~A8%}1Z#;}~qCbG<|JR5(%md?~4{}&s=0OH0T%+GCdFNZwX9rGT6G{ho)`#Uv zIEb@E3P%0tK?$!PU$h8r|xXr3MD9i3;2twXv{@s98}B5_~ic zSj9|-Zi)6v8lY7DfqxT;ErN(~_QtPg&8vJLvCxq;QG2JPK2@6fmdX@yfbf^`1lB?i zHLK=w7xMu$@_Zc16Is*3zI7iV&Ig%eOB2&zp)7BQ*n6DcOoQ638uKo1UXVVfjKfd9 ze-e4Ed5p?oqlz0vsu4t_F2UlN8P_#Oooi7`d)tUB-nB?wd9!pRxA3RkM54#>0vou< zbh(&rlMV>5n0MLXOw~%Ly^clun*FkT*WFwzHwcbf-(T}2k)8z?(|%U5HS5dlE}`;1a(x#h$ZDsQ!&ba(s{g94* z9p&>}d|@wQk--G>cB!9DmSZ*hCkLX7Ipa4w==MH+NpIVJzY6}}hzXF|e~Ju<5yH*f zsra6Rua}0*kuoX*SWF07lE%=3Labee{Fsk|!cI_3y zBwzvGTtWvx8$Q2}LwYSH9;i`zRPuYeXiRyJ2~h#kA+ofTLW-4|!JO@E43&wHym=IK z7(SInLUM&2tdf+AcJaXIfWT{JOH>LX=`syT93dyWvExSwoJu*b|ZSF zYTgig8Zo8l2;{6EVYoD?tS{R3qyw}#O>k6J!rN*tqJ#N?ukx*C?A?LU(icvUFL*9; z8$=THv_4WYI=@nuAk1Y2E+IG}JBvY%X(WIsN^qcn_-Uja#0VDbEGF#mScNBHWBrg` z<=0(v2~68mk14f7Bi>tID#Md)N2b+Q${jM{FVC(hRT87vw+@<2@f*XcBuUl3se4ik_!T#0 zbaN$xEdp&+K7s=Oj@@wBl@|*D8_{XM`TpMo703htW0hHj4Ub5E2!VElfR(c}oo>eK z3$irpSZ*{d8eo&+++WOWS|$J?t=AB57?AwS=Pd6`fdfOZGU3@ zk!$yy(^j8ESR9bpW}XY9*`~u4rXJ?`R~^^+5v|`v_<&bG_dNdIV1wg%F48l~^B{rJ zAeBK>vKk?mV>Z=a3M)Qt1xj@W?Gkbl2&)Ofc&QJfl7Pkj20NlM ze%12X4&ybIH@WW|f_$ZHR5jeK-K}H|0j;t-6(o)C4IG{_gYM#;_Pc$N;Es+ROM6iQYK)Iml@M(jiC!^eYVPO9wJoay*lt2tH{8#R(lVQR;mWjwWQMKS+YkfX|89W}sNu zSQbAUpIa4lN-nBVc~S+{0B{=EV&uxe$w}rZ=OwF)?}=}l({qGR_#;FudEd?i{48t` zg${-JMaqWzqW!d6G_CUi&c^2K8*NJKN;dFXOjp52@Cq;0pWtkl`E;5l6f2VVc+rG7 zj}UV-2bc)Cr2aunHDcX04iH_sbzc&YSwAN#toa9yYtBs5^Cj+4j@JExSO=ELp(HOw zn|h0kYwpTJ14I)rjAxe=N9_n>IP&=xpmD>tm8mz-#+Z^Bny&4A@p8KM_<>GuI3H;L zU#a|`%GnF@k058HmLL&K4*|!KuqFu>9}WB+a!sUdz-KGMEF5w~snr;P<{sy6qo|Nei?;%@V zv?7U&kStMbyCgd%4N8il!%WWiV16!{;OOMOX%73a9@_%c&c*+%=o0TxNP71kd6T+V z!{M2?z>PUK8y1qov7anq^){OKhT%w*NE4M;&lEA8j#^JJ7GhsFv6(~&I>-X(5?npc zcBuVpy>O*d%*5X#NT{10iEVKzj5hIX#yE&~Kx2RsKfh8r#K8*DupIzw(IJAmWH7{0 z&;6Hr$&SHnDAgm|7w`p>{mTfJWFeKo4JDZTW=$mwEB^=p7mZ@!AI$x(y~bSs@d7{{ zUdCC;@)PsZSsGIwlM^|Rg@=W}(rkLpOBwJ4e@S#JhZjIb6+kOum^uR83y3Y+#vey4 zr5_cO*adMRmFA|60`&s{s|{^{_PRHn_Z&a7{#nDte)!`<`H45dUVIb@sQ?!>b|VlF zVlw5)RA|*KyZ6)1&i%e0g_j}$X(We%%`gcFCM>rllIb>I9dLa<`cw=*lPtonrfOO@ zVZ6;p5cW|cmZ=KjXHsRE!d!%}Jig+~@U$7BsV+sJ6ddQJD;14>BP8z99lQdR)y(IH_BuD z(Fg}D7l*`uI@TcEFzf45?XWBxrLuGC9B@Wapr~{4+LxZCT<4Bz)P}A{`-im4;r(QV zt0dHJaMFyZ`!S9J%O34XOTy3F*I*0D3U={b?0q|OIuYv=Sj05+KfPce69JeG@zYj1 zA}Bw?pq;0>D`0ue7E(Gw0w&ljrcSr^amr3GwA#JF>UKE-5g2>)qkrgELYCfV31MIa zCGtD{oeALT;jcYs3@04*@eg-5+s&*vB)%c(hdX2;f;Lpv9%{lW1j|}!I>tVclF3Da z7ZVm9I@|CM2P@o(nz5sx-tuIbY!RX3d82Z-mzNDO22{E7CyimxADdJDdMm97-AP)F znc7WcHRFW&oh;sXkt@UmL`{l|sn+U3a#d5^i+zU^sCE>B`3Tf32s=L+q0M115C1(O z8Xb=GHBtELyB@zCchd^=A#7*zXjJ5Zl6#2S>oKSJc$eO$s%gejf`g*E9mG@CrPiN} z4w;3!oHiq3hxdi%>6H``U#vMkJ}k$~O_&6Q)T#Pc;ADvaCxokTCBpVV)3D&|2Tnjw zO#~qnbzmcRG`vvGOxw&s0Ji9zQJ)0N5iW#>*-K*K?#{+3Ov^~9#G)Ma+w#4>pAI!_zq%HvB}bOQ{Z;~0XjV#d@Zbc$1n*8F^34r6(8{Y8ryg5$iqoJ?Q8Cw6 zna`h|p8X$(3~8csq|sw)umbt!-L0Ds_SP14;kY@btQP;QXCA{k=^m4sB`{7OwWnB= z2=Qq^^nBjQxu{t@J|=qtc*Pacd=%s69<5cWRU*Eh_ZJqn2-_)7_a9k_Y$w3TT<;l(5e7WBpDhjCLYWhcL)Z!m|=j{fJBu>qQkpz6p$Dh7!)qQ zTIMMn7XU5}4PA|^1uBJW9W)M(eX>QQi-)GS50#EshchmcJ9aB6vj3ewsZGNVDaAU^=)8YRXWgR$0o0kcWR-iHKtci1^x%jB(m< ztlT!CmsSiX8K^S6-r_SKp#P&;pLdi;`tS;oDvG;!58ah%~X-y=%V5r z4zu#F$e!J;JuGHP{*FRVKf!4gG3grh@U2z?y~o+ch2W^y$LHc>kHSXHXe`=Wsbo3g zEg?osfO>kG^5mXbw2xrq`b852s}~5&q^NZW0kQ<7i|5>9nvq`zPQNtb${lhv_WC1_ z>Vi-7#DrDg+8@~;)&VKHnO2);(q>cJQ5;A)xKkQ6A{1H>3s3A-O^#5DeMZ0lG-$#| zleJjh$Oiv931s1CiT{7>{!h^Q?xK>Q6og{b`RQYclr)GV^o59@+%gt>JvDxte|0}p zxFNLiOP&qCBS1wAgQH4zQ;nY3+x2IIm}q3`J9pg3ITR(Q`r>*i=;)6aB^8*WzI|bK zgR}BX|KXiO#3fsV%*fYnA*~ch3PDQAHS@Fqm(iA2^>ol1d!KH$rV|-L8O@tbX_Jg= zBOl*omcm=(AA^Gq$8xJf8)OZ~vEadXGoV$dO%b5zNJ3b%rm51+%OstYU5H<`KEv5Rqt>5!BT4uMl}q zPZ6y4VOq-^s!)7{lNQYD%{V^xP$<8wNYz713N-4dU?UhA$#KRg zr}jkvN^q^(?tf?tv9t%%PEjB9Nm^t?E4bo#tb88^6OHP#k37r8Wzgxjrv~Bg@>I2t zB_kFeyaVT*pSIn&?>n>u_i?fdm>@+>wt>@rFzpB?lsjk;0vT+U8TY`$Zen*SIdO5M z`XcR7hg>HID)C7LaNsO6DYDviGPTK^0OGoMHM(Ms10mon#ur8H=jEqi$7VXeQ6y9LJ?z7sM1v;fL z3@8VDxrVjZK-rMyCU?vn_JrvGd4Ag_X_m#8xNtvYNu z-PvYS(}o|!t4YMjdU=NGO&=J(xj^q6{8Z|Y1f9LoTCm6aZO@6jftHq&LGmXusN_w# zP|*vD;jl18s{~4#m?}snS7H#i&JcA0aKv~24zn51j%1gYmlxb2+xZ&}MJHtk$*>Gg_#p(Kh%p>*^*?QzY@zWM=`1iN04f*rgggIHP0v4QGWjiCDZ zkByn~nIr47$`zXg4BSC8=VxG)UV|?2`Fh;ZO&m-@nxjJ?PhQGyY?b*QUIwryfo32T z+cN|LQu8lZ%(K2mlEnAQP30=Qf+$X;;I*Uu&Ox?AW#W(=3fagDzH_OgwBO!}=x9pEoF>Ds<3IaVZiW@ayuJ1&<#nm_$5M)GvaL4*Dg(KaE@hmQZWg=c?E~Dl zHs#hoq;UF~jd@jIKcZmtWO(Y6&|E4m1oh(cwmX!DuA^W6gA@PX+k5{*vL6Dv;Oa@0 z+&^yX7}lim;b5+I+v>q5_x#{+OIDm(4Uyw= zJ-QKdnXcOYiN6PVa;YoZq4mk;1&U&#WxG1i`{~i4jq5!BqdU^JcE@MrhsEWdIj(Q# zh}KH`M6ObKZoWwCQ!D2(vB%pT$0*@?j9fkg1fsr!9BRU zLvVMu;O(Wwkysem?1HUBwY+CD^> zYcz@Q?xUJP@xM{gz5}F>f+)Lu0T%p(3TJ|;{_H&X;(1ca71A?yE5zB32X;N6>vPyj zP#-XXj6yG8A6~4Sw+f2#b|3tWwNl9KQ#N8Kg0R3sYs>Y1=xJ%U+q;;N`TCKrs$wg< zdFy7KP_9#{UE9Jn1W@JUnC==r9`d&;zZGLmgInQzvwP|NoeXDxqW>!$7W`!J0s44{L`AB^Lld-VF`gV_FOyGa2t-!SObIH@R@FnPNrzQv7!(Wj`ycB{ z#h=AU0-quSXo|ApGcyFdp4LF)LhVitmp3I||31zC$!7wy$k3|VG~~yBin(V|TcyN1 z4a#w!PJKu&*OcZowsuA&S8nhlBhVIQHM1LWOvvIII=_Pd8HjeVJB(||=Bx2v@XCMg z9=K2AnF&dL6$-y8*CRm-uv#)z3kVtfOX^)Q@4xrp`eV97iOd!byr=UH&WDHf|N8R( zJZ^1JlCj2Uqm2mRI|&Gyq26t7>c2byi#_V4Pb~38 zJcU-VuKaI3{!eAIwd#4rX8CQR~Jae{1W^}y3hn^Nr|{B0-oqG{!p8bhsi$uNQ) zxa{pYR5D+BeBCPKYgx&dICJrEC>5O(*s|u_yBdrMy|j`lo5an_t;q?brK+nJk311> zbG?&e>=m4PU$``jDqm%xe<-B?6WBeWAt4`95u(NEFzRDN|K*%zr|*jm6yy|BZ&{ZKIOFd<=I0WKh3CWOYKkNU8id?B5VveT zn739ZJ{S|*d@rpTsE9VuYI?gVa#A?B`v%B;^gKsV0fCIaKkc`o09$iy8&@Y>AOlhF zYT&AM;~jU~Z631cn~9lhXN2rGE9s4uty0!7D@ry#l|nC*@j};4>N#;pFy zfp}|iYPrK=FS?B_)9Jp~pdv_LKaarLiY4w03()`Rl3RxbPRjveb8>h7wWBlD%X#PQ z;@ZOJ-)G^c7`mmUb;H?4nb4(YE%EU%C1p-aUR?vydS5S?ULb2=ipQJEK2C(&FhLhM z-Lo*s;F??1O4^BO8WR(_9*628OR_*t@-7hQ`L8-{$Kk0oECnGbiY-YBN6I`=`d&;!+HXfSRo6_cI^rwFB z$^||0>!bd~A>;!XDGRSU1S?7rz8MY>%pCHo;`=aKp#E52kk~DskJeZz7|ekjQW8Th z97i*qVLtEdQ6oBw=$GDM1^8;F)AB<8=HJu&*ZDt%g054_%p_pOK+w`*l$2;2o5SL0 zZedk&VXMby4l$Rc3~A7849%1~AFKCacQu8t{u?eLkko)Wy_Ftk2Ux{ymVg6RG2&3H znfViVHoqGSV5`qA%fE##Y}{u9k6v$Ge)SO?*$mFr3B9a+d;$hDL;jwa@CaqKs+r%W zK62@FuKl^i*nULdP0J9;{GKW4z1~a6XP8aIrq(lBi%}wMmh>r9Z*(p?Bdn2g;UlF0$RPJ%E_~L)(&_seMkC*q^o(#rZU+ z{I1%+KLwMLG)=?l5Q{lSRkVVKopK+o-N2`I4dCJqR_lXGIN%}#=y4Xs>>q+T$E9Ra z>^_|;vG&k>&I3|^`VApy`f(3aAj188)>>YvKCwgA_x0o)*)7key7Tgfooc7`m(4bh z>m>Ql;~VSG$j2JhD!K42TV36$wVZoLXia*1OXKA@9)nS_Dt65#)_K7R8!v0x0@z^< z*35W!@BZAY<#3tIPVGw+6x(=R&0)ZwwdcydP(jW&iY03G5AD%`Q1B-fsTHnbGo#wf{HVa7WBSonL6Vzxh^!TmU_X90acP!bAyiQre$@tZz0!;(8uO!mmPS@${@ATxv8$#3A| zYhS1AIk$3VS!Za+o|GD_5A`d8x0Z+SkW-nBZ4p6vD;FD`!rleG?V6^|fl`heEzDlo zl-U5E-}RN-(^d@q&)j9^tiUx#3KD+DJ4LKeZ&7Gm5-g(DWkYOeJ8E;C!Rz71;GNqX z8-BQ>FJ8a6C`s04Mf2#3bP*#ua^gGd1>dYh$Q}L1kae$#d=Ar@*2&t+H~W#jMLzC> zw*Qy5|9>wa;y@tcSx0kI3xHA?X>|A@TPTCkWuv$! zihsH9jsH?)O|g>MvYHJ#8<=&Rj3wFMv|QvLB~WEZWlo@5-4}oY3vTv%lDO*<(S+t@I{%)fY@iBFN+ps$IvpQ=+oAj1{3Y499(A^=%asMCRN@onF>8Mu5WSXC zT^R#1tpTPfj8mN6aNDe@+w z_&Rx7j7>24bmYgOsM@9%-EW`XJNItgTQr%v#?#GgOR&U8n?PZY133G}V`dZXVC(sf zw+Noiop$N)XLnNcw%*-gU)wfT;J?$3_bkYYY&PIW>C&EbtFG@g0Tg9SQ`sDj&ueSk z>Ya}O8(B$4|0743UU{8$qbeih&fjND|9&aa>;kDRBge1N_m(*!a3dmosAwh@c%HPr z2fy3*%^`xTB`~dz|5M44+D47Oc8ftTyUE-A6@?odtW7o1ZKLX^iWwvzslWdp6lAHn z+5B@hgjhdADS<3zz)&3SsmM$CXLq9>OD`|(Q_BF@k{Y9*yaoN2yNz(C{x8>*?)j}_ z7^?J7_oqRMx>~~*rbS@l7ceB4l&Hmm^t{ESs2Yvo(0!IfhBiPaH$G>AbOsc~ss!BZ z?y$u6;(`9Lwq($(Y+M#6%xC-d92W-&Htmj(VIRkLNMQvhrR-&QUq2RiUqo4TItp26 zt*%XmersCI{b|0~b7$L{qN??D%cu%L70GM4=9D^kA^&80ubKC9A|hanN@U-7w#@Bw zuyo>nHc8=ACQ2iOE!((#U#|Z&NJ`Q`OA@UkBS8N>gSEn7ZHORbm?{0`o|P4_`DQ-m zj19Ty9o8LwT$n)tk-qgC3GgpkgY9CbcZQmY*VI!RAo~t9r{{jcFnlTvg4d#2!W7kZ zkQ~zNx^rQY#>r2TpEkq!wZUv=TtS^U#he*IdcAG7wa?zb*UKRF^`(#+R|H zD@sFl@bfjF$mhmAt(f9+2wMJPLa~Ha0?~}mCKXvgZ@1?nb*Qys6?}=%4IeR#@HqDU zA6nx8b0l^7NM=OV0f*yE7U0k~?+w432@L41cH3}(l#NC>9b6V#EG;e2uq`iJ7AjMHyT`r?pUfx)PE4Gg{JF4r(V zU|k$m#OR{V`iMokXmG-g5Tq2ZT{$zX|>>@R3ht8XG)cg01s4m0g#R3CE@+o5$y(-~Qy> zx~VcdR-zAr4E3D^hC>~6A^j!hZoKyaR|Y(1Qr*%g=U~X>@98(RuVU?96pBXf3n7F9 z=Lw6L%7hK5)4yw`IYiQTk(n~PCyANk6?G=HOz3>Te4n&|WIF5+dI751zmlQ~3L3uc z8wgp(k;DP{H@H`{Zk4yRzZ>d?p)`RmgTatz4^(spV+d;PBmiWwFnvOkG79QEX923l zLE;gc274a5#AU~M-guK*CV%BDTky?&Gd+zb=iK3)aA?03Tm!f-|2(;63X8>8l( zHHthIQ9_y*^l+@STaPZW2}dNTY0*u#TxQRQkZRKN zTtp+fRq<6}qZ*c^quq-H6altH*dxpabq+{z+AqkR1G9jNakFND66h+ z>}V3yc?=RXu=%J9+qIglT*|&`a*We7<{{s!CQ@mWoDOmImtst5HTzNsu5{z=O*3$d z><><^kkh!GYmXR3;0-H)tG4*^63@!y?GO$Q{Fjp5#zw#OVKv#;nHlcyA2*M}!7x-e zZ4C{)^GtI_wz$pL5H1N@3bMi*9T%&c4;zHV1}x&Ix+77Rp*-%8CLmq$2KnQgP6>IZ ztI?R>t3H8B7|*F=%sgYwCE^AN^=LlLgqZ##xKW}ni7<~C(qsOA6v)RCkC z*6qfe3syc4X*VotNCfk3hugB5u5;cjP?=S zUg`tBs3-B+uV)c(sSaRAxOT!k4_`k)ghYU55nsz?p1?Qt?or`W7}r`pFSqnhOCW@a z#<#%mE66i1EEo`2M$ESyN$6MG4Y8k39O@fIl^THbjoCi|H6Ga)i`2g!fLCf2jiOmW z;tjh<3z~w)s7{lYfBVQ>vk?{Z?+JOlu&RQ^x=N>%1m_)kuR()NjMj1-{aw%V$uC#U zURjzp-C&O%m)Tr?^kx~-^Hzj` zU&A!lJSkhM3B<>cpP})(2+#Mb#lG&)Sk=|&7`LVr#iKr-^F-{0zq!5on z5lyr;IfH_9Su4zI8n@;4&A4mB$6-VGOWM|2cFNIazlk2C+94LWWB7J&(#^x3)NB(^ zqpA=n5)$!AI)JMxCUuB&Kk4q^G&IeENi1SK81)v|>Td&W4CjC%$dvoDYsAktY?dhc zC&H%RJA!&G@6KWwpL5*zKT#R~IFY=@u^(#I+aR%+`3h1t&}pK2 zmLV;WQx=}H*z|sP74!7RsEyZ{aXjO}#n?yPkiZ`JJ)M`+{FA~gjlWGsSHFdc+`!f0 zDfIg$_SljhWzaOx(h;&+dGZc+S$<*NM6GD+@_idYq1xEe4 z=$17!?EE;aL>^YtyI;SS-2)RJhh`?GQyw}3+W$GG-vvxA-~E%&Yhx)$@I$_C3I7!&hGp-`3m}Wyf^of^FMK&2Lh!lUX=1K{PQ7oh z)B=+9`|Jc#vRC0%tOh=;31MXhWNb+0g9 z3x!(D2{;_Ua~kz*?O~9D>lWgwv_@*y$Sovf-Qp=8xHjY{q~@cn1g`t5Uckzwvx()J zP~(M3U65@U3?bqM68M%R+Rmy*yJhpaX8>RkZU15W^w-NS>BuSWxmmO&j?Kx}j@cYI z($faakR)W3A*T5TL#vvX>MZeZ<7h(X-R}dMWn!DzxW@P_%x?A91+;!&;Z`AM1v-jb@cR(YLMV2Q#0z zy08&cDZ7ggL3w!5JD!xuR)s0No zP!Iwjmc|Tgp%I2l2j)z6?CQei7ei!XFywGyeYr`yaA4`l%?(=f$D{3Di1qkSGGGDgjl^uH*a%uYVE^odY; z`VSgFCW6H~Xs?RrM1HNisp_OG75DO*hr$OOP z1RxW|0LUns=kqd@6qu+1SR{?V9FMr+t5<|&=N#0`$^^2+CtVpnr%DO%1?>OB1s^ZE zmXz1bEK#gY5D<@|>kp<3@1r^SdXT>4DHDHGw*Pq?|AhG8n^PHnjpT@ol9i0N1_L&J z;7Rl#YVBKCI8x=(4xYh7^Tz|bx!_W%&tbmm(t1mHUCg$c8);uml2eXZ-Uq(Pbq+t#8^vPG9i~fy}okB^ZKDX+55F-~w z-a~*G18hVc0Sj!#8X?yLWgHYiZ{c)%s}qMjvl<8s6`v3+TEuFQT*IqzHA2-WEJ%F= zMuvaMvKVznCIZO@{eFlS^5bKWNlPag!8fSG+S*mG=00t(3%tY(Mo*c8mQX`wkZ+p5 z42o>vH*CC`ZwSbb5to@Hnkr=E%)G0qb#%!~vPC>==Sw8T9M5Q-ElmvngX3kB>rl5cCdaaz=Glaori=XhuBH($(K_1-SI zjD#9`co8tsv85#G49JO0+A90aJ|D@z?SNj`Z>Xf1k{C_y(@qfwVtc(TrZ++^(y6-- z+~>_2r3QLi+EeZASf0!p7?QpgH~)?k9ggFHN8(~}2QqlNQ;~(Lut`(56Gjv}ZjF}F zV9_PDdTeC68CQ>7y5q%i12Hl&SbGKSEow_`6WG$w{`3z^XAx+l!G}^O9Gd5%l~Jo% zq<9sR0!#NTnMjpIYqAfVosR^wli_#tdHukSS?LuO$kz zD8A`>C6~8SZzni=GSd&rGg8U%8skmH|EMI;^w?U?JbODZNB6q<(IeS0eAQGdnG1}e zA0xEHu4H@r98ZLJhbUGibqNdn@*6n$!yQLf^@LyNMQM-$jqx_AtmFK(`&$8sE8VY? z@SpYMJ$!KauV^6nNq~$@VmtwdL;Zwzm7QBBYz4M`d*!@$>d<@iTz;jbAVd{fG@_03Ktr4h+Ynpy9`$r;qcxT= zb>Q<(o`n}@dClw1ZQyD^Z{|48S_nuFRvBI9!9P9uK|TibAgn!OYw#7lDb|>WXo&Dz zkm}LC&=J=%8t*vb_e+hYnswnjlC72*obM&fhx?7tX{j3H%~NUD%W|2Hpi_bQvg%!b z!_(J!DSe~EE=N8iEvi=y=dsy@C;`fA86%@#_Tq%=6&510#U&%yfE{D%tfIM0Nz3Xx zolfwZ3ipx(cpzT4Xa}j;r36<{T%~W(z}Ypd`f=Xy$fc+?X~@X1IylZ9Q3;OTUoy-V zw8;GUx@xP2B1aX+5Ls-U#2c5@+4TsX{s-~M`MW=J^}iJT*5~MewP+hHgqy>s5@&(b zgd(Mr{$Bggm(PN0cRe;+E5%F3u51K^>e=5J1=oXkV_*K_bWwRC&Rk3BuWHnJdvMo( zQPzefS1}9ADWPE>^+x?L3O~*I`*Jk?f_4MGy12kjsnNu=-$QlI| zwd>0`G`{ReLBzfU!g*jb>@;;>r)H@l|6kPn|3%g?SuWOh{v%flQv! zx>7cY_!|0mg&T6e`@umT;kU>fLdGrRI`soza`6z?Y|Bt4G!m!%7Z`EVAlDqqaAQc! zfi!s0DFS(ZRk%Z~4%1-@q+?YmelWVjsbiK+6x=iqwBP;|)7K zQFhfdDX}$ip%xdVfZVLejPXc}15lG7%MVr6j7+77Ynfe^@PTB&Fsxd|WrZ?1&d{Y@ z;Yvq`t}km3iy!95^p=P=@T`*_`Gpc%;-(LnDdVy_j=l@DyVsbQRZ!x3T32x@58{vu zRXmaxN@r!_S)*V!NHAyu!vtZVkx7t$$*qRyg?9um(R3$VoIxW0#&$f3V1ZSZ3v=q_ zSET5g#i>Fsjh%eUm&ufiX+pk9oMM)LPsPP{=>#@+I*jP0ZtWwPV4a*cp4I4|SX08V zzhO7hXJuxJPU2ieyGNZ~fWYMM1r=y;f)xC}ltNz`OyJ8E)pPlFb%fi{V$|OY&^p;H zbl@yDs<_m+W=MK=K2X*ijl(~_@(1v9Wy&uzg$VUCu9aC_O{qpP0!$$BfFb2vr0oUr z4xkwoN_G?5r4nyEU6KelTG%u&-kfJ+vlRQ|Y%HA#vH4tw=+FmNr~!&A z?6rGx?fWXUw1U?u~kXi(!dPKnVJ);ow+1|l?jCzJ_(;?Bm9 z(J_(vtq;IoCzAuN2?A_i-UqO=`A&2K{$B$~#C=SGC9-VX9&K%GD zLG8K$6$D9CPHZIZ>Un#X)`{q(!1TdNvYCdSE@)%Z3%sFwY@>pPApfo}3RtM|rqkwg z_uobX=d(yC&8$Q+l$*x3KDV|2pk&~)L?t=rqXX!n|4h2Z?-WErnd7>&)ai1a`ktKG zg_0fCzoJ0Pmn$E1`X?_1^#fLLTOc@xnK?`dD9Kxg47F0Os4O`;4ZUw82!sYznnZK+ z=e{0`N~BR*q>>W^Vf8#E>EP4hzOZ;J>IFn4hUtjuJ*u7#V~2QT%0Mx~h{zg&_3-NN z7+iCa7AOfXyY(_YYrQhcckw_T^I+5B?@zouk5oJCQh64o?~~?IwMg`FxyR&{VWXr3 zTf(vy@vE@)+SN&h|-^`dY}b29jY*A6pSOQ0BfFV95Mi^D568SR%vELyx`Y9SUemvxcQI>H%TVg zRkE1a0ETJ~LxCym70fy6EIXA>wT4%ieokPi7sf2ZmAh63?YbSJF$!)9R0wMh)y~&W zXlbjqVif^2R3W#nb=c0!>_R(61uq?A`%atCL}5@2jyM)K_t0sT3%rmwAW<4LkYN_4r)c;(Yc{HB{DMPh0!GR0G5T8x` zdeUAF8U9t+c@+qN7hdlkIEdF@MU(gD`Ee(87A(d3lEE87t_unZmXXFuq z{_e9FCkv^43KDmlf~(BCUZO$Y6{>en*HIH8&|w>Wf9-vUSc5ln#sHgLiDU}lt7G9$ z;PAJQSA+(yv%rQ)!nKEh}GD(76S>uWBP3ILiyWpCHjM9uX=2 zceHTe`#~rOoCp(e7IY2S^y8OV#v61*1)abAMdu8W8mX-YEJYFxZh{m*Qgy}XP?WsYCr3<)1WbNR$IWSGzea#@U|CV^YL@OtkN z2Tby`Yf?jmY&=~<-lYryFm=_>_rHkw(CTaRDX(UaK0YOFPJkvSr8*nRAUlBp@dvAM zY2dAo@U>9E?%N3;pEUfn8%SSXiuH1)2@|l;coccqLU_kD;OyL-r(Z`D8dKHqFenO4 zirIY+cCeq;4?0e5Hu-Fa!pxs;v!WmfMj~b_?p>bzl#nlWB2u6cmPycw!z73gp1bv9 ztRYfsE6@o(bMs|GOy>b~lFGylNKl1>bUPpeoaUj@=J4?eb?Oy%q;70KU0 zws~|UB2dLO%nPDZB^*=+IN}ze2%JJW^^Ld-%XgmPF~^yY)rk0h@(@@f5$4G`i(Yo) zO|Rc8(xVtj(s8?2p&^tj$H~%bNNokAAmMNIS*G6WE!Q=@vq)+HWgD9;sUmQ)#_;Eo zC9}%*lY!npFrLmPA}GSR%~5DOaQ!7zK~+eeVBbYeU>RYE{iMK4o2`x2#l-gZP8;0W zrebgAQr6k4U9m%kjG;f-?4NP<3m;bYswueCmpT4T#Q)F{7pr7sMI|`%v%n?m-X~V& zRXO!DPQ2)K-$QA##MQ{V!9OJ-FS}{iXzihXsPp{?eQDnG!$6wffMAqf$MneWX^V=EX|2oum9j`_GY<8L?<&*NQ1PZDe z@~oNzSrg$imLWNAj9ewsa)U?i-!Vrsu8ea`GkETuSgx{gklD;fA9%h$!5yCEv}%?? z$+x=dw1cD^pu{hyT;Rg9nN=SRnwUhi@FqR%9z0Za-=Xxb5C-GKiy+!Xa`O6T^$__M zqXt{kIA-?bl zrih;+UKAE4Af&GgUIK9VsU8oZmxct!elKGPPvcP4KjN~bWvK?rjSG1OAYc(etI}Q# z^V6W4c9$Kwr`7iWj=Q0xov1eSj+M`&{4Kxwr;YDrvF!HU^z?3oV;NpQ^Q4=2N|zEw z050(UTEo1$PFe!M#mddQCgS@pZw}M~-7C3h`Yzo5+~k=OTa_hH*2k7E(crGMk2ur0 zHTV>fJy3dc9QO)t{HK$y>&Ay`~j<{CCQ#JS|$bR^+8B0Mgfa&ho1d(Y<$6mB}0lOq! zcZomfciXT)!;IZuB^)pcC-jRiRGYQqzU}|XybRwcwvd!^L5ORgRq=*x7_mD;oCI9E zW@7V>n?dY>yNAR9x1-h=|ISY!7Ls>_`YUtRJ_Cil>N_7jwD(8ZNB zo7S4bvZaz19A;h8k?xNeqadVj6MRoO?`;!sC?lt{h5!;D<34=LUq{N!rr_rc7@ylE zdY{X6NyBagmFcA&;JhtR|8X^)nPut9TJ~h?A4D#T+qWbV-)drYesk`Y!z;$;o;xud-5RW^Ja{=9@(HL zdE@d0C`YFVKiDyZLh5GW%tAmCBGL6So!AYF<8YDrqucr-RWXtk(NdGCXtkb(G84A= z!ZyM35E2tiFKL9LF+B}@yF6!vl=gZ6dYm&INyK%+P#My`*6mYcP*ASNPAEV&+C!$O z7?kR63uGR_AZ0f#`mEM#(#ch2{mh0H5qXIU(dP@>_F*s59$piF7`cndccSju1PmF~ zfCcjn*y<1KNRpG7;YDAQPOrWx$}eOL4MJjMI?*UXC-JO30Aqj@i3Gw z=}H|t3AEd_&V?r5sn>^dqB8=^#?Ni6(a)?YtcMdgiP9+xlZ{l4k<4K^IUG}G0mxwQ5= z2fvffDfm8b+PM}#L$v$FZ+GW5WeYSCEhVp^ITYO%9V$(;$He(*$Zi`Z_cB}MUf4Zu zbI0Z|PU>RybBp+Qo5eaAsI#&%0%X4$1&ex|arK{9%(IS+<*orNcDkmKySf_(V@ZVq zp7cJmL`ezwv&iUbxo{M-#3ex())JEy`K|^?Pfw7>(DdN_*c% zSUNpt*JIzB1mqVi`Pw^uyun+Rs2+c+rei#-3Nmzv1j ztz?13Z!7=u)M$l;Tzo_#WAlHCn>5^yxj1ZUQSDvbufSqOeVrzAq`INdY`0h@{^}_A zhBP`X>d1c2TkY}bYy|?IWkudJSN7R{mVS@)`QB7p(;N~PS~Z~fN;c*_Bp?8J`@gPnDyAZjZ7&l z&bU$-Z6wc3N%)vqYiE)MGjkd{jhf=q$14IOLO}_qObGp-9Sv5!mZQ(;O-w30>;^5| z(H1zbt8A%OkfqNGki55v5%_AJA|WvvOpd%{csV*W`-?_g7Eq{%PIVevvOO0E@G||I z_9P++oT}=g7c&^x4QYL@lItHv9{f8>-(pCE{<6c8!DI)v+Y zyj=@=fp-Y@*esaw{N9wnP7|FL6cI|b+sE`s)fg|a;IdrQ@a6w#0l+Ajv)3H~uyU#1 zZdoT{H?-2HfM4|S@g0BY=+>5h`!ga?s|@%vKpoN2)x@StL&jDEE@K4kVp z>JQ^|1aOvim~&CrjK;Rc0>_E zi{zDm$~*6tkvq0JNo1F2M*b-S+Y0~uWfO6V7;2QH2uKe>2tY#Y|_)}MDfTv=Jhm1*xQ1S6BH`l z4Lh?T50OE5CwETMyvVGxBG&*&!F&Ll^-DT^LTZI17y8H%s1^F8pJ@u?IhBo-(7^)s zX(s1#{(CGiF5`+?Rh3=D>J!wuaB?*2=LhQ53Qo=)@t@-fY}l%-k6vuob6meyqEye} z*@x?`1!6;GG(PYwDJ5*GR16XPI6v-%k zpXBe=sp_DZ^XP#o6V!jW$zfbS*KW+0n0*uTFMvdpt^8Ey$FM~D%C$<}MZV207)VbQ zHzI{$OcrGktlr|mm0J-fjE^+dNcy9U)odN!GEA55liS?>UWLW*F#~crNo^X&jMoYa zwcU26pZl_I=dniDUSNUW&w!OradBsAV2gz8A2tILEHrim#POOsNfQBc1T;4Z49k5? z^}2P~;?zxr)iP7s{;kF($$2Ytr|G%62<}d&1S#;PSw_sH;m#i~RR`S6FZ7Sl_yWj& zcO_|Xn|orycM1YASbUI&==Y8=iu-GEucm{wNP4l4=Qz#mT)@Szf4(kzO-1V=`uS_5 zJl%%HJZ9kTXe9EWMmiVhXvuTg$K#Hd4LF=~=tD~hiY>D*p75@?r-w%!mVAvZ9s9Eo ztq+5*gl1JOTlxt##Prw0K9Iz8xf}%~%}!8Db!5HS&uAV!ixQbw!y%JsU&Y7-vGb0y z&29oubuf$V(=Wu#uP)E1Zc&&ZWO&|$jsNJ~KU(Ds!LuS*;*}j)Z0R`X)?kPpg5?}! zx7t6Fagn^ z-PL}zM$ho|Tkf@ZMe`Ah!OF*ikLN4XuTVU*3o$%$j47g`df|s&Z&jTlbWk(M>1$>= z2EP~HGqX5p9bA3JZN3~QO)q;{J{Eu}1+pqZv0g=I53de0M7hS{Szhv6n*EkB9Tpl{ z#kvqrT$lg3E9R)Ft$83eyvIU&eTi=Fv-Ob-_W{7G=8zC1PAWOM*aBSb=Pmqx{~KZk z&X3VJ)6_;Z1d)00K#n?(%cs*pDzeS`IX%Oup|MNj;m#(Vgui~FXqH$<3N(ds1Z4(G zd@6FX5$Z?PoRwdfb$I?>fuqEpHPVjpWqN31u0cC8>t(eB340%Dm5>~?;6;es9>rU8 z7L&W&EO5_)H<8uU4fMMk$ol}d;rJ*`8WhK0+~J>gRq@4^Xe29!!<`U{xQiXd&2IV5 zwx4$?B^8(t-P!V1%{X$~c@`nmTy%Ed>8&7;d5>;4Ak>5%`9gupORe6&>@E-IQTZ$V zyucC5IXIpR9sny`Tdb1$x1eHl0jf1GhQ0U4cfa2MH2vR;Q}iM04=4UXT^~MIgbDUT zGEunmt2P$kg=5BCKW(M^;&aV9!_G0!fVahKyTozqpGe@zq8rRUD#kM!ki94`lJ$Db zH{{zPwa%~f7YRWSHuYU2)!fF#>=5(7fwRZb0D(0;@#G2Rsj?SMs!8maHyjFPB0^18 zncbmkm{LhgNZ;*Oc6kCO=(w8oG>m8Q98fNF`&1q(Oy6W4dT@@loqn?QyHrp^#CO>t z`=nLhvsR`r(eKIHgjq#m=&&tD(csVv#W;dHfTOp!zol8IdB;>Ui9GDvOw4QL3ICW- z-r3D$k&6}MOqCT!gz0=!%J4YWtQW^gidd;M;u=60TbDTxbNA8(`4Ry1pePQqXbm88 z6mWkj0#ZH8S+>HM*^_ zxn42XO)UU*@SdvArQJL+tM8{_h59alVO0#t)3Jtp(ZY*$h2a#k0-3AUwOe`BU|;wr zom3Ul@4HauiBk~D`+|c=QS=s#t2GuV~lk_dh&;QyB(Uj23`(DC=vd zD?@UeoiOYFdSbkeD*RqOLj2ZpmT3EEG#_&x*5Ep_`-XOFEi$$4{2}k*JLYvw3m;O& zi~vKUAEsCdK<35o9oH$7vs=7B-v26f90!lPL<#Fts_2VO!{53OZXAy>I9m2>?v^#7vq1_QRx z->4$y#d1F&<)S9kVz#x3`uhlwpL8Ec)qI$1E}_|&{@o&@t;4*ryx+yMjF-2|aoLMj zhcq(W>?~mRX!0Lwv=0~gqld#@tG`L5wnqP*hez#N=3UZ#V#`U& zHs^`!I}4_G8Jo4;U%CK$j(>|bb%Y+;rPBE=iS<;9eijeTRkfTG%_|HVz{gS)! z!0$}Bk(ZLsiT5kI30QW29@J&TU>xxaXNbR(+`Aj=eTN?b6+{Y0sC>`iQ)^ww&LR)@ z4O^=FA@pnu!i)Nih9}7^@AK3FYX41{{Gt3~WnoInr(F|_{xa%$2=>qyJ2(@a^49C2 zWgp29E|cy*Zo*%HW(#T5g+q9vjuD;_y6287?fyus(Hd6rY|}to~|wCAdd$d%B0+WBqC8tD{ePu$7$=|GysYlAv1zJH!6Uf#7>UNh3railSN8URUs`ru(uVdsy-J!vXq> zGHS2R((YJ{4*3oJ>55_r%)XL%p)Xz-;2^}4>RW|Ac_X~EV@&w2xFh4eF4l>Sr-h{) zD1$nkhUv~GPsgmLlZLHS6HFt#KuoV3@w7mWFLUu>jjX-EUK4!lwnXs5cZr>gVR^$$ zCN2hGsJupjS1{fAQP4Fw5nXcTDw#Df<`A<`^{08k zniu>ilsF?;n>@{L`1WLhho^fN>vym^x2AkZq((ZjRK)vl<>5YXti zG;UF0Gq%y|2-c38GIkDI{LcPrm$~-?X`xzl`Of<&;-=g5~pgr5_yw!N1>F64g&1lRuMcORH5C)_36YenV%( z8`Fbt5Oy_QEW}2K_9kOXm#rb#uQTG`Rm$pA9Uf0|8 zA#Kv%rolbH(VKSPdx?#*b!6}5C%O8SK87T@k%7u;w>1ktw;~VtGe=3er8gv2^KQ$4 zdY>6nh+W^-Wa`rRnTIcL7*j$3-=-$Xv295ySI|~sa)fR7YcLy9*xZzymv1tDCz9mK z-oy@1l(RP*Z7@MlZ!s?+o@COeJ^LDqn7q1KHRp(H{ah{z@)^IML+C`7adux%9uw`K zgjQmH*U1u~8x`*B&i$;lk+-^bjhTby0nZ_SJjZxssKq(ZE7B9Nw?T6Q{|Og=7G12C zh@G5aW{VAFNFYe=*eQQi_`jO>AqbxWEx-hFpM||W<_T2LvpQQ$^PoR)zLCs4#u~%Ryo@(gv3J zPzDp6YfD@uP0M~tO|ZM)jx@R-aeH$IAp1*u5*n?j#H{s6LQ7C=s;>gL9VixzzOWp- zeRY`6t>N4v35jbjiJpk@V_j85C#c2?eVKmx6j(vXUxDp1=>V)(=rzx6%LgoCB21fMmvXM^RUuQHe|Cn@=R2b&SXP}MQe!;Py0`-QDoHX}>? zQ*8hf!ye*}72;SFSy%*4>vqTM(qG}7pTS(L=$Q=w8XU%Z`!BWT8lTuTeAqc~5lqVYV zB^1QIIV4F=YFN*39e{VlWYtMD8Fel0EFBFCd%Z**l7RZD!ENvhONRB>DS-PFB783& z{Eq~_*kbzc1ImLCC)yIYs)9M1@9aeh2Y6GemI*Ip5EM{*OW~gKz=n$O9!}v*;EX}D zxmf9{eoj)+QMCJ7y(r`l=w_Y*wj>EBRr5K`(}N}!!vnoE<=5k}FeA1d7Rsx`;D{-$ zuBkxjq8LA|KwxWA9tT9e-_tv3;!%+?} zrY%xaff@$3Es6lqq8!YNUo~x^%K$^86kh&Dum$Qn5W)a1Q+6OYW^BDl4C@**n_1rE zvPs6RQzZ_LDty?X&Q*i#~;mZ!)cpztSwYo8bxS=6D88psl|@s z13;NFX;B9$*?^lctuMpuSN0kxNF0_d!Wt^m#bvZKRvQqIArhAbH(XyJ(6l^&)6{Pd zB!$(G%1Ch&Q5LpWsg$thRpf>~J_EWpE~|G}7;?Bowr+3|s`5E*C_IIF7s-cp&u3uT zIrzj))ZE=ZhX+}>Sd}BjF zzWWx{zKjEkFpfmtEQIf-QxQX9C=;WRhVctJwcNcF04nJ z+qOW#BIAmqe(M3iS?mLOULD3U^aYqKy@2r|CKAYiG&?p|2kJZc@mK;1v7$Ikg*?_# zVa(I(qS;U7-Jy9Bz5=DM9zuZ@&7HZZvGT6HFp@$JUtj&XM5D8dD88RC&~p!|C%9+!&0Ffz?iz1LchPiXo+|a?U;nNw_YKh&Hj=5-$F>)F(JIpO#GP=vi?~t@Wv3oT;!(Po z#?@;L4My$rkfeWyYvL?eG_=qo&@6?&@+f?#((J*T4~dh<7K38IJy0_CYX#U524gG3 zQixX-5+>4L{ZW8~N(c$eK}}m7Umq&BRRs)4h!u9E2G43I_#xo&F#&&X`J5p?I{rU^ z?dlVtZItoJa))b)YnHr@`m4<9snf4^gwM@XPQDr_{dF}6b=Ym*g7p^Rd3&<1cFXa; z2Q-s7>IGkDkIQu{_?chpWV8yyG9`mUO~#6cuXFkQSF|mQD<5|@BG00~CbkfhwQ=a| zd*!TF;u9N%_}G|*?r_;Mo#*dve+NI#JHFHW?ZqXv786*)jtw;}^N*;+3SM++N_NX!4Vq8cjxv~KZD?kYz{f!sm z{)Bhsw|g_h$$+H!0N3tOr2~H!;pn`mN0>iLKke~zzcWH258nQ>y#BqjF;&AiGl(=K z5%i`YQ}C!#O}EtJ=rJLC!5zs*ME&7b>Yx7W#p^TZ)7lI-VFQ=NPb`eY8C>@&*-s_M#4S z`<#&jqi7PT#vq}LC`P4f<)W03f^iEa(2+qfMD-YzI0^x_HB`B!_0D{v!awB}4vWLP zkpWESHRka>vMc%|F{FuhoCiF{kzX&V&`8PVg4Un7JB1}9IAy3K?NN1uZJ5K({Vpyv z)3itwuFjd{QBpV#aRCtq=LCOkV8{p2@2qjned&?xs?&0`f~`jwL! zST`%ZJ2ptvV%N7nK9D&ExQMePi#b|abWAP520=LY38QKdA(J8UB1Q}=xa|WeRO77` zFkJOGX?Ba7doRTo)Ka1(Jhr;Y$V$zgiWOvXf22X*VW3veVbEU!J0U@v1 z!I+HEo?l+oX%v_)2EIGV2%rfTEXkr0mDoE`M9#C>OSds#DIv0g|D@`!F4D*NODl<< zfqAh*Dkf8@@~oI}1lE7-7?I@jS1?3K88AXz9lxgkHb~QbBg!pMzN&yLq7)<^C%y3{ zAFqKqzjm{Rq9~N6B)~$VX^v%8xO6@*hecDWE_72oDt?zySi#i6o`zytF{&%uw@kqJ zL1|fQ#IeA49rcA}%jP1`IyEL%qL}_>fQiv=(`5gH%-B!@elJn4epG7Z=N{R4Nrw zUAz&fXu<>W`aItA1&89BVooEFasxTrIY|;VT;{2;h!y14!D#(!yoYDWLzX}Q55F%F< z4k-WE7O4;4^HJxTnNb?B;r?J%zMFBOfYc=2Wxo)6|Ayre6>4y=@7JM@e)H&>X0sAv zxaF(ganXBA>4XXQJQ7<8IB9%e&F9=5vWg4k88&F)*`yeuN~HPj-QkdTbF*dGPiX1~ zoeL$o-nRP#NvV;4SP`B8ei^+6|1N||9kAr}r8cA z^mt-TGe=F>h)foKh&6nVU~gLX_V%gjZ5?jc z9V;UZ2@ugs9F}borM|A&X~^b2kMwD+DcLw>JV0<6b!Jb-kf|>Zbx@nMQN{73{nv~W zJYoLjTFi$9`q?NqFnFrgU)a7(TF6?(S`>_2Y|k0X?<*%ji{F9jK^u`_gJ`uK^|J)} zwk0&0IbGzpsCulvTcETomEmhRPS_agOxX(AKLn^4IHVJl;gqM+xNM@90+YS^76yu0RGbIv4dq`Nu90`;XYcl?IeZe4= z$pEAJ;7E?nn()fm(F27u<0XPittVy7EZ2}Ul-jb*JoVqzZ_R>&$P*zam^u;a7oQ>% zYhP}0k&ftADubW5T_#xu$!Rz=ZnpP-*iP84L z_f+uK-xmwr2$EW@TIq!Mr%w8D9o1k@`91xO_TqWkOM6m)<7OMC=$S$V+`lN7?U0>L zk-TYF7rLMx_8M{pR^}%#%G?6RG0PsJU@2A8!e&tdoGNH=(i%986ikKt2NHl&&`wO} zB-=`f)0Wi028$tuBZKCn#)L3RfxKW0kc>S?ro$;6K>=KMqixBgtt+5_3PK{sa!iUe zIY6Y86P3(O9G;vXPFjk(F*^pEI83EOcSA>sE!Y4>6JawG*>=yB57BxbpQ7CVRePfm z$$?1^i_W}J+NN?MRW=mS3?q^q{{HB44s)x#;YgwgDecdw#b||{k6RR!x5S+NI_n~b z@QL(PrnRjf&7yjPV{2ie$i_B!jLyuA~coM7avEcU9@=p2#!<+1Y3|U_p+3n|7 zRe-_psH5%k6_&A8gBrtxbIeHXAIPc!=7DG-KEP86Wp_Lw_|P2X$mn9oNqN~e5qV=U ztr1XiAa%(v!E-(Xgs5VeAt_Y16i)x8pPH5#Ge1wA-TT34C#SHAZ1l`+ST5KHD(BWk ztc*DkQl@6a^)l~$wTv$4+o`e0WfJt-h0=#IzBQt*dei(8A%};_1zb8p5SE)c>0i`2tb? zEoG=?*sXcA(d_3uw$J4YwN6#$@tJ3@>yY33-)vn=nY3t^5518a!%@GZ;o6Jl@9!@3 zQ5@%L#)NwPl(+X^1l!ZUzrc{)l_r+dN}I`A>uWlbyV{?=U-|Omy!iZrcKw<#l72w;!hBBYCeZ;%0Fval3Y1m6+h%5FqC^I0lr2{zsV)Ia7!0~;*5TB-5xi*o_ zQDGuShA)cYSAIsvW4#4XfHHHMY1<&fv_r&5#_bg%nAB*DMKOEk3D46~?CQf3do^vO z#_g;8Yl!q+n3*R_!yEjV-tvl)&wpy;j9}{7-8;gG`^H6JAHzuKb2F0yC#V@WY4$)4 zGO1jNi%{Zc%J;E$`M=R<<{69p1dipn=}>ldOJc^D;BnUVm9Z*{04?wj(91P zQ>%TUtcd#JKQ1J?fgNHeNYCGd?AgbL5k3>mm5>fFlH$V2I0_?Zx1So&$rTh)in?ju zjeporP_lMR?S>UtagB?C*hqQQhln?YgFv9LXTjwGp7|hF8Y1c__z~`eJqPiMkss*! z#1H$MCpN>Yz`%M;w=`7t2r^*R;fv5Ap#)>Zq#P8aF`z)H9X3|6UwzW zYKQZB#`LWHH~ByPP!n$Kh)oRec=gMBV@>6Yyfk2E$S7ns zG}iii#I!-s@JleFGg3{{uvDOra=N&3T>5>_YixD_)%n~VkTzK&v5Y*C^Q%wZ)AnLl z({{w&*I?MMB|o(8Zo{+%m}~Ld*i*$oX+{0hGD$iNQJm|bFK~=B1-!jh1H+yLkO|49 z37U5~U-TPQ%#DSj*pS)9i+$a8TUl#Kg_n&j&Bnj8R4p_-JJyEKtu_7mtGLFjqF~BXhRO?ZR|!CiY`uRo~-iH151vyytQP}o%0(j8ph7tZyJVKE@+~UF3#}1 zN55MG+WGiYb+83WqPM+yoR`<6#omY5EZg{*t0Z7j5d(U_n7E~iYei=k0<~A%C@L-n z)elNMRV(EF8;XOfM6|lCuyj9BVxai^ILsBaLLR0cXCFlJ%=9V+snm=Je+HFWC2VaP zG*nMGokD$JL@JuK^eH9t7KR3$%im+V$qYfJPpi@Vs3s7049sg8jHoJy7{*s~MiXuK z7?h4cNZjND3P@N%x4$kr`Qn;u)BuiR@G~WAY!A08lAdIgAb7u$5>AU;@bQMWg2ZWQ z6Sbg7R$?iY;G4U!PAj>!TiD9kkFUTp``R^)E)yBdF3|Q(ng@$h$Y5h*&GvK#`Qd2z z?bxLZ$WUVy*IoiyV=Q>6O)sa}VIen5c~1UzBx7m4X3C=;XqaaM?GB}l-aKmPl8LBmF?D(>Fy`Q-ap-XS!3SrN#hxBt*zp?6(5KCfNQqSj^E z)+9cE5McWEsqZnc+U2$NaEDSNZgGYSDdDUk*3<-KZ{^@p5`H-Cmz(`EOi z2JH|G$~9)P;xGJ7tsZ2}G0SIJWkakz@bT&W1~6(NoRw^Eg~`G_s3@8}d1aDr0Q!B% zih*i3fIuZG&AD}!qZmF}>m$kKd%W>ixVQL&5=CrQ};Cbe8R4IFe}Kdf8ofsDsO- z^0$57jEwQVO7uoRS&)%_cQg)LmZIvv;>V6q@kRvP3PYXBG#RsY1G1G{g9Azt#gO1E zSn7XvQ&pN&1w9vwvXyf*p7aRX}tH*%52qidH)i!tNC}ED}3la=n zk_Q_$V-_5>w{KE9zwuWhSHE7m2PIA=| zM1?%-U)0V8%M45}VMx?c|I+^3_Bj*5WBBQiI`oI`D{rbFxT`r)ho*l{f3rIU}zqq+mLPK|P=sg0wv zqVlVO{f*#4_C^`f71IRjemcD7r0bJovUM}9O{Zq~W4ib0(rL=^c;E_t%MQI1B@O03 z@9X!S9w<)cuiCHo(zYMZd3A}-tJUU54H2mpqRk>{I;UWw_3H00MoN|TZj-jg#m{K~ z65#D?js?x5Zgz!XSf0*aK4|Z{ZZd`H|2z?RByWQgakm0W5x2(wnM|+G3Go z48S<20X@gxs8tG7ML&fRF{{IIDu#JCvIwLXQ;3qh4Wi#f;oAoSC)`QGgbSikD@8*_ zf%TfZfiz?DUL2>omK~$de9|2 zKVCD1%UGj!@^nqCc^#*fd)vq&T7H=gxY?jmP+bGVgj_bJJkh#lxU8nB>3nL5W#lb+ zO%xs~HtWU~tTEkB{G~)(_Zs;w*atLQn`Vm`IPEGTm1R@d~_nS#P5_65lxV-aS$dI-)8z*sqxU|8K9rm03C zK!?9EBBiq&I2Tx!#Q)+u4i;01jBSXIMM{p8Fbp1_Mdtp5dq%(c+RKd^(F1mMt|AC5 zmiA?;SRSL>gv^`;GIsj3x`r}~AOSfhu6`~rE{%~D=E*e>tT^Z$=pt&k_u}*E!K0$* zajBh^#dI+b%>*Lj-C{Jl_I^z@hb}1u6;?BDA2FdPwC9?sui4HY2c% zr2x4S*OzwlN71TWcJKD8sbp+F>{mh)Vs(57Mw#z-1nZ}g`qk(z$-V1Oos7dBpOYaZ z$UROGIdNLbYxfb!0;Bv3)k_3b4R0oj=K*wjv{bcQ>z*r4s|7rGW?Pmggc%ioq92u9 z64q&?-IWb~Cs3|;*9qBLi6UKf3csq*hj5xtk%oH-u4EgmyvIg8)_y&2S^Vd@!4_)` z$qbb)Fg!Z>VFczU>zClusa&eK5!xYOvU;Vs8EzajYUtM=s`j7+ud;G~0HM<0WL-}q zAI%`xXg!OUje0Ijb}-SQE92NZvXWc`-~BIZy7JYv@20d=m2*o)l4b+MA$+Js`|nc= zE7uP|v{)rw17&eM?6=HJ` zO3*W9S~sdsv|B3S0nP#3qh<;lvuK#2T=rt{L23i%2{PtR>H&>?IS%Ljipl8=q=IWK zAwml5`9RG)fF#a>_zwa+R?wm(9AAY(lA7;cu}|-3nkXE%m%(X`Y&&q+ooFatVbNAN zYe;DoKMSuK`(GMBqMAQC!e+4mD=a!6jY77#jfYKoog#Y%X&N^36ww-I-9mUj!k&M5 z)o5~x3$16mzHXV#kML%YMz0=0`#iJP`5ysAJqhGi^WU7b!@MjP)afWCPI!~TLP{A! z)8$c^`amx>YB9)G*i~*Z05(Awq^KTydz)VV*i;yKyi2Yz19oa*oN6H~cAZpm*A(1~ zQYAkw1txNH+k)N(C&1rYD&Q-Bw8Hvfn)5f>JlFLAi7Mb7%Qyz#$*GTm`6=H`f}%Zh zhBsDQS4aAq^TP$rR0Ye!+5T(3%O_8Kn5UGn?DZ^POAY&bTP_WHf@rx_>50O`b4Y9{ zKw7*?N0`TTtxA3-!pSX|dA3CNWas3+@1%Uf5ZmRg%c~tDUv~1Jja%K!7L4v3$kx0Z zg0|FgBbQr)3wbX;fN5xvy682g+OwuQbNa6>9DQi&lHEwl5{H=8;JAQ%W&^3UaaCGA zSF|*#YHR|(Drp_qvM#Ke_^nR7%U$2!%NyV#=>V3fS+cmc{{AB0`?;bV!T7*z0hRX0 zTG}Ko8%@SdN)!USX5a4SD#^*z^rK5-PV>^~=~XL{lZ1!n%~hY2W5N(UTb_$zEl*wDbGxUn`$^-sk$x;#MjVSn z7lD|rIAX=sS*vN7Q32#ySq>WJyO6=$&MTXG(I+n5aIzz(T~4;l`fv9f9IYl>8)uAQ z#xHNzitFtOJZ1)qxMMS@EiK{~36i{aPn=2($}!qFqY?=#bBffA2m?Moiq+=m+I(J| z_Qx=dRmY+4G0`!G)lL)l9^scgA8mbuS!5kOZZ6wQ2HEuorHLMoKiAP4&yS-YL7+g9 zpiW!IB!Mx4`1s#n{@-aWQ_ID#abpi7X4sz>UVl~*a`@8f;svuCz}Iu9t#@3d;XmmO zA$0$9maJD#+*ISqjx6X4Cn2{hLoqpGkH%Oc{SD_xlvE}M)Kodz{02s;cST85B|AYI zi77g#LhuBUs*EOwm2{P~-*W*ZB%&BbX*pzWo@}aq)tA+?t`)VRsi`nFPf51bfWFXF&N3f$I2WSQO7Y1Xz&>ZHA z&`SlX8`$HT71tKhaSrXenv>x>>2`e3KiB^K_?+XCHdA7ihq6UafxQT z%n_ZqjQ*yR$8#b+ghWO!uJEK4YKamZz`(#Hs3sbwdT8IN`@dk7X}b&y^Cp(!)BJ`# zTH{uCf~adsY#krBPIf48HUdV?KR!NcM9*U_MN7xFk644*m+!F~`*Fq!o7q|fflw!> zM)~)|LO~Hrk*#DCBXm+?x%^RgWTonPj#`xBM3v$OrJDG$!QOPZRu~jKnE%dRQxezf z$GW6NEObJ%&sk`eM3dK+w@p|_?1+L%WE;LYKpi0mRTYgX%E^vU{7H%-hl>$g!$MS< z3iNoUUkvB>;_xTK5nJ_V`NPKGBC;1&F{`8rFgLb$F>4(Yf-;LnBJmN!sW_n0^K^qK zQR-f@J8Q7s996-&Guw3f3dLMO!L*Eh^|Uc2=lPax%62!mx%4}%N9CX|@d6chGy1^8{gzl;ZbJj9RK zB@7nn_K-=luoqY|qq+MzmI}D##W~%&sQxK1o9>Yt8!L}&BINm+e|^0^zC8Gbdz@I& zoZ*UczP@dx_TiIX9<=qYs9^q z#-f(!9y7k$8=fckMjlgTG6-#cRJa6m8l~-1kA$J<7(3 z>-xRlFlF-yMEHnrvqgpVi8Oofn0q-iF{k5gN^>FLmF{CklPGaFKOxdI>>E&8qYdYn zf)`(Hu7Q*3POZ0GSrqgc-RuctS5m6cD9VMZVZ<<|k@s+ift>ZApxj;Y!9J&^r&hR>>0$Arl%;{yQoVE32cewRlYG&+q#R;cGqk z3>4RxcAg~i zF3=t_dXs%GENQ%m5)`XMs2hkit2Hh9$Lr6gc(}`HSg)`tDQ<}97!stciOswU!<82Z zg0LDXL>M}=jPtLf{C*~)p(_cE1&icKXa2C~H|gjXhL5a4sf1Cm=4$*E=7LkG2of?+ zRC&adqr3zWIiW)yc0r_h$t;uzYgRS|`IrqjP6|G69Rf{R5D%_M+(epXO}w0uN$__H z#D-F$RSS(fKX(x;V#p0%R|mSdXv7gzO)e>t*iehaU~p2ePnvyu6p3ZyV7yY$s_o*sA$FF8T`^XF)PE61R_D=h}hX0e_Yg$&|Rra3)@=9 z&Ba}}6?>rmvtP6&QRHDt!QB4fw>1WiSQn&ow?H>3hLeGEH4=B}^mx_xO@t@=bQ4<0L9 zsqDfYo_d1yJ)W30IZ>Rz22Oh(HU0m7ur4!+L*|zrUT-YfG4tCOD7`(OaY#Zwz^1M#h zeQZ6!UYuwzr{N-B&~TyFG-DgAJ1`3((|W{|v12qoRt~CqxHv7-c3$E{T&?3{&@}Y< zxTk=((GvUk3{s7~{oBpzym*0iwjO4r5KM(xLvKwQ)E?;_|K(S-wC;^}@i&ZQVBhHI zdrn;f?&W)0WYvNZb64GO1HbyoR~;J$WYV`R$JjL|m*Ll^=Jn4UvF{rr`G{Flazw^y zcGzy|YpZxI%zoBs-S1|VOC;HW-wr(P)z-tP)4ue%7K(!Fc6>rxYOGRpg>}{LZ)k)> zg70p%AAgCjnh5nTRJD$0a(^{&?TkYEP1`+vV)^U}O?i8?u40p78G=E-mF^H46YcJ+ z%p-TFvX*A5Zp3YS`_v@e{O$W$LcQ>~U0)Pilxp{;0OH4#fX%tCSCB}OdtJ0I#cLL&D%-e|6QSAj`Rt~>SSDj?OPdxLpfdb7FhD~J?4?`~g zG_cj>kh{bVpde6+yy#&E+MyTJKr4K>f3p2lM!fk&U{GhY=d3W!yY#}-Keoc&i zO3Lri=QwxPWEl^CJ{~v+W{&@RTiD@-yGb_F*D)h@d0j2~c<~>1rbFKMAhU&=MSi`w zJw7Q`=$QF&RZ7kF*iD?>oZE|ed}BS#`poA|hOo!i*DJ^Dk$_B#)&t>McVvd>M8w=E^v zSUxQ8<qFHiq!rQfN^C)t&(P~*!WIyd&)^$#!r(kBiHFsu{2Mxm&Eb-FD zR_8hQ@GilH2sqMy1MsMN5Ql7HU z6Z=NLuRDpQ<^>r|IM+78m)eKdB590znb%zPl=5w&5r^BPxy4g4NuZ9;M%1x!#@K(h zw-;^&2@&v{BNb<6HNMAx#K4PO7z7@Jky~`}a_oIEE~#Uu=~?{^XdP!ox&ofpw%GCd z<@(CEKww>&&c=ki{ojvGxRCn;*K|tFMimWto<9=EOv2f*-46t zvi{Qr%kxW%^6?#MJwX+Bvq!V)(o|1M$esVrsq*rqtnm${S@++Ds)u(nl0RB`A;gPw z7{jCLK5hnd{@+)8QIB`GbwM5u$lHagaz?uirwnZhw!<&I7oG=mV7GZZh1+CSFPQr6 zDGk&M=0&Az>KMXszyA4je-zJ0Zx7Ujv{6!~ReRNEvuz?+W%m0!v?q$u@2OLu)?r>> zH^2XzEuH=E-?#V2xWn;$5PybKQa3oXYu{p(@Pc5Yb_R;i{ce7k$woh`$WYOCkeSQ* z&fd)@{HTD?{5<+dyeMj#aeXCW{o$o7q0Ie|kpwM~Ku`l$IWDlOxl`>ENER_rNu1)T zy`Gorf;q8Im6R*Ha4YgQUggnN~lG?pDG=VT&fP? zw6|Hw*obl>y&5lZi+<3VN)PpL?fF!+_()$_cg-6CxhO&vqx|oE%VCbt;hphD;W80< z4-VjzXso#dopfoZ&i6k)4Gkgw)5bU9&{69T+brUf8CnB)Cy=Zh#lE4!FD6ZmpcYd2Bx2 zYs%qCsB0C{*TZ>w6e_d>44% zOBeOk3&9W#_+A`Eo2)Pp8ILsMBvcwq&C}b^)u!kkye!u}hza2txs?<9xJZ840@)f7 zsOtPSUOAZkrNo_aqNAQgx^wI&~m&g>R<23Un21PQ!nRRts%hU7^MygDWm)o_t6|8rPt^@U3Mi6 zuBgTgo@w!rnks(-j6^&MhAEnZ1OV=(7Fd^rsGKO@L$Va_C{42w$y0EEz}v?fePty= zlJXh(4Qk@i%fUffS&MPGByDx^9WqW&dR*b zZu4J$XsOk}nAnAX#l!GlsY5|p(BpdGbeG|zrb4nn~s)d{Ekgg#U-euNDQKd4s$}7KK54>Cz z%)M&6EA#8fnB=Trk6Ico%MY8ZG3k8m@TZn+phzy~=Z@QL!5B>vMjIMsz%sgx4rzq| z#cH*!d&cr>+W%Z+7bxs*|M22ko5fj=t&(iVbysf=Kc2F zMt*h2-(3C{n}u{%Rvib2U9Uritr4(hmSz`x-t^YpvopHHCCu)B;DWC3BYxk6_7u%uYdZHd7=q+S4PuVPzxUirrOz}h=M5$wK;|Ujyby6@}#;T3qwoL#PY&g zY?0%&EPY1P43pI%DsrO7{0yUg`%3Gr{>nLhv`1Nk=+dk0<+S?l7ma#EU{x)|xmQhR z0CW5q8C|I{rW3<%um9k|e3%-%$ZT@WV3i@R@RZ~fR!uUxBnqi{TK!VotXy;|kVB#Y zrS53q3Y(#>?z#%&;&Jodl!O1Pu>?j+C(EX)lMljLP+5#cp~*9Z@b5dn zFMeRx-H>>`%C2R9ycv<(o!12Z?)rQFh`MjTbR=XXNyHJ>NEKqiNs(?WoxIV3^YUHKEWRYl~sG^RAIkzE<}vG6HD(ZuD&5>oyZ)Ogtaw2w8~Espligh@RmMOo7vokuk9;jh@YPFlu)FV@-| z1myN`f$5>=ZrH5_y*-#gm{BSlELZdoPdNTDe8Bhy6_SC(DC`~J44L@GEr=r`s8$b) zk=XwbTNX-A?iKCHwEl@4S{T1e6{P3XYc=!%`v!2x1I5Kn?>4~SrVrF%aPuj{JjFa8 zRz**=o<)c*p}W?#3}lkTq~N=!*70E+M3G5Z+}hO8HW=c?rL!WcPriw4i?5e=!N07p zl>(d=Lv81N`S4adDt31F>A4sEx|fHJMCBOEj$(_%V)bTk8M6uwwEoa@-w45!5^cos z1f=#Irq&k)X+EYjk=oQ-;FZ3VuH5>>4<)B%20mT#V`C9y3Hqwu^ z>A9r;rmDj}Z_$D81#IRzj~^ZS`L;)cME>krXGumPt9YV1(3J1a zi`ecum7z~S9*hx$Y%j1FIEGNHNujALKBz`J%6)MiUjt#d+w~eC9A^Leh3StauPm~C z3F}0%Tv*EI=-gk;ROz!{GAE1fHXVWhf`=NPb!W`5s;o~vDDc0_U%8*Jx*_?LGLtFG zG+s4TRJA^5N2yffLK5YuVG#9khY4#CG)V(iwab(gCj{A+`ykz-Mvm#-;5Ep0sIf{b zG6b#shJBKRr4nvz8mkPRrT~IrWh$F{lf>g@RJ46rO?KWTK6)k`M6F8GL4I9{PZx5{ z`pP@dN~Y(q2f_=3=Y`i{SHy-LrTN&8qgaYEB^JUqvQlCuRqp<1T-M{ zS#t3egA})KiO0xCOB92NxmR-k`D@DJzxpM7K&5%6`9HS=TTsBi><$>?m)6(+{^dc! zjqNIhLY&9I;%}16w?W9OcN~tCgnWtQr@c-uGc|b=(oCxA%Gp$Cc*E%1?wM-T^D!qw z`(6ldD$8W)n>sf!iV17CUA4iBL-jv(=*&4@?nvOaeSC%_*qj{gf;}Nyqe^o(kga_t zK0ni(u?00o;7WsBdS`>kj}IPK`~&&q{JSrMMhpCXa_qxH8ZcH6@@4Q$LOQ~OMc%Re zA6KwRvfbpntI*DLV}mPy5KEt?%;m9nteI!jJPlMJU*ghF)a1%R;j%3+ANSw*R{%ep6jZh zl+>YVKb1Wx<&*ikh9AV?IrKLE_m$>9f298b-T%WUH$xTa(q5Pg?0qk;i}I7sOxsie z`tipwLlDuXLiZ$Oh_W8QK%40&MR`5bLvsj)0VZ@d#ZYx1{-&u!E0gh&yMfWsU%|UG z+2H+zA12es&Au14BOXc-$wooQuA6b9>m?~hy&b74nA($R)RU(1{2|m~0{Wq1!4nel ztw_pWZ^3ALRzaP|&v?S`xiQ+q#BA;{u+IuS(%Dfl$M?my^8%uV^_n=>J%Qis{coaV zA1{ya*y~UgAt|CAw~G0L7|!XLbyJ$bMYH5JwH!|kozD~J+OKUipAWTdabgDGE-GtO zhs#aA0@+V?o_qvz7M}D5)7+6rnQx}DE-5q?ZSy-Ku$A?^VkAiVDF-%0uaU|!WfAOq zc1y=fMX6HmH>I=G*nhg69K}%^7GVILg=P9$l52M~k1CgN&y0d$16kZ!!x;$4(`q=Mv8`$&XxwZ4|permp(271sps|_LQ#R8|1z}L~pyaE72u}u1cp)=PQk0(;XlpJt3sG1&1aB>c(YiZjz z&^AC3S;xvF0>qACM&9d^r%FmKYAiVR2QT%AJ$ifz*MVFD%40Z=e8Hi(RvIN}6k z1vk=v{;S1xFzFTI4354n&(hgBHXUh+iVkMvt57#E=S~(nMyK*9X5E1#-BZ zHU?QuOlKTjbnJARd2`#jKDP(Uui{r@4AsV(-uMeRctNg~xBE&qT==I-D#p5VANz zBHxmI!XneB`1!`g9<@P}|KH!WIx&JXGv*gtxu4Y^M^A>$2&Ql=Pa6f@e&h~OP!800 z8}g@hhLQjOGWi;QnUR5*CoM)@hN`d|y^`Q-;<9-lrOnDND73o*_i~!K0>GY6a>7R* z0=ozXOsaT*o~hB8vKZPFN&0{KdV?_D>$W`ogPYMKDddV`0N;gCaSFykDaH;|#{;EI zCMQZw2?B3;m*8i}ivj3DJeMTABf(D`+Qf4L6?Hu{LzmD4m2`nwbx={ap?lh6QfbPm z!5$}b4n8MWFwudJ0hxod&7}1dyi#8&hQ1xYThOG*l_;ULVvx_#FKbCE#^Enz0sudr zww!12q$`JZU3a%NeBPCUx4Q9*Al4`RDS=(F`U@0SwBaZEadQFwH|5&35_RFG6P(fIB(veESt;-+l;qyBDDs-#9C%0D$z6YPh6zs!{WJh4GW_UBEp`IQy5W9i>MeZrCrB!W*~nSavtH`*Uq~f#vk=>S7_eA*LU;L zJ{t9t-6H<``c*+_HgSRfeJ_^jh^gX(jvK3G;h=R4gSM=(QF`e`*q;ai=d$5URt<(d ziKNVtP_n>&n1gY1=@zUf@(l{4_GV*7pS2_4W3rc7rT(gWp3_Y+>~pKF@&`w=miG zziY(SDsnj!DP!uY~Aoe zdRnhXU$54f2G8&UW@BANduOo0($Zjjgc0%fUlj>;*h_rNhg&}dmF%qB0Z>gHKmI^C zsX8czkaFwlDdj1wpE<-BIR>gMhMtGeK~ne4#Y2*+U=f3-{bfker?X8p^#Til<8$igQSa^_5V+&ID7qRT{K=)32nh(^Obns`f4kyiR zhE`P#%2y{O+Tsa;GF^$=Wd3@}4TQ3`Sc$-8N zuQI7l3!|SA*yHlHx7N$-hIk-f0%l7Eb87M*8-=G%-@E1X653mQ0q^rjxGY_MCZD!J z@21h9rmD7lMkq6%WXJN4%RG|SYqra}u8vdkZRE8YVa4g$e8;@~Z&ojNMeNvn7=ZmN zls6da?oWan8G|?W*0TZp4;T8AQV8B^bGp8}hmRk*SD6XR7%-TqZsj#4Un^p|% z9^zak1GOCPO8Q(qe-`wow)!3IE06-ke7EcIT{OF;=8*Ah#I2Brupyk(s*zXpGn69W zAQMMj)n3uJo@EVu&7Y3@3rWC4AM822{bbs&@9(m>{lphp5O_=_1=AsEE|EWpS#A~s zuC?qcHTuys6e`Ek?ogDLRIo1OXgoh3$vh|S3{Ju8s4AieV1jR_cOWH=-{aBS%Q6u% zc)F0wQkY)*5#O@bO`^IPbXrxZd5}8M_A$9m0I!B-QtbV2Ni|E$bVgZ=a7PMRo~(;A z4s_9tE_3ca?6{fk32{Pp)5Mnp_So!F(H7JhvU^hTL zuNrB?Yy@#RVgOkJ&@*%Te9gJ+9J;uTgUQY1W96;m))T%@3TDJB8z*2&O(@C|o*{Ps zm|NDcv9t?LlXS`8PnWVkg0-d1`Bx3?i$%Dv>9ipWvA3C$;^6;+<~jP7#HL12xr@T{ z6TQIk4=x|hdr&k|%OF&rvJ?UY!yx9%v^RCAcS6N^Xn6 z-|eqz=$g*o#@#=Zr#A~M2@ukyFop$!@KUWF4#6a=7%e6IJU+FkjlDBOJ~r>hUPxj8 z8vm$e6*=2t}JNrIOs}#-VAVG2TWZB;5zm&^s z(u26Ve5T3xgxQUe<@|g!U%%TLlFy<_z7p2|TJ;u-xpNC8_7Ch9j;dgfBAO)EMOkQ! zp3W&%;Uq>$(SU0xs;@yThCN1FDyXBNG00)WaODYQhQ`d74=MK%VgCXd1Q@)WoyFh1 z4d6D}wd~E4c+(Hi*`t)s=mQeZiQ{I^nNnr^EJ|Kk>g$_@crp`jxoyjkm1fWd@MNJ^ zf1`jFujlH^QbjrnM<%LxxVibdXUSsZlWFOJmrbiDDS{A+5E(spxTi8K?bs+sRm&9- zvv-BFQP?RV6jIc@yj7}QNw!css z%BCsyhYHF7=tb!gmOXG%e;a3{&Z4@|Otw}XvprLiqE!VgqY6nj0}6U=)E}9B*|OtZTI&q zkeug+3bmL)2^o7Yy6)ERFAiq!`p$6q+*|nH^MB!Ic7C)bP+~@OrK{vlj0Fy*AD~Vg zsQs0b|G7s7ZfHVIBv?@IKZ~=o6iYH1nc#`btqn~iFsM+FecYgxepkyUwewKKjtEfQzC&NX45O};XLmRf!t&`uE!@|iMp{LJd z5|t8R8Tu$rFga#7<~z@9t|YSOVLd7AsoT3%wc;!(2V8760AB5+r)F`Z3`MUo-zL3_ zExq!GkS+o=p?>&v4qU}9!iM$-1n=9TeuKH8n?b~|x)}Ls_E6(JlEofWIlTxC8=d6L zRVL9UB1Qf+$7m;A>|Guw<{lBZq62uo+QS41ZAq|*`Z6w;Akp@$j?FFRmV9H*-#li_ zmkI6VR2&6Ql?;RsCzeN|C~iE7J*-?8&N-k1R&-nSd_~a8ubKP%;%=yNn!hcjpoF|W z^YKI%5=iq&Pa1MIigD0}dW*7;gpT_0lB#~8c`BEWy9&TZ@+a|2Fv>UeCcQzvULc_k z#7ThUp8F$wV_MdAWm?%B_PN)=pU+6ti%#kfPXT{TXZntK;oow-YGW4~jXV0CnODey zUHpipw{b1@LFH z1sNW`oIP%p=qu62f_jQKc5Rz=_0QL|`c)47jP&Ea5O^?41#G`rn{`lDM$T?it821_ z#<$zrqq8a+Rgx?8X_F>`Zb#+>$0lrEo`)x5%c6XTP&dO!1>l`5C(p0utca+DZ0*a( zog9Q>Hl_4)L`n&l4=zPFF(#^qLw6~(ntPBN|A4R!XM4MeOcK5Sg->UB5DDSQr7Fh)71b?+l6P$iRO~KZRb3^3Hoi1 z5`b%Mhq5z0T^q_lxsW!LW<=xiB5`vLvzp$V8350+lyu^P)4^92M^j(d)jz|jO|3ij zf^C|?&Fr4^;qcT>#kbPyP8&~l+>e{!wA*-l9uU29_hh{;R{Gp|{uUZj@~EGrqiyqvYmA%&zC=0Y_-*yHKg-qoS>0dSiC}# z5FKAb3-q*1ZK9k}Ex$3UfUU;HX+jF9*!Y>xSf5;0#zNen+$czqt2^;;!j_vy;}q7< zk`B0i*iWFjd{q~dxyNOBqqQO55f0extHnZw+P9xi+un8$r}X|KAg7dlT4ZAU0o>3> zIWcHKvofXZCZLm{ATePS(y+2e?tlJhhvW1PcqX+7GX|;a6t~i{DeRDfQmXgU?N6ugU8fsbluU_C0wwW2jHQ@8av0h`v;F)K z>vU8^=(Hd0Fj;PL2s*Dx&)81Vb_w6Dul_BXXo({Ia)1H;cGKto+^K z-tGQY0?SOx0GBjgJyJ+=chD}4W(<0f>_HY*NG#4@s7bD`u0i`Sn+_*$!yDt$czet(#>&bzozqS9=8UqOt>V`tO4+ zdj3%x)$PeC#3g0l8wR(36umIA<6QTemUY8!Z=Uc7RF4&-jUmI;dqW&3>2Om_1mz&a zq81H&qUO<_|DO)9>Wxs>Y?L>ypQxyaoEtZ?OOnQVg^5(|LOyqbbuPE^p~kBFhLqU?&o!lGRAN_1uTe+=c8E7bzy&S{QQIJC zphT)FdthTyAyZo(^TQWO`nh8C$UY7(3sD#INt;lNK9I)-`B>&&>0>P@EU#mGK;o$Zxj^j8zs$hGE`#Qz?&Cku+O z=83Ks({_Tv)$_sdK;Ns(6^Ul|Hg;Vuy)x6o)K}K-)}HL26}PNT4I{*_#!Fo4jNK3Z z%lN}1&5uQk1i0nhL4|H4eu!P+9@X}%l=Jax=~$Y)L_bXu4FT4l+9dWWnn?{ZivA!U zET}o}GYW90M7*4v11M^}%j26!Xo|i`&5nA2APiKCi)^vUX>?3%jvVR5q@=nu_vd_=D#{PH}pBgoNqBf*R2`h%nm{-v$4 zH-&kcG0f|y9NFpE#^7c}xT`Ipmm=no$fTUKjtv4l- zC8P-m|B8Jz6c$b}?i*&+WV1szeN@0&|9+2w|rQOck(Ag%$UD!Ccp}}e z%xHj=RBmIaugUk7y&I75dOWzPkli*A)cpZb--Ljc8a8Vb1F+E{I^TX%T;7fdgf`;~ z4m`MvZ0mZOZpb0J-nAPF2U4z^RHZnozMR=98>}`gSP8}g4Uf7tHsU0QI(xxZyl3(rv2aq${mcYkzi2-cP5;@^A;m%uDIm3(N)gja(kVx22sI5OE9- zYEyz&O+cv5zkXoVk!%c+$EDM|j?~Zs1kh=R0YLsK{OK6FbGn$=)$zzHCBZJ$a`M-o z$YIVbQE0-e=73fipyM1QtaUIxc>lMR;OljWuV*?KO3t-~HWv0zRL5oM)T0S3J|}_V z^8?pk_Uzoz)zH9Xk}f`n2hl~j42nuv=_I8|=gB(LOVX)T;^71dEMmgKC=uIB3;pw` zwK#p_j)6M;kxt!!1RHMw1<$sapBrsCgT0A5Bax*H+I_~GYgo-taG6zb!ShchSnl3F z2v9#3!55KQ{suWx;t7-eA|e9W4Yy^1mfbnw^<7{4>i41tN<9EwzX!H_L{EVYs;+fr z@b%{8lbN5oRlAFvpgXgbg^Y_2FZIygd8*2IS>=wZNS^=F|N3_SJ>rh-(5qT?aB>Z` z*j{HN=L?YM86loa1sjwA)*KjqXttMRwE<}>Ex8D3|Laqj7o2d>ai*qrv}jKUG=0dzzatzk=rqlvc%jyAoN%P z`ox4ZICL)>*@fuc=t4X?-xi<~ayy6b%B2HCo}0;X8hgCWW?7YtcU26SQmbioZQ@0? z?Pf$jn{k%u49S-&!gPBQy0UXrJ<484B!Bo6E=gV*;T zmb9iYVdEwHMTB|J`pd^yS>4(RxhDH$1V9hDcFh?^hJgi$FA1LKjWA6S7v({YH`hlm zp!n`>n-1^(Q6+oq7`SDVSQ&1l*yFLX$je-!tol4(fivBokOwwracg+pvlbiLHoBz3 z=@>-i{ow5dE5L;YioA1%Rz1*U2|QO;SA$hQK%^3ADmG>7&{CFn-f~20)BNHtZ8SaX z21PL1C3RadE^r=7CjjdBa2($DaTt%wfdd7=^uW?yjN~3Sz+KvZwM9rUhtw?Y;tsLV z>I+E3JB2xrRN%ZmW+&v+#6uR>i{<8d|IEWj8cE%&o*g`^nxKhpTkSl*fm$lrtl4Lo z1~W@Z#}_3OUed1SyzUHx--Zn4;*=Md{~oYpph`5JZ?4#~`5JX>E&MQ{`HN0(V7BSp- zA{B2wQ+<={q;c=;hUUx#m2WNYXzh}3WUC*nx{~sA+Y!t#dN>Zr$j0+=dZhLrD>N0A zNuF)ofP;O6!FBnDoOC!~^~fwxeSKH1Dd?I4S?$a3DiYD8x?4O>a;lp)Hs)W7cC(~> ztL7cgMa#>W&1^o+JJQ3A;4T9tY%ZQ`h55INIp>Le%(ZA>$Y-P}tSFaZqc=*~4tG{xI zKB&VDO5>BNq=uT}XA4Fv^Hp>fC=n%dNj z+URN94WSDMIl`e&ZF-hWUK}rFV-+_tI62*8^2+ZphXbrPzK9>A>gu33y11x547qT` zn~VCf8~J}cSg=ErPsk2}K~@2;2#YWSfX4x&m%H!FhX-Vl*`eqe8L}KQgUKk2!Okih z)sWQF6KoExM_(__&nQxbi}A_2xy>*AGMivd9jrcgXO5PI*Om6%HiO80cY7a!2_z)x zWyCY9>L^VG6@(!pCv9C|qxlQ|MLWS4%E7FWKN#9|tp+1vsnz7fTE9tFNt*3Ci0mAc zdtrwuoHDNePt zaq3IiyHt%Ty#ClW`k(RZ*;|Qw1j>8AZtA)|g|NqFa%4m&Bie_^)YRINL+g~9#l7rd zx3t(MtrOQ(#-z8`+C)f2Ox-r<6118T-LPR3*)Re){_wo&mImw(gwXH>dW>>CS1q{z ztV8ZM!Sx92dFl8K9xzY>rq)9qiO;s&kDMF4P56Rm-_wO%|4b!udc|OQWqrr-#2u~W z2q6T43 z{H<4e16;z?JN;P#=Pzn9Pj1v22wM2x1{(=RQb=DApPHaU_tkC_H@~OK6Or_J9-!ms z8)0TJZ%JtLWmHvub8w3nVFI^$MjKBvg*c?Ok?aQl>07-U7R_rk=q71Zh6T0+e3-&8 zNjKRKUMq7M*5h}id)T7~;j;Du*+2LGp*d+KI_q!Dn;PgL(10zm@ zNXQIY@Os)TW3PhlzKzcXvX_H@H<=xq=$VY>en`}UwlG?rtP-#%0%@uKPluv%3c!Nv;S`6dV9s8(E9prNI&Q@Mqns^0XF#GNY;N$ zIbBF*;mQ+hZ&wY(+YD*fxR>=$u=sWIC5!Jnt|W7oYIZPmST*%Axd65^GxV73DSm{siY z8~9CFEc9O&Ayo}uSi{I!VJ=M28h5Q{Av3}coQPpwzP>`4I3*C!uei`VA&EUH=jJs- z2^OM?>fA+kyK?#+gluK%OOk$a{2W&i$3 zj499vu(n+2WA=`CZ(0c-;t5}=ayno^N9&{A zY#Q>->;c&!8VMVtMPpXQZ_JswdUv{VmdZG9s^2QeFJmzJTYB({zT1`Lh=`cbDV3#* z;{Q!}K;#(xF-)ILMF~riWnX{Too_dS?(~^Vrhhl;!7S2HqXd0|ut&d~>z1m(dBUt+X9Ee{=@+uL4-ocUVbw1!j zYl3y5bz1V>$}CjJo3Md!1>OiCgh!ma)cRv%?0LW}TcVMkWU8!XSZ9dy5A?GyF*ajDq?}~aN=_98~vpX4} z%BdJ}{KLY6`R!L@vx_`wuyT<;F{1ab=h~J-m%bRksFU|H#kV_kwd5rEUG$^KWDb#( z)-6Z9#f4?}Lc0+1xxu0YvgGlJrnRJ?+8Z@^>}x4osDCOlO}}CCnXsrBULHqdXjFYF z6JT1aO0^O`38t8ynu%EFi#myeM`DkT4n=(zWe9~!HTuZVxA?!lvdZ@9i{T;-4&}_S z)$Iac1C6(=i;{0}hNWQ{!z~%ObnU-!x1}tqo6P>os!k^f#vP4a&X>X_2=#W($j&il zHmA69z3szbW?93B&qFfd?{IMFlA`<)Au~X(ZC%9y`dR8b&p}iQqk^k`=K0gqF`?d$ z^xG0-M0v%G5y-IFD9mrFa&JRv4ej&{^HK9l-B}m3 z{{<)cAo$o*lan?G6l&gKM1JRaL$+KImt9;FNl{GsMXRk7c?F}Hf~s%2nwPHnI9*A{ z!oQAncelJzV<#R%G=J4L+9H*Bj{S5#I)>aqpVZ2H@)^6*-8caVzC@#<<~Va|5ha~5 z+Ps5oW7=6lTFqi->9Og;;>MOQLR+gcSy*KOEsRy+AZP^@*M4o(3Re}ruVNv1Q*Xv; zEp7UrXr;3Mm4<6piofD?1Mg8N1Ro76h2La5?9M0zWPGca#ytYzLP#;Wv-QKtNvkE- zLXy55gw2xRQ_4TLp%kZFnAlg zOk+x!LYE#1vA0Nt$uVW`rDsuaAi#%lnGK#YA+X8+QXvq@W;~5*n>J{Q^sZ!YamC=^ z`o6R07^mBd&lwpVBzeHxLsKFsQIhnabKQVhG{8K^E4@oi@Wpo_kmGF;@5OG$7j%_e z3_>2x5tne#3sE*Rc1B&v4Gl8xfoYzw6t5U`T$1%%B#($3YG(#|Eb#0V_FZMTtKt9P z()OA~3~sYH5HsNlfznt(hJ!~MOYR7VL1*#9u=z#Gir9(({Cim9ck>I5i_CLmrlct- zKNS4O#+;b^JtmBucf=DsZnehPz1-4<%j%i22qTZpsX z4*>_RcxhO%?5{1H;VUEcA4zQXD(&uXkfeif1;6D`3JIb~)#Vq>% z*UV<4VW7}H`=D}C1gF;cqQ}3eauuL;muBbM$LxOxmxw5)VW447W!S_y>+)~^TQ-li zmQs1a*L9c|(f2a~B6)NH{f@#V7)Y92DH>g}#xy^hthZ%O5bo&Iy21OC3^=%z(?_Er z%#NJ8T8^T>T=c$Q>1DsRmH$D*gUOBg+oe{Obj3A zg}ps~s!b~Jb2%hXB2h`hC~32;{{^Q1J4@7EQcAU~CAGTj5F#rwx+HotwpmL*C6KTU zud_wvKC@WL$=4b5%YUxx$UNB;!)h7b;pg95A>~V}XLcE7UhSKS@qF8-td2kR>3IWu zTnTlTZ^x9`3|%b_L_lSaMeQB1wBTI?UXTs$LnP7>raSaQ)MbxASO-)Tstbf8Ey0n+Zs>eMW6F&pq+9Lz zqPBEzHH)iD${$G>x(@#@pM2v7q_zcQz7mnswOwBqw|u%=>I};7+-nL7!hft$I<^tq z&xozH3^ETa^=t}ato~r1sC(i#2K*5n@xbE*`hI;|w=JFDw_wkrZa5&NO;C)`w0M}y zT9b z3~+BW$lho-@!PQf`=9Zpi1&1_`RB*AxhEocYs;QZbORv|Rx)v91oV5rNzmIGHUxt1 z_6Br7;enUPMCx0ylKZz~S}JGtj|Ph*E0@g)l;q!ZY!QZJ?WFrz*Rne`4mMtrLF|zSJnMZ|;ac-J8o6zC)yfYSs8l+$7>iT8O)&@~a(_>UX?5ArGrKJPX zMt7H@Mj*zagFlb`Si3?Pd2K#N&xFVOd~A&{KK&z8`FL!JS}JTSVH;EBgZwZnF_Q=4 z1h9efFZa#-)lFlRrS4Q$23`zVih3Q^%TW9bALU#$0)?&X+s!LS$iXfEBPggV&&Ij^ zB9-X(gu-pjAO926WhFl$hYy7JbY3*BwF{E_m@gM>EaJOy6gGW|rS0X*-hhFYGPc2! z4Sqf`uJCa#=b|Vc0Eug?w0Mnh+Ve1N+TR2HeY*dxZa!i@P9+Akuu1Ufd*?cAx^L}Y zEWK@(KBWCKS<~vdv1G^$=Xzi1fxf;f0IQ_9&-B>?o=WUHEYddBd1_pWlvyj&h-%Ho zn9>Z~QkVwrWqjFIc9LLIQ?+V{x3s;L<(3=nQ~mq~YM0y*8SFIMTax{{yQgis44(9I zIa~^Vxz|rR-~aXMgA+$K>R7V@w`%IJz=^1$Vy{fDF(vnOldjPES$&^a((3HFyEOBq zAD!RY5AjZEXe;CMh&N*Iv9zKyx`})P!`cIezr3INUm$^fC zTHOMkGmjz{c1|}N=0HQFW6kXqbX%x-Fj#>hHC~7QICPP@hT8j`9tPOtsNvB@d?*fM ziqLl=!h}2iTjqj-TQdL8XHf@+Hw{N-b`&LUp>VxF!4I|`mA*_O^Ax-ASNExkYHXYA zrpKp$-c9e9nK`*$Hc}P|O;rPj-FL67!GpX9J7&U+P9hQDwWP zb$dnfmwuu><08|W4*(GrewP{FU!IYH)8o^d%>yH-xV?g$s;aJ@&ShPOLiwJ+mkZ2&u!clKC$C zxhNJ~n8$_^ju15%I5#iz{Z4aQf)fy{D$e;?Q_W#3yPawCO8IltBl~74xmoU?C0cFe zwvXz|)~~aKaXmdjrL=!#lv6G_8YeMRaX(=XG;RObO2`GT61 zH_P^IG5P>w@!o`xKlBwwPhr-laY2_V6Me-bKk(taP$i#GlUp=EOZSQxFT=b7RgE}T zEUd@6_}yQvgAJ)<+Y8=pYT4NSS|D6aBv1H3S~5No-9xA&K8W|~ouzWSsaub%$o07` zkpxxHT9U{cdDF9*5$Eb3R9uYmU=F>h<`Pj-jWy9PUsglwW>C>9vil6 ziH4K7phqsX`AuOlCVwON{ZEU*!G0r>+gJO63UttGBUd(dw};J4zYEcb68NRi9w}te z3pPcVIVI}3Ech`n%}iPWqQ6n!6{O5L=zaPBlyCpqarGrE1L+DwlY$w8Gz> zVbh-DqTqKNn4Y)r8W!-}epJms=sqdyZM-V|T)SGIVHKcA@djmkOZxXjbni!RS%C7F zJV*mXm|e3?f6vl>9owB0NBtb1O=SaNSqIT|r~jX@SqBR*R9>*|7moJp52?JV#&_M8sUp-zl<(_W_|EZj!sToiPp^ zmj4c;78Bh(jTcO*U7YVp1P>8rASo}8BeWHuUg3QwG35ohkZWaxr;*a1D}%qpNPe*y z+GxPhZPxbu8hUCvikcoaLRbd6W-zlaN<+Is#_39VtEpr{9o7wna+rcp$qNRLPp$;P z(Srp#m7;l2r#H#*4fg2^a7oyTt`Q7{1IjE%uL;IHxrN+ISbAMzzui*Q^~@7O4U;SP zi3bZ|nRZT1i6gUfdx9nfCtG5MOZ)l+HGf83qG{VOJzCStz`|MBWyiQH+*Klq2nLE* zHxf7so3|lBeB^U3_60A)*sW}bX!-PUs;m7Yj(MdQ`&if=VoAaNaaRCIqlBL;it1+o z2da;zC;2Z6U=a$QsJFM&)(I;_7A0}eP%Yyq{X8oQNAWLj7@=ug*f2TpYcuLZEm`lD zA+@2L*LYHMJu0$T;6i*H1cxA>OsP!rPqUjK>ziohKsc!{2JP4R^iK(=iMVarxW4*F zNS5()rcB6EncCY01l(uClwA&N_EvTjJY20FH5-CnLW83k^(KJ%znS57l)r%q#`};7 z^x4R37r}B04g6*n=BtZ*-+R*%%-da4J>hum9pUYjFKQZpe~J}MV#*h@rEB$^(*=ai z@~KkuKVo8v$8>=pb0)W0IHQR3&w2>}q9i%F9O`1-XPR;_Rlc~VWVA9cdJS?sgtWo3 zn5ZhnwNVhvv%UZbTf3uU*}sWDah#Q|s>5sOl1xLBozIwND+I#6P@thiduA;%WGVc} zk>ZOewJ4y9KSj6S$gI=gu_gYNon&TbP(1hts6gtGpL3HM7&0xuAsB5jb%L?sRCULuO+O0&oDG6e2I}r(W7qh`88n#v@BF=ZQu=wt4Wa|;>4OoDx=o6C)(-3( zeoN5=q_RRx-DpABr)Z>g96hOG8{yX&u&wIQ?SbbTQBrrw-GLNpo$V*HIcJj-b~+o6 zNLtoGeIahH!d4&W4<9KYTSa4til)S~vh(PD(RyWN12KjJuNGU~-yhZg`Lab|d=Q0D zvC-M;b20Z?eD{B0#Lw2_1k91BZgfx`Hgw3X=D@m~axk3wd-<%^B#qgA$u+fWZ^ep~ z2C@@o6@d@1?py2>2Hh9jg&*-wJCcdUx1E8Qe1=CB=hg-q(`7bFqRbE3i7X-FqAtaW zoWdua%HOiLMwe4%WJw5=Hp*v4K&x&ApA-W z-K%*WubIr35;Em6BuM=zcaL+WRlkrM-%l(*jJTjWk}^!**KlLQpoE2qv|$kE{6Jn8 z>i9Yo@-;RdIx-ySjLmJGr|#!-Ulo3!!8kDRssDDW1&5*wmNF_V_>JL(_`K#p1fSPd z@GCn#)>YMZBb^o811&;iEP5{TWyHuNq|f1%$}p=QaVybPRT*ok9xTCBIC3P=auk)s zx`hrbX=~1GCrZ`H10|gvbsQ$h3Y@}tIh-KTFz7P8>|8Qgpf9~a*MqILd&xWpuq_I1 zcE}=2Vaq{K({gczfl|^s&LjD)F?JA4HvHWtMFjXy7 zAaO}L?;Achm2KY+m_WH7IM3z}K_V+d9d{z-iqb5A=W1Ra+&-Q-m%kccy_Pzy^+ zbM5f|oR1DM)sPL@wvv6bZ7+?hy-N?|vGyY(&Xj?Jn=8(i> z>1J0gJah>}xAFh0Nvg=n!vQQQ8Dd{B4iW7FyLNFW%`3|{K_V7#xpE}y7)&!T;%S%> zCjBeNT~eYM$72`#nsFA_-*f!m%RoF0Op{fIX#3`l7aU$Cbyf&RUgzVDds@MyS=0Xa z)c$V;a5JR#0m?cJ{vTy;Jegi!i&D8o;SC-(EA_xSyRNRy#;biQ>dD+lEMfh|hw+me zE6=B(w}aPPTMw_e-mLKyMj-?N^zS`yplWdNicU2~En2AI=)^avx7r%Z;RMXa z$VyJImZ(Mfg8gRI08#95vBd#`guvFXoUYe(jhC&Riv~%(mUGLfy5%IilcZ)_ z#r21ecT@nFeE18r?UyB_Rekw@fW~E(^=+1`l+Yl(&fGwUyBIxvz{4$@4<43Ac)~kn z?x1p$rq0Zob`}MUFMRhi1S!T1Gc950wWhY8!Dc2xXt%33Vt^~8j;gWj*wu#UamxT; zf0{}C0q>+k$ReKDy6KZC*@Sdu6YcN5BoZKlL*BEj-X>xko@?L2tEv*CPt^LG47|R? z-ex?^Oxi(MqGqExsqbqS?FJLg&x~M0jjB7dtI0k3lb~D32)-q zW(7hSZ&yMDnG&S*?5|8e=raFEznid}*;JF)JP(!imNM)z@$x8E$oQL}Fy5BeYpDgs zt}WpaR*?q2PkoXWzsYFZL8sME`lK`iFIKjc@SzFV5s7)kA!yyuZP4^|iUU^;e}$I5 z{xjIIB6-Q2%Vqn8?GvQotk??FPbF1e1N_1j;g2mP2#q?`{)ywp+<>tBX~vU%G`@k? zEthH0@u?uJFOJQFEpEw_Q7$(3@q~0h#xjgr# zW)8Ag@XH5dP{TJZ8|q-TwS#}E_yt7ns*~jf;1<73##4!9~xk0d>{mS#6k2H*q z<#0OG9hqy0wR)5Z6d)zNiq(f=XI~^ej?`RTS}?)?IeJt=3jXHHWJcc zH53cuyGKpUNmbWdH0j3hI^e#%81&`Ned3c@2@Z`x*^kru@`IA*2l8+Mf{g^_;ARd% z9a8SJ#S-veb7c!ZGQq8R^z%}X@r4z()`tEixjz4hkEyD2;6~7jq=~{{i;0wFgoeP? z%8xwcjUn+)&GeBQcKent!L5-bCmXHFNVu3&cID}=Qzf*QofqsYhTYHk(rY6A1)-~9 z84aCLC$XG*0dLucS~M;WC3+gdORR~K9_{}7`pouFBUjgP=l6J_Il~UV`0m>WcO8ez zs<$2)x@+nC=`9qdtzCs2&b>VFr*ypYrijcL!pPhwq$|Q{Bnkk`szq~y?{4kDlYMP& z_6|)BCigQIhvz$mx~iGlIY0NH{(5k)W;wX7UA}5(VJIS@@mlrx_^;X{?`w_c6JAOB zzOUh)QO8gq#?!S}ZvD=TR=YyawpP4~sP*gEc>jl{YOBt&I&ul7qTi{zU0Lvi0gT6! zN9C{VY+AF4{&Y~pUxo#gfr8`1&R=8sk*Q}BukgK!)G_)gtCHACdhu<{D+>me-(tA} z4dZx0Yct{JP(q}*71!?D7_HUSp1D_g+-+1hp7#AV;P4N_aNoDFw!Xtvbi@#}c(nD5 z?rgiQEyEq}_N8_BzISb|h*_oMv0kkXc2qigB5D^}eT5i-kD9`g4oH%cL&sMowB0#$6^p@^{2y&hmTt;r@zK3)K z%85bvwSB*P$NcxFw|77uB#~;broF9X&Im}5M?8=k^z+ECX&<$7ag{i!x3y zOh8E)>+~u*kQQmWzuLM@ZpdzU&=GThm#fh*5}rY&70V^OfvPuf`eWIH1nIe4Rq(xh za~J!^iSXYWw!E}9QHAjsb^w-^l2PzbX&93<@RB!>K?l%+gS4= z1^lUe{U{m;gABvrk-NE1N2w5wK2eo_$5*iTLG$aq{}fB0m{IiKEo=P zA#VmBz$y~qM95z`j)Czb=Vxp!8=~(yIW3i6ChPIEL42^`xfp0Yd$=N2!93$V&bgFV zqHNuZpT+#{FXDHvPfjnvqMfE>c^S=8&<|)zN~;cUj%mJFY(cCcpt3xxR**yd+CL`( zJx1oJ70~Sr_XcmST@!rJ%fH5NVE^}#XitE-?yvf3AA~`N9$v$6@X+}>bU_zVVesI_ zk`zRG3Vwh)sdb}6XTy*m5?cbabh$}So6*Nie9an-kdljR?X)FzErQzU#X62hx*itP z5AgejFxdY?t9FWcUpb=o6UMJY9KYa$I*CyH8hhOHmpAL|llil2(R=0*KTNt&05i zduP))=PFXrV`by^1WOVoq=T}9tg$!jj!>5>AxlBR+c3k#j+Xh)pg_x35W z(g!NZwJem$kIn0NiX$UkiHx5-*NA@%3g}a$*5ETFNl~71#4*=tm9446dY7JCTp|~7 z&uAf1sO&2LgNt0?GQpTBJv`^sJr@|Aoflu9Lf_Tm4_S!(KCR@(@p+^Mr!I7d9mO2C z(JKG;hM(JRdwX-BLYZhq(V54me_QWd2%|M~WphgO8s-fO7t2+_HXD%57tr*_F-aeO zvqKasR{o;jwJa(l3m481UO1a2^P)BwDNBl)F)|d zWS>atGc*03!Of?F@MBK3W_5wRx2~HnwpSopQw&)OaU9!jQQ<2l1GY?XlM}Ay;EZzY zMyQ{b50~b@mT2FT;z{+O-${07hRft(s`znfP7La87=A*y!X zlKJG5r3zttpAc+{cBrMTnt8h3d zp*(TT_g>xnvgvQsc|J9fR6)_gbpK874t@X;0WDJ5d;xKmTURn@M8fzOo~7A@uyd7g zAzfQD$_43t6={kME~f$((( z*EX(7eO`yABgH*vUwbUEA_t66U<2Pk?jOvh98Q*qV+6$&+%Al6Ek0!S(35Cb_>&V< zda#B5?aON&AxhP^GlY;hAC1QXz$3DQ4da-c^+17lnZ+L(j~U+xgMZ;P%coz9Zy!?+_Jd;wZ!XO-1yFj*xY&N4YF0T7q1Pd$*{>MWwoi7xT=l-|UZXbD|lT;8y;D}ZePHbI4m z>EQiD4ycsm;(3OnThKL%r{Kl}sr7-bcouECI!Fb5p}}W>J39LcxY;ho*V}flH|0aU zogml8pb=$IKDhQbBvS>DPhy=`J8Gf)t&fflY*3xy2EoAsGE@zDJH_Vf4YT`KhL*`L zt`Ma|PX;}u+pd6GZg}S0juYqZ79)^qw&oFTPZThBy!2OgPu#}aOR{fCP??1fW zcH{Y%20<*tt?)qWX;#*SBSpiy?y{n{{W#6Mp+Qv5v5)u2%24&olMdyUAD*WCUL*ZN zw}Ovt>TcGSJfF5gMxfNx&C_w0>-u_AyqQG(m3f+`cs{x}KaL~xAt(njzOT*yC@DPP zpYZbCoW?MJIk^Sv^1Yw9l<7UUE5f?NvzMF+En5PO_xiNkK1YD>W z^$n1^7{Cx%e1=LHtf89tdn+ezfFnfnP{_<>FN232mgZ4O-dW(fz^$;C|NNeu0P_5i z7hP1aY=f!CA!>~dJ!C(o-|Fi4f_rh?RY(o@C<4>0?UPC(7?7&d|9Ud@cFv#$EPO*p zi4cZR++Y0dq`?E#1QD&OJJMR$^A#JO+~wwq*s-!9Gu9^q)MC~-1i{Rmj*;ym4AezD zO)W>7D>&g2h6|7#)SW;69&D)HSC?*aY&n;Et`{h769GFQ=z!q_vS z!k_N2(yI2zHyBC4S7AEYhVV&UW6MOcJ%&<`)s^s5GKlBKf)#r5y!#5gM`TT4xR&8u zu}9Q;ZO!7QvDx#&@R<)!>AA*+zqB~Nh5a1piSioW+SGTb!na8OBSG9@taflrCsIsHZqaUy^!?Uef&1&H_P*1L?HeG5LU7qKq%+R;7;P1QMJbZciwcSnE~Idec?kvpLvMt6HhvcMCC0!YipF zfbX4HNuRmEUn+|p`l6M*SfPvO+Xlt<$91K6>fov6X{;?@1A5MdW?X9$#jRVn#zQk( zNGr;fh7Qx&wFQ|xJORJmhv5io_2f)BnwZ8*OgQ)XPfF-UW#dTg9~D`B+Ngs~%R1h5R^6z!ilr$!b`8-hp*e{ik%MizC~+S)XO{tmpTays4PzHRrRunhOmii|j(;hjF=-~12Q_mT0f z0s?0<-ycZciAanc{-NYB{A+x4&L%1bMC+8GUa9KjD*^7>M2KoJrQVvhR2E{am7`~qk zwle~olC!Ya-eebduEQKeJr&ohQ+H8eRd^@V5=+q&SEkewF44%i1z&A-XJQ-dgvv_) zRUw{avo%V=Wd~i;b6-p=(dT_!b!(=Wgffxmh7F!YBXlfA>8WgcHseVejdd7)2L4Rx zx?dhq(Y`c>(a>l_cZ?R;**Zy}dL-{k{SVxIlKK|yY`2I!gwU#up-TAFr;%stn$Ujh zs9T;a!8?%d$YR5N)qTZ9WAkr$?(S|_@NIVBM6dQ6HWz;PDbEzZjef7fnLm0cwHpO} z8ju=@SVEwoiMIVX5?6MWPOmxR{8%!4k~wkL;uc8Oyxw%n{E5W_JAT`fZOugUP<=AjCu;*d)R&#~WG zFsR2(J?iVA`P4$^?RY8cf#rJ8X4xg}S9jg!xNAf_s)XIbec00)*~G%_0OTjZdb~^R zL~ARDrLl9kzJ@4GMs41Z^zw>?XH;l2Ey<(r({k1Hzi($JEC33FDtbm30$$lcfE^8utN#o<(D0qXl4*X z1|)VnQ-kf2`DfW)h6LyWRyD5Nk=9{wy8^LX4hMFprhNL$nFq6GiWbFN^H8cre&sg( zVHq;5Mc?+Rn{(fYzW(|MhjMY&rUg9`g^eCnzzu*PXVU3|WMZk>3!|7~3AjlNmSol< zWu9bg>dz|o+#UO(N*fatHER-%5T6UMHijw~p+CY(9+GgSR37|Qk%z-ZBe-3_KBe4L zy(pU+eK*fsWf2JLTX2sUj%v(mdUfO0l-eA;OxaZuEc1`@d;OhjR8J7luWPvFT*5n$ zcda=)UI+|#DWb++J!J6%z{_x#aV=Ly$wOWi(xzYBaj!S6*p^qkYGqMY9r4f0X>E0M zDwFl^$>JIEI`B!X+|h*};xngCL`17iCBm?9%33DN;5C{1>+5W7+qaKwE%%+*Z6*Cz zVsrsO^@#)h&CGU5e?@{ymIV@{iWh6%3~dkz(C8wt5+MCmGH=LSMu0k?y1v8{G0VRU zwYy;>DO^tstJ@+Hn8N`I=jY621cF!+K~qqSn*B-zi4U=*_A7lJgYm z1Zq{9VV9YCvDId&r^g&(KlN^!>Iy{D`Dd1+Px;t(A;Ebl4v4c8?ttJ+-NDsTT*Z4^ z;?sKm8_qF@ez8EPg0R>#b)fy0ZX3UILDPaaab5nQxnc`>R$Hs^j{^9`tku;&T}l+~ z9~+fhP+)3#_S**U`&E-`ML=s>c#2Mmj)Qy|SjI>PDtI~`j&F|*RSw%Z9Y|LPiw-5) z42?-Mw>gge*2l35-yF?zzQ9hRkM@zMzM0MvXU>(rDLjO zEQd{vhr{SmWb3K-k;3+lf)8k8N)|EG>E8}*&1yQIY7AaZ_(SLu=a-Cfpkki)%opm{ zf5C}hE>JC5ip+veY@jUOI9s;`GxeSwnks|d_HQ6}ALjr8Wklzi6qNRsR_E8@EG`TO zr+Ekv^uvgrXnumL3HKYYm?8rR3wpz26EM+VKS~@`&y`9Y9I4eT%U64K{K2~+DE+kQ z1(zj({TKXRfm2+fs{MplLt;8TB@_}Lko2mPe&0Jl7>PS>npQ zMP6OivT9k>z6Nou)i9d6Hvw^Yu^96MOHg6V{FF>;N!Gfozv`UbCpT+m1HoG}2YX>aM68_iG--|ERbKiR;n)`sa#)E$tm&T2+x+6%F4? zOq-i)5*r6gfBI2h5UwIiY-}bp=OcXgpJYK6$U#AT(~L_{Nb|nOcUn(?MHF{V*_x;5eL*Q)B#N+QY^M9UU?p zSiJVkKL>43#J-tYwLPXlWWdO@+Z}S3CuB47x`}lKWOw4ojj3i;^z-r zhqwu)0KE=Q1cJ+an~g-hjgc0vgxfwjFx9`idnp5aMW9A2FHa4 zI)*-J;&PnApa3$VHUP3BwwJV}x4L-^u{?`dUQEJzyXOFqCBT@~6|n0;ww_tQl;|Y> z`mQl?0~c_jr0j10sZk&Xfz`7f_*-@B+fO#2w@8jFbO+Hbx&}QFn)h=>wxx#+e6;e~ z&Q(9)S_2wf&TVEAxkQJXv9Q;^0H}my+<3+i8%M%f6(+1&5b_jQh`3E=3iF-o6iDhZ z%o{p$wHN)@c?q9bd~RmWc^)mIawheROIgSB>C?IP`{$L4-wUn5+~cM98|Yip*9$GR zlmPGt*ZXam;6Fb|8RODlpUg+SOxPdaJ&ro~d2Wf!e2_7q4XgXn+BJUqf{ZM`;V%&C z^{aEx9m3kE50yi+KUF*d>ro(?;G_23nw`e+T~%uf$J$T-o&O-fiR{Sdg)q@MA5r3N zb^|^p-V}oR%9*WxFV{^qetUliuC-yDE=EyPiN#VwyNC5z!`-_~;>oZJ6K$mckOBM3 zTINkPO@kI97&;w*0B5o}Rd^n%$lBfWzI=i zLM_t`f+w0Nk0+c<=LDwm#u*sBSZ8b>$xlAx@2TI>CXt?-#TkgV3sw_v?Rd|l1=TW{ zh_&eG$arqUp4^%0j>ijWbpoN>E`+=vD{@97*}14WIo}?VOX2RJJs~!om2$qN96R<7 z@r3yK3CANuKRJG(xT2ocr=-l5A?1aV2)1MzsWq3<1&(1R`|Z&0O%HS94!X-CMafhv zh&8O83%Z<~!$RN1z<<0aCpAatwD%`_R8pp5`QcaNG@@axf4N_l7;$>T1wu~^s_Q@Q z!jz+#z!{a$?>mNzl;~pIv5(PJ?VHsM78Y|mH?O#f+$<*5Lgk{WT94Y1ZaWzgZl58+XI)iLlF7l;=i)8M%NyV5~uMqiuXeix3{#&J21UwThZ zR?I593T`fl9!^Y6aR9N&?wAuZhI)tTFn!f4Sm;x=?lKJK&6TF_FPN@1OWNdjU9r;fiJ$$3;<<(y-@_~owu(n zWtJhUuxd~yC#J_;ZIbGd)3ylldR6MPk8Ns5%?)S?#y4!NSiLBgzJAR!@ zs8gxnJA#zSXKV9t>XLS)0Ih+<$O2KzWZ^aSR1sUMn@@8zl3YZxw*9_gH?+S?Y_xao zz}?LHX#oDqol4($QgRh>3R*!B{TfF+1mF*wi*_v@d*pxa+nc;rJZx@+w3Nuxt2LR2DvlSSZZ6u}`AHB~(Ha96 zF`=MO?K2rpcLh+Rj|)AtBX zzs=FV@o6b_2)sBo9T^Rc;22rlkfrCWmQCu8cW(a8P3CF8CYnI+ooR#+lazyT>u&Bh)crpdqPf964hHpk zg|)RHH?$nG7I;HAF6&@E_x|y?J^$|!=lkLe`GX)ABOU%`(X-!}=q*sNQ0`@bVDN@--Eu9qd{UJR55orgO`a$s#H7w?`Luzpyk1ztPS}vutRkmACM^q9{ zI?n~jKJq5XK&)eQP9O(HNR5gTJph=0BOqBLdm@i?u)LyXB5s0yt--hl9{+D`xTpPk zn-Ik+#abs}P_jVWpO!lpCe-Vnbm7E)=w*caC{qcP7$oP6r~>}m8tXLk3c4ubrZVta zr^NNbIW$fEljQ}hOg=&28YTcBqscSX7C{RYF5=OBlhG(#{2HU<*^M8ylb3E~)}?ji zvt8f)RpFxD+d#(p#k_|2o(vXCqA)IKd=7(uNe!>i_>`tJ|JGbkM+RIFojdl18O@Sm zh+VXS{W4Zibwg?d!d1JjXEt{$81N^6ZI7GEv6aZ3^=s2#Y-P6E>V3;QPQZq7I@!;S zM2Yfh8B*XwvjgY6nW?^5l~zjYgElAdy!w?ux#81Xc%2s8OYV0R)GJ4kvlCeJLo|7z zy~~7Z9Nh$R!jP+jaAooSQ;XNEql7lGpG*;jM3MRe!O*w4u%$&c-5F2#GVW`t3h|#S zR5fkzCit!U{3{#&=l(p~k^KbkA?Q6mwh5CB^m(8`J-Xlq*ZEs-_7Z9Kl3 zSXK?UDgQA} zG?Z=q%SI3OD6-gjcc{eaS0s$GgYM|azZCT9{b4uS$;N)%i*oC7JIX2OS2*hfk6;89 zSpqaoFL)e6bzGX^j>vU1Tld(++htWTV%CmiRuSjbw7Y^>hHD>|9ZxDkO&9zGnkq*C z_1NGDA{$sbeZQLcgVS9ckV?-PNs#^XFQQ3xBbAR0FNDw2%3T9|z9QZrA!|2sDnWu? z#+!2HQ5#oSq`9(jIf+3_rA2DoCvxW|N-UR8VzPyjgwWHM>ku^aV`H*t<+gilW;bQ~ff|*(hfF6{o zVY&6_=W{fz`jq1$nRt{xr+yNc0}IlSV0wyUASzrDfTh1Z^Vwk;g0x{#+&oB&4nY`I zu8+)W@-NKlbR_D}OnH~`i=E`_)D}&F-w7rbjfI6GFzhy}HfHj9PKUS50ww!7AhE1z zPqaDeUWw63AZYK$$zD&+UpXDl_nKakXE}H4+K}KHC?r7=t97nE!TBq9V`hNsH3wd< zkH`G0dHS6%WaYVIFP_j&Y&?@Eg%?m)CDJYx0_$HccitFi_9lt2$e*8{kgXfc72*+a z5!m!SXv8Gfx~asMbU0sUZ$&Ji%uGz{-hp0l(YWA+MMW2h)}1CLe*hA+ z>D?sZ&&jgIi+h?nw-}cegBu1D(NC5fw4eqD#ihM%Ks&DdMmAj*X&`E>z+mFO`ZVmc zLAiYq$0E}aAat!h)tr$6xyEb~NvP24&RX$!N9`sH@DkaBwlHqu9{yAZAo*a@x&>4{ zs9+!l%RoN$XxG!|3E+Sov0ugT@J5zqYA0O4L8wD5@O#;h{n6sXRu+J@Bo>CY>rOwn zyL(q34iVB?_=wFtjrPJskVf?FF^bogfCnxZlLP9(I{+OM5JrUvUy=`PG{Dt#ybfdO z=ik#dAp#w^5g*~6Wm%R)S09EqNFz%FDswiD>86Lb3a#SFRh@#dKYfvvP}%}o;I1YO zayMRg^}mTF@&~AMJzC}%3tsyP$D4*B^+ z3p7Ukf&22b?P8mTxpEkPyC1mo)^B}YbXjkCJ^nN|oQ>*wFn;WMKl|#dM3Bj0xb?B= z+Mj%0;1xl==4QovFsK$}nunAF0;URB%Z$k>nwfM@f6BXDSMK_tU^#=Bt+riU#<@&4 z-Bqi584_YxSn2ge30+c+W0SWfNrkmap@Ro;*|0!5bre*9D_;I1k`W(|2c`Tn)3DO| zS-GVQj&)0&Lt2S!p zPhb~Q)nAQ`+Ld0bS%DaRwAu6PcHG-NJ)jsP@bSG0*r4$0@HbAMUQINgg)0g(xpJqpZJ~j!9sqY50aSz&JpT z?`Kt3b>8>pmP$BS4Nsf+^pJcu;olBp zizkX09wEOz%QITp%rSK|Zc5IoX#SH!ct%x`@KrntMWF%COz5xLEyZ zoNhgf{95D)l`G%Sgq?;zlyj+ zyCK&7xn(9Tw_u;N>duA;-0PM*Ih_56GG6msd)3!=hh`#yIH1|ZGu`satm&~3%+wWH zlj9}__v0AH16Qf{_Bz$we9-%pQtThZJWN{Ai@aZNbyZwvlz=UP()`(?s>|rC_MzTb zD7v{|kK5%Ju8$esLFSg;@sR^R@c_(|2fu$KeaPE|S#8k%?cHcQ z3lbp4!s2hWLWn0V4_j6n91f~{{aC0=OHzC`W2wR8$Wa`?A$tT$2o-C}Q?z8$d5F|K zGc)!&MW^;tuaux|YE}D+|Cf<{Mr|epSJFjWzqy2T?(;W!;-9x|BD@VaVDJcTtJs#? zlsO6%wtE`b+qwK~fqV`)d)u4jFZJH`+^=;y*fn*>=ZXtF`So@{>t!VvZ+i4_+Z??O zJei`c_~Uzo68S-ox1esh`C3NsFs~T^%3FHrRj}&l2J(rdnW40b^mc>g*GfbS_pvem zcS{Q2H40dXz;SGCXvxHak8_wXYdZaYQzbrsZ6Nc{Y14=hchH!zf6uG-zb1-*mDEeQ z?!A)<1_ghoUH`mA?}G?5V+Eiot@^*9EohKO$=bqz=h4U^lf|3r)-r-*DrFbjD7A3b{1eg~NqyX!|les<-V0+q#N_u;eTy-sWCj^GmX_ot_Tr zNYP6VN^mu{Ruhwgv!)xy_7^Uhvc0i-M|~Zz%`@?dCeJ-Q`wRKt*4{UHT(@JVhvKYT zfqLPpuhXgA&9>+?gsaz!Oy`dRzw|xXaZ0Ovc_#}yo|c(Jd0c|bCSlM4{S2(d@ZZH6%!u?C*f3UKk9R@#PPWzq0-^}IFr!uiM6gcq#Z z9Lxg+tu9dPwOYFjYIZ4G7&x8*DKzZcN*MdQ+41(Gl!b^0K$yOZ7*8Zl<6EvF>_$&Z z3D&)))s6+?G*E$djZp4JSQy#`t@i8u@QliB5!sn%SGguL&W z@#FbJm-Lf;_J`kr1{OslH}pGpeV#FR+8=o;r~`EeMBu>QzuF=PMh<6ynFB@2PWec1 zOz#FeKSqNb5r5zYoUYmdWwiH1k5>}ho5QA5F3S!tut93Wp5Xgj*FaA`$?)-R2I1Fz=Xos9vFNQ6lQ*PMMJ=ED8MObhVoM&Y{E760W)Z zgs{&Z-6CY=^8?zPIJsq3+mOMvP15QjT1I+J#SPELvP7<(p#Ji)GXH`I--*<5-{fjk zXR`4sXEG{;c!WmIH@i&ZG%HT@143*K0)Et ztz;@4$q++C7OGl)rNGuMIqjk;F$Avqs#!%IxA;~Mw<%Dv|2VV!Iux;o0hQ&^l;mov z1S5_06SBN(D10RcDYf8MDc0I7T3r=xC8?p>GP2z4U&YvuAdbSqI~MxPnjGHs)RK9^zRa09-P)zE0si~9xS=Yt9~u_l5OZk&=D zjINe}$8$*)(3a7%k>2>Uk<~z2lJowP-`(QiNiE+U4a=CTDMZi6qESq%n-{j#oU%lG z?ck`;dDC~{Q=}EiEjYx1>!tU$X33XeBUH5&lzQ$@Bo!r{iv>2}H8hUv<1ykE9v+Jj zZ;Vd2;}KHEVO*dYV`0f66@#s{^J#j_EH0+AH1KHUNaNv}5ynSJ)eAXnX&KbZ z*E*|K8WJnU#zoZrAS13+^q=CW3>)rg=cY|vi*i0`2t}$cA=e?0sqpXXd7nN2W z<%FN=|9*e}<+iFIz()-dszic6w7gRP;r6vj99dPHz`Rszap8fktkKl1m56{FA&3X# zwapurv`uO*GIRrMxzG^>%!`FCRa#mCq++F=4CwUXMBgr+F~}v3^5^n@&M(Hiy?4h~ zJdSsU9L(CYNS_AV=+8IGDuYZx9^laGoNTS2HMkT{ZGM`pixgImmO*?b>!etOEu+XV zt<1dSLXwIvOyoL$fa)LaOqv?BbWvU$6boPd+YRUYdF=gE6!w7Ei|^4o5`|-- zQ%=SrHi8XJI8C^39uZC^%@oAFK`+rcXKh@S|3eVZ8W@$EQVr`J;(C6$v2CXFVM56r z04~Q6qoRI?P&PUCwm)% zQg#*$IU?G>?6aV5@_~K3l)Zzd&?s9C(Wxc)lC2n z>T^7{x2NZDqeZAHyl$mYr-`SFq6SWatn^yqyWsKwI@LMkBXNZuj~|eBJs;V20#<2J zAw*Ao4TN6FS`w+;>;80QcA?zco;VpB2^H_90w!?+kDkW{>O3#K|QOZcXx>uAw!T~%MBvi4@CEiOQk4Alfzod4U>gLkjrq`iR@95#b={ zFt^-f(LXlE@d`?9sT0dQbZHscUM`Rbe_Q6qpR|#DyAOg*ls@*cso#me7W(Y?&kD2@ z>6;+oS=7mps{u`!n56oq20RC;^7L6vfI}0bQWL@-);Oo)o`Te-GT7&i2`ItW!zFxj zF@~_<6NXaLX=}1xIaH?k+HPJZ1m>m!-hX~#pHWz+_jE=~7#L^AqW4XJ%Yjc|Gg)(uj5&M(e-f6D$D1{JIi zA263w1FxW$H|6~}!*=qF*pe}9XmIL3A7-q0Je9CqoWZOI!NYUK?*0fA~0(ko$uT5IkVs_8i}O&1Mx#LW5hJ z3;tN2#EnEpFS>%WM6_@g_P83xrJ{3&DcFd8OO`ehMHelu4~ul}8ctt2ta|JA6-&_O ztLP7dWf3EsUB90sfB7g7BI(fI#H-ODtRz=uzMO!sdb* zASDE!=fSwZ4$n)^c>U4{0r%5@O&0;@$hmQcIe+N&ZgF1d6aE9=?C(@qwc^% zttG$FF0W|S|9n!PX9IC>ubJ%p!sfie3sptDakL})-T=j|lcm|qsiU69?N|)V(NCom zKHY^!-J%4H`%Q|rWm2rR+{HV=kG$Sy{o4g_6;ZMRj*6}55*TnE_?<$4+Qg*o*bKeR z0f~-j@35!P7j~82u0a$Coo(SZ-E!}kXlwwp*gkE};NdjY)(~rnNf8ftxZp%?)Hm-x zy-Dka7Hm7G7S05JAoDjnf`U$)T%G!?9g@~G!kPCBXj$%pIgpZ=*Yd0x;mNynkXa-j z7-dxq#Dl&P3Jc4W(0`n33bt_Z*eoS%_)aC_KHj51yB`+Nv~!k1C6lj4^NOp&6AStL zsh8Pbop+2;K9CL;^KR@_^{Wq=2zUx~h&e%c=&wo#XXY;c=&h zy`48HxHpAD?tZ+$?s27ozRiu>umjYdB?l3`@``=hMCM##nFdwwt!q(t3x5d4NN=(d z$*xw+Wfbr`MLjsaw{+WAcQyUn-7Ew}bdy3;OISJ;fkc8pt>gI=j zR0eBOuxBo1V)DYMz3BcxEUVDw_nTzT_Zhz{L7{uk8@2T}rN%?%rN6d9`2wLW+`gQA;b&HJz2CtJa%2 z-MFvYDETknPqZ~_oW0?%e|1HId%@nqtMmdq?~3f|RBA(VdEb&^ zTnt@B`x{o9IA%q!l4cE6oIb%XvvyOnc@(b%k=lKe-{ypKs}7oImx@4fAPC$~s#za1 zSsHbW$_y%dJ=#!_t85>yAKvwSje4P10+^m=G8r3~si642ks_ciL%Ds`0N)mOYfwCF zdOA#s^Vb$~POti*PfX%T#JzH8sg=Ysa_pY;-8Ne0R=U{R!vNO&?RbQ(vpR*LH)>99 zW6(TTi98KN?Q9k^kOik#jn&VhFWoL~@1JHDF5u@|D0K*v27i;sk)hHXw$+&$gUf&U z7J=Ejo>!cGx83GBl~(BppYm<#41McVM0g0XWSFFR?AJ8?Ce!JL9HrW|+VeJFk>>G_ zJ+$r@qP(~@y6u#H_|nUf%C|`rPRmX3q~+%B6l}~%W{F(%QvY9F0yuW09y-2Su1P%%PBjOo!WVA)y>Y+!YqgS>o(s5y_GI7i{{qW2(~UA1^NzqKPC^J#3`ky(XwJ1%ma@t7VHQ@(NV}?XhL=d%~k3@HX{QxaxKo%FaofnNYKB&(N zMf1Bn;^mGges9cua7^&#ED0epgSJ(*c7Su9irQ!a3lbIpJkdLSW1=P0_g zaAlGTWE%z7Y^LpwZ8_uA(lMdth@#N?@=4Hw##M6ofSK)1r_n4NeHzT>?0}8z;gH~R zbd8W7y~=-N*ZsnO4f^t4A}oNHqqm2ihH>?BqeEBH(rmN)3>Mi@o#py1&K;PcWcD#` zk0wH2laZL#9Z%N7iYI?&nxZn(yYJ{k*u<-a1G%Wd%*E-0Qco1T%J&>{^LFVNOGhmq z8OtZ7-gVNVhdIUCB%zGoR8GbWoc1k78q1(;GI_eJSZsyOf^kKvSOyxB%sHJehdjVe zvJ)pJ(7DPF{(ZFoqMJhx&+9i0EPO-op}yUu8-=S@(aA(rVqRx76l8Tk&%4)^5>cgP zo)&jdZdOEJkLjU5&5%JJMS++q0C=Fo<0MD_O;=qLSVeV-CZm-iOwF6 zdjEVJMwJn3f_lKatnAoepdr}en=K1hM-Ux_vspB!K%O47C1yycgXq?pT6g^^8n8XI zf&(jLv6N|<)J?XqwPh@rcO77F>QW*q(e#GD>;j|fDjlK$$+ntQGO7NgKbb5 zSD%X1`rfkTA};)t@ijGw>f?7VgOu!@F_VHww+gj+TlJ^1GtWY9f=i?zQ zbb5A82RreX((L?T09=%#^SEQS;oRV+Il9b1JZK3_$+uTO)cv4swsISNpkwB)3} zYHoe4P9B+lL{Mj;2@NVmoO~8A33;TT36kp}0o+``X0KU`oTsW%*Lr#2k)zbk4?%sG zY`Q9dfP5D7ElA2Q!8|f_S)Kv80B}P7UdC(`$>?i30>=dxc<2ZgYr=R}D$e`(MwfN& zEaJXVvh0kK``m^dY9vq(=78FwT0fh}1FiAAk>4x<7Wm@j@Ft5u&E=@{xry?PB0z5z zrWO8PVR8wa_*%cz5L$Jm$Tiam)TOqj|H44pk`WpP$DJCyv>br~HIt}l>n^s8%gseN z0)P~x+ES#Le{sO>D8|V(rijSQbxO~fyVbteFbzL+JyiYINBZsSUr+VV&8{gJadK#i zn<<|yBq5Qrir~7P8QZIg4Y+Sv5rejlU`U0e>PDfUO6mXLdv%zkcji)v`+mlSjvTi%Y z*Z^{$fHcGK|a+ zbTlX=z6sd`|A+UK01yJgM47EION|wrmL{qj>&%x%M;_%i=4tPocp4`V{A9vM0#rbj zFah2oARCfv(VH$BhCE=qm`&8()8A4SMA+=(yO4XjZyPV5tMJ&Yjv+Da1W=pIB@-YN zSVqdBH*uX8olyd$L*WSo!?P}rZ6`Q5FYX-la?7)@1^&gv()CLjbE?f|9$B|Nc?`TN z6yxU7&B&7H=N=GehYAI+Nydz6HyV1cB1K)TK_pF6UrhQi{2yna7jVQV5TzSUPpm?j#V zZM46MSqNiICr%$|sv6({2cs0__bS^ca;~`B&bD)~+YGj-(2S~NI`pz=1srVc{P#9L zSOa=;Xi<~V9mwUmeVf~53MeF&m;gnBWSV5|i6+{@i^+&qkpM{Y+;8y`t)^Jphf<4_ zB4!$+2UV^yBJnl(t!PUZF@FaCeUau30+4&*Q>A1(NAyexN;r+AyZPV?p*DOwBe3-_ zN5Nfbw7;t4LyviyQU7&&v}7xMR!a)}`Da;_Tc_wP(?Xz5f(_ya=c~K%`Fg6{Tb1_$ zBR4voS2MRx^)$nr|7cC8{|icAjGM=y!YP|;fGQeGz`e(3QWnb3MTPX zWTk|xFol5{Lf6AF^5z9%Js{N*NE%4g^a8t7AuI#r(#2IQsP)QQ%SK=Kkm?n6EraNo zLsTD_z(vBU`e4XdLTIG>ZB%3} zHXq;~l8E*}W0)fN>R6ZjJ(WBxMoo<_lWqwF7rgEtIX=W^MXO!zS1%TLFkVCf$)KV?>b;n$V|-^?+(@i9vh5k4y|x6e&{8HhBheP zgJ%;yW{nRIT6jsRlISR91^4Ochg5SPn{^9G9URJxZO*|%h;rMxp~NaHo*vGPqlP@^ z%RKC8o^?^AyaEVC@~X+_A$QiG1M^%%G|Q1|qUd9F&f&joX?JK(cC6MeDXodm-MViZ zja8eTSiY>rYSv=-vx6(zvt)lVn%;vxI#&`7JL5V<^`MI!q!1Z+)6*Jza%>U0zYZ(Xy_88hEM9+n z4T=f@M?4u+7T0}1 z%=-AiSph1pg;(YkJ=TS08H*Pcd@2M@aNx_5&8#>w5VMu99Gt014wX@gUjZ4--VTN# zUVYo~REP}YCA)(+*2UWt$QXpfhjHf0?gCc>Ow~mEV+A+c%D#04*m4hc(-OTbZ6Dq8 z&s*lHO<1I6Mn3F7ukHPY*4v95gXg{e!SwH`Ld5>w8-%YH{iRFs{yU@yxfzXSCC6P<6>NZ_?NO`h> zFk%YIvzs0r3$1FPEDoOQJl~}&Jf9Y`G+GmC5Hg|Qn3aOXokZdP_gs0M8I0rm$8WRvITC-eWljuRy*JS;`LAm1`|;)>BnI z{|%uD0jPF6LTmECt013~*n#MaY%?|v)4TZG(31i&1}CVRo%ghh|Ew$v&#msk42~gQZ@|g^8wqDH=U;{NrPQy$ zKJhowKTwvBzG2b7FlZdZaocx5Pq)|VpoY>5N__Vv4YW;Cyu@^nf-rhdd-QO9&v<_U zrPhQ9_J8qojp21hTl2)WZQC{)v$5?HHFjesjT@)2ZJgM)Z98f7^||-C-@o_&e)sG( zYt5PgN&{-rD;1Tebz$47e$ghOf)8d+NK_!YaPy{Jl&i%||SQC3)IG)tTzfnCxCm(qZQ(8m^bH&t7G*QEB(Xq|v_#gAW{ zZviZ0v$Y@E|GJGB?tXKl$srEL@>8Vz!z`#D#jMkX?s>s1nwxRj2yuc^JB5!=jveAW zu`9+YC@8j#r)+b}!Cuv)+o^$n#uW$nar2@~cg7yrudGlA>%+qTn-Rs@LjUX=sfwMO zC-QKC8+Jb`L%$Aj{9Oj7=wDOqMNJ*mb?~Yf>HWZQ3}&M)Nl&k}Vx8Qg?K)yKT1Up; zjj)11?o6t8za=`wvGVr#7Gk8uXI49Ae7IR;Gblij0 zs{H)^Sk-gQRrnP}5JXQGwIDbCiYxspL-DE>bp-v4+|u99b*?i89!>Svx=+d=!7Nf@ z8~H3oG%jv|)cV}_hcF@mu!JK-Uhuh@+X{nH#1$YnO1`Q9UwPxuQd}GnRfL`eIo{CS zrA6=z)7B;7kO{ZWo=hq>9Mi6->GT74-yHq?UQw6s4FUNMkv3NNsbF6Xvf5Rdcnepm9wT&zE=Rv79s=X%5F?VDcpz**T zxqknQJ(WM-!?L@%hsiGR^GW2fv%{rPRmU(KB3dvL1Ea-BNm|9+u~~@@ve7A#TjC_l z->RYCs;)iOi34?-yoji)&XxW7z;19eC%qpx=RilywSKIgeZuKAkYmaCZL_8R-*|{H zQ(6m_&`pD#kR}fklfZ9&WfS0Zj0#fZO zPm0B5z;HueRsvj_STjnbC39U~it!i~q*P;_1Q0__W_9|$DodAWliTR&k;m{kcqLJZH5dJv#ZO|x>F%zl*5PRL-70to z{tcr>ox{Ufmh(Hpf%8L)f@`t`Dv<7kXBS8z{4=8{!lYQF4d~onzH{ueOL5axEXFv3 zD^jNftemK!;la!VJa^e@4h`VjSpyr9onDJ!h2$yh85FV-s~=3-+Bc^Nh7vzwRi{q-6a}R1fWnzP^?%;Xv_@!}Q9h@cPS zAASzIZ@S6(v5~YcR;a;jp0PA_rA`;{OV<}_`P*pAs-2N)#6;dJ&7tc?B z4tw*fe6qT%m%?^MC`p5DkA*d5;;Hpbr0(O;b3F-09Z}4T)p9(^F;u1S3 zuv&Ww#<84Wz8yu`5^;9&DAWYY@I>&m_r;^ONmLRsgQ_cBi;1yPh(E=DNBecHf@|Os}1r&6LbhLsze)G?1(go_Fn(I>~fE zeFE?wD*j*4!jcHS^;*vPuMsB`{i>6h)wh3j(b9AAFbSa zT_G$vqP~ej7(ezA>Lu*JD;Kp#Wf|~)x8pNQ9Cng|c|hM!i=pPyzSl!7Xldl)H|Y}n znEHo!?C>=-HDUCSqE=(7GN$R`|GJDnUsWy7K|M5rT3MD)IZ*s?Yp2ZwXx*= zoc+V2U2FX^-(mchyDRxr(U&v{ywSxHhR6dZEmz5PDFb-my@> z@Uh?Y>h$W{YC^4Da9cVmNM%cJ1}#$Myp0iyJdkm7A_(hAF(_5@d?00GJ&hg*8-?}& zJc6GWHwwIuQ>CS2$X~C#BM<5QHNo7?6;TfsdIAC?sQbHs-I#o&dTzpM^`^T{$yOAM z47h-fQyl=^(#S?5_U-kqh!YyeinO{YBwKuA&VUP=t?k&5v+4oPaDW+F@EtzV`oAc{Puriq9U<5H` zeK}Dubvf7Mw6BQ~FPM5+R0=@uAs^`!@BLNWghd5`SzSnSa;;IM)e|BLoOo)b7K_0~ zL+!dz8yP;Hw}4|LE!<<&X=Q@956mSaz61SV^-@V$2v}f5`uFK+{Di58@&w`AVh6bc zax&Pr0^3~p!`?>$0wC$aD!6m%e;%+IB_0)(QJlxul&?EO*)o@|oiOEa3Cfd{{vBy; zeQ07YbexH@q%#+&s{gD|P54)3A!Eki{A0N*(n(n|#k8A-q5GQ?jL~*<194nvve3ej zL$k~DumKi)k-4-iNE)4f)pf9U=zP*!!b(pKjOJ4lv7ycns3zvB5qdL}@_`g=*yy(` zCzq;p5*P+3COq$?;N)i^(CnG_lEgN9-E!RylgIy@bO*5S;t1;rlDkWKCgTWU*AKmt z-TBb~Ro}hBQKUTiP7XvLHK|7H-jh$EPKkB78DkaW#8T4IiT42AW!bcklR)R~RaYhk zFeW*GWIeJhdJZQGK;}FY-7k_QIDz3eyl5Zy!s2+{vVqhH9OD37$x#(}W5tzl^3~~6A#YaIEhj}$*=3stB)MukRy#}w2dD$>VO{Ra zzTtwbA$0%@xO1({;f?jAb60+$aTP)25B^X^fC}<>s1A8OC9k0FH~&p-+8AW_KNGGI zN0{2nm42`J9Ojn{jWn}{t%-gXIe%%2Ey+4rYI706yc;`i&q3n*qB-i$8`z+n)?v4$ zAXxrOqc|3cyf`e#v36uI0yc!J(^GOm3!V+|>?`v&5SeN>puiP^H_uB{p0*zEupVYm zP+kDLz!}8QMoy+I`U@FTi>`nwd3vzTYn(E5Lzn{q%f`? zsXX|}aL9<;C}Mb=fUyj*mZ@{v0PE)V+^CZ*Y<;aItLDD$6g?M|+c<84sR)J$?3mQ= zJ|Eyz6|l5~3!ea+ulw`oBJ%JjL_Cq)nOmP%m_Yj_uI5^mAc;eusSc#k&W$27U%2#} zN;*MpY6HEsw*4QBAQ3~+uFXr?C z<1E)6Brfc{(I$r2@a%4(=L2^Vq^ygNw?QiN4tK-V-4V(&R$J#bIPEwO@Tj!`Wp0yP z`;i!*09ii4}CHpEV$w+kmM>@ABc9?+7A))ru459gv-B9z~7A~ZiAy$F-1 zWI|=tFpmHLwobB^f`8Mk?YtI;CLr8uN8Zp;ueiDcfW#`>kkKMcfc`U*>Cs19FH6*j*fQH+zG4!>xbMKyBp{Ew{7( z0zD+ObA2=oL%a$!3e8PGvX3C+j;@DCv3GF%3-Bw<*A#vNLDagh2W6D?{rJ8WdC%!g zMca%MI9Ve8sPh4m*cE=q!1LLH$2j+uNOixbv!rynCi|6joPGDJ28&DyI(LxN;{7GD zPeEko%cE*vFZN}yGCFrOgD8P`i=MO z85IR!ID_%Z6PEsNIs2S3CXHpt-q&&ln)Q_buT0BF;G=*+c|$aY*WW;UL*m97;cY7e z$yq0s8W*r~=>wHA-e`@&(bU0@J5 z6KP&KiEsTxv}P*I!Wex!c$`>Cm%__f5~n01*M@4su8@jW7zd>e&BR-XCnEy}_|*4rPt zxeda%)SHHK-5r9&8MqV(>8jJ>)wj1puUBJ_*H2VC@}Peil|gTmU`rL!N{K})Pf_Z5 zq9Z>xFxUy#D(liUL=mdO`EOK4M!A(51yq>LU4#|WSFo6xb)U5%bX9;bJ6E{Uk(c}5 zjfTg(P%(yKLdmHSuDYuIxTsWs>X3tVIIbUwy-HSMWx<8iU_@(QGH8&V=~l!lkt0*s`*?2j7TDsa96HWHWNy)rww9gJKX6%+>?mzYJst-PDb|U)S;BmUrchld5 zH5bmc@&R}^4b%uayR5>v^>cF|Hb~)3jA|gdG+nfLcIP;~{I=Tf2x!}dn9F4nAig^r za)LFty@H?A|DIku7;wuZby3a<{(cID5Up(s$P)7R9SI_bxG5RI;fFyDe2sUtIz9dk zisrl`SO%-FR+2vDiSlF&%4sx-&%uRM=(YL~lWnYmLoLTSF9tFiasx(>td1kAc^&I` ztL1!INV&jn-1k;+vFQLN0K(u4n>00qKudlnNK^nR{Qbou$G0S>TK#T*3%W|-&PlE} z#Vs*)!E^Gpnu*{Jw4kG6(F|(PoaKg193e48f=s^zIBM@_&tCg>h}%M7X87gOIus1D zs62fJxOR&0ndO%y$AfQK_NDeVY~k*rwJhTr!#^(_s+D%-=5?NK=aKHa0e~i2x(IgH2yqvhWB%CBr)C#l=4FH~7!T^GeT#7v z?B-+t>x9^>lLpCR$m^dfwubzH6m_Q-KKqwY0)9D3-WM-iudOB(w8C<2NbHecFViG- zHGTvOvGe!bDyL{8oM4ZaYegYlZ%@g7{E1YMG)qud3+?)l&lL(-dM1lc$vyZ;%-d>S zWZz1Xp6-pNFtRCN6D>xRgjY?!C=jLzQ>ic0soZzaDfSrRxzhPP!T1@FqN-u&gaX{R zm=)cFFY2|$%pY<;#=l(-rg1P1v_7GRRdfhgLq3DnLR>MA5wxxXifkBdqZYTq?mid( zIZjt;g=M=%iC0@XkM77v4ATa@W{V?M(3LOC3hl z1x3^pRx{kMk>Fm-$+s?3_JS<1hDf>P^mYJtNw}IDUMcFFnRI${jhSSoPIggORk%ounBu9+ z0+o9o2`NkO?`1&{*D7L~=r(JVpBL{ym{29ye=vH*B6ya%aNYCbxBd*t7{9I(j* z`wGXeSug{Mb4)zwiLvxyhGGO!L=eTWSoIaOrYUSf!E_W}arll@ z4w2vEAX@cl8U@H;j85>YTwXhcqTjSXGjH<^5b7v3*#Nj?G8d~27Np;qcq_;&t@-e- zC1q2a>M<4R5*QxZ*yHqyHg{B*^Cc5WT9Q~!eChT_27-LfXcX!A{L+#N3ibhF-Q%I!%_?BByua>2E zy4$L~mVm=CUUmb^>PNQeosj%VzmGur3pX;y#(W5hC4lGrK$y{zAO=T+_QSwQbWZlA zSzdKSOe0D~EcOQSQf;xEX%%HOZv3HMOLBJOMv0`5y_e??f*pCJN+5 zjeyrFJ(54L0kziGzUhF61oqAkF?FwskoxT0@w+)!N15l9seSt0N)m28pZ#zyl`$T} zT#!}Ml%aR?cpOOxDW*q=`zMxafmIF5`e|7u6rU{b!nXOpx5!*9{?QkXFCKmKApGlE zBhx#7JBNQ&jCUoENO(nhOy4EM~ zlJY$t1`A5LI*rdFx-QscXL${My9;!KdmmpPmlk@*l;?KS?iggnvta`dlbM>I@mj*! zEwbVl)X_-)1ow>G={ZHa6stBF^eZ$-_wVn7AFF^_=u+@Mu0os1ku(_&B3$w2Td!>U zO|5`TFZT&Ue|$i~KPna>Ks4$yb~o&|AQY5*ebN>6tc1uPDylE7V}t$uL+tV#9*rx z(bg-rRtSs~tznv4^wO^5T3+_;lBH1 z)uReQ4C7_GMDIii@2O>%JI#w<_Di#?=i+=U8Mp)4^??4*nwIOyW_TQxguW&Sb}1%3 z3k%CN%C$scn9#DfU2@&E800|B* z>iOW@%_WoN%|^^$O>ZsaA2`;DprU*nkF%T{*d-dirzj{^NKCUMpuiHRR39pf^IXml zy5u@BkFxEOn-31vwHqV-id^cUlpb9`>}}fO#8hF4oSQryj?p^werum+Re}+F*~53A zd5u=_umg}JhPO6w{$OCM^voRE|Y9&TuHDVj~&=ig$O*Rwc>h?&wy1MEpTkUy{ zqs)U-1BJ_tov@HfCwF|~k^IfS&O(lPU4ok^?fRv|WNFW1)bsU+ z{L3edW9R38&Y8dN;uHBbKfNHEC2RoDPs=4X-Y3 z1+3dIc0nr_2%q~RgLi;-?Ps7c(GHG;?b}P2Ow7(vT1N~NQ-5PWD39g#!s3{qwV2~3 zJBLrBtU%xM?j=iXHoHk4nKOeJAcGjAMGkkxLD$^=-Q;}dutR~HnKB3-ZSrc6U#Fcu zwQ^pgyn5*WKIEM#sM(N{9X2QL zV_Nugn}IRvR8NSR>r&Ths)sC%52^e4B_ZVYUbA_GDq^{=I>d~)&WF_22l{ZR%lh0; z+Q^&k@x%;9tYi*mb+INjHw3Cw3DL<6YO{Lo8fJzqz`xo*9u{+5+{(w+i5Ix7+^F<$ zjL(m1Qwuw&FArbNGbACCx@I;ArJh*b|35=z0jv8btf6>m!JrGF;*JKdL#0Tz&Y*0p zkI`&VkjwDwjoM(ibM1;Qv?8R3~&{;Uv&9B(d&SL(tc)x9jKt^ zTgRYA!$^l||FPIyvz0&%7^>Y;K!7dXI!t-SZLgu_(JT1_)NwfZJ4Go0Lo~)vRhQ~h zGNSi=WhZZOI!nhy<>sC~)%l+(H_>Y>T!Az6Wee$4JC2B--MCr})5n9r62P5N0%nX=hAe&B~by8M(O%gd$B_ii=I z>Sno_rpsl|bj9byh+REYIxEvgL7tyg6lVg+j{QBB}qQBEGg=IvEvKU(Lp$cNX- z2en?uuZv#(^>2Huk?hL5!qAj46RA@X9;7Oo06McS9BiJ7JAWQdjbUFD>^`pXtV@~o$7UTqLjFf&Y>HHkdQ(2JpZ+F8ZQ1%=EVwx+pOB`zGvAm^@Dx}qy(K8_YDH?S0g2&2MDL)2 zm(sq}dJQi~&7mInJvgeZ%0HslYqLOGLNd`NQEK5<<~y*Q^QTSySmZ>;^Ra_1yVMJp zf(cedc35$$mZ+1PuJ|9F$tj5LQ};&+mR32{zf2%m(&Vv(fz5QX`CZAGq^TgRL23h8g0sC4qaYmsP+#9d`8;aAf|b3`kFc zH=$%$FQ01=NMbeQBdi1Ye(=8v2Bj2IJI`Ev3pw9XKoz{vkj3mQXQ)qA-f5DD7blo@ zjBMl^6!C+&;p$k9D111%%gJHG}^HY;!a7O8?>h&ff=oY%j>C+$4c z{2H^ng{SFT-X&65h&ZcZSOvbs#uUCEszh-=OZ?J%&3(e=p}wR|c~Jv|`rp=62hlPE zz$GArTCc-+v!_f>GmmORBxr{8Rn^Uz^0@q9<#$}jc(8l!qr%h=uRFW10~&_IIVi<4 zwZc2}SC%y^3C+25Da)k=QuzrcIuNT#7+Qt-K8&~5^FB5%9g4@M7IG4RYdK@BUiFwe zrG-L{79(i)Pn5BGcgw}zjvTgcAMYA;Z(%G~Pv96ed(~MMpUk~M1+{H1B5SGK0-;LK zo>0JDt|(c(14&gAQ<{>qj2Z^!kn$H+OoMN;7H4^PaQ#8*!|4ZF#dXN@0u?cWz>0&X z&r&Oq3RrHT8ALUCt?TjkLz zlB@YAk}($PeXI=z?1BpJ(Y^wl?v9{Lpyx{S_SL70rQZf-{O&X^6rKBX1uQ92S4gSE zZeFY zU}PGw_+#sXKI6?RKY8R&JiWWmouRZH?bRvyEI(|;*;1?#_&FlQF3YV=a z9pR?i6eSdW;?K&(c=AX2IKw^^OpLCIH0x;QQtOne*znI3SBJMIuevR^QlmETiWcCK zo4LE@9mcc-{YmUedF+bWCv*7x})yhUaf{nC8l1x*kFTV;OPGm)g?2v4tItsIJ zwhxQADv-hb-*V+`>){u{q}1ueA}w z>%4L-yqOALUd8v~ErzTb0v6z$w$GdXQyrliyr&ELTo{qDD2(G@y~FcxMsj@5DNYI% z$RCUHSatM&#wTjdBKKTux?OsJU6Th`K1l=_Nk1}404U_Kp(`GV5=&uGtLkokhs|eN zk^O^mO5c=#D+m^_ zXMZQ0af`)nw|m>e&#`N0LveP|g>AW%?Jc90Pp^(w1}g7oFS!l~s`B&h6jJ@IsSDW^@niyg0n)m1n3muyB=YMoE)B@7Z3+{xn|*`{2n^-*dknjc!`f7EFq!kW$& zxpY1y`NND$NopZ(d`1dv|IVXav^eJ0O%LH%hg&T+tbcsE{r3ldL4Wb3+DSI_-Rc0) zkxOz}molKS3t1q@Ys2$e*jT-FO4TsUDZ&0l|#^>FFg0@ z`*>`I_VYm$g$E{GSsaW=p1}XS00_joQ3L5toGNp`g?I&^M^q*8JZu}QFp0!Rrw*Rf zCW&3@*ke8tmoAsFPGQjkj&g=Rh6%`*E!}S z0Ve!lhoK3U`Jj7uh2*CHaDx1E5lcw(O(Kba#mXkGqMk<++{5Vx%F~Wqvh=T3-@DEZ z;U8&YqGUK&b0rfUCS-(AA?fvE_}E{b+_+kv7T=U(bGtLgtD6r~Js>_%?qiVh&$*(llNzEwN~IPKcux zy4(3rm8)w^dJ`${5m)e6v;`{=gm`_Yd%9AKjulYM zDR@qtE1|Kj-fvg8h6G;iYA$PZn*SQzN3VByYB{ual>>9$nE$%A__eOTId0W;1^BdF zo($yPD(74@;-K_bPf72a>AQa(A%>KPbkFsvF`TZ_R~KLQy~bZyDN6Ov|3lXsI{^`4 zD;xcQCy114jhcYTe-pa@K=qRZ_*zhno_r>G&JWY3*%&v5)UuF($+4avJcp=IR4AT_ zN0hEyN#^&;l*xP}-|93kLNOF%OKznq1u;;s1|tz`YgZh?BbZZz0c!B^`kGgso2Ew8 z1@d$XcOESH$#%*OZaaO3NK*B|v}F0DXcI3=O9*Psf}#TgD=Gqx0$|3KZLo|Cn#=3r z5!yP4ob%7*Szxq4T7PoZOG>#AQAp-R1&nz=@GeM zFbeHc$TsZ0v%$9Yiy^5F=dWfv216oh8y}rhl~+`cLvOR z``OyL-_KUxtDdn%<=}xJZ6}E5M?S_ZbZzh|9gGi^X-He!tW+}_(RfIcn0;>k4YNya zPjk|+M`EuXgbhu8QgR{?yxe|0W_16zQY+QKCCgZk$6gqoD>+}3<4LNC2d3|GgG*>NEVljE1g5I;=Pvq3}@%T%ehJB6z%T#+=EAQ)Y62h{#gmHYn%JVxc7(PsQ4BM+fj%Xftw~#rdDi@(MqBP}><5)xmz zQ|8PWUF+3e^U$gXWM~i`oin+xtH@wMjX3@R`9c zyNW>APINBs2#c{h0>e1ptalH8@L(g*UTCwQ)NUmz7dT<3N0*1n=lZC1Xhs~N(davw z)Dp3lV?~#k(B2*qiIUBTW5d$KA&ZYO_bp%@aiEzvk>NOjK@MyvuA=8hyutY~$b+>- zSFz^c@jCy8V-s+9f(1c3LG)_mA=T`U1X=OZ1S0sAR+B{_=!6r2qHK{ji?=2hi3x~) z>Zd|fyUEBprX2jrrYV=lrt9 zA=j$F;4X!2K<|dxgvwhf&RW)1Iza?fC~X}0Hs>7Ul=lpfrR0o73V$(#Gt6_>a#E-)8JgKImNU16a zCPw54_5W;GF{kUxH0-IqDb`!UX*mn$7|nr3SFak`UFglvTS z*r(yYJfuJy$ygz+U0*cjM|k z{k*sC=G1I;A$S!nP!yBS{*>B~^Tn($2uAIZk}bczWzGGT2>K2gqNy+|QCV`Hz2QEK+H6#G2#I{G5O-f{Vn4h?q7^Py zyRc#WRvk9Ob`YKta)gRh5>0@gg!~&N6gyNBUrVHIWkue~Ys2{*NqA5g(HtLfQv9AInL z;-6CNmRLwOO3{aKKFz#tb@#bvr(yIO<54P!N^IFTnkKg7b>@O?F4qBuB;UQDIr|LXXi*}SV5izqN|RljeP;TR zSP*m+&Da|KSb%U|ZG8F?LSm~%fjUF_va-SiEITPWxLs*Mol`C{?2k2pmIP2a_%iD6 zrl>vj8Jqj6v98$bruY;aUvEA$zSQF@nTUC3Nzv`aFhsxHY>M)A@PS&TeA8@ZEP!{a zgD+u8U@Y1B5IU)NNEY`U0w#KGZ6hQdh53P>c7T0eX?4M_4I;znW6*mPb{%=!Y%f+@ z(1nVffO)<-L-Mizg&Ci=a_8{uPkXgeYr#JpqRhq=f#qrlL90# zycR7NGxZId9;F%m$(d09NX{ zW2iKD`1_ZTkjt)m`!bN1$t4&Juz=JD&Y21>oTTkOsNU+sN2Qntw(J#N7^$)Xc7i>y zNiWl30Kp|2h=&R8ox$fv)SZjCzjIL4z3reTwhsSi+q)W$FVmoW*jA=6PXll4_3=U$ z8*RhMo8Ou2=L0v5mPnLW1@I2=LRoRPeVo)qMh#ReyTf7afRPj9b99k2fJ>lF z8%2*3vLI7hV%xdt+cwp3reUvIg~uxNzsptl!hw^hA+p8zSa#H@Ed=Aij_{<|F^TR} z5u}v@Aa_ID=orQCz>^hWQei-;oA9QoBsZ{&=0_m`f)VdcC#7l<8apWuTL7(;JEhg> z9;r}Hm0Rv}bOD6*(r7`=VPsk6JxmCYiw2T58Y9L~&okSLvKq3tZdsTQN@LZ@$DFgy z3n%uKsml8lD zqr>#QU|K9Eu(X^wK`BvRJfwVZYP?C+Ro$1p{(WuwAei8dZZf>I@6Ztdt-B9o<>1&?n36~zk=J2rOY%9B z9<8X*yBjM~p|jnCTiFhoi+&qAc!`;086fk3+{mwy5{)DTB0>4f&!!Dfay03{TQR3K z+bz4Gl9>tt{uW@kw-t>1q&fyAcMIE71pTdYmPzEEoA{?}1q$(H?pzHhb95vp9%?z}wOP4IljQ45{UAs__t zHG%#Qx?D96IgSCA^^!Go0CD?x$~}!T?$WFUmp&co$pTKE`(wOw%^)O6dWttbTu+vUTfdYFH!=2`&jfg4E5ZxdLpensXL^`EQ5K^ zxj*VI>Cih>$SWcKLL>R!aFit)O9|w&i3qjY(NNvaCWu-CtTe-`kOE`{85Q8hSki`q zvtS-FMlJ09v7fPYfPmr=cF8-`p3tKU_~|u%O|;DCl=b@U9&@TBr4q_^mu+jSSFe?` z59mZa@m7FT=go>Y33x3v7~FMeb|?z);+b|^%*#9R_N4V|Gs^?R^ZDBea{0^N*3XLv z)@23pjus(fF4tF_>rhB-^AzrI>G_Pb*M5$Q+sK3TAqT?+6t`k}n+D7wi@{tY4e?u% zzyIB5AWIV^Dv4&w%Bwyv#3T7C;{zeVvNg$fR6NSRCBBgpv>DN+Lo$=>edcM>Maf;! zhzL?u7Pz<70AKR`Qa;-tz6iRz7NKg8q6izrNAwmex+@II*L83%ctd+MB3s_N&XN$6 z@R?&3+P=29_0FGwe#1+UP~1sJIWb8~&F=C)?OFf~SuSO9TN+;r-lK4&!9u{*5fcxz z1cOwBff3MNDF(XQ3qR>xGJ-cal$t&pabSZFn#ThYQY972sLEIQ{N!;`NivS6EA0ax zj}9f+G`s%WOQw*(7@Q~dIB6xT3+;QgB>>=<#q|Z`jxWn>wscM$Iqdu=Oz&G%?rIUy z>Uxr?!uXn+99Ht$eI&i6pFV@v|_n|lTBVlhdxfh`<5k_W6 z$?Z2lL9PqqWm~!B7ogrw)SZ2hUVYVm6Kpl8g_U%=L)W}H>Oo{P402-(^g>ICVY!pib$w>uFOqwcetGiGizd@=Bph7$EN+q-71J}E#q4=9*&PlIs!@?JARo}bdIIK zoO^H_{F0FDJ+*hBZimj7+KosckS4ssYFi!2&zFNo(cI_J`_sGX3}_y{U1RKv1-8X> zAwC?%f{Ql;OaHcJ>~96E*aq8;YPtedAD8?x@!CA5TO7B3Tno1zM=~{5eixQwndb#i zaWC5e)X2lTaQ?BetI+Syy3~%DNXa+7iJIqUfrGziwL@bpO!*Pam}T}2$6tRPRYi{m)<+xJH9dY-{;4(wbq_%&bc06(P925HdBBzGd&<9{;|d)o0F=oBjFBm zr|I%DFSr%kdyL0Wl|0`^^)eh|@DE9K$r32rNM4yIxpCvnS~&@9NZ|W~{s@l$r-tYc zs}8{mO*7wfIKJuIDcEAvxWMY#^#?p3rCmzzQFu-PiqZco+aIwnc*J-$!sMpMEQ(|y zd)MD4CdA;A5yFr7e~>mCU-{lr*;&GKRZ#crz2vVwZx2-t?`s*u_IUK?42UUOkMdzKy=@CS6Wa^3= zQ6mlU*yF4REq$<eMi*cRCMbMwqn8&XM4Uq5Lnj33$BKG3D6jS&Qp5%A0! zeZ2X4PQ)N_tVmYt)#`P!+jjG0I~df6#oUw9{HZo;JN9DO7?=tEUBtaeWaA_dlgYdN z`|2(BUjgW!)ae>cR)3iHgY0rNHSeTuBY8{EfEm0v;z?rtTU}lA6U5a^L+I{&ro1h< zPgroPxRyMvw)KoSm9z+DD-!%K;m6@reV9ibuWPD2b&DsY=N!((&joJ{vsD!)de0^Y zvuLVPQ)tmLX!hy((yeuW?Bi9P>P%F6dIA`#(pCV_VfS>`O|G_w6L{w>K#Y6~8}vg( z**G*4I=}C$FaibIf;7RlhHqq*U&gybkInIPZ|WjPb!D(OLY6*w&TQ^`qC<-XJ@GgNWB*G8A2Hn0DD(PZE?^zexUpB7 zDO1nLRItpCQz7$zI3>IHL_>K3XII-O6m){w)R@$GbrOk7?C;k;!@UC4_9seA*ZS5K z>v~N@p~<_o<@%djZLD?fg$h&hcV0lz$fmq-Gj_2Fk#6CyB)P_P0~bjz`NrM}kY{kx0ki}n6w z8Ff(Hz5i$d$FQRxL5<~-OoEsx9JU***k-GhMe>E-eZZ11J z8l|1Eu2vcgJHC#y8wWl^Gp|qNNj}h?Z0fwxkM6mrx*?6>Q!~305hGwD z{MZ@CL-N>{Gd!=7*m9{X{IQR-?TW6Kb>KBBVVNHTR~`BI*N1DFxkuKCbML=EldT z$~uQNjI|#`zJ4#as~Hh7+L0bVtK6T)voI8J%hT0h(KA+l^!^Xc)rGLpVPH;trWWYCaVT<4naD7|PNy z0*jVLg_S{t0Z|xDHvl%vt5=svrf5?uCNc+!&Z39a@y=NP2;l45i;nJO)h0GCoMfmQ z4+>Ejz?P00U8q(W7XpS3$(J{EL}?#y~#lc9HqWDxkxBNbF;QlYO-?7PJzMc`?Fb9N16@ z&SDU3S5#}ZT2eX-T64wZAkZUgUJ8-XJJSjPtuDb@8Rvy>gDgRtwx$=a_k_39ByUQ% zi-#q7pPhjH_&rFCM+dWXIumM8nTxEUTlPkeLN1njYwjDvvwm;J2(JFx#)}K!(D=yL zefcYBS%*}6YO+w6Hwf*e;rp=Ipp(XsG~6(ou-g6LJ7Bp~pQ}!9t*{?~l4UuaS%<%| zJvGS!rUH$%IyLrI_rHN-TXx$&!O!!}vhx=SB>R_&nwT$_daJ)lv=kPz<>`Z8mY}*z zUOuH?Oy*VYyy!%E-*)c0bN_w190=uQXM`%!R;uBqb#kX3naltuw))lEyg4;3Zk==y zdh0#05R}`&VfL0Cz1gYlS_R3IN%ON-sYbX4k6Iy1@9{Lw z`>otXYbudhwLxDp(zyXzdqcBM^<#YMenp+wys`V$cj)5QISEv^7J>5WDamsloCAsUd zfn00reO2TA7A(LkKuDPdEbXN$0t(@B8^fhNVhc^53Fg)`Z|XFlGXG*Y0+NG;$L-w= zdz?RXFB(2qA%UOj>eC;&McqRpv zB|_+h&gPL9M!Gj>YryVx)N4 z)ARYCZy?X9hvh;)iVR){J99I%v>9W5*tQE;3Y~_8AUXVUuEYrqD?W=<@P@@j`g(CZ z7iwGGNK$pj4xaVb$t={}tqcnI9x4qRsIl3qhM#p;JtmTBOg^{&-NDQ?P=G^CjjQ(d z%u0BDid)hvE=wS))y;68b5b5vXv#S}$s#-@(GZ{dr2f)~{O5t+=q_=-OsY+GC` zXLG7J+1LQ4uFK?5c*{KjT*=0{le$oWr$oH*{9letZ>g?}6a7$ayf#H+V3Pk-Y8tvP z;3M3p$l2pFJs>n%rsC8pAoHvSE-wBs9j^rr?pCCb*NNtE>haqk7ZzGCt_2&eF8!M7 zT(&(5r^XOWtYIjvW=Vaji1aD zJI~al(JXx6w@%Nl2t|_IK4WjeXM@@mA=~K~qHusGrNy`BEBJ8Y*$w@ol@QoI7cU0m z3qYgxlht5~M%gW3M2eS~FnXEZ4{^9Q2W}@LVH~*HS|KfazzOti`2+JDusHLjAyMz7 zQN4;ugW;QP!R7W1R%SOy6& zAa==57xaXLb3V==#)`fJD0iG1kYqabV8-gD4lRdf6HoYdM#&&Xu$0kGarOln!$4XG z-voCe#LQ+EdTPAHQV>z2%u`3T0d(+zgtR`6{7p_yB}c0%9A>JgL3D#^0SBfj$ADOh7vKGZg^6jCK8A2eKb&c5U)B5U89y7(5 z`87#f-Dcm9LT=d|gcmf%vIl3lgAyVX&h~rmTnavhP_apm$F8Pb5dEB6C^^|n;1x-| zE#HPk1tIm@;UDvt11&@jSR*r&HP;ua7uQch$*oimET_Oyt;9D7#?xh+jriA%dK&g4e*krgTaTLqu4x;gGP(>Xi~Cc{I0F2{i8 zY@W?uBt98+YzU1C88eQxQqzKuSle1=V);2~VQW)90@h215gZ;h{R;U~zR^G5-)9s& zAzYJN2ft)0Y!88b1j(`)W{ViFOoJ(k>ew#`Jeh4yX2~wAG#oSVcEN@YCbn23MTwo4 z8_xLLP)B??Y-+&FU#6?ydA~?g+>RLAFd3bc7i4$i)+4I3rc9(P%b|U`L?0%rFJ2s& zwqI<|C`1n1AGNeurQ)b@|Kap2&XV7m{r;`p0@fqLYG0`R-kELX{Pp(%JuXT&uRR$) zA>0a@JBNN_SCH%oIjMBc&)i=z1}H=qb*04C%`QE^kG&?Vv}KkRYn{>|d))5m`#}>w zvt4ch757Nnivd&35eHF270G5Qsd zw=Lao?OkpJ?wZi9iqCflJ|5;lFGhfF(xKbg()$~61KhGv1bU=l!|u<^UtrhHzr(9>zs>mnI`HtH7;$gM#Y2-k3Ygnt1!+F9~#it@X+v*a|U zw~$AwAV#|s(B`^(&c2`84(%8Sok*P7@L}%Fd-kt>EZFl3%o_HGdYt365HiVEx6Flj zy3Ziz*(4r7AuX~3BP3?$sx zwR*m30X2;%mZ_@kJUI;y3d}2slKmV($;08FmoDt%H(E|o5OG9<&$(ME5&yb^Q?$hR zK%xsieFoRB;(L~arhsX|SXC)s)5PKec+VHtIS{8{Mx-K636T`3*|O~`qpZS*op z$FxZ-pMXBiFc^WMc;=VXv8LMP%I0?MR5Y`_`NukW?Ll#g6kIWx6W&GJYGGZ?SSlsD zlv>oy-1<>7ya5OC+8$Qdv;y`-4%ETd-+y!>SGu3{_72=TFV}e&IgL-0{x-p0k=N;r zVQaU$a#WFn1b{yKE_-m=Bq=@@=TRTc|Gh=me?Vxq8<)FwFR^Guj1@HZWMoZ=vN6$1 z84&pVCIbicZ{uu0UVyS~Noo!kIx3k;tz-DD82(=SL2harbxGq~KVY*Dt#t3p>bw5i zD3^7uwp(FYbvzrV;?&|6x0++%Wcf|hQbGsB0g>=D@FK)cbR=b=1mL@p;`?TZ{d<10 z0d9mvR_a*a#r;O~rS0!`$*~b>wFVRN;9Zjq^UXfJ*gUWLgZPon&6u$v14SzVR8Y0l zBM!dBP*>4fBZn6obMTqHhQbIAMnXMuqXOKRjh5BOD@x24fmyfLGhvNy)G_Viy&Ot0 z$!Cu8D$KL0qjENc)_1nJA`8F7*_3gfaICC_VsfgDFlY36;Nv5j5$I9CP(BP1@m}t{ zCVm;cEjqRgb~*eG6la4ShF4Y);>?JD+lvb$#<#4MKulh_s*)Vs+XIRv5pATH1+Z(CTa-iW^-j_UG(%estFm2b z#TavtgS?#;s3XeyI_#>j@rmE9?JSDTSR!#-%iX`xd8AM!>Onuu&*5d>mBn6)E$w7i)TBYeIE~m6tie3NHFE>5!*W3 z{vE?_XeD5IMC#rQ==_wDH>sv$ZSILXo4%}XorbO!b^5393mn(0^=6x&z272e=u*3$ zRbOtk4b!_ev#REU;ugru#N07+H6fW+9cXQ6abFZ;9gT?mMjR=B)HiQw=_nQ>CtLn5 zWKCHm*`#JVY3ND2!L$VPxC-tPw$9rU#l-eZ#`P5({HYC; z)QA?LFR<|@;femitw59O>f_MZeCWZh!f@z9hwt4wx^&|^J}i(Aq1xZ}+ep6vm>&p( z)Muc?q{N8Ktt;wX9=S!ld?SnVe}{O!*b^;UTw z7z!3j9u4pSE}z6=TdU2O)QkhoI@we>*%^*Q>FoTPr z6Z%Ci2+nnt^6b&>)Bj`&dUWt+-^D^TaGjzkl2_Z67@@L|=scNHonIzLULrfe4y^19 zo$ndY7bmCIw(LigcCUV%Y}#xc)M>5&%d>w>FX+c_!jgdyYMNEjp82ZA2$Ib_4w&4= zIzZp9+iPh$ZOe;a z8!wZmYgrfkk^ktKUSL19fg!8o2%fGF#Z)Q9xKw<}%l{Dy8`mRw=ytFyOJRH8n%8B> zy?3zn2SSy#K)d<|YZUf$DHnX*hpO45E6LJ!aa7n^J@f6KBBRGgW_z=X=%K#YN27zR zarrs`3Ff>tHT3+!*S3e4clK9hob;HPQ8G?7Iqz3?LvK~==m6M3u7gk=91Ky=AJfUhOSvz)eQRMienQq zMjSMH$;M#BXQ-Czk;udq|2jKAZ7eF*J8uA0vAp72b0Aebds*~{NUXs#ubsomrji|X z1gt3#r(%C0Uy6YNYG*mfJ*d~9X+WzO=GMq#;RmV$br5QPS<<4`9K$%V=AY7Auk_o7 zYmB$%f~dhq=}}^%C;Tm`r0eLlty_1-n^Lk~E&msCeH4-MQ-7^&raD7&F{*X{Jx3hu z2sEhFC+YZZl#6j-Rp`Z6g!w`&wU+B%vV~%>;<2*_?fEiMR|{=-U^xgco^k$!1A8$- zsyxy1(-Wh3_q0vk2>f|+#bmrS%s{&Pne@BwVXF@T1;bnNU*&cd9jBs0Sc!fy zXGFtZP1y41h;07$FiV?>L%U>W^!sJ5a4#?Ynhjm|@~s#mi$)X_@M||`>s7p-ouY)t3p?0e zC=Vxdxh0h`O?O1cb=9I1n+y4GcAfVkWvJ2F6f3^ZPv~c#`p%JT^gL?ACCG(8aX+^TlB4PR(8(L(e7;bAb2Q^ZnUhV&xB}!qQfk zv!|xz;O^wn!(7vpR21>KJwrj3c(6Paa)zq%-y0vqsVYonQVMlng5n z@iJ;epE?0&r)EI66oWl%Iq3qY%4EBSSP}xes_aoRUFA0Kp;8!n+R%FBl&ZeBRnl4hY14UnxRVTQG-SW&LmWX*nB2LfZjn>7 z{spr&QLKt;oc9i+K!_szX^i|hcQzLz;V*`@Y$XR~GmsLwD2P$l)m0PS``y9EXJZIO z0*I*_(zx(NOH*oXKrZIm9!!D{%Yy+QoaD5 z@(agcCTQf3w(sv`o;Fz52X+-yrx_Q@DE{Pte2)5KYf{(?nA|>{rf+%;dV3jO-tK<_ z07b+vGP!RRf`S%`L^KeDp_(?iyF4+2qUBqH$BvMo*+iX1;^i97t+a-% z&So+@0;$5CDv=g_Pg{F47-Y%)7qFbj1h zHmEI9R6ZW0G5B?#<7carMy}GjO=Ljdj==3*T<5iMb2O-@qBg76_j#24<%9rHOmI|m zU7UNJ1gUY^_f3$`+;@E`07GlTp<1KPPGbvDe->o+mQ=qQV*N-^-V;# zxz@8c7|=_HJXd&3uygHRml?pcIqya1s)SOHrt(q2!_v>GfyA^}C8-nSOk2Whjo7Fh z^;Gn3(|29B{>?jc2GpJAmOgW)q`w|uL<@7~-0)o9$&SJsG&DeT&YS_L*AjWHN<|D+ zd%ZXd)ppBNdKCr0u$^s>s1D=@O%Ok3g-$#pVWU*kx8!|N@@Kveq4fHPwA+98EauVv zbWE>UdM40vlvFQMOzHmg(J;pIx|`8m>$x?IX1V@;-Ko=&GBn7oFS)IJ7M#i;OQx{gEYI&G#0^;a;DTLowI^IQ zGqV!Q9}PklixKp)n4tzguA8Y=7X}2%#TUmc&&`eN&Xt0BWVcaX&z4Nj8JJe<8jkTT z>eXxa%^0dyOdAXgU7(kAwbjs4*S!vzXc+o?lR$Q>O9;zu&#h!HslFc5L(iFvc(O)c z61J!9798fCkIh2)hkb6sOJVHSBvQ*W>o~U*amB4JzBgVO=Pn}AVGRBL&Vpa-!XSl@ zSK&y)w2Wo^mmunjRP7t=v%TyP)e5iIyc}IDg$`ZvB7?9~*GanHmjriNj7IB9FR2!J z;&+mN0e3#BrxTjfLhVGa<0rfn8LC9UBJ%DQ!h@*CH=FLqqcadem>?#K_hMuYjxz-z zIs?*$oymyc%v^^JFnK-i91x)2J6)tIh7Ccv^5wW&%5%AI`Q93c!w5T8BSz!_Z3_Xz z0OveUvl)RBCVxgS+oU_76M*ysgm3q9OgZUBAE``)s6;2rhOt{$jabaYtVuNiIjrEc zruRAuPHa_SaI6V*G-Nr zH$}f7M$)2>9H%o-EoGLxAbB@Os8f`aY?aK!GqJIu5My?4m{u>?wy}h9S2dk4pwZ@<}PDJW-$3 zK{BhLTCzmC^RE-i+xf#i3dI1@iq!4!65^7_y0hfm3kaNkh~OQJGs%KYCaG~KBOA}& zpI&K)WD9=P%B%)6p+(BsSN2xah~m9^7J1V-F$!>}cdhnlj$hpzoMd6$*jr!sDg8+O zFS76~ynig`9aAXoHwvRYuBA7=jn7O;ivT4CTJW;!^uWB~-SX^if8eaEl zFc45{7%8R23Qp!ARc!cxd{i|oliCDRRV2fm^*pFQ7=}2s}eq&ZK$&+D}Ys16;O;dPX9gM+`w&RqX&x!ofi* z{NM-VRBSENtv;TL{!9#rwaVBp4hq&YluKhg*)T|4{otRbXi*F{sDf&y%=~Lc_fZ@B zl;~jLWr$`3Oa5QBBMbwe?@w5Ea(ilaY zMc5%^@{0xs{p6>33#&tG0wU@m7<^Tw5uUx@baiajaSeqinPd=UaJL%97YX{F0cEAT znM;kxQm%Z7^v#|K(y;EM@Zr@;B9h@`m9d1pVf$Ao{tC!yHby~B?Eq^sUS~=xk{rbq zq*4NeeyXk6cM5cZ0_1qKMHrQ)mb7{#X6tl!I6mc7zs$l$MmbN<^J8H@F!3e*;+C!) z=EBvn{kqM&P?@9Y=e$i)P|{fsu|L&1sgqs`#aZ4p^m9`{ zW0k9tZtCPD|KWEa&&0d9u=H`O)7Vn@21)V%~eC^ZY?`$qG(!SB2eXJSvD!P3pCOQ?BJ_kcP7UO>! zt}2j(2(W5pS9>xATpTK_MK0(sqXSQV#~(H)w9VMVp`ZiO@B_Y}Xyw3Y;>(7vs$Yue z-RXSk3<#(|yMel6cyZ0^$_2mKoI0(jXT9ogcOf+?TwN?nY~7d|#*x~5!$p-<iG-D2CW0L>=2 zCX46gN;SHXw?H(;nX6mC&2j7{^S-#=bs#m}pAFox)? zVXyy>-}eg88Yj)gl@Fig(ji>=B(gsuZC}Nb@cMfha(%PgZLQ6?ezG0RJMxjTPCGnj z_n3s(hsjD|jv|^l%6wrAWLL7#$iDqX*x>cL)9Pg3l)Zb^Kv(Q8o70`F*4JJ%fBos9 zn1;3SsRvCs%fvaf=65|`?**n9Q!;rfSOS}i@q|H`m)!SzPemLT+$vT{?yV=P``lzRk1e(jIVFKUK4 zv--tqS^t;83{P=qvnj*UpjTwC_GhOHhG8iNqEN(O9-i;aQSM1?z5r0Fj3$+Kd4P@C zb+DjvF=Bx$qnxGiR?^8raO;dsdu2Y5pK1cUQHlyaHUk{2%oa{l@GxFcV9k2((O+VY z3f34{yBaR^Wn)7_0dB&~!n{twuz-Y@uoq$oF`S;Yy31efvSo8?(0C2sCD$>8kKq+a%TUPgGY)PjmbDpGg6%aMz=(cxD|wDJdR=tkl_265l!2< z`BY zm0;QlV>|Pc@464?#L@PZ~(t{DBP)OHIZf&ubGVocD+I_N`qKQ zUM#acRjoZH^y(BQk60hov1R?Uzj_ZbH9S{j0G;1QPWP>b zol{sICM7u1Lr0fppm4F~Q$fA-%M5eLIYZDfw-TIvFm0th3)}bffmK!P{Ckc*gB5VB z4;CAazjJt=M;o|{M7#6)%woP+RQt2R;xGHB!US$@3n9ar7hu}PP_vL+Wea|tx`|$V zH34H;rm;Yk#$RW-D5UsiqDGLfLXa;uP|#$ZzAsAleJ! z%bv=DHHD}qFs>vkLvr)IG;-?iU+YcYn#hr0v%$m&c{Z(?1rkFF3ub;LP+6V@(h?;0 zbSa4BRaI$X8J5XniXnifDZ?$uFN;JM=;^cP(1&0hAxwK)xQ7D($%IiK2tUpoJJ22`cjPZGa4Ry?97kA9h=QotmDUkaN$kI ztH$gu^QR(D7khk92c=_dd$Yc#!$y+$toA79)PIEj{FP()TXmLhk88tjND%W8zLZ!} zO}q5i>91547vQ+Am&FQxpN1hAtTkBo)KPbV59vdlw(D8&_in1+vI-n=V(#HijfSf$ zre_0~3Xqzkf-2OB>defLzegubD?aoF6i;=t5~=(RHp{H9YWBIpVz1c!7_D_(5-To2 z|Mytt>%eHalA%V7Sj1X*aR)M1>2i4gO&sU=(BVd@7Ip)&0!*^@GcwlCx1;FXg++quXZ8P|oZ+EVH-bP~o7SW_HL;dn3eSsR zejuwyR6m@HJ75i^i_}nI17ynJo@I`R`#LV0Ip^9*TCAq7+n)TMSK6jjNskts<7Tk@ z&fCH~3t(@^BbBuC_nRA~W*$DriL=TPm>T8R#+!V$Ig`Ik(u()3YGzw&{@8EnyuAo0 zc09wjux-k5*>(dWFzIT4J_|DHrb@P_dQ8Q?^>fZONw|-F5XA%PgvtYNR+8TC)a#kB z&UWH|@!l=_y>SkED)VW`_-zV$n6CQVxvHP%PKjPWk?oA=o`)b+%z03dz2G!_R4_;5 z;`#^Mn};{-Fo%=p>VLB-6~ri_@cnJ^{!6f>Ntt1l#F#tLo{U)HL4~5FK=RYq1e6U!x^S3#BKgvhIY8%j~W&C}&8)1!;jM;jim=db2_EUj8 zngZ4VP}(@H%B;}ZyB9;ZVe-OgjGcVw$J#W9%$5h;UlG^h=xRAq=O6=IjF%f;*FG+y z-u62wnS}1jJ(_E1h%(5yQYjloTyHdMvcHV=botUK3Br% z8k~6_v_4f+lN|-LvE=jjaV=N+imS^f&~tPXGj2;;(^9P>mWCM;&S{p^B1c8}WrKiY z97jLdio;IH0#I238{_B2{iW$_nzdqX;=vS1;?egBqs8_gFYkr=w7pu2tTcjNJTo(5 z?b8~Q3M?8D^r?v!+fxGk4C4jooDB!QJ#T14?vzZ=!u?+4uygAc2w(vy$ zDZYT{Dk89MVU5}ugD4}*9b{8O&eau7e?KYeythwyF&AV$K6Pyfqb~kRQ#cOFRIKAb z7@buT+%F(9q*<;ROi%K@-i+$>4BP>5> zxxkMk;RHv}9FoKb=NC*oUpCy=j;)##hsm43!55&gam8Cr=cNpHaTCBsY z955sQh*o^e7|xFTYtR}UL-C*M$zyQHr{sqzli<`j5A_t(l<2Q=iWac%R;`Ozgw!5 zc&6dkl*Hjk{M`O9z2eWM7@xiC;_M83_?n0C)wX^0K`!&;~d3o|9M9m;6WE)xG-@V+B=Y8OREZ$RsK(-Od-DT`pQ4i$s4PT=o_STfltY@#Cw+J-$czHE7M_(4g14*f`|TSWvS zz^QC*{Gxu>_K9Dz#X;V;I+>V=X16Mbc5^*(*apTh0vBBHQAML4q8kp*Zv#CE+83hw zig$r**PgJ^o=q0~<+;dV!+93(Zdl zknSPJQY(f}tb^h;JA$N{^U3=~GnbdUsQm0T=z8bum1Z^rpPfuz>zye6RM_Eg0fjNJ zA~eBzhkRepnvw2jaupp{E2l$W1P}xI{yk!xtDXDfG5bp2{le5`+1a6MT)^#xYcnEX zSJz*_M)kMeD9uTW#pa;h%>!E=;w1!ZFxuOZwn;sn4Jxv=m22Ke%va)>k!IgOo*BOv zlD+_3(y`?wlkZrqbun7!L&sBVG1DF2pRqK~-^hW^WLMTqEs70@DZ(9FCWk=L7MYE3%2}f+JdIc-< zN}s2r^XKyF;iwwIl7Dl_c_#AELxTU({@G$E3vm%fiwF$eN+4Z)u~;DS($r~u7S>@X zP_kqrnDR;|8VBk!?vZ*;cc+m0441R!uO|x^mRvDDN)raT)FV?0L3|EWQ7}bhC#!jO zQPZglJLLNvJb!cQ?{E_cq(0I9#Z}rX`Y?NLa0j`A zPp%tP{ZES^P0Z8_&+4WK$hR784yq;Q=#j4h?aarNisgv{6FgY}aeuDosPRZ1T4->{ zUU4mhL0*;^A37dpjxxqo^;@z7Z{A(4vd03oJcEv1x31r^QYbIxYG}^R_j!6}y+sKb zfoU#=?Whx{y%Tb;Uju(2F4DU?A~l`4Yab`#r&<8nLQe$8TeGmo;a1K2rKgeBH_Ix=`NiYWi>J{fPhAJe|ENB4Nai`vy(Q2hLr{cgZG&g#GU$lN^#y8QqJbuTn-hF9x#WeJm1Egq* zK?WcrN2F|i)LjSdz}HW6XARxDN%O^Omi1%Jo#+H5K=Y9>M3df8+% zd#&3U`m5&f5Q#3Ei;RBrLAdN0zX0vh*!Rw!_R#QgVDNE0{xp8?i72xM(P+c_a8bBL zqepQz#v+#2%tBu!&7_S}EhR(R3Wv{%pfwg8TJJG=BSu4n8$gPLxa*k{B!_r~1VDud zP4=|QaP&oIw%CvQl(8yB@@SpI2hZYSDpJWER^Uxt9d7p2Iw%6?Eje3z+OS1WUj$B| zKCwn5x|lc4oRM@&yDYVLG$@L^KQm`7bi%0=z3dIXMb;A42!ApEC#He1(HuT6Wnfah za?N<^h6*!_GnsAK&s=3ILV-XQpkZ7q1q&JO^3CF zhWx$mjhufYhmMASD0<2@HycUv%?wq#U-CsdM>XGHxlk_`-T;6(yd08&U{dLuMk953 z#f>Z?gXO}hJ32N{_9j`t@nrRULvv10IIFXS;`Z5fODLt?Xt&>%v6hX|`9#_M?2Q?1 zo>+~2EEEx*&<+BNlkN#(kGfbOdU4On6M3e$iArF52*}BcRbc*mUd2Hc(FSBOlONdD zF`6sjcmK$kQd2y4KKj|-&}b&6&joO5?DH@q!R=cst}AxBF)7L}MWaREsKn71Hf4YE zCXFW?6&uaSwAjSLOt(z3!~D}!icJVRHJhR)%&CUQvd~c3AXmosOg=4qwi|q|xvYic zWAdA!^uP12bPsj?D&Oznf|3H}2zTexF-S4hHBGf8O5HehMKchqugOgA_aZyhEJMV= z;3&0YlM-MWs_EI#M6PO)iHOOJThZ@b+>V(p?zP%oC#xeWG?{IlVuG~EUM9+#kwTSa zi*Z?j{~&_nOXxz*tkeIh18=|_K)-Q|v# z7B!AnB>gtz>Jjg+fEFjf;5g15#?yHF;%g`8v6h7)8C846iM{`vZ4zD)%)($yv4dMz zM_B}`u^My7F*45VJf^}z3+c#x!o5Q7``E5CyRiGExe2`*!WjBq_ytW#5LqEX z(l94@XRKo7DnrxzYlZ0K{p0@Ry|nvTwsJawOnCc<)0~Q8dQz3Xu#W3{A+jLYTlDeA zDym$%4k(P@AogJ6><=1@OtB;EzmEA8x8 zQ_I-#4YO$@x;eflb`vY1Nr4&atw))_M}z0I0bsw~N8O!Hc1p7V(pa6n^a|7Z@H}-+ z6N5iA)USmoS{}!r>))-!yuN%vX#IDj!QfSR!3PdyD2uKx0|>?AK!}~8`_nVnPt_mp zLO!te^&RYR@0X5-b2@up-oSi zy1`_7*2=yQVqS1T5%D0+AHlFME{44hcV`Awgg-ADvPY;Gh!Ck%+H+7BHk5`q@^fXb zH0}$w#g<4fWLGvv$Ii~O=Pa=V^ndJTe=~pUMK`~)i2vA)wb*xt+C(ZOYOKjQwm=O- z2H&do;h^sB#mBBWr! z*Zds+V!iYXP2X$xi<%)-AT2(zdx@5@Gx}D-lql^N+K1ftOsa#SP{`ZrkGYzGLjzg} z>YqmAx7Yzq4=zvp3z7G?jZ3j(m{Tr27#vixaCw)|A44$o%bpw=aoZzVwPhM>BuW5O zH_Q9;76F*u=ZMR(48nk=^!KnF?Bwx25%IMO7dmaJJWV*I^|BQDY+IU3+j28)0XJK~ zuh0?`10IGq?+kD#Y*K2VqtEc|)oB1LKVg#WRf%!I7IXX)6<)Tm1#@Sj`uH2Bb>m+C zH|U>3ioU#bH#2!$R8;l#_E3h=VYM5H@leet`XNSC*`C*;n@ruF2>?ts^B+i+ zmg8oRG@`=X!QlT%1@p$>%kuJsNvL6t4mi7OrI8IKy)I59;P9EuEgNOcXla*h6bq-4 z;@V=UxtC4?2E|3tp3j9{!KVn3b-SXx*|W<|)X1op2wi5T;s__&Yk&7R(n7_H>99l% zRZWqf7a0mu2FY34y|p;HqQ8Af3dF@QnuFu2z8M)~I8+XK4n>#pG_%vd$56n_j*u2n zO_j&C_An!(vwc4ocut3ZS{x^GfU_ZvmNMT8vgRr0h)DveGbxU4b#jw?7utzRSA~Hu z=ge&1jkqiwkB_S7+N2nTdhJ#?O2aZ}|0)%|J)@u7wU&_JhO$moQVBpbulOT=|AXn< z1ViVO>zkqM&_Q3hrg_y;&kM?VwJ->s0IDEZvn~z#R362CRN@lm;Nx{JlNEGf^E5Un zVQ%9f?Vb1&{|-D>x`Ru*`YM`{-0r2r>-C9Y9q?m!Ov_pyM<|sAVg@z;Lp}8UZe9xN zLW_Htsk=NAIiOX_Q89KN`gPZTB}Dh?U{K?A^K$3GpMg$Ol8I6Y8p=g|U8xl(&>zVr zg1QCc_3=J7cif0PZwd0l)Ieu?iIZk-j|F)^)ZV@~BYE>QT5&@$B|!P49opW=cy#}} zB#dUEk6=j(f65#k^TBki?&xk1Ch8v`Lm^~cN0KlH22}olV)j(Ru!FhFV-a_{UhO)| zTWY~9KMNj8Pw0x1MP$Jo+tp25lsrs ze_9(1(8h1J@?oO8>R_jQn0Xfs%T$i^`yJLC1NI0<9NXa0A2>1qD-5fLNN2F%4(`9a z^*oq&r;{765Qf{B#y8z_wYNuxM+{GKwg#j{J%p>76guT$Y#AsJ8Cy4&^0{$^yd4d- zNlcg6kZi;R%JoO0YapC)leAbF>wjVO5d12r5rQ}~3=So5A4wuCkFF*^8y4IYG~qmF z$Bsh27^2&)gcNESulZY-Yk&yvI5RnSLTvv6za74Yhj<~{?I0M-Ydx9@k*t3YWhDLQ zaB&xhKF|PT$Ni-HzSzs@psALx5hCV%Xhnk>{+t=2_zHm+Ac@RYw8F*qqv840-hBef zF7S_xro>Gg?$CkKm$kAFkJv54g}Wy-P>YMd&u!NdQo&X zuIIGzT=g_*9;({SPiV~P+?HQ*wX-WHsydz|bL`poOs#^JgX%lZ)!UJOekcCR=upt6 zkNkdF6NsEHXdt;*vUR~Mn*ZYd<5TixSW7dlIIYXtgvF=kw8jSEs6eq?Vngp!a$MC` zwTiWqj18)d=W1|&4;DqH^HF;nG%i^;w-O&$Z#>z2ox%`FXrug*`9X(VSxy8>_XjIjk$rw4qRs{?#D&aEMf5(Cnu;n- zyp}6Xb+p9kH9p5=vxS{73>0Y?W6vAOJ<)$;yfJ(Y4#hQ)49uFG8l;>?eIOfHoo+jW zvL;+)Gcb<(>1yR@=Fd`4T;Vy+3KfUOdR2)X?cCmdTs^?lg%8zm#f>C96kWuG2d|(w zmdwmFetk#Y? z#rJLByDtrUc^^l)JLX=+jl=qaDQ>x^1+Ow0HBOC=g&*hjVA21tXsIpGCIa7nou@cz`l!z z#%M#N;6Kv=+hNETkUR($^c34uTFXb6J_<$fwDV52_Lt*rjITsVSD}DLAuk7WQ&mD( zb!4QcOzQrFi9G`3HD|jaO{%tsivhfZHdcqs-9mp<_<2*c@T_gkjr|<%Y=U{il#}g& z-}<$<5x1Ho^l4yrd6kD2E!w*u$V1<}y@`umR+ZC{izzF2{~uRx0aR7g?hi|c(%lWx zN_R+iN_Xd>yFt3U1qtaE>F!SH?vQQ?zkTq1@BiL!<_yD`*=Mh{p7=ezcgUef1*2S* zkIa}fYLz#$)d|y+I5+}Lmg>aURx?|@ld5p}P@YKo>!(G_SYX2m$2#F7>c9!+wnWLJ z{4c|i-CS#B?MA>Mv?>%dAksGrG=npbqwp!3pG=gSTrsT z1<4L$Zf;`ZO$d>^;WdC}hG3y121$8d3~h$0Q<1bA^0`tj1Bz)oBUqq6WoJ>M0wtCP z0)4>;lKXygALn^_?yM~6>r)7wd)1t)dRL(-2K`rm_QO0is;+e5hseL@=IHiaMCLX^ zpw-p@4cb9t5r_P(Di(rjen>AGP^Sdro6i>|=6VV0`o4Pj*3V&WLAm0SPN_K5rrsOh zN9EzlVg^y7~%Z|XwF}LB&C%n3eukoKe`Xq4}253(cA?9vq{i)+_jIT=m zebL9$yy}|7a!s{D)CE=tibxA@J-ci|0Y{y$zL%DkpyB|OG$J4&64o14JY3KHu&Ce% z@mJJ^d(j}EUIH1!WUDK&+gH^_{&uxuD`sLSx}6N6EQq)-8fn7;1AwR=R7opwAPaFe z-nCWkF1(&Mb;i-<@v$MY;>R|Vc<&i_@2{U02_eRE zSy_kNwCmp6aF?6A1T2q>NIMWUTlPteK?L9&+6EasgSbjqdu4k?%9nWNPc10X9u1gQ z1Rf0D0Fy~yKMNR;OBjYW(fq<=iNC$TTqErlcX#+C;OVBtr0@CmyRyq=>`Rd!nM5qH zN>C|dQ0CYm7=oxGa3FFj~dJ>I1j zQPsho7HU{6U(CdJN9Z|s*Cxw%2PMt59V{gblV3;I@dr8>hh>sw}hn9*-{*J=xP#osBvy%ofAiDz8xwrVj^?<*{ z)5cj|zcI<4?E=C}i|%PQ4$__w08x~^!Ww1uSN}1~)MortLLN+15+iBmi2)OD7P=bM zMftrypXf}Y2Tqbsdmy-H53v*kU~s=$a$iRl6?L-^$N~YaF50Jd=8bscHV6m9f<$;4 zQ(pn5nJWaoT)fP@Prfnb)Chc-0mE3FE6C|z2~%Fa3FBqy#Qh8bHQ8@Q{ittcPP`f+ z=$53o5|)_ocoO}H+P^1$F3O&nM2@?w5D)5q)jNQP zK2AA(p}Gh-ptov#$#N&K-d$ejr(1vYY+-x2;0w~KI+3ALxjbEdzMR_4jgn(dw+Vf` z<>^MDij;1+`7zk>l)7J7-%qyPK(jk)vVK0U zJw{GD6-JO-fuGr8?Id-D-uDM2h^L+e@p3y z*9r*Xl~n|`hK7HU>1cHqV7uF#J(tTD)J_{SldWjsT1Z%rH|4Yw%gjLGm-UsoL+{ST z;)Ci!rb*pgjTXqa<&ZHSB`b<%$Jg)EpEJg9Pvt*|tWr2%NEvmjHE0(!Cv;qE0{lJ zKV=VCu??D3>d@`n!`b>OHa@OXIn)SXMrNbHg zh36C`13ZVhTCikF>skg)6p#Tzb}uuo|`~>rh`HQu$a< z`^Eu_NO(qR7pb7skKTq4z}_y3+&hnvtp@L9#}7m2_s^G4PA8Y67&lvzG&_;jbz63} zDe5`a33Fzt3+HiR3|*hLK#IOEd&SoZl;Elfa0Szn z%N?3S4T3JWO}dJ+6FGC`hqO;v`Z(aPW%~Ir;9bak z>hI5E`A|AX_8t;aWFj2 zx6j|(LC{Pz*kx?-644X_2mQK28-;Vwt=&TuvLsFRPGPXZF!amz!%s4Tw+w{(?uT*S z_45?^jdckTl)IN>aSQdE7Jhd_W)C!e-pXJ3wP}cMgRlbvFlmz;PAoo0KIg*gpm;jAT7!ZNY$@V=2$D=%R>8KCpI8w)3Dq-pa zG-C0F)tpkn7aYB|cdEOx3}iKd$sEb9GPDaYkLMH!aY8p}ljDiEZP=ezI^jm!DgtuX zk9>|&YR%X?{*&VU0O52dQ65@YSiEwWnW~sAavB>#g)D+_%_75WmQ|T(!cihN9KYSh zfp?gbOya{uKSCRt#kC4;m@g$ycsa~EL6TCmo|(Yep(DPI739#M?MWv8w0TC9oKHlI zFvdJWL!vM;7Es;Gg3Ns2%Q9Wq>pQM);1K=6zrUuE?2(Y`O0UW}T%FXLTYLj_D75!1N#>`aQ5H2Z- zZrE5%nw&gd3UWv3c_TIk$6h>h1bM|ylzrf*0|sIP#sI>=2rUPXYvh2IzKp)(pE7US zyZVU6*m*BrZCIR8FluKu@gC}-iTLRq%&T>6;!8 z#~n@dB&$LsSfRRYEeuYPmO#eau%sk3ZR!|j;#U}Djh$SZ%y{R~wyd3ya!upY?U?Yx zgIArsZaBeRJa3SagV*Zi7!HA9a(s@JOm70N9DSv ze_VXLUE4HSQOh%zdSn(i3sb6L+c#Ha-kJkZ|Mp?VhZ8fBI+B+Fx~-s_?J47W^m866 zwzl{BzAUtYe-_h5Dds{DsflGty`O5d$`{bA6=*(2ZUvWrRqP{*ZwmK#==dult5dxq zxv|LKkv6GO`Fpd0X1E6#v7o5fpSZ##A_9_v8)4Nf1nQ<2SDBwd$XD->rG3 zl!(&AXjC7qnh9PnAPtD1jtkNh1`^h8DeRRn8AhPL1nZkFWzBp=s<=(O%sKknfxANe zjVY8YWOxT!2?mWS`1pkGmaSa9$wD@W^?F>hD&`EVfvji`J9XSmyoBtF&CV|;z93AkH_HYrFC#*0?8MGo(o zu04~tI?4A;#3eG7-nIflH$-y9rY%@ZP(ziRmR8HIq*u{J9|18*e0UQTLf62DB7taA zrY~mFCANntZ*iE1gPn>B=ByD2q2>kSwS72u#}CRsqVzh2ze;2|I(#>w+N-~Ay}H#K zO_BJ$St$l>9-IOc52Bgi=pr0wMg7s89x63I-XSw(8e3bF>Li3NH|mHxQ5I7M5w`Zh?;|OdzrGjR!~G*w z&6YU+csOGy@9y9x6ZgvEdnWBqr#Gt!O%E_b8%#}8jLgBan$dD?ms>LHDSw|Ht7M$A zzgN_b6y|)QjgcRDuHly2ptcJwd;2O-ijY>Hc!M+xenb!~VbCA29ZH;-aLilm73M{I ze)x~bm6GR9ytD57`o$}PLpKNMK)stpE6A&~F;2u)5Gkc(0tJTw9da;UOW!zD=&P7o zN3m%9E!*1>GzuOeMyVbKd%R;2B|`1|-3nbb5*e$eHxN{~U^ET5LaRel!-~iOaXMe+{jG_pXzVq7v~p|gzb zbkzGhY9a<#jr9`-AI$$Gi3nvO>0~KM6*W9mBbGI0F@72@%C z9Xzf$RW5h=i&;HMUm5nERB>-y19%Lc@9dkejUUbJ>l%33()J@rR$V6`ocJ@BnFnHS zhBrlQ^EB}W#9>WLY+yY)+0Wys{pcL-82C^|7uj&*1?*=V6lT2B-{jNPJAnh?0CGrA zb9nM<&8GJ!o&%Pjn)z?*BRc<8Q&brtM(B*`$0o>o^zkv=q#Df+u6nnFyx2Vtt=?JO zq_{AysBE~N2$86D*#58>b_{imd)xmvj&B3s7=v@zo@}eQxrCAo--ds!U);P;MVa&X zEr}DA4i`Igu>?tiSf53wqHewaD3jnz8Smwkoy4xaTQp(cF;`#|kkgVvG5SJyWEpSQ z<5JXroKRBo$bu%`pO#n4VbpC4m_E3i&b0`FMvo@@B3Fw0q8^BK=lwm-{hQP`4es!0 zYt7gz=bnXS`4A5^+m3g)NO+m)2}uq#w2g^X%$?;99zNAe=R!UGx>GB-tmq;pNA84E zNwoG!-}--1Dy~8+D{^2PZY}HX5lOICjZ53LosR@R)3$@rbuT8$#ppp&BBzU8*( zlgG!D?z*8P0^Z5H3ZzCfJLFU5FyW#aL3GRd=<4!}dCYL9JZ)PB$|F8dV+|=9&+xoT z+Ds+}Hero272FpL&f3fcHbgpVN;G(dSfNhEhFJ>w2w&~LxMD;f5b)p9m{tqY=8HsL zrZ1D}!Q)jB)nA^R5htJdRD8}j_Ce)6GF;qDh$nDxsM2W0oDC!a&QzM#`8AxN$wupm zo$);9IG$paNODt)zR0ts_t-T9ehOdGQpGwl`EBwD&+ce2&Th$-y;Q)&=A~*!>(v?@ z3!V-O=f+qi&2RC7=L0m$<5RXzk!!ga-y8}Cn`s(Ci#FbtNT2UP4w>z5x&ks6BqsD| zD+5oLG4^NFW=dLcoK2ZMn!01zQDzFMFS|V`II!Y1NrS#X2KlFP($~?1mPrLeNow)9 z5D)4PjXbOg#}9_7T{dD`?h@gU&blJ}!V=gmapY5`V3cpk%?ETo4^}?#*hi@|s1PCh z?ABrO$ULZ3$fv|7dL=g$Vn^OuW`Rn*Rf>ipRH5Bst47M`A!2Mvn3h5u)%`p;<%Nb_ z7Jr7g-p*+U@H;TLj!;e~1TIGr)re#ev~C3lhglKey{D89W5|}6WP;}?{2F_NRcQ?A zvfX7lMvpL`g<4wxjkC37TN1yY0|W)K*rIeWb! z(4Av83}CJN%6$qdAzLUx&RQcUAxe2;{k@g;ZvA`*HGilUB^M8=zMiK{d#j6nULzh) zb|$+N)`fo-k83aCSs+iP+m3cl2Xl_E2i9RNw$fWT*%8k2A*~2-%|VeJ8e5h}!+C9X zrJ>mT7yP;@eXhl?4Fi#XqU`Gbh7HPuOKNCTwb$hN3a-`hR5t(J7-{i=C?Vt*k!CNe z-IWV`4f>6TAOX7vm^`RIJvVZwobqgg$pHml;$6gGVlilr3W)HOb2fJi~8!FBoP| zA&(@En{iS@Z~RFR+nAp7&*EMsh0usvu(jTfBmG+JZh+-@DO*v{gP^G+Iq1lRghOOP zGO|P(#YMRhTAlZ6*0XDxGG3Hm@iX&Zj16q`Vr9roS_s|{G1gd0BSSnqyri=EN=YK` zJ=IBjE**44`C*H{wkxA(V-^&lF7HS1&+EQ_lm9)4;rm?YX0kDd^{~NtHn`&rABtTz z-uG)@702{mySbpKL1IZO)803;7e`_z+iflJv4_>(IsgVCINHemNgndEg3zNPkIc{9 zLxT$l*aqt~aKFOl(d5NT-@;g+CZszV| zRf9}4fl7My-Urxe*r?`qQbt=JA>VyLUb5))FHscq!_VG0V@E~mN+FFeS@7&%EILr_uTGMyKJJ5TE0!MPF0a>k=gI^}IAQdR`f$oAZ)%lUkk z`X~m*>hAY?*W4+e2%GQLiIQpL=ZBq&jtaKz^PhSILDfbxhJlwe`nMxF&-S7z-S)P^ z8q?`RVczFmKXiT{#_x^8K6H`H4Iam1QX_w?fxi&MQXnh)CW7ztUT3-8QQ1emDm`Qm z|6%xo1EDpdUErwkU22l*5?Q?*$nu<17?tUPS_G;#9LI;5@RQF+{ad3r5sp}-$JQ&{vO=@#*8 z=w2C2v1gyl5T`|F%9r{6OQul*Inf`SWuOIPib%)ZQWc%>Mp-6OTY4So%L%A#lef%N zJL44|7Yv&oA0u!H6uH@Ea!xU0sduNfp-?|=UG{VG6t?^dLJ)@j`=zgYqcd*>NMn&a z<aCNR-~LZpa7Ddd+mM<@I)r!gc1 z$i9rT;Li*2OT92azSKirhTB(KOqKxg^1~|5P5aq=g>8+jr^34DoglCfu##iSs&E zb5v80&$0;TAZWtlza?y$w(*vvV!3>fFrIcZm41@^ScxY* zExCx_*B&}S*v%6vg+d@8rk(d&ROo_K1S3W6! zsxBVvB=o##eSmtqR)JZ;osNq*tRKrvR!Ov_kLjGOQq4A{T8m#it4b2gI2OE9hkH7Q`lU|jXH?_2J7n((=08fj;f(BavRJ1d9_0R0 zJS|3t50Y(} z69z_7o);dliRsq_=^9n<;o|jkyC9=g$Zosa+EGJo!XBFEA4hiKV|l8cce+B8Ws5D3 z5>ir=ea4l}U4tJp9PfC^+OvNV0cW(&CyV=GghgByB`Ye$8EjaA^z$C{cQkGR-8o07 zY~_NI85@~Go{G%Waeia*^ngM!NnJsM zy{Sn5+^UWR28aS-qShA5pzV*ELTYFyze)`U|A^6?L2F8&-WgG~L^$|$HJ{uDG}LvR zmZhsG(?iD2$xO6p;ys+Czx#q3&C@)#eZvP%T$N@DxkM2B@)=w71kgQ{GB;4{Lj)dY zHa}gB1x+pY2Af$U6^9enYbEOqLK2lhFH&Q!vkT{NgZ^1cBn^S7CkNe#lFPdN;I~sx zZij_Xh9I@0452#-NMITbk{TYq1QwmYnuq*vW^79~!hv zxn-BH;|A>RyjOC73i<|WZli;JC6zXyWYuucYyuNTd=#roZ-GAPKf02KMf@zQ<6kuq zM8QoJ;Id+f5Ntb62AZiWEhCaGgyl(j02jpkJ3eI=`;#<6y|@X1uSq$r?2N{ep&4Sm zi1jOzMbYhBlIuKzIdQmwDIw=gqxq$yi%l7?)XxOwLWbLY%lQFD5o0J086gOZ)oJFW zW6>vZsm@ru(|{0@3r$$+C)OwW8Uv*KB;XBxWbHm%ODKYhP~~vbq$zi|QN&HGY=oPe zpQfh|&~k8AU3w=p-igOEnkxMXB}W(cMywxoH^>fP4l{SS**1J|t7A=oA29W#X8PFM zlsn#@UDoLX*blmNU@Ow?p5;IZvD28W+JEp2Uv)NS@S=dL4!YtnbOg~We zK!v0m%`iyifadE=^^OWbEb;AASAp_jV(i~JGSUI9&F&-oKxlOqevg>Asu1LQ^ru_w zZt6{5evoiMUf=$^By=4azLH{F%)l*kuz{BY2Dt*-TWtt|rGeIt_5#-@oH$?RAe72H zrhHJ9dIRP)?Q55?P$Bvtsj-;2h-xV#sh4+G}9$a_tY7VL# zf4n5GRR(^eO>Pj1h-P7tTsiLQu&7qfRn3$wJGj+N$l@_7e6V0anvLZ~N;($JsJxFt zirkwZD=Q}*;XwMcH;4CMX~QNK;N8i^)@%FBn-L@|&bbKuV2!2YU+jG$3m{ca#HMoq z3WRo12X|SF++YeG7q}nb)?YI2bwTiWl2Q?W^ESnroK9+=fS2e}g~}3_nGsoM6s6#6 zUd*u#rx)qP(a4h|n%FC^J?&1ISU4h>R-^@%bBE~v!w|`hP4KjdyDMJ;v}7(KLh;#y z4g5z=pvA(bLY>wi`f!^QE=zo+I;ugP^9l;A*Q&@6E0ct-v4p&yO1@{09nAIhdJf$2 zuCsedgWUx>mxVj(9i9qC@GPniKgqa56=HIHq}T4d2!J;qNqZV9zRXOlr+yJ1 zSeZP^cT}wT4yHX&@C_0g+MjwU#srC3sLwrr^} zQojY1U2bp4gJXvK4=~zfzhUJ3IUeB!?u+s#i<34t`S?}1sO`Gd zIRAT4UpdvP@_i5>=f%#Dht66?;bEfr4nyS9j!X?=rXmEDcf!3Sh|FVy&%8d4yK`aS zAoZ7e2X%IZOQQnlhRL$;Y!a8)bDh!ctipM&T=3sP^U-n}FD#zifpZICp({!mb0;J) z==4XnJS?T!y=#;kjb>Srz(O}p1OrOun=Px(?oW&#f3_^3s#By!jI8XWt+cwx)XFzp zfA!FkClR1z2hn_q7^N07;=LU*o2IX7t!`a($iy5O`^QeWm^Z7+$SYK)L4c&)1_}un zN31sa9@Pw;Y1FJNL}r{5g0HLYK-fY3MuDxHG8B#wPRWX1Y?mT(Qn7X6Ntr5Xr{R&g zilm-~e0XHh>s!4vXhzw}#eCJ3%Rs~C^)Jf`AQ~7LHkbpznEX>%hy zMDtv8o8GFj1x^D}=}I!#UZ5*_p=64Z15Lfeap6s2Syr}jtmd&Y+q>2*4XCu4Y+@BW zD{ItE_uT}S66=0?wnCJN??$3WNCv&^{%Tb!FtC^nbUklYo4#({78tzw865=&E|3W! z3yaNA6P2U!M)(E3 zqmkVA-)8}=U^{Ky!ku46tin)iu}r0Ye(&0OByyHt;*3q(=#(H%xsPQDwZvjq{rL*R z=rt{3@D}0z{CD*TliOzSL)4^1At=^bSJ8GkOD`CejT*i;()s;Eu(F*5U0W6mY6O78 zQUgH<0_+QEfHL$CW8x?i19DxfGWNuUay*sU(jMpSvCwH=C&t$!K|r*7YrouW!d*5f9S+SNW%JzL#HC1z}wHmyi%1u4EV*zNZ* zaVQOE=pPGhWQAI`E=1L^jinC9n`BN`4hBxJgwL+mGDap8Q&tk`NoWGHGMcvW9l#b= z4l)II^P;J+u!1=6i|*D4nUrW=f?VN+ZbPa*14J)6hcojPk*STh>ceH@zd?`<1N0AD z;xaO2&Tb^)y+_m8*FlTUKO?}TC64irAV~91G8v~>EJvyfv)(7J&sozVi94+n*+Mcm z;2C4O?}=Y~&R1PIfB29n`*^z=I>xHX3DF|*B(Q>`O%UZZwDlW^-x;MKw?gDNK2Y|b z%pBf^4eaV-vDTI(QL-YyC;o~~s`%Hrs3FtIC-<_W@WRe>V46)lPuyh(W~vZmKV6T< z&4gIUT5y0ky67At?!J6Z9Le80!Q?Esx+I-jP6!>IhRCg#mv+f=T~my~u%5$IU}*Eu z$~HtnT3MW?-xRh>45LIx^9)@M?b!LxPH%eA4ndXo=autM00y#r^p*gvHIg@knvpWi zuou?)5rDnJHweE8At*px*#z(Rc-@#%M8R8ui}df%tWAp+{oA6k?7u#o(0IKj9oZNJ zG!Fnu9bk(tgsM22^Weiiz3ipataU`03_pC5$!Y4~06ZI#nU-E*gDaJS)O@^y^6!}7 zW6_o?IhG9_le(b0ilfZ7W1SzO10P|gN4-s5`${&L1(LbS2#UXoXXyt#fr=fU9%Kf} zob^LY8Qi^=3lxY>Qr%*tfY^@614s@-pu3wuHg;_K7BT6d)EyXdYpApRa2_nVQjn}z zW}4}_+g1A;#vry04CvV(JCU1~3!?h(!fxRfW^Y}&$6FETF4V(1p{Vc^zlJrG_Wtl6 z!sj5G(`*pIo-+ERe0>9U_HnIK4E!+KQV-&VDwsLdZ=q3fxl>0ZIa1fy>W2Q*m60Z( zrP7evhBoi6l<2{Cd_3&%&<&y4?~R~p?&4?D&(#_%2`lfz`vxO?u0-?Nod(=SQPMw! zAeikw{nZ@EzBvlf0wui@4YyrFh-Ka;a{cw8W$&LGA@Qbxsi=p>H&(^!$p!xC;R6$? zE5dBUaQxS8$Jz$ntxde9%iVpJ23!`%;EHmgch+bt(kEe|+f`+(;`kiN>KiHO;o>HE z>1v}sYQbjzUH=VW6Yg075(3c?)rQhYv|~5SEK&pkicHD?;PCy1f<(?#;c1B-N!(YE zl9Q{uc5WbZ4UzaA4!F-P&sRyoSPFBp@X~g$ZV0=Vilxg&@1@B=ykXu504U=hx+ZL;%mcy$Q1%9yPVFXs+m5joB7{wSl8^M?$6pS`#8eamv`1Pyz3-4ve%f#PVbavFgpLW04Q&Kd^ zra!J(@fw;!q$XtmW2h@v6C+m>S&5S?Us|89a%9#A8Ok|S+ZQaPj1k9a~`UuF%% z2soh0qla*ET4_l9{**+?Gls8rF-Q=g?QpzC9~=OEN>3u{+5n#OO1vSm%j_{_$d|#6 zh@z#I>%`U>mLZ-<2Ft^FBfOBupqUDbfU~ym!ya>Q853>e@*)|FsK}M$)gL*jo~*ob zp>JDab0I>xAcV2l;TwhZ6d8=W9U42|_euqR$(P*9q=+K8!Vy?*x#Gw-#K}D;w#72? zW)h&`mQ;cGOrIX=5$Q!IK_)XkX0Vb1p%aym!*^s z(+K1BpHEPLxj08D>g1%sT39Wn{qeh8)MUXGR0W-cl$wBLmPEPXqO%uKfev5wy%b}6 zDMOrF2u5G9g@8a;g!E=PcYDPy6uCFqZgxRZPVWaPtNRDFy!I}{nhW{Zw239b)Iu;Z z^oW-gr{uz+)l9zZ61r;PrOcL>p(Dgb&|H+`5QH5l{Sf$$_QFE4Qvapm$!-Sf1I5tb z?0uZ<1fv!cBir(z$lkvW)J{Z*2!bctSBm7L`ZpQ|MwF)2m??o{le9{EBy3-@0BQ1E zsDn}xQ9;>|qyk#w^T!SyC&uRKW+&j0HP!<1m*}}uJ9q8>#kA=sLL%38wemSlmS6Js_X^~ zQ|=}F0#&@M@QM(sTv*bgtroGWp4XjqG|8%?NypD9aHI$Ry(VO)g!9tkRSg^XD;F2E z>d5(=5r;#Nwk3M|9ys+um*C$lT!6-xm_mT%@t|P|zK%42j-pyPwslytx(R&1YFkJ} zPCge@PU&6GJ`Q%vdFN_w%qGp#C&*QZBV6#Xz5=S;?Y=k4*yf8Jxo4l{Z#{ual0&W~*OacENLPD!S*5s~p|NN$?`?vd zQCqaI*pS}1=w{OC~-=Y4IWbpX#s-XaYnnV61 zOwr(?&4+SAkELGhTAomkbfExf;|EO&avpmYJ)+17XH5O{Jcpd1aw?Da`+h&c@zWba zgDD@kXPfHQLxClRn=uH!zqh(K8sCV>bJYm4x)#aO7()KTlUrH{4XY(dCl5udLqn&YN=#5!)WW(tI_GhJR zdu~Gcme$)Knu1Ct&MgXr4)WE?w&|ka{1^8BIC%isffQV)Woan% zKo3};NEJv^ZY4R?fyP)Lt1Jge5<=m9kBfii|Gz@vngQe+vZ;qJfghAc_-onpP77$5 zZn$bGuMdW=$LGpOe}1N0(D;9HK^o9T4fy_5;aA?k*5P7=7*3LHEKPgLZ@9@oosL02 z3E)nr>xBPvtXD06`~s}u<4C4~1nh8_3N=_;x-v@}v7Gy<5|_onf^H$?7dEEI_jSt)_S%x&lrN^;HNv-*u2h}O4D}(+HvTpDHtnwASieI>fFt7@M z$wA@$tZTJ%1OlnMDYfg%U4Q;1Sim?2wsnQ*xs6IC>M=Lq;SULDwqlFJMuH5gzBAPZ z%rV~P*wWF`|6Ka-2b~b)fHiodE6V|($i@Y#;u!hYi8SRrL;;C+5Mu0mu>X2^{>eU= z6|1BGnK+AUWr6>X$lth7AUcSws>~|qyI)jlfXdgETWP@adH;J6zIVWpQADB#UU!HV z0E}AFVkpmADumB`4Y8J<5(bBluO$7?-)aiz_n_bK;@E%>qj&<%MpO>CH`-Qg#?lC7 zF^-Q;uiobWTcL9f3;;azr&8eCP$3leiQ5FF5#USBo<&7w9~#J9ZcG0!P6LjgDgdg) z$)w;hGg1iDutK%k!lVytlb;35(E;uKvAR+9&)NSUaS8yKpzA+O4j@u#tbcRi&Tvzq z(CT>;ur=T?-+Wa`n0}`HA3nP#0Y4$?8|ns2V6qUyv3RYzgyvy)(MQgM6y*v*l%JXZ z>w1G!@DWTq8NLD-#shEhVamN`HNmqvZx9Hb0mKK++FVpzD1Nb}U9+OmQRuD!Zi@aNWCi|N zU;-GuOx6GV3f%7k(tvGkrsTEop3t^V+sn~9eE|3G|3Ah79y%LXG@@hRfb#*M*QA5Y zMBbt8J&)fvEkxx0GrLJP9OhsALqF2lbDiWd zx5cDizci-2_-pGuWM8p-WTR>5z)e+UxjNsH3y;j(+Nsm$Wg_m75%4TiFp4igV=%U? zN@f)E`(qu=GpZjnpWgXcim94sQu)uQ{|`~zaluj@vLr%m98~FW+SIdk(Y&))M1hXn zCHnlWXoRE|y~R5XA0n%9lB^>8CbIzvs`HVP#>CM?sWv??euHT~u}>TN2>;pfPYF}_ zfU@J`m68(umy;JtFNFpvpqw9t0&RNzl4U}=VkDkAY z>8EYI@^JBqeGs%&8>%Z||FhV9dK$s*igNYpneqtI`B2FqFbt{Y;UxHfc)1V-=*k%U z$)qdMbpuIh>f4DsX%*|}(I`gg9eJZr>2-y^ot=Yp`Bp7cLniV=6Z~X){^HrLjoAZ8 z*qISr!lT^gYhSNLo;LZ%Tzs#&9iT_*iQ21o+hG{DH@tU8dGYvjsj|F=4d3I<_VnB@ zQu$C_>QEff3WuLY^(v*U(z>aCcg0yj8X&Gw=Jq7#O&5(6g5)Lnx&N)9Kz5HFkX0`i z(+^exp5J@k-LfNh#;e$}@c*Qof`{dWvtg?kLtv)aQTMwkgf2?`=+rK}qGewL9sG1| zU&SODer3RR%hepqE|WWM9pliH zomazD>M^B(e-R~dG^}BFuWkfVWu^G}|Fd&Jov``it6aJ_DTnsUg^%u$HLAEI@1*ah zurKDxZ7Ge22xt4CHANqPM4ReA7C*Q|3vgfUkYPxC)9OWXiI=9HzSm`vMuFzU;{Cbe zdVovAjae1eXz={)BVXzAGUS%Hz^V8Y$av+K4{_yndbWp2hA-k!xaHEZp>(nTB^O=~wIE`f1^# z1Qi+0^i(;3YA(VUx1y&3vgtvz=wHo&On2Qcbt<59T-8(J5LMB8JYE|+m6jSL#1Kaj#|l%cjpS16-v<_ z_8dl$*0SYvWtoSYm&7q`%XTeK2{KMjPbrkyTo2b&+RAyViS6P|0jnGvcS3#yW|T<)1FccFC?tF=q#zEBjhezdx`g}IZLP-}I; zA4_QGS5G1r%^ql%U!=KOq3@w#R+-CXIZ9-jJn5=zIOx%m*duF^Nvsp;Uxa7Ylsmq8 zb0fnu42-*`U4>4<@^_$dGMGH9&b1!r>QwOlIAymVvihWbLwLnX_MzJjr(<38U4ZFL zarAJPi{c$#o1SNOvIHWYR!SgRYo5hOe}Lx=7r!2iIFfJkR9*)XCvNamKSSk=8unOP zpuWP8Zgx!4b(RI{0Glt)NDWd|W`qViL>1fCnNEe(wHvBd)X4DSD6uR!bFNkCHEc)-IrWCnX zc#3hI=I(VC1Kq%G5^8ViNJmqUW8m@dqMVLJ2A(7__Y?z;J|E)6cQY5E!&&Fd`?EQ7 z3dfUeLke8X79nU-)GWW3uq>|FMm&Lf$!bwzEE#7^pQ}2FOi={tFWzw*htYP*{5Wi zjaQnYa>D#$r3s+$9wq$ogw9qTeZQg^FsUE~GG1x;SpWMJRpD^r$9DuJeM zOLFgi&&x}m9lGi;FCVSPyoh7(b>E+?1>vHxs$ur+9$XYTyi|D~C$--6l(9J4&4t1C zJs!F}`|w}YZ#!-V6F+47JqC;_g#N_*z>esU{{x6SK%`&Wu8$0*U_5u0Xha;LPNF4L zjw>@y-7x3$CfYr_;q~dti_gE?sw(ZR=IABTLQ~E?{!e-7zg|RCEWBX@0_oen5?3jM zTYW|pHkAnEpP`1sRZ#lb&%{TWX!U!*L*)0ZQvyDG!;?6|f4b~d_bMc=`<0`A#0kYp zbZ{^V8i=Rn{=0(qFh?R_3p^hDytGev(||K5pqjR*CO2z6FY85<)nM9@$17uJrH=po zKHkH1Hm{?~$fEddly|oJy36m+pr_B2hzF#1LpWaX8P{R5Q4F|f#-7e=n7@{kXa<;x z6C(Bb>0H^4Mo?hqO*1U5Dqupzo7{Xpg>Cjo%hj2ZsrlP^|5jS`Ty7n1T<+ie8 zojm&a&DeV!6&g(a|WG@%s9aYSCTYZbKB~=h{Km! zzmr@~&9^T~Pfjl}r6_+Nd&PV`OyA*z+>tFT6ZW3{eto049HwBt>WQ6UO}#@zK2c*B zt^wn?q0W_rt8~Mz>1^5sA*&+5-VxGkIcm_5LCwDMX&UC+@$uh{K-sOk^R;!oRBjED z2U5{`=Q;d&vicw2s^g3L-2RX22E-x99S~ULOU*}OtOcSX2&^CG6UEOx5J7Y_AGv-B z<;%R~(boOYOk1pp9TP#> zt?rO@I}Po)>`*tNqv)j93+I>R?_@BqT!Du; z@WU9_Ujdmp7c(=ZCfj3<(7SNiX5wY+s1`8?L~Wvj)7iXA zQXb5?-weKu8?DdV^x{u+FGyLwwQr$rDIA|?$eM09^#whXsvGtWvb!|>ozN0b#-RSx$PESrba$zM7Sn(fmEAZ-WK zAcuHH(ia&GH_tWh^%*2CZ%1P&NjSGWZeTP3`(&^avE4H_HSMu}%t|7n%TD-i=e5Ej zeL&9Fd|C`BAB%|VVH^xaj;Pk>hRWt2LCWkA1n}}j!fS*}*~dChXXfskb#aM(pV`lE zk$?Zl`A*da-nT!1;An3p%YWKJtn0m$NxPZVUgUzq;s|J@jLxMuDw>3VEz(pUQADWG}i2nT&9nBG#uuAG~ zxJ)@x{Dj$ONemK(BwqFUTtttEyQ0EOjUhJxlRm~Xj3A2Tof;f^XbJgx>=FNM4DP%c z8PgLy8b3ld_@1DOo8g;>t+1lE&-x_3)MXvbxMt6@$Ra`5aHGk(wUHL>x|P%&LyZo< zWMqhA;&oj9F)b;1gdBF8AStlu6@8v6}ya zQPh2%?!Sv$v!<$&4iGp= z_Z?Ll@JuEY7(s|NcQpt$2{jm~RhJv5kd+qpP>n~sArK9pABE^}b5<^g7RP!v22NAz z_~~669r)$&*gQlXyp*QB=?yCz3iHRX?cX`l{da$W?`i3Tp+%8g_mz9jUpF$1yxoip z0Dmc-@dh-^I`5Q=B10*`e1UU&x=f=U-_ZIcQY4fPEt*@Y$n}1L#1hca>en}NbomS6 zjl+5x>kS+hcXm~z&!3LNLYs!bItWTWRQm9f3pzRswekVbdoyRAQE@*`Mxeu-*1NX6 z#F~0xiqX$_GKHc=zidN%a<)vH1}?sco}Ya1XgJCcwnGZ;hsTT8#}Z6#kfgj{`MqW7 z3T1Zk>Bo#+jHa94Vk=yswaShonWrLOY{!DEs4+IA^XFB|sFf@EDQ!&KsXS!Y6O`7`lJRj-HMNMCN+;yssrpL4qD1#k3d3 zF%eO|=`Pg*uN7XsMR08Drs>Ec&t6M5`g>!lddohck5OKI7Y)67Tkj=>m9&QLj1Ft) z{{JNEf1C`jr)BVmP&wv?^&A2NT3<0w__8*;#CK z@g(<;jZ0+}A?g&BkVL+asX&yfyy+NKOr1*`!;B4KwB(*Y(&Wq!gzZiLK7FMi+a~Q1QSy6%$XS6vB7CT7ViTcY`&t-A7%?F}U-We_a0a zA07Ew{Gm%5%9+6_2lQf;0v5ht_6=^L&~d6v{Q}En#5*}`yn?LOH~S%mw}Jsp_;_SK zte6o)PfgU(w6O|q2?_TjB?cT!d#1~qJ70sNw2!(_D`)sc=bPO1u30cV#_&+7(Yaft z+M@zC*w^3n78c`M3uyU~#X+;d%|ht`V&c=V{v!>lDuCk7TyySQZ5HPph_T^uwUQzS zZ^)e`i_zy;}EV@ShvV^`#soBQtC{i2aa$e*2W=l{1nzk0%TS3deK- za2of-R}?oP4bpXl&_q;c(z4*vfRy<@%QS^-w$!p;X^G z(_d_U-)6tcRixQ#d=_ygdj|FMxovn_nOBfjvxTYNge58;j}7gKnE)lPuBX# zV0MUqXubTB13}t=o&n8KtK%Yy=G~p3grZC2{of!HF>$BKa0Dy?7-@#D@~MZS5I$*i z+>Oc&pm?}XNHjg0g3XgFpFdb|Na-f_QW~`pzn`&w&Y@HgDU6~cQ^RK$1`>F)w7R-w z8P0p@IGLak=|YTij9NC(DF+*sB@Iya)E+V8wIKe2;8b6ej9natV0&3oV3h%xv{d-k zZtdDDzlh_>XiLA8%#WESH^Rm_fDT&BUdb}{*v?eX!tDy|1VK7aP?h$uF3=0db35e7 zmHRLnfkqsi4J5IBjU4ezpVF~iN1xo9bGo$__Fjdu{dmrj7HRp z)WzDfVaop|d7$f=mWsi0QZHFwf7(O?3*$5Khxgql;5Q5;mm?^#(SQQ;-Th}A;a$Mf z78G~iea-!CilZ6l-W&N-*E1?ZD6;eS9D13=)4^{i3-Tvc%u*(?W8Y5CW@gSz3N0)f zTvSq)@*L#mQlz$eO;0}9p*b;`9MP~mH-ZfiL@@iT2=VoZvsVFI*UxZ2LHtQ2W45k` z^mNxfU)L)1dEd{u;(vU!n56}wC_%^=Cdk(0RvoC{dE^!JZ@;%&! zjYbHh;=9<1I&H`8Ch^P|y_Dy6y62mEnV*@6`(Q-76`2e6X76Y+QMZ^Gg5#OWO_$cy zmRDQ%dCXWfxuOGCTc3yR!AP-4_kHt8NTwmx9%HUj5URXP{2yWO+I^7mXsc+5xZH2j zkwoSz*QN*O)y|%!4H<+mVBqyBP=-RL#*A`22T3KTwk%R;IX?-E21ZLZ0!U59N~OSZ z?^L`SpB-r;O#ODOkz8(fXt?+yecb%!zj-Yoo9ZgHC%#52#uhP>E@F6SLfWE8*GZlz zYl{gdK1*!iZLgnvoZGjnY2LftvC{P4U4fmaR76fOvZl^u z3<>{{?@zG!PSZ%i@{TqE$MDug7-SwXYU#m@z3@?8#sz_K(Vs-Y;X4mla5Y}09OpB^ zQ?&)LXNTid0rZ}01_$(e(N%4-!c?1-Tip!VwEaiY9OxpBOnfOmnA8RdKI$%WH#Nyn zT0L9_y`g@m#+0D|T&mCCfD1vs)gcZw4#S@n%Gl7%ecoP0I7h(BkW+~odW`ABR|jqE zA|}K*W8O%oaxpiiZ@mU?0hGZ|*XoccNHC&ajmS}2;8$Lbarb1h5>M>iU}6C$xd91Y zq_u{ROW##M#O7!xa&K4<3m}b!^Q!4dq0(Z!kg&RXZ~E9%7?Fq8^}!-jdW;ZKJjVIM zrTY#Vl5ttL@`*ycd`0zWC|y!7;%A1g-)Rhu-Oo*o0COgc7JNC!s!`1UWZ1Wp1}wU;V$v6So8saI)P z?@w%Qn*Og*e+c~t@foiF%D^r%(GP33i+s`~eOcs%;A#U#&Y^87EDGIqa21vG`NZHB>whj|t@49w6 zB$Jvm7Du*pq>OQqtc?&pn*CCg)KD@k_{b^ty_&gMTzVFmdnEHUsx(E`^v2awARgAE zaKZ0u1dkLVpoB`a}QtV<&zP!*hb|@N-KI2DDr|bu@|S=x9qK8J^+kGKuEr05Y0e##QMj4vwcJ*&2?0apRo2bwny=9O3M>(igqRu^>c&4Yc;k<%^5fo zOLNl`%=0oxh0qK=>H$Ri0E|FYyp(gmZ?}Sx&h+}leHke&eIg>!ZpwKg!%zqjb7Shu z!#V)lbj40PV-DySa1zO?^?Y8^K+zXBQh||lOFu)Y1Oh0CqF+>kC~-Bzb!3~=i=m<{ z*D-g>as!94={Q~-eDg}sRz!)M*4k>84-;4vzx`M@Lv~TwNn0hU+a*JnfD*lwGJ9dJ z3NSJ=%~yC14&O#n6n3BMt-dBK9&4F~+L5f`z?>oAgdrwN>CiZ8juM%M{xYZ2>qFg> zOF6e&&QT$vy3U9@ekCj!lI;GHJ0D{6%3`K+=_bzif5R47MfcNu!;Ab)p^q!JWdxMPSjfWo^IujU|M^ zu+bOP_0(ch`%Ty-fKX z@7;`f#361IMxF#lQ7pnFJe@{SaI*DO2BD)0PaG0YO~AUxH4{lJ)kj(2Brqm(bv_|8 z%VhEQRM)3754}gA_XKQ`d6vFIU2r)hBXYChUY4Q1awID&lKyN8b#1luFe4wZU$$wQ zwlPKOLa7%Lr!V)!l1#WF`ojwkjrIDtbzIkk_tRflU?VtypEU`->$6`SK%BA^FM)kV zs>N%Qh1aQOU>TA#UR$JPt`H+Mw~`BG()gX`!9B_sA6pY48%dqXWb!f^JMC$f`>V!y z-T{@FRi<2Q@pqq9j$3Cs_re$7=U-;a_eb#qDom1h5mNFTb~h$M}Et~lpG?wh9lE*<@C79;z9i_xFezYkMmbhB`z zDc--#z%3{V{J|<*0@k*Prae^L(OG0j^_M>;FFN*)eA4H!5^B49k`hYsz~q5k3Os&B z#}`!rmlfkDs?6U}GEW*~KKMGuSGehXQzN~RFiAw(qfg~d@_=kmiF<9CmK*{yVgBkoyK zkP8 z^Wa;IB9!&n(q`cqk@ffV+Ug|hv3|V^R)q`)VDKt?9`uGqu)W}`roIp>I}hB%d2Xgq z$@7697CBr!KoVOe)j(CSMUensykPDlnj}eG#pj5gDZ7)k8qhVeXfs1fDC6z0WyU()%f8e`^E$`KecrfhE&vWJBAJkO$n4eXsQ-5BJqd+pBCei?gm_`3@$|3adsjou?<(<`wc=1srZzN|v!0#5f1rJq5OV z+;s+@$f}Ang4eWZeIGexbnCOuayMAQ>=Vh*>gm^vlPWWs6CJ!>?liusVX?ce_I0)l zY9S^ayjB}{mlY*Wiz`HbI=Ar*(=sziR_wWgR%t4TbiFA#N`Chf?uCU(zee8nc9KBB z;xOlO~5%S@P0F!S~fKfr7z0kYbAIYjG;gxm^3CtV4Lmswq$lRI>i*PPAOYO~v5h2e$@bbhdtU7gSiV%!m4S^Ite}6%B z;jbT#KP>x2!=5AMJD<#SQi4f7D;q_Ibh{MkwEfU7PZB?Bf&$xn(ZwJy+#3Orx%T6Z zO4-#hE9usyGHm`$ie5 z0`>C5d;@<@apURXl*QzHr&Y^cAy9-KefoVUH+P{i_Sq1_p4jr@=JznON3fNdxVvFl z(FsYn$sXcFen{q`Ubm(PCA8ETF3-H$>Y$~KA)TWw7!Z-Nd(lcfh5ttB52*j2Xd?)< zVh*OdqTY>%K_Y(E7+J|Ztar}2#fmGZ+#?1g5+B~Z7C{WC$Q!kkwiwR17$xez02}c; zbKk{W-PMc$5O0!gu@Q!%@19*P8uIo3;E<9vcN=rfd*ZTLIn$)-mu&0=`j{yaJXVq< zz&iW+d@v&JR691UR0KThQcY_V9V#Ks$%78@`gn8&H)bd$<3@pyOx=B$1PA*3;2yYn zHkyTW68tBxedZ`Ogv3##rf4SJCh6{+cWuu7s;PF2`+xyU1)Q5{m((~&f#-FuaL)s- z>erZrf;P;(4v$^1pg5u5N$K>Npu{hdU!uNw1gg$fu&xfO$6jyeaww!R*T(Lswh9FX zjFK%6CV-_I$jUUWo!pxm##H5@eq6Z+$$oCdv_?_gZbh!#Wb{K=mRANgZB?Oseya}! zP9mZ~x_n~yqea;>{S&_+_sW^f){8eOA5S_x4^7(2Lhl0ulE+-RTG#5FnM{I0O zWN4L0pRcKx2#N!0AcGQ>Ax$IXMn&yhm`Ehe<2gSnK15=V8xesNi4D7n9SREBHg={i ztCI}cT;g#R^-}q1O5|@Fq4Y!b<6SeB4I6p**t1D_{v&d~b-AJvU4hWh&ImT4uR+#U za!GHf79)2r#ny@=6i)YfKL9fa!*6J_(+^tuT4}UpT$y^BgPI7fX|z$bV|t=yeZ>!% zyxZ$G3qkg@4l^d`q;T-2N4btQ75&Im>39?SV}PEGws`C$ZoyA={K7$}FFgyhLTJx7 z+M$^XMbD&eqEJFuDt{o}GJ%&oSEPtE7i&c8&Mdo0s!_bCh|@4X8;4X^N-!m@Y-b;l zNm>|&wAKu`C0_rlW>Ou-JFzbPu&_JOKm|kuD=>6aidPyoZU|ROs8Dmyi+p>e3K8bf zYwFs_cX~JBS!S0Fsedbjl^fHjdNN21KV{%VM3}wTQtGNSFgZ@-PC7Qj1fIp#J;w8x zxiE#7kPMZK4i#E%$ixRibY~Fvy3Ciz2R&HKyWJe&s4Z#8sJTKmt7poib*Tylg~`-L z5A4usLGDn~)h~`%4%SEK87XmPY)ZR8)4TOM=n6KH3&B&jc9PvpQKLElyhqFNjM~!_0;;C}(>!KlaX*oV zNFaAE%W@u^yZf~?kZb?|=@ZUHdPBBBka9m0)c^gUCLIqNsVBQfB@)0rz70L~(uG^=z_Kr>4?~UoZg?Kq)rJ(pyW%?5HI3)f3Rk@MQ z(jRL=A+ch7!mwm(OG5qyQ~$xNFeu4s@%o$Bw<`bjMvWiyFeqJ(sv|Lh1&)0YUNnwd ziZ{v6LkZk-6Mq-WKadB-J{Sot)f>J)U+r{8hMaN?Na1$4Gia!`Dkm#krJ1Id+xu zMR)2v?DC!tMc$1eNcH#X?i|Cnrw^(8G=l~0V!!$b?sHDjn-n3(hL8xHHW3N;Te;^B zGWj`^VN5Dce&+cS>8HpgA6Y7>TWKZKwUemPDlhuCSFad>QroN;ZK(020}!nxnhR5R zt56T>a0{|}=GQ0cfMwPn#6-}KDar$Lq9H-CDoKD_7m3>RyFy5oq5i3G3m{2Yoi?-dZOn@5mAK6p}E2 zWOeZaaq+5mM??2K(Y}rCeToR`7rY~1Ro( zmtR>`{R>62BrIg>bnxNTxUFnez5rOX=55a8vp;m0(&rr_gU`& z{mDO1`1!FoN|33`8YUGit0^!9bbzy7EmP2skCRLmAR;~qz)^-Xu2+NDs1BA>m)?u> zm|z^%4)9Vikl(}4R_CV&&g0mVsQEf-9`O9HVrQJ_=T?74&3v=+$1}p2a-n6ae}fxJzX`?Og48v8O~7*R2WfVyL5nAfd}g(2G2~p^(S1 zjI^7$D(+$T&sD433_}(=+FZy8`l*!IzTET6AsUcw$Ln3HS(ELk^Va za;mmZg>3Qemg9!!qIBRredOx{=?J%Q{}spo!$!dR6)i^%fsu6XhFq?D{1@gFv;j3# z!nnJ&>K1_`$N_9pG_5e_Z4J#88)BYQ|C}L`lH6B~W*dXX1DSF6#Egw?d9n1G4vXud z1N4@j5Ox{mHtpw~>yUt4vA%Z{YE>ZpWdG3WKP>g1Oi(7$`EkrF4K1_VOO(W3o%U$d z{@5yN{3Go7X~z6OkZWYc>c++m#QVLgTeF`!FE3 z;64}L$CG5`<&_pn=2qPRRMetQW5Y@Q@I{9Ahs$4-i(L)m4w<_w5Q1rvCD@F0oCnC| z-(x#x<%IiBX0jIUj0NTt?+;L35bUqMt4hslsbtlnb#h}dW@0$Gj`7oci7wcU^O)5)l2 zvuVE_ENR4fJ9sMduViFH`Ee@M>-CsnL+$)D+p_#R7``CmpD4gD1GXdnj=`R=?d#Ku zgcjryoJBi!gnYB}FyczT-QxL-1dtWRgH8;#EY~?`g=|=k&^=uYudc4vDm?J~zl!?9 zSv7!w$W_{xrS%E`cB8hq1dh0U%0%ljkQ+}SWtB(W4i^}8c$k2StX-{`^BrJ^1`7lO zmID$tPm606NfY$1x>^)KcFmFnJU=3@wzm~DnUj1d!v-A$ zi5S^w{vl2GcH<%+3dP%@?I{?NyyK@j^G4K>ZERXy40q(eBplyqtstnt=uYzqvQY4(da2vbL?-HTHvVZ&Dh53i;ZCeykP%xQObs1*)g0$3ENa(lE1{O&6 zzv@j!y`Hy4X0_Erpmb59)D|0#&Th?KCv=`l3g!U=ZdM-FB-$PVB(N1v>b2wAiBbnM zRY)2+QJFxCjH$M?Y$SeioBr*Rb_stFhP0l82k}JY@R3KUdKI!5K8}?OL@Pc6u@FIU zOJt9{5=qwu>W4T%%Gs4b8>m2#$lHDOAdiorFK+so7gE05SiErbT9P2jY?w-@=-?11 zAN>r$@7*87$eg~@ThO=Z$X z)i(r7q5kJC04jz{)!&P!D%PW)nx6JRWfcliUg98Rm;u{aUV%nGA>>=&BF1Q{wlnc4 zoj9q?7SNUI?eXcEL$w?k<7D1h*s^B0NwbSI8Xny|+KL2IPJ*AbK0F^sju2U4V`BYG zT*jTBea^Gof^%rj@uwkfp+^89l+z=B{fpXcOJ?c6HS-gUe;Ldsy|L>BMd}UHIlwos zP&71tBiabGoj0!3z-6J|Da~lQo1GXm56*?@Xm>#PQcv0}xsEG^No;1rS1LGqaEN8} zotFkDKM>?e?;`!Hwvb3|G@+7682o1RA9`B%;+J{s49PSF}!Uhzhpz?XrcA(`t$4TaRC zQ8rJy|ElZ#aHKb`)RUpa8MYPp*MX@9`#D$VgZFBIFlLt zizIddy>Qs)tmW6MI&Qr9jjNA0c6Q0b#6;vPuweelkY5n9WIGLj7pUq?C#yCH>GBi? zn~1_yQf{q5lvLC?5~9eP8(`uC9~UzAupXOYpO@+iuXaU(Du9HAhi^J8$iV|Ir;ygZ z)O8+iJzP?D$50N)4dJ=^jZE zBEx?ytC||}0ux4(O{JzvNxT*atK7768zdum#OGLfl;!8-8hRTd!_*U2j_hg5YXl6% zaWxt+-!!gPGM$KViXU{J(a{RqOEIN057;4Y8ycR_1U4cmFk9b#5FvhPVuHPs4a;8Y zx-5Y8m9KD7FFsA(_e%Wyt0=v1;V(MZuF(nWrhI=V{Esxu>z9wgaX_bE#ig`f3u-xE zCM2SbH*D_Zxk}%gs|19-&F#2nme1r5x<1W-6gO;@pzbz;8w_< zhbaBthF+qXsDwMzb0EnOiO(JQ+j@Ue;6wztC0S55yC>*9B5k$WidK@ykvnGjn&Qf z!5*W*s2+m$5Q6s+gp2X69!SNFXn_N|ab@1Sp!LoQff^LN!aGA&jUS249*ubLJ2fZ8 ztMFQ!Z%96u!y^wvBORjiosc42rInTI1qsKOn(J?Pymik+sMC{K$&lUB=Wx2`hLEJC zzsiaR78QK_%4L$67?~qE+eoftjxQH}>vml$tVFas66kMCf4PB+QWD?rRt;m_b(VYR z_r15Zzb{sP>3AQ(T;id>EVl^k4hAr(`I@rvWA%6|ctdsEm+4AaT61kxaS~U%1TQtN zxX6lwaz0*$xIIVcZsc>UiDlXH@}cL$c?7Gn)o<~1yoW5*mV()~yFKn@$)8 zv(0eoH5O>~d>5Flkzn+<&f9~J^aasBk?4J&f0GIG;(n`GkNjc#3F7WtNK=9UTdiV1 zJB|IP@hdy;=mp%R-2o1^NZ=aO*_QN)&z+^)(NxH0UH`?9#QD~CPvIu5O=k}Rzo1_p zP@m2vjAkx=AZX`mLlOB&KbwUS*U{gdKbd6bXcw5`$&iVeznfo4 znsYg;dKHA_ZP@1-g?m-4J(adzNZjp&~^JktTBI`-@vHF3L|S9riZ|TNt+A zpMDDrgbXZW;j8#pcwMh=MZIqvo0XR9?K20fV_>EDPmp0|3K;0^RPq2PiLI62hhQsS zE0ra=+&8>m9;aOG_jb%{U2hqqB$D%u{fZ0Z9;TBz!fbs`M`&4=f!3NFo2#!BNU4y< zed~_s%nElssKjpjrUJbJZx;g(4-Z_u`V#KmFmr74gsl8vzVVsN9xx9}95R~Jeq@A% z2L0g@5|x%>P?=h4(nM^xG!(z`|q9X9<3EV?mJ(+_g*v$?WV|kg8y9$OZ;ma?hD+QkSEd&;Kv%SGw zy(!-}FdffVKCKgiAewc*H5k)_GJAA{)tU@4H9pFl#7-jb=J3f{eG&_5X+$i7UK)mX z+=21i&NI6n7;gF4!CO&-funEa{-P>?(80UJAF)UMR2yd zyoRl^q_};=YdC*G=0y<_Vv#U1#rtBKHmsbzzH?oC2wD-V^WgRx3_lQTTii5bTKjj_ zalrrI*dBW_$|3W=-Zok;yYN1A2B^<21%mF4B9iFv@?7!hgdvH+mH?)fyStAFXy-p} zy+{$U(YZr#GBhJ5q{Bodeh=D&iDU#Z#}uQl&la*Zb_-SgR#ZgIF{{-7OrQJefY28} z+J=Ye(5;2n-UXu7%nI1J<7}B&LCe6WVc8pVlVuDo3drUVEyOkxx7K$d*+tx}d9XgB7D_TK@+RW}Y?3Q-u(nlO# z*g!;yt)2!pSFP`4%xuPgaFo&OY89E7B44XeI^G5O#I%FgF4Yz_3&khJHbuI(SV?z} znk-=cM)lD|mAza2q&c)LPlGHS!3+Y=s20$4oDZ9t53`x~_D5b6{Z!1HZL-wO_B1}* z$GRdcG?19vUXdUZhf>60JEI6YUyE-_ra+a;S9v9XU`z1iu!c62;3bul;f5v(yc*bf z+&d^-J8+Lu1Se|J?K%lvMCh=Z#<#JD5guZwa;|)V>o5HkhQ~+bTHLO%9WT2$4+ow0 z4+q)Avqp1PEeIWVFEHx!iv9LfZ^>^3&4V7@xI|jDZk`7pbwlT=kYIbo`m@<5%MxBU z3q!1&DpDVpz(;%H(WqSF-P5X2Zkd0-t)FIM=X4m(jeMmWb*00pg=!*3_Upy*!}IwY z-W`EH8v=PFP)=M?Q75GPXt&=ci$+unk7A14fVPmE9iP?yG&9x#H8DwP_dpKo?dGb| z9a>3Q8M4rNPdUGAg^tZw_t8K;5;~0LF;ZD2*yT+_iUAdKdX3awd3yFtmMdb^LX`QJ zW*e3>+gCt@^Crdmd&R?{r6=PS3?s%6X&Z+XWwGuUa-A$Sr&?IbY+CyVZM@+^inw|I z%&^u5EP%#%8Za2M-<~TM4aI;8O4)Y1HstBVMf@VDn>90AqB*!Bz=kxyT-)p&A)Sj zjRGbs*GuhfeGK6=)uFNN_LcsME)H`T)2q$?h)fN%e&aEeZ%?cE4?Rp^8pmAQ5iTS0 z>J$Zk7h5);L1n0IBr^e|ZHNXjl;kPmIWCVDw7QF7euAj_l=~Cqxa)0>z6=e%_Y+q7=5-q?XX#^t+ZoJ+{%*BJB#R0oVi5`Y zT@L}KNc16ND;e5Cll5nL?Z!afj)Rnn=+#Qm^f8OuXwF zi_idL^LafAnr2arem2>t$X+8D6oZRB9l0;xFLH3k>F2uk{K+!aqROks`yt@H9Tg#?>64m1+#HK&!KArqGBg@2 zyA)ojYNv7$O1h<~8V0wgL}gPxXq5LQ&c$4*D93qeeRa-IL0yR~l%~01tY?9ysen3_ z-i>ZEpq|CF(jAYF;XC`rvhT%!Ebfv_1rRfMDQcUb{hqd{pqQoZDH+ zx}exAw9_3hr@)@8-g-5#`AE9a%7xSbDelL!Vq(=tqk1?-0zVOEX*{4KMr4x|Dk(1p4#)CFjzN>}B~x)pW=22-r{KOlki;`J418YZ$Zl-~w`|P^Ov&Uj zaP>PcVOki7>F%4iUYFb}=?q3EO=GWkp9iS3Ac?onhqo?((K}~`v{ZrymGgc}%1#RD z?Ka}F1I$a<8G!l;@;fHyHObMiA3h%bKl%TU;QV;PJCVb2(Bl|#wtIbE4s;KuCZ7q} z%eCMumZj_ofm7K!>HLn+tDpI2*x9ptw|h71<;k(-_VWSHmmeUUaKaZ#n*zRv05~1s zXM@XIN_)EW+XuYE>r@PNk;4AO2&bF%j_#XJCLnuzM(AZkK%DYhdT&^PM`H#&*vBBL z*9NK!?;Y7pgW^hZo;tCyK?I@Td594%o@wOPtX(ph(Q{RdzpPL@3{r537OyLEhnZOuO7Q^TDG~!3 zh@gHy$3g&yt|yl1FyF(D69g$&wp+8&goOO;|89E!13No2MD;@Z;HtXHB>O=*-48y) zBHV6@TB1PnJ>JwNciQ<1aHr@w@nV`djDd2VqJw$)h!$}r2j(&f+;!Y@%j4T&`*m?3 z<%Wn^gei~Z9lw~jPP?YeO2$HhlOx6t6Ps`IOKoPy`S4>7xx1(k@^E?a8+Z6Z{QHOZ z5&3-)DVaD9Rg%J;(OIwQ_(Ha7JkZc9l@4N8Qnc8;4~>B-gOMBWp~`-HGMwGSPj|p}`~2 znb~wa_)U~G-i<_)I~$|?$JC!tMQ_ zY~t*>TX+}U%1Bg6Vt&@jGGkFoRhXN(Df+)J`k(Qcj}8;79}ck%B-3SBG%w1F?wJVe z8{9R>&K3W;qj5n0G()ccEWrHe65vAXhX z+_|`af*QRxZ^-?cI}AirRlw?F@%;i#Ygoy4xjf20S`9B{TaQv2aR`g6@_kK$1 z{kkXid1-U3_SBUUXO)drq<#b5Os_k8=gbGo}9&SQ=!V=kChG zo(cr+-=Y^pMEdRoqD_CHO>MN4We!qfzt(a%O(4=}{hX8Qh(*y9c(EVoGCn`D2 zO!H(T(hTiLQS6~{N`J*se-%OGFkMjyZ;UJfJCg-{(e5eMQs%cfdbdtCq>{21=|MOj zr4d&9PZMBe5iC)41+fCEWwoz^BMv~?O-du4kDb_XL`Lq)ixL`$lq(u9y?WW2xj3w? zdT2X5Bk4@eU~ZS}(82NGAz7VX`CA+K)cl>3j_Ky^uA2iXlikOGF6ugM6ky+~Rd(|* zWk~O5HKi;~>6@A?%FC9t`1NdFm4{wInlCNw#WCq=(Tn1kTi5J8u*55b#S5-bGgcDW zM&V0p+VO9){yq%`1VfS6gR^r9TKW8K@b~s6vZ{l;f2qO2>iF<^bz^#dk12s7Xf9H9W0WqcPnC+cV6mIyJJ~4T6xcbyr>}?;pFuaxx zqSI1J>dLO|dg3lP{C({1spG~C4BYc?&(S*0E;z$YzVK&RrF@8~qmSMV*Ma_^JX-KD zTT@cNM`D0Cwx%~(pgTOPwHxgF<+?9Q(4z}_zjU{F%-tYF#g@S9n1Ip-=H5Gkwl>u{ z4O~&yUpBwe{D;VFbWSQ}86igu@2_;&UVU)wxTl*z3%qRbz0bNViQ*R`6(@<#=9_ou zJ*cYj`Zyt!yZ*+UI2%vMB()uyO%#Vju(o^+?sKt?F*VB~*I3`yYK@e z^BuX~T0J=shnaO9LL$j)S+|Mzj`O;OuLLw`wf1~g))s7Cw zE?`+z6@6oQT_!p+DOOIT$4VMt+oYul0dz`I$8k|Q7kG71gt%fsuscEq0Y#=yG= zy6rhmP9w>B@8N6QxuuvhQRcfe&Dr-e;qxG(msMY`fJpsfiLFBi7V?$_CQgEo22?pY zI!dShDfgl{&{NW!Dzonz9r-3L3ttc2{|6QRV}kRNA6<7#FxM6i6hnq}mB7SZnn`7J zVf)Y|@nQh>ynT2gM;A(*y(m?BV7LB4hK=giZ(Rv${`FVN!L&o#s+aEjJ+aGpsKzJw zCteRsrPkWux~@2P_}swkODK}cQOm@sK1t*$c2>8lYA%AQGotO3=;r*kow!zS1{@u1 z_(HVXh#>V@GZBr@2opv=<3t}445MD{;8_!quL(Fb-@~F0T)R!OLnev$^s6)!bq@O- z>S1;iawh5MO&s{vK%Pg*!X)~u3xrEcWpip5ELuT!}_$xggSEEtB}>3&c%cUaZnTrY^Lxs3>2|wR&R@b8yuq)&`}yK4bCuYF?;f_KN$f=nd;cxNov|}npyS8Ra8`nB5@RYoqXf>(nD`)8G z2a=p}A*8G)G)nR~=ItlATd%Qu=aq-gNU^-LtzWVt#JR?in9PmmUn3?SzSX2k^TX>K zQjqwN*@TlIMQjK!OP%nz;Vbo1hH^w|Y*{mTX5{^)BjrCjqN1d)Mlc-2^zIQIwm&0z}X%hZiQey*#WR%@-rp$vfO7~8z6?d z=r;hOiDs>kf%#Ea!F5c>z%F)As1&1cs$3wr;@zq>JPCL-ZvUXP=J#*%HU_l_6p`@6 z=%I#krJuI_B*))Oqmquow-q%QgJ~84luTGjd4;1zgoc9ySe6dr5X9&UlMBt*C8@`H zq0e?N3+BM5e8P(@Ag+t`UJ4VwsZ-Xia6ycOO|=ixi>xHL5~;byz@tbArXjt26g8jB zEqIDyD%N_{RA^s6Kju(RJyd<1yzIV4uGBjfk?2!gEpn`IXNn0yE>e<(X>jt?S*orb zys|ZM)b{YwiAiZ5lmL?^Vb(~j1HLjHRbQVL0j~nGn{@SDAZFS$Gk6!>sh&ukj4bRh4jQ$?lSzi5l3tBRUhDM&(!F15BQoIvtELY=dt@+bfsK zZG-%hS<@w0BJBg+fT&=aKCF~$p#E6s)ibGcIV&*u_jT-Jp06^=&O=g`Oj7!mwbKQ6 z`1L)xZ`LqdQc~gjybF$Xru^4`!7CYI!FNzCWT4tnU;*ekr`jC*G~P;tA|YTa%tAFG zNzbbh0}rD`Z;U})&YMq~PhM1nKV85Rye%LX78$8W9QG#|oCL-8rsb5PT2%G4aC_Ph zUn|HY23=u=cSOUneQc?I(%b|Uov-1E{p9g1erCMbf&)z~b}Wa5H)-C3mPswJUuV%? za4#a@_1&CCZ|KnD7yRLEkt~W!Iw`;0I5~F-2q6iT(!G(!+KVQ~s~|h}Cj%8>Bl>Wa zcSV8y-P6>;fSKIp99$c6!Ssvg$z?~ZBu|f&PfWe#y?N=Dg5UAztrqIx>Ws-QwE2S# zDaMI0Bc}(4?B5K?Stqz020DIH671&7$E8gIg=F+;$_w)-&Ho|mt%KU^+U?;MC>q>@yF10DxE5`3cPU;Vc+udlEe?f3 zad!*uTBKNTch@h^`R2^;ectnDW-^n^z4yKMTGzGKy1e=)ZDqICM?Fown$cqF*>N>^ z`GB(4+TZd}up5Tn8`=4y%-GoM)Ei`A#<5^_RCBfr)dr*=kGk`Ts$yd~UrZcgI z3)L=yl49bK%=T*9y0PbZO1!;)O*krKHI>kY+u5a`qy(yN*`79zF*z9t*nW%XWRjQZ z)S}$3cw%Zb^3GIQ#R9%d2S%u);uoKHueRF=uSG;)CGA_1VGzbZEk|5lpE1p5zQ1Q= zVgA62^FjMjAvy#NT9}aRa2gJgSJ-wHd1^NN#ZnL5N_Ymn7Qhq5hS~B?b``!=S6_=} z$N`c5tROOaIgfLGg1gkK!sRNBeli6BP~0=PXKJmI4@*EOoa3FAbPcPuxFquxvhKRX zztbkap*`3*du*(~+`2rB3SI%?eh44Q3UC_!MD%D;s;Y4pyf9Qo^*)Dd0fS>0LT%Wi zL32qpMDlE9Q1~jCeyT~kD;~zWD5s0s(hdJh19L7x_3=io{PkV)ccE zwtCbN{isQ4sGJDbQwL`|81d})GyEP2!34eAL3F^;Xzg@O3*e(Z>g1@$sCs1eIp@+} z*|Y8rv9DK!%sYiZx(sknC_YPMm4l{WpA5euTDjF44O9RT30}a(41t1z+|+DlKlA-Y zg65)Hcx&TlGcWj1ibxHkND@~BCaoB?Svz6aTy^V#sZ}yQd~c0S;;c9nwd1DKZtuEJ z3oc=g%Y?ONkR=>eZla5wI5-t3^p!r`Q z{Og%gI7iKBa9xZsBLhi0PL=cof za1e8`EyC3NNq7E}4yW||x0lG1>F%5;#q%9vFQ~>wW$ou;p%`%e7af_>WT<=~zB=JYvi$?ljh(TfRUV$nvbl)(= zc`=cG6x}Djz?4v&p5jYr0cpRoGLFF%8Hn|v=&kT+*nke?6dig~K=@l+I%?N?FMOgDtFKq(&Sp2`@<+`f=kPvfJK62L ziG~aJT?L@^laIWkq~T|=PB&DQy*3}82cxcvH9cy5=@lNM_tRFcfZ4TnN)qop0-#>L z_lpeBYB?9sE4lC0IhA+0vfAuo(Bnxv;qQ}{9LEQhub#Ii&R3L@>WYN6Qbq<-Blb4Y zgg&-l*{ONIh*c&ov7GN`PnR1&YJjnzQe4!~Ii*wx9Iu|O=Y*ZT*6>NMlDQ4#w;OZZ zrdeF+SG9B$z`mc_abHo)wrt@-duxIbJK&hp)QjZf%Se%fuG?kV<5HNfrsN*lY$uzrChjH5}lpM=@DBOJuc&JBnT^{^ou$iF0%| zHcc^6Kkdv``sWeFvstP4HWh_$0}3%I$Vv0(KlUPjh(YhODR(miWd^+=8D03;kd{d~ z=dP^v_Y~6VbiMvX8+k-46`JtKOuV5H^4O4Ux(4NnB>5_*NMXglFfIcvj5Y$uVO*P@ zN`wUnW`az#-}6MF~wFA-4@7`?K>a;#6Mv4O~5 zr+NErCB9*ndDFuD1VU4r-FDE9>0S*@QNjnFzMle@3kEhMJqbzX$kJ>8bCwV0&2yl- z!P}`+r-7xI&!B?@p-=dFlm6aVFkM{STkoeY%LbuHr%XJOrmJPAKERI$fp!Mz8+(0d z=8Wk8({F2F%1+E42gFz6n5+L(g2OJRyDJo!y9M{QYx%SY>Uwi#s;Xoa zpIbomed3_vPvBSVxYMPu~(kI6;-aOL!K4UQ-P-AE@rL&^rc_B>dU>}vfHgkrUlVN^D4F*k^z7nn+w%z z02Zxeg{vNoiNP(0%vM8sPqs4}7BpjXOZ3v&DJW~6p#U^T3A<4fP#{U4*_~0_))0Tm z#Rad%a?Q&i({%hg@4%A!BZK+v`iI@wKtL0Qy?y&aO&-zdbQL*ISmBm(!v2T$8B0?zwK@Y#Lwk28^WI7O!T<@RzsB$fL~arWEsVoYqf)qvk*0nfo0b+^65 zDi)A8ZVtY?EMv-rdSNk#k&_D5+rGxp^L=x`eBN9JHl2iM;(y86c*^bi>NNpgl&$up50*Jt&p>u^+XT7mrq#hbvn+f|XS5B%sc2lecj z>e+p#1M{E;^5Dg%$4N1}&NkxZ26<6!x7m7`G*4D?SzV z`n19$0OFJ9U_e{idp;9?YDHgI`1B3ww7lyv=WZR1o(T9Uu>3cgn#C@;uQPFF$COMQ zGlk@qhLPx?Jy+zKk0~*bxTelt2}Q;5AWvSRlFBIdcmU~Kn-I~t+8)kNO1-F49wq1m zj*#5D?(N#s0czPV5A)|14ZKrg$sFC?D}(Kp1dd-xM(h-A?$~Pq^|%8QNFR}-el&~d zeHWFVhS-XH*}hJS`-o?9Fmg<^PqulQo&<6~Wt3e0+bB*HvQqmF(~(W`1#Ux|yfYqt zMa=hsFaRnRAdm>Tr={is z8Yw65VbO6hqy z*9I;QxW5AKnqs$_Y zo7SXB=iJ-anVAjvB6d{8EacwbDBSH;cLC5x2cX>B&X*eR1;%{8WX|{1O5JVU3GA^3 z`O*`S9gq#2C(!`xe@V}k`tmkTQgVDX`DR2*43+O8f^{oFhuQ#9VyjgIZxC$;spYBM zGhTbCdTj^RwYw3$se68VPK5YA9k=myOy;9PFrNYk+BrR=b^0jnCdqg<1q3|c<)?w? zJPGOM-zo+};?Ki=T_{6RaC#zaNRe(ZxeFsrsg;{_jh%|;5#=^sXqhx zNw3@h@%KJw62f*l*@ynK@eZml`uc-a-<2ntDRA+!eMj}Fsfk;yUZYS?hF3B=e*YJ- z!TAS};q2|3;vgfzeSL5J0tak0v+1L0Dw}jB@E9*L4RJy#k4h>8Et_O@&0Xk+0 zxHYfQX4XwpCiN4fDu@O&pqZQFUR^PoVSvqmWB1NeUF@tERj47s&tX%2B6z02JhNZn zLy7!DYSEj=Wmec`uG3#M?YxWlA2L|yPCW8U>ffqxEwJ5#Zhmw|F?1@9#M$E(jE_ds zeNj@3T(@vUa>D)%{)C_ITCEX7<1XK$RfB8)T_bi1mz#pk727O4l8XPedv#w_-DtZn z4E^Uqb2(i3*p(2Ym6xFlk*O56OdB4O#fhWV#`@QL-xj&^j=h5)x$$&x;aeO~@MsR_ za2qlWp7w?Us+O{tUatj!TE!xjazYmDyO)p&EPJ8yoZ7F04-jVws_KywII%n-K#F8k zTMz)JyWnP!dvZ-=;&CdXva%N2NyNJmA6R1`Dg5brM^p{=yZMTk{*@<;8C7TzoW6-e zUG3*X=i$gHkt{cThb{nPVe_iB-tFZ_RntsD-YYJ}aecCmp0D?%{pGNXA`Vf@Z{s7ipxTZo z(YGLTCFl&s!(a00vFP&-rRk6_JODOF5$@KC&HT{+Q;CAsh;Vt$ds4-ShA<=ueNEYNY|9 zT^i`w+rrBMWg-5(^u%GN*OWPu--z5jUYdHlil&&w?y1&@(79G=b{}2k-$f7JaVBw2 zb7mfl`hLB9fzI|ik9by7BQ<}#Q1MGVOyB5sMslOg_?vGH2jBL(I=AA~_GVP!BF{Ou z3On-7_DvMm2`37BnzusFGIV@EbE0menL)QZKaw1Sw^WXiU0zbLy1iWMdqzf#GMYAb z-zb%tGArZfkKCuu56c(|>_8mVcQ;hBb_qXJn<|vGFQ^Z!Z4GhtslHlF@lFXAQ?6(u zBncZY4b`XF*{iIJbLSknw)N{|Th=SaEx$41C{c?fdW^2AbLiif7E5IsU_wT)elLHx&MJGQm`q!W?aZwfzSBz@-6-b- zsq7!~JD(3IX1@QRGiAcY&OmT&Q8fz0i^M59h#pM^z7wS$OeN=Y&FW9b;)QK|F&?l1 zAxcApdTw$DsRxpQK_OGd$Qr!xiE-H4>mSrIyTRgL#6?8-P|uuYPz7j9{>P|?Ga9$% z(AOM)i*2GE>x@FI)XQ~Jm_-;TnOkq@iug?ZXapx@>sEvHgA-Rq06|aYv*FASYORP- zZofk50ut_pE6V={0EPdc>6o;j1!)3r)uwhMphPcY0Oax`EK7qs;6uY(VISoC0`WPM z8-`|M)oDh)&2yE?aNLrcncZQ-KeIke1q#YSf@=$Wb(g*2iCA%q<~xg9>=$tTU>)Yg zKas?Vh`g^nMo2T7O&2_|JZtg>Zx*Nv%>`=XS z4d!&t!mSGzyLG*Lbs{*JIxU(i=i_t}Iv0Jf0m)*)8IPChc!G;X_6*Wmvie>2zV!qA z_>KPxT#hBI); zkq!CD16gQj28;mF)U+XIcsp!N))5Q49Sf0&scGo1BVMFd8Y(DA8D7rWZmU1MWGl4$ zS;EO_OAcAw`oFGaSocWXK(I{4lV_i1L<%#$eD_+{yUVn=r+D2COXm39rQO*C7_ld1 zofrAWzuFQ4FP8zfq;_>>Q-Wvn8B0-`Maa<-$F)m>~u6o>VaBiNQTeRH!J<^oJiJ82dQGJFTeE7puKSgZ{`V7#P3dRj`?Ru~IrLlwUYh zKAU`H?YgE*r=1z&45l&=2uN*~=p_@!e~G^k!%9jh!Z{67s!`yI*0gG0epWH5`i&+E zU!e2%Q&gBG8>92Kc?cAs&?$5h>C!Ie!+z87;Y78reNd zGREjC)PU-6wd^O)MiC-cn!B}Eoxg|4lfZKEbWVM(bd|k9HO$5LOIGyEtHPlv08KXy z1*9RRM$Ex zLK8)Qzw_wqrBrP>f(lHmNdB3rD^$J%;*}=6V=QCmkBn4OXAeS+DG-2ATeKDZNs-0l zVt>x?s3o^mq#-)5jxMDAP>5}$6CI`jY&J-a<}iy{o=agS-c`x$?v=)R_qlwb!-Wl9 zc|1wBbNM+nhc-r4&YNB4UFawerIu$u(U$yj)ENjri)0 zvhWSHqK?g-EO9oL$%0J0WXO3cdb z{2n8f$MOV&9zI18e-qDJmg&>vOO{?f+GPiPx1_D1 zd@C{)?^Ha_sb8R?aQ@hw!@ZU*ej|*>I376!h&d?$&4n8b1|1C6?cQ+zo_S}ic=(BF z0evJ&ZPYni0rd-Yjn|;-`;3<6fdZ+w0Cm>C-SXgc5}{;-8}ho zsKOk5b&u+8Y!LMOt20>9~k#s&w4J- zpy&!az7>y^^K_I!QA)@b_=<5%$iZVux3bFcOmtv7Fw}sy&{X>N&~rJC!Gofo#mU!1 zQOYr3?{cBtji*gnGQ*D_=1@R+eRH}|I~^30qL5l?GT2QJ&Anw#Yu4TGk)|y=3ll$$U0AwBZD6VVCSh%B|N$E92{jO*-_duwh z@xrqGp!P)TE2pB5D$W>GK)WkDM%KRXAN8y4bm<<1RW`Cb*Q8+BUt>tUuL<5w`p#u< zGx%=$#PFAsVVwI2iOYsBe-g9GE^%|L1D{#SmQ6Shg^X7yq5XVXt`Be6cn7kZuRzs# zg{#Qz5%($}-)W&jY%@|y4R8a=i7?bol zNt;-eYWxgkoJ#+f62zoWS9g7sM_2t`uAXGy4-7^JuzQ=MHk+hfs1uY)Ds6o<*kww1 zsqa7F_?jY6>{!c_?YGYo7QX%8EP%~N0^rVZS^V@0VbS^cb@}fu+#K_o>BfgWZ0`Hl z+S?ro*VWyuwZ@74ll|sXALq3<5^Y63b~tZR>f(W@-5s=FcUcUKD;Z zyL>LKSzK{lR2klOr&fw}*PxbBZ*#)wRag%025{caiTbT+cTAifBYGq9yg@{e#{&6J zD|>dHKDLTEVMb2e`eA%ooQf?bKlmWp_bqYg*))c-nP!?bi-k@~c%GkY(GH&4f?VlM zOu3QB^(V~i4gbDh>=&TEQG99YfG9j%PF*auQJ{bd6r||7}OZ!BtCPS+tr%)-@G4InGAyk}qq>sGc?m1a9;^!vS zklg~w>m1IY-C*x@=VY_N$G0K-OjF_uA(i5P!S2V(cO+5JGcNiI3l;SH*1?NMYPj&m z3_3ks^xa-i*pTL>)rp)y+NYL%#~tzRM3y4B9U*>3sOXdlsWq6UDL^jkK3nwBfM+_XEZI0sM!1+MCBWGvI?d$)C ziuND#Hw1>K&xsTC1UY+?s}nJd_Dg;ZHBwUHfA;?L@Jut zyyjoq(liHlw+o+Dz8(d1b;zmEm|so}u10)>H`K`#ioq1~r`Tn%`WXA>kAE(RGX|cb zJ+57t-|RNb(KjbW-s8w03x(cux!H%m<-7fTZ@MYVXL1Y-v54#(vVn|*OW&F{jI7|l z=gYS*2=Jbwft-CYF+ce7D#r$jk9Y?yj(?xkPUWhMs&1}#(~M?JMu=v<{+{qhBJmVe zhUXjDij2Bje0`=kKY-SzQ&9A%8iTM`%F&gXn@L_TR9{8Oy?xW0S@V6(R4c<`J+7{< z^63e??K_QnTQ#VAL?D&OZ+ck4Q!`-agC_*}74(!0>& z`GS&8l&EvKk*Dc`pTz_i{lU?$PpYvp2_`B4`?2k}cYX3anZI)2Ae|3sj+vHoi4P4z zM@xSIIay(M)n1vvNDVyGbg5=Q*|lNKL)1VB$eKv+w@Lh9LB_cZ>BHB+Rbs&VeaEhu ziI%Cxig!^F)QEh%W}2$#0~`TLBl2XUP?rM>Ec07lQ#V!7mos7g>fH*)c2!ioK)dQ} zY8f;stzKbCN2%+pi;jp#&`Gm9=;j=NVjw* z9{<*F=I4;Ow#JdwXt{7wHvZ=ROnsr8%=(wrvP*Y1|ECx6l`w}}89|0udYyO0V-pwU zkhi|h0^tJfx`jC7qoFds&Ws;U3skPY*mTeDFV?u|lZaX}?oHxak05HDLZ5~G<#BTQ znZ_s0gI{aLYu8E=h=gpf#P9Yf5BB|d#BwBQ`U*ah&?SRcg=!de?e8%a_P(3DW`2zQ z@kIG&;B!D!IDRoBDEjtv=J;cir#)N8_*Ucd;SX%ns|CbJyJt3oWM#F$rUM&$SPAj; zg6wx4^=TvRhx!h-lmhS5GVQbpc5+BBo3{N|?wfG7(Dn$@r`)*sIgLv1`8OiF2NccqBqx zliS@LK3)_SnE)jWOg8SX1q9KnoXrLnUK;&Zy-?e>LE7Bct$lWH!uxZR*0pev3s24o zW`4P*27*YsEOb^Lr75Y787t2x{D?wqC=h9Hd#WcVam(5{d@XW})9-Yhb@kqv0Fs0! zy2Tg@K!M708A#vYn~MP!=ll9k1pUQFv+o;esfY;`6vd%b+Xl6|${ZGhwFH==qrSWd zyIV#@<49+5;$;1Snq@Z9+0^V7iMCHS{JW;R&psD{9y=+&`r2>RiIT6Ygg$29o=ye| zKHa75y7&iv))Xk%`Wx}i90Z*=AJsG)s6el8Iy}KRTKClJKf{IrQ zw2KV)s9SQp3%ix>5q!~)hjzXLY;yCTY&bHfWoTpT_Wj-)jd`C3g=y^7 zHF7H6S@0=LB^!_%1NVvN_k7_`Gc;{)DPY>CwWaPzQL#%u_G%IL^jCJ3G4^z|0y^Vi z5^m^r@InUKujUebLqo0_Ma@0a=3`3{j?xjWKI9c%c`X=Un zJr{lzfYuy3&0L_4i!#2RbZZR8RamZfW6>iyOfXl88FDMuoiZbJSn>PiZt0v#MVd;* z_P}R-$h3;Uu&+C5YA`l4ggl-l7?@XWdj%ku%Hjb-OT6f34IXA2YV5sQ$nyyY zp>eBL#KUi-7+Z85XCH>Qd0aBT1Jcz{yw0*QsxJorR_5bjnla$wdU(`z>u;Loj{y3z zv8g`}R8Cv$5`1 zr(yGl+pBQ z8BS#)buU6=L=8O*L7f*FQA5B(Bs|HQCS0i_tkfyCOnpa%2Y1`Pk}bFVH@WCBIjWd) zzEDmt3b0mARW5_FZaAF7N!gVQkcF|n+d0aa$I{8RB{DLJTXe zqfghS{WyUlE|kuwi31r|q1zwB4Fh}W12c-YdlxNqL-H7LMmCb-ib~)ts?0UXo|Ikn z@~fzN`UJ=MNk+K`LUM+jkBNj8B%Z6DNg)0!29^hDcs@pc%OY0Yhw zReD{XdZ}dxHp5*4f@C@_1tY(JF_fv=v3lX!X>`zUsI5y(ctvq~lsIB~#%g+!-tH7} z2^TuczdgO15)9K+=Pf+L&K;a;?09?lXs0fPPXkogSawFHAO&byn0NK=L}R1+JDiaO;u9 zGVS;;6{<1T+ae^!l#bWu(|=fwRXu78mTwq66-IrBUeX74DR?~*>}Z(_;{LYJt<$Rs zyA8^WgPA0C{j}Plbt32yaBeuPUUnP|HfK#t;Q}@mJGO7Pe;ziuS#VJT8~~%{JD^XW(sOir`|SnZJCZRx6}W@W&lq zL&Tb#Lik`Q{uKxXEUA2uUUWWq$n>S`OWj%KOkBYE>5JUcrIUDQgj8qu>p5Y7R6)`z z0X?CNCA~e${H1;yLMAy$j6P-zsQFYmcc^&Xls-B-;uqa*`oqTr)fKw;p+`eAe6CmvNt-{E z6oqgc=n95raO?JkscxDJYD&np5u=h8Fkd6yySA6nPOt8Sfl)s6{g!S+mmFoGHi9cN z`y!1dHp_2Bma@N{gM9CZ64j50iU<%)JfFFONvrK8n47{Y!6OzTKfzFQicP22oyzl#LkjAQ?T0jo0pw1*E%{Xo z=2d#UQ+*xDsj|b8;ae5J9>eH-gnM}se@3Ub4%+c?gT={cy!c(;`+|0lF(;>|UF%1# z+y3P~VN>k#j`ojYKhZKuT@aH2V(S9=@geHH?=Ay2KRai*_agnZwO2CBycEAvx{4oE zV~nSV3*J|jqE!lBg&9f>yjaC{`HE{x1lTj<(qAdl{1g95Bzg|xYPtoImf2b1UzH6i zbWU+T>N1d7zWv$7j5WCZXH`5fB1cx#_G^JTScWXp6~YviT>#jW z%x>yqHT`;gu}4Zc`Pt$V@hrsOa_DBIhHr)+S?4hdI@t^>K{8u+?a1IuWW+^n*8s`M76=8ew{m!^@R{pAz7Qkm%_9 zTbc37wu;h`F@4>jvd`(`uH!=&nbZhFE~e%k81n+F7v~(0qa5JMym!x{hRRw865owT zc405w)udP0ntF1+v)c@CF?44F8pCfS!zN?a45t1PlPO{!f`NAM@rNPOze{U5 zcQqcN{v#Evrvu!sX*v2Yx&TfE6&;r|9sd`*9K*T|+1QS#PJ#Ly^0yv9FG1j6%OdSV z-^?r69o8t~9(r$U?qpwPh7->(Rn%3oWVI^OuKQ~lUrx^vO$EZX}rT&rhFG`e;#>f zFgQ8MT&Mv^A%^U^FsP8nO`Wj}pc+`d^%CRSjT)Ovg!DZrUcI94jD=hGaH?QNz%@B|Y6>!3o^O-VRW8$Ykr-cBNzGhA-tg7F zTu-~;KZPvLt=KSsUAgC{3%J2xPY1a zYWw>Z&i^P`Eps8vTIg+-dvJ3VCu`0YA^eg2Ao9MVPv86}S7qaxqWM18e|-D@x5xjJ z$tueK2c*5?$X|O-7|&jVS2{b`cCUN+r>W&txPvhuQy~h)`kyD=(sExg@VK%p@KTKI9>iz>T0&|?~d#~|Rni~t{Nht5%?Rsl)P(@!OT$fB)+{4cIbnD!I3IeCu1H1>})N znvl;MuavP>w}C(1>68p%HQa+*mRYGr zW5gNZ(y&PbQ}kbH&;JwpaA)?<0odpKdNIZ<+jfu5<8@6R#{1Dp4BFTYt(h7LKK&xI zl~F)r%%}mtf=VKQ58_i3KOf94OmO>0_#Hc6{>>=<@qx=2W=;COITDI3jP7{D&LQ7p zvoP@y6Mj5tteQf2_&zevsN;;MGg*2r-cN%z{y?xsOIL6QujOsz{l1mA)57m*>c8@sV1&5o9jt0w{pUyi%?f_Ry`ACS(N%syIpRlj z_PaLLEF~rJj6rj*#Qv)-bjI9ZDm+mbUCYbjK;h)5TAPJ?-0nN; z#O9yCv9@dKFQVsfX|O6AOu`h@dkt!#{X2$$`#-e zX$$vU%k1mUEJ5aPP4VPo*}6=7T^`QtPQBjGB)vG?$RG;gN|f7uNy$(z|@&DB9o060T;;u+s3Pu3yZpcgy*l-@zXf zS)AZ-uuO#S%6^|mV(K}11rfl==6cW9#B8D?x2d1`$Z-QP108(fa-MUW%Tt2UVUY{< zzwr;59PjLo25S=)oY}+RNjJCWtvT_>XMRH>Ufg5umNZ2dTFc*#p*wD}Y2-5~(I)qB z#a>Q3(Q|VD;K_SWFf|e|0vz*8yB>^zow@%i$^7qjbU|B9)^||YaE~8SW0l`dD3Uax z=GM}qt<>*qCca&siYDTx){EyQ<>c5}glyt3FQ3d%Aw0z2pJf*8Gl`Zb zzpV)G$4qWZMUWE@;XM|iUWuGd3-`n=0lREDs7SLQ7T*vB2jb)7Q$55zPHX~O(;X-+j7-ADEkSm^)Ys)tEBrF~Un{SMv{NDaY=N>RB1aqxoG}z)denrzD{` ze7=j{^Tk{g34S_bu#i&$UAa87iE&xpOAZh7sn8P)!pN`Y30Sz5orpj7w{bF5Hw;fn zzo?V6JRY3&ZnlHMBsG27;i1l`Qx@1p-haLPNG6Y+cW8c7 zsJ*EKd9mKI=$7P=|K9`|-&L5t<*qFvpk${YVvH7u4eUq47&mbojoF7{;ou2rU9IOr zbBf-$?pVC3g^BT_`>-v3y~BTdb;WNoe?lPQtj$C2Q$tv=8$^_oBbVq=Pc-^&CkSh^ zIjl!IJV1W{~PgMu7cVS*&k zeLWYc0C8yNV%%;HNmsk@kF(jCU;!`se$E7B90L^@0iDD0`1cLB4eMU9w_DE+&NSep zobipx>NjEIQP$zXrdw!R4mM zRrckP!+A~TGagoJUy-0jjJ_rY76v)Xn0F^!N+*WF=r+;T&e50tUj@QH)P)m-?R+VS z=Mhel4ih;TJfrfC@O`{D;%z~_+Dq^Mdv%bmJD8tfHo2*azTVyX{jA){vFq*=suOvn z8Z8&_lci8n6pd8i{#A(=2u;C)1}}WW3v%LpZ%Ase_P>cc{~M|V^Bev%!@`ks@~^!B zDl+fhZV~kzm;z>aX$-mMUAr)8`chfv9T&hIqe(HTWJVtfY5{TWhMejk7bGcMO`@64 zo1nXWxr2JyzeS;6FXKIr3?Gv_8%dKrANF}0f=fp#N&<%LS{X&Lf1QSyvOqpyWEIL| z?<}Om`m%BHZgwGU-aY0RbxD8ACW-Yb{4)AFwF4=tKs;L3i%Z(wlt)A@TDvCNqb~Zg zHWOy3PXcwrb50F=od^&N^27+i`luGO?xh_A55_>6lp{p5!?Jo^*>JCJ#vs2D1UUm= z?=ICd$(4Te7FhRdTtzd&q<#Az?Dfe`mbBtjW91}7ZA+F+ZiZ{xZm46Ab9&aEE%4;R zaIXsAE|Vj9KR!8gT7;=E@8!O6T@yXFDXBN#x+8^Uq|{mvShHg^JE5gVE~ex51sguc zEVQ;`gH`%CRsH8`;z9~~Y4F7Sc_KZHEC9+xxeBa!rTPE~xO{~Y zO>APx-YerUdwu91mm9>l`SM$iqum0r#cr_e+kOVZ!FONKnS}T|iu12j0Y0fihQv!e z>yVwd^p2L?vu|+ohV_2G!-x=1e3U`LpZU_Dl%i8c^PvBqoaldS=g#*3%tiRqgxUDS zoSN!rXxpq|R2PCl57&O$fg$1=ARO}`H2cU}V0_^bZXK4EW1FJMn41Pqqrh$pxI)fN z5aS^!7(hZluJJcB?^L7gE2Z}G_-zXJkwJ>BY^zA5uOH3JCCCVIM!kjWjHj4ufO`rL z4$4Y<49-yyd@k!`CPyKh!|Q1OL*Fbxnhn&fE=&#TXJQXfJ-~bthn|9#3~eHJW1a~?-ml(j=a_4wiWC%ehg7h_TvgcGlQhvL z+SNn+SQ>Pw-33*PS>8Q{z0TcBFvq#d{1pCqp`mgfKqIJFh{C6!!A$36pW!>ViQ66d z?A7K?;aZLgl$7{Cu)a~~pOfKQ_=--~I6{!B&AtAvzp-(>b!|_AM2ebVkB&=Jh6Atz zWt@z^nT{#|N3iSbAkNiUrq)F#m}et#wXhWTlB@2zjiPjOEtG^&zda7vG^gxu;+orU zNhl6dAB>`;d_mI42{FROQ`%5&85#t72QJu7x*1@LmBkh{B6DA8+HR0R8Kd5$n5Sq9 z_hB=P)YSAa&nH{H#HJ`VYE|s8fbk`|AywGe+_V%5VjK#x79AHD&HWk^gN~(f2ww4T3Oz_)T`VXb_#5$|G(zg-cBUHTN#x<4A zrZqGED(e`fzTaGewPER&w?E%7K2|)H`SBkAmsk5A@bC|zG9Y1SKVU|nwS>g3IKUFe zF{*TsZvq5j*E$3v;2HA6d!ys(s(MJ>YxT%2YzOou0UKnvlXQWlCoC{RL;L%Z~E8VR!CR&DZkd8&*A7G`b zC8GurbB$fpXo7;(Sri2sMCs97gx*b?&oYKhm@ki^NGEc;U0rwXx;@ep_CUD5DKk7m zUoHF{{x3d-%{S1Rl9aq?Y-98lrDa62u9dKUOpkWB|a0mW{WT-NeBCxYgunnZyFa&KauQreNCu$ z@r@{7aNby8@;{Gp8Xb5mzS^wgznnN`fo|f7%ve}ZJY+gN$}R^>Pa|0$)qsFUL4uU* z`d-XjA`zoHR++U&)BF3qpq0scJPbA}-ty2D<=MdMpD?6Ni)Cd+>0z8{sn2-3h_42^ z@^#NZEips<(@j&E*pFFCv@U~S7mdg^#-t>maX-}cWYX<9$2Eqi=_0^>vepR2-(W9A z+2&aWTWd28gN&Bje=YXoScb>u?=8`a)$b^URe{I8r%By(S_z40NKO?_ERdB1;d}57 z#?;(SPrSZ8{{Hm`$-teweQnW%5H~0Vt@#76S~a$IH2nAyz|O=ic`glrtyRrQq(}jD z6NQE@|Hhz@4e1Nck6OA2#@9B>g$^kdOB}DK(P&s+1fpW4qtT!n6E7sw(G>_zHa75| z>>rj7_%RJE?H5MC%3v|J)Ts2W9%oO=?*Oi8)kXcya~0%l98&pfFe4b7sD!3r0(6dQqGSbImRD$E z&D`0;>0l6}KG54@q{8Ukyq74HpbIOnWwj7rwK{Rz%4xIr{8(%ty|*t&DR1_5F}mcl zyh{uoOgMxjmd1X(+pTwiyk{H=|E*w9z}3K7SIMjU0uq=@J2!H_frcB~NbXbcvryr0 z*Z&(VjPP zBSJ-%+$dPM_~eLUG?Irl1R{!vk6`U#uQ4$qM2oq!$7F%UAfU3764U&A<*2?Udmw&9 z3@4n^)qaAq-`YUnb9X}~%95ORXX)h5VBcR=D(8IX_X!_VdfxDhCt4k>j;zy*F5iCp zoqHpR7M8U(u~<_cKcfH(&EM^flE8G(I4r-QfeDXyw7HQsq(@Uo2+(m(xvk9njGBpd z!mNVB6yxh!oW6eJ-iGM&l@$a_du`w%C_Wf9C33F0Hlurj@~&Zhum$h@@<|kt;TpRM zqB~@D*Q`LRp`6K-R*C!SgUnzY*WY2PF)DBjXspNwF7B-?*90-z`u(E&x1aN&djXbM z?q>&`dQ=mLWF)S2Om^UG6}|WSdmE~W&`^DR+GuuaZ%@_=@}a9c0inoKg-ZD8!n~FS zRC@&n32n%m^L#HN!4#tmtqa8Rx?klmW~!2$*IXM)uXcDjIWqZa6g|!U;f<`En{8;c zPIl>092~B+8}+l@L7teA91)&^>Ao16ti8mpI2l5uyUavU6KT>iRie#gWF2mlv6 zgF*CQ27ptNqOTi#O+$qII7e%%Ul+zMd?fj7CU&jrb#x+IRS0X?_61?W*A(e?Wj;nb zd|)A6;#=`z%$!BqW<4HgV}^bcoo=L)iQonx3^I;)mIN^7mqkhe(`@vlz#IDcM&!0X z=%Zlq-GojQT<*^(b%%Eilncv#0k$ocsNtTYN?{jUhOv1c2s`z4kET{m_s~*Y*|SZ3 zsREre6rH%|P5lz!u)ZQM8igg=ycKPa9Y~lU-?%JDFlHiLD&tc=PY3AtW}N}oLlU4G zSt)1Liyk8@l#nd|kG@f(1E76hOsE%syQKxIieKa+Q{LIR$5iViFxr5PIHsyFR1 zb%3U3RHNC1irCso=Dsw2=qc}ep+1vQ=R9w>Iqj|uZZSSBf#z>%zSU(Pu8YK7$P8UP z_sYOOC4{E)_b_0M;1l>hkK^;e(a5phww(oYM0$TL-zr#&;G`;T?ctsL7hWz@{7@$O{130Lh+GiVN z*L&)=`%!UqzeuNAFs!~7i38U@Ecz3wWjP}LRPo-IgmS!S)40k9GX;qpYuCkENs~Zc z>0cU){~Vg;WY)u@2{&<-IHG7usZ5KKBpa@mtDNL%7Afy&y@>-dcw6%~=0L(_)*dh^LI!K+~ z_8>%yq`Tu|Bk8%9AR4OhEQ=I?*!?-U+2cV=_al^5Bq2eAMkKYjASQ->bc_aMED$E? z-6Zof$>x*jbgf0>xTZgEY{*$7h9Qt*aJrI<;2!a1!zA-XP4xnAbto) zO(4g~;8MaWoNH2sn(_%Z%>u8fyXH$yatDIp+pA(^*RPOZ9uyW#My(2w;3GZF9(4wt zwz{L;Y|?e;U#BFa6KIFBM&pudcE0)cY~7BCayDano`zQw;XNDxrBG_$!xX$&5H_P* zB!mGcg;?x+Dfa928eaC2=t%nVVF5gi9crh0bOI9&<93^0S{P<5%}hl(!^7{2SURDfH%NIoliHV# z6EPd|_h!wO3bX~5w9M9GX%R$N&|;PlAPeroHb;>^VE204J84O;#Wu<)%is5=`dhRG zISGy=A|OZxHRfAHob0IStqop+T(rS|Ud#kDj2L+S(TyTIyth%dDGi%p6a zk1nrlzTZ-+0_x9D21xSvP8=g4r!E zz1oc0;khPwlwFP#f4=IuTALrKb82pn(T-!8^kjp*n%akbPC!ybk$G>w;w?)ZTK<@Y zx;b7(7sDCGyKUpK)v!hSg&7F&5!c_>+Zi4~!|}g`xEFtuxE4d*o*6OGRSE_%zd-B` z;Ab(Bqg(t$AZf{z4#Ix|9(DSX&;wajtZnAxzCCD51Tr$KgDH$}!suu|2nIaDzivbr zH`)SJhpQlaqLd`dc%jARg51fBZk zA~=Kf*Z8LV-ae&39Gz=Y8cE07^!qS{F(AC7-U|=Or`Eh#$y?Pk!Hk}AfygFAOgF;4 zAeoFN(%ka98t2E)r05Q(Ql0%f^m;Gw8^7%}J=X;GIi?FTC0mJz!5)#0p48jOUJJ75a#LdK=wWfeYr*I-f`x{E9P=zDm!OGg0Zk zCvpo*#brEVH$g(0kb6zZ%JT17s%V%CK?$ip_dbT7=wlln=FMo$o?+RSyOT-ySIUzv zXgMh`ny_I@zs=Z(p?epU@0bS@@M!4RU3FuCd_p>q`)h|FJ9i@MGMC%#Gru|6(uyPv zg4cRE%6*Szi*lx3Cu4Q|M%W!>WUbt2J&nZJUi5;$2i|K|KrIAHG0*FxdxR<>XnxJ{(f8~cibiS1q`E~Rwt%bT_A^84W7t`HaK=H?1^G3Oo7PXLLt-1eV z+Lkl_`dy3iIq!x$Z!DSdrBp@NV&{Qtr{+;`^>*;gi)0i4=TdSzTJhM&3VqGHgg71q z@7Ts2x7JE3!Q^&QpkV&3zI91E;``i@*r$m&fWPqB^)scdap^q6zsf)bEr)?e(6%*& zt9kiq@d@CwD)rIuhTa#UV$#OHLIT~S@@op)l5D~~^PM7H5ZWWbxDf(9d)Sa6L&t)W zszfwIizvK`$ zMH0Hu<3jkfFSIFg;h@WN>aC`B<8>;jwezRYn)C!}|HRz)I!Rl&D8M7gEYSAyaQ^$I zOGw!rl~v8t(58Tqz)U|UwmP_gLb$dzlp3FDt8-qmtGz4diAuL>KGG+=;ir_;%y+|# z+>9<&%vyR&PsGof^n>62up9nNaIfcGp3XpiTXKWF`0mPY(9seAyCP-znve?C+s-gW zGm>Tx3{n+wxL?fEbfo+g;y#g(i^MZXM@0czeK9BCE(X~=#Q0LB`D~+*M&k5JM__3O zFuU?ZV%Bz?y3Sm>3>e5VzVC8g>C5sqPKOWgtz3Ttowwteljuz0IIUk+QB(fp8S}Rr zbRni#iEQ86)G>b{IlVgGppVVX2#edG9x@}z=w_fmrp3%!+xT8Mz7_a!3wME*sMw@a zwDMQR#z&@Nip$1@i6ghpt?%Sn8+v9G6rnS+uUv?TL?v=+tb|xeBI8YCg5Ms8+vKR< zmQbwVC-pW5ft>7G#!v_)ahq1Tq63_VH$I#NVey`d_M#+NR6H;xg_ABaO-VII;zf6) ztwA}DbCk-{=t>y0vp|oNVI4WY{6}^D|9MTVAn;f+icEcJj zoa|ab$xz3|GQ}X`s|&2nz}~cGi#@pLTyu{G5Ptt=vZg5PJcZpK;$^q=GZsa#9-BK_ zD?MgoaDyz~rJZi?D_2eT&nl3N1Y~JXkTl6br$_@_4=pZeQx9^=tUk!!`c38`u=B7F z3eBycp)ZP~6c@Cv1+5*8=X2*~EZ>30tn(=4yKd{s9F#PLA5N<_s5;0d!;DJn@?mmX zC8p)}yXjpT-?tx4Nh4;U-D?T~dlE|MK*+x0Ws}Mnc`3%H)wTwdW$xL+^0_Eafw+ws zQD9i~{!w+YcVa)>6CNuT`i*2JybLuZ`*pQ#XZVdX>5Em>mLSzcp9mtY60FnqJq6@w zahH6#5l_uiJM%IAb(gG)&GIeZk7l}T&ik69D#eAvqog5~l{}*)^UEJEq~jYa>T~x( zLz(i}F|DrvyT$3)77yX{G#A`1Z`8=Jo+4Rff|tseGXznY9w_IpA&-C>7W#+$#k zo(;cquns+0-h~Zj3pAFEC7deARPbPmJf~V|drqSaA-^KcB<^2F@ZeP|D9!5T^ zvzN@M`LdRK-ryuI|LHF|yqwk!m^1DVtP_VcwPxd<0!-XxeNg$eg%E4^kKV`oZX2b- zv0Lk<7%C<;XI&%$K875=dJXUh%z%rn(!KNLc^WXNSZhOESs#+gfWR%z;CZ}(Ytw_| zOv0xyCW=o|h=@BGEqqmJ{K%=;Ov9KP*@u--WgY zq;gl1Ej9Y=O|@rVl~_|kn-xXQ$)27wGcnolwo%~ph$$0cp>w z2)(o%c!IFZm5;+`X7K_&Z!xncbk9T$Bi1}c7wk0_-*2@U6~Qo8^wJ>H9lL&iP(I4P z6Q#3s){KM@;wK(hGzR>7{&I9|EfS!Fa+sn1V`LJ;n9KmH_1MJ4St-lIb#v-znRRkU z(uyV+tB1{up3d&)kHC0EygQbomW41=*4Cb*zVX!)Z1$zeWu1^tAU5q{^?pUZG>~$x z+Em-H|2b3@wUHWkkTV(&mDc^_q|-Fv)@Wi*>c`xNeO`>Nd)H(@I2Iu{cYaGLYJ%|3 zi6^`se(3@!)(nD#O;7zO+uFjf4X7`AjH~ZhIkI|P$!tThE}jQ_Ikb@XzJ%f{!+-8c zL31-&Zq_L(K*%PFq7=F48+uq8YW_NIoE+9nut@JPl=m2d-f)0g4@buK$5bwGLOEFfdYZ?Td7OyQq(&|nC`&g)`|FyI z$Iq99rCliGB8@gr}jVwy^zCtQjzOL0*%!KDQ<0254z*!#Oqj? zL|k8!xsQIvl0n4J8h1dB7kv%#dd|2p+YX~l?j0)R z@JzX)Nf++vLP?`bRv6vhH#RO^r;cGRD6EFy=VJR1<|{;#yVlB|K!7Obt`U520>+iO z35_cCCSia7_zokCDIxLh=ciufLa0L8LYZAN&;`z4C$i2@V&Gf_|Bj;JywC{^vPn=r zh47PXb~qJ^x=zpKwce#ZlcV!%=Mxssxpxi;;pi6;7D%{CC4@|Tf*4bG^03z+*yH8> z;=90Y3oVgg1ZGBsZsFi9(dTQtLeD!ZX!>Y6@#{v=ocH9^T+D41pWW(;%mu~p3L&<_ zeTN*Q#T6J*jC4N~qo!ph1WzV`(HWJa2zbo4y}N}aD!mi)W$xfQLs=T42Fz{knZmoQ zD2BUy``(nnE#7TyQ)4Sl`N+?776~=bEwh|`?T|MQixn`ghMlejCZN!yn5l*l2;+6r zebo5IKUMn$4Q(fl$432ox8`XKQx^Xb=&nktMu#b$D5HPv*D<7&k4kF6kL4Q6axRgAP_ zEifLYrQuyc+s}Bc2uRO^`7O__H+|TrhC~81yBz0rfMJi0xv^}D)%(5?HTVQs#Se7- zWx(Qct%~KTvF+OHTUl^H3k8{$M!>iCCV!^)FuiVMCBs&`@HxuY9} zP!QqEgWM&drr4T4zoeWOVb)PNfRJsmZD;2k`Ma;!p3kK%A5_!<;V0)lnmJXEVt&ZuURy-j)+_9?~{CK-eKznAt~h!IdB96*ZWEj_LFGv;N0ae&|D zneo5*5lkm`clD5ayH0j~Y((>>$QG=RbgHs^@+NI=KAKe4gjyzAg*St+t??($Hu7hV zSLTcf@<)8DV8Dt419&_}PvxE_4)i-Dgf4~^DO5Gc zD0Nf15#tQ!+z;vdv!=rb8gn+Kk>pWIS^ESt%g5arQABks2{W=ojA!icwahwH75e&K zyy-5@%K9s^k9y-dd$OG$Adi6pEiOuoC8m-<)xfA?XY7Vc8FmTT%kK-v@`CCWZXY!# zuJAf?^~xk$9ufbZxD&~JJdD59$K6uB=wr$;P zzR3|9!b>5cmsnS(=>PN?MFm587Z>vy+o)hzZmZUcE~;_+v1T^?8CwBcu!0C@Cx*v> zB3okOXB@_xHzd5W5~AvXKPmca(nPKG_Ar6$fT3x8Jhr|t9R{S$picN?YWu|jYTIO_ zlVj{W8Fs9}b?^x~8js}Lw+pvC5~vVx;@~_PdCl?kioAYb!jNAQ8*ofHDv43N;H)2a zx69 zJUN?ZC1z3Kg2_keCOJKNr92cz{=f#TN{k^M!@gYTcn)XToFxy@bOU zYG;}N7~h8tt`G4{rI9yaY0 zIQkBkk=xfNScFB8eea6u$aXeBND8wbB$lx|ty*P``fphNKlKwsz$ZhGe!upEmZICh zF%J(w0!oKqf@a3T)ed(FXwxpcU$_rWr}%{saPIi(ir0M0H4SfAMFxs8F>0arRVS!1 zk!i7lZb@fV_m+X2=ERbU7E);fh9blDkfz1cTW|}KTGhr(9ieG8(mEKBiYw;aYLKMh^G6fOoAm#1e zWlc&(M3h1tz?eC2rAL$d2`s&0;9WZ;@DS{2&8i+sE_Bw;6-?SD5TTW+7PVPhbYb$D zyjv2nMq`boX|!4Alt}lX?-BIin3RXw#vOE38nPhfWD3$3oSQ+iKw|ugeC=I(7G$vH zhLs05%pXBHn6}sEOC?zFr|?b!d%DL5yNW2=CM{kFeuO zkNeOw$&)jipEIuG@d;y8LLOVdY)2zS4{HVsBfM#Qa&5EkR^Ps?+Krrtptnm2ul6X( zjcH;j_|q$eFLD4uMx=>K@@MDV4`e*>9!$EsO7)$pBsl{WSi3|lEY2@ol0*C|61s&* zC&*#7ixkOny_Z79`!f7J{=UdpwbXCXDcGqFBry>!<9N0#yJyBpG?;7EuvxodT)ck# z$ZP39eRmz0zpL)BJ=%*Ovw_^BhiGLX^novJ-kb`pM^vJnRVP}zp&qz9xZYN~{FyOX z%eIEO4l|;zu3{1IlOT{M2IxL@RV48BLsdOTgp3t={9T6hwzvk+sdnq}E%CkO+FBs3 z7OZBsIjj~}!>$BE>?HZvrQQzPwd5Z`S@210$Xg9pZk+>jox|4@W+v343WcV z4SfZyu{+YxKv=9TMp*vNX)4$T(<{GSuxeS$^Rs{2nsymaBIj)t4FF6HhZDwU`{_ zBFxeB#QHZ(N=F>V92RKd7){P-1kJ76bkJVBQj}QLbC>>uFAd$=Y2M?5m1U0QqKe#2A}a21QqT>9IGRGIHImx zcD)Q3Bx8&%S{x5HcnFP>pXrDN{z&5%!bb$s`hp%#AZ zNqUlphFnu*6w-#KU$Hl^9X-ekkUL`|IXi@#v)V~Ygy9gP9Ak?sm*Ay)P#Pw1rOAKF zHEo(1!r!&PYU~`Qivq$m1BfHxzT%TcP5FpnV~;!)71yJ_To-`Uc!pHc4@@7l_`c@# z9?}T<-7NUvwW-n#f1pE_pv#M$pl&*a7kc1~{e+EqD=-;y7E60ECoNjEk8<_HC8RM1 zN6DsrOSB1C5w^Wdm*0IXCYV#*#U{A=O`TtgCYfur5g zhLdn&Yw?O{WWx~gATmxRTA!r9^S#9Aae^ePCur-f zl~)8%iYRvLQ^UaI)NZ%t9ayl_l549y0xsP#BnRUdD_bc)YSw@`JTlUt z^Q8|D%QNcAym4ZW)Oz62{?q+T7jF2MqOM4iaGWM>#d-VThn!bd6D^IWMwy!|4sfos zVsfUWme@~M_k3VfJlWJL&NGUt8v#s}BJN{+Dx`6hnXcloU0E1Iq&M^{BTVY!6iY&u zLzcG&3sXB)5a%DTqLO5qo~DIf9IF^_)m4{}{0H~_KS{(uaGG+mx(kJzjGSaD4B!7U z#RVPe<){9bl^MG{OIQ?8KpZVpJ9fIIYk4Wq4Ou*$D2k&4>V@U=H)y_h>%B?z&<9d% ztjYtgsDML+Y;&2REJvly6iQLx`&O3E8gIozEhs(rX6Rt&-{r*=NJYUuvZkn$2y%wt z8RYic$qc8dEA%%|d>>T=V@yDSkkOvF*lhiEMae+*>)@R+MWt8_z*+8fGwZTVPX-o* zhVl@_#1;~VO6#u+rfDR%bkeK=pjNBIen=;;c?EPBqO44_49$Zx&tMzi1)fgi_n5pTD)P8 zk!3=dqU^p5fxqs44H-MP!ufE`s?mYS6!Q0eY^d~RwHe{>tWy_Z50lIANi_5ONbLhHLTj+;k`lRVntQs zH0e0J$X9L>37;HQj2`9UglTHRWw?1q7RjxJVlGa~Sgd^Sq;ichMad&wYZVP#%k7@p zA_v0u35RW|uZe4a`x}Jy2AodKHw+<^63M3TpMX900HQvi#?6*^N3hv!)e>1)9&B`i zFlnzC8q-JFl{~^dr{#d!+JWl)8Kn{ZYaIZIw<(ca-0T6cso?~-p9;C9qWvlki8xb7 zV4JRSa$_hs6xuJRd3&RouU$Pm>1$YXd$F&~WK>6Y#>RcgteYu)pSyVCiF4S)+w`c9 zqNy`7Okv!5=%d6U_AaH4~8<*kF`wwUmz44UffXXMe3|L_8zU zF}o9Ezhuq^;usU_b-J#?)RCbTS|4 z_FZqex2o)K%G^>x**R1j@Gz9@hQX<}6YQauNe_|**CI~Jd5JXn)D(I6Xc+zm%y#K9 z+k6H&H1q23<}Zg1g(PVdQx$qCwsGZ^-^i!ewK*blQS5e56*$n!4zK@)>@cLlvfgP-M-vb-FxvFl zNyD$DBB0nX%~$|0<1$Y>C$(~n>QX&heJ>fUzQ7@&z(Oa(+crTr8tB0x)+CKxO4*1M z9&ZpJVnT}G@GEdp3-)AbS8wisPuM@lMSl;o{%;-t9e5W|b=O~WU4GN}BOQGmdB{gojPO zPe)MPB7Cv3{w%5x7xNPXCYv9t)0&3x1@wzZ%7WbpHkO}gVX5QmlK3A-gCO06fAaCP zn?e(OX=#kV08fWIAF&eG5$We2H(FA7uF8qkun>we<~Kh>!muQ&mhDZzUVJel;-wit zuJs;xzbeRV*nQ#=+O2CO6x0BYXb&mbPR^Z%t@Wt33J3|~4K%r^Fs0it>-cTowmdXb z{u|v2vIHxpna14-W?2>h5s6f9RbAtY5zrVy^k+F)3_W)e1Ba~|?ijxWurBUiU|s!) zO&1Q4Wud+c$0?|J91p93SKz2PX5%I2eYZGoZJ9h_Yu@p=(9lCrzRms)T>1)zMl)5N zz?^9lL?tVG;$Cj{->}O*zpJ{&-LXwLV1zhn=EZTn~skY*qz6xog zDC#VZn~zp@mgYWP6EzkqtE`Voa!5|Bs^7BrZ}XM3SA<&`xux$8|NG0)c_IZ~SnqzS z$VcYUiN-8#Rc)x9GuS5OK=XRLZ>JN@Tpns!pF0~m#$9m9fGG*!qt(*iIHZ4Q+DDYX zO&7$7duiohm=HmFkND`rzd`qOx5sWBoAZE8>hBCch25jM|GuU*HQK8Y(0o3)m0+CL zgc~8`jaDkn%sL^_hE$xT+ zIMUaf?$mnGodCMrM+n_I!0AJgDyYc}!@3i=M1I|Gia39r&^@0IxI^O+VviHRqFA{g zXJDk9FZ>HK_xouYzn%4GD-Gcd5FZ9&>_)w2K-h*_1Z$%)f-G^wpG(lbs zK-Vq^yaRw>X}d;juQP=s@qO0LCIa-|uY7?I#i}9TPGm%`na=b66Vm%npE`+w>!=fE zUVa`wH=HR1go6xUo+H3YEPDa{NFDx2DOVV3CtEsF6;6ww+d+RKeiz*MUH=uIvo`!= zYwlDbjfq*Ox0FZk{%aRb1%Bh=?gUx#6Iob;tRP^@tB2extwsBMD7l2Alu(HW#OOlo zTW(DT(r&D-V<^W`rIQffK4eq9P0h{Dcp4TQoxc8?xAr@dwVVcCm4<_r^*tKH>kFmQ ztZe)dk5yohLn}@Qaix*ukI$ENA6dqCE(@B93G6H1TJ0sQ9hD433=@7#nWp?NFAFL~ zpUfsSoc!d@YoFfC8aowbeZ0sQ30BZFx(XLUR(MO1HC_ujvg5@q2i%?dy5ax)vw=j? zr7a>u)`jPMN#64)r7*Gx2`J_5mCIt~!&mtybnGAdoQMOzb}r#zG1j6M4Hp)wQFspF zlo>AZr?!1k{J=FA%nS8!Ji}9UwYtTrr4AG&fd`^T{=@x^BvJQE?ArkEw8d-l`t-ivTm7`zk<*l^I2XB>@b=iWkh9cZUihC^U(V>X5uuaIFWjuZkaUpR`Rsaz`^!+kX8T933M31)fj?tl>4Fv^mf6ii}e4;Lg1MV@QR{d9#gNkq3}$ z;xxVX-wc8FH!Cuxb@_Vb{Lv3IV_Vp(oMOTy?-)DV13Vt>VRLzf_vdsD+@ROvsYh-Z zgNf*I!cp0dSt%O-8;bM$bGnP!c(EDIgI4i@Wpnp*OP+^MEqr>hpuX}aJkg>$dXx+_ zFM>+aYQ=UaxkxyI`@jjIDZE^=f)o$=7U+WZ(v59O2!K%7-0Q14~aA|6J%Kp+4nPt?- z*9LH5=l_1-Bj)?U@23j)J!?;}OIb&8jZT*-^XJ35q$cFqdD0QPtBve#f`3N)fA^(< zGKdgNKZ*}@n9I3bgnWdZH(eFMvmxdM#%A~IKU@v6^OjNJ>6hbxK@W5F1Z$6T9+PXc zCd^(V-vblwNJ|{~!|$HlKz-Hg`E;+^8pQPn;{&6YD>xBxnM%BN;Z z-}^UL_#nl2*VHk4oXfUgshQXc0Q&RCcDa5n$TB+kx0tF82N}FEl=|Nm|7m-lB4|z? zx_KW0ZBHdrC7i#Q%Ce0mQ$a}g?v4Gs0L=Lx!q+)M83NTw4L35NQp7De z+58nLdnf@Nt}17(@5)G)6*qO)Vt#-M*fdu&1H7AAy(CE$fOK94KPq*FZLN!o8XZ>U zp1)1aRlHmKayKBPz9wRnDqvszTB)sjabagW@VdRLK)Bo`&JO*rF%x+cWJ)2@IMo#s z-7V2mDl}=SS$%LhV2T)=efc25pIYZ`Ti<^e^R4cNN_AtK-p_ld4u+yFGow9!xntD& zt6ptufr5zfn2r)M>e2LqHp(rI9jRQ=vhYh@#-f+~;zg~yjNQ@)-}pC>@MMAixrk^Y zp*y&qd;53`q&8^0o{kJ{KcgR;W9Eb*M}-k*Eum5#^-S@VoinAE3Fywm zId-U$pIMvw>F*HFb8Nj^)@Bl+zmL*L!*%0cne1t@Ud=F6b(?o1bNj-jGcn(_#rF3I zQ~w^}qe~`WWRHHO5dG4=h0D|n`}fC!DGy3>94S*f?9b|Y*2qo+?d|QF;eG>$scC0N zBP>GmQDTZ&N-^HgW@uK8CBK_cHb!gZ&N+9(CI6W|-(cfSi+9|m?3 zV`VMOecY9-Ng`kh`~Q~NZ)=UO7byYX^95rdDU1*ov=Go6#Z~vKVI$smN9^CL2W((? zkQw2GRd!}CRC)pM0KImRUDQ8p5CR%iiR=p0^KNweuiL>7lMAHUEGx=@`};1*K+u-5 z7Jc-8EbIUNlcrveFt+kzr{G@*z0H*l8yEPq+hmEKM5@#o$^(omlb@VM#HorIEpRA;! KM3tCP@c#k#S+1`D diff --git a/a1/assets/modularity-2.png b/a1/assets/modularity-2.png deleted file mode 100644 index b750ab058c2fab3e62af8b0960c5224098322cff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34181 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 diff --git a/a1/assets/ng-clean-code-banner.png b/a1/assets/ng-clean-code-banner.png deleted file mode 100644 index b35c537fa57245815ee2b11e9a7e44b14e38e5df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56340 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

LkK6~ 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 diff --git a/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet deleted file mode 100644 index 5a46e787..00000000 --- a/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet +++ /dev/null @@ -1,27 +0,0 @@ - - - ngcontroller - text.plain, source.js - diff --git a/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet deleted file mode 100644 index a4bbd6a2..00000000 --- a/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet +++ /dev/null @@ -1,40 +0,0 @@ - - - ngdirective - text.plain, source.js - diff --git a/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet deleted file mode 100644 index 64ed5656..00000000 --- a/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet +++ /dev/null @@ -1,27 +0,0 @@ - - - ngfactory - text.plain, source.js - diff --git a/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet deleted file mode 100644 index 784c3673..00000000 --- a/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet +++ /dev/null @@ -1,23 +0,0 @@ - - - ngfilter - text.plain, source.js - diff --git a/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet deleted file mode 100644 index afb742f9..00000000 --- a/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet +++ /dev/null @@ -1,13 +0,0 @@ - - - ngmodule - text.plain, source.js - diff --git a/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet deleted file mode 100644 index c57ed25f..00000000 --- a/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet +++ /dev/null @@ -1,24 +0,0 @@ - - - ngservice - text.plain, source.js - diff --git a/a1/assets/testing-tools.png b/a1/assets/testing-tools.png deleted file mode 100644 index 5ed095eda4b698da41f0ae0cae55caf2e56baa25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95536 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 z 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 MUPG={#v<(h03FwF$p8QV diff --git a/a1/assets/vim-angular-snippets/angular.controller.snip b/a1/assets/vim-angular-snippets/angular.controller.snip deleted file mode 100644 index f8a0cc7c..00000000 --- a/a1/assets/vim-angular-snippets/angular.controller.snip +++ /dev/null @@ -1,22 +0,0 @@ -snippet ngcontroller -options head - (function() { - 'use strict'; - - angular - .module('${1:module}') - .controller('${2:Controller}Controller', $2Controller); - - /* @ngInject */ - function $2Controller(${3:dependencies}) { - var vm = this; - vm.title = '$2Controller'; - - activate(); - - //////////////// - - function activate() { - } - } - })(); diff --git a/a1/assets/vim-angular-snippets/angular.directive.snip b/a1/assets/vim-angular-snippets/angular.directive.snip deleted file mode 100644 index c8335fa5..00000000 --- a/a1/assets/vim-angular-snippets/angular.directive.snip +++ /dev/null @@ -1,35 +0,0 @@ -snippet ngdirective -options head - (function() { - 'use strict'; - - angular - .module('${1:module}') - .directive('${2:directive}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - // Usage: - // - // Creates: - // - var directive = { - bindToController: true, - controller: ${4:Controller}, - controllerAs: '${5:vm}', - link: link, - restrict: 'A', - scope: { - } - }; - return directive; - - function link(scope, element, attrs) { - } - } - - /* @ngInject */ - function $4() { - - } - })(); diff --git a/a1/assets/vim-angular-snippets/angular.factory.snip b/a1/assets/vim-angular-snippets/angular.factory.snip deleted file mode 100644 index 83817faa..00000000 --- a/a1/assets/vim-angular-snippets/angular.factory.snip +++ /dev/null @@ -1,22 +0,0 @@ -snippet ngfactory -options head - (function() { - 'use strict'; - - angular - .module('${1:module}') - .factory('${2:factory}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - var service = { - ${4:func}: $4 - }; - return service; - - //////////////// - - function $4() { - } - } - })(); diff --git a/a1/assets/vim-angular-snippets/angular.filter.snip b/a1/assets/vim-angular-snippets/angular.filter.snip deleted file mode 100644 index 4999f3b8..00000000 --- a/a1/assets/vim-angular-snippets/angular.filter.snip +++ /dev/null @@ -1,19 +0,0 @@ -snippet ngfilter -options head - (function() { - 'use strict'; - - angular - .module('${1:module}') - .filter('${2:filter}', $2); - - function $2() { - return $2Filter; - - //////////////// - function $2Filter(${3:params}) { - return $3; - }; - } - - })(); diff --git a/a1/assets/vim-angular-snippets/angular.module.snip b/a1/assets/vim-angular-snippets/angular.module.snip deleted file mode 100644 index fe43cd48..00000000 --- a/a1/assets/vim-angular-snippets/angular.module.snip +++ /dev/null @@ -1,10 +0,0 @@ -snippet ngmodule -options head - (function() { - 'use strict'; - - angular - .module('${1:module}', [ - '${2:dependencies}' - ]); - })(); diff --git a/a1/assets/vim-angular-snippets/angular.service.snip b/a1/assets/vim-angular-snippets/angular.service.snip deleted file mode 100644 index 99d15fc1..00000000 --- a/a1/assets/vim-angular-snippets/angular.service.snip +++ /dev/null @@ -1,19 +0,0 @@ -snippet ngservice -options head - (function() { - 'use strict'; - - angular - .module('${1:module}') - .service('${2:Service}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - this.${4:func} = $4; - - //////////////// - - function $4() { - } - } - })(); diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets deleted file mode 100644 index 51cfdc16..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets +++ /dev/null @@ -1,22 +0,0 @@ -snippet ngcontroller -(function() { - 'use strict'; - - angular - .module('${1:module}') - .controller('${2:Controller}Controller', $2Controller); - - /* @ngInject */ - function $2Controller(${3:dependencies}) { - var vm = this; - vm.title = '$2Controller'; - - activate(); - - //////////////// - - function activate() { - } - } -})(); -endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets deleted file mode 100644 index 14c882f5..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets +++ /dev/null @@ -1,35 +0,0 @@ -snippet ngdirective -(function() { - 'use strict'; - - angular - .module('${1:module}') - .directive('${2:directive}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - // Usage: - // - // Creates: - // - var directive = { - bindToController: true, - controller: ${4:Controller}, - controllerAs: '${5:vm}', - link: link, - restrict: 'A', - scope: { - } - }; - return directive; - - function link(scope, element, attrs) { - } - } - - /* @ngInject */ - function $4() { - - } -})(); -endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets deleted file mode 100644 index 41966ff7..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets +++ /dev/null @@ -1,22 +0,0 @@ -snippet ngfactory -(function() { - 'use strict'; - - angular - .module('${1:module}') - .factory('${2:factory}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - var service = { - ${4:func}: $4 - }; - return service; - - //////////////// - - function $4() { - } - } -})(); -endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets deleted file mode 100644 index e2b10ef6..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets +++ /dev/null @@ -1,19 +0,0 @@ -snippet ngfilter -(function() { - 'use strict'; - - angular - .module('${1:module}') - .filter('${2:filter}', $2); - - function $2() { - return $2Filter; - - //////////////// - function $2Filter(${3:params}) { - return $3; - }; - } - -})(); -endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets deleted file mode 100644 index 972018df..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets +++ /dev/null @@ -1,10 +0,0 @@ -snippet ngmodule -(function() { - 'use strict'; - - angular - .module('${1:module}', [ - '${2:dependencies}' - ]); -})(); -endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets deleted file mode 100644 index 53a8692d..00000000 --- a/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets +++ /dev/null @@ -1,19 +0,0 @@ -snippet ngservice -(function() { - 'use strict'; - - angular - .module('${1:module}') - .service('${2:Service}', $2); - - /* @ngInject */ - function $2(${3:dependencies}) { - this.${4:func} = $4; - - //////////////// - - function $4() { - } - } -})(); -endsnippet diff --git a/a1/assets/vscode-snippets/javascript.json b/a1/assets/vscode-snippets/javascript.json deleted file mode 100644 index 0b1920ad..00000000 --- a/a1/assets/vscode-snippets/javascript.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "Angular Controller": { - "prefix": "ngcontroller", - "body": [ - "(function() {", - "'use strict';", - "", - "\tangular", - "\t\t.module('${Module}')", - "\t\t.controller('${Controller}Controller', ${Controller}Controller);", - "", - "\t${Controller}Controller.$inject = ['${dependency1}'];", - "\tfunction ${Controller}Controller(${dependency1}) {", - "\t\tvar vm = this;", - "\t\t$0", - "", - "\t\tactivate();", - "", - "\t\t////////////////", - "", - "\t\tfunction activate() { }", - "\t}", - "})();" - ], - "description": "Angular 1 controller" - }, - "Angular Service": { - "prefix": "ngservice", - "body": [ - "(function() {", - "'use strict';", - "", - "\tangular", - "\t\t.module('${Module}')", - "\t\t.service('${Service}', ${Service});", - "", - "\t${Service}.$inject = ['${dependency1}'];", - "\tfunction ${Service}(${dependency1}) {", - "\t\tthis.${exposedFn} = ${exposedFn};", - "\t\t$0", - "\t\t////////////////", - "\t\tfunction ${exposedFn}() { }", - "\t}", - "})();" - ], - "description": "Angular 1 service" - }, - "Angular Factory": { - "prefix": "ngfactory", - "body": [ - "(function() {", - "'use strict';", - "", - "\tangular", - "\t\t.module('${Module}')", - "\t\t.factory('${Service}', ${Service});", - "", - "\t${Service}.$inject = ['${dependency1}'];", - "\tfunction ${Service}(${dependency1}) {", - "\t\tvar service = {", - "\t\t\t${exposedFn}:${exposedFn}", - "\t\t};", - "\t\t$0", - "\t\treturn service;", - "", - "\t\t////////////////", - "\t\tfunction ${exposedFn}() { }", - "\t}", - "})();" - ], - "description": "Angular 1 factory" - }, - "Angular Directive": { - "prefix": "ngdirective", - "body": [ - "(function() {", - "\t'use strict';", - "", - "\tangular", - "\t\t.module('${Module}')", - "\t\t.directive('${Directive}', ${Directive});", - "", - "\t${Directive}.$inject = ['${dependency1}'];", - "\tfunction ${Directive}(${dependency1}) {", - "\t\t// Usage:", - "\t\t//", - "\t\t// Creates:", - "\t\t//", - "\t\tvar directive = {", - "\t\t bindToController: true,", - "\t\t controller: ${Controller}Controller,", - "\t\t controllerAs: '${vm}',", - "\t\t link: link,", - "\t\t restrict: 'A',", - "\t\t scope: {", - "\t\t }", - "\t\t};", - "\t\treturn directive;", - "\t\t", - "\t\tfunction link(scope, element, attrs) {", - "\t\t}", - "\t}", - "\t/* @ngInject */", - "\tfunction ${Controller}Controller () {", - "\t\t$0", - "\t}", - "})();" - ], - "description": "Angular 1 directive" - }, - "Angular Module": { - "prefix": "ngmodule", - "body": [ - "(function() {", - "\t'use strict';", - "", - "\tangular.module('${module}', [", - "\t\t$0", - "\t]);", - "})();" - ], - "description": "Angular 1 module" - } -} \ No newline at end of file diff --git a/a1/assets/vscode-snippets/typescript.json b/a1/assets/vscode-snippets/typescript.json deleted file mode 100644 index 716dc70e..00000000 --- a/a1/assets/vscode-snippets/typescript.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "Angular Controller": { - "prefix": "ngcontroller", - "body": [ - "namespace ${module} {", - "'use strict';", - "", - "export class ${Controller}Controller {", - "\tstatic $inject: Array = ['${dependency1}'];", - "\tconstructor(private ${dependency1}: ${dependency1Type}) {", - "$0", - "}", - "", - "\t${property}: ${propertyType} = ${propertyValue};", - "", - "\t${fn}() { }", - "}", - "", - "angular", - "\t.module('${Module}')", - "\t.controller('${Controller}Controller', ${Controller}Controller);", - "}" - ] - }, - "Angular Service": { - "prefix": "ngservice", - "body": [ - "namespace ${module} {", - "'use strict';", - "", - "export interface I${Service} {", - "\t${serviceFn}: (${dependency1}:${dependency1Type}) => ${returnType};", - "}", - "export class ${Service} implements I${Service} {", - "\tstatic $inject: Array = ['${dependency1}'];", - "\tconstructor(private ${dependency1}: ${dependency1Type}) {", - "", - "}", - "", - "\t${serviceFn}: (${dependency1}:${dependency1Type}) => ${returnType} = (${dependency1}:${dependency1Type}) => {", - "\t\t$0", - "\t}", - "", - "}", - "", - "angular", - "\t.module('${Module}')", - "\t.service('${Service}', ${Service});", - "}" - ] - }, - "Angular Module": { - "prefix": "ngmodule", - "body": [ - "namespace ${module} {", - "\t'use strict';", - "", - "\tangular.module('${module}', [", - "\t$0", - "\t]);", - "}" - ] - } -} \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml deleted file mode 100644 index c0e9ea43..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml deleted file mode 100644 index 61e73945..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml deleted file mode 100644 index 566b434c..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml deleted file mode 100644 index a55e2e0c..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml deleted file mode 100644 index 761b56f6..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml deleted file mode 100644 index fa4c92f8..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml deleted file mode 100644 index ba57b16f..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml deleted file mode 100644 index 3955ec12..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml deleted file mode 100644 index 8941f99b..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml deleted file mode 100644 index 445893a4..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml deleted file mode 100644 index 9dcf5bbf..00000000 --- a/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml b/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml deleted file mode 100644 index 71188130..00000000 --- a/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - - diff --git a/a1/i18n/README.md b/a1/i18n/README.md deleted file mode 100644 index 40e24863..00000000 --- a/a1/i18n/README.md +++ /dev/null @@ -1,41 +0,0 @@ -#Translations - -The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - -*All translations are created by and maintained by the community.* - - 1. [French](fr-FR.md) by [Eric Le Merdy](https://github.com/ericlemerdy) and [Xavier Haniquaut] (@xavhan) - 1. [German](de-DE.md) by [Michael Seeger](https://github.com/miseeger), [Sascha Hagedorn](https://github.com/saesh) and [Johannes Weber](https://github.com/johannes-weber) - 1. [Italian](it-IT.md) by [Angelo Chiello](https://github.com/angelochiello) - 1. [Japanese](ja-JP.md) by [@noritamago](https://github.com/noritamago) - 1. [Macedonian](mk-MK.md) by [Aleksandar Bogatinov](https://github.com/Bogatinov) - 1. [Portuguese-Brazil](pt-BR.md) by [Vinicius Sabadim Fernandes](https://github.com/vinicius-sabadim) - 1. [Russian](ru-RU.md) by [Vasiliy Mazhekin](https://github.com/mazhekin) - 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) - 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) - 1. [Korean](ko-KR.md) by [Youngjae Ji](https://github.com/zirho) - 1. [Turkish](tr-TR.md) by [Uğur Korfalı](https://github.com/kel-sakal-biyik) - -## Contributing -Language translations are welcomed and encouraged. The success of these translations depends on the community. I highly encourage new translation contributions and help to keep them up to date. - -All translations must preserve the intention of the original document. - -> All contributions fall under the [MIT License of this repository](https://github.com/johnpapa/angularjs-styleguide#license). In other words, you would be providing these free to the community. - -### New Translations -1. Fork the repository -2. Create a translation file and name it using the 118n standard format. -3. Put this file in the i18n folder -4. Translate the original English version to be current with the latest changes -3. Make a Pull Request - -Once you do these I will merge, point the translation links to it, and enter the translation credit to you. - -### Updated Translations -1. Fork the repository -2. Make the translation changes -3. Make a Pull Request - -Once you do these I will merge, point the translation links to it, and enter the translation credit to you. - diff --git a/a1/i18n/de-DE.md b/a1/i18n/de-DE.md deleted file mode 100644 index 99be9755..00000000 --- a/a1/i18n/de-DE.md +++ /dev/null @@ -1,3098 +0,0 @@ -# Angular Style Guide - -*Dogmatischer Angular Styleguide für Teams von John Papa [@john_papa](//twitter.com/john_papa)* - -Sind Sie auf der Suche nach einem dogmatischen Styleguide zur Syntax, zu Konventionen und zur Struktur von Angular-Anwendungen, dann treten Sie näher. Diese Vorlagen basieren auf meinen Erfahrungen mit [Angular](//angularjs.org), Präsentationen, [Pluralsight Trainingskursen](http://pluralsight.com/training/Authors/Details/john-papa) und der Arbeit in Teams. - -Der Zweck dieses Styleguides ist es, eine Anleitung für die Erstellung von Angular-Anwendungen bereitzustellen, indem ich die Konventionen, die ich nutze, zeige und - wichtiger als das - beschreibe, warum ich sie wähle. - ->Wenn Sie diese Anleitung mögen, dann besuchen Sie meinen Kurs [Angular Patterns: Clean Code] (http://jpapa.me/ngclean) auf Pluralsight, der eine Begleitung zu dieser Anleitung darstellt. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Außergewöhnliche Community und Anerkennung -Arbeite niemals im leeren Raum. Ich finde, dass die Angular-Community eine unglaubliche Gruppe ist, die ihre Erfahrung mit Leidenschaft teilt. Also haben ein Freund und Angular-Experte, Todd Motto, und ich viele Vorlagen und Konventionen zusammengetragen. Bei den meisten sind wir uns einig, und bei ein paar sind wir verschiedener Meinung. Ich möchte Sie ermutigen, sich [Todd's Guidelines](https://github.com/toddmotto/angularjs-styleguide) anzusehen, um ein Gespür für seinen Ansatz zu entwickeln und ihn vergleichen zu können. - -Viele meiner Vorlagen entstanden aus Pair-Programming-Sessions, die [Ward Bell](http://twitter.com/wardbell) und ich hatten. Mein Freund Ward hat sicherlich die endgültige Entwicklung dieser Anleitung beeinflusst. - -## Schauen Sie sich die Vorlagen in einer Beispielanwendung an -Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebenso hilfreich, sie auch in der Praxis zu sehen. Diese Anleitung wird von einer Beispielanwendung begleitet, die diesen Vorlagen und Mustern folgt. Sie finden die [Beispielanwendung (namens "modular") hier] (https://github.com/johnpapa/ng-demos) im `modular`-Ordner. Fühlen Sie sich frei, diese zu holen, indem Sie sie clonen oder einen Fork erstellen.[Anweisungen, sie zum Laufen zu bringen, finden Sie im Readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -##Übersetzungen -[Übersetzungen dieses Styleguides](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) werden von der Community hier verwaltet. - -## Inhaltsverzeichnis - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Module](#module) - 1. [Controller](#controller) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Datenservices](#datenservices) - 1. [Direktiven](#direktiven) - 1. [Promises für einen Controller auflösen](#promises-für-einen-controller-auflösen) - 1. [Manuelle Code-Anmerkungen für das Einfügen von Abhängigkeiten (Dependency Injection)](#manuelle-code-anmerkungen-für-das-einfügen-von-abhängigkeiten-dependency-injection) - 1. [Minifizierung und Code-Anmerkungen](#minifizierung-und-code-anmerkungen) - 1. [Fehlerbehandlung](#fehlerbehandlung) - 1. [Namensgebung](#namensgebung) - 1. [Anwendungsstruktur: LIFT Prinzip](#anwendungsstruktur-das-lift-prinzip) - 1. [Anwendungsstruktur](#anwendungsstruktur) - 1. [Modularität](#modularität) - 1. [Startlogik](#startlogik) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testen](#testen) - 1. [Animationen](#animationen) - 1. [Kommentare](#kommentare) - 1. [JS Hint](#js-hint) - 1. [JSCS](#jscs) - 1. [Konstanten](#konstanten) - 1. [Dateitemplates und Snippets](#dateitemplates-und-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Automatisierung von Aufgaben](#automatisierung-von-aufgaben) - 1. [Filter](#filter) - 1. [Angular Dokumentation](#angular-dokumentation) - 1. [Beiträge](#beiträge) - 1. [Lizenz](#lizenz) - -## Single Responsibility - -### Rule of 1 -###### [Style [Y001](#style-y001)] - - - Definiere eine Komponente pro Datei. - - Das folgende Beispiel definiert das `app`-Modul und seine Abhängigkeiten, einen Controller und eine Factory in ein und derselben Datei. - - ```javascript - /* zu vermeiden */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Die gleichen Komponenten sind nun in separaten Dateien untergebracht. - - ```javascript - /* empfohlen */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* empfohlen */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* empfohlen */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - - Packen Sie Angular-Komponenten in eine Funktion, die sich sofort selbst ausführt (Immediately Invoked Function Expression, kurz: IIFE). - - *Warum?*: Eine IIFE entfernt Variablen aus dem global scope. Dies verhindert, dass Variablen- und Funktionsdeklarationen länger als erwartet im global scope bleiben. Und es verhindert zusätzlich, Kollisionen bei Variablen zu verhindern. - - *Warum?*: Wird Ihr Code für das Deployment auf einem Produktionsserver minifiziert und in einer einzigen Datei zusammengepackt, kann es zur Kollision von Variablen (auch globalen) kommen. Eine IIFE schützt Sie hiervor, indem sie den Gültigkeitsbereich der Variablen auf die jeweilige Datei beschränkt. - - ```javascript - /* zu vermeiden */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // Logger-Funktion wird als globale Variable hinzugefügt - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // Storage-Funktion wird als globale Variable hinzugefügt - function storage() { } - ``` - - ```javascript - /** - * empfohlen - * - * es verbleiben keine globalen Variablen - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Anmerkung: Nur zur Verkürzung des dargestellten Codes, wird beim Rest dieser Beispiele die IIFE-Syntax ausgelassen. - - - Anmerkung: IIFE's verhindern, dass Testcode private Elemente, wie reguläre Ausdrücke oder Hilfsfunktionen ansprechen können. Diese können oft gut direkt für sich selbst getestet werden. Sie können diese jedoch auch durch zugreifbare Elemente oder durch ihre eigene Komponente zugreifbar machen. Zum Beispiel können Sie Hilfsfunktionen, reguläre Ausdrücke oder Konstanten in ihrer eigenen Factory oder Konstante platzieren. - -**[Zurück zum Anfang](#table-of-contents)** - -## Module - -### Namenskollisionen vermeiden -###### [Style [Y020](#style-y020)] - - - Benutzen Sie eindeutige Namenskonventionen mit Trennzeichen für Untermodule. - - *Warum?*: Eindeutige Namen helfen Kollisionen bei Modulnamen zu verhindern. Trennzeichen helfen bei der Definition von Modulen und deren Untermodul-Hierarchie. Zum Beispiel kann `app` Ihr Root-Modul sein, während `app.dashboard` und `app.users` Module sein können, die von `app` als Abhängigkeiten genutzt werden. - -### Definitionen (auch: Setter) -###### [Style [Y021](#style-y021)] - - - Deklarieren Sie Module ohne eine Variable, indem Sie die Setter-Syntax verwenden. - - *Warum?*: Bei einer Komponente pro Datei besteht kaum die Notwendigkeit eine Variable für das Modul einzuführen. - - ```javascript - /* zu vermeiden */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Nutzen Sie stattdessen die einfache Setter-Syntax. - - ```javascript - /* empfohlen */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getter -###### [Style [Y022](#style-y022)] - - - Wenn Sie ein Modul nutzen, vermeiden Sie die Nutzung einer Variablen. Nutzen Sie stattdessen eine Verkettung mit der Getter-Syntax. - - *Warum?*: Dies führt zu mehr lesbarem Code und verhindert Variablenkollisionen oder Leaks. - - ```javascript - /* zu vermeiden */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* empfohlen */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs. Getting -###### [Style [Y023](#style-y023)] - - - Nur einmal setzen und für alle anderen Instanzen lesen (get). - - *Warum?*: Ein Modul sollte nur einmal erstellt werden und ab diesem Punkt an nur noch gelesen werden. - - - Benutzen Sie `angular.module('app', []);` um das Modul zu erzeugen (set). - - Benutzen Sie `angular.module('app');` um das Modul zu erhalten (get). - -### Benannte vs. anonyme Funktionen -###### [Style [Y024](#style-y024)] - - - Benutzen Sie für Callbacks benannte Funktionen, anstatt eine anonyme Funktion zu übergeben. - - *Warum?*: Dies führt zu lesbarerem Code, ist einfach zu debuggen und verringert die Schachtelung des Callback-Codes. - - ```javascript - /* zu vermeiden */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* empfohlen */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Controller - -### controllerAs View-Syntax -###### [Style [Y030](#style-y030)] - - - Ziehen Sie die [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/)-Syntax der `klassischen Controller-Mit-$scope`-Syntax vor. - - *Warum?*: Controller werden immer wieder neu erstellt. Man erhält jedes Mal eine neue Instanz und die `controllerAs`-Syntax ist näher an der eines JavaScript-Konstruktors, als die `klassische Controller-Mit-$scope-Syntax`. - - *Warum?*: Es begünstigt die Bindung von "Punkt-Notierten" Objekten im View (z. B. `customer.name` statt `name`), was kontextbezogener und einfacher zu lesen ist und Referenzproblemen, die ohne diese "Punkt-Notation" auftreten können, vorbeugt. - - *Warum?*: Hilft die Nutzung von `$parent`-Aufrufen in Views und geschachtelten Controllern zu vermeiden. - - ```html - -

- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax -###### [Style [Y031](#style-y031)] - - - Ziehen Sie die `controllerAs`-Syntax der `klassischen Controller-Mit-$scope-Syntax` vor. - - - Die `controllerAs`-Syntax nutzt `this` innerhalb des Controllers, welches an `$scope` gebunden wird. - - *Warum?*: `controllerAs` stellt eine syntaktische "Versüßung" `$scope` dar. Sie können immer noch Bindungen an dem View vornehmen und auf die `$scope`-Methoden zugreifen. - - *Warum?*: Hilft, die verführerische Nutzung von `$scope`-Methoden innerhalb eines Controllers zu unterbinden, wenn es besser wäre, sie zu vermeiden oder in eine Factory auszulagern. Man sollte die Nutzung von `$scope` in einer Factory oder einem Controller nur dann in Erwägung ziehen, wenn es notwendig ist. Wenn zum Beispiel Events mit [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast) oder [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) abonniert oder publiziert werden, sollte man überlegen, diese nicht in eine Factory auszulagern und vom Controller aus auszulösen. - - ```javascript - /* zu vermeiden */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* empfohlen - schauen Sie aber bitte im nächsten Abschnitt */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs mit vm -###### [Style [Y032](#style-y032)] - - - Benutzen Sie eine Variable, um `this` zu übernehmen, wenn Sie die `controllerAs`-Syntax verwenden. Wählen Sie einen konsistenten Variablennamen, wie `vm`, welcher für ViewModel steht. - - *Warum?*: Das `this`-Schlüsselwort ist kontextbezogen und kann diesen Kontext ändern, wenn es innerhalb einer Funktion eines Controllers verwendet wird. Wird der Kontext von `this` übernommen, wird dieses Problem verhindert. - - ```javascript - /* zu vermeiden */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* empfohlen */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Anmerkung: Sie können jegliche [jshint](http://www.jshint.com/)-Warnungen unterbinden, indem Sie den Kommentar vor der Codezeile einfügen. Allerdingst ist dies nicht notwendig, wenn die Funktion großgeschrieben ist (UpperCasing). Diese Konvention besagt, dass es sich um eine Konstruktor-Funktion handelt, was einem Controller in Angular entspricht. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Anmerkung: Wenn Sie Watches in einem Controller einsetzen, der über `controller as` genutzt wird, können Sie die `vm.*`-Member über die folgende Syntax überwachen. (Erstellen Sie Watches mit Vorsicht, denn sie belasten den "digest cycle".) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Zu bindende Bestandteile nach oben -###### [Style [Y033](#style-y033)] - - - Platzieren Sie zu bindende Elemente alphabetisch sortiert am Anfang des Controllers und nicht verteilt im Code des Controllers. - - *Warum?*: Die Platzierung von zu bindenden Elementen am Anfang verbessert die Lesbarkeit und hilft Ihnen, die zur Bindung und Nutzung in einem View vorgesehenen Elemente des Controllers schnell zu identifizieren. - - *Warum?*: Anonyme Funktionen einzusetzen kann einfach sein, aber wenn diese Funktionen die Länge von einer Zeile überschreiten, wird die Lesbarkeit des Codes verschlechtert. Die Definition der Funktionen unterhalb der Deklaration der zur Bindung vorgesehenen Elemente verschiebt die Details der Implementierung nach unten, hält die zu bindenden Elemente ganz oben und macht es lesbarer (die Funktionen werden quasi "hochgezogen"). - - ```javascript - /* zu vermeiden */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* empfohlen */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Anmerkung: Falls eine Funktion aus nur einer Zeile bestehen sollte, können Sie sich überlegen, diese nach oben zu verlagern, so lange die Lesbarkeit nicht betroffen ist. - - ```javascript - /* zu vermeiden */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* empfohlen */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Funktionsdeklarationen, um Details der Implementierung zu verbergen -###### [Style [Y034](#style-y034)] - - - Nutzen Sie Funktionsdeklarationen, um Implementierungsdetails zu verbergen. Halten Sie Ihre zur Bindung vorgesehenen Elemente oben. Wenn Sie eine Controller-Funktion zur Bindung vorsehen müssen, dann lassen Sie diese auf die Funktionsdeklaration zeigen, die weiter unten erscheint. Diese wird direkt an den Abschnitt mit den zur Bindung vorgesehenen Element geknüpft. Mehr erfahren Sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Warum?*: Die zur Bindung vorgesehenen Elemente am Anfang zu platzieren, erhöht die Lesbarkeit und hilft Ihnen die Elemente zu identifizieren, die gebunden und in einem View genutzt werden können. (Das Gleiche, wie zuvor.) - - *Warum?*: Das Platzieren der Implementierungsdetails einer Funktion weiter unten in der Datei hält diese Codemenge außer Sicht und Sie sehen die wichtigen Dinge am Anfang. - - *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktionsausdrücken der Fall wäre). - - *Warum?*: Sie müssen sich niemals Sorgen darum machen, wenn Sie in Funktionsdeklarationen `var a` vor `var b` platzieren, weil `a` von `b` abhängig ist. - - *Warum?*: Die Reihenfolge ist nur kritisch in Funktionsausdrücken. - - ```javascript - /** - * zu vermeiden - * Nutzung von Funktionsausdrücken. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Berücksichtigen Sie, dass die wichtigen Teile im vorangegangenen Beispiel verstreut sind. Im folgenden Beispiel befindet sich der wichtige Teil ganz oben: Beispielsweise die Elemente, die gebunden werden können, wie `vm.avengers` und `vm.title`. Die Implementierungsdetails befinden sich unterhalb, was eben einfacher zu lesen ist. - - ```javascript - /* - * empfohlen - * Nutzung von Funktionsdeklarationen - * und bindbaren Elementen weiter oben. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Verlagern Sie Controller-Logik in Services -###### [Style [Y035](#style-y035)] - - - Verlagern Sie die Logik eines Controllers, indem Sie diese in Services oder Factories übertragen. - - *Warum?*: Die Logik kann von mehreren Controllern wiederverwendet werden, wenn sie in einem Service oder einer Factory untergebracht ist und über eine Funktion bereitgestellt wird. - - *Warum?*: Logik in einem Service kann in einem Unit-Test einfacher isoliert werden und die Aufruflogik des Controllers wird einfach simuliert. - - *Warum?*: Beseitigt Abhängigkeiten und versteckt die Implementierungsdetails vor dem Controller. - - *Warum?*: Hält den Controller schlank und richtet ihn auf seine Aufgabe aus. - - ```javascript - - /* zu vermeiden */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* empfohlen */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -### Halten Sie die Controller auf ihre Aufgabe ausgerichtet -###### [Style [Y037](#style-y037)] - - - Definieren Sie einen Controller für einen View und versuchen Sie nicht, diesen Controller für weitere Views zu verwenden. Verlagern Sie stattdessen wiederzuverwendende Logik in Factories und halten Sie den Controller einfach und ausgerichtet auf seinen View. - - *Warum?*: Controller in mehreren Views wiederzuverwenden ist kritisch und bedingt eine gute End-Zu-End (e2e) Testabdeckung, um die Stabilität in großen Anwendungen zu garantieren. - - -### Controller zuweisen -###### [Style [Y038](#style-y038)] - - - Wenn ein Controller mit einem View verbunden werden muss und eine der beiden Komponenten aber von anderen Controllern oder Views wiederverwendet werden muss, dann sollten die Controller bei ihren Routen definiert werden. - - Anmerkung: Sollte eine View in einem anderen Kontext als einer Route geladen werden, dann benutzen Sie die `ng-controller="Avengers as vm"`-Syntax. - - *Warum?*: Wird der Controller innerhalb einer Route verbunden, dann ist es möglich, dass unterschiedliche Routen auch unterschiedliche Controller-View-Bindungen verwenden können. Sind Controller in einer View mit [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) angebunden, dann ist diese View immer mit dem gleichen Controller verbunden. - - ```javascript - /* zu vermeiden - bei Nutzung mit einer Route, wenn eine dynamische Verbindung gewünscht ist */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* empfohlen */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - Services werden mit dem `new`-Schlüsselwort instanziiert, und benutzen `this` für öffentliche Methoden und Variablen. Auch wenn sie den Factories so ähnlich sind, setzen Sie stattdessen aus Konsistenzgründen eine Factory ein. - - Anmerkung: [Alle Angular-Services sind Singletons](https://docs.angularjs.org/guide/services). Das bedeutet, dass es nur eine Instanz eines Services pro Injector gibt. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Factories - -### Single Responsibility -###### [Style [Y050](#style-y050)] - - - Factories sollten [eine einzige Verantwortung](http://en.wikipedia.org/wiki/Single_responsibility_principle) haben, die in ihrem Kontext gekapselt ist. Wenn eine Factory einmal über diesen einzigen Zweck hinaus erweitert werden muss, dann sollte eine neue Factory erstellt werden. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Factories sind Singletons und liefern ein Objekt zurück, das die Bestandteile (Elemente) des Service beinhaltet. - - Anmerkung: [Alle Angular-Services sind Singletons](https://docs.angularjs.org/guide/services). - -### Zugreifbare Bestandteile an den Anfang -###### [Style [Y052](#style-y052)] - - - Halten Sie die zugreifbaren Bestandteile eines Service (sein Interface) oben, indem Sie eine Technik anwenden, die aus [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) entlehnt ist. - - *Warum?*: Die zugreifbaren Bestandteile oben zu platzieren, erhöht die Lesbarkeit und hilft Ihnen, schnell zu identifizieren, welche Elemente des Service aufgerufen werden können und getestet (oder simuliert) werden müssen. - - *Warum?*: Dies ist besonders hilfreich, wenn die Datei länger wird, weil ein Scrollen unnötig wird, um zu sehen, was verfügbar ist. - - *Warum?*: Einfach nur Funktionen einzusetzen kann leicht sein. Wenn diese aber den Umfang einer Zeile überschreiben, kann dies die Lesbarkeit verringern und es muss mehr gescrollt werden. Ein aufrufbares Interface im zurückgelieferten Service zu definieren, verlagert die Implementierungsdetails nach unten, hält das aufrufbare Interface ganz oben und macht es lesbarer. - - ```javascript - /* zu vermeiden */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* empfohlen */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - So spiegeln sich die Bindungen im gesamten (beinhaltenden) Objekt wieder. Werte können nicht selbständig durch die Offenlegung des Modulmusters geändert werden. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Funktionsdeklarationen, um die Details der Implementierung zu verbergen -###### [Style [Y053](#style-y053)] - - - Benutzen Sie Funktionsdeklarationen, um die Details der Implementierung zu verbergen. Halten Sie Ihre zugreifbaren Bestandteile der Factory ganz oben. Lassen Sie diese auf Funktionsdeklarationen verweisen, die weiter unten in der Datei aufgeführt werden. Mehr erfahren Sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Warum?*: Zugreifbare Elemente am Anfang zu platzieren, erhöht die Lesbarkeit und hilft Ihnen, zu identifizieren, auf welche Funktionen der Factory von außen zugegriffen werden kann. - - *Warum?*: Das Platzieren der Implementierungsdetails einer Funktion weiter unten in der Datei, hält diese Codemenge außer Sicht und Sie sehen die wichtigen Dinge am Anfang. - - *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktionsausdrücken der Fall wäre). - - *Warum?*: Sie müssen sich niemals Sorgen darum machen, wenn Sie in Funktionsdeklarationen `var a` vor `var b` platzieren, weil `a` von `b` abhängig ist. - - *Warum?*: Die Reihenfolge ist kritisch in Funktionsausdrücken. - - ```javascript - /** - * zu vermeiden - * Nutzung von Funktionsausdrücken - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * empfohlen - * Nutzung von Funktionsdeklarationen und den zugreifbaren Elementen ganz oben - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Datenservices - -### Separate Datenzugriffe -###### [Style [Y060](#style-y060)] - - - Verlagern Sie die Datenzugriffslogik und die Operationen mit Daten in eine Factory Machen Sie die Datenservices verantwortlich für die XHR-Aufrufe, die lokale Speicherung, die Ablage im Speicher oder jede andere Datenoperation. - - *Warum?*: Die Verantwortung des Controllers liegt in der Zusammenstellung und Präsentation der Informationen für die und in der View. Er sollte sich nicht darum kümmern müssen, wie er die Daten bekommt, sondern wen er dazu ansprechen muss. Die Datenservices zu trennen verlagert die Logik der Datenermittlung in den Datenservice und belässt den Controller in seiner Einfachheit und seinem Fokus auf den View. - - *Warum?*: Das macht das Testen der Datenabrufe (simuliert oder real) einfacher, wenn man einen Controller testet, der einen Datenservice nutzt. - - *Warum?*: Datenservice-Implementierungen enthalten spezifischen Code, um die Daten zu handhaben. Dies können bestimmte Header sein, die beschreiben, wie mit den Datenquellen oder anderen Services wie `$http` kommuniziert werden muss. Die Separierung dieser Logik in einem Datenservice kapselt sie an einem einzigen Platz und verbirgt die Implementierung vor den Konsumenten dieses Service (z. B. einem Controller). Das macht es auch einfacher, die Implementierung auszutauschen. - - ```javascript - /* empfohlen */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Anmerkung: Der Datenservice wird von seinen Verbrauchern, wie einem Controller, angesprochen. Wie unten gezeigt, wird seine Implementierung vor den Verbrauchern verborgen. - - ```javascript - /* empfohlen */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Rücklieferung eines Versprechens ("Promise") von einem Datenabruf -###### [Style [Y061](#style-y061)] - - - Wenn Sie einen Datenservice ansprechen, der einen Promise wie `$http` zurückliefert, so liefern Sie in Ihrer aufrufenden Funktion ebenso einen Promise zurück. - - *Warum?*: Sie können die Promises aneinanderhängen und weitere Aktionen ausführen, wenn der Datenabruf beendet ist und den Promise im Erfolgsfall entweder auflöst oder bei Fehlschlagen zurückweist. - - ```javascript - /* empfohlen */ - - activate(); - - function activate() { - /** - * Schritt 1 - * Bei der getAvengers Funktion nach den Avenger-Daten - * fragen und auf den Promise warten - */ - return getAvengers().then(function() { - /** - * Schritt 4 - * Aktion ausführen, um den finalen Promise aufzulösen - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Schritt 2 - * Beim Datenservice nach den Daten fragen und - * auf den Promise warten - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Schritt 3 - * ermittelte Daten einsetzen und den Promise auflösen - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Direktiven -### Begrenzung auf Eine pro Datei -###### [Style [Y070](#style-y070)] - - - Erstellen Sie eine Direktive pro Datei. Benennen Sie die Datei nach der Direktive. - - *Warum?*: Es ist einfach, alle Direktiven in einer Datei zu halten, aber schwer, sie dann wieder herauszulösen sodass sie zwischen Anwendungen oder Modulen ausgetauscht werden können oder einfach nur in einem Modulzu genutzt werden. - - *Warum?*: Eine Direktive pro Datei ist einfach zu warten. - - > Anmerkung: "**Best Practice**: Direktiven sollten aufräumen. Sie können `element.on('$destroy', ...)` oder `scope.$on('$destroy', ...)` nutzen, um eine Bereinigungsfunktion auszuführen, wenn die Direktive entfernt wird." ... aus der Angular-Dokumentation. - - ```javascript - /* zu vermeiden */ - /* directives.js */ - - angular - .module('app.widgets') - - /* Bestell-Direktive, die für das Bestell-Modul spezifisch ist */ - .directive('orderCalendarRange', orderCalendarRange) - - /* Verkaufs-Direktive, die überall in der Verkaufsanwendung genutzt werden kann */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* "Spinner"-Direktive, die in verschiedenen Anwendungen verwendet werden kann */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* Implementierungsdetails */ - } - - function salesCustomerInfo() { - /* Implementierungsdetails */ - } - - function sharedSpinner() { - /* Implementierungsdetails */ - } - ``` - - ```javascript - /* empfohlen */ - /* calendarRange.directive.js */ - - /** - * @desc Bestell-Direktive, die speziell für das Bestell-Modul der Firma Acme bestimmt ist. - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* Implementierungsdetails */ - } - ``` - - ```javascript - /* empfohlen */ - /* customerInfo.directive.js */ - - /** - * @desc Verkaufs-Direktive, die überall innerhalb der Verkaufsanwendung der Firma Acme genutzt werden kann - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* Implementierungsdetails */ - } - ``` - - ```javascript - /* empfohlen */ - /* spinner.directive.js */ - - /** - * @desc "Spinner"-Direktive, die in jeder Anwendung der Firma Acme genutzt werden kann. - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* Implementierungsdetails */ - } - ``` - - Anmerkung: Es gibt viele Benennungsmöglichkeiten für Direktiven, weil sie in einem schmalen oder weiten Gültigkeitsbereich genutzt werden können. Wählen Sie eine, die den Namen der Direktive und ihren Dateinamen eindeutig und klar verständlich macht. Einige Beispiele befinden sich weiter unten, aber schauen Sie sich den Abschnitt zur [Namensgebung](#naming) an, um weitere Empfehlungen zu sehen. - -### DOM-Maniuplation in einer Directive -###### [Style [Y072](#style-y072)] - - - Benutzen Sie zur direkten Manipulation des DOM eine Direktive. Wenn es alternative Wege gibt, wie zum Beispiel CSS, um Stile zu setzen oder [Animation Services](https://docs.angularjs.org/api/ngAnimate), Angular Templates, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oder [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), dann nutzen Sie diese anstatt. Wenn die Direktive zum Beispiel einfach nur etwas versteckt oder zeigt, dann benutzen Sie ngHide/ngShow. - - *Warum?*: DOM-Manipulationen können schwer zu testen oder zu debuggen sein und es gibt oftmals bessere Wege (z. B. CSS, Animationen oder Templates) - -### Vergeben Sie einen eindeutigen Präfix für eine Direktive -###### [Style [Y073](#style-y073)] - - - Vergeben Sie einen kurzen, eindeutigen und beschreibenden Präfix für die Direktive, wie `acmeSalesCustomerInfo`. Dieser würde in HTML als `acme-sales-customer-info` genutzt. - - *Warum?*: Der eindeutige kurze Präfix gibt den Kontext und den Ursprung der Direktive wieder. Ein Prefix wie `cc-` könnte ausweisen, dass die Direktive Teil einer "CodeCamper"-Anwendung ist, wohingegegen `acme-` auf eine Direktive der Firma Acme hinweisen könnte. - - Anmerkung: Vermeiden Sie `ng-`, weil dieser Präfix für Angular-Direktiven reserviert ist. Recherchieren Sie viel genutzte Direktiven, um einem Namenskonflikt wie zum Beispiel mit `ion-` für das [Ionic Framework](http://ionicframework.com/) vorzubeugen. - -### Beschränken Sie Direktiven auf Elemente und Attribute -###### [Style [Y074](#style-y074)] - - - Beim Erstellen einer Direktive, die sinnvollerweise als alleinstehendes Element entwickelt werden kann, sollten Sie diese auf `E` (Custom Element) und optional auf `A` (Custom Attribute) beschränken. Wenn die Direktive ihr eigenes Control sein könnte, ist `E` generell angebracht. Die allgemeine Anweisung lautet, `EA` zu erlauben. Aber man sollte so entwickeln, als würde man ein alleinstehendes Element entwickeln, ein Attribut nur dann, wenn es ein existierendes DOM-Element erweitert. - - *Warum?*: Es macht Sinn. - - *Warum?*: Auch wenn wir es zulassen können, eine Direktive als Klasse zu verwenden: Wenn die Direktive tatsächlich wie ein Element agiert, macht es mehr Sinn, diese auch als Element zu entwickeln - oder gegebenenfalls als Attribut. - - Anmerkung: EA ist der Standard für Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* zu vermeiden */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* empfohlen */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Direktiven und ControllerAs -###### [Style [Y075](#style-y075)] - - - Benutzen Sie die `controller as`-Syntax bei einer Direktive, um `controller as` stimmig mit einer View-Controller-Zuweisung einsetzen zu können. - - *Warum?*: Es macht Sinn und ist nicht schwer. - - Anmerkung: Die folgende Direktive demonstriert einige Arten, in denen man "scope" innerhalb von "link"-Direktiven-Controllern einsetzt, die "controllerAs" nutzen. Ich habe das Template eingebunden, nur um alles an einem Platz zu haben. - - Anmerkung: Bezüglich Dependency Injection, schauen Sie sich bitte [Manually Identify Dependencies](#manual-annotating-for-dependency-injection) an. - - Anmerkung: Berücksichtigen Sie, dass sich der Controller einer Direktive außerhalb der "Closure" der Direktive befindet. Diese Richtlinie verhindert Probleme, bei denen die eingefügten Abhängigkeiten als nicht erreichbarer Code nach einem `return` generiert werden. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // $scope wird nur zum Vergleich eingefügt (injiziert) - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Bemerkung Note: Sie können auch den Controller benennen, wenn Sie ihn in die link-Funktion einfügen und Attribute der Direktive als Properties des Controllers nutzen. - - ```javascript - // Alternative zum obigen Beispiel - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - Benutzen Sie `bindToController = true` wenn Sie die `controller as`-Syntax mit einer Direktive nutzen wollen und Sie das äußere Scope an das Scope des Controllers der Direktive binden wollen. - - *Warum?*: Es erleichtert die Bindung des äußeren Scope an das Scope des Controllers der Direktive. - - Anmerkung: `bindToController` wurde mit Angular 1.3.0 eingeführt. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Promises für einen Controller auflösen -### Promises beim Aktivieren eines Controllers -###### [Style [Y080](#style-y080)] - - - Verlagern Sie die Start-Logik eines Controllers in eine `activate`-Funktion. - - *Warum?*: Ist die Start-Logik an einem einheitlichen Platz innerhalb des Controllers, wird ihr Auffinden vereinfacht, sie ist besser zu testen und diese Methode hilft dabei, zu verhindern, dass die Startlogik überall im Controller verteilt ist. - - *Warum?*: Das `activate` ist eine komfortable Art und Weise, diese Logik für einen Refresh des Controllers / der View zu nutzen. Es hält die Logik zusammen, liefert den View schneller an den Benutzer, erleichtert Animationen mit `ng-view` oder `ui-view`, und macht auch einen flotteren Eindruck beim Benutzer. - - Anmerkung: Wenn Sie die Routennavigation bedingt abbrechen müssen, bevor der Controller gestartet wird, dann sollten Sie stattdessen ein [route resolve](#style-y081) nutzen. - - ```javascript - /* zu vermeiden */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* empfohlen */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Promises für Routen auflösen -###### [Style [Y081](#style-y081)] - - - Ist ein Controller abhängig von der Auflösung eines Promise, der vor der Aktivierung des Controllers aufgelöst sein muss, dann muss diese Abhängigkeit im `$routeProvider` aufgelöst werden, und zwar bevor die Controller-Logik ausgeführt wird. Wenn Sie eine Routen-Navigation bedingt abbrechen müssen, bevor der Controller aktiviert ist, nutzen Sie einen Route-Resolver. - - - Nutzen Sie ein "route resolve" wenn Sie bestimmen wollen, ob eine Routennavigation abgebrochen werden soll, bevor der View eingeblendet wird. - - *Warum?*: Es kann sein, dass ein Controller Daten benötigt, noch bevor er geladen wird. Diese Daten können von einem Promise aus einer Factory oder über [$http](https://docs.angularjs.org/api/ng/service/$http) kommen. Ein ["route resolve"](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) zu benutzen, ermöglicht, dass der Promise aufgelöst wird, bevor die Controller-Logik ausgeführt wird. Also kann es notwendig werden, eine Aktion aufgrund der Daten aus dem Promis auszuführen. - - *Warum?*: Der Code wird nach den Routennavigation innerhalb der activate-Funktion des Controllers ausgeführt. Der View wird ebenso geladen. Die Datenbindung steht, wenn der aktive Promise aufgelöst ist. Eine "Busy-Animation" kann während der Einblendung des views (via `ng-view` oder `ui-view`) angezeigt werden. - - Anmerkung: Der Code wird vor der Routennavigation über einen Promise ausgeführt. Wird der Promise zurückgewiesen, wird die Navigation abgebrochen. Resolve bewirkt, dass die neue View auf die Auflösung der Route wartet. Ein "Busy-Indikator" kann vor dem Auflösen und während der Einblendung des Views angezeigt werden. Wenn Sie den View schneller einblenden wollen und keinen Kontrollpunkt benötigen, an dem geprüft wird, ob der View überhaupt zur Verfügung steht, sollten Sie die [Controller `activate` Technik](#style-y080) in Betracht ziehen. - - ```javascript - /* zu vermeiden */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* besser */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Anmerkung: Das untenstehende Beispiel zeigt die Stellen, an denen die Route mit einer benannten Funktion aufgelöst wird. Das ist einfacher zu debuggen und vereinfacht auch die Handhabung von Dependency Injection. - - ```javascript - /* noch besser */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Anmerkung: Die Abhängigkeit von `movieService` im Code ist bei einer Minifizierung nicht sicher. Um Details zu sehen, wie dieser Code für eine sichere Minifizierung vorbereitet wird, schauen Sie sich den Abschnitt über [Dependency Injection](#manual-annotating-for-dependency-injection) und über [Minifizierung und Code-Anmerkungen](#minification-and-annotation) an. - -**[Zurück zum Anfang](#table-of-contents)** - -## Manuelle Code-Anmerkungen für das Einfügen von Abhängigkeiten (Dependency Injection) - -### Unsichere Minifizierung -###### [Style [Y090](#style-y090)] - - - Vermeiden Sie es, die kurze Deklarationssyntax für Abhängigkeiten ohne einen für die Minifizierung sicheren Ansatz zu verwenden. - - *Warum?*: Die Parameter der Komponente (z. B. Controller, Factory, etc.) werden in abgekürzte Variablen gewandelt. So kann zum Beispiel aus `common` und `dataservice` ein `a` oder `b` werden, was von Angular nicht gefunden wird. - - ```javascript - /* zu vermeiden - nicht sicher bei Minifizierung */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Dieser Code wird abgekürzte Variablen ergeben, wenn er minifiziert wird und deshalb Laufzeitfehler verursachen. - - ```javascript - /* zu vermeiden - nicht sicher bei Minifizierung */ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Abhängigkeiten manuell identifizieren -###### [Style [Y091](#style-y091)] - - - Benutzen Sie `$inject` um Ihre Abhängigkeiten für Angular-Komponenten manuell zu identifizieren. - - *Warum?*: Dieses Verfahren spiegelt die Technik wieder, die von [`ng-annotate`](https://github.com/olov/ng-annotate) genutzt wird, welche ich für die Automatisierung der Erstellung von minifikationssicheren Abhängigkeiten empfehlen. Wenn `ng-annotate` erkennt, dass eine solche Deklaration vorgenommen wurde, wird diese nicht dupliziert. - - *Warum?*: Dies bewahrt Ihre Abhängigkeiten vor Problemen bei einer Minifizierung, bei der die Parameter abgekürzt werden. Zum Beispiel wird aus `common` und `dataservice` ein `a` oder `b`, was von Angular nicht gefunden wird. - - *Warum?*: Vermeiden Sie Abhängigkeiten direkt im Code in Form von langen Listen, da diese in Form eines Arrays schwer zu lesen sind. Dies kann auch zu Verwirrungen führen, weil dieses Array eine Liste von Strings ist, das letzte Element aber die Funktion der Komponente. - - ```javascript - /* zu vermeiden */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* zu vermeiden */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* empfohlen */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Anmerkung: Wenn sich Ihre Funktion unterhalb eines returns befindet, kann `$inject` unerreichbar werden (das kann in einer Direktive passieren). Sie können dies vermeiden, indem Sie den Controller aus der Direktive herauslösen. - - ```javascript - /* zu vermeiden */ - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* empfohlen */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Route Resolver Abhängigkeiten manuell identifizieren -###### [Style [Y092](#style-y092)] - - - Benutzen Sie `$inject` um die Abhängigkeiten in Ihrem Route Resolver für Angular-Komponenten zu identifizieren. - - *Warum?*: Diese Technik löst eine anonyme Funktion aus dem Route Resolver heraus und macht sie lesbarer. - - *Warum?*: Eine `$inject`-Anweisung kann einfach einem Resolver vorangestellt werden, um Abhängigkeiten bei Minifizierung sicher zu machen. - - ```javascript - /* empfohlen */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Minifizierung und Code-Anmerkungen - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Benutzen Sie [ng-annotate](//github.com/olov/ng-annotate) für [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com) und versehen Sie die Funktionen mit den notwendigen `/** @ngInject */`-Kommentaren, die für die "automatische" Dependency Injection genutzt werden sollen. - - *Warum?*: Dies schützt Ihren Code vor Abhängigkeiten, die keiner minifizierungssicheren Technik entsprechen. - - *Warum?*: [`ng-min`](https://github.com/btford/ngmin) wird nicht mehr unterstützt. - - >Ich bevorzuge Gulp, weil es gefühlt einfacher zu schreiben, zu lesen und zu debuggen ist. - - Der folgende Code nutzt keine minifizierungssicheren Abhängigkeiten. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Wird der obenstehende Code durch ng-annotate geschickt, wird folgende Ausgabe mit der `$inject`-Anmerkung erstellt und der Code wird minifizerungssicher. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Anmerkung: Entdeckt `ng-annotate` bereits vorhandene Kommentare (z. B. bei erkanntem `@ngInject`), werden die `$inject`-Befehle nicht dupliziert. - - Anmerkung: Wenn Sie einen Route Resolver nutzen, können Sie die Funktion des Resolvers mit `/* @ngInject */` markieren, und es wird eine korrekte Code-Anmerkung erstellt, die alle eingefügten Abhängigkeiten minifizierungssicher hält. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Anmerkung: Ab der 1.3er Version von Angular können Sie den `ngStrictDi` Parameter der [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)-Direktive nutzen, um potentiell minfizierungsunsichere Abhängigkeiten aufzuspüren. Wurde eine solche Abhängigkeit entdeckt, dann wird der Injector im "strict-di"-Modus erstellt und verursacht Fehler beim Ausführen von Funktionen, die keine explizite Code-Anmerkung besitzen (was nicht minifizerungssicher ist). Debug-Informationen werden in der Konsole ausgegeben, um den betreffenden Code nachvollziehen zu können. Ich bevorzuge die Nutzung von `ng-strict-di` für das Debugging. - `` - -### Gulp oder Grunt für ng-annotate nutzen -###### [Style [Y101](#style-y101)] - - - Benutzen Sie [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) oder [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in einer automatisierten Build-Task. Fügen Sie `/* @ngInject */` vor jeder Funktion ein, die Abhängigkeiten hat. - - *Warum?*: ng-annotate erkennt die meisten Abhängigkeiten automatisch, benötigt manchmal aber Hinweise durch die `/* @ngInject */`-Syntax. - - Der folgende Code ist ein Beispiel für die Nutzung von ngAnnotage in einer Gulp-Task. - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Code-Anmerkung vor dem "Uglifying" einfügen, damit der Code korrekt minifiziert wird. - .pipe(ngAnnotate({ - // "true" hilft dort eine Anmerkung einzufügen, wo @ngInject nicht genutzt wird. Die Notwendigkeit wird aus dem Code-Kontext geschlossen. - // Funktioniert nicht bei "resolve", muss für diesen Fall also explizit gesetzt werden. - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Fehlerbehandlung - -### Decorator -###### [Style [Y110](#style-y110)] - - - Benutzen Sie einen [Decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator) während der Konfiguration, indem Sie den [`$provide`](https://docs.angularjs.org/api/auto/service/$provide)-Service im [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler)-Service ansprechen, um eigene Aktionen bei einem auftauchenden Fehler (einer Ausnahme) auszuführen. - - *Warum?*: Dies bietet einen stimmigen Weg, unbehandelte Angular-Fehler während der Entwicklung oder zur Laufzeit abzufangen. - - Anmerkung: Eine weitere Option neben der Benutzung eines Decorators, stellt das Überschreiben des Service dar. Diese Möglichkeit ist gut, wenn Sie aber das Standardverhalten beibehalten wollen, dann ist die Erweiterung mit einem Decorator angebracht. - - ```javascript - /* empfohlen */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Der Fehler könnte zu einer Liste im Service oder - * zum $rootScope hinzugefügt werden oder bei einem - * Remote-Webserver oder lokal protokolliert oder - * einfach wieder hart "geworfen" werden. Es obliegt - * ganz Ihnen. - * - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Komponente zum Abfangen von Fehlern (Ausnahmen) -###### [Style [Y111](#style-y111)] - - - Erstellen Sie eine Factory, die ein Inferface bereitstellt, mit dem Sie Fehler elegant abfangen und behandeln. - - *Warum?*: Dies bietet eine konsistente Methode, Fehler abzufangen, die in Ihrem Code geworfen werden (z. B. während eines XHR-Aufrufs oder bei Fehlern in einem Promise). - - Anmerkung: Eine Komponente, die die Fehler abfängt stellt eine gute Möglichkeit dar, Fehler an den Stellen abzufangen, von denen Sie wissen, dass sie auftreten können. Zum Beispiel, wenn Sie Daten über einen XHR-Aufruf von einem Webservice anfragen und Sie jegliche Art von Fehler, die von diesem Service zurückkommen, speziell behandeln wollen. - - ```javascript - /* empfohlen */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Routenfehler -###### [Style [Y112](#style-y112)] - - - Behandeln und protokollieren Sie alle Routingfehler mit [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Warum?*: Bietet einen stimmigen Weg, um alle Routingfehler zu behandeln. - - *Warum?*: Bietet potentiell die Möglichkeit die Akzeptanz beim Benutzer zu steigern, wenn ein Routingfehler auftritt und dieser auf informative Weise mit Möglichkeiten zur Behebung am Bildschirm angezeigt wird. - - ```javascript - /* empfohlen */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Abbruch des Routings: - * Gehe bei einem Routingfehler zurück zum Dashboard. - * Biete eine Möglichkeit wieder auszusteigen, wenn dies - * zweimal versucht wird. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionales protokollieren über einen Logging-Service oder $log. - * (Der Logging-Service muss als Abhängigkeit eingefügt werden.) - */ - logger.warning(msg, [current]); - - /** - * Gehe bei einem Routingfehler zu einer anderen Route oder anderem Status. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Namensgebung - -### Richtlinien der Namensgebung -###### [Style [Y120](#style-y120)] - - - Benutzen Sie stimmige Namen für alle Komponenten, einem Muster folgend, welches die Hauptfunktionen (Features) einer Komponente und dann (optional) ihren Typ beschreibt. - Meine empfohlenes Muster ist `feature.typ.js`. Es gibt zwei zu vergebene Namen für die meisten Komponenten: - * der Dateiname (`avengers.controller.js`) - * der Name der bei Angular zu registrierenden Komponente (`AvengersController`) - - *Warum?*: Richtlinien der Namensgebung bieten einen stimmigen Weg, Inhalte auf einen Blick zu finden. Dabei ist die Stimmigkeit dieser Namensgebung innerhalb des Projekts auschlaggebend. Die Stimmigkeit innerhalb des Teams ist wichtig. Die Stimmigkeit innerhalb einer Firma bedeutet enorme Effizienz. - - *Warum?*: Die Konventionen der Namensgebung sollen helfen, Ihren Code schneller wiederzufinden und einfacher zu verstehen. - -### Dateinamen von Features (Komponenten) -###### [Style [Y121](#style-y121)] - - - Benutzen Sie stimmige Namen für alle Komponenten, die einem Muster folgen: Hauptfunktion einer Komponente, und dann (optional) gefolgt vom Typ. Mein empfohlenes Muster ist `feature.typ.js`. - - *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu identifizieren. - - *Warum?*: Bietet ein Suchmuster für alle automatisierten Aufgaben. - - ```javascript - /** - * allgemeine Optionen - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * empfohlen - */ - - // Controller - avengers.controller.js - avengers.controller.spec.js - - // Services / Factories - logger.service.js - logger.service.spec.js - - // Konstanten - constants.js - - // Moduldefinitionen - avengers.module.js - - // Routen - avengers.routes.js - avengers.routes.spec.js - - // Konfiguration - avengers.config.js - - // Direktiven - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Anmerkung: Eine weitere allgemeine Konvention ist die Benennung einer Controller-Datei ohne das Wort `controller`, wie `avengers.js` anstelle von `avengers.controller.js`. Bei allen anderen Konventionen bleibt aber der Typ-Suffix. Da die Controller das Gros der Komponenten ausmacht, spart dies Tipparbeit und sie sind trotzdem einfach zu identifizieren. Ich empfehle Ihnen, dass Sie eine Konvention für sich auswählen, und diese im Team strikt anwenden. Ich bevorzuge `avengers.controller.js`. - - ```javascript - /** - * empfohlen - */ - // Controller - avengers.js - avengers.spec.js - ``` - -### Name von Dateien für Tests -###### [Style [Y122](#style-y122)] - - - Benennen Sie Testspezifikationen gemäß der Komponente, die getestet werden soll, gefolgt vom Suffix `spec`. - - *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu identifizieren. - - *Warum?*: Bietet ein Suchmuster für [karma](http://karma-runner.github.io/) oder andere Testrunner. - - ```javascript - /** - * empfohlen - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Namen für Controller -###### [Style [Y123](#style-y123)] - - - Nutzen Sie stimmige Namen für alle Controller und benennen Sie diese nach ihrem Hauptmerkmal. Benutzen Sie UpperCamelCase für Controller, weil sie Konstruktoren sind. - - *Warum?*: Bietet einen stimmigen Weg, Controller schnell zu identifizieren und zu referenzieren. - - *Warum?*: UpperCamelCase ist eine Konvention, ein Objekt zu identifizieren, welches über einen Konstruktor instanziiert werden kann. - - ```javascript - /** - * empfohlen - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Namens-Suffix für Controller -###### [Style [Y124](#style-y124)] - - - Hängen Sie an einen Controllernamen den Suffix `Controller`. - - *Warum?*: Der `Controller`-Suffix ist üblich und explizit. - - ```javascript - /** - * empfohlen - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Namen für Factories -###### [Style [Y125](#style-y125)] - - - Nutzen Sie stimmige Namen für alle Factories und vergeben Sie diese nach deren Hauptfunktion. Benutzen Sie Camel-Casing für Services und Factories. Vermeiden Sie es, einer Factory oder einem Service ein `$` voranzustellen. - - *Warum?*: Bietet einen stimmigen Weg, Factories schnell zu identifizieren und zu referenzieren. - - *Warum?*: Verhindert Namenskollisionen mit eingebauten Factories und Serivces, die `$` als Präfix nutzen. - - ```javascript - /** - * empfohlen - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - -### Namen für Direktiven -###### [Style [Y126](#style-y126)] - - - Benutzen Sie stimmige Namen für alle Direktiven gemäß der Camel-Case-Regel. Nutzen Sie einen kurzen Präfix, um den Bereich zu beschreiben, zu dem die Direktive gehört (Firmen- oder Projekt-Präfix). - - *Warum?*: Bietet einen stimmigen Weg, Direktiven schnell zu identifizieren und zu referenzieren. - - ```javascript - /** - * empfohlen - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Module -###### [Style [Y127](#style-y127)] - - - Beim Vorhandensein von mehreren Modulen, wird die Datei des Hauptmoduls `app.module.js` genannt, während Andere oder Module in Abhängigkeit nach dem benannt werden, was sie repräsentieren. Ein Admin-Modul wird zum Beispiel `admin.module.js` genannt. Die zugehörigen Namen für die Registrierung dieser Module wären `app` und `admin`. - - *Warum?*: Bietet Stimmigkeit bei Anwendungen mit mehreren Modulen und beim Erweitern zu einer größeren Anwendungen. - - *Warum?*: Bietet einen einfachen Weg, bei einer Automatisierung von Aufgaben, alle Moduldefinitionen zuerst zu laden, und dann erst die anderen Angular-Dateien (z. B. zum Packen (Bundling)). - -### Konfiguration -###### [Style [Y128](#style-y128)] - - - Trennen Sie die Konfiguration vom Modul und lagern Sie diese in eine eigene Datei aus, die nach dem Modul benannt wird. Eine Konfigurationsdatei für das Hauptmodul `app` wird `app.config.js` genannt (oder einfach `config.js`). Eine Konfigurationsdatei für ein Modul namens `admin.module.js` wird `admin.config.js` genannt. - - *Warum?*: Trennt Konfiguration von der Moduldefinition, den Komponenten und dem "aktiven" Code. - - *Warum?*: Bietet einen leicht zu identifizierenden Platz, um die Konfiguration eines Moduls vorzunehmen. - -### Routen -###### [Style [Y129](#style-y129)] - - - Verlagern Sie die Routenkonfiguration in eine separate Datei. Ein Beispiel könnte `app.route.js` für das Hauptmodul und `admin.route.js` für das `admin`-Modul sein. Auch in kleineren Anwendungen bevorzuge ich diese Trennung vom Rest der Konfiguration. - -**[Zurück zum Anfang](#table-of-contents)** - -## Anwendungsstruktur: Das LIFT-Prinzip -### LIFT -###### [Style [Y140](#style-y140)] - - - LIFT steht für `L`ocate (auffinden), `I`dentify (identifizieren), `F`lat (flach), T`ry to stay DRY` (versuchen Sie, Ihren Code nicht zu wiederholen). Das bedeutet also, Sie sollten Ihre Anwendung so strukturieren, dass Sie Ihren Code schnell auffinden und auf einen Blick identifizieren können, für was der Code gut ist. Dabei sollten Sie die Struktur so flach wie möglich halten. Vermeiden Sie es unbedingt, Ihren Code zu wiederholen. - - *Warum LIFT?*: Bietet eine konsistente und gut skalierbare Struktur, ist modular und macht es einfacher die Effizienz eines Entwicklers zu steigern, weil er seinen Code schneller finden kann. Prüfen Sie Ihre Anwendungsstruktur, indem Sie sich fragen: Wie schnell kann ich all die Dateien, die zu einem Feature gehören öffnen und mit ihnen arbeiten?" - - Falls ich mich mit meiner nicht mehr wohl fühle, dann schaue ich mir die LIFT-Anweisungen an: - - 1. `L`ocating - Finde Deinen Code auf einfache und schnelle Art - 2. `I`dentify - Identifiziere Deinen Code auf einen Blick - 3. `F`lat - Halte die Struktur so flach wie möglich, solange es geht - 4. `T`ry to stay DRY (Don’t Repeat Yourself) - Versuche Deinen Code nicht zu wiederholen - -### Locate (Code auffinden) -###### [Style [Y141](#style-y141)] - - - Gestalten Sie das Auffinden Ihres Codes intuitiv, einfach und schnell. - - *Warum?*: Ich finde diesen Punkt sehr wichtig für ein Projekt. Wenn ein Team die Dateien, an denen es dringend arbeiten muss, nicht ebenso schnell findet, kann es nicht effizient genug arbeiten. Die Struktur muss geändert werden. Zusammengehörende Dateien an der intuitivsten Stelle nahe beieinander zu platzieren, spart Ihnen einen Haufen Zeit. Eine beschreibende Struktur kann dabei helfen. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify (Code identifizieren) -###### [Style [Y142](#style-y142)] - - - Wenn Sie einen Dateinamen sehen, sollten Sie sofort wissen, was die Datei beinhaltet und für was sie steht. - - *Warum?*: Sie brauchen weniger Zeit, um nach Ihrem Code zu suchen und werden so effizienter. Wenn das bedeutet, dass Sie längere Dateinamen brauchen, dann sei es so. Seien Sie beschreibend bei der Namensvergabe und sorgen Sie dafür, dass eine Datei nur eine Komponente enthält. Vermeiden Sie Dateien mir mehreren Controllern, Services oder gar mit beidem. Ich weiche von dieser Regel ab, wenn ich sehr kleine Features habe, die alle miteinander verbunden und leicht identifizierbar sind. - -### Flat (flache Struktur) -###### [Style [Y143](#style-y143)] - - - Halten Sie die Verzeichnisstruktur so lange es geht so flach wie möglich. Sollten mehr als sieben Dateien in einem Verzeichnis stehen, denken Sie über eine Neuaufteilung nach. - - *Warum?*: Niemand will Dateien in einer Verzeichnisstruktur über sieben Ebenen suchen. Denken Sie an Menüs von Webseiten ... Alles, was über mehr als zwei Ebenen geht, sollte ernsthaft überdacht werden. Für eine Verzeichnisstruktur gibt es keine feste Regelung, aber sollte ein Verzeichnis sieben bis zehn Dateien enthalten, dann ist es vielleicht an der Zeit, Unterverzeichnisse einzurichten. Machen Sie es für sich selbst an Ihrem Wohlbefinden mit der Struktur fest. Benutzen Sie eine flachere Struktur, bis Sie den Punkt erreichen, an dem es Sinn macht, ein neues Verzeichnis zu erstellen. - -### T-DRY (Versuchen Sie Ihren Code nicht zu wiederholen) -###### [Style [Y144](#style-y144)] - - - Seien Sie "DRY": Versuchen Sie Ihren Code nicht zu wiederholen. Aber übertreiben Sie es nicht, indem Sie die Lesbarkeit Ihres Codes dafür opfern. - - *Warum?*: Sich nicht ständig zu wiederholen ist wichtig, aber nicht entscheidend, wenn Sie dafür andere Punkte von LIFT opfern. Ich möchte für eine View nicht session-view.html tippen, da es ja öffentlich eine View ist. Wenn etwas nicht offensichtlich oder einer Konvention unterliegt, dann benenne ich es. - -**[Zurück zum Anfang](#table-of-contents)** - -## Anwendungsstruktur - -### Allgemeine Richtlinien -###### [Style [Y150](#style-y150)] - - - Sie sollten eine kurzfristige und langfristige Sicht auf Ihre Implementierung haben. Das bedeutet: Fangen Sie klein an, behalten Sie dabei aber im Auge, wohin Sie mir Ihrer Anwendung wollen. Jeder Code der Anwendung wird in einem Stammverzeichnis namens `app` abgelegt. Für den Inhalt gilt: Ein Feature pro Datei. Jeder Controller, Service, jedes Modul, jede View befindet sich in ihrer/seiner eigenen Datei. Alle externen Scripts (3rd Party Bibliotheken) werden in einem anderen Stammverzeichnis, nicht aber im `app`-Verzeichnis abgelegt. Ich habe sie nicht geschrieben und ich möchte nicht, dass sie meine Anwendung durcheinander bringen.(`bower_components`, `scripts`, `lib`). - - Anmerkung: Sie finden mehr Details und Gründe für diese Struktur in [diesem Originalbeitrag zur Anwendungsstruktur](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Platzieren Sie Komponenten, die das allgemeingültige Layout der Anwendung ausmachen, in einem Verzeichnis namens `layout`. Dieses sollte eine Shell-View mit Controller enthalten. Der View agiert als Container für die Anwendung und enthält die Anwendung an sich: Navigation, Menüs, Bereiche für die Inhalte und andere Bereiche. - - *Warum?*: Organisieren Sie das Layout an einem einzigen Ort, damit es innerhalb der Anwendung von überall her genutzt werden kann. - -### Verzeichnisse nach Hauptmerkmalen -###### [Style [Y152](#style-y152)] - - - Erstellen Sie Verzeichnisse gemäß der Hauptmerkmale, die sie darstellen. Wenn der Inhalt eines Verzeichnisses wächst und mehr als sieben Dateien fasst, sollten Sie darüber nachdenken, ein neues Verzeichnis zu erstellen. Dabei ist der Grenzwert aber individuell. - - *Warum?*: En Entwickler kann den gesuchten Code schnell auffinden, auf einen Blick identifizieren für was jede Datei steht, die Struktur ist so flach wie möglich und es gibt keine redundanten Namen. - - *Warum?*: Die LIFT-Richtlinien sind alle erfüllt. - - *Warum?*: Hilft zu verhindern, dass die Anwendung durcheinander gerät, weil der Inhalt nach den LIFT-Richtlinien organisiert ist. - - *Warum?*: Gibt es viele Dateien (mehr als zehn), ist es einfach, sie in einer konsistenten Verzeichnisstruktur zu finden, als in einer flachen Struktur. - - ```javascript - /** - * empfohlen - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Anmerkung: Strukturieren Sie Ihre Verzeichnisse nicht nach Typ. Das hat zur Folge, dass Sie sich in vielen Verzeichnissen bewegen müssen, um ein einziges Feature bearbeiten zu wollen. Vergrößert sich die Anwendung auf fünf, zehn oder gar mehr als 25 Views und Controller (und andere Features), wird es sehr schnell unhandlich, im Gegensatz zur Organisation der Verzeichnisse nach Features. - - ```javascript - /* - * zu vermeiden - * Verzeichnisse-Nach-Typ-Alternativefolders-by-type. - * Ich empfehle stattdessen "Verzeichnisse-Nach-Feature". - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Modularität - -### Viele kleine, eigenständige Module -###### [Style [Y160](#style-y160)] - - - Erstellen Sie kleine Module, die für eine einzige Aufgabe zuständig sind und diese in sich kapseln. - - *Warum?*: Modulare Anwendungen machen es möglich, dass Funktionsmerkmale (Features) einfach eingeklinkt werden können. Somit kann ein Entwicklungsteam vertikale Stücke einer Applikation sukzessive ausrollen. Das bedeutet, dass neue Funktionsmerkmale nach ihrer Entwicklung einfach eingeklinkt werden können. - -### Erstellen Sie ein Modul für die Hauptanwendung (App-Modul) -###### [Style [Y161](#style-y161)] - - - Erstellen Sie ein Hauptmodul für die Anwendung, dessen Rolle es ist, alle Module und Funktionen der Anwendung zusammenzutragen. Nennen Sie das Modul nach Ihrer Anwendung. - - *Warum?*: Angular begünstigt Modularität und Muster für die Aufteilung von Code. Ein Hauptmodul für eine Anwendung zu erstellen, die andere Module zusammenzieht, ist ein einfacher Weg, um Module in eine Anwendung einzuklinken oder aus ihr auszuklinken. - -### Halten Sie das App-Modul klein -###### [Style [Y162](#style-y162)] - - - Stellen Sie nur Logik ins App-Modul, die dazu dient, die Anwendungsbestandteile zusammenzuziehen. Features bleiben in ihren eigenen Feature-Modulen. - - *Warum?*: Weitere Logik außerhalb der ursprünglichen Aufgabe ins Hauptmodul einzubinden, wie zum Beispiel Datenabfragen, darstellen von Views, oder eine Logik, die nicht zum Zusammenziehen der Module dient, bringt Verwirrung. Es wird schwierig, diese Features zu verwalten und auch zu testen. - - *Warum?*: Das App-Modul wird zum Manifest, welches die Module aufführt, die die Applikation ausmachen. - -### Funktionsbereiche sind Module -###### [Style [Y163](#style-y163)] - - - Erstellen Sie Module, die Funktionsbereiche darstellen, wie zum Beispiel das Layout, wiederverwendbare und allgemein verfügbare Services, Dashboards, und anwendungsspezifische Funktionsmerkmale (z. B. Kunden, Admin, Verkauf). - - *Warum?*: Eigenständige Module können reibungslos zur Anwendung hinzugefügt werden. - - *Warum?*: Sprints oder Iterationen können sich auf Funktionsbereiche beziehen. Diese können am Ende eines Sprints oder einer Iteration eingebunden werden. - - *Warum?*: Die Trennung von Funktionsbereichen in Module erleichtert das isolierte Testen der Module und deren Wiederverwendung. - -### Wiederverwendbare Bausteine sind Module -###### [Style [Y164](#style-y164)] - - - Erstellen sie Module, die wiederverwendbare Applikationsbausteine für allgemeingültige Services, wie Fehlerbehandlung, Logging, Diagnostik, Sicherheit und lokale Datenablage, darstellen. - - *Warum?*: Diese Arten von Funktionen werden in vielen Anwendungen benötigt. Also können Sie durch die Trennung in ihre jeweiligen Module und aufgrund ihrer generischen Natur, von anderen Anwendungen verwendet werden. - -### Modulabhängigkeiten -###### [Style [Y165](#style-y165)] - - - Das Hauptmodul einer Applikation ist abhängig von den applikationsspezifischen Funktionsmodulen und den allgemeingültigen oder wiederverwendbaren Modulen. - - ![Modularität und Abhängigkeiten](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Warum?*: Das Hauptmodul der Applikation enthält ein schnell ersichtliches Manifest der Anwendungsfunktionen. - - *Warum?*: Jeder Funktionsbereich enthält ein Manifest, aus dem ersichtlich wird, von was er abhängig ist. Der Inhalt dieses Manifests kann also als Abhängigkeit in andere Anwendungen eingebunden werden, wo er dann auch funktioniert. - - *Warum?*: Interne Anwendungsfunktionen wie allgemeingültige Datenservices werden einfach aufgefunden und innerhalb von `app.core` (wählen Sie ihren Lieblingsnamen für diese Modul) genutzt. - - Anmerkung: Dies ist eine Strategie, die die Konsistenz innerhalb einer Anwendung begünstigt. Es gibt hierzu viele gute Möglichkeiten. Wählen Sie eine für sich aus, die stimmig ist, den Regeln von Angular in Puncto Abhängigkeiten folgt und einfach zu verwalten und zu skalieren ist. - - > Meine Strukturen unterscheiden sich von Projekt zu Projekt, aber sie folgen allesamt diesen Richtlinien zur Struktur und Modularität. Auch die Implementierung kann sich in Abhängigkeit der benötigten Funktionen und dem Team unterscheiden. Mit anderen Worten: Bestehen Sie nicht auf die exakte Nachbildung der hier vorgestellten Struktur, sondern passen Sie sie Ihren Gegebenheiten an. Behalten Sie dabei Konsistenz, Wartbarkeit und Effizienz im Hinterkopf. - - > In einer kleinen Anwendung können Sie darüber nachdenken, alle allgemeinen Abhängigkeiten im Hauptmodul unterzubringen, in dem die Funktionsmodule dann keine direkten Abhängigkeiten haben. Das erleichtert die Wartung kleinerer Anwendungen, erschwert aber die Wiederverwendbarkeit von Modulen außerhalb der Anwendung. - -**[Zurück zum Anfang](#table-of-contents)** - -## Startlogik - -### Konfiguration -###### [Style [Y170](#style-y170)] - - - Fügen Sie Ihren Code in die [Modulkonfiguration](https://docs.angularjs.org/guide/module#module-loading-dependencies) ein, die vor dem Start der Angular-Anwendung konfiguriert sein muss. Ideale Kandidaten hierfür sind Provider und Konstanten. - - *Warum?*: Es schränkt die Stellen ein, an denen konfiguriert wird. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run-Block -###### [Style [Y171](#style-y171)] - - - Jeder Code, der laufen muss, wenn eine Anwendung startet, sollte in einer Factory implementiert, über eine Funktion im Zugriff und in den sog. [Run-Block](https://docs.angularjs.org/guide/module#module-loading-dependencies) eingebunden sein. - - *Warum?*: Code, der sich direkt in einem Run-Block befindet, ist schwer testbar. Wird er in eine Factory ausgelagert, ist er besser zu abstrahieren und zu simulieren. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document und $window -###### [Style [Y180](#style-y180)] - - - Benutzen Sie [`$document`](https://docs.angularjs.org/api/ng/service/$document) und [`$window`](https://docs.angularjs.org/api/ng/service/$window) anstelle von `document` und `window`. - - *Warum?*: Diese Services werden von Angular umschlossen und sind somit besser zu testen als wenn document und window in Tests benutzt wird. Dies hilft zu vermeiden, document und windows simulieren zu müssen. - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - Benutzen Sie [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) und [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) anstelle von `setTimeout` und `setInterval` . - - *Warum?*: Diese Services werden von Angular umschlossen und sind besser zutesten. Sie nutzen Angulars "Digest-Cycle" und halten somit die Datenbindung synchron. - -**[Zurück zum Anfang](#table-of-contents)** - -## Testen -Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige meiner Empfehlungen zu Unit-Test-Grundlagen als Links zur weiteren Information beigefügt. - -### Schreiben Sie Tests pro Anforderung -###### [Style [Y190](#style-y190)] - - - Schreiben Sie Ihre Tests für jede Anforderung. Beginnen Sie mit einem leeren Test und füllen Sie diesen, während Sie den Code für die Anforderung schreiben. - - *Warum?*: Testbeschreibungen zu verfassen, hilft dabei festzulegen, was in der Anforderung passiert, was nicht passiert und wie ein Testerfolg gemessen werden kann. - - ```javascript - it('Soll einen Avengers-Controller enthalten', function() { - // TODO - }); - - it('Soll einen Avenger enthalten, wenn nach Namen gefiltert wird', function() { - // TODO - }); - - it('Soll 10 Avengers enthalten', function() { - // TODO (mock data?) - }); - - it('Soll Avengers über XHR zurückliefern', function() { - // TODO ($httpBackend?) - }); - - // und so weiter - ``` - -### Testbibliotheken -###### [Style [Y191](#style-y191)] - - - Nutzen Sie [Jasmine](http://jasmine.github.io/) oder [Mocha](http://mochajs.org) für Unit-Tests. - - *Warum?*: Die Nutzung von Jasmin und Mocha ist sehr verbreitet in der Angular-Community. Beide sind stabil, gut gepflegt und liefern robuste Testfunktionen. - - Anmerkung: Wenn Sie Mocha nutzen, sollten Sie in Erwägung ziehen, eine sogenannte Assert-Library, wie [Chai](http://chaijs.com) zu nutzen. Ich ziehe dem Mocha vor. - -### Testrunner -###### [Style [Y192](#style-y192)] - - - Benutzen Sie [Karma](http://karma-runner.github.io) als Testrunner. - - *Warum?*: Karma lässt sich leicht konfigurieren, um einzeln oder automatisch bei Codeänderungen aufgerufen zu werden. - - *Warum?*: Karma hängt sich leicht von allein in einen CI-Prozess (in Grunt oder Gulb) ein. - - *Warum?*: Verschiedene IDEs wie [WebStorm](http://www.jetbrains.com/webstorm/) und [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) haben damit begonnen, Karma einzubinden. - - *Warum?*: Karma arbeitet wunderbar mit Task-Managern für Automatisierte Aufgaben wie [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) und [Gulp](http://www.gulpjs.com) (with [gulp-karma](https://github.com/lazd/gulp-karma)) zusammen. - -### Simulation durch "Stubbing" und "Spying" -###### [Style [Y193](#style-y193)] - - - Benutzen Sie [Sinon](http://sinonjs.org/) um Komponenten im Rahmen von "Stubbing" und "Spying" (Mocking) zu simulieren. - - *Warum?*: Sinon arbeitet wunderbar mit Jasmine und Mocha zusammen und erweitert deren Fähigkeiten der Simulation von Komponenten. - - *Warum?*: Sinon erleichtert den Wechsel zwischen Jasmine und Mocha, wenn Sie beide ausprobieren möchten. - - *Warum?*: Sinon liefert gut verständliche, beschreibende Meldung, für den Fall, dass ein Test fehlschlägt. - -### Browser ohne Userinterface (Headless Browser) -###### [Style [Y194](#style-y194)] - - - Nutzen Sie [PhantomJS](http://phantomjs.org/), um Ihre Browsertests auf einem Server laufen zu lassen. - - *Warum?*: PhantomJS ist ein Browser, der kein Userinterface hat. Dies befähigt Sie, Browsertests ohne "sichtbaren" Browser durchzuführen. Sie müssen weder Chrome, noch Safari oder IE oder andere Browser auf Ihrem Server installieren. - - Anmerkung: Sie sollten trotzdem Tests auf allen Browsern in Ihrer Umgebung für Ihr Zielpublikum durchführen. - -### Codeanalyse -###### [Style [Y195](#style-y195)] - - - Lassen Sie JSHint über ihre Tests laufen. - - *Warum?*: Tests sind Code. JSHint prüft die Codequalität und kann Qualitätsprobleme aufdecken, die dazu führen können, dass Tests nicht sauber laufen. - -### Erleichternde Rahmenbedingungen fur JSHint und Regeln für Tests -###### [Style [Y196](#style-y196)] - - - Lockern sie die JSHint-Regeln für Ihren Testcode, damit `describe` und `expect` erlaubt werden. Lockern sie die Regeln auch für Ausdrücke, da Mocha diese benutzt. - - *Warum?*: Ihre Tests sind codiert und somit sollten Sie ihnen die gleiche Aufmerksamkeit in Puncto Codequalität widmen, wie Ihrem Produktionscode. Die Lockerung der Regeln für globale Variablen, die von Ihrem Test-Framework genutzt werden, erzielen Sie, wenn sie folgendes in Ihren Testcode einbinden. - - ```javascript - /* jshint -W117, -W030 */ - ``` - oder Sie fügen folgendes in die Datei mit Ihren JSHint Optionen ein. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Tests organisieren -###### [Style [Y197](#style-y197)] - - - Halten Sie Unit-Tests bei Ihrem Client-Programmcode. Die Testspezifikationen für die Server-Integrationstests oder die Tests für mehrere Komponenten platzieren Sie am besten in einem separaten `tests`-Verzeichnis. - - *Warum?*: Unit-Tests stehen in direktem Bezug zu einer spezifischen Komponente und Datei im Quellcode. - - *Warum?*: Es ist einfacher, sie auf dem neuesten Stand zu halten, weil Sie sie immer im Blick haben. Während Sie programmieren (ob Sie jetzt TDD betreiben oder während oder nach der Entwicklung testen), sind die Testspezifikationen weder aus der Sicht noch aus Ihren Gedanken. Deshalb ist es wahrscheinlicher, dass sie auch gepflegt werden, das dazu beiträgt, die Abdeckung des Codes durch Tests zu verbessern. - - *Warum?*: Wenn Sie Code aktualisieren ist es einfacher, auch die Tests im gleichen Zuge zu aktualisieren. - - *Warum?*: Code und Tests nebeneinander zu halten erleichtert es, sie zu finden und beide bei Bedarf zu verschieben. - - *Warum?*: Werden die Testspezifikationen neben dem Code gehalten, wird es einem Leser leichter gemacht zu verstehen, wie die Komponente eingesetzt werden soll und er entdeckt ihre bekannten Einschränkungen. - - *Warum?*: Die Testspezifikationen beim Erstellen eines Installationspakets für die Anwendung vom restlichen Code zu trennen, erledigt man einfach mit grunt oder gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Animationen - -### Anwendung -###### [Style [Y210](#style-y210)] - - - Benutzen Sie die ["subtle" Animationen von Angular](https://docs.angularjs.org/guide/animations) um zwischen Status, Views oder primären sichtbaren Elementen hin und her zu wechseln. Binden Sie das [ngAnimate-Modul](https://docs.angularjs.org/api/ngAnimate) ein. Die drei Schlüssel hierzu sind "subtle", "smooth", "seamless". - - *Warum?*: Angulars "subtle" Animationen können die User-Experience erhöhen, wenn sie entsprechend eingesetzt werden. - - *Warum?*: Angulars "subtle" Animationen können die wahrgenommene Performance von View-Wechseln verbessern. - -### Sub Second -###### [Style [Y211](#style-y211)] - - - Nutzen Sie eine kurze Dauer für Animationen. Ich starte immer bei 300ms und passe diese dann entsprechend an. - - *Warum?*: Lange Animationen können sich negativ auf die wahrgenommene Performance auswirken und einen gegenteiligen Einfluss auf die User Experience haben und die Anwendung langsam aussehen lassen. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Benutzen Sie [animate.css](http://daneden.github.io/animate.css/) für konventielle Animationen. - - *Warum?*: Die Animationen von animate.css sind schnell, sanft und einfach zu Ihrer Anwendung hinzuzufügen. - - *Warum?*: Bietet Konsistenz Ihrer Animationen. - - *Warum?*: animate.css ist weit verbreitet und gut getestet. - - Anmerkung: Schauen Sie sich diesen [tollen Beitrag von Matias Niemelä über Angular Animationen](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) an. - -**[Zurück zum Anfang](#table-of-contents)** - -## Kommentare - -### jsDoc -###### [Style [Y220](#style-y220)] - - - Wenn sie eine Dokumentation planen, dann nutzen Sie die [`jsDoc`](http://usejsdoc.org/)-Syntax, um Funktionsnamen, Beschreibungen, Parameter und Rückgabewerte zu dokumentieren. Benutzen Sie `@namespace` und `@memberOf`, um Ihre Anwendungsstruktur mit abzubilden. - - *Warum?*: Sie können die Dokumentation immer wieder aus ihrem Code generieren, statt sie neu zu schreiben. - - *Warum?*: Bietet die Konsistenz eines Industriewerkzeugs. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Anwendungsweiter Logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Protokolliert Fehler - * @param {String} Meldung, die protokolliert wird - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## JS Hint - -### Nutzen Sie eine Optionsdatei -###### [Style [Y230](#style-y230)] - - - Benutzen Sie JS Hint um Ihren JavaScript-Code zu prüfen (Linting) und versichern Sie sich, dass sie die Prüffunktion für sich angepasst haben und die Optionsdatei in Ihrer Quellcodeverwaltung abgelegt haben. Schauen Sie sich die [JS Hint Dokumentation](http://www.jshint.com/docs/) an, um mehr über die Optionen zu erfahren. - - *Warum?*: Erstattet erste Meldung, bevor Sie den Code in die Quellcodeverwaltung übertragen. - - *Warum?*: Bietet Konsistenz im ganzen Team. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## JSCS - -### Nutzung und Optionsdatei -###### [Style [Y235](#style-y235)] - - - Benutzen Sie JSCS um die Anwendung von Programmier-Richtlinien in Ihrem JavaScript-Code zu prüfen und versichern Sie sich, dass Sie die Prüffunktion für sich angepasst haben und die Optionsdatei in Ihrer Quellcodeverwaltung abgelegt haben. Schauen Sie sich die [JS Hint Dokumentation](http://www.jshint.com/docs/) an, um mehr über die Optionen zu erfahren. - - *Warum?*: Erstattet erste Meldung, bevor Sie den Code in die Quellcodeverwaltung übertragen. - - *Warum?*: Bietet Konsistenz im ganzen Team. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowMultipleLineStrings": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Konstanten - -### Globale Drittanbieter-Konstanten -###### [Style [Y240](#style-y240)] - - - Erstellen Sie eine Angular-Konstante für die globalen Variablen aus Bibliotheken von Drittanbietern. - - *Warum?*: Bietet einen Weg, Bibliotheken von Drittanbietern in einem sicheren Umfeld anzubieten, die andererseits "Globals" wären. Dies verbessert die Testbarkeit, weil Sie so einfacher die Abhängigkeiten Ihres Codes erfahren (verhindert lückenhafte Abstraktionen). Auch die Simulation dieser Abhängigkeiten wird zugelassen, wo sie Sinn macht. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Benutzen Sie Konstanten für Werte, die sich nicht ändern und nicht aus einem anderen Service kommen. Wenn Konstanten nur für ein bestimmtes Modul gebraucht werden, welches zudem wiederverwendbar sein soll, dann platzieren Sie die Konstanten in einer Datei (pro Modul) und benennen Sie die Datei nach dem Modul. Bis dahin halten Sie die Konstanten im Hauptmodul in einer `constants.js`-Datei. - - *Warum?*: Ein Wert, der sich ändert - wenn auch nur unregelmäßig - sollte von einem Service ermittelt werden, so dass er nicht im Quellcode geändert werden muss. Zum Beispiel könnte eine URL für einen Datenservice in einer Konstanten abgelegt werden. Besser wäre es aber, diesen Wert über einen WebService zu ermitteln. - - *Warum?*: Konstanten können in jede Angular-Komponente (auch in einen Provider) eingefügt werden. - - *Warum?*: Ist eine Anwendung in Module unterteilt, die in anderen Anwendungen genutzt werden können, so sollte jedes alleinstehende Modul für sich selbst funktionieren, eingeschlossen seiner Konstanten. - - ```javascript - // Konstanten für die gesamte Anwendung - angular - .module('app.core') - .constant('moment', moment); - - // Konstanten, die nur vom Verkaufsmodul genutzt werden - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Dateitemplates und Snippets -Nutzen Sie Templates oder Snippets, um stimmigen Richtlinien und Mustern zu folgen. Hier sind Templates oder Snippets für einige Editoren und IDEs zur Webentwicklung. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular Snippets, die diesen Mustern und Richtlinien folgen. - - - Laden Sie die [Sublime Angular Snippets](assets/sublime-angular-snippets?raw=true) heruntern - - Platzieren Sie sie in Ihrem Package-Ordner - - Starten Sie Sublime neu - - - Schreiben Sie einen dieser Befehle in einer JavaScript-Datei, gefolgt von einem `TAB`: - - ```javascript - ngcontroller // erstellt einen Angular Controller - ngdirective // erstellt eine Angular Direktive - ngfactory // erstellt eine Angular Factory - ngmodule // erstellt ein Angular Modul - ngservice // erstellt einen Angular Service - ngfilter // erstellt einen Angular Filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Angular Dateitemplates, die diesen Mustern und Richtlinien folgen, finden Sie unter [SideWaffle](http://www.sidewaffle.com) - - - Laden Sie die [SideWaffle](http://www.sidewaffle.com) Visual Studio Erweiterung (vsix file) herunter - - Starten Sie die vsix-Datei - - Starten Sie Visual Studio neu - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Angular Dateitemplates, die diesen Mustern und Richtlinien folgen. Sie können sie in Ihre WebStorm-Einstellungen importieren: - - - Laden Sie die [WebStorm Angular Dateitemplates und Snippets](assets/webstorm-angular-file-template.settings.jar?raw=true) herunter - - Öffnen Sie WebStorm und gehen Sie ins `File`-Menü - - Wählen Sie die `Import Settings` Menüoption - - Wählen Sie die Datei aus und klicken Sie `OK` - - - Schreiben Sie einen dieser Befehle in einer JavaScript-Datei, gefolgt von einem `TAB`: - - ```javascript - ng-c // erstellt einen Angular Controller - ng-f // erstellt eine Angular Factory - ng-m // erstellt ein Angular Modul - ``` - -### Atom -###### [Style [Y253](#style-y253)] - - - Angular Dateitemplates, die diesen Mustern und Richtlinien folgen - ``` - apm install angularjs-styleguide-snippets - ``` - oder - - Öffnen Sie Atom und öffnen Sie die Paketverwaltung (Packages -> Settings View -> Install Packages/Themes) - - Suchen Sie nach dem Paket 'angularjs-styleguide-snippets' - - Klicken Sie `Install`, um das Paket zu installieren - - - Schreiben Sie einen dieser Befehle in einer JavaScript-Datei, gefolgt von einem `TAB`: - - ```javascript - ngcontroller // erstellt einen Angular Controller - ngdirective // erstellt eine Angular Direktive - ngfactory // erstellt eine Angular Factory - ngmodule // erstellt ein Angular Modul - ngservice // erstellt einen Angular Service - ngfilter // erstellt einen Angular Filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular Dateitemplates, die diesen Mustern und Richtlinien folgen - - Laden Sie die [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) herunter - - Brackets Extension Manager ( File > Extension manager ) - - Installieren Sie die ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Klicken Sie die Glühbirne am rechten Rand vom "Gutter" in Brackets - - Klicken Sie `Settings` und dann `Import` - - Wählen Sie die Datei und danach `skip` oder `override` - - Klicken Sie `Start Import` - - - Schreiben Sie einen dieser Befehle in einer JavaScript-Datei, gefolgt von einem `TAB`: - - ```javascript - // Dies sind vollwertige Dateisnippets mit einer IIFE - ngcontroller // erstellt einen Angular Controller - ngdirective // erstellt eine Angular Direktive - ngfactory // erstellt eine Angular Factory - ngapp // erstellt einen Angular Modul-Setter - ngservice // erstellt einen Angular Service - ngfilter // erstellt einen Angular Filter - - // Dies sind Teilsnippets, die zum Verketten gedacht sind: - ngmodule // erstellt einen Angular module getter - ngstate // erstellt eine Angular UI Router State-Definition - ngconfig // definiert eine Funktion für die Konfigurationsphase - ngrun // definiert eine Funktion für die Run-Phase - ngroute // definiert eine Angular ngRoute 'when'-Definition - ngtranslate // nutzt den $translate Service mit seinem Promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - vim-Snippets die diesen Mustern und Richtlinien folgen. - - - Laden Sie die [vim Angular snippets](assets/vim-angular-snippets?raw=true) herunter - - Setzen Sie [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - Kopieren Sie die Snippets ins Snippet-Verzeichnis - - ```javascript - ngcontroller // erstellt einen Angular Controller - ngdirective // erstellt eine Angular Direktive - ngfactory // erstellt eine Angular Factory - ngmodule // erstellt ein Angular Modul - ngservice // erstellt einen Angular Service - ngfilter // erstellt einen Angular Filter - ``` -**[Zurück zum Anfang](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -Sie können den [HotTowel Yeoman Generator](http://jpapa.me/yohottowel) nutzen, um eine Anwendung als Startpunkt zu erstellen, die diesen Mustern und Richtlinien folgt. - -1. Installieren Sie generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Erstellen Sie ein neues Verzeichnis und wechseln sie dort hin - - ``` - mkdir myapp - cd myapp - ``` - -3. Starten die den Generator - - ``` - yo hottowel helloWorld - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Routing -Das Routing auf der Client-Seite ist für die Erstellung eines Navigationsflusses zwischen den Views und den zusammengestellten Views, die aus mehreren kleineren Vorlagen und Direktiven bestehen. - -###### [Style [Y270](#style-y270)] - - - Benutzen Sie den [AngularUI Router](http://angular-ui.github.io/ui-router/) für das clientseitige Routing. - - *Warum?*: Der UI Router bietet alle Funktionen des Angular Routers und zusätzlich Weitere, wie geschachtelte Routen und Status. - - *Warum?*: Die Syntax ähnelt der des Angular Routers und ist einfach auf den UI Router umzustellen. - - - Anmerkung: Sie können einen Provider, wie den unten angeführten `routerHelperProvider` nutzen, um die Stati während der Startphase der Anwendung dateiübergreifend zu konfigurieren. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - Definieren Sie die Routen für die Views in den Modulen, in denen sie enthalten sind. Jedes Modul sollte die Routen seiner Views enthalten. - - *Warum?*: Jedes Modul sollte für sich allein lauffähig sein. - - *Warum?*: Wird ein Modul zur Anwendung hinzugefügt oder aus ihr ausgeklinkt, enthält die Anwendung nur Routen, die zu vorhanden Views führen. - - *Warum?*: Dies erleichtert es, Teile eine Anwendung zu aktivieren oder zu deaktivieren, ohne dass man sich um verwaiste Routen Sorgen machen muss. - -**[Zurück zum Anfang](#table-of-contents)** - -## Automatisierung von Aufgaben -Nutzen Sie [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com), um Aufgaben zu automatisieren. Bei Gulp geht der Code vor Konfiguration, bei Grunt Konfiguration vor Code. Ich persönlich bevorzuge Gulp, weil ich denke, es ist einfacher zu lesen und zu schreiben, aber beide sind erstklassig. - -> Erfahren Sie mehr über Gulp und Muster für die Automatisierung von Aufgaben in meinem [Gulp Pluralsight Kurs](http://jpapa.me/gulpps) - -###### [Style [Y400](#style-y400)] - - - Nutzen Sie die Automatisierung von Aufgaben, um die Moduldefinitionsdateien `*.module.js` vor allen anderen JavaScript-Dateien in der Anwendung aufzulisten. - - *Warum?*: Angular muss die Moduldefinitionen registrieren, bevor die Module benutzt werden können. - - *Warum?*: Das Benennen der Module gemäß des Musters `*.module.js`, vereinfacht es, diese zu filtern und zuerst aufzulisten. - - ```javascript - var clientApp = './src/client/app/'; - - // Immer zuerst die Moduldateien - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Zurück zum Anfang](#table-of-contents)** - -## Filter - -###### [Style [Y420](#style-y420)] - - - Vermeiden Sie es, Filter dazu zu nutzen, alle Eigenschaften in einem komplexen Objektgraphen zu prüfen. Nutzen Sie Filter um Eigenschaften auszuwählen. - - *Warum?*: Filter können sehr leicht missbraucht werden und dann die Performance einer Anwendung negativ beeinflussen, wenn Sie nicht überlegt eingesetzt werden. Zum Beispiel: Wenn ein Filter auf einen großen und tief geschachtelten Objektgraphen angewendet wird. - -**[Zurück zum Anfang](#table-of-contents)** - -## Angular Dokumentation -Für alles Andere und die, API-Referenz, schauen Sie bitte in der [Angular-Dokumentation](//docs.angularjs.org/api) nach. - -## Beiträge - -Öffnen Sie erst einen "Issue", um eventuelle Änderungen oder Erweiterungen zu diskutieren. Sollten Sie Fragen zu den Richtlinien haben, können Sie diese selbstverständlich als "Issue" im Repository hinterlassen. Wenn Sie einen Tippfehler finden, erstellen Sie ein Pull-Request. Die Idee ist es, den Inhalt aktuell zu halten und GitHubs Möglichkeiten zu nutzen, die Nachricht über "Issues" und PRs zu verbreiten, die über Google gefunden werden können. Warum? Weil es sein kann, dass nicht nur Sie, sondern auch andere die gleichen Fragen haben. Hier erfahren Sie mehr darüber, wie Sie beitragen können. - -*Wenn Sie zu diesem Repository beitragen, erklären Sie sich damit einverstanden, dass Ihr Beitrag unter die Lizenz dieses Repository fällt.* - -### Prozess - 1. Diskutieren Sie die Änderungen in einem GitHub-"Issue". - 2. Erstellen Sie in Pull-Request, beziehen Sie sich auf den "Issue" und erklären Sie die Änderung und warum sie Mehrwert bringt. - 3. Der Pull-Request wird geprüft und entweder aufgenommen oder abgelehnt. - -## Lizenz - -_tldr; Wenden Sie diese Richtlinien an. Beiträge sind willkommen._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (Die MIT-Lizenz) -Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt, sie uneingeschränkt zu benutzen, inklusive und ohne Ausnahme dem Recht, sie zu verwenden, kopieren, ändern, fusionieren, verlegen, verbreiten, unterlizenzieren und/oder zu verkaufen, und Personen, die diese Software erhalten, diese Rechte zu geben, unter den folgenden Bedingungen: - -Der obige Urheberrechtsvermerk und dieser Erlaubnisvermerk sind in allen Kopien oder Teilkopien der Software beizulegen. - -DIE SOFTWARE WIRD OHNE JEDE AUSDRÜCKLICHE ODER IMPLIZIERTE GARANTIE BEREITGESTELLT, EINSCHLIESSLICH DER GARANTIE ZUR BENUTZUNG FÜR DEN VORGESEHENEN ODER EINEM BESTIMMTEN ZWECK SOWIE JEGLICHER RECHTSVERLETZUNG, JEDOCH NICHT DARAUF BESCHRÄNKT. IN KEINEM FALL SIND DIE AUTOREN ODER COPYRIGHTINHABER FÜR JEGLICHEN SCHADEN ODER SONSTIGE ANSPRÜCHE HAFTBAR ZU MACHEN, OB INFOLGE DER ERFÜLLUNG EINES VERTRAGES, EINES DELIKTES ODER ANDERS IM ZUSAMMENHANG MIT DER SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE ENTSTANDEN. - -**[Zurück zum Anfang](#table-of-contents)** diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md deleted file mode 100644 index 1b1f7072..00000000 --- a/a1/i18n/es-ES.md +++ /dev/null @@ -1,2838 +0,0 @@ -# Guía de estilo AngularJS - -*Guía de estilos colaborativa de Angular para equipos por [@john_papa](//twitter.com/john_papa)* - -Si estás buscando una guía colaborativa sobre sintaxis, convenciones y estructura de aplicaciones con AngulRJS, este es el sitio. Estos estilos están basados en mi experiencia desarrollando con [AngularJS](//angularjs.org), persentaciones, [Cursos en Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) y trabajando en equipos. - -El propósito de esta guía de estilos es proporcionar una guía de cómo construir aplicaciones con Angular enseñando convenciones que uso y, lo más importante, el porqué. - ->Si te gusta esta guía, echa un vistazo al curso de Pluralsight [Angular Patterns: Clean Code](http://jpapa.me/ngclean). - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Asombrosa comunidad y créditos -Nunca trabajes solo. Personalmente, la comunidad de Angular es un increíble grupo apasionado por compartir experiencias. Como por ejemplo, mi amigo y experto en Angular Todd Motto, con el que he colaborado en muchos de los estilos y convenciones. Estamos de acuerdo en la mayoría, y en otras no. Te recomiendo que le eches un vistazo a [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) para que le des sentido a esta guía y la compares. - -Muchos de los estilos han salido de las muchas sesiones de pair programming que [Ward Bell](http://twitter.com/wardbell) y yo hemos tenido. Aunque no siempre coincidimos, mi amigo Ward me ha ayudado con la última evolución de esta guía. - -## Mira los estilos en la aplicación de ejemplo -Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta útil verlos en práctica. Esta guía viene acompañada de una aplicación de ejemplo que sigue los estilos y patrones. La puedes encontrar en [aplicación de ejemplo (llamada modular) aquí](https://github.com/johnpapa/ng-demos) dentro del directorio `modular`. Siéntete libre de cogerla, hacerle clone o un fork. [Instrucciones de cómo arrancarla en su readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -## Traducciones -[Traducciones de esta guía de estilos Angular](https://github.com/johnpapa/angularjs-styleguide/tree/master/i18n) son mantenidas por la comunidad y las puedes encontrar aquí. - -## Tabla de contenidos - - 1. [Responsabilidad - Única](#single-responsibility-o-responsabilidad-única) - 1. [IIFE](#iife) - 1. [Módulos](#módulos) - 1. [Controladores](#controladores) - 1. [Servicios](#servicios) - 1. [Fábricas](#fábricas) - 1. [Servicios de Datos](#servicios-de-datos) - 1. [Directivas](#directivas) - 1. [Resolviendo Promesas en un Controlador](#resolviendo-promesas-en-un-controlador) - 1. [Anotación Manual para la Inyección de Dependencias](#anotación-manual-para-la-inyección-de-dependencias) - 1. [Minificación y Anotación](#minificación-y-anotación) - 1. [Manejo de Excepciones](#manejo-de-excepciones) - 1. [Cómo Nombrar](#cómo-nombrar) - 1. [Estructura de la Aplicación El Principio LIFT](#estructura-de-la-aplicación-el-principio-lift) - 1. [Estructura de la Aplicación](#estructura-de-la-aplicación) - 1. [Modularidad](#modularidad) - 1. [Lógica de Arranque](#lógica-de-arranque) - 1. [Servicios Envoltorios $ de Angular](#servicios-envoltorios--de-angular) - 1. [Pruebas](#pruebas) - 1. [Animaciones](#animaciones) - 1. [Comentarios](#comentarios) - 1. [JSHint](#js-hint) - 1. [Constantes](#constantes) - 1. [Plantillas y Snippets](#plantillas-y-snippets) - 1. [Generador de Yeoman](#generador-de-yeoman) - 1. [Ruteo](#ruteo) - 1. [Automatización de Tareas](#automatización-de-tareas) - 1. [Angular Docs](#angularjs-docs) - 1. [Contribuyendo](#contribuyendo) - 1. [Licencia](#licencia) - -## Single Responsibility o Responsabilidad Única - -### La regla del 1 -###### [Style [Y001](#style-y001)] - - - Define 1 componente por archivo. - - El siguiente ejemplo define el módulo `app` y sus dependencias, define un controlador, y defines una fábrica todo en el mismo archivo. - - ```javascript - /* evitar */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Los mismos componentes están separados en su propio archivo. - - ```javascript - /* recomendado */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recomendado */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recomendado */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## IIFE -### Closures de JavaScript -###### [Style [Y010](#style-y010)] - - - Envuelve los componentes Angular en una expresión de función que se invoca inmediatamente Immediately Invoked Function Expression (IIFE). - - *¿Por qué?*: Una IIFE elimina las variables del scope global. Esto ayuda a prevenir que las variables y las declaraciones de funciones vivan más de lo esperado en el scope global, evitando así colisión de variables. - - *¿Por qué?*: Cuando tu código se minimiza y se empaqueta en un archivo único para desplegar al servidor de producción, podrías tener colisión de variables y muchas variables globales. Una IIFE te protege contra ambos, creando una scope por cada archivo. - - ```javascript - /* evitar */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // La función logger es añadida como variable global - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // la función storage es añadida como variable global - function storage() { } - ``` - - ```javascript - /** - * recomendado - * - * así no dejamos ninguna variable global - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Nota: Para acortar únicamente, el resto de los ejemplos de esta guía podrían omitir la sintaxis IIFE. - - - Nota: IIFE previente que el código de los tests llegue a sus variables privadas, como expresiones regulares o funciones de ayuda que normalmente vienen bien para hacer pruebas por sí solas. Sin embargo, puedes acceder a ellas creando accesorios o accediendo a través de sus componentes. Por ejemplo, poniendo las funciones de ayuda, expresiones regulares o constantes en su propia fábrica. - -**[Volver arriba](#tabla-de-contenidos)** - -## Módulos - -### Evitando la colisión de nombres -###### [Style [Y020](#style-y020)] - - - Use una convención de nombres única con separadores para los sub-módulos. - - *¿Por qué?*: Nombres únicos ayudan a evitar colisiones en los nombres de módulos. Los separadores ayudan a definir los módulos y la jerarquía de sus sub-módulos. Por ejemplo `app` puede ser tu módulo raíz y `app.dashboard` y `app.users` pueden ser módulos que dependen de `app`. - -### Definiciones (aka Setters) -###### [Style [Y021](#style-y021)] - - - Declara los módulos sin usar una variable, usando la sintaxis de los setters. - - *¿Por qué?*: Con un componente por archivo, es raro que necesitemos introducir una variable para el módulo. - - ```javascript - /* evitar */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - En su lugar usa la sintaxis de los setters - - ```javascript - /* recomendado */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - Al usar un módulo, evita usar una variable y en su lugar usa encadenamiento con la sintaxis de los getter. - - *¿Por qué?*: Esto hace más legible el código y evita que las variables colisionen. - - ```javascript - /* evitar */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recomendado */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-y023)] - - - Setea sólo una vez y usa get para el resto de instancias. - - *¿Por qué?*: Un módulo debe ser creado sólo una vez y recuperado desde ese punto. - - - Usa `angular.module('app', []);` para setear un módulo. - - Usa `angular.module('app');` para recuperar un módulo. - -### Funciones anónimas vs funciones con nombre -###### [Style [Y024](#style-y024)] - - - Usa funciones con nombre en lugar de pasar una función anónima en el callback. - - *¿Por qué?*: Así el código es más legible, es más fácil de debugear, y reduce la cantidad de código anidado en los callbacks. - - ```javascript - /* evitar */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recomendado */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Controladores - -### controllerAs Sintaxis en la Vista -###### [Style [Y030](#style-y030)] - - - Usa la sintaxis [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) en lugar del `clásico controlador con $scope`. - - *¿Por qué?*: Los Controladores se construyen, renuevan y proporcionan una nueva instancia única, y la sintaxis `controllerAs` se acerca más a eso que la `sintaxis clásica de $scope`. - - *¿Por qué?*: Promueves el uso de binding usando el "." en el objeto dentro de la Vista (ej. `customer.name` en lugar de `name`), así es más contextual, fácil de leer y evitas problemas de referencia que pueden aparecer con el "punto". - - *¿Por qué?*: Ayuda a evitar usar `$parent` en las Vistas con controladores anidados. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Sintaxis en el Controlador -###### [Style [Y031](#style-y031)] - - - Usa la sintaxis `controllerAs` en lugar del `clásico controlador con $scope`. - - - La sintaxis `controllerAs` usa `this` dentro de los controladores que se asocian al `$scope` - - *¿Por qué?*: `controllerAs` es azúcar sintáctico sobre el `$scope`. Puedes enlazar a la vista y acceder a los métodos del `$scope`. - - *¿Por qué?*: Ayuda a evitar la tentación de usar los métodos del `$scope` dentro de un controller cuando debería ser mejor evitar usarlos o moverlos a una fábrica. Considera usar `$scope` en una factory, o en un controlador sólo cuando sea necesario. Por ejemplo cuando publicas y te suscribes a eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), o [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considera mover estos usos a una fábrica e invocarlos desde el controlador. - - ```javascript - /* evitar */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recomendado - pero mira la sección siguiente */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs con vm -###### [Style [Y032](#style-y032)] - - - Usa una variable para capturar `this` cuando uses la sintaxis `controllerAs`. Elige un nombre de variable consistente como `vm`, de ViewModel. - - *¿Por qué?*: La palabra `this` es contextual y cuando es usada dentro de una función en un controlador puede cambiar su contexto. Capturando el contexto de `this` te evita encontrarte este problema. - - ```javascript - /* evitar */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recomendado */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Nota: Puedes evitar los warnings de [jshint](http://www.jshint.com/) escribiendo un comentario encima de la línea de código. Sin embargo no hace falta si el nombre de la función empieza con mayúsculas, ya que esa es la convención para las funciones de los constructores, que es lo que un controller en Angular es. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Nota: Cuando crees watchers en un controlador usando `controller as`, puedes observar la variable `vm.*` usando la siguiente sintaxis.(Crea los watchers con precaución ya que añaden mucha carga al ciclo de digest) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Miembros Bindeables Arriba -###### [Style [Y033](#style-y033)] - - - Coloca las asociaciones en la parte superior del controlador, ordenalas alfabéticamente y no las distribuyas a lo largo del código del controlador. - - *¿Por qué?*: Colocar las variables asignables arriba hace más fácil la lectura y te ayuda a identificar qué variables del controlador pueden ser asociadas y usadas en la Vista. - - *¿Por qué?*: Setear funciones anónimas puede ser fácil, pero cuando esas funciones tienen más de una línea de código se hace menos legible. Definir las funciones bajo las variables bindeables (las declaraciones de las funciones serán movidas hacia arriba en el proceso de hoisting), hace que los detalles de implementación estén abajo, deja las variables arriba y más sencilla la lectura. - - ```javascript - /* evitar */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recomendado */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Nota: Si la función es de una línea, déjala arriba, siempre y cuando no afecte en la legibilidad. - - ```javascript - /* evitar */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * líneas - * de - * código - * que afectan a - * la legibilidad - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recomendado */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Declaraciones de Funciones para Esconder los Detalles de Implementación -###### [Style [Y034](#style-y034)] - - - Declara funciones para ocultar detalles de implementación. Mantén las variables bindeables arriba. Cuando necesites bindear una función a un controlador referencia una función que aparezca después en el archivo. Esto está directamente relacionado con la sección: Miembros Bindeables Arriba. Para más detalles mira [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *¿Por qué?*: Colocar las variables bindeables arriba hace más fácil la lectura y te ayuda a identificar qué variables del controlador pueden ser asociadas y usadas en la Vista. - - *¿Por qué?*: Colocar los detalles de implementación de una función al final del archivo deja la complejidad fuera de vista así puedes ver las cosas importantes arriba. - - *¿Por qué?*: La declaración de las funciones son movidas arriba por el - proceso de hoisting así que no tenemos que preocuparnos por usar una - función antes de que sea definida (como la habría si fueran funciones en forma de expresión) - - *¿Por qué?*: No tendrás que preocuparte de que si pones `var a` antes de `var b` se rompa el código porque `a` dependa de `b`. - - *¿Por qué?*: El orden es crítico para las funciones en forma de expresión - - ```javascript - /** - * evitar - * Using function expressions. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Nótese que las cosas importantes están dispersas en el ejemplo anterior. En el siguiente ejemplo, lo importante está arriba. Por ejemplo, las variables asociadas al controlador como `vm.avengers` y `vm.title`. Los detalles de implementación están debajo. Así es más fácil de leer. - - ```javascript - /* - * recomendado - * Usando declaraciones de funciones y - * miembros bindeables arriba. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Diferir la Lógica del Controlador -###### [Style [Y035](#style-y035)] - - - Difiera la lógica dentro de un controlador delegándola a servicios y fábricas. - - *¿Por qué?*: La lógica podría ser reutilizada por varios controladores cuando la colocas en un servicio y la expones como una función. - - *¿Por qué?*: La lógica en un servicio puede ser aislada en un test unitario, mientras que la lógica de llamadas en un controlador se puede mockear fácilmente. - - *¿Por qué?*: Elimina dependencias y esconde detalles de implementación del controlador. - - ```javascript - - /* evitar */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recomendado */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -### Mantén tus Controladores Enfocados -###### [Style [Y037](#style-y037)] - - - Define un controlador para una vista, no intentes reutilizar el controlador para otras vistas. En lugar de eso, mueve la lógica que se pueda reutilizar a fábricas y deja el controlador simple y enfocado en su vista. - - *¿Por qué?*: Reutilizar controladores con varias vistas es arriesgado y necesitarías buena cobertura de tests end to end (e2e) para asegurar que todo funciona bien en la aplicación. - -### Asignando Controladores -###### [Style [Y038](#style-y038)] - - - Cuando un controlador debe ser asociado a una vista y cada componente puede ser reutilizado por otros controladores o vistas, define controladores con sus rutas. - - Nota: Si una Vista es cargada por otra además de por la ruta, entonces usa la sintaxis `ng-controller="Avengers as vm"`. - - *¿Por qué?*: Emparejar el controlador en la ruta permite a diferentes rutas invocar diferentes pares de controladores y vistas. Cuando los controladores son asignados en la vista usando [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), esa vista siempre estará asociada al mismo controlador. - - ```javascript - /* evitar - cuando se use con una ruta y queramos asociarlo dinámicamente */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recomendado */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Servicios - -### Singletons -###### [Style [Y040](#style-y040)] - - - Los Servicios son instanciados con un `new`, usan `this` para los métodos públicos y las variables. Ya que son muy similares a las factories, usa una factory en su lugar por consistencia. - - Nota: [Todos los servicios Angular son singletons](https://docs.angularjs.org/guide/services). Esto significa que sólo hay una instancia de un servicio por inyector. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Fábricas - -### Responsabilidad Única -###### [Style [Y050](#style-y050)] - - - Las fábricas deben tener una [responsabilidad única](http://en.wikipedia.org/wiki/Single_responsibility_principle), que es encapsulada por su contexto. Cuando una fábrica empiece a exceder el principio de responsabilidad única, una nueva fábrica debe ser creada. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Las Fábricas son singleton y devuelven un objeto que contiene las variables del servicio. - - Nota: [Todos los servicios Angular son singletons](https://docs.angularjs.org/guide/services). - -### Miembros accesibles Arriba -###### [Style [Y052](#style-y052)] - - - Expón las variables que se llaman del servicio (su interfaz) arriba, usando la técnica deribada de [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *¿Por qué?*: Colocar los elementos que se llaman arriba hace más fácil la lectura y te ayuda a identificar los elementos del servicio que se pueden llamar y se deben testear (y/o mockear). - - *¿Por qué?*: Es especialmente útil cuando el archivo se hace más largo, ya que ayuda a evitar el scroll para ver qué se expone. - - *¿Por qué?*: Setear las funciones puede ser fácil, pero cuando tienen más de una línea ser reduce la legibilidad. Definiendo la interfaz mueve los detalles de implementación abajo, mantiene la interfaz que va a ser llamada arriba y lo hace más fácil de leer. - - ```javascript - /* evitar */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recomendado */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - De esta forma se asocian los bindeos desde el objeto que lo mantiene, los valores primitivos no se pueden modificar por si solos usando este patrón - - ![Fábricas Usando "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Declaración de Funciones para Esconder los Detalles de Implementación -###### [Style [Y053](#style-y053)] - - - Declara funciones para esconder detalles de implementación. Manten los elementos accesibles en la parte superior de la fábrica. Referencia a los que aparezcan después en el archivo. Para más detalles visita [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *¿Por qué?*: Coloca los elementos accesibles en la parte superior para hacerlo más fácil de leer y ayudarte a identificar instantáneamente qué funciones de la fábrica se pueden accesar externamente. - - *¿Por qué?*: Colocar los detalles de implementación de una función al final del archivo mueve esa complejidad fuera de la vista, de esta forma puedes dejar lo importante arriba. - - *¿Por qué?*: Las declaraciones de las funciones son "elevadas" de esta forma no hay problemas en usar una función antes de su definición (como la habría si fueran funciones en forma de expresión). - - *¿Por qué?*: No tendrás que preocuparte de que si pones `var a` antes de `var b` se rompa el código porque `a` dependa de `b`. - - *¿Por qué?*: El orden es crítico para las funciones en forma de expresión - - ```javascript - /** - * evitar - * Usar función como expresión - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // detalles de implementación van aquí - }; - - var getAvengerCount = function() { - // detalles de implementación van aquí - }; - - var getAvengersCast = function() { - // detalles de implementación van aquí - }; - - var prime = function() { - // detalles de implementación van aquí - }; - - var ready = function(nextPromises) { - // detalles de implementación van aquí - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recomendado - * Usar declaración de funciones - * y miembros accesibles arriba - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // detalles de implementación van aquí - } - - function getAvengerCount() { - // detalles de implementación van aquí - } - - function getAvengersCast() { - // detalles de implementación van aquí - } - - function prime() { - // detalles de implementación van aquí - } - - function ready(nextPromises) { - // detalles de implementación van aquí - } - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Servicios de Datos - -### Separate Data Calls -###### [Style [Y060](#style-y060)] - - - Refactoriza la lógica para hacer operaciones e interaciones con datos en una factory. Crear data services responsables de las peticiones XHR, local storage, memoria o cualquier otra operación con datos. - - *¿Por qué?*: La responsabilidad del controlador es la de presentar y recoger información para la vista. No debe importarle cómo se consiguen los datos, sólo saber cómo conseguirlos. Separando los datos de servicios movemos la lógica de cómo conseguirlos al servicio de datos, y deja el controlador simple, enfocándose en la vista. - - *¿Por qué?*: Hace más fácil testear (mock o real) las llamadas de datos cuando testeamos un controlador que usa un data service. - - *¿Por qué?*: La implementación del servicio de datos puede tener código muy específico para usar el repositorio de datos. Podría incluir cabeceras, cómo hablar a los datos, u otros servicios como $http. Separando la lógica en servicios de datos encapsulamos la lógica en un único lugar, escondiendo la implementación de sus consumidores externos (quizá un controlador), de esta forma es más fácil cambiar la implementación. - - ```javascript - /* recomendado */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Nota: El servicio de datos es llamado desde los consumidores, como el controlador, escondiendo la implementación del consumidor como se muestra a continuación. - - ```javascript - /* recomendado */ - - // controller llamando a la factory del data service - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Regresa una Promesa desde las Llamadas a Datos -###### [Style [Y061](#style-y061)] - - - Cuando llamamos a servicios de datos que devuelven una promesa como $http, devuelve una promesa en la llamada de tu función también. - - *¿Por qué?*: Puedes encadenar promesas y hacer algo cuando la llamada se complete y resuelva o rechace la promesa. - - ```javascript - /* recomendado */ - - activate(); - - function activate() { - /** - * Step 1 - * Pide a la función getAvengers por los datos - * de los vengadores y espera la promesa - */ - return getAvengers().then(function() { - /** - * Step 4 - * Ejecuta una acción cuando se resuelva la promesa final - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Pide al servicio de datos los datos y espera - * por la promesa - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * setea los datos y resuelve la promesa - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - **[Volver arriba](#tabla-de-contenidos)** - -## Directivas -### Limitadas a 1 Por Archivo -###### [Style [Y070](#style-y070)] - - - Crea una directiva por archivo. Llama al archivo como la directiva. - - *¿Por qué?*: Es muy fácil colocar todas las directivas en un archivo, pero será más difícil de partir para ser compartida entre aplicaciones, módulos o para un simple módulo. - - *¿Por qué?*: Una directiva por archivo es fácil de mantener. - - ```javascript - /* evitar */ - /* directives.js */ - - angular - .module('app.widgets') - - /* directiva de órdenes que es específica del módulo de órdenes*/ - .directive('orderCalendarRange', orderCalendarRange) - - /* directiva de ventas que puede ser usada en algún otro lado a lo - largo de la aplicación de ventas */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* directiva de spinner que puede ser usada a lo largo de las - aplicaciones */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* detalles de implementación */ - } - - function salesCustomerInfo() { - /* detalles de implementación */ - } - - function sharedSpinner() { - /* detalles de implementación */ - } - ``` - - ```javascript - /* recomendado */ - /* calendarRange.directive.js */ - - /** - * @desc directiva de órdenes que es específica al módulo de órdenes en la compañía Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* detalles de implementación */ - } - ``` - - ```javascript - /* recomendado */ - /* customerInfo.directive.js */ - - /** - * @desc directiva de ventas que puede ser usada a lo largo de la aplicación de ventas en la compañía Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* detalles de implementación */ - } - ``` - - ```javascript - /* recomendado */ - /* spinner.directive.js */ - - /** - * @desc directiva de spinner que puede ser usada a lo largo de las aplicaciones en la compañía Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* detalles de implementación */ - } - ``` - - Nota: Hay muchas formas de llamar a las directivas, especialmente cuando pueden ser usadas en ámbitos específicos. Elige un nombre que tenga sentido para la directiva y que su archivo sea distintivo y claro. Hemos visto algunos ejemplos antes, pero veremos más en la sección de cómo nombrar. - -### Manipula el DOM en una Directiva -###### [Style [Y072](#style-y072)] - - - Cuando manipules DOM directamente, usa una directiva. Si hay alguna alternativa como usando CSS para cambiar los estilos o los [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) o [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), entonces úsalos en su lugar. Por ejemplo, si la directiva sólo muestra o esconde elementos, usa ngHide/ngShow. - - *¿Por qué?*: Manipular el DOM puede ser difícil de testear, debugear y normalmente hay mejores maneras (e.g. CSS, animations, templates) - -### Provee un Prefijo Único de Directiva -###### [Style [Y073](#style-y073)] - - - Proporciona un prefijo corto, único y descriptivo como `acmeSalesCustomerInfo` que se declare en el HTML como `acme-sales-customer-info`. - - *¿Por qué?*: El prefijo corto y único identifica el contexto de la directiva y el origen. Por ejemplo el prefijo `cc-` puede indicar que la directiva en particular es parte de la aplicación CodeCamper, mientras que `acme-` pudiera indicar que la directiva es de la compañía Acme. - - Nota: Evita `ng-` ya que está reservado para las directivas AngularJS. Estudia sabiamente las directivas usadas para evitar conflictos de nombres, como `ion-` de [Ionic Framework](http://ionicframework.com/). - -### Limitate a Elementos y Atributos -###### [Style [Y074](#style-y074)] - - - Cuando crees directivas que tengan sentido como elemento, restringe `E` (elemento personalizado) y opcionalmente restringe `A` (atributo personalizado). Generalmente, si puede ser su control propio, `E` es apropiado, La pauta general es permitir `EA` pero intenta implementarlo como un elemento cuando sea un elemento único y como un atributo cuando añada mejoras a su propio elemento existente en el DOM. - - *¿Por qué?*: Tiene sentido. - - *¿Por qué?*: Mientras permitamos que una directiva sea usada como una clase, si esa directiva realmente está actuando como un elemento, tiene sentido que sea un elemento, o al menos un atributo. - - Nota: En Angular 1.3+ EA es el valor por defecto - - ```html - -
- ``` - - ```javascript - /* evitar */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recomendado */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directivas y ControllerAs -###### [Style [Y075](#style-y075)] - - - Usa la sintaxis `controller as` con una directiva para ser consistente con el uso de `controller as` con los pares de vista y controlador. - - *¿Por qué?*: Tiene sentido y no es difícil. - - Nota: La siguiente directiva demuestra algunas de las formas en las que puedes usar el scope dentro del link y el controlador de una directiva, usando controllerAs. He puesto la template para dejarlo todo en un lugar. - - Nota: En cuanto a la inyección de dependencias, mira [Identificar Dependencias Manualmente](#manual-annotating-for-dependency-injection). - - Nota: Nótese que el controlador de la directiva está fuera del closure de la directiva. Este estilo elimina los problemas que genera la inyección de dependencias donde la inyección es creada en un código no alcanzable después del `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true // porque el scope is aislado - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Inyectando el $scope solo para comparación - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -###### [Style [Y076](#style-y076)] - - - Usa `bindToController = true` cuando uses `controller as` con una directiva cuando quieras asociar el scope exterior al scope del controller de la directiva. - - *¿Por qué?*: Lo hace más fácil a la hora de asociar el scope exterior al scope del controlador de la directiva. - - Nota: `bindToController` fue introducido en Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Resolviendo Promesas en un Controlador - -### Promesas de Activación de un Controlador -###### [Style [Y080](#style-y080)] - - - Resuelve la lógica de inicialización de un controlador en una función `activate`. - - *¿Por qué?*: Colocar la lógica de inicialización en un lugar consistente del controlador lo hace más fácil de localizar, más consistente de testear, y ayuda a evitar que la lógica de activación se propage a lo largo del controlador. - - *¿Por qué?*: El `activate` del controlador hace que la lógica para refrescar el controlador/Vista sea reutilizable, mantiene la lógica junta, lleva el usuario a la Vista más rápido, hace las animaciones más fáciles en `ng-view` o `ui-view` y lo hace más rápido a la vista del usuario. - - Nota: Si necesitas condicionalmente cancelar la ruta antes de empezar el controller, usa en su lugar [route resolve](#style-y081). - - ```javascript - /* evitar */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recomendado */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Resolución de Promesas en la Ruta -###### [Style [Y081](#style-y081)] - - - Cuando un controlador depende en una promesa a ser resuelta antes de que el controlador se active, resuelve esas dependencias en el `$routeProvider` antes de que la lógica del controlador sea ejecutada. Si necesitas condicionalmente cancelar una ruta antes de que el controlador sea activado, usa un route resolver. - - - Usa un route resolver cuando decidas cancelar la ruta antes de hacer la transición a la Vista. - - *¿Por qué?*: Un controlador puede requerir datos antes de que se cargue. Esos datos deben venir desde una promesa a través de una fábrica o de [$http](https://docs.angularjs.org/api/ng/service/$http). Usando un [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite que la promesa se resuelva antes de que la lógica del controlador se ejecute, así puedes tomar decisiones basándote en los datos de la promesa. - - *¿Por qué?*: El código se ejecuta después de la ruta y la función activate del controlador. La Vista empieza a cargar al instante. El bindeo de los datos se ejecutan cuando la promesa del activate se resuelva. Una animación de "Cargando" se puede mostrar durante la transición de la vista (via ng-view o ui-view) - - Nota: El código se ejecuta antes que la ruta mediante una promesa. Rechazar la promesa cancela la ruta. Resolverla hace que la nueva vista espere a que la ruta sea resuelta. Una animación de "Cargando" puede ser mostrada antes de que se resuelva. Si quieres que la Vista aparezca más rápido y no necesitas un checkpoint para decidir si puedes mostrar o no la view, considera la técnica [controller `activate`](#style-y080). - - ```javascript - /* evitar */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // sin resolver - vm.movies; - // resulta asincronamente - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Nota: El siguiente ejemplo muestra una ruta que cuando se resuelve apunta a una función, haciéndolo más fácil de debugear y más fácil de manejar la inyección de dependencias. - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviePrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Nota: El código del ejemplo de dependencia en `movieService` no se puede minimizar tal cual. Para detalles en cómo hacer este código sea minimizable, mira la sección en [inyección de dependencias](#anotación-manual-para-la-inyección-de-dependencias) y en [minimización y anotación](#minificación-y-anotación). - -**[Volver arriba](#tabla-de-contenidos)** - -## Anotación Manual para la Inyección de Dependencias - -### Insegura después de la Minificación -###### [Style [Y090](#style-y090)] - - - Evita usar la sintaxis acortada para declarar dependencias sin usar algún método que permita minificación. - - *¿Por qué?*: Los parámetros al componente (e.g. controller, factory, etc) se convertirán en variables acortadas. Por ejemplo, `common` y `dataservice` se convertirán `a` o `b` y no serán encontradas por AngularJS. - - ```javascript - /* evitar - not minification-safe*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Este código acortará las variables cuando se minimice y causará errores en tiempo de ejecución. - - ```javascript - /* evitar - not minification-safe*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Identifica Dependencias Manualmente -###### [Style [Y091](#style-y091)] - - - Usa `$inject` Para identificar manualmente las dependencias de tus componentes AngularJS. - - *¿Por qué?*: Esta técnica es la misma que se usa con [`ng-annotate`](https://github.com/olov/ng-annotate), la cuál recomiendo para automatizar la creación de dependencias minificadas de forma segura. Si `ng-annotate` detecta que la inyección ha sido hecha, no la duplicará. - - *¿Por qué?*: Esto salvaguarda tus dependencias de ser vulnerables de problemas a la hora de minimizar cuando los parámetros se acorten. Por ejemplo, `common` y `dataservice` se convertirán `a` o `b` y no serán encontradas por AngularJS. - - *¿Por qué?*: Evita crear dependencias en línea, ya que las listas largas pueden ser difícil de leer en el arreglo. También puede ser confuso que el arreglo sea una serie de cadenas mientras que el último componente es una función. - - ```javascript - /* evitar */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* evitar */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recomendado */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Nota: Cuando tu función está debajo de un return, $inject puede ser inalcanzable (esto puede pasar en una directiva). Puedes solucionarlo moviendo el $inject encima del return o usando la sintaxis de arreglo para inyectar. - - Nota: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) introduce una funcionalidad donde mueve `$inject` donde es alcanzable. - - ```javascript - // dentro de la definición de una directiva - function outer() { - return { - controller: DashboardPanel, - }; - - DashboardPanel.$inject = ['logger']; // Inalcanzable - function DashboardPanel(logger) { - } - } - ``` - - ```javascript - // dentro de la definición de una directiva - function outer() { - DashboardPanel.$inject = ['logger']; // alcanzable - return { - controller: DashboardPanel, - }; - - function DashboardPanel(logger) { - } - } - ``` - -### Identifica Manualmente Dependencias del Route Resolver -###### [Style [Y092](#style-y092)] - - - Usa $inject para identificar manualmente las dependencias de tu route resolver para componentes de AngularJS. - - *¿Por qué?*: Esta técnica separa la función anónima para el route resolver, haciendola más fácil de leer. - - *¿Por qué?*: Una declaración `$inject` puede ser fácilmente preceder al route resolver para hacer cualquier minificación de dependencias segura. - - ```javascript - /* recomendado */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Minificación y Anotación - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Usa [ng-annotate](//github.com/olov/ng-annotate) para [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) y comenta funciones que necesiten inyección de dependencias automatizadas usando `/** @ngInject */` - - *¿Por qué?*: Salvaguarda tu código de cualquier dependencia que pueda no estar usando prácticas de minificación segura. - - *¿Por qué?*: [`ng-min`](https://github.com/btford/ngmin) está obsoleto - - >Yo prefiero Gulp porque siento que es más fácil de escribir, leer, y debugear. - - El siguiente código no está usando minificación de dependencias segura. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Cuando el código de arriba es ejecutado a través de ng-annotate producirá la siguiente salida con la anotación `$inject` y será seguro para ser minificado. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Nota: Si `ng-annotate` detecta que la inyección ya ha sido hecha (e.g. `@ngInject` fué detectado), no duplicará el código de `$inject`. - - Nota: Al usar un route resolver puedes prefijar a la función del resolver con `/* @ngInject */` y producirá código propiamente anotado, manteniendo cualquier inyección de dependencias segura para ser minificada. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Nota: A partir de Angular 1.3 usa el párametro `ngStrictDi` de la directiva [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp). Al presentarse el injector será creado en modo "strict-di" causando que la aplicación falle al invocar funciones que no usan explícitamente anotación de funciones (éstas podrían no estar minificadas en forma segura). Información para debugear será mostrada en la consola para ayudar a rastrear el código infractor. - `` - -### Usa Gulp o Grunt para ng-annotate -###### [Style [Y101](#style-y101)] - - - Usa [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) o [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) en una tarea de construcción automática. Inyecta `/* @ngInject */` antes de cualquier función que tenga dependecias. - - *¿Por qué?*: ng-annotate atrapará la mayoría de las dependencias, pero algunas veces requiere indicios usando la sintaxis `/* @ngInject */`. - - El código siguiente es un ejemplo de una tarea de Gulp que usa - ngAnnotate - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Agrega la notación antes de ofuscar para que el código sea minificicado apropiadamente. - .pipe(ngAnnotate({ - // true ayuda a añadir @ngInject donde no es usado. Infiere. - // No funciona con resolve, así que tenemos que ser explícitos en ese caso - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Manejo de Excepciones - -### Decoradores -###### [Style [Y110](#style-y110)] - - - Usa un decorador o [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), en tiempo de configuración usando el servicio [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), en el servicio [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) para realizar acciones personalizadas cuando una excepción ocurra. - - *¿Por qué?*: Provee una manera consistente de manejar excepciones de Angular que no están siendo capturadas en tiempo de desarrollo o en tiempo de ejecución. - - Nota: Otra opción es sobreescribir el servicio en lugar de usar un decorador. Esto está bien, pero si quiere mantener el comportamiento por default y extenderlo se recomienda usar un decorador. - - ```javascript - /* recomendado */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Pudieramos agregar el error a la colección de un servicio, - * agregar los errores en el $rootScope, - * logear los errores a un servidor remoto, - * o logear localmente. O arrojarlos llanamente. Dependende totalmente de tí. - * arrojar excepción; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Cachadores de Excepciones -###### [Style [Y111](#style-y111)] - - - Crea una fábrica que exponga una interfaz para cachar y manejar excepciones elegantemente. - - *¿Por qué?*: Provee de una manera consistente de cachar excepciones que puedan ser arrojadas en tu código (e.g. durante llamadas XHR o promesas que fallaron). - - Nota: El cachador de excepciones es bueno para cachar y reaccionar a excepciones específicas de llamadas que tu sabes van a arrojar una. Por ejemplo, al hacer una llamada XHR para obtener datos desde un servicio web remoto y quieres cachar cualquier excepción de ese servicio y reaccionar únicamente. - - ```javascript - /* recomendado */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Errores de Ruta -###### [Style [Y112](#style-y112)] - - - Maneja y logea todos los errores de enrutamiento usando [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *¿Por qué?*: Provee una manera consistente de manejar todos los - errores de enrutamiento. - - *¿Por qué?*: Potencialmente provee una mejor experiencia de usuario si un error de enrutamiento ocurre y tu los rediriges a una pantalla amigable con más detalles u opciones de recuperación. - - ```javascript - /* recomendado */ - function handleRoutingErrors() { - /** - * Route cancellation: - * Cancelación de la Ruta: - * En un error de ruteo, ir al dashboard. - * Proveer una cláusula de salida si trata de hacerlo dos veces. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - var destination = (current && (current.title || current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + (rejection.msg || ''); - /** - * Optionally log using a custom service or $log. - * Opcionalmente logear usando un servicio personalizado o $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - } - ); - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Cómo Nombrar - -### Pautas para nombrar -###### [Style [Y120](#style-y120)] - - - Usa nombres consistentes para todos los componentes siguiendo un patrón que describa las características del componente y después (opcionalmente) su tipo. Mi patrón recomendado es `feature.type.js`. Hay dos nombres para la mayoría de los assets: - * el nombre del archivo (`avengers.controller.js`) - * el nombre del componente registrado en Angular (`AvengersController`) - - *¿Por qué?*: Las pautas de como nombrar nos ayudan a proveer una manera consistente para encontrar contenido en un vistazo. La Consistencia es vital dentro del proyecto. La Consistencia es importante dentro de un equipo. La Consistencia a lo largo de una compañía provee de una tremenda eficacia. - - *¿Por qué?*: Las pautas para nombrar deberían simplemente ayudarte a encontrar tu código rápidamente y hacerlo más fácil de entender. - -### Nombres de Archivo para Característica -###### [Style [Y121](#style-y121)] - - - Usa nombres consistentes para todos los componentes siguiendo un patrón que describa la característica o feature del componente y después (opcionalmente) su tipo. Mi patrón recomendado es `feature.type.js`. - - *¿Por qué?*: Provee de una manera consistente para identificar componentes rápidamente. - - *¿Por qué?*: Provee un patrón de coincidencia para tareas automatizadas. - - ```javascript - /** - * opciones comunes - */ - - // Controladores - avengers.js - avengers.controller.js - avengersController.js - - // Servicios/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recomendado - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // servicios/factories - logger.service.js - logger.service.spec.js - - // constantes - constants.js - - // definición de módulos - avengers.module.js - - // rutas - avengers.routes.js - avengers.routes.spec.js - - // configuración - avengers.config.js - - // directivas - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Nota: Otra convención común es nombrar archivos de controladores sin la palabra `controller` en el archivo tal como `avengers.js` en lugar de `avengers.controller.js`. Todas las demás convenciones todavía usan un sufijo del tipo. Los Controladores son el tipo más común de componente así que esto nos ahorra escribir y aún es fácilmente identificable. Yo recomiendo que elijas 1 convención y seas consistente dentro de tu equipo. - - ```javascript - /** - * recomendado - */ - // Controladores - avengers.js - avengers.spec.js - ``` - -### Nombres de Archivos de Prueba -###### [Style [Y122](#style-y122)] - - - Nombra especificaciones de pruebas de manera similar a la del componente que están probando con un sufijo de `spec`. - - *¿Por qué?*: Provee de una manera consistente de identificar componentes rápidamente. - - *¿Por qué?*: Provee de un patrón de coincidencia para [karma](http://karma-runner.github.io/) u otros test runners. - - ```javascript - /** - * recomendado - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Nombres de Controladores -###### [Style [Y123](#style-y123)] - - - Usa nombres consistentes para todos los controladores nombrados a partir de lo que hacen. Usa UpperCamelCase para controladores, ya que son constructores. - - *¿Por qué?*: Provee de una manera consistente de identificar y referenciar controladores rápidamente. - - *¿Por qué?*: UpperCamelCase es convencional para identificar objetos que pueden ser instanciados usando un constructor. - - ```javascript - /** - * recomendado - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengers', HeroAvengers); - - function HeroAvengers() { } - ``` - -### Sufijo para el Nombre del Controlador -###### [Style [Y124](#style-y124)] - - - Agrega el sufijo `Controller` al nombre del controlador o déjalo sin - sufijo. Escoge 1, no uses ambos. - - *¿Por qué?*: El sufijo `Controller` es usado más comúnmente y es más descriptivo explícitamente. - - *¿Por qué?*: Omitir el sufijo es más breve y el controlador es fácilmente identificable más seguido incluso sin el sufijo. - - ```javascript - /** - * recomendado: Opción 1 - */ - - // avengers.controller.js - angular - .module - .controller('Avengers', Avengers); - - function Avengers() { } - ``` - - ```javascript - /** - * recomendado: Opción 2 - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Nombres de Fábricas -###### [Style [Y125](#style-y125)] - - - Usa nombres consistentes para todas las fábricas nombradas a partir de lo que hacen. Usa camel-casing para los servicios y las fábricas. - - *¿Por qué?*: Provee una manera consistente de identificar y referenciar fábricas rápidamente. - - ```javascript - /** - * recomendado - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - -### Nombres para Directivas -###### [Style [Y126](#style-y126)] - - - Usa nombres consistentes para todas las directivas usando camel-case. Usa un prefijo corto para describir el área a la que la directiva pertenece (algunos ejemplos son un prefijo según la compañía o un prefijo según el proyecto). - - *¿Por qué?*: Provee una manera consistente de identificar y referenciar componentes rápidamente. - - ```javascript - /** - * recomendado - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // el uso es - - function xxAvengerProfile() { } - ``` - -### Módulos -###### [Style [Y127](#style-y127)] - - - Cuando haya múltiples módulos, el archivo del módulo principal es nombrado `app.module.js` mientras que otros módulos que dependan de él son nombrados a partir de lo que ellos representan. Por ejemplo, un módulo de admin es nombrado `admin.module.js`. Los nombres de los módulos registrados serán respectivamente `app` y `admin`. - - *¿Por qué?*: Provee consistencia para múltiples módulos de aplicación, y para poder expandirse a aplicaciones más grandes. - - *¿Por qué?*: Provee una manera fácil de usar tareas automatizadas para cargar todas las definiciones de módulos primero, y luego todos los otros archivos de angular (agrupación). - -### Configuración -###### [Style [Y128](#style-y128)] - - - Separa la configuración de un módulo en un archivo propio nombrado a partir del nombre del módulo. Un archivo de configuración para el módulo principal de `app` es nombrado `app.config.js` (o simplemente `config.js`). La configuración para un módulo llamado `admin.module.js` es nombrada `admin.config.js`. - - *¿Por qué?*: Separa la configuración de la definición del módulo, componentes y código activo. - - *¿Por qué?*: Provee un lugar identificable para establecer configuración para un módulo. - -### Rutas -###### [Style [Y129](#style-y129)] - - - Separa la configuración de la ruta en un archivo propio. Algunos ejemplos pueden ser `app.route.js` para el módulo principal y `admin.route.js` para el módulo admin `admin`. Incluso en aplicaciones pequeñas prefiero esta separación del resto de la configuración. - -**[Volver arriba](#tabla-de-contenidos)** - -## Estructura de la Aplicación El Principio LIFT -### LIFT -###### [Style [Y140](#style-y140)] - - - Estructura tu aplicación de tal manera que puedas Localizar (`L`ocate) tu código rápidamente, Identificar (`I`dentify) el código de un vistazo, mantener la estructura más plana (`F`lattest) que puedas, y Trata (`T`ry) de mantenerte DRY. La estructura debe de seguir estas 4 pautas básicas. - - *¿Por qué LIFT?*: Provee una estructura consistente que escala bien, es modular, y hace más fácil incrementar la eficiencia de los desarrolladores al encontrar código rápidamente. Otra manera de checar la estructura de tu aplicación es preguntarte a ti mismo: ¿Qué tan rápido puede abrir y trabajar en todos los archivos relacionados a una caracteristica? - - Cuando encuentro que mi estructura no se siente cómoda, regreso y reviso estas pautas LIFT - - 1. `L`ocating - Localizar nuestro código es fácil - 2. `I`dentify - Identificar código de un vistazo - 3. `F`lat - Estructura plana tanto como sea posible - 4. `T`ry - Tratar de mantenerse DRY (Don't Repeat Yourself) or T-DRY - -### Localizar -###### [Style [Y141](#style-y141)] - - - Has que la localización tu código sea intuitivo, simple y rápido. - - *¿Por qué?*: Encuentro que esto es super importante para un proyecto. Si el equipo no puede encontrar los archivos en los que necesita trabajar rápidamente, no podrán trabajar tan eficientemente como sea posible, y la estructura necesita cambiar. Puede que no conozcas el nombre del archivo o donde están sus archivos relacionados, así que poniéndolos en las locaciones más intuitivas y cerca de los otros ahorra mucho tiempo. Una estructura de directorios descriptiva puede ayudar con esto. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identificar -###### [Style [Y142](#style-y142)] - - - Cuando miras en un archivo deberías saber instantáneamente qué contiene y qué representa. - - *¿Por qué?*: Gastas menos tiempo buscando y urgando por código, y es más eficiente. Si esto significa que quieres nombres de archivos más largos, entonces que así sea. Se descriptivo con los nombres de los archivos y mantén el contenido del archivo a exactamente 1 componente. Evita archivos con múltiples controladores, o una mezcla. Hay excepciones a la regla de 1 por archivo cuando tengo un conjunto de pequeñas features que están relacionadas unas con otras, aún así son fácilmente identificables. - -### Estructura Plana -###### [Style [Y143](#style-y143)] - - - Mantén una estructura de directorios plana tanto como sea posible. Cuando llegues a un total de 7+ archivos, comienza a considerar separación. - - *¿Por qué?*: Nadie quiere buscar en 7 niveles de directorios por un arhivo. Piensa en los menús de los sitios web … cualquiera más profundo que 2 debería ser seriamente considerado. En una estructura de directorios no hay una regla dura o rápida en cuanto a un número, pero cuando un directorio tiene de 7 a 10 archivos, tal vez ese sea el momento para empezar a crear subdirectorios. Básate en tu nivel de confort. Usa una estructura más plana hasta que haya un valor obvio (para ayudar al resto de LIFT) en crear un nuevo directorio. - -### T-DRY (Try to Stick to DRY - Trata de Apegarte a DRY) -###### [Style [Y144](#style-y144)] - - - Se DRY, pero no te vuelvas loco y sacrifiques legibilidad. - - *¿Por qué?*: Ser DRY es importante, pero no crucial si sacrifica otras partes de LIFT, es por eso que lo llamo T-DRY. No quiero escribir session-view.html por una vista porque, obviamente es una vista. Si no es obvio o por convención, entonces la nombro así. - -**[Volver arriba](#tabla-de-contenidos)** - -## Estructura de la Aplicación - -### Pautas Universales -###### [Style [Y150](#style-y150)] - - - Ten una visión de implementación de corto y largo plazo. En otras palabras, empieza con poco pero ten en mente hacia donde se dirige la aplicación. Todo el código de la aplicación va en el directorio raíz llamado `app`. Todo el contenido es separado en 1 característica por archivo. Cada controlador, servicio, módulo, vista tiene su propio archivo. Todos los vendor scripts de terceros son almacenados en otro directorio raíz y no en el directorio `app`. Si yo no lo escribí no los quiero saturando mi aplicación (`bower_components`, `scripts`, `lib`). - - Nota: Encuentra más detalles y el razonamiento detrás de esta estructura en [este post original sobre la estructura de una aplicación](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Coloca los componentes que definen el layout universal de la aplicación en un directorio llamado `layout`. Estos pueden incluir una vista caparazón y un controlador que actúen como un contenedor para la aplicación, navegación, menús, áreas de contenido, y otras regiones. - - *¿Por qué?*: Organiza todo el layout en un lugar único reusado a lo largo de la aplicación. - -### Estructura de Carpetas-por-Característica -###### [Style [Y152](#style-y152)] - - - Crea carpetas llamadas de acuerdo al característica que representan. Cuando una carpeta crezca para contener más de 7 archivos, comienza a considerar la creación de una carpeta para ellos. Tu límite puede ser diferente, así que ajusta de acuerdo a tus necesidades. - - *¿Por qué?*: Un desarrollador puede localizar el código, identificar cada qué representa cada archivo de un vistazo, la estructura es tan plana como puede ser, y no hay nombres repetidos o redundantes. - - *¿Por qué?*: Las pautas LIFT estarán cubiertas. - - *¿Por qué?*: Ayuda a evitar que la aplicación se sature a través de organizar el contenido y conservarlo alineado con las pautas LIFT. - - *¿Por qué?*: Cuando hay demasiados archivos (10+) localizarlos es más fácil con una estructura de directorios consistente y más difíciles en una estructura plana. - - ```javascript - /** - * recomendado - */ - - app/ - app.module.js - app.config.js - app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - session-detail.html - session-detail.controller.js - ``` - - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Nota: No estructures tu aplicación usando directorios-por-tipo. Esto requiere mover múltiples directorios cuando se está trabajando en una característica y se vuelve difícil de manejar conforme la aplicación crece a 5, 10 o 25+ vistas y controladores (y otras características), lo que lo hace más difícil que localizar archivos en una aplicación estructura en directorios-por-característica. - - ```javascript - /* - * evita - * Alternativa directorios-por-tipo - * Yo recomiendo "directorios-por-característica", en su lugar. - */ - - app/ - app.module.js - app.config.js - app.routes.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.j - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Modularidad - -### Muy Pequeños, Módulos Autocontenidos -###### [Style [Y160](#style-y160)] - - - Crea módulos pequeños que encapsulen una responsabilidad. - - *¿Por qué?*: Aplicaciones modulares hace más fácil el plug and go ya que permiten a los equipos de desarrollo construir porciones verticales de la aplicación y lanzarlas incrementalmente. Esto significa que podemos conectar nuevas características conforme las desarrollamos. - -### Crea un Módulo App -###### [Style [Y161](#style-y161)] - - - Crea una módulo raíz de aplicación cuyo rol sea unir todos los módulos y características de tu aplicación. Nombra éste de acuerdo a tu aplicación. - - *¿Por qué?*: Angular incentiva la modularidad y patrones de separación. Crear un módulo raíz de aplicación cuyo rol es atar otros módulos juntos provee una manera muy directa de agregar o remover módulos de tu aplicación. - -### Mantén el Módulo App Delgado -###### [Style [Y162](#style-y162)] - - - Solo coloca lógica para unir la aplicación en el módulo app. Deja las características en sus propios módulos. - - *¿Por qué?*: Agregar roles adicionales a la aplicación raíz para obtener datos remotos, mostrar vistas, u otra lógica no relaciona a la unión de la aplicación enturbia el módulo app y hace ambos conjuntos de características difíciles de reusar y apagar. - - *¿Por qué?*: El módulo app se convierte en el manifiesto que describe qué módulos definen la aplicación. - -### Áreas de Features son Módulos -###### [Style [Y163](#style-y163)] - - - Crea módulos que representen áreas de características, como el layout, servicios reusables y compartidos, dashboards, y características específicás de la aplicación (e.g. customers, admin, sales). - - *¿Por qué?*: Módulos autocontenidos pueden ser agregados a la aplicación con poca o sin ninguna fricción. - - *¿Por qué?*: Sprints o iteraciones pueden enfocarse en áreas de características y encendarlas al final del sprint o iteración. - - *¿Por qué?*: Separar áreas de características en módulos hace más fácil testear módulos en aislamiento y reusar código. - -### Bloques Reusables son Módulos -###### [Style [Y164](#style-y164)] - - - Crea módulos que representen bloques de la aplicación reusables para servicios cómunes como manejo de excepciones, logeo, diagnóstico, seguridad, y almacenamiento local de datos. - - *¿Por qué?*: Este tipo de características son necesarias en muchas aplicaciones, así que mantenerlas separadas en sus propios módulos pueden ser genéricas de aplicación y pueden ser reusadas a lo largo de varias aplicaciones. - -### Dependencias de Módulos -###### [Style [Y165](#style-y165)] - - - El módulo raíz de la aplicación depende de módulos de características específicas y cualquier módulo compartido o reusable. - - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *¿Por qué?*: El módulo principal de la aplicación contiene un manifiesto rápidamente identificable de las características de la aplicación. - - *¿Por qué?*: Cada área de características contiene un manifiesto de lo que depende, así que puede ser extraído como dependencia en otras aplicaciones y seguir funcionando. - - *¿Por qué?*: Características internas de la aplicación como servicios de datos compartidos se hacen fácil de localizar y compartir desde `app.core` (elije tu nombre favorito para este módulo). - - Nota: Esta es una estrategia para consistencia. Hay muy buenas opciones aquí. Escoge una que sea consistente, que siga las reglas de dependencias de AngularJS, y que sea fácil de mantener y escalar. - - > Mis estructuras varían ligeramente entre proyectos pero todas ellas siguen estas pautas para estructuras y modularidad. La implementación puede variar dependiendo de las características y el equipo. En otras palabras, no te quedes colgado en una estructura igual pero justifica tu estructura usando consistencia, mantenibilidad, y eficacia en mente. - - > En una aplicación pequeña, también puedes considerar poner todas las dependencias compartidas en el módulo principal dónde los módulos de características no tienen dependencias directas. Esto hace más fácil mantener aplicaciones pequeñas, pero hace más difícil el reusar módulos fuera de esta aplicación. - -**[Volver arriba](#tabla-de-contenidos)** - -## Lógica de Arranque - -### Configuración -###### [Style [Y170](#style-y170)] - - - Inyecta código dentro de [module configuration](https://docs.angularjs.org/guide/module#module-loading-dependencies) que necesite ser configurado antes de correr la aplicación angular. Candidatos ideales incluyen providers y constantes. - - *¿Por qué?*: Esto hace más fácil tener menos lugares para la configuración. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Bloques Run -###### [Style [Y171](#style-y171)] - - - Cualquier código que necesite ser ejecutado cuando una aplicación arranca debe ser declarado en una fábrica, ser expuesto a través de una función, o inyectado en el [bloque run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *¿Por qué?*: Código que está directamente en un bloque run puede ser difícil de testear. Colocarlo en una fábrica lo hace fácil de abstraer y mockear. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Servicios Envoltorios $ de Angular - -### $document y $window -###### [Style [Y180](#style-y180)] - - - Usa [`$document`](https://docs.angularjs.org/api/ng/service/$document) y [`$window`](https://docs.angularjs.org/api/ng/service/$window) en lugar de `document` y `window`. - - *¿Por qué?*: Estos servicios son envueltos por Angular y son más fáciles de testear en lugar de usar document y window en las pruebas. Esto te ayuda a evitar que tener que mockear document y window tu mismo. - -### $timeout y $interval -###### [Style [Y181](#style-y181)] - - - Usa [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) y [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) en lugar de `setTimeout` y `setInterval` . - - *¿Por qué?*: Estos servicios están envueltos por Angular y son más fáciles de testear y manejar el ciclo digest de Angular así que mantienen el bindeo de los datos en sincronización. - -**[Volver arriba](#tabla-de-contenidos)** - -## Pruebas -Las pruebas unitarias ayudan a mantener el código limpio, así que incluyo algunas de mis recomendaciones en los fundamentos del testeo unitario con links para mayor información. - -### Escribe Pruebas con Historias -###### [Style [Y190](#style-y190)] - - - Escribe un conjunto de pruebas para cada historia. Comienza con un test vacío y llénalo conforme escribas el código para la historia. - - *¿Por qué?*: Escribir descripciones para la prueba ayuda a definir claramente qué es lo que tu historia hará, qué no hará, y cómo puedes medir el éxito. - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // y así - ``` - -### Librería para las Pruebas -###### [Style [Y191](#style-y191)] - - - Usa [Jasmine](http://jasmine.github.io/) o [Mocha](http://mochajs.org) para las pruebas unitarias. - - *¿Por qué?*: Ambas Jasmine y Mocha son usadas ampliamente por la comunidad de AngularJS. Ambas son estables, bien mantenidas, y proveen de características de pruebas robustas. - - Nota: Cuando uses Mocha, también considera elegir una librería como [Chai](http://chaijs.com). - -### Test Runner -###### [Style [Y192](#style-y192)] - - - Usa [Karma](http://karma-runner.github.io) como test runner. - - *¿Por qué?*: Karma es fácil de configurar para correr una vez o automáticamente cuando cambias tu código. - - *¿Por qué?*: Karma encaja en tu proceso de Integración Continua fácilmente por sí sola o a través de Grunt o Gulp. - - *¿Por qué?*: Algunos IDE's están comenzando a integrarse con Karma, tal como [WebStorm](http://www.jetbrains.com/webstorm/) y [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *¿Por qué?*: Karma funciona bien con líderes de automatización de tareas tales como [Grunt](http://www.gruntjs.com) (con [grunt-karma](https://github.com/karma-runner/grunt-karma)) y [Gulp](http://www.gulpjs.com) (con [gulp-karma](https://github.com/lazd/gulp-karma)). - -### Stubear y Espíar -###### [Style [Y193](#style-y193)] - - - Usa [Sinon](http://sinonjs.org/) para el stubeo y espíar. - - *¿Por qué?*: Sinon funciona bien con ambos Jasmine y Mocha y extiende las características de stubeo y espío que ellos ofrecen. - - *¿Por qué?*: Sinon hace más fácil cambiar entre Jasmine y Mocha, si quieres probar ambos. - -### Headless Browser -###### [Style [Y194](#style-y194)] - - - Usa [PhantomJS](http://phantomjs.org/) para correr tus pruebas en un servidor. - - *¿Por qué?*: PhantomJS es un navegador headless que ayuda a correr las pruebas necesitar una navegador "visual". Así que no necesitas instalar Chrom, Safari u otros navegadores en tu servidor. - - Nota: Aún debes testear en todos los navegadores de tu entorno, así como sea apropiado para tu audiencia meta. - -### Ánalisis de Código -###### [Style [Y195](#style-y195)] - - - Corre JSHint en tus pruebas. - - *¿Por qué?*: Las pruebas son código. JSHint puede ayudar a identificar problemas en la calidad del código que pueden causar que tus pruebas funcionen inapropiadamente. - -### Mitiga Palabras Globales dentro de las Reglas de JSHint en las Pruebas -###### [Style [Y196](#style-y196)] - - - Relaja las reglas en tu código de prueba para permitir palabras globales comúnes como `describe` y `expect`. - - *¿Por qué?*: Tus pruebas son código y requieren la misma atención y reglas de calidad de código que todo tu código de producción. Sin embargo, variables globales usadas por el framework para pruebas, por ejemplo, puede ser relajado al incluir esto en tus specs de prueba. - - ```javascript - /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Organizando las Pruebas -###### [Style [Y197](#style-y197)] - - - Coloca archivos de pruebas unitarias (specs) lado a lado con tu código del cliente. Coloca tus specs que cubren la integración con el servidor o que prueban múltiples componentes en un directorio `tests` separado. - - *¿Por qué?*: Las Pruebas Unitarias tiene una correlación directa con un componente y archivo específico en tu código fuente. - - *¿Por qué?*: Es más fácil mantenerlas actualizadas ya que siempre están a la vista. Al escribir código ya sea que realices TDD o pruebes durante el desarrollo o después del desarrollo, los specs están lado a lado y nunca fuera de la vista o de la mente, así es más probable que sean mantenidas lo cual ayuda a mantener la cobertura de pruebas. - - *¿Por qué?*: Cuando actualices código fuente es más fácil ir y actualizar las pruebas al mismo tiempo. - - *¿Por qué?*: Colocarlas lado a lado hace más fácil encontrarlas y fácil de moverlas con el código fuente si mueves la fuente. - - *¿Por qué?*: Tener el spec cerca hace más fácil al lector del código fuente aprender cómo se supone que el componente es usado y descubrir sus propias limitaciones. - - *¿Por qué?*: Separar specs para que no estén un build de distribución es fácil con grunt o gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Animaciones - -### Uso -###### [Style [Y210](#style-y210)] - - - Usa sutiles [animaciones con AngularJS](https://docs.angularjs.org/guide/animations) para hacer transiciones entre estados en vistas y elementos visuales primarios. Incluye el [módulo ngAnimate](https://docs.angularjs.org/api/ngAnimate). Las 3 claves son sutil, fluido, transparente. - - *¿Por qué?*: Animaciones sutiles pueden mejorar la Experiencia de Usuario cuando son usadas apropiadamente. - - *¿Por qué?*: Animaciones sutiles pueden mejorar el rendimiento percibido como una transición de vista. - -### Sub Segundos -###### [Style [Y211](#style-y211)] - - - Usa duraciones cortas para las animaciones. Yo generalmente empiezo con 300ms y ajusto hasta que es apropiado. - - *¿Por qué?*: Animaciones largas pueden tener el efecto contrario en la Experiencia de Usuario y el rendimiento percibido al dar la apariencia de una aplicación lenta. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Usa [animate.css](http://daneden.github.io/animate.css/) para animaciones convencionales. - - *¿Por qué?*: Las animaciones que animate.css provee son rápidas, fluidas, y fáciles de agregar en tu aplicación. - - *¿Por qué?*: Provee consistencia en tus animaciones. - - *¿Por qué?*: animate.css está ampliamente usado y testeado. - - Nota: Ve este [ excelente post de Matias Niemelä sobre animaciones AngularJS](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Volver arriba](#tabla-de-contenidos)** - -## Comentarios - -### jsDoc -###### [Style [Y220](#style-y220)] - - - Si planeas producir documentación, usa la sintaxis [`jsDoc`](http://usejsdoc.org/) para documentar nombres de funciones, descripción, parámetros y devoluciones. Usa `@namespace` y `@memberOf` para igualar la estructura de tu aplicación. - - *¿Por qué?*: Puedes generar (y regenerar) documentación desde tu código, en lugar de escribirla desde cero. - - *¿Por qué?*: Provee consistencia al usar una herramienta industrial común. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## JS Hint - -### Usa un Archivo de Opciones -###### [Style [Y230](#style-y230)] - - - Usa JS Hint para resaltar problemas en tu JavaScript y asegurate de personalizar el arhivo de opciones de JS Hint e incluirlo en el control de versiones. Ve los [JS Hint docs](http://www.jshint.com/docs/) para detalles sobre estas opciones. - - *¿Por qué?*: Provee una primera alerta antes de hacer commit de cualquier código al control de versiones. - - *¿Por qué?*: Provee consistencia a lo largo de tu equipo. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Constantes - -### Globales de Vendor -###### [Style [Y240](#style-y240)] - - - Crea una Constante de Angular para variables globales en librerías vendor. - - *¿Por qué?*: Provee una manera de inyectar librerías vendor que de otra manera son globales. Esto mejora la testeabilidad al permitirte saber más fácilmente cuáles son las dependencias de tus componentes (evita abstraciones malformadas). También te permite mockear estas dependencias, cuando tiene sentido. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Usa constantes para valores que no cambian y no vienen de otro servicio. Cuando las constantes son usadas solo por para un módulo que pueda ser reutilizado en múltiples aplicaciones, coloca las constantes en un archivo por módulo nombrado a partir del módulo. Hasta que esto sea requerido, mantén las constantes en el módulo principal en un archivo `constants.js`. - - *¿Por qué?*: Un valor que puede cambiar, incluso infrecuentemente, debería ser obtenido desde un servicio así no tendrás que cambiar el código fuente. Por ejemplo, una url para un servicio de datos puede ser colocada en una constante pero un mejor lugar sería cargarla desde un servicio web. - - *¿Por qué?*: Las Constantes pueden ser inyectadas en cualquier componente de angular, incluyendo providers. - - *¿Por qué?*: Cuando una aplicación es separada en módulos que pueden ser reutilizados en otras aplicaciones, cada módulo autónomo debería ser capaz de operar por sí mismo incluyendo cualquier constante de la cual dependa. - - ```javascript - // Constantes usadas por la aplicación entera - angular - .module('app.core') - .constant('moment', moment); - - // Constantes usadas solo por el módulo de ventas - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Plantillas y Snippets -Usa Plantillas o snippets para ayudarte a seguir estilos consistentes o patrones. Aquí hay plantillas y/o snippets para algunos de los editores de desarrollo web e IDEs. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Snippets de Angular que siguen estos estilos y directrices. - - - Descarga los [snippets de Angular para Sublime](../assets/sublime-angular-snippets?raw=true) - - Colócalos en tu directorio de Packages - - Reinicia Sublime - - En un archivo de JavaScript escibe estos comandos seguidos de un `TAB` - - ```javascript - ngcontroller // crea un controlador de Angular - ngdirective // crea una directiva de Angular - ngfactory // crea una factory de Angular - ngmodule // crea un módulo de Angular - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Plantillas de Angular que siguen estos estilos y directrices pueden ser encontrados en [SideWaffle](http://www.sidewaffle.com) - - - Descarga la extensión [SideWaffle](http://www.sidewaffle.com) de Visual Studio (archivo vsix) - - Corre el archivo vsix - - Reinicia Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Snippets y arhicos de Angular que siguen estos estilos y directrices. Puedes importarlos en tus configuraciones de WebStorm: - - - Descarga los [snippets y plantillas de Angular para WebStorm](assets/webstorm-angular-file-template.settings.jar?raw=true) - - Abre WebStorm y ve al menú `File` - - Elije la opción `Import Settings` - - Selecciona el archivo y da click en `OK` - - En un archivo de JavaScript escribe estos comandos seguidos de un `TAB`: - - ```javascript - ng-c // crea un controlador de Angular - ng-f // crea una factory de Angular - ng-m // crea un módulo de Angular - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Generador de Yeoman -###### [Style [Y260](#style-y260)] - -Puedes usar el [generador de yeoman HotTowel](http://jpapa.me/yohottowel) para crear una aplicación que te sirve como punto de inicio en Angular que sigue esta guía de estilos. - -1. Instala generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Crea un nuevo directorio y entra en el - - ``` - mkdir myapp - cd myapp - ``` - -3. Corre el generador - - ``` - yo hottowel helloWorld - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Enrutamiento -Enrutamiento del lado del Cliente es importante para crear un flujo de navegación entre vistas y vistas de composición que están hechas de muchas pequeñas plantillas y directivas. - -###### [Style [Y270](#style-y270)] - - - Usa el [AngularUI Router](http://angular-ui.github.io/ui-router/) para ruteo del lado del cliente. - - *¿Por qué?*: UI Router ofrece todas las características del router de Angular mas algunas adicionales incluyendo rutas anidadas y estados. - - *¿Por qué?*: La sintaxis es bastante similar al router de Angular y es fácil de migrar al UI Router. - -###### [Style [Y271](#style-y271)] - - - Define rutas para vistas en el módulo dónde éstas existen. Cada módulo debería contener las rutas para las vistas en ese módulo. - - *¿Por qué?*: Cada módulo debe ser capaz de funcionar por sí mismo. - - *¿Por qué?*: Al remover un módulo o al agregar un módulo, la aplicación solo contendrá rutas que apunten a las vistas existentes. - - *¿Por qué?*: Esto hace más fácil habilitar o deshabilitar porciones de una aplicación sin preocuparse de rutas huérfanas. - -**[Volver arriba](#tabla-de-contenidos)** - -## Automatización de Tareas -Usa [Gulp](http://gulpjs.com) o [Grunt](http://gruntjs.com) para crear tareas automatizadas. Gulp deriva a código sobre configuración mientras que Grunt deriva a configuración sobre código. Personalmente yo prefiero Gulp ya que se siente más fácil de leer y escribir, pero ambos son excelentes. - -###### [Style [Y400](#style-y400)] - - - Usa automatización de tareas para listar archivos que definan módulos `*.module.js` antes que otros archivos de JavaScript en la aplicación. - - *¿Por qué?*: Angular necesita la definición de módulos para ser registrados antes de que sean usados. - - *¿Por qué?*: Nombra módulos con un patrón específico como `*.module.js` hace más fácil tomarlos con un glob y listarlos primero. - - ```javascript - var clientApp = './src/client/app/'; - - // Siempre toma archivos de módulos primero - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Volver arriba](#tabla-de-contenidos)** - -## Angular docs -Para cualquier otra cosa, refiérete a la API, mira la [documentación de Angular](//docs.angularjs.org/api). - -## Contribuyendo - -Primero abre un issue para discutir cambios/agregados potenciales. Si tienes preguntas acerca de esta guía, siéntete libre de dejarlas como issues en el repositorio. Si encuentras un typo, crea un pull request. La idea es mantener el contenido actualizado y usar las features de github para ayudar a contar la historia con issues y PR’s, los cuales pueden ser encontrados a través de google. ¿Por qué? Porque las probabilidades son que si tu tienes una pregunta, !alguien más también! Puedes aprender más aquí de cómo contribuir. - -*Al contribuir a este repositorio estás acordando hacer tu contenido disponible a ser parte de la licencia de este repositorio.* - -### Proceso - 1. Discute los cambios en un issue de Github. - 2. Abre un Pull Request sobre la rama de develop, referencia el issue, y explica el cambio y por qué agrega valor. - 3. El Pull Request será evaluado y ya sea mergeado o declinado. - -## Licencia - -_tldr; Usa esta guía. Reconocimientos son apreciados._ - -### (The MIT License) - -Copyright (c) 2014 [John Papa](http://johnpapa.net) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Volver arriba](#tabla-de-contenidos)** diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md deleted file mode 100644 index b3fae0d8..00000000 --- a/a1/i18n/fr-FR.md +++ /dev/null @@ -1,3185 +0,0 @@ -# Charte stylistique Angular - -*Guide de style subjectif pour Angular par [@john_papa](//twitter.com/john_papa)* - -Si vous cherchez un guide de style pour la syntaxe, les conventions, et la structuration d'applications Angular, alors vous êtes au bon endroit. Ces styles sont basés sur mon expérience de développement avec [Angular](//angularjs.org), mes présentations, [mes cours sur Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) et mon travail au sein de diverses équipes. - -Le but de ce guide de style est de proposer des conseils sur le développement d'applications Angular en exposant les conventions que j'utilise et plus important encore, pourquoi je les ai choisies. - ->Si vous appréciez ce guide, visitez mon cours [Angular Patterns: Clean Code](http://jpapa.me/ngclean) sur Pluralsight qui va de pair avec ce guide. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Remerciements individuels et à la communauté -Ne jamais travailler en vase clos. J'ai trouvé que la communauté Angular est une incroyable communauté dont les membres ont à cœur de partager leurs expériences. Ainsi, avec mon ami et expert d'Angular, Todd Motto, nous avons collaboré sur de nombreux styles et conventions. Nous sommes d'accord sur la plupart, et nous divergeons sur d'autres. Je vous encourage à visiter [le guide de style de Todd](https://github.com/toddmotto/angularjs-styleguide) pour vous faire votre propre avis sur son approche et en quoi elle diverge. - -Beaucoup de mes styles proviennent des nombreuses séances de pair programming avec [Ward Bell](http://twitter.com/wardbell). Mon ami Ward a assurément contribué à influencer l'évolution ultime de ce guide. - -## Visualiser les styles dans une application d'exemple -Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est utile de pouvoir les visualiser dans la pratique. Ce guide est accompagné par une application d'exemple qui suit ces styles et ces modèles. Vous pouvez trouver l'[application d'exemple (intitulée modular) ici](https://github.com/johnpapa/ng-demos) dans le répertoire `modular`. Vous pouvez librement le récupérer, le cloner, ou le *forker*. [Les instructions pour l’exécuter sont contenues dans ce readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -## Traductions -[Les traductions de ce guide stylistique pour Angular](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) sont maintenues par la communauté et peuvent être trouvées ici. - -## Table des matières - - 1. [Responsabilité Unique](#responsabilité-unique) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Contrôleurs](#contrôleurs) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Services de données](#services-de-données) - 1. [Directives](#directives) - 1. [Résolution de promesses pour un contrôleur](#résolution-des-promises-pour-un-contrôleur) - 1. [Annoter manuellement les dépendances à injecter](#annotation-manuelle-pour-linjection-de-dépendances) - 1. [Minification et annotation](#minification-et-annotation) - 1. [Gestion des exceptions](#gestion-des-exceptions) - 1. [Nommage](#nommage) - 1. [Architecture L.I.F.T.](#architecture-lift) - 1. [Architecture de l'application](#architecture-de-lapplication) - 1. [Modularité](#modularité) - 1. [Logique d'initialisation](#logique-dinitialisation) - 1. [Services $ d'Angular](#les-services--dangular) - 1. [Tests](#tests) - 1. [Animations](#animations) - 1. [Commentaires](#commentaires) - 1. [JSHint](#jshint) - 1. [JSCS](#jscs) - 1. [Constantes](#constantes) - 1. [Templates et snippets](#modèles-de-fichiers-et-snippets) - 1. [Générateur Yeoman](#générateur-yeoman) - 1. [Routage](#routage) - 1. [Automatisation des tâches](#automatisation-des-tâches) - 1. [Filtres](#filtres) - 1. [Documentation Angular](#documentation) - 1. [Contribuer](#contribuer) - 1. [Licence](#license) - -## Responsabilité unique - -### Règle d'unicité -###### [Style [Y001](#style-y001)] - - - Définissez un composant par fichier. - - L'exemple suivant définit le module `app` et ses dépendances, définit un contrôleur, et définit une factory le tout dans le même fichier. - - ```javascript - /* à éviter */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Les même composants sont maintenant séparés dans leurs propres fichiers. - - ```javascript - /* recommandé */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommandé */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommandé */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## IIFE -### Les fermetures (*closures*) JavaScript -###### [Style [Y010](#style-y010)] - - - Encapsulez les composants Angular dans une *Immediately Invoked Function Expression* (IIFE) ou Expression de Fonction Immédiatement Invoquée. - - *Pourquoi ?* : Une IIFE supprime les variables du scope global. Cela aide à éviter que les déclarations de variables et de fonctions ne vivent plus longtemps que prévu dans le scope global, ce qui aide aussi à éviter les collisions de variables. - - *Pourquoi ?* : Lorsque votre code est minifié et embarqué dans un unique fichier pour le déploiement dans un serveur de production, vous pouvez avoir des collisions de variables et de nombreuses variables globales. Une IIFE vous protège contre ces dernières en fournissant un scope différent pour chaque fichier. - - ```javascript - /* à éviter */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // la fonction logger est ajoutée en tant que variable globale - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // la fonction storage est ajoutée en tant que variable globale - function storage() { } - ``` - - ```javascript - /** - * recommandé - * - * plus aucune variable globale ne reste après. - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Note : Pour des raisons de concision seulement, le reste des exemples de ce guide ne seront pas écrits avec la syntaxe IIFE. - - - Note : Les IIFE empêchent le code de test d'atteindre des membres privés, comme les expressions régulières ou les fonctions utilitaires (*helpers*), qu'il est souvent meilleur de tester indépendamment. Cependant, vous pouvez les tester à travers les membres accessibles ou en les exposant à travers leur propre composant. Par exemple en les plaçant dans leur propre factory ou constante. - -**[Retour en haut de page](#table-des-matières)** - -## Modules - -### Éviter les collisions de nommage -###### [Style [Y020](#style-y020)] - - - Utilisez des conventions de nommages uniques avec des séparateurs pour les sous-modules. - - *Pourquoi ?* : Les noms uniques aident à éviter les collisions de nom de module. Les séparateurs aident à définir les modules et la hiérarchie de leurs sous-modules. Par exemple, `app` pourrait être le module principal (*root*) tandis que `app.dashboard` et `app.users` seraient des sous-modules utilisés en tant que dépendances de `app`. - -### Mutateurs (*Setters*) -###### [Style [Y021](#style-y021)] - - - Déclarez les modules sans variables en utilisant la syntaxe *setter*. - - *Pourquoi ?* : Avec un composant par fichier, on ne devrait pas avoir besoin d'introduire une variable pour le module. - - ```javascript - /* à éviter */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Utilisez à la place la syntaxe *setter*. - - ```javascript - /* recommandé */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Accesseurs (*Getters*) -###### [Style [Y022](#style-y022)] - - - Lorsque vous utilisez un module, évitez d'utiliser une variable en utilisant plutôt le chaînage avec la syntaxe *getter*. - - *Pourquoi ?* : Le code est plus lisible et évite les collisions de variables ou les fuites. - - ```javascript - /* à éviter */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommandé */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting ou Getting -###### [Style [Y023](#style-y023)] - - - N'utilisez le *setter* qu'une fois et le *getter* pour toutes les autres instances. - - *Pourquoi ?* : Un module ne devrait être créé qu'une seule fois, et ensuite récupéré à partir de ce point. - - ```javascript - /* recommended */ - - // pour setter un module - angular.module('app', []); - - // pour getter un module - angular.module('app'); - ``` - -### Fonctions nommées ou anonymes -###### [Style [Y024](#style-y024)] - - - Utilisez des fonctions nommées au lieu de passer des fonction anonymes dans les *callbacks*. - - *Pourquoi ?* : Le code plus lisible, est plus facile à déboguer, et réduit l'imbrication des *callbacks*. - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommandé */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Contrôleurs - -### Syntaxe de la vue avec `controllerAs` -###### [Style [Y030](#style-y030)] - - - Utilisez la syntaxe avec [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) au lieu de la syntaxe classique avec `$scope`. - - *Pourquoi ?* : Les contrôleurs sont construits, recréés, et fournissent une unique nouvelle instance. La syntaxe utilisant `controllerAs` est plus proche de celle d'un constructeur Javascript que la syntaxe classique avec `$scope`. - - *Pourquoi ?* : Elle encourage l'usage du *binding* entre un objet (avec la notation pointée) et la vue (ex. `customer.name` au lieu de `name`). Elle est plus contextuelle, plus facile à lire, et évite tout problème de référence qui peut arriver sans la notation « point ». - - *Pourquoi ?* : Elle permet d'éviter l'usage des appels à `$parent` dans les vues avec des contrôleurs imbriqués. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### Syntaxe du contrôleur avec `controllerAs` -###### [Style [Y031](#style-y031)] - - - Utilisez la syntaxe avec `controllerAs` au lieu de la syntaxe de classique avec `$scope`. - - - La syntaxe avec `controllerAs` utilise `this` à l'intérieur des contrôleurs qui se fait *binder* à `$scope` implicitement. - - *Pourquoi ?* : `controllerAs` est une simplification (sucre) syntaxique de `$scope`. Vous pouvez toujours vous *binder* dans la vue et accéder aux méthodes de `$scope`. - - *Pourquoi ?* : Permet d'éviter la tentation d'utiliser les méthodes de `$scope` à l'intérieur d'un contrôleur. Il est par ailleurs, meilleure pratique de les éviter dans les contrôleurs mais plutôt de les déplacer dans une factory. Considérez l'utilisation de `$scope` dans un contrôleur seulement si nécessaire. Par exemple lorsqu'il faut publier ou souscrire à des événements en utilisant [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), ou [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considérez déplacer ces usages dans une factory et les invoquer depuis le contrôleur. - - ```javascript - /* à éviter */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommandé - mais voir la section suivante */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### `controllerAs` avec `vm` -###### [Style [Y032](#style-y032)] - - - Utilisez une variable de capture pour `this` quand vous utilisez la syntaxe avec `controllerAs`. Choisissez un nom de variable consistent tel que `vm` (pour « ViewModel »). - - *Pourquoi ?* : `this` est contextuel et son utilisation au sein d'une fonction à l'intérieur d'un contrôleur pourrait faire changer son contexte. Capturer le contexte de `this` évite de rencontrer ce problème. - - ```javascript - /* à éviter */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommandé */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Note : Vous pouvez évitez n'importe quel avertissement [jshint](http://www.jshint.com/) en plaçant le commentaire suivant au dessus de la ligne de code. Cependant, il n'est pas nécessaire lorsque la fonction est nommée en utilisant la CasseEnMajuscule, puisque cette convention signifie que c'est la fonction est un constructeur. C'est précisément la nature d'un contrôleur dans Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Note : Lors de la création de *watchers* dans un contrôleur en utilisant `controlleAs`, vous pouvez *watcher* les différents `vm.*` en utilisant la syntaxe suivante. (Créez des *watchers* avec prudence puisqu'ils ajoutent plus de charge au cycle de *digest*.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Placement des membres *bindables* au début -###### [Style [Y033](#style-y033)] - - - Placez les membres *bindables* au début du contrôleur, par ordre alphabétique, et non pas dispersés à travers le code du contrôleur. - - *Pourquoi ?* : Placer les membres *bindables* au début permet de faciliter la lecture et vous aide à identifier instantanément quels membres du contrôleur peuvent-être *bindés* et utilisés dans la vue. - - *Pourquoi ?* : Définir des fonctions anonymes *in-line* peut être facile, mais lorsque ces fonctions font plus d'une ligne de code elles peuvent réduire la lisibilité. Définir les fonctions sous les membres *bindables* (les fonctions seront *hoistées*) déplace les détails d'implémentation en bas, gardant les membres *bindables* en haut, - - ```javascript - /* avoid */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Contrôleur utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Note : Si la fonction est un *oneliner*, vous pouvez la garder en haut du contrôleur, tant que la lisibilité n'est pas affectée. - - ```javascript - /* à éviter */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * Nombreuses lignes - * de - * code - * affectant - * la lisibilité - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommandé */ - function Sessions(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // *oneliner* acceptable - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Déclaration des fonctions pour cacher les détails d'implémentation -###### [Style [Y034](#style-y034)] - - - Utilisez les déclarations de fonctions pour cacher les détails d'implémentation. Gardez vos membres *bindables* en haut. Quand vous avez besoin de *binder* une fonction dans un contrôleur, faites-la pointer vers la déclaration de la fonction plus bas dans le fichier. Ceci est directement lié à la section du placement des membres *bindables* au début. Pour plus de détails, vous pouvez lire [cet article](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Pourquoi ?* : Placer les membres *bindables* en haut facilite la lecture et vous aide instantanément à identifier quels membres du contrôleur peuvent être *bindés* et utilisés dans la vue. - - *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus bas dans le fichier permet de masquer la complexité. Ainsi vous ne pouvez voir que les choses importantes en haut. - - *Pourquoi ?* : Les déclarations de fonctions sont *hoistées* donc il n'y a pas problème à utiliser une fonction avant qu'elle ne soit définie (alors que ça serait le cas avec les expressions de fonction). - - *Pourquoi ?* : Vous ne vous préoccuperez plus des déclarations de fonctions déplaçant `var a` avant `var b` cassant ainsi votre code car `a` dépendait de `b`. - - *Pourquoi ?* : L'ordre est critique avec les expressions de fonctions. - - ```javascript - /** - * Evitez - * l'utilisation des expressions de fonctions. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Remarquez que dans l'exemple précédent les choses importantes sont dispersées. Dans l'exemple ci-dessous, vous noterez que le contenu important est en haut. Par exemple, les membres *bindables* au contrôleur tels que `vm.avengers` ou `vm.title`. Les détails d'implémentation sont plus bas. C'est simplement plus facile à lire. - - ```javascript - /* - * recommandé - * Utilisation des déclarations de fonction - * et les membres bindables au début. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Déplacez la logique métier dans les services -###### [Style [Y035](#style-y035)] - - - Déplacer la logique d'un contrôleur en la déléguant à des services ou *factories*. - - *Pourquoi ?* : La logique peut être réutilisée par plusieurs contrôleurs lorsqu'elle est placée au sein d'un service et exposée via une fonction. - - *Pourquoi ?* : La logique dans un service peut être facilement isolée pour les tests unitaires, tandis que la logique d'appel dans un contrôleur peut facilement être *mockée*. - - *Pourquoi ?* : Cela supprime des dépendances et cache les détails d'implémentation au contrôleur. - - *Pourquoi ?* : Permet de garder le contrôleur le plus minimal et focalisé possible. - - ```javascript - - /* à éviter */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Obtenir l'URL de base du service crédit à partir de la config - // Positionne les headers requis pour le service crédit - // Prépare l'URL query string ou l'objet de données avec les données de requête. - // Ajoute les infos d'identification de l'utilisateur afin que le service obtienne les bons droits de limite credit pour cet utilisateur. - // Utilise JSONP pour ce navigateur s'il ne supporte pas les CORS - return $http.get(settings) - .then(function(data) { - // Décompresse les données JSON dans l'objet de réponse - // afin de rechercher maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpréter l'erreur - // Gérer le timeout ? Réessayer ? Essayer un service alternatif ? - // Re-rejeter avec une erreur appropriée de l'utilisateur final - }); - }; - } - ``` - - ```javascript - /* recommandé */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -### Gardez les contrôleurs focalisés -###### [Style [Y037](#style-y037)] - - - Définissez un contrôleur pour une vue, et n'essayez pas de réutiliser le contrôleur pour d'autres vues. Au lieu de cela, déplacez la logique réutilisable vers les *factories* et gardez le contrôleur simple et focalisé sur sa vue. - - *Pourquoi ?*: La réutilisation des contrôleurs sur plusieurs vues est fragilisante pour l'application et une bonne couverture de tests *end-to-end* (*e2e*) est requise afin d'assurer la stabilité sur l'ensemble d'une grosse application. - -### Assignation des contrôleurs -###### [Style [Y038](#style-y038)] - - - Lorsqu'un contrôleur doit être associé à une vue et qu'un composant pourraient être réutilisés par d'autres contrôleurs ou vues, définissez les contrôleurs avec leurs routes. - - Note : Si une vue est chargée via d'autres moyens qu'une route, alors utilisez la syntaxe avec `ng-controller="Avengers as vm"`. - - *Pourquoi ?* : Associer le contrôleur dans la route permet à différentes routes d'invoquer d'autres paires contrôleur-vue. Lorsque les contrôleurs sont assignés dans la vue avec [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), cette vue est toujours associée avec le même contrôleur. - - ```javascript - /* à éviter - lorsque l'utilisation avec une route et une association dynamique est voulue */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommandé */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Retour en haut de page](#table-des-matières)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - Les services sont instanciés avec le mot clé `new`, utilisez `this` pour les méthodes publiques et les variables. Puisque ces derniers sont tellement similaires aux *factories*, utilisez à la place une *factory* pour la cohérence. - - Note : [Tous les services Angular sont des singletons](https://docs.angularjs.org/guide/services). Cela signifie qu'il n'y a qu'une seule instance d'un service donné par injecteur. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Factories - -### Responsabilité unique -###### [Style [Y050](#style-y050)] - - - Les *factories* ne devraient avoir qu'une [seule et unique responsabilité](http://en.wikipedia.org/wiki/Single_responsibility_principle), qui serait encapsulée par son contexte. Une fois qu'une *factory* commence à dépasser cet unique cadre, une nouvelle *factory* devrait être créée. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Les *factories* sont des singletons et retournent un objet qui contient les membres du service. - - Note : [Tous les services Angular sont des singletons](https://docs.angularjs.org/guide/services). - -### Membres accessibles au début -###### [Style [Y052](#style-y052)] - - - Placez les membres appelables du services (son interface) en haut, utilisant une technique dérivée du [*Revealing Module Pattern*](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Pourquoi ?* : Placer les membres appelables au début facilite la lecture et vous aide à identifier instantanément quels membres du service peuvent être appelés et doivent être testés unitairement (et/ou *mockés*). - - *Pourquoi ?* : C'est particulièrement efficace lorsque le fichier devient long et permet d'éviter de faire défiler tout le code pour voir ce qui est exposé. - - *Pourquoi ?* : Placer les fonctions au fil de l'écriture semble facile, mais quand elles font plus d'une ligne, elles peuvent vite réduire la lisibilité et causer plus de défilement. Définir l'interface à appeler via le service retourné déplace les détails d'implémentation plus bas, garde l'interface d'appel en haut, et rend le tout plus facile à lire. - - ```javascript - /* à éviter */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommandé */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - De cette façon, les *bindings* sont répliqués à travers l'objet de capture, les valeurs primitives ne peuvent pas se mettre à jour toutes seules grâce au *revealing module pattern*. - - ![Factories utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Déclaration des fonctions pour cacher les détails d'implémentation -###### [Style [Y053](#style-y053)] - - - Utilisez les déclarations de fonctions pour cacher les détails d'implémentation. Gardez les membres accessibles de la *factory* en haut. Faites-les pointer vers les déclarations de fonction qui apparaissent plus loin dans le fichier. Pour plus de détails, vous pouvez lire [cet article](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Pourquoi ?* : Placer les membres accessibles en haut facilite la lecture et vous aide instantanément à identifier quels membres de la *factory* peuvent être appelés depuis l'extérieur. - - *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus bas dans le fichier permet de masquer la complexité. Ainsi vous ne pouvez voir que les choses importantes en haut. - - *Pourquoi ?* : Les déclarations de fonctions sont *hoistées* donc il n'y a pas problème à utiliser une fonction avant qu'elle ne soit définie (alors que ça serait le cas avec les expressions de fonction). - - *Pourquoi ?* : Vous ne vous préoccuperez plus des déclarations de fonctions déplaçant `var a` avant `var b` cassant ainsi votre code car `a` dépendait de `b`. - - *Pourquoi ?* : L'ordre est critique avec les expressions de fonctions. - - ```javascript - /** - * À éviter - * Utilisation des expressions de fonctions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // détails d'implémentation - }; - - var getAvengerCount = function() { - // détails d'implémentation - }; - - var getAvengersCast = function() { - // détails d'implémentation - }; - - var prime = function() { - // détails d'implémentation - }; - - var ready = function(nextPromises) { - // détails d'implémentation - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommandé - * Utilisation des déclarations de fonctions - * et des membres accessibles au début. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // détails d'implémentation - } - - function getAvengerCount() { - // détails d'implémentation - } - - function getAvengersCast() { - // détails d'implémentation - } - - function prime() { - // détails d'implémentation - } - - function ready(nextPromises) { - // avec les détails d'implémentation ici - } - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Services de données - -### Séparer les appels aux données -###### [Style [Y060](#style-y060)] - - - *Refactorez* la logique pour faire les opérations et interactions avec les données dans une *factory*. Rendez les services de données responsables des appels *XHR*, du *local storage*, du stockage en mémoire, ou de toute autre opération sur les données. - - *Pourquoi ?* : Les responsabilités du contrôleur sont la présentation et l'assemblage des informations pour la vue. Il ne devrait pas avoir à se soucier de la façon dont les données sont récupérées mais seulement de la façon de les demander. Séparer les services de données transforme la logique du contrôleur en logique de « À quel service vais-je demander ces données ? ». Le contrôleur est alors plus simple est plus focalisé sur sa vue. - - *Pourquoi ?* : Cela rend plus facile à tester (*mocké* ou en utilisant le vrai) les appels aux données lorsque l'on teste un contrôleur qui utilise un service de données. - - *Pourquoi ?* : L'implémentation d'un service de données peut contenir du code très spécifique pour gérer le système de données. Cela peut inclure des entêtes, la façon de dialoguer avec les données, ou d'autres services tels que `$http`. Séparer la logique vers un service de données permet d'encapsuler cette logique dans un unique endroit en cachant les détails d'implémentation des consommateurs externes (tel qu'un contrôleur), en rendant également plus simple les changements d'implémentation. - - ```javascript - /* Recommandé */ - - // Factory jouant le rôle de service de données - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Note : Le service de données est appelé par des consommateurs extérieur, tels que des contrôleurs, en leur cachant l'implémentation, comme ci-dessous. - - ```javascript - /* Recommandé */ - - // Contrôleur appelant la factory faisant le service de données - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Retourner une promesse suite à un appel de données -###### [Style [Y061](#style-y061)] - - - Quand vous appelez un service de données tel que `$http` qui retourne une *promise*, retournez également une *promise* dans votre fonction appelante. - - *Pourquoi ?* : Vous pouvez chaîner les promesses entre elles et entreprendre d'autres actions après que l'appel soit terminé, puis résoudre ou rejeter la promesse. - - ```javascript - /* recommandé */ - - activate(); - - function activate() { - /** - * Etape 1 - * Appelle la fonction getAvengers pour récupérer - * les données «avenger» et attend la promise. - */ - return getAvengers().then(function() { - /** - * Etape 4 - * Exécute une action à la résolution de la promise finale. - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Etape 2 - * Appel du service de données pour récupérer les données - * et attend la promesse. - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Etape 3 - * Définit les données et résout la promesse. - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Directives -### Une directive par fichier -###### [Style [Y070](#style-y070)] - - - Créez seulement une directive par fichier. Nommer le fichier en fonction de la directive. - - *Pourquoi ?* : Il est facile de placer toutes les directives dans un fichier, mais il l'est moins de les séparer après coup. Certaines sont partagées dans toute l'application, certaines par modules, et certaines juste par un seul module. - - *Pourquoi ?* : Une directive par fichier leur permet d'être plus facilement maintenables. - - > Note : "**Bonne pratique** : Les directives devraient pouvoir s'auto-nettoyer. Vous pouvez utiliser `element.on('$destroy', ...)` ou bien `scope.$on('$destroy', ...)` pour lancer une fonction de nettoyage quand une directive est enlevée" ... - Documentation d'Angular - - ```javascript - /* à éviter */ - /* directives.js */ - - angular - .module('app.widgets') - - /* directive spécifique pour le module order */ - .directive('orderCalendarRange', orderCalendarRange) - - /* directive pouvant être utilisée n'importe où dans l'application sales */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* directive pouvant être utilisée n'importe où dans l'application */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* détails d'implémentation */ - } - - function salesCustomerInfo() { - /* détails d'implémentation */ - } - - function sharedSpinner() { - /* détails d'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* calendarRange.directive.js */ - - /** - * @desc directive spécifique pour le module order de l'entreprise Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* détails d'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* customerInfo.directive.js */ - - /** - * @desc directive pouvant être utilisée n'importe où dans l'application sales de l'entreprise Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* détails d'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* spinner.directive.js */ - - /** - * @desc directive pouvant être utilisée n'importe où dans l'application de l'entreprise Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* détails d'implémentation */ - } - ``` - - Note : Il y a plusieurs options de nommage pour les directives puisqu'elles peuvent être utilisées à des échelles (*scopes*) plus ou moins larges. Choisissez un nom qui rend la directive et son fichier clairs et distincts. Des exemples sont définis plus bas mais regardez la section [nommage](#nommage) pour plus de recommandations. - -### Manipuler le DOM dans une directive -###### [Style [Y072](#style-y072)] - - - Quand vous manipulez le DOM directement, utilisez une directive. S'il existe des alternatives, comme par exemple le CSS pour définir les styles ou les [services d'animation](https://docs.angularjs.org/api/ngAnimate), les *templates* Angular , [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ou [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), utilisez les à la place. Par exemple si la directive affiche ou cache un élément, utilisez ngHide ou ngShow. - - *Pourquoi ?* : La manipulation du DOM peut être difficile à tester, déboguer, et il y a souvent une meilleure façon de faire (ex : CSS, animations, *templates*). - -### Utiliser un unique préfixe de directive -###### [Style [Y073](#style-y073)] - - - Définissez un unique préfixe de directive court et descriptif comme dans `acmeSalesCustomerInfo` et utilisé dans le HTML de la façon suivante : `acme-sales-customer-info`. - - *Pourquoi ?* : Un préfixe court et unique identifie le contexte et l'origine de la directive. Par exemple, un préfixe comme `cc-` peut indiquer que la directive fait partie de l'application CodeCamper tandis que `acme-` peut indiquer une directive de l'entreprise Acme. - - Note : Évitez d'utiliser le préfixe `ng-` car il est réservé pour les directives Angular. Cherchez les directives populaire pour éviter les conflits de nommage, tel que `ion-` pour les directives du framework [Ionic](http://ionicframework.com/). - -### Restreindre aux éléments et aux attributs -###### [Style [Y074](#style-y074)] - - - Lors de la création d'une directive qui fait du sens comme élément indépendant, autorisez la restriction `E` (élément personnalisé) et éventuellement la restriction `A` (attribut personnalisé). En général, s'il devrait avoir son propre contrôle, `E` est le plus indiqué. La convention la plus utilisée est de permettre `EA` mais se dirige vers une implémentation en tant qu'élément lorsque la directive est indépendante et en tant qu'attribut lorsqu'elle augmente son propre élément de DOM. - - *Pourquoi ?* : Cela a du sens. - - *Pourquoi ?* : Même s'il est autorisé d'utiliser une directive en tant que classe, si la directive agit vraiment comme un élément, elle fait plus de sens en tant qu'élément ou au moins en tant qu'attribut. - - Note : EA est la valeur par défaut dans Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* à éviter */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommandé */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives et `controllerAs` -###### [Style [Y075](#style-y075)] - - - Utilisez la syntaxe avec `controllerAs` avec une directive pour être cohérent avec l'utilisation de `controllerAs` pour l'association de la vue et de son contrôleur. - - *Pourquoi ?* : Ça fait sens et ce n'est pas difficile. - - Note : La directive ci-dessous montre une façon parmi d'autres d'utiliser *scope* à l'intérieur de la fonction `link` et dans un contrôleur de directive, par l'utilisation de `controllerAs`. J'ai *inliné* le template pour tout mettre au même endroit. - - Note : Concernant l'injection de dépendances, voir [Annoter manuellement les dépendances à injecter](#annotation-manuelle-pour-linjection-de-dépendances). - - Note : Remarquez que le contrôleur de la directive est à l'extérieur de la *closure* de la directive. Cette façon de faire évite le problème d'indisponibilité des injections après le `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller : ExampleController, - controllerAs: 'vm', - bindToController: true // parce que le scope est isolé - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injection de $scope seulement pour la comparaison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Note : Vous pouvez aussi nommer le contrôleur au moment où vous l'injectez dans la fonction de `link` et accéder aux attributs de la directive en temps que propriétés du contrôleur. - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - Utilisez `bindToController = true` lorsque vous utilisez la syntaxe avec `controllerAs` avec une directive quand vous voulez *binder* le *scope* externe au *scope* du contrôleur de la directive. - - *Pourquoi ?* : Cela rend plus facile de *binder* le *scope* externe au *scope* du contrôleur de la directive. - - Note : `bindToController` a été introduit à partir de Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Retour en haut de page](#table-des-matières)** - -## Résolution des *promises* pour un contrôleur -### *Promises* d'activation du contrôleur -###### [Style [Y080](#style-y080)] - - - Résolvez la logique d'initialisation d'un contrôleur dans une fonction `activate`. - - *Pourquoi ?* : Placer la logique d'initialisation toujours au même endroit permet de la rendre plus facile à localiser, plus cohérente à tester, et permet d'éviter sa dispersion à travers le contrôleur. - - *Pourquoi ?* : La fonction `activate` du contrôleur rend pratique la réutilisation de la logique pour un rafraîchissement du contrôleur ou de la vue, garde cette logique en un seul endroit, envoie la vue à l'utilisateur plus rapidement, rend les animations faciles sur `ng-view` ou `ui-view` et rend l'interface plus réactive pour l'utilisateur. - - Note : Si vous avez besoin d'annuler de façon conditionnelle la route avant d'utiliser le contrôleur, utilisez la [résolution de route](#style-y081) à la place. - - ```javascript - /* à éviter */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommandé */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### *Promises* de résolution de route -###### [Style [Y081](#style-y081)] - - - Lorsqu'un contrôleur dépend d'une *promise* qui doit être résolue avant que le contrôleur soit activé, résolvez ces dépendances dans le `$routeProvider` avant que la logique du contrôleur soit exécutée. Si vous avez besoin d'annuler une route de façon conditionnelle avant que le contrôleur soit activé, utilisez un *resolver* de route. - - - Utilisez un *resolver* de route quand vous voulez pouvoir décider d'annuler la route avant même de commencer à naviguer vers la vue. - - *Pourquoi ?* : Un contrôleur pourrait avoir besoin de données avant de se charger. Ces données pourraient provenir d'une promesse via une *factory* ou de [`$http`](https://docs.angularjs.org/api/ng/service/$http). Utiliser une [résolution de route](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permet à la promesse de se résoudre avant que la logique du contrôleur s’exécute, alors seulement, on peut exécuter les actions basées sur les données fournies via la *promises*. - - *Pourquoi ?* : Le code s’exécute après la route et dans la fonction `activate` du contrôleur. La vue commence à se charger tout de suite. Le *data-binding* démarre quand la *promise* d'activation se résout. Une animation de « chargement » peut être affichée pendant la transition (via `ng-view` ou `ui-view`). - - Note : Le code s’exécute avant la route via une *promise*. Le rejet de la promesse annule le routage. Sa résolution met la future vue en attente de la résolution du routage. Une animation de « chargement » peut être affichée avant la résolution et lorsque la vue change d'état. Si vous voulez aller à la vue plus vite et que vous n'avez pas besoin d'une étape pour décider si vous pouvez l'atteindre, il est conseillé d'utiliser à la place la [technique de la fonction `activate` dans le contrôleur](#style-y080). - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // non-résolue - vm.movies; - // résolu de façon asynchrone - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* mieux */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Note : L'exemple ci-dessous montre que la résolution du routage pointe vers une fonction nommée, laquelle est plus facile à déboguer et dont l'injection de dépendance est plus facile à gérer. - - ```javascript - /* encore mieux */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviePrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Note : Les dépendances sur `movieService` dans l'exemple ne sont pas directement compatibles avec le processus de minification. Pour plus de détails sur la façon de rendre ce code minifiable sans risques, voir la section sur l'[injection de dépendances](#annotation-manuelle-pour-linjection-de-dépendances) et sur [la minification et les annotations](#minification-et-annotation). - -**[Retour en haut de page](#table-des-matières)** - -## Annotation manuelle pour l'injection de dépendances - -### Risques pour la minification -###### [Style [Y090](#style-y090)] - - - Évitez l'utilisation de la syntaxe raccourcie pour déclaration de dépendances. Ulilisez plutôt une approche compatible avec la minification. - - *Pourquoi ?* : Les paramètres des composants (ex: contrôleur, *factory*, etc.) vont êtres convertis en variables raccourcies. Par exemple, ˋcommonˋ et ˋdataserviceˋ deviendraient ˋaˋ et ˋbˋ et ne seraient pas trouvées par Angular. - - ```javascript - /* à éviter - non compatible avec la minification */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Ce code pourrait produire des variables raccourcies après minification et provoquer des erreurs à l’exécution. - - ```javascript - /* à éviter - non compatible avec la minification */ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Identification manuelle des dépendances -###### [Style [Y091](#style-y091)] - - - Utilisez ˋ$injectˋ pour identifier manuellement les dépendances de vos composants Angular. - - *Pourquoi ? * : Cette technique réplique celle utilisée par [`ng-annotate`](https://github.com/olov/ng-annotate), que je recommande afin d'automatiser la création de dépendances compatible avec la minification. Si ˋng-annotateˋ détecte que l'injection a déjà été faite, cela ne la dupliquera pas. - - *Pourquoi ?* : Ça assure la compatibilité de vos dépendances à la minification. Par exemple, ˋcommonˋ et ˋdataserviceˋ pourraient devenir ˋaˋ et ˋbˋ et ne pas être trouvés par Angular. - - *Pourquoi ?* : Pour éviter de créer *inline* une longue liste de dépendances car les longs tableaux sont difficiles à lire. De même, cela peut être gênant de voir un tableau composé d'une série de *strings* alors que son dernier élément est un appel à la fonction du composant. - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommandé */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Note : Lorsque votre fonction se situe sous un `return`, le `$inject` peut ne pas être accessible (cela pourrait arriver dans une directive). Vous pouvez vous en sortir en sortant le contrôleur de la directive. - - ```javascript - /* À éviter */ - // à l'intérieur d'une définition de directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Inatteignable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommandé */ - // à l'exterieur d'une définition de directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Identification manuelle des dépendances du *route resolver* -###### [Style [Y092](#style-y092)] - - - Utilisez `$inject` pour identifier manuellement vos dépendances du *route resolver* pour les composants Angular. - - *Pourquoi ?* : Cette technique divise la fonction anonyme pour le *route resolver*, le rendant plus facile à lire. - - *Pourquoi ?* : Une instruction `$inject` peut facilement précéder le *resolver* à manipuler rendant n'importe quelle dépendance compatible avec la minification. - - ```javascript - /* recommandé */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Minification et Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Utilisez [ng-annotate](//github.com/olov/ng-annotate) pour [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) et commentez les fonctions qui nécessitent l'injection de dépendances automatique en utilisant `/** @ngInject */`. - - *Pourquoi ?* : Cela prévient votre code d'erreur provenant de dépendances n'utilisant pas les bonnes pratiques au regard de la minification. - - *Pourquoi ?*: [`ng-min`](https://github.com/btford/ngmin) est obsolète. - - >Je préfère Gulp car ça me paraît plus facile à écrire, lire et déboguer. - - Le code suivant n'utilise pas de dépendances compatibles avec la minification. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Lorsque le code ci-dessus sera exécuté par `ng-annotate`, il produira le résultat suivant avec l'annotation ˋ$injectˋ et deviendra alors compatible avec la minification. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Note : Si ˋng-annotateˋ détecte que l'injection a déjà été faite (ex : ˋ@ngInjectˋ a été détecté), il ne dupliquera pas le code ˋ$injectˋ. - - Note : Lors de l'utilisation d'un *route resolver*, vous pouvez préfixer la fonction de résolution avec `/* @ngInject */` et cela produira le code proprement annoté, en gardant toutes les dépendances injectées compatibles avec la minification. - - ```javascript - // En utilisant les annotations @ngInject - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note : A partir d'Angular 1.3, utilisez le paramètre ˋngStrictDiˋ de la directive [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) pour détecter un potentiel oubli. Avec ce paramètre, l'injecteur sera créé en mode "strict-di" qui fera échouer les invocations de fonctions de l'application qui n'utiliseraient pas explicitement les annotations de fonctions (et qui rendraient l'application non-minifiable). Des infos de débogage seront alors logguées dans la console pour aider à retrouver code à l'origine de l'alerte. Je préfère utiliser `ng-strict-di` uniquement pour le débogage. - `` - -### Utilisation de Gulp ou Grunt pour `ng-annotate` -###### [Style [Y101](#style-y101)] - - - Utilisez [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ou [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) dans une tâche de *build* automatisée. Injectez `/* @ngInject */` avant toute fonction qui a des dépendances. - - *Pourquoi ?* : `ng-annotate` va intercepter la plupart des dépendances, mais parfois va nécessiter des indices grâce à l'utilisation de l'ajout de `/* @ngInject */ˋ. - - Le code ci-dessous est un exemple d'une tâche gulp qui utilise `ngAnnotate` - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate est avant uglify pour que le code soit minifié correctement. - .pipe(ngAnnotate({ - // true permet de l'ajouter là où @ngInject n'est pas utilisé. C'est inféré. - // Ne fonctionne pas avec resolve, donc nous devons être explicite ici. - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Gestion des Exceptions - -### Décorateurs -###### [Style [Y110](#style-y110)] - - - Utilisez un [décorateur](https://docs.angularjs.org/api/auto/service/$provide#decorator), au moment de la configuration en utilisant le service [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), sur le service [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) pour effectuer des actions personnalisées lorsque des exceptions se produisent. - - *Pourquoi ?* : Cela fournit un moyen cohérent pour gérer les exceptions non interceptées d'Angular pendant le développement ou à l’exécution. - - Note : Une autre possibilité serait de surcharger le service au lieu d'utiliser un décorateur. C'est une bonne option, mais si vous voulez vous conformer au comportement standard et l'étendre, un décorateur est plus approprié. - - ```javascript - /* recommandé */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * On pourrait ajouter l'erreur à une collection d'un service, - * ajouter les erreurs au $rootScope, loguer les erreurs sur un serveur distant, - * ou les loguer localement. Ou alors les rejeter directement. C'est entièrement votre choix. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Intercepteurs d'exceptions -###### [Style [Y111](#style-y111)] - - - Créez une *factory* qui expose une interface pour intercepter et gérer correctement les exceptions. - - *Pourquoi ?* : Cela fournit un moyen cohérent pour intercepter les exceptions qui peuvent être déclenchées dans votre code (par exemple, pendant une requête XHR ou lors d'un échec de *promise*). - - Note : L'intercepteur d'exceptions est bon pour intercepter et réagir à des exceptions spécifiques potentielles provenant d'appels qui pourraient en produire. Par exemple, lorsque on fait une requête XHR pour récupérer des données d'un service web distant et que vous voulez intercepter n'importe quelles exceptions provenant de ce service uniquement et réagir seulement à celles-ci. - - ```javascript - /* recommandé */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Erreurs de routage -###### [Style [Y112](#style-y112)] - - - Gérez et loguez toutes les erreurs de routage en utilisant [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Pourquoi ?* : Pour fournir un moyen cohérent de gestion des erreurs de routage. - - *Pourquoi ?* : Pour fournir potentiellement une meilleure expérience utilisateur si une erreur de routage se produit et rediriger vers un écran approprié avec plus de détails ou les possibilités pour s'en sortir. - - ```javascript - /* recommandé */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Annulation du routage : - * sur une erreur de routage, aller au dashboard. - * Fournit une clause de sortie s'il essaye de le faire deux fois. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Loguer éventuellement en utilisant un service personnalisé ou $log. - * (N'oubliez pas d'injecter votre service personnalisé) - */ - logger.warning(msg, [current]); - - /** - * Lors d'une erreur de routage aller sur une autre route / état. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Nommage - -### Conventions de nommage -###### [Style [Y120](#style-y120)] - - - Utilisez des noms cohérents pour tous les composants en utilisant un motif qui décrit la fonctionnalité du composant puis (éventuellement) son type. Le motif que je recommande est `fonctionnalité.type.js`. Il y a deux noms pour la plupart des ressources : - * le nom du fichier (`avengers.controller.js`) - * le nom du composant déclaré à Angular (`AvengersController`) - - *Pourquoi ?* : Les conventions de nommage permettent de de nommer de façon cohérente et de retrouver du contenu en un clin d'œil. La cohérence à toute l'échelle du projet est vitale. La cohérence au sein d'une équipe est importante. La cohérence dans une entreprise apporte une efficacité redoutable. - - *Pourquoi ?* : Les conventions de nommage doivent simplement vous aider à naviguer dans votre code plus vite et le rendre plus facile à comprendre. - -### Nom des fichiers de fonctionnalités -###### [Style [Y121](#style-y121)] - - - Utilisez des noms cohérents pour tous les composants qui suivent un motif qui décrit leur fonctionnalité et (éventuellement) leur type. Le motif que je recommande est `fonctionnalité.type.js`. - - *Pourquoi ?* : Offre une façon cohérente d'identifier rapidement les composants. - - *Pourquoi ?* : Permet de faire *matcher* par nom de fichier au sein d'un processus d'automatisation. - - ```javascript - /** - * Options courantes - */ - - // Contrôleurs - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommandé - */ - - // contrôleurs - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constantes - constants.js - - // definition de module - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Note : Une autre convention courante consiste à nommer les fichiers des contrôleurs sans le mot `controller` dans le nom de fichier comme `avengers.js`au lieu de `avengers.controller.js`. Toutes les autres conventions tiennent à garder le suffixe du type. Le contrôleur étant le type de composant le plus courant, cela permet alors d'économiser à la frappe tout en restant facilement identifiable. Je vous conseille de choisir une convention et de vous y tenir dans toute l'équipe. Ma préference est `avengers.controller.js`. - - ```javascript - /** - * recommandé - */ - // Contrôleurs - avengers.js - avengers.spec.js - ``` - -### Nommage des fichiers de test -###### [Style [Y122](#style-y122)] - - - Le nommage des spécifications de test est similaire à celui des celui du composant qu'il teste avec le suffixe `spec`. - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement les composants. - - *Pourquoi ?* : Permet de faire *matcher* par nom de fichier dans [karma](http://karma-runner.github.io/) ou d'autres moteurs de tests. - - ```javascript - /** - * recommandé - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Nommage des Contrôleurs -###### [Style [Y123](#style-y123)] - - - Utilisez des noms cohérents pour tous les contrôleurs nommés d'après leur fonctionnalité. Utilisez le UpperCamelCase pour les contrôleurs, car ce sont des constructeurs. - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les contrôleurs. - - *Pourquoi ?* : Le UpperCamelCase est la convention pour identifier les objets qui peuvent être instanciés avec un constructeur. - - ```javascript - /** - * recommandé - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Suffixe du nom des contrôleurs -###### [Style [Y124](#style-y124)] - - - Ajoutez au nom du contrôleur le suffixe ˋControllerˋ. - - *Pourquoi ?* : Le suffixe ˋControllerˋ est souvent utilisé et il est explicite. - - ```javascript - /** - * recommandé - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -### Nommage des *factory* et des services -###### [Style [Y125](#style-y125)] - - - Utilisez des noms cohérents pour toutes les *factories* et les services nommés d'après leur fonctionnalités. Utilisez le lowerCamelCase pour les services et les *factories*. Évitez de préfixer les *factories* et les services par `$`. N'ajoutez le suffixe `Service` à leurs noms uniquement si sa nature n'est pas claire (par exemple dans le cas des noms). - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les *factories* et les services. - - *Pourquoi ?* : Permet d'éviter les collisions avec les services et *factories* d'Angular qui ont déjà le préfixe `$`. - - *Pourquoi ?* : Les services aux noms explicites tels que `logger` n'ont pas besoin d'être préfixés. - - *Pourquoi ?* : Des alias tels que 'avengers' sont des noms et ont besoin d'un suffixe. Ce dernier doit être nommé `avengersService`. - - ```javascript - /** - * recommandé - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - - ```javascript - /** - * recommended - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // credit.service.js - angular - .module - .service('customersService', customersService); - - function customersService() { } - ``` - -### Nommage des directives -###### [Style [Y126](#style-y126)] - - - Utilisez des noms cohérents pour toutes les directives en utilisant le lowerCamelCase. Utilisez un préfixe court pour décrire le domaine auquel les directives appartiennent (par exemple le préfixe de la société ou du projet). - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les composants. - - ```javascript - /** - * recommandés - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // l'usage est - - function xxAvengerProfile(){ } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - Lorqu'il y a de multiples modules, nommez le fichier du module principal ˋapp.module.jsˋ et nommez les autres modules qui en sont dépendants d'après ce qu'ils représentent. Par exemple, nommez un module d'administration ˋadmin.module.jsˋ. Les noms des modules déclarés seraient alors respectivement ˋappˋ et ˋadminˋ. - - *Pourquoi ?* : Fournit de la cohérence pour les applications modulaires, et pour prévoir l'extension des applications. - - *Pourquoi ?* : Fournit une façon aisée d'utiliser des processus d'automatisation des tâches afin de charger toutes les définitions de modules en premier, puis ensuite tous les autres fichiers Angular (pour l'assemblage). - -### Configuration -###### [Style [Y128](#style-y128)] - - - Séparez la configuration d'un module dans son propre fichier nommé d'après le module. Un fichier de configuration du module principal ˋappˋ est nommé ˋapp.config.jsˋ (ou simplement ˋconfig.jsˋ). Une configuration pour un module nommé ˋadmin.module.jsˋ est nommé ˋadmin.config.jsˋ. - - *Pourquoi ?* : Sépare la configuration de la définition du module, des composants et du code actif. - - *Pourquoi ?* : Fournit un endroit bien identifié pour régler la configuration d'un module. - -### Routes -###### [Style [Y129](#style-y129)] - - - Séparez la configuration du routage dans son propre fichier. Un exemple pourrait être ˋapp.route.jsˋ pour le module principal et ˋadmin.route.jsˋ pour le module d'administration. Même pour de petites applications, il est préférable de privilégier cette séparation du reste de la configuration. - -**[Retour en haut de page](#table-des-matières)** - -## Architecture L.I.F.T. -### LIFT -###### [Style [Y140](#style-y140)] - - - Structurez votre application afin de pouvoir `L`ocaliser le code plus rapidement, `I`dentifier le code d'un seul coup, garder la structure la plus plate possible (`F`lattest), et essayez (`T`ry) de rester DRY (Don't Repeat Yourself). La structure doit suivre ces quatre règles de base. - - *Pourquoi ?* : L.I.F.T. fournit une structure cohérente qui peut grossir facilement, qui est modulaire, et augmente facilement l'efficacité de développement. Une autre façon de valider la structure de votre application est de vous demander à quelle vitesse vous pouvez ouvrir et travailler dans tous les fichiers liés à une fonctionnalité. - - Lorsque je trouve que ma structure n'est pas confortable, je reviens en arrière et consulte à nouveau les règles L.I.F.T. - - 1. `L`ocaliser notre code est facile. - 2. `I`dentifier le code d'un coup. - 3. `F`lat structure (structure plate) autant que possible. - 4. `T`ry (Essayer) de ne pas se répéter (T-DRY). - -### Localisation -###### [Style [Y141](#style-y141)] - - - Rendez la localisation du code intuitive, simple et rapide. - - *Pourquoi ?* : Je trouve que c'est super important pour un projet. Si l'équipe ne peut pas trouver rapidement les fichiers sur lesquels elle doit travailler, elle ne va pas être en mesure de travailler aussi efficacement que possible, et la structure doit changer. Vous ne connaissez peut-être pas le nom du fichier où la position des fichiers liés, alors les placer dans les endroits les plus intuitifs et proches les uns les autres permet de gagner beaucoup de temps. Une structure de répertoire descriptive peut aider. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identification -###### [Style [Y142](#style-y142)] - - - Lorsque vous regardez un fichier vous devriez instantanément savoir ce qu'il contient et représente. - - *Pourquoi ?* : Vous passez moins de temps à fouiller et vous perdre pour trouver du code, et devenez de plus en plus efficace. Si ça implique des noms de fichier plus long, alors d'accord. Soyez descriptif avec les noms des fichiers et leur contenu ne doit correspondre qu'à un seul composant. Évitez les fichiers avec plusieurs contrôleurs, plusieurs services, ou un mélange. On pourrait admettre une exception à cette règle si j'ai un ensemble de très petites fonctionnalités qui sont toutes reliées entre elles, elles sont toujours facilement identifiables. - -### Plat -###### [Style [Y143](#style-y143)] - - - Gardez une structure de répertoire à plat le plus longtemps possible. Lorsque vous avez sept fichiers ou plus, commencez à penser à séparer. - - *Pourquoi ?* : Personne ne souhaite rechercher dans sept niveaux de répertoires pour trouver un fichier. Pensez aux menus des sites web… Rien de plus profond que deux niveaux ne devrait être sérieusement pris en considération. Dans une structure de répertoire, il n'y a pas de nombre d'or, mais lorsqu'un répertoire à entre sept et dix fichiers, il serait temps de créer des sous-répertoires. Ajustez cela à votre de confort. Utilisez une structure plus plate jusqu'à ce qu'il y ait un intérêt évident (pour respecter les autres principes L.I.F.T.) à créer un sous-répertoire. - -### T-DRY (Essayer de ne pas se répéter) -###### [Style [Y144](#style-y144)] - - - Ne vous répétez pas (DRY), mais ne sacrifiez pas la lisibilité. - - *Pourquoi ?* : Ne pas se répéter (DRY) est important, mais pas critique si vous en êtes réduit à sacrifier les autres principes L.I.F.T, c'est ce qu'on sous-entend par T-DRY. Je ne veux pas écrire `session-view.html` pour une vue parce que c'est trivial. Si ce n'est pas évident ou si la convention le précise alors nommez-le. - -**[Retour en haut de page](#table-des-matières)** - -## Architecture de l'application - -### Règles globales -###### [Style [Y150](#style-y150)] - - - Vous devez avoir une vue court terme et une vision à long terme. En d'autres mots, commencez petit et gardez en tête là où veut aller votre application. Tout le code de l'application va dans un répertoire de départ nommé `app` à la racine du projet. Tout élément fonctionnel doit être rangé dans son propre et unique fichier. Chaque contrôleur, service, module, vue doit avoir son propre fichier. Tous les scripts externes doivent être rangés dans un autre répertoire à la racine du projet et non dans le répertoire `app`. Le code que l'on a pas écrit ne doit pas se mélanger avec le code de l'application (`bower_components`, `script`, `lib`). - - Note : Vous trouverez plus de détails et de justifications concernant l'architecture sur [ce post original sur la structure des applications](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Placez les composants qui définissent l'agencement visuel principal de l'application dans un répertoire nommé `layout`. Il devrait inclure une « vue-enveloppe » et le contrôleur devrait agir comme conteneur pour l'application, la navigation, les menus, les zones de contenu, et les autres régions. - - *Pourquoi ?* : Cela organise tout l'agencement visuel à un seul endroit réutilisé dans toute l'application. - -### Structure « répertoire-fonctionnalité » -###### [Style [Y152](#style-y152)] - - - Créez des répertoires nommés d'après les fonctionnalités qu'elles représentent. Lorsqu'un répertoire grossit jusqu'à atteindre plus de sept fichiers, commencez à penser la création d'un sous-répertoire pour ceux-ci. Si votre seuil est différent, ajustez au besoin. - - *Pourquoi ?* : Un développeur peut localiser, identifier ce que représente chaque fichier en une seule fois, la structure est aussi plate que possible, et il n'y a ni répétitions ni redondance de nommage. - - *Pourquoi ?* : Les règles L.I.F.T. sont toutes respectées. - - *Pourquoi ?* : Aide à diminuer l'entropie de l'application en organisant le contenu et en le maintenant en accord avec les principes L.I.F.T. - - *Pourquoi ?* : Lorsqu'il y a de nombreux fichiers (plus de dix), les repérer est plus facile avec une structure de répertoires cohérente que dans une structure à plat. - - ```javascript - /** - * recommandé - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Image d'un exemple de structure d'application](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Note : N'utilisez pas une structuration « répertoire-type ». Cela requiert de se déplacer entre de multiples répertoires lors du travail sur une fonctionnalité et cela devient rapidement difficile à manier lorsque l'application passe de à cinq, dix ou plus de vingt-cinq vues et contrôleurs (et autres fonctionnalités), ce qui complique la localisation par rapport à une structure en « répertoire-fonctionnalité ». - - ```javascript - /* - * à éviter - * Alternative aux « répertoires-type » : - * Je vous conseille les « répertoires-fonctionnalité » - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Modularité - -### Nombreux petits modules auto-suffisants -###### [Style [Y160](#style-y160)] - - - Créez de petits modules qui encapsulent une seule responsabilité. - - *Pourquoi ?* : Les applications modulaires rendent facile la compossibilité rapide puisqu'elles permettent aux équipes de développement de construire verticalement des sections de l'application et de livrer incrémentalement. Cela signifie que nous pouvons brancher de nouvelles fonctionnalités au fur et à mesure de leur développement. - -### Création d'un module applicatif -###### [Style [Y161](#style-y161)] - - - Créez un module racine pour l'application dont le rôle est d'assembler tous les modules et fonctionnalités de votre application. Nommez-le comme votre application. - - *Pourquoi ?* : Angular encourage la modularité et la séparation des responsabilités. La création d'un module racine pour l'application dont le rôle est de lier ensemble des autres modules fournit un moyen très direct d'ajouter et de retirer des modules à votre application. - -### Garder le module applicatif léger -###### [Style [Y162](#style-y162)] - - - Ne placez dans le module applicatif que la logique d'assemblage de l'application. Laissez les fonctionnalités dans leurs propres modules. - - *Pourquoi ?* : Ajouter des responsabilités supplémentaires à la racine de l'application pour récupérer des données, afficher des vues, ou toute autre logique non reliée à l'assemblage de l'application trouble le module applicatif et rend plus difficile à réutiliser ou éteindre les ensembles de fonctionnalités. - - *Pourquoi ?* : Le module applicatif devient une déclaration qui montre quels modules aident à constituer l'application. - -### Les macro-fonctionnalités sont des modules -###### [Style [Y163](#style-y163)] - - - Créez des modules qui représentent des macro-fonctionnalités, comme l'agencement graphique (*layout*), les services réutilisables et partagés, les dashboards, et les fonctionnalités applicatives spécifiques (par exemple : clients, admin, ventes). - - *Pourquoi ?* : Les modules auto-suffisants peuvent être ajoutés à l'application avec peu ou pas de friction. - - *Pourquoi ?* : Les sprints ou itérations peuvent être focalisés sur les macro-fonctionnalités. Elles peuvent être activées à la fin de ceux-ci. - - *Pourquoi ?* : Séparer les macros-fonctionnalités en modules les rend plus facile à tester de façon isolée et à la réutilisation. - -### Les Blocks Réutilisables en tant que Modules -###### [Style [Y164](#style-y164)] - - - Créez des modules qui représentent des blocs réutilisables dans l'application pour les services en commun tels que la gestion des exceptions, les logs, les diagnostics, la sécurité et la gestion des données locale. - - *Pourquoi ?* : Ces types de fonctionnalités sont requises dans de nombreuses application. Donc en les gardant séparées dans leur propres modules, elles peuvent être génériques et peuvent être réutilisées pour d'autres applications. - -### Dépendances entre modules -###### [Style [Y165](#style-y165)] - - - Le module racine de l'application dépend des modules des fonctionnalités spécifiques et de certains modules partagés et réutilisables. - - ![Modularité et Dépendences](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Pourquoi?* : Le module principal de l'application continent manifeste des fonctionnalités de l'application. - - *Pourquoi ?* : Chaque groupe de fonctionnalités contient une déclaration de ce dont il dépend, de ce fait il peut être importé comme dépendance dans d'autres applications et continuer à fonctionner. - - *Pourquoi ?* : Les fonctionnalités propres à l'application telles que les services de données partagées deviennent faciles à repérer et partager au sein d'un `app.core` (choisissez un nom de votre choix pour ce module). - - Note : C'est une stratégie pour la cohérence. Il y a ici beaucoup de bons choix. Choisissez-en une qui soit cohérente, qui suit les règles des dépendances d'Angular, et qui facilite la maintenance et la montée en charge. - - > Mes structures peuvent varier légèrement entre les projets mais elles suivent toutes ces règles pour la structure et la modularité. L'implémentation peut varier en fonction des fonctionnalités et de l'équipe. En d'autres termes, ne vous paralysez pas sur une structure exactement semblable mais pensez votre structure en termes de cohérence, maintenabilité, et efficacité. - - > Dans de petites applications, vous pouvez aussi mettre toutes vos dépendances partagées dans le module applicatif où les modules fonctionnels n'ont pas de dépendances directes. Cela pourra rendre la maintenance de petites applications plus facile, mais rend difficile la réutilisation de ces modules en dehors de celle-ci. - -**[Retour en haut de page](#table-des-matières)** - -## Logique d'initialisation - -### Configuration -###### [Style [Y170](#style-y170)] - - - Injectez le code à l'intérieur d'une [configuration de module](https://docs.angularjs.org/guide/module#module-loading-dependencies) qui doit être configurée avant l’exécution de l'application Angular. Parmi les candidats idéaux, on trouve les *providers* et les constantes. - - *Pourquoi ?* : Cela rend les choses plus faciles d'avoir le moins d'endroits possibles pour la configuration. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Blocs `run` -###### [Style [Y171](#style-y171)] - - - Tout code qui nécessite de s’exécuter lorsque l'application s'initialise devrait être déclaré dans une *factory*, exposé via une fonction, et injecté dans le [bloc run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Pourquoi ?* : Le code directement écrit dans un bloc `run` peut être difficile à tester. Le placer dans une *factory* le rend plus facile à abstraire et à *mocker*. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Les services `$` d'Angular - -### `$document` et `$window` -###### [Style [Y180](#style-y180)] - - - Utilisez [`$document`](https://docs.angularjs.org/api/ng/service/$document) et [`$window`](https://docs.angularjs.org/api/ng/service/$window) au lieu de `document` et `window`. - - *Pourquoi ?* : Ces services sont *wrappés* par Angular et plus facilement testables qu'en utilisant document et window dans les tests. Ils vous aident à éviter d'avoir à *mocker* `document` et `window` vous-même. - -### $timeout et $interval -###### [Style [Y181](#style-y181)] - - - Utilisez [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) et [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) au lieu de `setTimeout` et `setInterval`. - - *Pourquoi ?* : Ces services sont *wrappés* par Angular et plus facilement testables et gèrent le cycle de *digest* d'Angular conservant un *data-binding* synchronisé. - -**[Retour en haut de page](#table-des-matières)** - -## Tests -Les tests unitaires aident à maintenir un code source propre, ainsi j'ai inclus quelques unes de mes recommandations sur les bases des tests unitaires avec des liens pour plus d'informations. - -### Écrire les tests avec des scenario -###### [Style [Y190](#style-y190)] - - - Écrivez un ensemble de tests pour chaque scenario. Commencer avec un test vide et complétez-les à mesure que vous écrivez le code pour le scenario. - - *Pourquoi ?* : Écrire les descriptions de tests aident à définir clairement ce que votre scenario devra faire, ne devra pas faire et comment mesurer la réussite. - - ```javascript - it('should have Avengers controller', function() { - //TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - //TODO - }); - - it('should have 10 Avengers', function() { - //TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - //TODO ($httpBackend?) - }); - - // et ainsi de suite - ``` - -### Librairie de test -###### [Style [Y191](#style-y191)] - - - Utilisez [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) pour les tests unitaires. - - *Pourquoi ?* : Jasmine et Mocha sont toutes deux largement utilisées dans la communauté Angular. Toutes les deux sont stables, bien maintenues, et fournissent des fonctionnalités de test robustes. - - Note : Lorsque vous utilisez Mocha, utilisez aussi une librairie d'assertion telle que [Chai](http://chaijs.com). Je prefère Mocha. - -### Lanceur de Test -###### [Style [Y192](#style-y192)] - - - Utilisez [Karma](http://karma-runner.github.io) comme lanceur de test. - - *Pourquoi ?* : Karma est facile à configurer pour lancer les tests ponctuellement ou automatiquement lorsqu'un changement est fait dans le code. - - *Pourquoi ?* : Karma s'intègre facilement dans votre processus d'intégration continue soit seul ou via Grunt ou Gulp. - - *Pourquoi ?* : Quelques EDI commencent à s'intégrer avec Karma, c'est le cas de [WebStorm](http://www.jetbrains.com/webstorm/) et [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Pourquoi ?* : Karma fonctionne bien avec les principaux outils d'automatisation de tâches tel que [Grunt](http://www.gruntjs.com) (avec [grunt-karma](https://github.com/karma-runner/grunt-karma)) ou [Gulp](http://www.gulpjs.com). Si vous utilisez Gulp, utilisez [Karma](https://github.com/karma-runner/karma) directement plutôt que via un plugin, son API peut s'utiliser directement. - - ```javascript - /* recommended */ - - // Exemple de Gulp fonctionnant directement avec Karma - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### `stub` et les `spy` -###### [Style [Y193](#style-y193)] - - - Utilisez [Sinon](http://sinonjs.org/) pour les `stub` et les `spy`. - - *Pourquoi ?* : Sinon fonctionne bien avec Jasmine et Mocha et étend les fonctionnalités de `stub` et de `spy` qu'ils offrent. - - *Pourquoi ?* : Sinon rend plus facile l'alternance entre Jasmine et Mocha, si vous voulez essayer les deux. - - *Pourquoi ?* : Sinon fournit des messages descriptifs quand les tests ne valident pas les assertions. - -### Navigateur sans interface graphique -###### [Style [Y194](#style-y194)] - - - Utilisez [PhantomJS](http://phantomjs.org/) pour exécuter les tests sur un serveur. - - *Pourquoi?* : PhantomJS est un navigateur sans interface graphique qui peut vous aider à exécuter les tests sans avoir besoin d'un navigateur "visuel". Ainsi vous n'avez pas besoin d'installer Chrome, Safari, IE, ou d'autres navigateurs sur votre serveur. - - Note : Que cela ne vous dispense pas de tester sur tous les navigateurs dans votre environnement,en fonction des clients que vous ciblez. - -### Analyse du code -###### [Style [Y195](#style-y195)] - - - Exécutez JSHint sur vos tests. - - *Pourquoi ?* : Les tests c'est aussi du code. JSHint peut vous aider à identifier les problèmes de qualité de code qui pourrait amener les tests à fonctionner de façon incorrecte. - -### Assouplissement des règles de JSHint avec les variables globales dans les tests -###### [Style [Y196](#style-y196)] - - - Assouplissez les règles sur votre code de test afin de permettre l'usage des variables globales courantes telles que `describe` et `expect`. Assouplissez les règles pour les expressions puisque Mocha les utilise. - - *Pourquoi ?* : Vos tests sont du code et requièrent à ce titre la même attention avec les mêmes règles de qualité de code que votre code de production. Cependant, les variables globales utilisées par les *frameworks* de test, par exemple, peuvent être négligées en les incluant dans les spécifications de test. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Ou alors vous pouvez rajouter le snippet suivant à votre fichier d'options JSHint. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Outils de test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Organisation des tests -###### [Style [Y197](#style-y197)] - - - Placez les fichiers des tests unitaires (specs) parallèle au code client. Placez les specs qui sont en charge de l'intégration avec le serveur ou celles qui testent plusieurs composants dans un répertoire `tests` séparé. - - *Pourquoi ?* : Les tests unitaires sont en corrélation directe avec les composants et fichiers qu'ils testent dans le code source. - - *Pourquoi ?* : Il est plus facile de les mettre à jour puisqu'ils sont toujours visibles. Quand vous développez, que vous fassiez du TDD, des tests en même temps que l'implémentation ou des tests après l'implémentation, les spécifications ne sont jamais loin ni des yeux ni de l'esprit, et ainsi ils ont plus de chance d'être maintenus, ce qui permet aussi de tenir une bon *coverage*. - - *Pourquoi ?* : Quand vous mettez à jour le code source, il est plus facile de mettre à jour les tests en même temps. - - *Pourquoi ?* : Les placer en parallèle les rend plus facile à trouver et facile à déplacer si vous déplacez les sources. - - *Pourquoi ?* : Avoir les spécifications proches permet au lecteur d'apprendre comment le composant est supposé être utilisé et découvrir ses limitations connues. - - *Pourquoi ?* : Séparer les spécifications de test afin qu'ils ne soient pas inclus dans le *build* est facile avec grunt ou gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Animations - -### Utilisation -###### [Style [Y210](#style-y210)] - - - Utilisez de subtiles [animations avec Angular](https://docs.angularjs.org/guide/animations) pour les transitions entre les états pour les vues et les éléments visuels de base. Incluez le [module ngAnimate](https://docs.angularjs.org/api/ngAnimate). Les trois clés sont la subtilité, la fluidité, l’homogénéité. - - *Pourquoi ?* : Des animations subtiles peuvent améliorer l'expérience utilisateur lorsqu'elles sont utilisées de façon appropriées. - - *Pourquoi ?* : Des animations subtiles peuvent améliorer les performances perçues lorsque les vues changent. - -### Moins d'une Seconde -###### [Style [Y211](#style-y211)] - - - Utilisez de courtes durées pour les animations. Je commence en général par 300ms et j'ajuste jusqu'à ce que le résultat soit celui attendu. - - *Pourquoi ?* : Les animations longues peuvent avoir des effets inverses sur l'expérience utilisateur et les performances perçues en donnant l'impression d'une application lente. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Utilisez [animate.css](http://daneden.github.io/animate.css/) pour les animations classiques. - - *Pourquoi ?* : Les animations que fournit animate.css sont rapides, fluides et faciles à ajouter à votre application. - - *Pourquoi ?* : Fournit de la cohérence à vos animations. - - *Pourquoi ?* : animate.css est largement utilisée et testé. - - Note : Lire ce [super post par Matias Niemelä sur les animations Angular](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Retour en haut de page](#table-des-matières)** - -## Commentaires - -### jsDoc -###### [Style [Y220](#style-y220)] - - - Si vous prévoyez de documenter votre code source, utilisez la syntaxe [`jsDoc`](http://usejsdoc.org/) pour documenter les noms des fonctions, leur descriptions, paramètres et valeurs de retour. Utilisez `@namespace` et `memberOf` pour s'adapter à l'architecture de votre application. - - *Pourquoi ?* : Vous pouvez générer (et regénérer) la documentation à partir de votre code, au lieu de l'écrire intégralement. - - *Pourquoi ?* : Cela permet d'avoir de la cohérence grâce un outil industriel standard. - - ```javascript - /** - * Factory de logger - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Logger de niveau applicatif - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logue les erreurs - * @param {String} msg Le message à loguer - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Retour en haut de page](#table-des-matières)** - -## JSHint - -### Utilisation d'un fichier d'options -###### [Style [Y230](#style-y230)] - - - Utilisez JSHint pour analyser votre JavaScript et assurez-vous d'avoir personnalisé son fichier d'options et incluez le dans le système de versioning. Lire la [documentation de JSHint](http://www.jshint.com/docs/) pour avoir les détails de chaque option. - - *Pourquoi ?* : Cela fournit une première alerte avant de *committer* son code dans le système de versioning. - - *Pourquoi ?* : Fournit de la cohérence dans votre équipe. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## JSCS - -### Utiliser un fichier de configuration -###### [Style [Y235](#style-y235)] - - - Utilisez JSCS pour valider vos styles de code pour votre JavaScript et pensez à personnaliser vos options pour JSCS et de l'inclure dans votre gestionnaire de versioning. Vous pouvez consulter la [documentation de JSCS](http://www.jscs.info) pour voir les détails et les options. - - *Pourquoi ?* : Fournit une première alerte avant de *commiter* sur votre gestionnaire de versioning. - - *Pourquoi ?* : Permet d'assurer une cohérence au sein de votre équipe. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "jsDoc": { - "checkAnnotations": true, - "checkParamNames": true, - "requireParamTypes": true, - "checkReturnTypes": true, - "checkTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Constantes - -### Globales des Librairies Externes -###### [Style [Y240](#style-y240)] - - - Créez une constante Angular pour les variables globales des librairies externes. - - *Pourquoi ?* : Fournit un moyen d'injecter des librairies tierces qui seraient sinon des globales. Cela améliore la testabilité du code en vous permettant de savoir plus facilement quelles sont les dépendances de vos composants (et évite de faire des abstractions qui fuient). Ça vous permet aussi de *mocker* ces dépendances, là où cela fait sens. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Utilisez des constantes pour les valeurs qui ne changent pas et ne viennent pas d'un autre service. Quand des constantes ne sont utilisées que par un module qui peut être réutilisé dans d'autres applications, placez les constantes dans un seul fichier par module nommé comme le module. Tant que c'est possible, gardez les constantes dans le module principal dans un fichier `constants.js`. - - *Pourquoi ?* : Une valeur qui peut changer, même rarement, devrait être récupérée d'un service afin de ne pas avoir à changer le code source. Par exemple, une URL pour un service de données pourrait être définie comme constante mais il serait mieux de lire cette valeur par appel à un web service. - - *Pourquoi ?* : Les constantes peuvent être injectées dans un composant Angular, y compris les *providers*. - - *Pourquoi ?* : Quand une application est divisée en modules qui peuvent être réutilisés dans d'autres applications, chacun de ces modules individuels devrait pouvoir fonctionner tout seul, y compris avec les constantes dépendantes. - - ```javascript - // Constantes utilisées par toute l'appli - angular - .module('app.core') - .constant('moment', moment); - - // Constantes utilisées seulement par le module de vente - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Modèles de fichiers et *snippets* -Utilisez des *templates* de fichiers ou des *snippets* pour vous aider à suivre des styles et des *patterns* cohérents. Voici des *templates* et/ou *snippets* pour quelques uns des éditeurs de texte pour le développement web et EDIs. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - *Snippets* Angular conformes avec ces styles et règles. - - - Téléchargez les [Snippets Angular pour Sublime](assets/sublime-angular-snippets.zip?raw=true) - - Placez-les dans votre répertoire `Package` - - Redémarrez Sublime - - Dans un fichier de type JavaScript, tapez ces commandes suivies par la touche `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Les *templates* de fichiers qui suivent ces styles et règles peuvent être trouvées sur [SideWaffle](http://www.sidewaffle.com) - - - Téléchargez l'extension [SideWaffle](http://www.sidewaffle.com) pour Visual Studio (fichier vsix) - - Exécutez le fichier vsix - - Redémarrez Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Vous pouvez importer dans les paramètres de WebStormLes les *templates* de fichiers et les *snippets* Angular qui suivent ces styles et ces règles : - - - Téléchargez les [*templates* de fichiers et *snippets* WebStorm pour Angular](assets/webstorm-angular-file-template.settings.jar?raw=true) - - Ouvrez WebStorm et allez dans le menu `File` - - Choisissez le menu `Import Settings` - - Sélectionnez le fichier et cliquez sur `OK` - - Dans un fichier de type JavaScript, tapez ces commandes suivies de la touche `TAB` : - - ```javascript - ng-c // crée un contrôleur Angular - ng-f // crée une factory Angular - ng-m // crée un module Angular - ``` - -### Atom -###### [Style [Y253](#style-y253)] - - - *Snippets* Angular qui suivent ces styles et ces règles. - ``` - apm install angularjs-styleguide-snippets - ``` - or - - Ouvrez Atom puis son *package manager* (Packages -> Settings View -> Install Packages/Themes) - - Cherchez le *package* 'angularjs-styleguide-snippets' - - Cliquez sur 'Install' pour installer le *package* - - - Dans un fichier JavaScript tapez ces commandes suivies de `TAB` : - - ```javascript - ngcontroller // Crée un contrôleur Angular - ngdirective // Crée une directive Angular - ngfactory // Crée une factory Angular - ngmodule // Crée un module Angular - ngservice // Crée un service Angular - ngfilter // Crée un filter Angular - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - *Snippets* Angular qui suivent ces styles et ces règles. - - Téléchargez les [*snippets* Angular pour Brackets](assets/brackets-angular-snippets.yaml?raw=true). - - Brackets Extension manager ( File > Extension manager ) - - Installez ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets). - - Cliquez sur l'ampoule dans la marge droite de Brackets. - - Cliquez sur `Settings` puis sur `Import` - - Choisissez le fichier et sélectionnez `skip`ou `override`. - - Cliquez sur `Start Import` - - - Dans un fichier JavaScript tapez ces commandes suivies de `TAB` : - - ```javascript - // Snippets de fichiers complets avec IIFE - ngcontroller // Crée un controller Angular - ngdirective // Crée une directive Angular - ngfactory // Crée une factory Angular - ngapp // Crée un module Angular - ngservice // Crée un service Angular - ngfilter // Crée un filter Angular - - // Snippets à chaîner - ngmodule // Crée an Angular module getter - ngstate // Crée an Angular UI Router state définition - ngconfig // Définit une fonction de configuration - ngrun // Définit une fonction run - ngroute // Définit une clause `when` pour ngRoute - ngtranslate // Utilise le service `$translate` avec sa promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - *Snippets* pour vim qui suivent ces styles et ces règles. - - - Téléchargez les [*snippets* vim pour Angular](assets/vim-angular-snippets?raw=true) - - Réglez [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - Copiez les *snippets* dans le répertoire approprié. - - ```javascript - ngcontroller // Crée un contrôleur Angular - ngdirective // Crée une directive Angular - ngfactory // Crée une factory Angular - ngmodule // Crée un module Angular - ngservice // Crée un service Angular - ngfilter // Crée un filter Angular - ``` - - -**[Retour en haut de page](#table-des-matières)** - -## Générateur Yeoman -###### [Style [Y260](#style-y260)] - -Vous pouvez utiliser le [générateur Yeoman HotTowel](http://jpapa.me/yohottowel) pour créer une application Angular qui servira de point de départ et qui suis cette charte stylistique. - -1. Installer `generator-hottowel` - - ``` - npm install -g generator-hottowel - ``` - -2. Créer un nouveau répertoire et aller dans ce répertoire - - ``` - mkdir myapp - cd myapp - ``` - -3. Exécuter le générateur - - ``` - yo hottowel helloWorld - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Routage -Le routage côté client est important pour créer un flux de navigation entre les vues et composer des vues constituées de nombreux *templates* et directives de plus petites tailles. - -###### [Style [Y270](#style-y270)] - - - Utilisez le [routeur d'AngularUI](http://angular-ui.github.io/ui-router/) pour faire le routage côté client. - - *Pourquoi ?* : `ui-router` offre toutes les fonctionnalités du routeur d'Angular et en ajoute quelques unes parmi lesquelles les routes imbriquées et les états. - - *Pourquoi ?* : La syntaxe est similaire à celle du routeur Angular par défaut et il est facile de migrer vers `ui-router`. - - - Note: Vous pouvez utiliser un *provider* tel que `routerHelperProvider` montré ci-dessous pour vous aidez à configurer les états à travers les fichiers pendant la phase de `run`. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - Définissez les routes pour les vues d'un module à l'endroit où elles existent. Chaque module devrait contenir le routage de ses vues. - - *Pourquoi ?* : Chaque module devrait être indépendant. - - *Pourquoi ?* : Si on ajoute ou enlève un module, on souhaite que l'application ne contienne que des routes qui aboutissent sur des vues existantes. - - *Pourquoi ?* : Cela rend facile l'activation ou la désactivation de portions de l'application sans se préoccuper d'avoir des routes orphelines. - -**[Retour en haut de page](#table-des-matières)** - -## Automatisation des Tâches -Utilisez [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) pour créer des tâches automatisées. Gulp favorise le code plutôt que la configuration tandis que Grunt tend vers la configuration plutôt que le code. À titre personnel je préfère Gulp car il me semble plus facile à lire et écrire, mais les deux sont excellents. - -> Apprenez-en plus sur Gulp et les *patterns* pour l'automatisation en allant voir [mon cours sur Pluralsight](http://jpapa.me/gulpps) - -###### [Style [Y400](#style-y400)] - - - Utilisez l'automatisation des tâches pour lister les fichiers de définition de module `*.module.js` avant tout autre fichier JavaScript de l'application. - - *Pourquoi ?* : Angular a besoin que la définition des modules soit faite avant qu'ils puissent être utilisés. - - *Pourquoi ?* : Nommer les modules avec un pattern spécifique tel que `*.module.js` les rends faciles à aller chercher avec une expression régulière et à les lister. - - ```javascript - var clientApp = './src/client/app/'; - - // Toujours aller chercher les fichiers de module en premier - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Filtres - -###### [Style [Y420](#style-y420)] - - - Évitez d'utiliser les filtres pour scanner toutes les propriétés de l'arborescence d'un objet complexe. Utilisez les filtres pour sélectionner des propriétés. - - *Pourquoi ?*: les filtres peuvent être sur-utilisés et peuvent avoir des effets négatifs sur les performances s'ils ne sont pas utilisés de façon appropriée. Par exemple, quand un filtre touche un gros objet dont l'arborescence est profonde. - -**[Retour en haut de page](#table-des-matières)** - -## Documentation -Pour tout le reste, allez voir la [documentation de l'API d'Angular](//docs.angularjs.org/api). - -## Contribuer - -Créez d'abord une *issue* pour discuter de potentiels changements ou ajouts à faire. Si vous avez des questions sur le guide, je vous encourage à les soumettre en temps qu'*issues*. Si vous trouvez des erreurs de frappe, créez une *pull request*. L'idée est de garder le contenu à jour et d'utiliser les fonctionnalités natives de Github pour aider à retracer l'évolution de ce dernier via les *issues* et les *pull requests*, lesquels sont indexées par Google. Pourquoi ? Parce que si vous vous posez une question, il y a des chances que quelqu'un d'autre se la soit posé ! Vous en apprendrez plus ci-dessous pour savoir comment contribuer. - -*En contribuant à ce dépôt, vous acceptez de rendre votre contenu accessible en accord avec la licence du dépôt.* - -### Processus - 1. Discuter des changements dans une *issue* GitHub. - 2. Ouvrir une *pull request* sur la branche `develop`, référencer l'*issue*, et expliquer le changement et la raison pour laquelle ce changement est pertinent. - 3. La *pull request* sera évaluée et *mergée* ou refusée. - -## License - -en bref; Utilisez ce guide. Les attributions sont appréciées._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Retour en haut de page](#table-des-matières)** diff --git a/a1/i18n/it-IT.md b/a1/i18n/it-IT.md deleted file mode 100644 index fa87b1b5..00000000 --- a/a1/i18n/it-IT.md +++ /dev/null @@ -1,3233 +0,0 @@ -# Guida stilistica ad Angular - -## Approvato dal Team di Angular -Uno speciale ringraziamento a Igor Minar, a capo del Team di Angular, per la revisione, aver contribuito al feedback e la fiducia accordatami per la conduzione di queste linee guida. - -## Scopo -*Guida stilistica dogmatica ad Angular per i team di [@john_papa](//twitter.com/john_papa)* - -Se stai cercando una guida stilistica dogmatica per le sintassi, convenzioni e struttura di applicazioni AngularJS, allora questo fa per te. Gli stili sono basati sulla mia esperienza di sviluppo con [AngularJS](//angularjs.org), presentazioni, [corsi di formazioni di Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) e del lavoro in team. - -L'obbiettivo di questa guida stilistica è di fare da vademecum alla costruzione di applicazioni con Angular mostrando le convenzioni che uso e, più importante, perché le uso. - ->Se ti piace questa guida, dai un'occhiata al mio corso [Angular Patterns: Clean Code](http://jpapa.me/ngclean) (in inglese) su Pluralsight come complemento a questa guida. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Eccezionalità della comunità e riconoscimenti -Mai lavorare nel vuoto. Ritengo che la comunità intorno ad Angular sia un gruppo incredibile con la passione di condividere le esperienze. Perciò, Todd Motto, un amico ed un esperto di Angular, ed io abbiamo collaborato su molti stili e convenzioni. Su molto siamo d'accordo, su altro meno. Ti invito a controllare le [linee guida di Todd](https://github.com/toddmotto/angularjs-styleguide) per avere cognizione del suo approccio e di come paragonarle. - -Molti dei mie stili sono frutto di parecchie sessioni di pair programming che [Ward Bell](http://twitter.com/wardbell) ed io abbiamo avuto. Il mio amico Ward ha di certo una influenza sull'evoluzione finale di questa guida. - -## Guarda gli stili in una App di Esempio -Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di aiuto vederle in pratica. Questa guida è accompagnata da una applicazione di esempio che segue questi stili e schemi. Puoi trovare l'[applicazione di esempio (chiamata modular) qui](https://github.com/johnpapa/ng-demos) nella cartella `modular`. Prendila, clonala o fanne un fork liberamente. [Le istruzioni su come eseguirla sono nel proprio readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -##Traduzioni -[Traduzioni di questa guida stilistica ad Angular](https://github.com/johnpapa/angularjs-styleguide/tree/master/i18n) sono gestite dalla comunità e possono essere trovate qui. - -## Tavola dei contenuti - - 1. [Responsabilità singola](#responsabilità-singola) - 1. [IIFE](#iife) - 1. [Moduli](#moduli) - 1. [Controller](#controller) - 1. [Service](#service) - 1. [Factory](#factory) - 1. [Data Service](#data-service) - 1. [Directive](#directive) - 1. [Risoluzioni di promesse per un controller](#risoluzioni-di-promesse-per-un-controller) - 1. [Annotazioni manuali per la Dependency Injection](#annotazioni-manuali-per-la-dependency-injection) - 1. [Minificazione e Annotazioni](#minificazione-e-annotazioni) - 1. [Gestione delle eccezioni](#gestione-delle-eccezioni) - 1. [Nomenclatura](#nomenclatura) - 1. [Principio "LIFT" per la struttura dell'applicazione](#principio-lift-per-la-struttura-dellapplicazione) - 1. [Struttura dell'applicazione](#struttura-dellapplicazione) - 1. [Modularità](#modularità) - 1. [Logica di Startup](#logica-di-startup) - 1. [Wrapper dei Servizi $ di Angular](#wrapper-dei-servizi--di-angular) - 1. [Test](#test) - 1. [Animazioni](#animazioni) - 1. [Commenti](#commenti) - 1. [JSHint](#jshint) - 1. [JSCS](#jscs) - 1. [Costanti](#costanti) - 1. [File Template e Snippet](#file-template-e-snippet) - 1. [Generatore Yeoman](#generatore-yeoman) - 1. [Routing](#routing) - 1. [Automazione dei processi](#automazione-dei-processi) - 1. [Filtri](#filtri) - 1. [Documentazione di Angular](#documentazione-di-angularjs) - 1. [Contribuire](#contribuire) - 1. [Licenza](#licenza) - -## Responsabilità singola - -### Regola dell'1 -###### [Stile [Y001](#stile-y001)] - - - Definire 1 componente per file. - - Il seguente esempio definisce il modulo `app` e le proprie dipendenze, definisce un controller e definisce una factory tutto nel medesimo file. - - ```javascript - /* evitare */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Gli stessi componenti sono ora separati nei propri file. - - ```javascript - /* consigliato */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* consigliato */ - - // some.controller.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* consigliato */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## IIFE -### Closures di JavaScript -###### [Stile [Y010](#stile-y010)] - - - Racchiudi i componenti di Angular in una Immediately Invoked Function Expression (IIFE) (Espressione di funzione immediatamente chiamata). - - *Perché?*: Una IIFE rimuove le variabili dallo scope globale. Questo aiuta a prevenire che variabili e dichiarazioni di funzione vivano più del previsto nello scope globale, inoltre aiuta ad evitare la collisione di variabili. - *Perché?*: Quando il tuo codice è minificato e raggruppato in un file singolo per il rilascio ad un server di produzione, potresti avere collisioni di variabili e parecchie variabili globali. Una IIFE ti protegge in entrambi i casi fornendo uno scope variabile per ogni file. - - ```javascript - /* evitare */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // La funzione logger è aggiunta come variabile globale - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // La funzione storage è aggiunta come variabile globale - function storage() { } - ``` - - - ```javascript - /** - * consigliato - * - * non ci sono più variabili globali - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Nota: Per essere più coincisi, il resto degli esempi in questa guida potrebbe omettere l'uso della sintassi IIFE. - - - Nota: Le IIFE evitano che il codice di test possa raggiungere membri privati come regular expression o funzioni di supporto le quali sono spesso oggetto di propri unit test. In ogni caso, queste possono essere testate per mezzo di membri accessibili o attraverso l'esposizione di propri componenti. Per esempio ponendo funzioni di supporto, regular expression o costanti nelle proprie factory o costanti. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Moduli - -### Evitare la collisione di nomi -###### [Stile [Y020](#stile-y020)] - - - Usa una convenzione unica per i nomi con separatori per sotto moduli. - - *Perché?*: Nomi unici aiutano ad evitare la collisione di nomi dei moduli. I separatori aiutano a definire gerarchie di moduli e dei propri sotto moduli. Per esempio `app` potrebbe essere il modulo principale mentre `app.dashboard` e `app.users` potrebbero essere moduli che sono usati come dipendenze di `app`. - -### Definizioni (altrimenti noti come Setter) -###### [Stile [Y021](#stile-y021)] - - - Dichiara moduli senza una variabile usando la sintassi setter. - - *Perché?*: con 1 componente per file, raramente c'è la necessità di introdurre una variabile per il modulo. - - ```javascript - /* evitare */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -Invece usa la più semplice sintassi setter. - - ```javascript - /* consigliato */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getter -###### [Stile [Y022](#stile-y022)] - - - Usando un modulo, evita l'uso di una variabile e piuttosto usa la concatenazione con la sintassi getter. - - *Perché?*: Ciò produce un codice maggiormente leggibile ed evita la collisione di variabili o buchi. - - ```javascript - /* evitare */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* consigliato */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Stile [Y023](#stile-y023)] - - - Setta solo una volta e prendi (get) per tutte le altre istanze. - - *Perché?*: Un modulo dovrebbe essere creato solamente una volta, quindi recuperato da lì in avanti. - - ```javascript - /* consigliato */ - - // per creare un modulo - angular.module('app', []); - - // per recuperare un modulo - angular.module('app'); - ``` - -### Funzioni con un nome vs funzioni anonime -###### [Stile [Y024](#stile-y024)] - - - Usa funzioni che hanno un nome piuttosto che passare una funzione anonima come in una callback. - - *Perché?*: Ciò produce codice maggiormente leggibile, è più facile farne il debug, e riduce la quantità di codice posto dentro una callback. - - ```javascript - /* evitare */ - angular - .module('app') - .controller('DashboardController', function() { }); - .factory('logger', function() { }); - ``` - - ```javascript - /* consigliato */ - - // dashboard.js - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Controller - -### Sintassi controllerAs nella View -###### [Stile [Y030](#stile-y030)] - - - Usa la sintassi [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) al posto della sintassi `classico controller con $scope`. - - *Perché?*: I controller sono costruiti, creati con "new" e forniti con un nuova istanza singola, inoltre la sintassi `controllerAs` è più somigliante ad un costruttore JavaScript rispetto alla `sintassi classica con $scope`. - - *Perché?*: Promuove l'uso del binding ad un oggetto che "usa il punto" nella View (p.e. `customer.name` invece di `name`), il quale è più contestuale, più facile da leggere ed evita qualunque questione di riferimenti che potrebbe accadere senza "uso del punto". - - *Perché?*: Aiuta ad evitare l'uso di chiamate a `$parent` nelle View che hanno controller nidificati. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### Sintassi controllerAs nel Controller -###### [Stile [Y031](#stile-y031)] - - - Usa la sintassi `controllerAs` al posto della sintassi `classico controller con $scope`. - - - La sintassi `controllerAs` usa `this` all'interno dei controller che fanno uso di `$scope` - - *Perché?*: `controllerAs` è una semplificazione sintattica per `$scope`. Puoi ancora fare il binding con la View ed accedere ai metodi di `$scope`. - - *Perché?*: Aiuta ad evitare la tentazione ad usare i metodi di `$scope` dentro un controller quando sarebbe meglio evitare o spostarli in una factory quindi referenziarli dal controller. Considera l'uso di `$scope` in un controller soltanto quando necessario. Per esempio, quando si pubblicano o sottoscrivono eventi usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), o [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considera di spostare questi tipi di utilizzi in una facotry e di invocarli da un controller. - - ```javascript - /* evitare */ - function CustomerController($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* consigliato - tuttavia vedi la prossima sezione */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs con vm -###### [Stile [Y032](#stile-y032)] - - - Usa una variabile che "catturi" `this` quando si utilizza la sintassi `controllerAs`. Scegli un nome della variabile consistente come `vm`, che sta per ViewModel. - - *Perché?*: La keyword `this` è contestuale e quando usata all'interno di una funzione dentro un controller può cambiare il proprio contesto. Catturare il contesto di `this` evita di incorrere in questo problema. - - ```javascript - /* evitare */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* consigliato */ - function CustomerController() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Nota: Puoi evitare ogni warning di [jshint](http://www.jshint.com/) ponendo il commento sotto riportato al di sopra della linea di codice. Comunque ciò non è richiesto quando la funzione è nominata ConUsoMaiuscole, dal momento che questa convenzione è intesa come funzione costruttore ovvero ciò che un controller è in Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Nota: Quando di creano watch in un controller usando `controller as`, puoi fare il watch del membro `vm.*` usando la seguente sintassi. (Crea watch con cautela poiché aggiungono più carico al ciclo di digest.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - Nota: Quando lavori con basi di codice molto estese, usare un nome che sia molto descrittivo può facilitare nella cognizione e rintracciabilità. Evita nomi oltremodo lunghi che sono proni ad errori. - - ```html - - - ``` - - ```html - - - ``` - -### Membri che possono fare il bind in alto -###### [Stile [Y033](#stile-y033)] - - - Poni i membri che possono fare il bind in alto nel controller, in ordine alfabetico, piuttosto che dispersi in tutto il codice del controller. - - *Perché?*: Porre i membri che posso fare il bind in alto rende semplice la lettura e aiuta l'istantanea identificazione di quali membri del controller possono essere collegati ed usati in una View. - - *Perché?*: Settare funzioni anonime nella medesima linea è semplice, tuttavia quando queste funzioni sono più lunghe di 1 linea di codice possono ridurre la leggibilità. Definire le funzione al di sotto i membri che possono fare il bind (funzioni che saranno chiamate) spostano i dettagli di implementazione in basso, tengono i membri che possono fare il bind in alto e rendono il codice più facile da leggere. - - ```javascript - /* evitare */ - function SessionsController() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* consigliato */ - function SessionsController() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - } - ``` - - ![Controller che usa "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Nota: Se la funzione è di 1 linea considera di poterla lasciare in alto fino a che la leggibilità non ne è compromessa. - - ```javascript - /* evitare */ - function SessionsController(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * linee - * di - * codice - * che peggiorano - * leggibilità - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* consigliato */ - function SessionsController(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // codice di 1 liena è OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - -### Dichiarazioni di funzione per nascondere i dettagli di implementazione -###### [Stile [Y034](#stile-y034)] - - - Usa le dichiarazioni di funzione per nascondere i dettagli di implementazione. Tieni i membri che possono fare il binding in alto. Quando necessiti di fare binding a una funzione nel controller, puntalo ad una dichiarazione di funzione che compaia dopo nel file. Questo è direttamente collegabile con la sezione Membri che possono fare il bind posti in alto. Per ulteriori dettagli guarda [questo post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) (in inglese). - - *Perché?*: Porre i membri che possono fare il binding in alto rende semplice la lettura ed aiuta l'immediata identificazione dei membri del controller che possono fare il binding ed usati nella View. (Come sopra.) - - *Perché?*: Porre i dettagli di implementazione di una funzione in seguito nel file sposta la complessità fuori dalla vista così che puoi vedere le cose importanti in alto. - - *Perché?*: Dichiarazioni di funzioni che sono chiamate così che non c'è rischio dell'uso di una funzione prima che sia definita (come sarebbe in caso di espressioni di funzione). - - *Perché?*: Non ti devi preoccupare di dichiarazioni di funzione che sposta `var a` prima di `var b` romperà il codice perché `a` dipende da `b`. - - *Perché?*: Con le espressioni di funzione l'ordine è critico. - - ```javascript - /** - * evitare - * Uso di espressioni di funzione. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Nota come le cose importanti, nell'esempio precedente, sono disseminate. Nell'esempio sotto, nota che le cose importanti sono in alto. Per esempio, i membri collegati al controller come `vm.avengers` e `vm.title`. I dettagli di implementazione sono in fondo. Questo è certamente più facile da leggere. - - ```javascript - /* - * consigliato - * Usare dichiarazione di funzione - * e membri che fanno in binding in alto. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Rimandare la logica del Controller ad un Service -###### [Stile [Y035](#stile-y035)] - - - Rimandare la logica in un controller delegandola ai service e factory. - - *Perché?*: La logica può essere riutilizzata da più controller quando posta in un service ed esposta tramite una funzione. - - *Perché?*: La logica posta in un service può essere più facilmente isolata in una unit test inoltre la chiamata della logica nel controller può essere oggetto di un mock con più facilità. - - *Perché?*: Rimuove dipendenze e nasconde i dettagli di implementazione dal controller. - - ```javascript - - /* evitare */ - function OrderController($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Prendi la URL di base per il servizio del credito - // Setta le intestazioni necessarie per il servizio del credito - // Prepara le URL della query string o i data object con i dati richiesti - // Aggiungi le informazioni sull'identificazione dell'utente così il servizio prende i dati sul limite del credito corretto - // Usare JSONP per questo browser se CORS non è supportato - return $http.get(settings) - .then(function(data) { - // Spacchetta i dati JSON nell'ogetto di risposta - // per trovare maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Errore dell'interprete - // Affronta timeout? nuovo tentativo? provare servizi alternativi? - // Rilancia con l'errore appropriato per essere letto dall'utente - }); - }; - } - ``` - - ```javascript - - /* consigliato */ - function OrderController(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showError); - }; - } - ``` - -### Tenere i controller "a fuoco" -###### [Stile [Y037](#stile-y037)] - - - Definisci un controller per vista e prova a non riutilizzare il controller per altre view. Piuttosto, sposta la logica riutilizzabile alle factory e mantieni il controller semplice ed a fuoco sulla propria view. - - *Perché?*: Riutilizzare i controller con diverse view è precario e sono necessari dei buoni test end-to-end (e2e) per assicurarne la stabilità in applicazioni su larga scala. - -### Assegnazione dei Controller -###### [Stile [Y038](#stile-y038)] - - - Quando un controller deve essere accoppiato ad una view ed un componente può essere riutilizzato da altri controller o view, definisci i controller insieme alle loro route. - - Nota: Se una View è caricata attraverso altri mezzi che una route, allora usa la sintassi `ng-controller="AvengersController as avengers"`. - - *Perché?*: Accoppiare il controller in una route consente a route diverse di invocare diversi accoppiamenti di controller e view. Quando i controller sono assegnati in una view usando [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) quella view sarà sempre associata al medesimo controller. - - ```javascript - /* evitare - quando usato con una route e si desidera una dinamicità negli accoppiamenti */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* consigliato */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Service - -### Singleton -###### [Stile [Y040](#stile-y040)] - - - I Service sono istanziati con la keyword `new`, usa `this` per metodi e variabili pubbliche. Dal momento che sono molto simili alle factory, usa queste ultime per consistenza. - - Nota: [Tutti i servizi di Angular sono singleton](https://docs.angularjs.org/guide/services). Questo significa che c'è soltanto una istanza di un dato servizio per iniettore. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Factory - -### Singola responsabilità -###### [Stile [Y050](#stile-y050)] - - - Le factory dovrebbero avere la [singola responsabilità](http://en.wikipedia.org/wiki/Single_responsibility_principle) che è incapsulata nel proprio contesto. Una volta che una factory eccede quello che è un singolo scopo, dovrebbe essere creata una nuova factory. - -### Singleton -###### [Stile [Y051](#stile-y051)] - - - Le factory sono singleton e ritornano un oggetto che contiene i membri del servizio. - - Nota: [Tutti i servizi di Angular sono singleton](https://docs.angularjs.org/guide/services). - -### Membri accessibili in alto -###### [Stile [Y052](#stile-y052)] - - - Esponi tutti i membri richiamabili del servizio (l'interfaccia) in alto, usando una tecnica derivata dal [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Perché?*: Porre i membri richiamabili in alto lo rende semplice da leggere e aiuta ad identificare istantaneamente quali membri del servizio possono essere chiamati ed essere oggetto di unit test (e/o simulati). - - *Perché?*: Questo è particolarmente utile quando i file iniziano ad allungarsi così da evitare la necessità di scorrere per leggere cosa è esposto. - - *Perché?*: Settare funzioni mentre procedi può essere facile ma quando tali funzioni sono più lunghe di 1 linea di codice possono ridurre la leggibilità e causare maggiore scorrimento. Definire l'interfaccia richiamabile attraverso i servizi ritornati sposta i dettagli di implementazione in basso, tiene l'interfaccia richiamabile in alto e rende più facile la lettura. - - ```javascript - /* evitare */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* consigliato */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - In questo modo i binding si riflettono in tutto l'oggetto host, i valori di base non possono essere solamente aggiornati usando il revealing module pattern. - - - ![Factory che usano "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Dichiarazioni di funzione per nascondere i dettagli di implementazione -###### [Stile [Y053](#stile-y053)] - - - Usa le dichiarazioni di funzioni per nascondere i dettagli di implementazione. Tieni i membri accessibili della factory in alto. Puntali alle dichiarazioni di funzioni che compaiono dopo nel file. Per ulteriori dettagli guarda [questo post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) (in inglese). - - *Perché?*: Porre i membri richiamabili in alto la rende semplice da leggere e aiuta ad identificare istantaneamente quali funzioni della factory possono accessibili esternamente. - - *Perché?*: Porre i dettagli di implementazione di una funzione dopo nel file sposta la complessità fuori dalla vista così che puoi vedere le cose importanti prima. - - *Perché?*: Le dichiarazioni di funzione sono richiamate così da non avere preoccupazioni circa l'uso di una funzione prima della sua definizione (come sarebbe nel caso di espressioni di funzione). - - *Perché?*: Non dovrai mai preoccuparti di dichiarazioni di funzione che spostano `var a` prima di `var b` rompendo il codice perché `a` dipende da `b`. - - *Perché?*: Con le espressioni di funzione l'ordine è critico. - - ```javascript - /** - * evita - * Uso di espressioni di funzioni - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // dettagli di implementazione vanno qui - }; - - var getAvengerCount = function() { - // dettagli di implementazione vanno qui - }; - - var getAvengersCast = function() { - // dettagli di implementazione vanno qui - }; - - var prime = function() { - // dettagli di implementazione vanno qui - }; - - var ready = function(nextPromises) { - // dettagli di implementazione vanno qui - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * consigliato - * Uso di dichiarazioni di funzioni - * e membri accessibili in alto. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // dettagli di implementazione vanno qui - } - - function getAvengerCount() { - // dettagli di implementazione vanno qui - } - - function getAvengersCast() { - // dettagli di implementazione vanno qui - } - - function prime() { - // dettagli di implementazione vanno qui - } - - function ready(nextPromises) { - // dettagli di implementazione vanno qui - } - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Data Service - -### Separare le chiamate ai dati -###### [Stile [Y060](#stile-y060)] - - - Rivedi la logica per gestire le operazioni con i dati e con la loro interazione delegandola ad una factory. Rendi il servizio di accesso ai dati responsabile per le chiamate XHR, conservazione locale, lo stashing in memoria o qualunque altra operazione sui dati. - - *Perché?*: La responsabilità del controller è la presentazione e raccolta di informazioni dalla view. Non dovrebbe occuparsi di come recuperare i dati, soltanto sapere a chi chiederli. La separazione dei servizi per i dati sposta la logica su come reperirli al servizio dei dati, rendendo il controller più semplice e più focalizzato sulla view. - - - *Perché?*: Ciò rende più semplice da testare (vere o simulate) le chiamate ai dati quando si testa un controller che usa un servizio ai dati. - - *Perché?*: L'implementazione di un servizio ai dati può avere del codice molto specifico su come trattare i repository dei dati. Questo può includere header, come comunicare con i dati o altri servizi quali `$http`. Separare la logica in un servizio ai dati incapsula questa logica in un posto unico nascondendo l'implementazione a consumatori esterni (forse un controller), rendendo inoltre più semplice cambiarne l'implementazione. - - ```javascript - /* consigliato */ - - // factory del servizio ai dati - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR fallita per getAvengers.' + error.data); - } - } - } - ``` - - Nota: Il servizio ai dati è chiamato dai consumatori, come un controller, nascondendo l'implementazione ai consumatori, come mostrato sotto. - - ```javascript - /* consigliato */ - - // controller che chiama la factory del servizio ai dati - angular - .module('app.avengers') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['dataservice', 'logger']; - - function AvengersController(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Ritornare una promessa dalle chiamate ai dati -###### [Stile [Y061](#stile-y061)] - - - Quando si chiama un servizio ai dati che ritorna una promessa come `$http`, ritorna a tua volta una promessa nella funzione di chiamata. - - *Perché?*: Puoi concatenare le promesse insieme e prendere ulteriori azioni dopo che la chiamata ai dati è completata e risolvere o rigettare la promessa. - - ```javascript - /* consigliato */ - - activate(); - - function activate() { - /** - * Passo 1 - * Chiedi alla funzione getAvengers per i - * dati sugli avenger e aspetta la promessa - */ - return getAvengers().then(function() { - /** - * Passo 4 - * Produci un'azione sulla risoluzione della promessa conclusiva - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Passo 2 - * Chiedi al servizio i dati e aspetta - * la promessa - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Passo 3 - * setta i dati e risolvi la promessa - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Directive -### Limita 1 per file -###### [Stile [Y070](#stile-y070)] - - - Crea una directive per file. Nomina il file per la directive. - - *Perché?*: È facile mescolare tutte le directive in un unico file ma difficoltoso da separare così che alcune siano condivise tra le applicazioni, alcune tra moduli, altre solo per un module. - - *Perché?*: Una directive per file è semplice da manutenere. - - > Nota: "**Best Practice**: Le directive dovrebbero fare pulizia alla fine. Puoi usare `element.on('$destroy', ...)` oppure `scope.$on('$destroy', ...)` per lanciare una funzione di pulizia quando la directive è rimossa" ... dalla documentazione di Angular. - - ```javascript - /* evitare */ - /* directives.js */ - - angular - .module('app.widgets') - - /* directive di ordini che è specifica per il modulo degli ordini */ - .directive('orderCalendarRange', orderCalendarRange) - - /* directive delle vendite che può essere usata dovunque nelle app di vendita */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* dirctive dello spinner che può essere usata dovunque nelle app */ - .directive('sharedSpinner', sharedSpinner); - - - function orderCalendarRange() { - /* dettagli di implementazione */ - } - - function salesCustomerInfo() { - /* dettagli di implementazione */ - } - - function sharedSpinner() { - /* dettagli di implementazione */ - } - ``` - - ```javascript - /* consigliato */ - /* calendar-range.directive.js */ - - /** - * @desc directive di ordini che è specifica al modulo ordini in una azienda di nome Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* dettagli di implementazione */ - } - ``` - - ```javascript - /* consigliato */ - /* customer-info.directive.js */ - - /** - * @desc directive delle vendite che può essere usato dovunque nella applicazione di vendita di una azienda di nome Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* dettagli di implementazione */ - } - ``` - - ```javascript - /* consigliato */ - /* spinner.directive.js */ - - /** - * @desc directive dello spinner che può essere usato dovunque nella applicazione di vendita di una azienda di nome Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* dettagli di implementazione */ - } - ``` - - Nota: Ci sono molte opzioni per i nomi delle directive, in particolare dal momento che possono essere usate in ambiti stretti o larghi. Scegline uno che sia chiaro e distino per la directive e il suo nome del file. Alcuni esempi sono sotto ma vedi la sezione sulla [Nomenclatura](#nomenclatura) per ulteriori suggerimenti. - -### Manipolare il DOM in una Directive -###### [Stile [Y072](#stile-y072)] - - - Quando devi manipolare direttamente il DOM, usa una directive. Se possono essere usate delle alternative come usare CSS per settare stili o i [servizi di animazione](https://docs.angularjs.org/api/ngAnimate), templating di Angular, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oppure [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), allora usa questi. Per esempio, se la directive deve semplicemente nascondere e mostrare, usa ngHide/ngShow. - - *Perché?*: Manipolare il DOM può essere difficoltoso da testare, debuggare e spesso ci sono modi migliori (p.e. CSS, animazioni, template) - -### Utilizza un prefisso unico per la Directive -###### [Stile [Y073](#stile-y073)] - - - Utilizza un corto, unico e descrittivo prefisso alla directive come `acmeSalesCustomerInfo` che potrebbe essere dichiarato in HTML come `acme-sales-customer-info`. - - *Perché?*: L'unico breve prefisso identifica il contesto delle directive e l'origine. Per esempio un prefisso `cc-` potrebbe indicare che la directive è parte di una app CodeCamper mentre `acme-` potrebbe indicare una direttiva per l'azienda Acme. - - Nota: Evita `ng-` poiché queste sono riservate per le directive di Angular. Cerca directive che sono largamente utilizzate per evitare il conflitto di nomi, come `ion-` per il [Framework Ionic ](http://ionicframework.com/). - -### Restringi a Elementi and Attributi -###### [Stile [Y074](#stile-y074)] - - - Quando crei una directive che abbia senso come elemento a se stante, considera la restrizione a `E` (elemento custom) e facoltativamente restringere a `A` (attributo custom). In generale, se può essere il suo stesso controllo, `E` è appropriato. Le linee guida generali sono di permettere `EA` ma tendono verso l'implementazione come un elemento quando è a se stante e come attributo quando accresce il proprio elemento DOM esistente. - - *Perché?*: È sensato. - - *Perché?*: È possibile consentire che sia usata come una classe ma se la directive agisce davvero come un elemento è più sensato che sia un elemento o al meno un attributo. - - Nota: EA è il default per Angular 1.3 e successivi - - ```html - -
- ``` - - ```javascript - /* evitare */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* consigliato */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directive e ControllerAs -###### [Stile [Y075](#stile-y075)] - - - Usa la sintassi `controller as` con una directive per essere consistente con l'utilizzo di `controller as` con un accoppiamento view/controller. - - *Perché?*: È sensato e non è difficile. - - Nota: La directive sotto dimostra alcuni dei modi in cui puoi usare lo scope all'interno di link e controller di directive usando controllerAs. Ho usato sulla stessa linea il template solo per mettere tutto in un unico posto. - - Nota: In relazione alla dependency injection, guarda [Annotazioni manuali per la Dependency Injection](#annotazioni-manuali-per-la-dependency-injection). - - Nota: Notare che il controller della directive è al di fuori della closure della directive. Questo stile elimina problematiche dove l'iniezione viene creata come codice non raggiungibile dopo un `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller : ExampleController, - // nota: Questo dovrebbe essere 'ExampleController' (il nome del controller esportato, come stringa) - // qualora faccia riferimento ad un controller definito nel proprio file separato. - controllerAs: 'vm', - bindToController: true // perché lo scope è isolato - }; - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Iniettare $scope solo per confronto - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Nota: Puoi inoltre nominare il controller quando lo inietti nella link function e accedi agli attributi della directive come proprietà del controller. - - ```javascript - // Alternativa all'esempio sopra riportato - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` -###### [Stile [Y076](#stile-y076)] - - - Usa `bindToController = true` quando usi la sintassi `controller as` con una directive al fine di fare il bind tra lo scope esterno e lo scope del controller della directive. - - *Perché?*: Rende semplice il bind tra lo scope esterno e lo scope del controller delle directive. - - Nota: `bindToController` è stato introdotto con Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Risoluzioni di promesse per un controller -### Promesse di attivazione di un Controller -###### [Stile [Y080](#stile-y080)] - - - Risolvi la logica di start-up per un controller in una funzione `activate`. - - *Perché?*: Porre la logica di start-up in una posizione consistente nel controller la rende semplice da localizzare, più consistente da testare e aiuta a prevenire la diffusione della logica di attivazione su tutto il controller. - - *Perché?*: La funzione `activate` del controller rende il riuso della logica adatto in caso di un refresh del controller/view, tiene la logica assieme, porta l'utente alla view più rapidamente, rende le animazini più facili su `ng-view` o `ui-view`e da la sensazione all'utente di istantaneità. - - Nota: Se hai necessità di annullare condizionalmente il route prima di iniziare ad usare il controller, usa piuttosto una [risoluzione nella route](#stile-y081). - - ```javascript - /* evitare */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* consigliato */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Promesse risolte nel route -###### [Stile [Y081](#stile-y081)] - - - Quando un controller dipende dal fatto che una promessa sia risolta prima che il controller sia attivato, risolvi queste dipendenze nel `$routeProvider` prima che la logica del controller sia eseguita. Se hai bisogno di annullare condizionalmente una route prima che il controller sia attivato, usa una risoluzione della route. - - - Usa la risoluzione della route quando decidi di annullare la route prima ancora di iniziara la transizione alla view. - - *Perché?*: Un controller può richiedere dei dati prima che si carichi. Quei dati potrebbero venire da una promessa di una factory su misura oppure [$http](https://docs.angularjs.org/api/ng/service/$http). Usando un [resolver della route](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) consenti che la promessa sia risolta prima che la logica del controller sia eseguita, così da poter prendere decisioni basandosi sui dati provenienti dalla promessa. - - *Perché?*: Il codice è eseguito dopo la route e nella funzione di attivazione del controller. La view inizia il caricamento immediatamente. Il data binding è effettivo quando le promesse nella funzione di attivazione sono risolte. Una animazione di “attendere” può essere mostrata durante la transizione alla view (per mezzo di ng-view o ui-view). - - Nota: Il codice è eseguito prima del route per mezzo di una promessa. Il rifiuto della promessa annulla la route. "Resolve" fa attendere la view mentre viene risolta. Una animazione “attendere” può essere mostrata prima della risoluzione e durante tutta la transizione alla vista. Se desideri arrivare alla view più in fretta e non hai bisogno di un punto di controllo per decidere se vuoi navigare alla view, considera piuttosto [Promesse di attivazione di un Controller](#stile-y080). - - ```javascript - /* evitare */ - angular - .module('app') - .controller('AvengersController', AvengersController); - - function AvengersController(movieService) { - var vm = this; - // non risolta - vm.movies; - // risolta in modo asincrono - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* migliore */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Nota: L'esempio sotto mostra il punto di risoluzione della route in una funzione con il nome per cui è più semplice da fare il debug e più semplice da gestire nella iniezione delle dependenze. - - ```javascript - /* ancora meglio */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Nota: La dipendenza del codice di esempio da `movieService` non è a prova di minificazione in se stessa. Per i dettagli su come rendere questo codice a prova di minificazione, vedi la sezione sulla [dependency injection](#annotazioni-manuali-per-la-dependency-injection) e sulla [minificazione e annotazione](#minificazione-e-annotazioni). - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Annotazioni manuali per la Dependency Injection - -### Non sicuro per la minificazione -###### [Stile [Y090](#stile-y090)] - - - Evita di usare abbreviazioni sintattiche per la dichiarazione di dipendenze senza usare un approccio a prova di minificazione. - - *Perché?*: I parametri dei componenti (p.e. controller, factory, etc.) saranno convertiti in variabili dal nome ridotto. Per esempio, `common` e `dataservice` potrebbero diventare `a` o `b` e non essere piò ritrovate da Angular. - - ```javascript - /* evita - non a prova di minificazione*/ - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController(common, dataservice) { - } - ``` - - Questo codice può produrre variabili da nome ridotto e perciò causare errori a runtime. - - ```javascript - /* evita - non a prova di minificazione*/ - angular.module('app').controller('DashboardController', d);function d(a, b) { } - ``` - -### Indentificazione manuale delle dipendenze -###### [Stile [Y091](#stile-y091)] - - - Usa `$inject` per identificare manualmente le tue dipendenze per i componenti di Angular. - - *Perché?*: Questa tecnica rispecchia la tecnica usata da [`ng-annotate`](https://github.com/olov/ng-annotate), che raccomando per l'automazione della creazione della minificazione sicura delle dipendenze. Se `ng-annotate` rileva che una iniezione è stata fatta, non la duplicherà. - - *Perché?*: Questo salvaguarda le tue dipendenze dall'essere vulnerabili alla questione della minificazione quando i parametri possono essere passati con nomi ridotti. Per esempio, `common` e `dataservice` possono diventare `a` o `b` e non essere più trovati da Angular. - - *Perché?*: Evita la creazione di dipendenze sulla stessa linea dal momento che lunghe liste possono essere difficili da leggere nell'array. Inoltre può essere fuorviante che l'array è una serie di stringhe mentre l'ultimo elemento è la funzione del componente. - - ```javascript - /* evitare */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', - function DashboardController($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* evitare */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* consigliato */ - angular - .module('app') - .controller('DashboardController', DashboardController); - - DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function DashboardController($location, $routeParams, common, dataservice) { - } - ``` - - Nota: Quando la tua funzione si trova dopo una dichiarazione di return, `$inject` potrebbe essere non raggiungibile (ciò può accadere in una directive). Puoi risolvere ciò sia spostando il Controller fuori dalla directive. - - ```javascript - /* evitare */ - // dentro la definizione di una directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Non raggiungibile - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* consigliato */ - // fuori la definizione di una directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Idetificazione manuale delle dipendenze di resolver della route -###### [Stile [Y092](#stile-y092)] - - - Usa `$inject` per identificare manualmente le tue dipendenze di resolver della route per i componenti di Angular. - - *Perché?*: Questa tecnica evade le funzioni anonime per il resolver della route, rendendolo più semplice da leggere. - - *Perché?*: Una dichiarazione `$inject` può facilmente precedere il resolver per gestire la produzione di dipendenze che siano a prova di minificazione. - - ```javascript - /* consigliato */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Minificazione e Annotazioni - -### ng-annotate -###### [Stile [Y100](#stile-y100)] - - - Usa [ng-annotate](//github.com/olov/ng-annotate) per [Gulp](http://gulpjs.com) o [Grunt](http://gruntjs.com) e commenta le funzioni che necessitano di automatizzare la dependency injection usando `/* @ngInject */` - - *Perché?*: Questo salvaguarda il tuo codice da ogni dipendenza che non segua le pratiche a prova di minificazione - - *Perché?*: [`ng-min`](https://github.com/btford/ngmin) è deprecato. - - >Preferisco Gulp poiché lo ritengo più semplice da scrivere, leggere e fare il debug. - - Il codice che segue non usa dipendenze che sono a prova di minificazione. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - ``` - - Quando il codice soprastante passa da ng-annotate viene prodotto il seguente output con l'annotazione `$inject` e diventa a prova di minificazione. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - - AvengersController.$inject = ['storage', 'avengerService']; - ``` - - Nota: Se `ng-annotate` rileva che l'iniezione è già stata fatta (p.e. `@ngInject` è stato rilevato), non duplicherà il codice di `$inject`. - - Nota: Quando si usa un resolver della route, puoi fare precedere il resolver della funzione con `/* @ngInject */` e ciò produrrà codice opportunamente annotato, mantenendo ogni iniezione delle dipendenze a prova di minificazione. - - ```javascript - // Usare l'annotazione @ngInject - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Nota: A partire da Angular 1.3 puoi usare il parametro `ngStrictDi` della directive [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) per rilevare ogni potenziale dipendenza che non sia a prova di minificazione. Quando presente, l'iniettore sarà creato in modalità "strict-di" causando il fallimento dell'invocazione di funzioni che non fanno uso esplicito di annotazione delle funzioni da parte dell'applicazione (queste potrebbero non essere a prova di minificazione). Informazioni di debug saranno mostrate nella console per aiutare nel tracciare il codice non confacente. Preferisco usare soltanto `ng-strict-di` per i soli scopi di debug. - `` - -### Usa Gulp o Grunt per ng-annotate -###### [Stile [Y101](#stile-y101)] - - - Usa [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) o [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in un task di automatizzazione delle build. Inietta `/* @ngInject */` prima di qualunque funzione che abbia delle dipendenze. - - *Perché?*: ng-annotate carpirà la maggior parte delle dipendenze ma talvolta necessita dell'uso del suggerimento sintattico `/* @ngInject */`. - - Il seguente codice è un esempio di un task di gulp che utilizza ngAnnotate. - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annota prima di fare l'uglify così che il codice sarà minificato correttamente. - .pipe(ngAnnotate({ - // true aiuta ad aggiunge @ngInject dove non usato. Inferisce. - // Non funzione con resolve, quindi deve essere esplicitato qui - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Gestione delle eccezioni - -### decoratori (decorator) -###### [Stile [Y110](#stile-y110)] - - - Usa un [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), al momento del config usando il servizio [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), sul servizio [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) per eseguire azioni ad hoc quando l'eccezione occorre. - - *Perché?*: Fornisce un modo consistente per la gestione delle eccezioni non trattate da Angular sia durante lo sviluppo che a runtime. - - Nota: Un'altra opzione è di fare l'override del servizio invece che usare un decorator. Questa è una buona opzione ma se vuoi tenere il comportamento di default ed estenderlo un decorator è consigliato. - - ```javascript - /* consigliato */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Potresti aggiungere l'errore ad una collezione del servizio, - * aggiungere l'errore a $rootScope, fare il log degli errori ad un server web remoto, - * oppure farlo localmente. O lanciare l'eccezione solamente. Sta del tutto a te. - * lancia l'eccezione; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Ricevitore di eccezioni -###### [Stile [Y111](#stile-y111)] - - - Crea una factory che espone un'interfaccia per ricevere ed elegantemente gestire le eccezioni. - - *Perché?*: Fornisce un modo consistente di ricevere le eccezioni che possono essere lanciate nel tuo codice (p.e. durante una chiamata XHR o il fallimento di promesse). - - Nota: Il ricevitore di eccezioni è buono per ricevere e reagire a specifiche eccezioni da parte di chiamate che sai ne possono generare una. Per esempio, quando fai una chiamata XHR per il recupero di dati da un servizio di un server web remoto e vuoi ricevere qualsiasi eccezione da ciò e reagire univocamente. - - ```javascript - /* consigliato */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Errori di routing -###### [Stile [Y112](#stile-y112)] - - - Gestisti e fai il log di tutti gli errori di routing usando [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Perché?*: Fornisce un modo consistente di gestire tutti gli errori di routing. - - *Perché?*: Potenzialmente fornisce una migliore esperienza all'utente se si verifica un errore di routing e li puoi indirizzare ad una schermata di aiuto o con opzioni di recupero. - - ```javascript - /* consigliato */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Annullamento del route: - * Su un errore di routing, vai alla dashboard. - * Fornisci una clausola di uscita se tenta di farlo per una seconda volta. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * A scelta fai il log usando un servizio ad hoc o $log. - * (Non dimenticare di iniettare il servizio ad hoc) - */ - logger.warning(msg, [current]); - - /** - * Su un errore di routing, vai ad un'altra route/stato. - */ - $location.path('/'); - } - ); - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Nomenclatura - -### Linee guida per assegnare i nomi -###### [Stile [Y120](#stile-y120)] - - - Usa nomi consistenti per tutti i componenti seguendo uno schema che descriva le funzionalità dei componenti e poi (a scelta) il suo tipo. Lo schema che consiglio è `feature.type.js`. Ci sono 2 nomi per la maggior parte dei componenti: - * il nome del file (`avengers.controller.js`) - * il nome del componente registrato con Angular (`AvengersController`) - - *Perché?*: Convezioni sui nomi aiutano a fornire un modo consistente per trovate i contenuti a colpo d'occhio. Essere consistenti in un progetto è vitale. Essere consistenti in un team è importante. Essere consistenti nell'insieme di un'azienda è tremendamente efficiente. - - *Perché?*: Le convezioni sulla nomenclatura dovrebbe semplicemente aiutare a trovare il tuo codice più rapidamente e renderlo più semplice da comprendere. - -### Nomi dei file per funzionalità -###### [Stile [Y121](#stile-y121)] - - - Usa nomi consistenti per tutti i componenti seguendo uno schema che descriva le funzionalità dei componenti e poi (a scelta) il suo tipo. Lo schema che consiglio è `feature.type.js`. - - *Perché?*: Fornisce un modo consistente per identificare facilmente i componenti. - - *Perché?*: Fornisce uno schema di corrispondenza per qualsiasi processo di automatizzazione. - - ```javascript - /** - * opzioni comuni - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * consigliato - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Nota: Un'altra convenzione comune è dare il nome al file del controller senza la parola `controller` nel nome del file come `avengers.js` invece di `avengers.controller.js`. Tutte le altre convenzioni continuano ancora a mantenere il suffisso del tipo. I controller sono i tipi di componenti più comuni perciò questo risparmia digitazione continuando ad essere facilmente identificabili. Consiglio di scegliere 1 convenzione e rimanere consistente nel tuo team. La mia preferenza va a `avengers.controller.js` che identifica `AvengersController`. - - ```javascript - /** - * consigliato - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Nomi dei file di test -###### [Stile [Y122](#stile-y122)] - - - Nomina le specifiche dei test in modo similare al componente che testano aggiundendo il suffisso `spec`. - - *Perché?*: Fornisce un modo consistente per identificare facilmente i componenti. - - *Perché?*: Fornisce uno schema di corrispondenza per [karma](http://karma-runner.github.io/) o altri esecutori di test. - - ```javascript - /** - * consigliato - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Nomi dei controller -###### [Stile [Y123](#stile-y123)] - - - Usa nomi consistenti per tutti i controller nominandoli come le loro funzionalità. Usa UpperCamelCase per i controller, dal momento che sono costruttori. - - *Perché?*: Fornisce un modo consistente per identificare e referenziare facilmente i controller. - - *Perché?*: UpperCamelCase è una convezione per identificare un oggetto che può essere istanziato usando un costruttore. - - ```javascript - /** - * consigliato - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengers', HeroAvengers); - - function HeroAvengers(){ } - ``` - -### Suffisso nel nome di un controller -###### [Stile [Y124](#stile-y124)] - - - Aggiungi `Controller` alla fine del nome del controller. - - *Perché?*: Il suffisso `Controller` è quello più comunemente usato ed è più esplicitamente descrittivo. - - ```javascript - /** - * consigliato - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -### Nomi delle factory e dei service -###### [Stile [Y125](#stile-y125)] - - - Usa una nomenclatura consistente per tutte le factory e i service dando i nomi a seguito delle loro funzionalità. Usa il camel-case per service e factory. Evita di pre-nominare factory e service con `$`. Aggiungi il suffisso `Service` a service e factory soltanto quando non è chiaro cosa siano (p. es. quando si tratta di nomi). - - *Perché?*: Fornisce un modo consistente per identificare facilmente e referenziare le factory. - - *Perché?*: Evita collisione di nomi con factory e servizi di Angular esistenti che usano il prefisso `$`. - - *Perché?*: Service con nomi evidenti quali `logger` on richiedono il suffisso. - - *Perché?*: Nomi di service quali `avengers` sono nomi, richiedono in suffisso e dovrebbero essere nominati `avengersService`. - - ```javascript - /** - * consigliato - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - - ```javascript - /** - * consigliato - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // customer.service.js - angular - .module - .service('customerService', customerService); - - function customerService() { } - ``` - -### Nomi dei componenti directive -###### [Stile [Y126](#stile-y126)] - - - Usa nomi consistenti per tutte le directive usando il camel-case. Usa un breve prefisso che descriva l'area alla quale la directive appartiene (alcuni esempi sono prefissi relativi all'azienda o al progetto). - - *Perché?*: Fornisce un modo consistente per identificare e referenziare facilmente i componenti. - - ```javascript - /** - * consigliato - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // l'uso è - - function xxAvengerProfile(){ } - ``` - -### Moduli -###### [Stile [Y127](#stile-y127)] - - - Quando ci sono moduli multipli, il modulo principale è nominato come `app.module.js` mentre altri moduli dipendenti prendono i nomi da ciò che rappresentano. Per esempio, un modulo admin è nominato `admin.module.js`. I rispettivi nomi con i quali sono registrati saranno `app` e `admin`. - - *Perché?*: Fornisce consistenza per app che hanno più di un modulo e per poter espandere verso applicazioni a larga scala. - - *Perché?*: Fornisce un modo semplice al fine di usare processi automatici per caricare prima tutte le definizioni di moduli, successivamente tutti gli altri file di Angular (per il bundling). - -### Configurazione -###### [Stile [Y128](#stile-y128)] - - - Separa la configurazione di un modulo nel proprio file chiamato come il modulo. Un file di configurazione per il modulo principale `app` è chiamato `app.config.js` (o semplicemente `config.js`). Un file di configurazione per un modulo chiamato `admin.module.js` sarà `admin.config.js`. - - *Perché?*: Separa la configurazione dalla definizione, componenti e codice di attivazione del modulo. - - *Perché?*: Fornisce una posizione identificabile per settare la configurazione di un modulo. - -### Route -###### [Stile [Y129](#stile-y129)] - - - Separa la configurazione delle route nei propri file. Esempi possono essere `app.route.js` per il modulo principale e `admin.route.js` per il modulo `admin`. Anche in piccole app preferisco questa separazione dal resto della configurazione. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Principio "LIFT" per la struttura dell'applicazione -### LIFT -###### [Stile [Y140](#stile-y140)] - - - Struttura la tua app tale da poter `L`ocate (localizzare) il codice facilmente, `I`dentify (identificare) il codice a colpo d'occhio, tenere la struttura più `F`lattest (piatta) che puoi, e `T`ry (provare) a rimanere DRY (Don't Repeat Yourself - Non ripetersi). La struttura dovrebbe seguire queste 4 linee guida basilari. - - *Perché LIFT?*: Fornisce una struttura consistente che scala bene, è modulare e rende più semplice aumentare l'efficienza dello sviluppatore nel trovare facilmente il codice. Un altro modo per verificare la struttura della tua app è chiederti: Quanto rapidamente puoi aprire e lavorare ad una funzionalità in tutti i file che sono collegati? - - Quando ritengo che la mia struttura non sia confortevole, torno indietro a rivedere le linee guida LIFT - - 1. `L`ocalizzare il nostro codice con facilità - 2. `I`dentificare il codice a colpo d'occhio - 3. `F`lat (pitta) struttura quanto più possibile - 4. `T`ry (prova) a restare DRY (Don’t Repeat Yourself) o T-DRY - -### Locate - localizzare -###### [Stile [Y141](#stile-y141)] - - - Rendi intuitivo, semplice e facile localizzare il codice. - - *Perché?*: Ritengo ciò essere estremamente importante per un progetto. Se il team non è in grado di trovare i file di cui necessita rapidamente, non sarà in grado di lavorare il più efficacemente possibile, per cui la struttura necessita un cambiamento. Potresti non sapere il nome del file o dove sono i file a questo correlati quindi posizionarli in nel posto più intuitivo e prossimi gli uni agli altri fa risparmiare un mucchio di tempo. Una descrittiva struttura delle cartelle può essere d'aiuto. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify - identificare -###### [Stile [Y142](#stile-y142)] - - - Guardando un file dovresti istantaneamente sapere ciò che contiene e cosa rappresenta. - - *Perché?*: Spendi meno tempo a rintracciare e beccare il codice e diventa più efficiente. Se per fare ciò hai bisogno di nomi dei file più lunghi, fallo. Si descrittivo con i nomi dei file e tieni il contenuto del file con esattamente 1 componente. Evita file con più di un controller, diversi service o un misto. Ci sono delle eccezioni alla regola "1 per file" ovvero quando ho una serie di piccole funzionalità correlate l'un l'altra: continuano ad essere facilmente identificabili. - -### Flat - piatto -###### [Stile [Y143](#stile-y143)] - - - - Tieni la struttura delle cartelle piatta il più a lungo possibile. Quando arrivi ad avere 7 o più file, inizia a considerarne una separazione. - - *Perché?*: Nessuno vuole cercare 7 livelli di cartelle per trovare un file. Pensa ai menù di un sito web.. qualunque cosa oltre i 2 livelli dovrebbe esser presa in seria considerazione. Nella struttura di cartella non c'è una regola con un numero esattamente definito ma quando una cartella contiene 7-10 file, potrebbe essere il momento di creare una sottocartella. Basalo su un livello a te comodo. Usa una struttura più piatta fino a che c'è l'ovvia necessità (praticando il resto dei principi LIFT) di creare una nuova cartella. - -### T-DRY (Try to Stick to DRY) - Prova a non ripeterti -###### [Stile [Y144](#stile-y144)] - - - Si DRY, ma non diventare pazzo e sacrificare la leggibilità. - - *Perché?*: Non ripetersi è importante ma non è cruciale se sacrifica altri principi LIFT, per questo il principio è Try (provare) DRY. Non voglio digitare session-view.html perché è ovvio essere una view. Se non è ovvio o se per convenzione allora nominala così. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Struttura dell'applicazione - -### Linee guida generali -###### [Stile [Y150](#stile-y150)] - - - Abbi una visione a breve termine dell'implementazione e una a lunga scadenza. In altre parole, parti in piccolo ma tieni in mente su dove l'app è diretta lungo il percorso. Tutto il codice dell'app va nella cartella principale chiamata `app`. Tutto il contenuto rispetta 1 funzione per file. Ogni controller, service, module, view nel proprio file. Tutti gli script di terze parti sono poste in una altra cartella principale e non nella cartella `app`. Non le ho scritte e non voglio facciano disordine nella mia app (`bower_components`, `scripts`, `lib`). - - Nota: Trovi più dettagli e le motivazioni di questa struttura nel [post originale sulla struttura delle applicazioni](http://www.johnpapa.net/angular-app-structuring-guidelines/) (in inglese). - -### Layout -###### [Stile [Y151](#stile-y151)] - - - Metti i componenti che definiscono il layout globale dell'applicazione in una cartella con il nome `layout`. Questi possono includere un shell view e controller che agiscono come contenitori per l'app, navigazione, menù, aree per i contenuti ed altre regioni. - - *Perché?*: Organizza tutto il layout in una sola posizione riutilizzabile lungo tutta l'applicazione. - -### Struttura Cartella-per-Funzionalità -###### [Stile [Y152](#stile-y152)] - - - Crea cartelle che abbiamo in nome per la funzionalità che rappresentano. Quando una cartella cresce fino a contenere più di 7 file, inizia a considerare la creazione di un'altra cartella. La tua soglia potrebbe essere differente, aggiustala di conseguenza. - - *Perché?*: Uno sviluppatore può localizzare il codice, identificare ciò che rappresenta a colpo d'occhio, la struttura è piatta come deve essere e non c'è ripetitività o nomi ridondanti. - - *Perché?*: Le linee guida LIFT sono tutte coperte. - - *Perché?*: Aiuta a ridurre la possibilità che l'app divenga disordinata per mezzo dell'organizzazione dei contenuti mantenendola allineata con i principi LIFT. - - *Perché?*: Quando ci sono parecchi file (più di 10) la loro localizzazione è più semplice con una struttura di cartelle che sia consistente e più difficile in una struttura piatta. - - ```javascript - /** - * consigliato - */ - - app/ - app.module.js - app.config.js - app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - session-detail.html - session-detail.controller.js - ``` - - ![Struttura dell'App di Esempio](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Nota: Non usare una strutturazione del tipo cartella-per-tipo per la tua app. Questo richiede spostarsi tra molte cartelle quando si lavora su una funzionalità e diventa rapidamente scomodo quando l'app cresce di 5, 10 o più di 25 tra view e controller (ed altre funzionalità), per cui è più difficile rispetto alla localizzazione basata su cartella-per-funzionalità. - - ```javascript - /* - * evitare - * Alternativa cartella-per-tipo - * Consiglio invece "cartella-per-funzionalità". - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Modularità - -### Molti moduli piccoli e autonomi -###### [Stile [Y160](#stile-y160)] - - - Crea moduli piccoli che incapsulino una responsabilità. - - *Perché?*: Applicazioni modulari rendono semplice l'inclusione dal momento che consentono ai team di sviluppo la costruzione di tagli verticali dell'applicazione e il suo roll out incrementale. Questo significa che è possibile aggiungere nuove funzionalità mentre vengono sviluppate. - -### Creare un modulo App -###### [Stile [Y161](#stile-y161)] - - - Crea un modulo principale per l'applicazione il cui ruolo sia di mettere insieme tutti gli altri moduli e funzionalità della tua applicazione. Chiamalo con il nome della tua applicazione. - - *Perché?*: Angular incoraggia la modularità e schemi di separazione. La creazione di un modulo principale il cui ruolo sia quello di legante tra gli altri moduli consente un modo lineare di aggiungere o rimuovere moduli dall'applicazione. - -### Tenere il modulo App snello -###### [Stile [Y162](#stile-y162)] - - - Nel modulo principale metti solo la logica che serva da collante per l'app. Lascia le funzionalità ognuna al proprio modulo. - - *Perché?*: L'aggiunta di ruoli addizionali al modulo principale per il recupero dei dati, il mostrare viste o altra logica non correlata al tenere insieme l'applicazione sporca il modulo principale e rende entrambi gli insiemi di funzionalità più complessi da riusare o rimuovere. - - *Perché?*: Il modulo app diventa un manifesto che descrive quali moduli aiutano a definire l'applicazione. - -### Aree di funzionalità sono Moduli -###### [Stile [Y163](#stile-y163)] - - - Crea moduli che rappresentino aree di funzionalità come layout, servizi riusabili e condivisi, pannelli di controllo e funzioni specifiche all'app (p.e. clienti, amministrazione, vendite). - - *Perché?*: Moduli autonomi possono essere aggiunti all'applicazione con piccola o nessuna frizione. - - *Perché?*: Sprint o iterazioni possono focalizzarsi sulle aree di funzionalità e renderle disponibili alla fine dello sprint o dell'iterazione. - - *Perché?*: Separare aree di funzioni in moduli rende più semplice testare i moduli in isolamento e il riutilizzo del codice. - -### Blocchi riutilizzabili sono Moduli -###### [Stile [Y164](#stile-y164)] - - - Crea moduli che rappresentino blocchi di applicazione riutilizzabili per servizi comuni quali la gestione delle eccezioni, il log, la diagnostica, sicurezza e il data stashing locale. - - *Perché?*: Questi tipi di funzionalità sono richieste in molte applicazioni, perciò tenendole separate nel proprio modulo possono essere generiche e riutilizzate in applicazioni diverse. - -### Dipendenze dei Moduli -###### [Stile [Y165](#stile-y165)] - - - Il modulo principale dell'applicazione dipende dai moduli di funzionalità specifiche dell'app e da qualunque altro modulo che sia condiviso o riusabile. - - ![Modularità e Dipendenze](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Perché?*: Il modulo principale dell'app contiene un manifesto che sia facilmente identificabile con le funzionalità dell'applicazione. - - *Perché?*: Ogni area di funzionalità contiene un manifesto di ciò da cui dipende, in modo tale da poter essere usato come dipendenza in altre applicazioni e continuare a funzionare. - - *Perché?*: Funzionalità intra-app come servizio ai dati condiviso diventano facilmente localizzabili da dentro `app.core` (scegli il nome che più ti piace per questo modulo). - - Nota: Questa è una strategia per la consistenza. Ci sono diverse buone opzioni in questo caso. Scegline una che sia consistente, segua le regole delle dipendenze di Angular e sia facile da manutenere e scalare. - - > La mia struttura varia leggermente tra progetti ma tutti seguono queste linee guida per la strutturazione e modularità. L'implementazione può variare in relazione alle funzionalità ed al team. In altre parole, non ti bloccare su una struttura che sia esattamente uguale ma giustifica la tua struttura tenendo a mente l'uso di consistenza, manutenibilità ed efficienza. - - > In una applicazione piccola, si può considerare di mettere tutte le dipendenze condivise nel modulo dell'app dove i moduli delle funzionalità non hanno dipendenze dirette. Ciò rende semplice mantenere l'applicazione più piccola ma rende più difficile riutilizzare i moduli fuori dell'applicazione stessa. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Logica di Startup - -### Configurazione -###### [Stile [Y170](#stile-y170)] - - - Inietta codice nel [modulo di configurazione](https://docs.angularjs.org/guide/module#module-loading-dependencies) che deve essere configurato prima dell'esecuzione dell'app angular. I candidati ideali includono provider e costanti. - - *Perché?:* Questo rende più facile ottenere pochi posti atti alla configurazione - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Blocchi Run -###### [Stile [Y171](#stile-y171)] - - - Qualunque codice che necessiti di essere eseguito quando un'applicazione si avvia dovrebbe essere dichiarato in una factory, esposto tramite funzione ed iniettato nel [blocco run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Perché?*: Codice posto direttamente in un blocco run può essere difficile da testate. Metterlo in una factory lo rende più astratto e simulabile (farne un mock). - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Wrapper dei Servizi $ di Angular - -### $document e $window -###### [Stile [Y180](#stile-y180)] - - - Usa [`$document`](https://docs.angularjs.org/api/ng/service/$document) e [`$window`](https://docs.angularjs.org/api/ng/service/$window) al posto di `document` e `window`. - - *Perché?*: Questi servizi sono gestiti da Angular e più facilmente testabili che l'uso di document e window nei test. Ciò ti aiuta ad evitare di fare mock di document e window. - -### $timeout e $interval -###### [Stile [Y181](#stile-y181)] - - - Usa [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) e [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) al posto di `setTimeout` e `setInterval`. - - *Perché?*: Questi servizi sono gestiti da Angular e più facilmente testabili e gestiscono il ciclo di digest di Angular così da tenere sincronizzato il data binding. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Test -Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcune mie raccomandazioni per le fondamenta dello unit testing con link per ulteriori dettagli. - -### Scrivi test che abbiano storie -###### [Stile [Y190](#stile-y190)] - - - Scrivi un set di test per ogni storia. Inizia con un test vuoto e riempilo fino a scrivere il codice per la storia. - - *Perché?*: Scrivere la descrizione del test aiuta a definire chiaramente cosa la tua stoia farà, non farà e come puoi misurarne il successo. - - ```javascript - it('dovrebbe avere il controller Avenger', function() { - //TODO - }); - - it('dovrebbe trovare 1 Avenger quando filtrato per nome', function() { - //TODO - }); - - it('dovrebbe avere 10 Avenger', function() { - //TODO (fare un mock dei dati?) - }); - - it('dovrebbe ritornare Avenger via XHR', function() { - //TODO ($httpBackend?) - }); - - // così via - ``` - -### Librerie per i test -###### [Stile [Y191](#stile-y191)] - - - Usa [Jasmine](http://jasmine.github.io/) oppure [Mocha](http://mochajs.org) per lo unit testing. - - *Perché?*: Sia Jasmine che Mocha sono largamente utilizzati nella comunità di Angular. Entrambi son stabili, ben manutenuti e forniscono funzionalità solide per i test. - - Nota: Usando Mocha, tieni in considerazione di usare anche una libreria di asserzione come [Chai](http://chaijs.com). Io preferisco Mocha. - -### Esecutori di Test -###### [Stile [Y192](#stile-y192)] - - - Usa [Karma](http://karma-runner.github.io) come esecutore di test. - - *Perché?*: Karma è facilmente configurabile per essere eseguito una sola volta o automaticamente quando cambia il tuo codice. - - *Perché?*: Karma si aggancia facilmente al tuo processo di Integrazione Continua da solo o attraverso Grunt o Gulp. - - *Perché?*: Alcuni IDE cominciano ad integrarsi con Karma, come [WebStorm](http://www.jetbrains.com/webstorm/) e [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Perché?*: Karma lavora bene con leader di automazione di processo quali [Grunt](http://www.gruntjs.com) (con [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com). Quando usi Gulp, usa [Karma](https://github.com/karma-runner/karma) direttamente e non con un plugin dal momento che le API possono essere richiamate direttamente. - - ```javascript - /* consigliato */ - - // Esempio di Gulp che usa direttamente Karma - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### Stubbing e Spying -###### [Stile [Y193](#stile-y193)] - - - Usa [Sinon](http://sinonjs.org) per lo stubbing e spying. - - *Perché?*: Sinon lavora bene sia con Jasmine che Mocha ed estende le funzionalità di stubbing e spying che questi offrono. - - *Perché?*: Sinon rende più semplice il passaggio tra Jasmine e Mocha, nel caso voglia usarli entrambi. - -### Headless Browser -###### [Stile [Y194](#stile-y194)] - - - Usa [PhantomJS](http://phantomjs.org/) per eseguire i test su un server. - - *Perché?*: PhantomJS è un headless browser (browser senza interfaccia grafica) che aiuta l'esecuzione di test senza la necessità di un browser "visuale". Quindi non devi installare Chrome, Safari, IE o altri browser sul server. - - Nota: Dovresti in ogni caso testare tutti i browser nel tuo ambiente, come appropriato per il pubblico che ne è il target. - -### Analisi del codice -###### [Stile [Y195](#stile-y195)] - - - Esegui JSHint sui tuoi test. - - *Perché?*: I test sono codice. JSHint può aiutare ad identificare problemi di qualità del codice che causano l’improprio funzionamento del test. - -### Alleviare le regole sulle variabili globali di JSHint per i test -###### [Stile [Y196](#stile-y196)] - - - Rilassa le regole sul codice dei test per consentire variabili globali comuni quali `describe` ed `expect`. - - *Perché?*: I tuoi test sono codice e richiedono al medesima attenzione e regole per la qualità del codice come tutto il resto del codice di produzione. Comunque, variabili globali usate dai framework di test, per esempio, possono essere rilassate includendole nelle specifiche dei test. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Oppure puoi aggiungere le righe che seguono al tuo file JSHint Options. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Strumenti per i test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Organizzazione dei test -###### [Stile [Y197](#stile-y197)] - - - Posiziona i file degli unit test (spec) vicino al codice del client. Posiziona le specifiche che coprono l'integrazione con il server o che testano più componenti in una cartella separata `tests`. - - *Perché?*: Gli unit test hanno una correlazione diretta con un componente specifico e file nei sogenti. - - *Perché?*: È più semplice da tenere aggiornati dal momento che sono sempre a vista. Quando scrivi codice, sia che tu faccia TDD o fai i test durante o dopo lo sviluppo, le scpecifiche sono sempre di fianco e mai fuori dalla vista o dai pensieri, quindi è più probabile che siano aggiornati e ciò consente inoltre a mantenere una migliore copertura del codice. - - *Perché?*: Quando aggiorni i sorgenti, è più semplice andare ad aggiornare anche i test. - - *Perché?*: Posizionarli vicino rende semplice trovarli e spostarli con i sorgenti qualora ciò accada. - - *Perché?*: Avere le specifiche vicino rende più facile al lettore del codice sorgente imparare come il componente dovrebbe essere usato e scoprire le sue limitazioni. - - *Perché?*: Separare le specifiche così da non essere nella build di distribuzione è semplice con grunt o gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Animazioni - -### Utilizzo -###### [Stile [Y210](#stile-y210)] - - - Usa sfumate [animazioni con Angular](https://docs.angularjs.org/guide/animations) per fare transizioni tra stati per viste ed elementi visuali primari. Includi il [modulo ngAnimate](https://docs.angularjs.org/api/ngAnimate). Le 3 chiavi sono sfumate, dolci e continue. - - *Perché?*: Animazioni sfumate possono migliorare l'esperienza dell'utente quando usate in modo appropriato. - - *Perché?*: Animazioni sfumate possono migliorare la percezione di prestazioni nella transizione tra le viste. - -### Sotto il secondo -###### [Stile [Y211](#stile-y211)] - - - Usa animazioni che abbiano una durata breve. Generalmente parto con 300 ms e aggiusto finché non è appropriato. - - *Perché?*: Animazioni lunghe possono avere l'effetto contrario sull'esperienza dell'utente e di percezzione delle prestazioni che danno il senso di una applicazione lenta. - -### animate.css -###### [Stile [Y212](#stile-y212)] - - - Usa [animate.css](http://daneden.github.io/animate.css/) per animazioni convenzionali. - - *Perché?*: Le animazione che animate.css fornisce sono veloci, dolci e facili da aggiungere alla tua applicazione. - - *Perché?*: Da consistenza alle tue animazioni. - - *Perché?*: animate.css è ampiamente usato e testato. - - Nota: Leggi questo [ottimo post di Matias Niemelä sulle animazioni di Angular](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Commenti - -### jsDoc -###### [Stile [Y220](#stile-y220)] - - - Se hai intenzione di produrre documentazione, usa la sintassi di [`jsDoc`](http://usejsdoc.org/) per documentare nomi di funzione, descrizione, parametri e ciò che ritorna. Usa `@namespace` e `@memberOf` per adattarlo alla stuttura della tua app. - - *Perché?*: Puoi generare (e rigenerare) documentazione dal tuo codice, invece di scriverlo partendo da zero. - - *Perché?*: Fornisce consistenza usando un tool comune nell'industria. - - ```javascript - /** - * Factory di Log - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Logger di tutta l'applicazione - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Log degli errori - * @param {String} msg Messaggio di cui fare il log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## JSHint - -### Usa un file di opzioni -###### [Stile [Y230](#stile-y230)] - - - Usa JS Hint per spazzolare il tuo JavaScript ed assicurati di ritagliare il file di opzioni di JS Hint e di includerlo nel source control. Vedi la [documentazione di JS Hint](http://www.jshint.com/docs/) per i dettagli sulle opzioni. - - *Perché?*: Da un allerta iniziale prima di fare il commit di qualunque codice al source control. - - *Perché?*: Fornisce consistenza all'interno del tuo team. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## JSCS - -### Usa un file di opzioni -###### [Stile [Y235](#stile-y235)] - - - Usa JSCS per il controllo dello stile del tuo codice JavaScript ed assicurati di personalizzare il file di opzioni JSCS ed includerlo nel source control. Vedi [JSCS docs](http://www.jscs.info) per i dettagli sulle opzioni. - - *Perché?*: Fornisce un iniziale avvertimento prima di fare il commit di qualunque codice al source control. - - *Perché?*: Fornisce consistenza per l'intero team. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Costanti - -### Variabili globali delle terze parti -###### [Stile [Y240](#stile-y240)] - - - Crea una costante di Angular per le variabili globali delle librerie di terze parti. - - *Perché?*: Fornisce in modo per iniettare librerie di terze parte che altrimenti sarebbero globali. Questo migliore la testabilità del codice permettendoti più facilmente di sapere quali sono le dipendenze dei tuoi componenti. Ti consente inoltre di fare un mock di queste dipendenze, dove ciò ha senso. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Stile [Y241](#stile-y241)] - - - Usa constanti per i valori che non cambiano e che non provengono da un altro servizio. Quando le costanti sono utilizzate solo per un modulo che potrebbe essere riutilizzato in più applicazioni, metti le costanti in un file per modulo e nominalo come il modulo. Fintanto che tale necesstià non si presenti, tieni le constanti nel modulo principale in un file `constants.js`. - - *Perché*: Un valore che potrebbe variare, anche non di frequente, dovrebbe essere recuperato da un servizio così che non sia necessario cambiare il codice sorgente. Per esempio, una URL per un servizio di accesso ai dati può essere messo in una costante ma un miglior posizionamento sarebbe quello di caricarlo da un web service. - - *Perché?*: Le costanti possono essere iniettate in un componente di angular, provider inclusi. - - *Perché?*: Quando una applicazione è separata in moduli che potrebbero essere usati in altre applicazioni, ogni modulo dovrebbe essere in grado di funzionare a se stante ivi incluse ogni costante da cui dipende. - - ```javascript - // Costanti usate dall'intera applicazione - angular - .module('app.core') - .constant('moment', moment); - - // Costanti usate solo dal modulo delle vendite - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## File Template e Snippet -Usa file template o snippet che ti aiutino a seguire stili e schemi consistentemente. Qui trovi alcuni template e/o snippet per alcuni degli editor per lo sviluppo web e IDE. - -### Sublime Text -###### [Stile [Y250](#stile-y250)] - - - Snippet Angular che seguono questi stili e linee guida. - - - Scarica gli [snippet di Angular per Sublime](assets/sublime-angular-snippets.zip?raw=true) - - Mettili nella tua cartella Packages - - Riavvia Sublime - - In un file JavaScript digita questi comandi seguiti da `TAB` - - ```javascript - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngmodule // crea un modulo Angular - ngservice // crea un servizio Angular - ngfilter // crea un filtro Angular - ``` - -### Visual Studio -###### [Stile [Y251](#stile-y251)] - - - I file dei template per Angular che seguono questi stili e linee guida possono essere trovati su [SideWaffle](http://www.sidewaffle.com) - - - Scarica l'estensione [SideWaffle](http://www.sidewaffle.com) per Visual Studio (file vsix) - - Esegui il file vsix - - Riavvia Visual Studio - -### WebStorm -###### [Stile [Y252](#stile-y252)] - - - Live template per Angular che seguono queste linee guida. - - - Scarica [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) - - Mettili nella tua Place it in your [cartella dei template](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) - - Riavvia WebStorm - - In un file JavaScript digita questi comandi seguiti da `TAB` - - ```javascript - // Questi sono snippet completi che contengono una IIFE - ngapp // crea un modulo setter Angular - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngfilter // crea un filter Angular - ngservice // crea un service Angular - - // Questi sono snippet parziali intesi per essere concatenati - ngconfig // definisce una funzione della fase di configuration - ngmodule // crea un modulo getter Angular - ngroute // crea una definizione Angular 'when' di ngRoute - ngrun // definisce una funzione di fase run - ngstate // crea una definizione di stato Angular per UI Router - ``` - - *Template individuali sono inoltre disponibili per essere scaricati all’interno della cartella [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true)* - -### Atom -###### [Stile [Y253](#stile-y253)] - - - Snippet Angular e file di template che seguono queste linee guida. - ``` - apm install angularjs-styleguide-snippets - ``` - oppure - - Apri Atom, poi apri il Package Manager (Packages -> Settings View -> Install Packages/Themes) - - Cerca il pacchetto 'angularjs-styleguide-snippets' - - Clicca 'Install' per installare il pacchetto - - - In un file di JavaScript digita i seguenti comandi seguiti da un `TAB` - - ```javascript - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngmodule // crea un module Angular - ngservice // crea un servizio Angular - ngfilter // crea un filtro Angular - ``` - -### Brackets -###### [Stile [Y254](#stile-y254)] - - - Snippet Angular che seguono questi stili e linee guida. - - Scarica gli [snippet di Angulare per Brackets](assets/brackets-angular-snippets.yaml?raw=true) - - Brackets Extension manager ( File > Extension manager ) - - Installa ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Clicca sulla lampadina nel gutter destro in bracket - - Clicca su `Settings` e quindi `Import` - - Scegli il file e seleziona per saltare o fare l'override - - Clicca `Start Import` - - - In un file di tipo JavaScript digita questi comandi seguiti da un `TAB` - - ```javascript - // Questi sono snippet interi che contengono una IIFE - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngapp // crea una impostazione di modulo Angular - ngservice // crea un service Angular - ngfilter // crea un filtro Angular - - // Questi sono snippet parziali intesi per essere concatenati - ngmodule // crea un getter di modulo Angular - ngstate // crea una definizione di stato di UI Router Angular - ngconfig // definisce un funzione per la fase di cofigurazione - ngrun // definisce una funzione per la fase di esecuzione - ngwhen // definisce una ngRoute Angular con la definizione 'when' - ngtranslate // usa il service $translate con le proprie promesse - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - Snippet di vim che seguono questi stili e linee guida. - - - Scarica gli [snippet vim per Angular](assets/vim-angular-snippets?raw=true) - - setta [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - copia gli snippets nella directory snippet - - - snippet vim UltiSnips che seguono questi stilili e linee guida. - - - Scarica gli [snippet vim Angular UltiSnips](assets/vim-angular-ultisnips?raw=true) - - setta [UltiSnips](https://github.com/SirVer/ultisnips) - - copia gli snippet nella directory UltiSnips - ```javascript - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngmodule // crea un modulo Angular - ngservice // crea un service Angular - ngfilter // crea un filter Angular - ``` - -### Visual Studio Code - -###### [Stile [Y256](#stile-y256)] - - - Snippet [Visual Studio Code](http://code.visualstudio.com) che seguono questi stili e linee guida. - - - Scarica gli [snippet VS Code Angular](assets/vscode-snippets/javascript.json?raw=true) - - copia gli snippet nella directory snippet o, in alternativa, copia ed incolla gli snippet in quella esistente. - - ```javascript - ngcontroller // crea un controller Angular - ngdirective // crea una directive Angular - ngfactory // crea una factory Angular - ngmodule // crea un modulo Angular - ngservice // crea un service Angular - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Generatore Yeoman -###### [Stile [Y260](#stile-y260)] - -Puoi usare il [generatore yeoman di HotTowel](http://jpapa.me/yohottowel) per creare un'app che funga da punto di partenza per Angular che segue gli stili di questa guida. - -1. Installa generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Crea una nuova cartella e cambia la directory su di essa - - ``` - mkdir myapp - cd myapp - ``` - -3. Esegui il generatore - - ``` - yo hottowel helloWorld - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Routing -Il routing del lato client è importante al fine di creare in flusso di navigazione tra view e composizione di view che sono costituite da template più piccoli e directive. - -###### [Stile [Y270](#stile-y270)] - - - Usa il [Router AngularUI](http://angular-ui.github.io/ui-router/) per il routing del lato client. - - *Perché?*: UI Router offre tutte le funzionalità del router di Angular più alcune funzionalità aggiuntive che includono route nidificate e stati. - - *Perché?*: la sintassi è piuttosto simile a quella del router di Angular ed è facile migrare a UI Router. - - - Nota: Puoi usare un provider quale `routerHelperProvider` mostrato sotto per aiutarti a configurare gli stati tra i file durante la fase di esecuzione. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Stile [Y271](#stile-y271)] - - - Definisci le route per le view nel modulo dove queste esistono. Ogni modulo dovrebbe contenere le route per le view del modulo. - - *Perché?*: Ogni modulo dovrebbe essere a se stante. - - *Perché?*: Quando rimuovi o aggiungi un modulo, l'app conterrà soltanto route che puntano a view esistenti. - - *Perché?*: Ciò rende semplice abilitare o disabilitare porzioni di una applicazione senza preoccupazioni inerenti route orfane. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Automazione dei processi -Usa [Gulp](http://gulpjs.com) o [Grunt](http://gruntjs.com) per la creazione di processi automatizzati. Gulp si basa su "codice sopra configurazione" mentre Grunt si basa su "configurazione sopra codice". Personalmente preferisco Gulp poiché lo percepisco come più facile da leggere e scrivere ma entrambi sono eccellenti. - -> Impara di più su Gulp per l'automazione dei processi e pattern in mio [corso Pluralsight su Gulp](http://jpapa.me/gulpps) (in inglese) - -###### [Stile [Y400](#stile-y400)] - - - Usa l'automazione dei processi per elencare i file delle definizioni dei moduli `*.module.js` prima di qualunque altro file JavaScript dell'applicazione. - - *Perché?*: Angular necessita delle definizione del modulo da essere registrate prima di essere usati. - - *Perché?*: Nominare i moduli con un pattern specifico come `*.module.js` semplifica prenderli con un glob ed elencarli per primi. - - ```javascript - var clientApp = './src/client/app/'; - - // Prendi sempre i file dei moduli per primi - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Filtri - -###### [Stile [Y420](#stile-y420)] - - - Evita l'utilizzo di filtri per la scansione di tutte le proprietà del grafo di un oggetto complesso. Usa i filtri per selezionare le proprietà. - - *Perché?*: I filtri possono facilmente essere abusati ed avere un impatto negativo sulle prestazioni se non usati con saggezza, per esempio quando i filtri hanno come soggetto il grafo di un oggetto largo e profondo. - -**[Torna all'inizio](#tavola-dei-contenuti)** - -## Documentazione di Angular -Per qualunque altra cosa, riferimenti alle API, controlla la [documentazione di Angular](//docs.angularjs.org/api). - -## Contribuire - -Apri prima una "issue" per discutere potenziali cambiamenti/aggiunte. Se hai domande relative alla guida, sentiti libero di porle come "issue" nel repository. Se trovi un errore di scrittura, crea una pull request. L'idea è quella di tenere aggiornato il contenuto e usare le funzioni native di Github per aiutare nel racconto della storia con issue e pull request che sono tutte ricercabili via Google. Perché? Il caso vuole che se hai una domanda, qualcun altro l'abbia pure. Puoi trovare di più su come contribuire qui. - -*Contribuendo a questo repository sei d'accordo a rendere il tuo contenuto soggetto alla licenza di questo repository* - -### Processo - - 1. Discuti i cambiamenti in un issue di GitHub. - 2. Apri una Pull Request, fai riferimento all issue e specifica i cambiamenti e perché questi aggiungono valore. - 3. La Pull Request sarà vagliata e quindi fatto un merge o declinata. - -## Licenza - -_tldr; Usa questa guida. I riferimenti sono apprezzati._ - -### (The MIT License) - -Copyright (c) 2014 [John Papa](http://johnpapa.net) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Torna all'inizio](#tavola-dei-contenuti)** diff --git a/a1/i18n/ja-JP.md b/a1/i18n/ja-JP.md deleted file mode 100644 index 4b034ae4..00000000 --- a/a1/i18n/ja-JP.md +++ /dev/null @@ -1,3104 +0,0 @@ -# Angular スタイルガイド - -*[@john_papa](//twitter.com/john_papa)によるチームのための頑固なAngularスタイルガイド* - -もしあなたがAngularのシンタックス、規約、そしてアプリケーション構成のための頑固なスタイルガイドを探しているなら、どうぞいらっしゃい!本スタイルは、[Angular](//angularjs.org)を用いた私の開発経験やプレゼンテーション、[Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa) 、そしてチームでの作業に基づいたものです。 - -このスタイルガイドの目的は、私が実践している規約だけでなく、私がそれを行う理由を示すことによって、Angularアプリケーションを構築する手引きとなることです。 - ->もしあなたがこのガイドを気に入ったのなら、Pluralsightにある [Angular Patterns: Clean Code](http://jpapa.me/ngclean) の私のコースもチェックして下さい。 - -[![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Community Awesomeness and Credit -あなたは決して1人でありません!Angularのコミュニティは、自身の経験を共有することに情熱的な素晴らしい集団です。実際、友人でありAngularのエキスパートでもある Todd Motto と私は、共同で多くのスタイルや規約をまとめました。一部意見が分かれましたが、概ね合意できるものでした。彼のアプローチと本スタイルとの比較のため、是非 [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) をチェックすることをお勧めします。 - -ここで紹介する多くのスタイルは、数多くのペアプログラミングのセッション [Ward Bell](http://twitter.com/wardbell) および私自身が既に持っていたアイデアによるものです。いつも意見が一致した訳ではないですが、友人のWardはこのガイドの最終的な発展に大きく貢献してくれました。 - -## See the Styles in a Sample App -このガイドは"何を"、"なぜ"、"どのように"行えば良いかという説明をしますが、合わせて実践的に見ていくことが理解に役立つはずです。本ガイドは、スタイルやパターンに沿ったサンプルアプリケーションを [`modular`のディレクトリ](https://github.com/johnpapa/ng-demos) に用意しています。ここから自由に取得しcloneやforkをしてもらって構いません。また [readmeに実行のためのインストラクション](https://github.com/johnpapa/ng-demos/tree/master/modular) もあります。 - -## Translations -[Translations of this Angular style guide](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) がコミュニティによってメンテナンスされており、そこで翻訳を参照することができます。 - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Rule of 1 -###### [Style [Y001](#style-y001)] - - - ファイル毎に一つのコンポーネントを定義して下さい。 - - 次の例は`app`モジュールとその依存、コントローラの定義とファクトリーの定義の全てが同じファイルに定義されています。 - - ```javascript - /* avoid */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - コンポーネント単位でファイルに分割します。 - - ```javascript - /* recommended */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommended */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Back to top](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - - Angularのコンポーネントを即時関数式(Immediately Invoked Function Expression:IIFE)で包んで下さい。 - - *なぜ ?*: IIFEを用いると変数はグローバルスコープになりません。それにより、変数がグローバルスコープの中で期待以上に長く生存してしまうことを防ぐことがでます。また変数同士の衝突も避けることができます。 - - *なぜ ?*: コードをMinifyして一つのファイルにしてプロダクションのサーバーにデプロイするときに、変数の衝突や多数のグローバル変数の存在が問題を起こすかもしれません。IIFEはファイル毎にスコープを持つため、これらの問題を防ぐことができます。 - - ```javascript - /* avoid */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger function is added as a global variable - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage function is added as a global variable - function storage() { } - ``` - - ```javascript - /** - * recommended - * - * no globals are left behind - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Note: 簡潔にするため, 以下に続くコードの例ではIIFEのシンタックスを省略させて下さい。 - - - Note: テストコードは正規表現や単体テスト向けに有効なヘルバー関数のようになプライベートなメンバにしばしばアクセスしますが、IIFEはそのアクセスへの妨げとなります。しかしながら、アクセス可能なメンバを通してテストをすることや独立したコンポーネント経由でこれらを公開してしまうことでテストが可能になります。例えば、ヘルバー関数、正規表現、もしくは定数をAngularのファクトリや定数として用意してしまう方法があります。 - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [Y020](#style-y020)] - - - サブモジュールにセパレータを用いたユニークな命名規則を用いて下さい。 - - *なぜ ?*: ユニークな名前は衝突を防ぐことができます。セパレータはモジュールやそのサブモジュールの階層を定義するのに役立ちます。例えば、`app` はルートとなるモジュールであり、`app.dashboard` または `app.users` は `app` と依存のあるモジュールかもしれません。 - -### Definitions (aka Setters) -###### [Style [Y021](#style-y021)] - - - セッターを用いて変数を使うことなくモジュールを定義して下さい。 - - *なぜ ?*: 1ファイル1コンポーネントの原則下では、モジュールの宣言で変数が必要となるケースは非常に稀です。 - - ```javascript - /* avoid */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - 代わりに下記のようなシンプルなセッターのシンタックスを用いて下さい。 - - ```javascript - /* recommended */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - モジュールを用いるときは変数を使うことを避け、代わりにゲッターを使ったチェーンを用いて下さい。 - - *なぜ ?*: 可読性の高いコードとなり変数の衝突やリークを防ぐことができます。 - - ```javascript - /* avoid */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-y023)] - - - 一度だけsetして、その他のインスタンスでは全てgetして下さい。 - - *なぜ ?*: モジュールは一度だけ生成され、それ以降は取得のみが行われるべきです。 - - - モジュールのsetには`angular.module('app', []);` を用いる。 - - モジュールのgetには `angular.module('app');` を用いる。 - -### Named vs Anonymous Functions -###### [Style [Y024](#style-y024)] - - - コールバックとして、無名関数ではなく有名関数を渡して下さい。 - - *なぜ ?*: 可読性の高いコードとなりデバッグが容易となります。またネスト化されたコールバックの量を減らせます。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Back to top](#table-of-contents)** - -## Controllers - -### controllerAs View Syntax -###### [Style [Y030](#style-y030)] - - - `典型的な$scopeを使ったcontroller`のシンタックスよりも、[`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) を用いて下さい。 - - *なぜ ?*: 全く新規にControllersが生成されると、 単一の新しいインスタンスが生成されます。`controllerAs` のシンタックスは、`典型的な$scopeを使ったcontroller`のシンタックスよりも、JavaScriptのコンストラクタにより近いものとなります。 - - *なぜ ?*: ビューの中で"ドット."によるバインディングが容易となり、(例えば、`name` の代わりに `customer.name` となることで)様々な状況に対応可能となり可読性が高まります。また、"ドット."を使わない場合に起こりうる参照の問題を避けることができます。 - - *なぜ ?*: ネスト化されたコントローラを使ったビューの中で`$parent` の呼び出しを避けるのに役立ちます。 - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax -###### [Style [Y031](#style-y031)] - - - `典型的な$scopeを用いたコントローラ`のシンタックスよりも、`controllerAs`のシンタックスを用いて下さい。 - - - `controllerAs`のシンタックスを用い、$scopeにバインドされるコントローラの内部で`this`を用いて下さい。 - - *なぜ ?*: `controllerAs`は、`$scope`の糖衣構文(シンタックスシュガー)となるので、`$scope`が引き続きビューにバインドされます。そのため`$scope`のメソッドも利用することができます。 - - *なぜ ?*: コントローラ内部で`$scope`のメソッドを利用したいときに、それを自体を避けた方が良いかもしくはファクトリに移動した方が良いことがあります。そのようなときに、コントローラの中に押し込めてしまいたい誘惑を遠ざけるのに役立ちます。ファクトリの中で$scopeを使うことや必要なときに限ってコントローラの中で`$scope`を用いることを検討して下さい。 例えば、[`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit) や [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast) もしくは [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) を使ってイベントのpublishとsubscribeを行うときは、その実装をファクトリに移動するかまたはコントローラから呼び出すといった形を検討して下さい。 - - ```javascript - /* avoid */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs with vm -###### [Style [Y032](#style-y032)] - - - `controllerAs` シンタックスを用いるときは、`this`をキャプチャした変数を用いて下さい。ビューモデルを表す`vm`のような一貫性のある名前を選んで下さい。 - - *なぜ ?*: `this` キーワードはコンテキストに応じて変化するので、コントローラの関数の中で使われるときにはコンテキストが変更されるかもしれません。`this`のコンテクストをキャプチャすることで、このような問題を避けることができます。 - - ```javascript - /* avoid */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Note: コメントをコードの上部に入れることで [jshint](http://www.jshint.com/) のwarningsを避けることができます。 しかし関数がアッパーケースの場合には不要です。規約的にはそれはコンストラクタであり、Angularではコントローラに当たります。 - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Note: `controller as`を用いたコントローラの中で watch を作成したときは、次のシンタックスで `vm.*` のメンバを watch することができます。(digestのサイクルに追加の負荷がかかることに注意してwatchを作成します。) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Bindable Members Up Top -###### [Style [Y033](#style-y033)] - - - バインドするメンバをコントローラの先頭でアルファベット順に配置し、コントローラのコードの中に分散させないで下さい。 - - *なぜ ?*: バインドするメンバを先頭に書くことで可読性が上がり、コントローラのどのメンバがバインドされ、ビューの中で使われるのかが即座に特定できます。 - - *なぜ ?*: インラインで無名関数をセットするのは容易ですが、一行以上の関数の場合は可読性が下がります。バインドするメンバの下に関数を定義し(関数は巻き上げられます)、実装の詳細は下へ移動します。そうすることで、バインドするメンバを先頭に置いたまま可読性を上げることができるでしょう。 - - ```javascript - /* avoid */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Note: もし関数がワンライナーであれば、可読性に影響が無い限り上に置いたままにすることを検討して下さい。 - - ```javascript - /* avoid */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Function Declarations to Hide Implementation Details -###### [Style [Y034](#style-y034)] - - - 実装の詳細を隠すために関数宣言を用いて下さい。またバインドされるメンバを先頭に置いて下さい。コントローラの中で関数をバインドするときは、その関数がファイルの後方に現れる関数宣言を指すようにします。これは、「Bindable Members Up Top」のセクションと直接対応しています。 詳細は[このポスト](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) を参照して下さい。 - - *なぜ ?*: バインドするメンバを先頭に書くことで可読性が上がり、コントローラのどのメンバがバインドされ、ビューの中で使われるのかが即座に特定できます(上記と同じ)。 - - *なぜ ?*: 関数の実装の詳細をファイルの後方に置くことでviewから複雑さを排除し、重要なものがファイルの先頭で見えるようになります。 - - *なぜ ?*: 関数宣言が巻き上げられるので(関数式でありがちな)関数を宣言する前に利用してしまう懸念がありません。 - - *なぜ ?*: `var b` の前に`var a` を移動するという関数宣言が`a` が `b` に依存しているからといってコードが壊れてしまう心配する必要は決してありません。 - - *なぜ ?*: 関数式では順番がクリティカルです。 - - ```javascript - /** - * avoid - * Using function expressions. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - 重要な事が続く例に散りばめられていることに着目して下さい。下記の例では、重要な物が先頭にあるのが分かります。例えば、`vm.avengers` や `vm.title` がコントローラにバインドされています。そして実装の詳細がその下にあります。これにより可読性が上がっています。 - - ```javascript - /* - * recommend - * Using function declarations - * and bindable members up top. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Defer Controller Logic to Services -###### [Style [Y035](#style-y035)] - - - サービスやファクトリに委譲することで、コントローラの中のロジックを遅延させて下さい。 - - *なぜ ?*: ロジックがサービス内に配置されて関数経由で利用可能になれば、複数のコントローラによって再利用されるかもしれません。 - - *なぜ ?*: 単体テスト上でサービス内のロジックは簡単に分離できます。そのためコントローラの呼び出しロジックを容易にモック化がすることができます。 - - *なぜ ?*: 依存性を取り除きコントローラから実装の詳細を隠すことができます。 - - *なぜ ?*: コントローラをスリムに整えて、そしてフォーカスさせて下さい。 - - ```javascript - - /* avoid */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recommended */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -### Keep Controllers Focused -###### [Style [Y037](#style-y037)] - - - 一つのビューに一つのコントローラを定義し、他のビューで再利用を試みないで下さい。代わりに再利用可能なロジックをファクトリに移動し、コントローラをシンプルにビューにフォーカスさせて下さい。 - - *なぜ ?*: 複数のビューを持つコントローラを再利用することは脆く、大規模なアプリケーション上で安定性を確保するためには、高いカバレッジのエンドツーエンドテストが必須となります。 - -### Assigning Controllers -###### [Style [Y038](#style-y038)] - - - コントローラは必ずビューとペアとなる必要があります。どちらかのコンポーネントが他のコントローラやビューによって最利用可能であるときは、ルーティングに沿ってコントローラを定義して下さい。 - - Note: もしビューがルーティング以外の別の方法でロードされているなら、`ng-controller="Avengers as vm"` のシンタックスを利用して下さい。 - - *なぜ ?*: ルーティングによりコントローラのペアリングを行うことで、ルーティングによって異なるコントローラとビューのペアを呼び出すことが出来ます。コントローラが[`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController)を使ってビューにアサインされたときには、そのビューはいつも同じコントローラにアサインされます。 - - ```javascript - /* avoid - when using with a route and dynamic pairing is desired */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommended */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - サービスは`new`キーワードでインスタンス化されます。パブリックメソッドや変数には`this`を使って下さい。これらはファクトリも同様ですが、一貫性のためにはサービスの代わりにファクトリを用いて下さい。 - - Note: [全てのAngularのサービスはシングルトンです](https://docs.angularjs.org/guide/services)。 これはつまり、作成されたサービスはインジェクター毎に単一インスタンスしか存在しないことを意味しています。 - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Back to top](#table-of-contents)** - -## Factories - -### Single Responsibility -###### [Style [Y050](#style-y050)] - - - ファクトリは[単一責任](http://en.wikipedia.org/wiki/Single_responsibility_principle)であるべきであり、そのコンテキストに応じてカプセル化されます。ファクトリが一つの目的を超えて利用され始めた場合は、新しいファクトリが作成されるべきです。 - -### Singletons -###### [Style [Y051](#style-y051)] - - - ファクトリはシングルトンであり、そのサービスのメンバを含むオブジェクトを返して下さい。 - - Note: [全てのAngularのサービスはシングルトンです](https://docs.angularjs.org/guide/services)。 - -### Accessible Members Up Top -###### [Style [Y052](#style-y052)] - - - 呼び出し可能なサービスのメンバ(そのインターフェイス)を先頭に公開します。このテクニックは [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) に由来しています。 - - *なぜ ?*: 呼び出し可能なメンバを先頭に置くことは可読性に優れ、どのサービスのメンバが呼び出し可能で単体テストされる(モックされる)べきなのかを即座に特定するのに役立ちます。 - - *なぜ ?*: これは何が公開されているかを見るためにスクロールが必要なるほどファイルが長い場合に特に有効です。 - - *なぜ ?*: セッターの関数では簡単ですが、関数宣言が一行以上のコードからなる場合には可読性が下がりスクロールが必要となります。サービスからの戻り値を用いて呼び出し可能なインターフェイスの定義をすることで、実装の詳細を下に移動できます。また、インターフェイスの定義が先頭に置かれることで、可読性を上げることができます。 - ```javascript - /* avoid */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommended */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - このバインディングの方法はホストオブジェクト全体で反映されます。このrevealing module パターンを使うことで単独でプリミティブの値を更新することは出来なくなります。 - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) - -### Function Declarations to Hide Implementation Details -###### [Style [Y053](#style-y053)] - - - 実装の詳細を隠すために関数宣言を用いて下さい。ファクトリのアクセス可能なメンバを先頭に置いて下さい。それらのメンバがファイルの後方に現れる関数宣言を指すようにします。詳細は[このポスト](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) を参照して下さい。 - - *なぜ ?*: アクセス可能なメンバを先頭に書くことで可読性が上がり、ファクトリの中でどの関数が外部からアクセス可能なのか即座に特定できます。 - - *なぜ ?*: 実装の詳細をファイルの後方に置くことでviewから複雑さを排除し、重要なものがファイルの先頭で見えるようになります。 - - *なぜ ?*: 関数宣言が巻き上げられるので、(関数式であるような)関数を宣言する前に利用してしまう懸念がありません。 - - *なぜ ?*: `var b` の前に`var a` を移動するという関数宣言が、`a` が `b` に依存しているからといってコードが壊れてしまう心配する必要は決してありません。 - - *なぜ ?*: 関数式では順番がクリティカルです。 - - ```javascript - /** - * avoid - * Using function expressions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommended - * Using function declarations - * and accessible members up top. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [Y060](#style-y060)] - - - データオペレーションやファクトリとのデータのインタラクションのようなロジックをリファクタリングして下さい。XHRの呼び出し、ローカルストレージ、メモリへの退避などのデータオペレーションを担うデータサービスを作成して下さい。 - - *なぜ ?*: コントローラの責任は、プレゼンテーションとビューのために必要な情報を集めることです。どのようにデータを取得するかは気にせず、ただ単に誰に問い合わせれば良いかを知っているだけです。 データサービスの分割によってどのようにデータを取得するかというロジックをデータサービスに移動し、コントローラをよりシンプルにビューにフォーカスさせて下さい。 - - *なぜ ?*: データサービスを用いるコントローラをテストする際に、(モックまたは本物の)データの呼び出しを容易にテストできます。 - - *なぜ ?*: データサービスの実装は、データリポジトリを取り扱う非常にスペシフィックなコードとなります。 このコードには、ヘッダ、データリポジトリの利用方法、もしくは`$http`のような他のサービスを含むでしょう。そのようなロジックをデータサービスへ分割し、他のconsumers (おそらくコントローラ) から実装の詳細を隠すように一つの場所へカプセル化します。そうすることで、実装の変更も容易になります - - ```javascript - /* recommended */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Note: データサービスがコントローラのような consumers から呼び出されますが、下記で示されるように実装はconsumersから隠されます。 - - ```javascript - /* recommended */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Return a Promise from Data Calls -###### [Style [Y061](#style-y061)] - - - `$http`のようなpromiseを返すデータサービスを呼び出すときは、それを呼び出す関数も同様にpromiseを返して下さい。 - - *なぜ ?*: promiseを一緒にチェーンすることで、データの呼び出しが完了した後にさらなるアクションを行い、そのpromiseをresolveもしくはrejectすることができます。 - - ```javascript - /* recommended */ - - activate(); - - function activate() { - /** - * Step 1 - * Ask the getAvengers function for the - * avenger data and wait for the promise - */ - return getAvengers().then(function() { - /** - * Step 4 - * Perform an action on resolve of final promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Ask the data service for the data and wait - * for the promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * set the data and resolve the promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Back to top](#table-of-contents)** - -## Directives -### Limit 1 Per File -###### [Style [Y070](#style-y070)] - - - ファイル毎に一つのディレクティブを作って下さい。ディレクティブ名でファイル名を付けて下さい。 - - *なぜ ?*: 全てのディレクティブを一つのファイルに押し込めるのは容易ですが、アプリ間、モジュール間、たった一つのモジュールと共有する場合でも抜き出すのは難しくなります。 - - *なぜ ?*: ファイル毎に一つのディレクティブにすることで、メンテナンスが容易になります。 - - Note: "**Best Practice**: ディレクティブは自身でクリーンナップされるべきです。ディレクティブが削除されたときにクリーンナップの関数を実行するために、`element.on('$destroy', ...)` や `scope.$on('$destroy', ...)`"を用いて下さい ... Angularのドキュメンテーションより。 - - - ```javascript - /* avoid */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order directive that is specific to the order module */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales directive that can be used anywhere across the sales app */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner directive that can be used anywhere across apps */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* implementation details */ - } - - function salesCustomerInfo() { - /* implementation details */ - } - - function sharedSpinner() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* calendarRange.directive.js */ - - /** - * @desc order directive that is specific to the order module at a company named Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* customerInfo.directive.js */ - - /** - * @desc sales directive that can be used anywhere across the sales app at a company named Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* spinner.directive.js */ - - /** - * @desc spinner directive that can be used anywhere across apps at a company named Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* implementation details */ - } - ``` - - Note: スコープが狭いか広いかに応じて、ディレクティブには多くの命名オプションがあります。ディレクティブだと理解され、そのファイル名の区別がつきやすく内容が明解であるような名前を一つ選んで下さい。いくつかの例が下記に登場しますが、より推奨される名前に関しては[Naming](#naming)のセクションを参照して下さい。 - -### Manipulate DOM in a Directive -###### [Style [Y072](#style-y072)] - - - DOMを直接操作する場合にはディレクティブを使って下さい。もしもスタイルをセットするためのCSSや [animation services](https://docs.angularjs.org/api/ngAnimate)、Angularテンプレート、 [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) や [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) といった代替の方法があれば、代わりにそれを用いるのでも良いです。例えば、ディレクティブが単純に表示と非表示を行うものであれば、ngHide/ngShowを使って下さい。 - - *なぜ ?*: DOM操作はテストやデバッグが難しいため、しばしば良いアプローチ(例えば、CSS、アニメーション、テンプレート)があります。 - -### Provide a Unique Directive Prefix -###### [Style [Y073](#style-y073)] - - - 簡潔でユニークで内容をよく表すディレクティブのプリフィックスを付けて下さい。例えば、HTMLで`acme-sales-customer-info`のように宣言されれば、`acmeSalesCustomerInfo`のようになります。 - - *なぜ ?*: ユニークで簡潔なプリフィックスは、ディレクティブのコンテキストと由来を特定します。例えば、`cc-` というプリフィックスは、CodeCamperアプリを示し、`acme-` は Acme companyのためのディレクティブであることを示します。 - - Note: `ng-` のようなAngularのdirectivesのためにリザーブされているものは避けて下さい。 名前の衝突を避けるため、[Ionic Framework](http://ionicframework.com/)の`ion-`のように、幅広く利用されているディレクティブを調査して下さい。 - -### Restrict to Elements and Attributes -###### [Style [Y074](#style-y074)] - - - スタンドアロンで成り立つ要素のようなディレクティブを作るときは、restrictプロパティに`E` (カスタム要素) を指定し、必要に応じて `A` (カスタム属性)を指定して下さい。一般的に、スタンドアロンでコントロールできるものであれば `E` が適しています. 一般的なガイドラインとして `EA` は許されますが、スタンドアロンのときは要素として実装される方が好ましく、既存のDOM操作の拡張のためには属性として実装される方が好ましいです。 - - *なぜ ?*: 理にかなっているからです。 - - *なぜ ?*: 私たちはディレクティブをクラスとして使うことを許していますが、もしディレクティブが要素として働いているのであれば、要素の方がより理にかなっていますし、また少なくとも属性としても理にかなうはずです。 - - Note: Angular 1.3 +では、EAがデフォルトです。 - - ```html - -
- ``` - - ```javascript - /* avoid */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommended */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives and ControllerAs -###### [Style [Y075](#style-y075)] - - - ディレクティブが一貫性を保てるように、ビューとコントローラのペアリングで用いられる`controller as`を用いて下さい。 - - *なぜ ?*: 理にかなっていますし、難しくありません。 - - Note: 下記のディレクティブは、controllerAsを用いてlinkやディレクティブのコントローラの中でscopeを使う方法を示しています。全てが一箇所に収まるようにテンプレートをインラインで入れています。 - - Note: 依存性の注入(dependency injection)に関しては、[Manually Identify Dependencies](#manual-annotating-for-dependency-injection)を参照して下さい。 - - Note: ディレクティブのコントローラがディレクティブのクロージャーの外にあることに注意して下さい。この書き方によって注入が`return`の後で到達不能なコードを生成してしまう問題を回避することができます。 - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injecting $scope just for comparison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Note: コントローラをlink関数に注入して、ディレクティブの属性をコントローラのプロパティとしてアクセスすることも可能です。 - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - `controller as`シンタックスをディレクティブで用い、外側のscopeをディレクティブのコントローラのscopeにバインドしたいときは `bindToController = true` を使って下さい。 - - *なぜ ?*: 外側のscopeをディレクティブのコントローラのscopeにバインドした方が容易です。 - - Note: `bindToController`はAngular 1.3.0で導入されました。 - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Back to top](#table-of-contents)** - -## Resolving Promises for a Controller -### Controller Activation Promises -###### [Style [Y080](#style-y080)] - - - `activate`関数でコントローラのスタートアップロジックを解決して下さい。 - - *なぜ ?*: スタートアップロジックをコントローラの中の一貫性の取れた場所へと置くことでテストの場所を特定し、より一貫性のとれたテストを行うことを容易にします。またコントローラ全体にアクティベーションのロジックが分散してしまうのを避けるのにも役立ちます。 - - *なぜ ?*: コントローラの`activate`は、コントローラ/ビューのリフレッシュするためのロジックを再利用するのに便利です。またロジックを一緒にまとめることで、ユーザがビューに早くたどり着けます。`ng-view` や `ui-view`へアニメーションを入れるのも容易となるので、ユーザはキビキビとした動作だと感じます。 - - Note: もしコントローラを使い始める前に条件的にルーティングをキャンセルする必要があるなら、代わりに [route resolve](#style-y081) を用いて下さい。 - - ```javascript - /* avoid */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Route Resolve Promises -###### [Style [Y081](#style-y081)] - - - コントローラがアクティベイトされる前にpromiseが解決されることに依存しているときは、コントローラのロジックが実行される前に`$routeProvider`の中でそれらの依存が解決されます。もしコントローラがアクティベイトされる前に条件的にルーティングをキャンセルしたい場合は、route resolveを用いて下さい。 - - - ビューへ遷移する前にルーティングのキャンセルを判断したいときは、route resolveを用いて下さい。 - - *なぜ ?*: コントローラが読み込まれる前にデータが必要となるかもしれません。そのデータはカスタムのファクトリや[$http](https://docs.angularjs.org/api/ng/service/$http)から返るpromiseから取得するかもしれません。[route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) を使うと、コントローラがロジックを実行する前にpromiseを解決することができるため、 promiseから取得したデータに基づいてアクションを取ることができます。 - - *なぜ ?*: ルーティングとコントローラのactivate関数の後にコードが実行されて、ビューが直ちにロードされます。アクティベイトされたpromiseがresolveされるとデータバインディングがキックされます。ビューが遷移している間(`ng-view`や`ui-view`で)"busy"なアニメーションが表示されます。 - - Note: promiseの後に、ルーティングが行われる前にコードが実行されます。もしそのpromiseがrejectされるとルーティングはキャンセルされます。resolveによって新しいビューはルーティングが解決されるのを待ちます。resolve及びビューの遷移が終わるまで"busy" なアニメーションが表示されます。より早くビューを表示したい場合で、かつビューを表示するかどうかのチェックポイントが不要な場合は、代わりに [controller `activate` technique](#style-y080) を用いることを検討して下さい。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Note: 下記の例はルーティングのresolveが有名関数を指し示しています。そのためデバッグが容易になり依存性の注入が扱いやすくなっています。 - - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Note: このコード例にある`movieService`への依存は、minificationセーフではありません。どのようにコードをminificationセーフにするかの詳細は[dependency injection](#manual-annotating-for-dependency-injection) と [minification and annotation](#minification-and-annotation)を参照して下さい。 - -**[Back to top](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### UnSafe from Minification -###### [Style [Y090](#style-y090)] - - - minificationセーフなアプローチを用いずにショートカットで依存を宣言するシンタックスは避けて下さい。 - - *なぜ ?*: コンポーネント(つまり、コントローラやファクトリなど)に対するパラメータはマングル化された変数に変換されます。例えば、`common` や `dataservice`は、`a` もしくは `b` になるかもしれず、Angularが見つけられないかもしれません。 - - ```javascript - /* avoid - not minification-safe*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - このコードはMinifyされたときにマングルされた変数が生成され実行エラーになるかもしれません。 - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Manually Identify Dependencies -###### [Style [Y091](#style-y091)] - - - Angularコンポーネントの依存を手動で特定するために`$inject`を使って下さい。 - - *なぜ ?*: このテクニックは[`ng-annotate`](https://github.com/olov/ng-annotate)で用いられており、そしてそれは自動でminificationセーフな依存を生成することを勧めています。もし`ng-annotate` が注入がすでに行われたことを検出すると再生成をスキップします。 - - *なぜ ?*: このテクニックは、パラメータがマングルされたときにminificationの問題に対して脆弱であることから依存性を守ります。例えば、`common` や `dataservice` が `a` や `b` になるかもしれず、Angularが見つけられないかもしれません。 - - *なぜ ?*: 配列で読むのが難しいほと長いリストになる場合には、インラインで依存性を生成することを避けて下さい。さらに依存のリストは配列が文字列の連続からなる一方で、最後の要素がコンポーネントの関数となり紛らわしいです。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Note: 関数がreturn文の下にあるとき、`$inject`が到達可能になるかもしれません(これはディレクティブで起こるかもしれません)。これはコントローラをディレクティブの外側へ移動することによって解決できます。 - - ```javascript - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommended */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Manually Identify Route Resolver Dependencies -###### [Style [Y092](#style-y092)] - - - Angularのコンポーネントのroute resolverの依存性を手動で特定するために`$inject`を使って下さい。 - - *なぜ ?*: このテクニックは、route resolverを無名関数として外に出すことで可読性を上げることができます。 - - *なぜ ?*: `$inject`の文をただ前に置くことによって、resolverがいかなる依存性もminificationセーフにします。 - - ```javascript - /* recommended */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - [Gulp](http://gulpjs.com)や[Grunt](http://gruntjs.com)のために[ng-annotate](//github.com/olov/ng-annotate)を使って下さい。自動の依存性の注入が必要となる関数に`/** @ngInject */`というコメントをいれて下さい。 - - *なぜ ?*: このことはminificationセーフなプラクティスを利用していない依存性からコードを守ります。 - - *なぜ ?*: [`ng-min`](https://github.com/btford/ngmin)は非推奨です。 - - >私は読み書きとデバッグが容易なためGulpの方を好みます。 - - 次のコードはminificationセーフな依存性注入を使っていません。 - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - 上記のコードにng-annotateを実行すると、次の`$inject`が付与された出力が生成され、その出力はminificationセーフになります。 - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Note: もし`ng-annotate`が注入がすでに行われていることを検出すると(例えば、`@ngInject` が検出されると)、 `$inject`が入ったコ−ドを重複して生成しません。 - - Note: route resolverを用いるときは、resolverの関数の先頭に `/* @ngInject */` に付けることができます。注入された依存性がminificationセーフになるような適切にアノテーションのついたコードを生成します。 - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note: 潜在的にminificationセーフでない依存性を検出するためにAngular 1.3から導入された、[`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)のディレクティブの`ngStrictDi`パラメータを用いることができます。現在のinjectorが"strict-di"モードで生成されたとき、アプリケーションが明示的にアノテーションの付いていない関数(これらはminificationセーフではありません)の呼び出しは失敗します。デバッグ情報がログとしてconsoleに出力されるので、問題のあるコードを見つけ出すのに役立ちます。私は、`ng-strict-di`をデバッグ用途でのみ利用することを好みます。 - - `` - -### Use Gulp or Grunt for ng-annotate -###### [Style [Y101](#style-y101)] - - - 自動ビルドのタスクの中で[gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) もしくは [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) を使って下さい。依存性を持つどの関数よりも前に`/* @ngInject */` を注入して下さい。 - - *なぜ ?*: ng-annotate は大半の依存性を捕捉しますが、`/* @ngInject */`のシンタックスを使ったヒントが時々必要となリます。 - - 次のコードは、ngAnnotateを使ったgulpタスクの例です。 - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Back to top](#table-of-contents)** - -## Exception Handling - -### decorators -###### [Style [Y110](#style-y110)] - - - 例外が発生したときに[`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler)サービスを上書きし、カスタムのアクションを実行するために、configの呼び出し時に[`$provide`](https://docs.angularjs.org/api/auto/service/$provide) サービスの[decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator)を使って下さい。 - - *なぜ ?*: 開発時や実行時に捕捉されていないAngularの例外をハンドルする一貫した方法を与えます。 - - Note: もう一つのオプションは、decoratorを使う代わりにサービスをオーバーライドすることです。これも有効なオプションですが、もしデフォルトの動作をキープしたい場合または拡張したい場合はdecoratorがお勧めです。 - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Could add the error to a service's collection, - * add errors to $rootScope, log errors to remote web server, - * or log locally. Or throw hard. It is entirely up to you. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Catchers -###### [Style [Y111](#style-y111)] - - - 例外を捕捉し丁寧にハンドルするインターフェイスを公開するファクトリを作って下さい。 - - *なぜ ?*: コードが投げられる可能性のある例外(例えば、XHR呼び出しやpromiseのfailuresなど)を捕捉するため一貫した方法を与えます。 - - Note: 例外のキャッチャーは、例外が発生するかもしれない呼び出しに対して、特定の例外を捕捉しそれに応じて処理をする際に有効です。例えば、リモートのWebサービスからデータを取得するXHRを呼び出すときに、それらのサービスから発生するいかなる例外も捕捉し一意に対応したい場合です。 - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Errors -###### [Style [Y112](#style-y112)] - - - [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError)を使って全てのルーティングのエラーをハンドルしてログにして下さい。 - - *なぜ ?*: 全てのルーティングのエラーをハンドルする一貫した方法を与えます。 - - *なぜ ?*: もしルーティングエラーが発生した際に、詳細な情報もしくはリカバリーのオプションが備わったユーザフレンドリーな画面にルーティングできれば、より良いユーザエクスペリエンスになる可能性があります。 - - ```javascript - /* recommended */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionally log using a custom service or $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - - /** - * On routing error, go to another route/state. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [Y120](#style-y120)] - - - 全てのコンポーネントに対して、特徴や(オプションで)その型を表すパターンに沿った一貫性のある名前を使って下さい。私のお勧めは、`feature.type.js`です。大半のアセットに対して二つの命名箇所があります。 - * ファイル名 (`avengers.controller.js`) - * Angularに登録するコンポーネント名 (`AvengersController`) - - *なぜ ?*: 命名規則は一目見ただけでコンテンツが見つかるような一貫した方法を与えます。一貫性はプロジェクトにとって極めて重要なことでありチームにとって大切なことです。また会社全体でとても大きな効率性に繋がります。 - - *なぜ ?*: 命名規則はコードをより早く発見し、そしてより簡単に理解する助けになります。 - -### Feature File Names -###### [Style [Y121](#style-y121)] - - - 全てのコンポーネントに対して、特徴や(オプションで)その型を表すパターンに沿った一貫性のある名前を使って下さい。私のお勧めは、`feature.type.js`です。 - - *なぜ ?*: 素早くコンポーネントを特定できる一貫した方法を与えます。 - - *なぜ ?*: 自動タスクためのパターンマッチングが可能となります。 - - ```javascript - /** - * common options - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommended - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Note: 別の命名規則は `avengers.controller.js` というファイル名から`controller`を取り除いて命名することです。 その他の全ての規則はサフィックスを使い続けるものです。コントローラは最も共通なコンポーネントであり、タイピングの量を減らしながらも依然として特定しやすいままになります。どれか一つを選びチーム内で一つの命名規則に統一することを勧めます。私の好みは`avengers.controller.js`です。 - - ```javascript - /** - * recommended - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test File Names -###### [Style [Y122](#style-y122)] - - - コンポーネントと同様、テストするコンポーネントにサフィックスとして`spec`を付与した形でテストのスペックを命名して下さい - - *なぜ ?*: コンポーネントを素早く特定する一貫した方法を与えます。 - - *なぜ ?*: [karma](http://karma-runner.github.io/)やその他のテストランナーでパターンマッチングが可能となります。 - - ```javascript - /** - * recommended - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller Names -###### [Style [Y123](#style-y123)] - - - 全てのコントローラにそれらの機能から取った一貫した名前を付けて下さい。コントローラのコンストラクタには、アッパーキャメルケースを使って下さい。 - - *なぜ ?*: 素早くコントラーを特定し参照できる一貫した方法を与えます。 - - *なぜ ?*: アッパーキャメルケースはコンストラクタを使ってインスタンスを生成されたオブジェクトを特定する規約的な方法です。 - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Controller Name Suffix -###### [Style [Y124](#style-y124)] - - - コントローラに`Controller`という名前を挿入して下さい。 - - *なぜ ?*: `Controller`のサフィックスは、広く使われており明らかに直接内容を説明している。 - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Factory Names -###### [Style [Y125](#style-y125)] - - - 全てのファクトリに対して、機能に沿った一貫性の取れた名前を用いて下さい。サービスやファクトリ名にはキャメルケースを用いて下さい。`$`から始まるファクトリやサービス名を避けて下さい。 - - *なぜ ?*: 参照するべきファクトリを素早く特定する一貫性の取れた方法を与えます。 - - *なぜ ?*: ビルトインされている`$`から始まるファクトリやサービス名との衝突を避けられます。 - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - -### Directive Component Names -###### [Style [Y126](#style-y126)] - - - 全てのディレクトリにキャメルケースで一貫性を取れた名前を用いて下さい。そのディレクティブが属する範囲を表す短いプリフィックス(例としては会社やプロジェクトのプリフィックス)を用いて下さい。 - - *なぜ ?*: 参照するべきコンポーネントと素早く特定する一貫性の取れた方法を与えます。 - - ```javascript - /** - * recommended - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - 複数のモジュールがあるとき、メインとなるモジュールのファイルは`app.module.js`と命名し、他に依存のあるモジュールはそれが表す内容に応じて命名して下さい。 例えば、adminのモジュールは`admin.module.js`と命名します。registerされるモジュール名はそれぞれ`app`と`admin`になります。 - - *なぜ ?*: 複数のモジュールからなるアプリまたは大規模なアプリケーションへ拡張される際に一貫性を与えます。 - - *なぜ ?*: 自動化タスクを用いることで、定義されている全てのモジュールを最初にロードし、それから他のAngularのファイルを(bundlingのため)ロードするための簡単な方法を与えます。 - -### Configuration -###### [Style [Y128](#style-y128)] - - - モジュールのコンフィギュレーションをそれぞれのモジュールに沿って命名されたファイルに分離して下さい。メインの`app`のモジュールのコンフィグレーションは`app.config.js`と命名して下さい。(もしくは単純に`config.js`)。 モジュールの名前が`admin.module.js`であれば、そのコンフィグレーションは`admin.config.js`と命名します。 - - *なぜ ?*: モジュールの定義やコンポーネント、アクティブなコードからコンフィグレーションを分離できます。 - - *なぜ ?*: モジュールのコンフィグレーションを設定するための場所を特定できます。 - -### Routes -###### [Style [Y129](#style-y129)] - - - ルーティングのコンフィグレーションをそれぞれのファイルに分離して下さい。例えば、メインのモジュールが`app.route.js`であれば、`admin`のモジュールは、`admin.route.js`になります。例え小さいアプリであっても、私は他コンフィグレーションからルーティングを分離することを好みます。 - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - コードを素早く`L`ocate(配置する)するため、一目見ただけでそのコードを`I`dentify(特定する)ため、できる限り`F`lattestに保ち`、なるべくDRYになるように(`T`ry)アプリケーションを構造化して下さい。アプリケーションの構造はこれらの4つのガイドラインに従うべきです。 - - *なぜ LIFT?*: 一貫性のとれた構造によって十分にスケールしモジュール化され、コードの所在を素早く見つけられることで開発の効率性も上がります。アプリの構造をチェックするもう一つ方法は、次のことを自身に聞いてみることです:ある機能に対して、どのくらい素早く全ての関連ファイルを開き、そして変更できるでしょうか。 - - もし自分の構造が良いと思えなかったら、立ち戻ってこれらのLIFTのガイドラインを見直して下さい。 - - 1. コードを容易に`L`ocatingできること - 2. 一目見ただけでコードを`I`dentifyできること - 3. できる限りコードを`F`latな構造にすること - 4. DRY(Don’t Repeat Yourself)のままになるように(`T`ry) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - コードの配置を直感的に単純に素早く行えるようにして下さい。 - - *Why?*: これはプロジェクトにとって極めて重要なことです。もしチームが彼らが変更するべきファイルを素早く見つけることができなければ、最大限効率的に働くことができなくなってしまいます。そのようなときは構造を変更する必要があります。ファイル名が分からない、もしくは関連のファイルがわからないようであれば、ファイルを最も直感的で相互に近い位置に置くことができれば、大幅な時間の削減となります。理解しやすいフォルダ構成はそのようなにときに役立ちます。 - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - ファイルを参照したときに、そのファイルが何を含み何をしようとしているか即座に理解できるべきです。 - - *なぜ ?*: コードを探し中身をかいつまんで見ている際に費やす時間が少なくなることでより効率的になります。 これはより長いファイル名を求めているのであれば、そうするべきということです。内容をよく表すファイル名を付けて、ファイルの中のコンテンツはたった一つのコンポーネントのみにします。複数のコントローラやサービス、またはそれらが一つのファイルの中で混在することを避けて下さい。 さもなければ、これらはファイル毎に一つのコンポーネントであるというルールを逸脱してしまいます。ルールを守ることで、全てが相互に関係しているような非常に小さい機能の集まりがあったときも、依然としてファイルを簡単に特定することができます。 - -### Flat -###### [Style [Y143](#style-y143)] - - - できる限りフォルダ構成をフラットに保って下さい。7個以上のファイルがある場合は、分割を検討し始めて下さい。 - - *なぜ ?*: ファイルを見つけるために7階層以上のファイルを探したい人はいません。 Webサイトのメニューについて考えると、2つ以上に深い階層には再考の余地があります。 1つのフォルダの構成では、厳格な数のルールは無いですが、フォルダが7-10のファイルから成るときには、サブフォルダを作るときかもしれません。自分が快適であるレベルに基づいて決めれば良いです。 新しいフォルダを作成する明らかな(残りのLIFTに役立つための)レベルまではフラットな構造を使って下さい。 - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - DRYにして下さい。だがそれに捉われすぎて可読性を犠牲にしてはいけません。 - - *なぜ ?*: DRYであることは大切ですが、決定的に重要なことではありません。もしLIFTの中の何か一方を犠牲にするのであれば、それを私はT-DRYと呼んでいます。一つのビューをsession-view.htmlとタイプしたくはありません。なぜならそれは明らかにビューだからです。 明らかにではない場合もしくは規則による場合、そのように命名します。 - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [Y150](#style-y150)] - - - 実装に関する短期的な視点と長期的なビジョンを持って下さい。言い換えると、小さく始めるが、アプリがどの方向に向かっているかををしっかりと把握することです。 全てのアプリケーションのコードは`app`という名前のルートのディレクトリ配下に置いて下さい。どのコンテンツもファイル毎に一機能として下さい。コントローラ、サービス、モジュール、ビューのそれぞれを独立したファイルにして下さい。全てのサードパーティのベンダーのスクリプトは別のルートのディレクトリの下に置き、`app`ディレクトリ配下には置かないで下さい。私はそのスクリプトを書いていないですし、それが自分のアプリを散らかしてしまうことも望んでいません(`bower_components`, `scripts`, `lib`)。 - - Note: この構造の詳細や理由などは[this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/)を参照して下さい。 - -### Layout -###### [Style [Y151](#style-y151)] - - - アプリケーションの全体のレイアウトを定義するコンポーネントは、`layout`というフォルダの中に置いて下さい。このディレクトリにはビューのテンプレートやナビゲーション、メニュー、コンテンツエリア、その他の場所でコンテナとして振舞うコントローラを含むかもしれません。 - - *なぜ ?*: 全てのレイアウトを一箇所に配置することで、アプリケーション全体で再利用可能になります。 - -### Folders-by-Feature Structure -###### [Style [Y152](#style-y152)] - - - 特徴を表す名前でフォルダを作成して下さい。フォルダが7つ以上のファイルを含むように肥大化してきた時、それらのためにフォルダを作ることを検討して下さい。人により閾値は違うかもしれないので必要に応じて調整して下さい。 - - *なぜ ?*: ディベロッパーがコードを配置し、一目見ただけでそれぞれのファイルが何をしているかが理解できます。またできる限りフラットな構造を保つことで、重複も無駄の無い名前にすることができます。 - - *なぜ ?*: LIFTのガイドラインが全てカバーされます。 - - *なぜ ?*: コンテンツを整理しLIFTのガイドラインに沿って維持し続けることで、アプリが散らかってしまうことを避けることができます。 - - *なぜ ?*: 大量のファイル(10個以上)があるときに、それらを一貫性の取れたフォルダに配置することは簡単ですが、フラットな構造で配置するのは難しいでしょう。 - - ```javascript - /** - * recommended - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Note: folders-by-typeを使って構造化をしてはいけません。一つの機能が、5、10、25以上のビューやコントローラ(また他の機能)からなるときにアプリが肥大化してきます。そのとき複数のフォルダに移動する必要がありますが、ファイルを配置するのはfolder-by-featureよりも難しいでしょう。 - - ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [Y160](#style-y160)] - - - 一つの責任をカプセル化した小さいモジュールを作って下さい。 - - *なぜ ?*: モジュール化されたアプリケーションは、開発チームがアプリケーションの垂直なスライスを簡単に組み立てインクリメンタルにロールアウトすることができます。 つまり開発した新しい機能をプラグイン化することができます。 - -### Create an App Module -###### [Style [Y161](#style-y161)] - - - 全てのモジュールと機能を統合することを担うアプリケーションのルートなるモジュールを作って下さい。これにアプリケーション名をつけて下さい。 - - *なぜ ?*: Angularはモジュール化と分割のパターンを推奨しています。他のモジュールを結びつけるアプリケーションのルートモジュールを作ることは、 モジュールの追加や削除を行う簡単な方法を与えます。 - -### Keep the App Module Thin -###### [Style [Y162](#style-y162)] - - - アプリケーションのモジュールの中でロジックは統合する箇所にのみに置いて下さい。それぞれのモジュールの中の機能はそのままにして下さい。 - - *なぜ ?*: アプリケーションのルートにリモートのデータ取得、ビューの表示、もしくはアプリケーションに統合と関係のない他のロジックなどの役割を加えることは、アプリをより混沌とさせ、機能のセットを再利用もしくは無効にすることをより難しくします。 - - *なぜ ?*: ルートのモジュールは、どのモジュールがアプリケーションを構成するかを記述するマニュフェストになります。 - -### Feature Areas are Modules -###### [Style [Y163](#style-y163)] - - - 再利用可能で共有される機能エリアでモジュールを作成して下さい。レイアウトのように、サービスやダッシュボード、アプリケーションスペシフィックな機能(例えば、カスタマー、アドミン、セールス)がそれに該当します。 - - *なぜ ?*: 自己完結型のモジュールは衝突が少ないか全く無い形でアプリケーションに追加することができます。 - - *なぜ ?*: スプリントやイテレーション中は機能エリアにフォーカスし、スプリントやイテレーションの終わりでは、それらの機能をオンにできます。 - - *なぜ ?*:機能エリアでモジュールに分割することで、コードの分離と再利用が可能となり、モジュールのテストが容易になります。 - -### Reusable Blocks are Modules -###### [Style [Y164](#style-y164)] - - - 再利用可能なアプリケーションのブロックでモジュールを作成して下さい。例外のハンドリング、ログ、ダイアグ、セキュリティ、ローカルデータの退避のような共通のサービスがそれに該当します。 - - *なぜ ?*: これらの機能は多くのアプリケーションで必要となり、それぞれモジュールとして分離し続けることによって、ジェネリックになりアプリケーション間で再利用可能となります。 - -### Module Dependencies -###### [Style [Y165](#style-y165)] - - - ルートのモジュールは、アプリケーションスペシフィックなモジュールや共有または再利用されるモジュールに依存します。 - - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *なぜ ?*: メインのアプリケーションのモジュールは、アプリケーションの機能を素早く特定可能なマニュフェストを含みます。 - - *なぜ ?*: それぞれの機能エリアはそれが何と依存しているかを示したマニフェストを含んでいます。そのお陰で機能エリアは依存性を取得することができ、例え他のアプリケーションの中であっても動作します。 - - *なぜ ?*: データを共有するサービスのようなアプリ内の機能は、`app.core`(このモジュールは好きな名前を選んで下さい)のようにまとめることで、簡単に位置を特定または共有することが可能です。 - - Note: これは一貫性のためのストラテジーです。ここには多くの良いオプションがあります。Angularの依存のルールに沿って一貫性をとるためにその一つを選ぶことで メンテナンスしスケールさせることが容易になります。 - - >  私のストラクチャはプロジェクト間で少しだけ変わっていますが、ストラクチャやモジュール化に関してはこのガイドラインに沿っています。実装は、機能やチームに依存して変わるかもしれません。言い換えれば、同等のストラクチャにこだわってるのではなく、一貫性やメンテナンス性、効率性を念頭に置いて正しいことを行うということです。 - - > 小さいアプリでは、共有される全ての依存を機能的なモジュールと直接的な依存を持たないモジュールの中に入れてしまうことも検討して下さい。この方法は、小さいアプリケーションのメンテナンスをより容易にしますが、アプリの外からそのモジュールを再利用することは難しくなります。 - -**[Back to top](#table-of-contents)** - -## Startup Logic - -### Configuration -###### [Style [Y170](#style-y170)] - - - angularのアプリが走る前にコンフィグが設定される[module configuration](https://docs.angularjs.org/guide/module#module-loading-dependencies)のコードを注入して下さい。 providerやconstantsが含まれるのが理想的です。 - - *なぜ ?*: コンフィグレーションをする箇所がより少なくなります。 - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run Blocks -###### [Style [Y171](#style-y171)] - - - アプリケーションが起動されるときに実行されるどのコードもファクトリで宣言され、関数経由で公開され、[run block](https://docs.angularjs.org/guide/module#module-loading-dependencies)として注入されるべきです。 - - *なぜ ?*: コードをrunのブロックに直接いれてしまうとテストするのが難しくなります。ファクトリに置くことで抽象化しモック化することが容易になります。 - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document and $window -###### [Style [Y180](#style-y180)] - - - `document`と`window`の代わりに[`$document`](https://docs.angularjs.org/api/ng/service/$document)と[`$window`](https://docs.angularjs.org/api/ng/service/$window)を使って下さい。 - - *なぜ ?*: これらのサービスはAngularによってラップされているので、直接documentやwindowを使うよりはテスタブルになります。これにより自分自身でdocumentやwindowをモックすることを避けることができます。 - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - `setTimeout` と `setInterval`の代わりに[`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout)と[`$interval`](https://docs.angularjs.org/api/ng/service/$interval)を使って下さい。 - - *なぜ ?*: これらのサービスはAngularによってラップされているのでよりテスタブルになります。またAngularのdigestのサイクルでハンドルされるので、データバインディングが同期され続けます。 - -**[Back to top](#table-of-contents)** - -## Testing -単体テストはクリーンなコードを維持するのに役立ちます。より詳細な情報は、私のお薦めするいくつかの単体テストの基礎をリンク付きで紹介しています。 - -### Write Tests with Stories -###### [Style [Y190](#style-y190)] - - - 全てのストーリーのテストを書いて下さい。 空のテストから始めてストーリ毎にコードを書き中身を埋めて下さい。 - - *なぜ ?*: テストのデスクリプションを書くことは、ストーリが何をして、何をしないのか、何をもって成功とみなすのかを明確に定義するのに役立ちます。 - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Testing Library -###### [Style [Y191](#style-y191)] - - - 単体テストには [Jasmine](http://jasmine.github.io/) もしくは [Mocha](http://mochajs.org) を使って下さい。 - - *なぜ ?*: Jasmine と Mocha はAngularのコミュニティで幅広く使われています。双方とも安定しており、十分メンテされており、ロバストなテスト機能が与えられます。 - - Note: Mochaを用いるときは、合わせて[Chai](http://chaijs.com)といったアサートのライブラリを選ぶことを検討して下さい。私はMochaを好みます。 - -### Test Runner -###### [Style [Y192](#style-y192)] - - - テストランナーとしては[Karma](http://karma-runner.github.io)を使って下さい。 - - *なぜ ?*: Karmaは一度だけ実行するか、コードが変更されたときに自動的に実行するかを簡単に設定することができます。 - - *なぜ ?*: Karmaは自前のテストランナーもしくはGruntやGulpを用いた継続的なインテグレーションのプロセスに容易に接続することができます。 - - *なぜ ?*: [WebStorm](http://www.jetbrains.com/webstorm/) や [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225)などのいくつかのIDEはKarmaを統合し始めています。 - - *なぜ ?*: Karmaは[Grunt](http://www.gruntjs.com) (と [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://www.gulpjs.com) (と [gulp-karma](https://github.com/lazd/gulp-karma))といった自動化のタスク上で正しく動作します。 - -### Stubbing and Spying -###### [Style [Y193](#style-y193)] - - - スタブやスパイのために [Sinon](http://sinonjs.org/) を使って下さい。 - - *なぜ ?*: SinonはJasminとMochaと一緒に正しく動作し、スタブやスパイの機能を拡張します。 - - *なぜ ?*: もしJasmineとMochaの両方を試したければ、Sinonはそれらを簡単に切り替えることができます。 - - *なぜ ?*: テストがアサーションに失敗したときにSinonは叙述的なメッセージとなります。 - -### Headless Browser -###### [Style [Y194](#style-y194)] - - - サーバ上でテストを実行するときは [PhantomJS](http://phantomjs.org/) を使って下さい。 - - *なぜ ?*: PhantomJSはヘッドレスブラウザであり、"visual"なブラウザを必要とすることなくテストを実行するのに役立ちます。 ChromeやSafari、IEや他のブラウザをサーバにインストールする必要はありません。 - - Note: それでも尚、ターゲットとなるユーザの環境で全てのブラウザ上でテストをするべきです。 - -### Code Analysis -###### [Style [Y195](#style-y195)] - - - テスト上でJSHintを実行して下さい。 - - *なぜ ?*: テストもコードです。JSHintはテストが正しく動作しないかもしれないコードの品質の問題を特定するのに役立ちます。 - -### Alleviate Globals for JSHint Rules on Tests -###### [Style [Y196](#style-y196)] - - -  テストコードでは、`describe`や`expect`といった共通のグローバルな変数を許すようにルールを緩めて下さい。 - - *なぜ ?*: テストもコードであり、プロダクションのコードと同様に注意が払われ、コードの品質のルールが必要となります。しかしながら、テストのフレームワークで用いられるグローバル変数は、例えば、テストのスペックに以下を含むことによって緩められます。 Mochaが行っているように式にもルールを緩めて下さい。 - - ```javascript - /* jshint -W117, -W030 */ - ``` - Or you can add the following to your JSHint Options file. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Organizing Tests -###### [Style [Y197](#style-y197)] - - - 単体テストのファイル(スペック)はクライアントコードと横並びで置いて下さい。サーバのインテグレーションや複数のコンポーネントのテストをカバーするスペックは`tests`のフォルダに置いて下さい。 - - *なぜ ?*: 単体テストは特定のコンポーネントとソースファイルに直接の相関があります。 - - *なぜ ?*: いつも見えるところにそれらを置いて最新にしておく方がより簡単です。 TDDか開発中にテストをするか開発後にテストするに関わらず、コーディングをするときは、スペックが横に並びにあり、見えなくなることも気にならなくなることもないようにして下さい。こうすることで、よりメンテナンスされるようになり、コードのカバレッジも上がるでしょう。 - - *なぜ ?*: ソースコードをアップデートするとき、同時にテストもアップデートした方が簡単です。 - - *なぜ ?*: 横並びに置くことは見つけることを簡単にしソースを移動するときに合わせて動かすことができます。 - - *なぜ ?*: 近くにスペックファイルを置くことは、ソースコードのリーダーがそのコンポーネントがどのように使われるかを学ぶことや既知の制限が簡単に知ることが容易となります。 - - *なぜ ?*: gruntやgulpを使えば配布用のビルドに含まれないようにスペックを分割することは簡単です。 - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Back to top](#table-of-contents)** - -## Animations - -### Usage -###### [Style [Y210](#style-y210)] - - - ビューや主要なビュジアルの要素で、ステート間を遷移するために、さりげない[animations with Angular](https://docs.angularjs.org/guide/animations)を使って下さい。[ngAnimate module](https://docs.angularjs.org/api/ngAnimate)を含めて下さい。三つの鍵はさりげなさ、スムーズさ、そしてシームレスです。 - - *なぜ ?*: さりげないアニメーションは、適切に使われることでユーザエクスペリエンスを改善します。 - - *なぜ ?*: さりげないアニメーションは、ビューの遷移の際に感じるパフォーマンスを改善します。 - -### Sub Second -###### [Style [Y211](#style-y211)] - - - デュレーションの短いアニメーションを使って下さい。私は大体300ミリ秒からはじめて適切なところまで調整します。 - - *なぜ ?*: 長いアニメーションはアプリケーションが遅い印象を与え、ユーザエクスペリエンスや体感のパフォーマンス上では逆効果になります、 - -### animate.css -###### [Style [Y212](#style-y212)] - - - 慣習的なアニメーションには[animate.css](http://daneden.github.io/animate.css/)を使って下さい。 - - *なぜ ?*: animation.cssが提供するアニメーションは速く、スムーズで、アプリケーションへの追加が容易です。 - - *なぜ ?*: アニメーションに一貫性を持たせます。 - - *なぜ ?*: animate.cssは広く利用されておりテストされています。 - - Note: この[Matias NiemeläによるAngularのアニメーションの素晴らしいポスト](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)を見て下さい。 - -**[Back to top](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-y220)] - - - もしドキュメントの作成を計画しているのなら、関数名、説明、引数、そして戻り値を記述するために[`jsDoc`](http://usejsdoc.org/)のシンタックスを使って下さい。アプリケーションの構造に合わせて`@namespace`や`@memberOf`を使って下さい。 - - *なぜ ?*: フルスクラッチでドキュメントを書く代わりに、コードからドキュメントを生成(再生成)できます。 - - *なぜ ?*: 共通の生産性の高いツールを使うことで一貫性を保てます。 - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Back to top](#table-of-contents)** - -## JS Hint - -### Use an Options File -###### [Style [Y230](#style-y230)] - - - JavaScriptのコードにlintをかけるためにJS Hintを使って下さい。JS Hintのオプションを必ずカスタマイズしてソースコード管理に含めて下さい。詳細なオプションは[JS Hint docs](http://www.jshint.com/docs/)を参照して下さい。 - - *なぜ ?*: ソース管理にコードをコミットする前に最初にアラートが上がります。 - - *なぜ ?*: チームに一貫性が生まれます。 - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Back to top](#table-of-contents)** - -## JSCS - -### Use an Options File -###### [Style [Y235](#style-y235)] - - - コーディングスタイルをチェックするためにJSCSを使って下さい。 JSCSのオプションファイルを必ずカスタマイズし、ソースファイル管理に含めるようにして下さい。詳細なオプションは、[JSCS docs](http://www.jscs.info)を参照して下さい。 - - *なぜ ?*: ソースコード管理にファイルを込みとする前に最初のアラートが上がるようになります - - *なぜ ?*: あなたのチームに一貫性が生まれます。 - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Back to top](#table-of-contents)** - -## Constants - -### Vendor Globals -###### [Style [Y240](#style-y240)] - - - vendorのライブラリのグローバル変数のためにAngularの定数を作って下さい。 - - *なぜ ?*: グローバルになってしまうvendorのライブラリを注入するための方法を与えます。 これによりコンポーネントが持っている依存性をより簡単に把握することで(抽象性の破綻を避け)、コードのテスタビリティが改善されます。さらに理にかなうように、これらのコンポーネントをモックでテストできるようになります。 - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - 変更されず他のサービスに依存しない値のために定数を使って下さい。定数が複数のアプリケーションで再利用されるモジュールだけに使われるときは、モジュールの後ろに定数とわかるファイル名でモジュール毎にファイル定数を置いて下さい。これが必要となるまでは、メインモジュールの`constants.js`のファイルに定数を置いて下さい。 - - *なぜ ?*: 変更されるかもしれない値は、たとえ頻繁でなかったとしてもサービスから取得されるべきです。そうすることでソースコードを変更する必要がなくなります。例えば、データサービスのためのURLを定数で置くときは、Webサービスからロードする方がより良い場所になるでしょう。 - - *なぜ ?*: 定数はプロバイダーを含めたAngularのコンポーネントの中に注入可能です。 - - *なぜ ?*: アプリケーションが他のアプリケーションからも再利用される可能性のあるモジュールに分割されるときに、依存のある定数をそれぞれのモジュール上で保持することで、モジュールが単独で定数を運用できるようにするべきです。 - - ```javascript - // Constants used by the entire app - angular - .module('app.core') - .constant('moment', moment); - - // Constants used only by the sales module - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets - -一貫性のあるスタイルやパターンに従うため、ファイルテンプレートもしくはスニペットを使って下さい。ここにいくつかのWeb開発のエディターやIDEsのためのテンプレート、もしくはスニペットがあります。 - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - 本スタイルやガイドラインに沿ったAngularのスニペット - - - [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true)をダウンロード - - Packagesフォルダに置く - - Sublimeを再起動 - - JavaScriptのファイルのタイプで`TAB`に続いて下記のコマンドを打ちます。 - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - 本スタイルやガイドラインに沿ったAngularのファイルのテンプレートが[SideWaffle](http://www.sidewaffle.com)で公開されています。 - - - [SideWaffle](http://www.sidewaffle.com) で Visual Studio extension (vsixファイル) をダウンロード - - vsixファイルを実行 - - Visual Studioの再起動 - -### WebStorm -###### [Style [Y252](#style-y252)] - - - 本スタイルやガイドラインに沿ったAngularスニペットとファイルテンプレート。WebStormのsettingsへインポートできます。 - - - [WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar?raw=true)をダウンロード - - WebStormを開き、 `File`メニューを開く - - メニューオプションから`Import Settings`を選ぶ - - ファイルを選択し、`OK`をクリック - - JavaScriptのファイルタイプで`TAB`に続いて下記のコマンドを打ちます。 - - ```javascript - ng-c // creates an Angular controller - ng-f // creates an Angular factory - ng-m // creates an Angular module - ``` - -### Atom -###### [Style [Y253](#style-y253)] - - - 本スタイルとガイドラインに沿ったAngularスニペット - ``` - apm install angularjs-styleguide-snippets - ``` - or - - Atomを開き、Package Managerを開く (Packages -> Settings View -> Install Packages/Themes) - - 'angularjs-styleguide-snippets'のパッケージを検索 - - パッケージをインストールするために'Install'をクリック - - - JavaScriptのファイルタイプで、`TAB`に続いて下記のコマンドを打ちます。 - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - 本スタイルとガイドラインに沿ったAngularのスニペット - - [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true)をダウンロード - - Brackets Extension マネージャー ( File > Extension manager ) - - ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets)をインストール - - bracketsの右側にある電球をクリック - - `Settings`をクリックして`Import` - - ファイルを選択してChoose the file and select to skip or override - - `Start Import`をクリック - - - JavaScriptのファイルタイプで`TAB`に続いて下記のコマンドを打ちます。 - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngapp // creates an Angular module setter - ngservice // creates an Angular service - ngfilter // creates an Angular filter - - // These are partial snippets intended to chained - ngmodule // creates an Angular module getter - ngstate // creates an Angular UI Router state defintion - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngroute // defines an Angular ngRoute 'when' definition - ngtranslate // uses $translate service with its promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - 本スタイルやガイドラインに沿ったvimのスニペット - - - [vim Angular snippets](assets/vim-angular-snippets?raw=true)をダウンロード - - [neosnippet.vim](https://github.com/Shougo/neosnippet.vim)をセット - - スニペットをsnippetディレクトリにコピー - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` -**[Back to top](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -このスタイルガイドに沿ったAngularのアプリケーションを作る良いスタートポイントとして、[HotTowel yeoman generator](http://jpapa.me/yohottowel)を使うことができます。 - -1. generator-hottowelをインストール - - ``` - npm install -g generator-hottowel - ``` - -2. 新規フォルダを作成し、そのディレクトリに移動 - - ``` - mkdir myapp - cd myapp - ``` - -3. ジェネレータの実行 - - ``` - yo hottowel helloWorld - ``` - -**[Back to top](#table-of-contents)** - -## Routing -ビューと数多くの小さいテンプレートおよびディレクティブを持つビューの組み合わせからなるビューの間のナビゲーションのフローを作るために、クライアントサイドのルーティングは重要です。 - -###### [Style [Y270](#style-y270)] - - - クライアントサイドのルーティングには[AngularUI Router](http://angular-ui.github.io/ui-router/) を使って下さい。 - - *なぜ ?*: UI RouterはAngularのルーターが持つ全ての機能プラス、ネスト化されたルーティングやステートなどいくつかの追加機能があります。 - - *なぜ ?*: シンタックスはAngularのルータと非常に似ており、UI Routerへ容易に移行できます。 - - Note: 以下に示されるように`routerHelperProvider`のようなプロバイダーを用いることができます。それはrunフェーズでファイルを跨ってstateを設定する際に役立ちます。 - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - ビューが存在するモジュールの中でビューのためのルーティングを定義して下さい。それぞれのモジュールはモジュール内のビューのためのルーティングを含むべきです。 - - *なぜ ?*: それぞれのモジュールが独立して成り立つべきである。 - - *なぜ ?*: モジュールの削除と追加が行われるときに、アプリケーションは存在するビューを示すルーティングだけを含むでしょう。 - - *なぜ ?*: このことは親のないルーティングが出来てしまう懸念を持たずに、アプリケーションの一部分を有効にしたり無効にしたりするのを簡単にします。 - -**[Back to top](#table-of-contents)** - -## Task Automation - -自動タスクの生成に[Gulp](http://gulpjs.com)または[Grunt](http://gruntjs.com)を使って下さい。Gulpはコンフィグレーションをコーディングでリーンに行えます。一方で、Gruntはコードをコンフィグレーションでリーンに行うことができます。個人的には読みやすくまた書きやすいGulpを好みますが両者とも素晴らしいです。 - -> 私の[Gulp Pluralsight course](http://jpapa.me/gulpps)でGulpについてやタスクの自動化についてより学ぶことができます。 - -###### [Style [Y400](#style-y400)] - - - その他全てのアプリケーションのJavaScriptファイルに先立って、モジュール定義ファイルの`*.module.js`をリスト化する自動化タスクを使って下さい。 - - *なぜ ?*: Angularはそれらが使用される前にモジュール定義を登録する必要があります。 - - *なぜ ?*: `*.module.js`のように特定のパターンで命名されたモジュールは、容易にグラブしさらにリストの先頭で列挙することができます。 - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Back to top](#table-of-contents)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - 複雑なオブジェクトグラフの全てプロパティをスキャンするようなフィルタの使用を避けて下さい。選ばれたプロパティに対してフィルタを用いて下さい。 - - *なぜ ?*: フィルタは簡単に誤用されます。適切に用いられない場合、例えばフィルタが大きくて深いオブジェクトのグラフを読みだす時などパフォーマンス上にネガティブな効果があります。 - -**[Back to top](#table-of-contents)** - -## Angular docs -その他の情報、APIリファレンス、については、[Angular documentation](//docs.angularjs.org/api)をチェックして下さい。 - -## Contributing - -Open an issue first to discuss potential changes/additions. If you have questions with the guide, feel free to leave them as issues in the repository. If you find a typo, create a pull request. The idea is to keep the content up to date and use github’s native feature to help tell the story with issues and PR’s, which are all searchable via google. Why? Because odds are if you have a question, someone else does too! You can learn more here at about how to contribute. - -*By contributing to this repository you are agreeing to make your content available subject to the license of this repository.* - -### Process - 1. Discuss the changes in a GitHub issue. - 2. Open a Pull Request, reference the issue, and explain the change and why it adds value. - 3. The Pull Request will be evaluated and either merged or declined. - -## License - -_tldr; Use this guide. Attributions are appreciated._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Back to top](#table-of-contents)** diff --git a/a1/i18n/ko-KR.md b/a1/i18n/ko-KR.md deleted file mode 100644 index 5bd5ea2e..00000000 --- a/a1/i18n/ko-KR.md +++ /dev/null @@ -1,3255 +0,0 @@ -# Angular Style Guide - -## Angular Team의 지지를 받습니다. -Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타일 가이드를 위해 리뷰, 기여, 피드백을 해주었고 저를 믿어주고 이끌어 주었습니다. - -## Purpose -*팀환경을 위한 방향을 제시하는 Angular 스타일 가이드 by [@john_papa](//twitter.com/john_papa)* - -만약 Angular [Angular](//angularjs.org) 어플리케이션의 문법, 컨벤션, 구조화를 위한 스타일 가이드를 찾고 있다면 제대로 오셨습니다. 여기 제시된 스타일들은 제 팀 단위 개발 경험, 프레젠테이션, [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa)를 토대로 만들어졌습니다. - -이 스타일 가이드의 목적은 Angular 어플리케이션을 만드는 길잡이 역할을 하기 위함이며 더 나아가 왜 내가 이런 것들을 선택했는지 보여주기 위함입니다. ->만약 이 가이드가 마음에 든다면 Pluralsight 에 올려놓은 저의 강의를 참고하시기 바랍니다. [Angular Patterns: Clean Code](http://jpapa.me/ngclean) - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Community Awesomeness and Credit -저는 Angular 커뮤니티의 대단함을 알게 되었습니다. 그들은 자신들의 경험을 공유하는데 열정적이기 때문입니다. 나의 친구이자 Angular 전문가인 Todd Motto 와 나는 많은 스타일과 컨벤션을 위해 공동작업을 하였습니다. 대부분 우리는 서로 동의하였지만 어떤 부분에서는 의견이 갈렸습니다. Todd의 접근방법이 궁금하고 이를 비교해보고 싶으신 분들은 다음 링크에 가서 확인해보시면 좋을 것 같습니다 [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide). - -제 스타일의 많은 부분은 [Ward Bell](http://twitter.com/wardbell) 과 함께했던 2인 1조의 개발 세션을 통해서 많이 가져왔습니다. 저의 친구 Ward는 이 가이드의 원초적인 전개에 많은 도움을 주었습니다. - -## See the Styles in a Sample App -예제 앱에 적용된 스타일을 참고하세요. -이 가이드가 무엇을, 왜, 어떻게 하는지 다 설명을 하겠지만, 실제로 적용된 것을 보는 게 더 도움이 될 거라고 봅니다. 이 가이드에 제시된 스타일과 양식을 따르는 예제 앱이 함께 제공되고 있습니다. 여기에 가시면 modular 라는 해당 [예제 앱을 modular](https://github.com/johnpapa/ng-demos) 라는 폴더 안에서 보실 수 있습니다. 가서 코드를 확인하시고, 복제하시고, 개입도 해보시기 바랍니다. [실행하는 방법은 readme 에 작성되어 있습니다.](https://github.com/johnpapa/ng-demos/tree/master/modular) - -##Translations -커뮤니티를 통해 유지보수가 되는 [Angular 스타일 가이드의 번역문](https://github.com/johnpapa/angular-styleguide/tree/master/i18n)들은 여기에서 보실 수 있습니다. - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Rule of 1 -###### [Style [Y001](#style-y001)] - - - 각각의 파일에 컴포넌트를 저장하세요. - - 아래 예제는 'app' 모듈과 종속모듈을 정의하고 컨트롤러, 팩토리를 모두 한 파일에서 저장합니다. - - ```javascript - /* avoid */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - 컴포넌트들은 각각의 파일에 따로 저장되었습니다. - - ```javascript - /* recommended */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommended */ - - // some.controller.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Back to top](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - - Angular 컴포넌트들은 즉시 호출 함수구문을 이용해 감싸도록 합니다. (IIFE) - - *이유*: IIFE 방식은 글로벌 범위 변수들을 제거합니다. 이 방법을 통해서 글로벌 범위에서 변수와 함수 선언들이 예상 밖으로 오랫동안 유지되어 메모리를 잠식하는 것을 방지합니다. 또한 이 방법은 글로벌 변수들의 충돌도 막아줍니다. - - *이유*: 실 서버로 코드가 배포되기 전 코드는 최소화하고 묶어져서 하나의 파일로 만들어집니다. 이 때 변수의 충돌이나 너무 많은 글로벌 변수로 문제가 생길 수 있습니다. IIFE는 각각 파일마다 변수 범위를 제공하여 이를 막아줍니다. - - ```javascript - /* avoid */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger function is added as a global variable - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage function is added as a global variable - function storage() { } - ``` - - ```javascript - /** - * recommended - * - * no globals are left behind - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - 주의: 나머지의 예제들에서는 코드를 간결하게 하기 위해서 IIFE를 사용하지 않을 것입니다. 실제 사용할 때는 IIFE로 하세요. - - - 주의: IIFE는 유닛 테스트를 위한 테스트 코드들이 Angular 표현이나 헬퍼 함수들 같은 프라이빗 함수나 변수들에 접근을 못하게 할 수도 있습니다. 하지만 이런 경우 퍼블릭 함수나 변수를 통해서 접근하거나 이 프라이빗 속성들을 노출함으로 테스트를 진행할 수 있습니다. 예를 들어 factory 나 불변 상수에 헬퍼 함수나 레귤러 익스프레션 또는 불변 상수를 옮김으로 가능합니다. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [Y020](#style-y020)] - - - 하위 모듈을 위해 구분자와 함께 유일한 이름을 지정하세요. - - *이유*: 유일, 독특한 이름들은 모듈이름이 충돌하는 것을 방지합니다. 구분자는 그 모듈과 하위모듈 구조를 정의하는데 도움이 됩니다. 예를 들어 'app' 은 당신의 루트 모듈이라면 'app.dashboard'와 'app.users'는 'app' 모듈이 사용하는 하위모듈의 이름으로 지정할 수 있습니다. - -### Definitions (aka Setters) -###### [Style [Y021](#style-y021)] - - - 세터 구문을 사용하여 반환된 모듈을 변수에 저장하지마세요. - - *이유*: 1 파일에 1 컴포넌트를 넣을 경우, 변수에 넣어서 그 변수를 재사용하는 일은 없다고 봐야합니다. - - ```javascript - /* avoid */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - 대신 간단한 세터 구무을 사용하고, 체인으로 나머지 부분을 처리하세요. - - ```javascript - /* recommended */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - 모듈을 이용할 때, 변수에 할당하는 것을 피하고 그 대신 게터 구문을 이용한 체이닝을 사용하세요. - - *이유*: 이렇게 해야 더 이해하기 쉽고 변수 충돌이나 누출을 방지할 수 있습니다. - - ```javascript - /* avoid */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-y023)] - - - 한 번만 set 하고 나머지 인스턴스를 위해서는 get을 사용하세요. - - *이유*: 모듈은 한 번만 만들어지고 설정되어야 하고, 그 후로는 그 모듈을 받아와서 사용해야 합니다. - - ```javascript - /* recommended */ - - // to set a module - angular.module('app', []); - - // to get a module - angular.module('app'); - ``` - -### Named vs Anonymous Functions -###### [Style [Y024](#style-y024)] - - - 콜백 함수를 넘길 때 변수로 할당된 함수를 사용하고 익명 함수 사용을 피하세요. - - *이유*: 이렇게 하면 이해하기 좋은 코드가 작성되어 고치기 훨씬 쉽습니다. 그리고 네스티드 콜백 양을 줄일 수 있습니다. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Back to top](#table-of-contents)** - -## Controllers - -### controllerAs View Syntax -###### [Style [Y030](#style-y030)] - - - '전형적인 $scope 을 사용한 콘트롤러' 대신 [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) 구문을 사용하세요. - - - *이유*: 컨트롤러는 새로이 만들어진 하나의 객채 생성하여 리턴합니다. 그리고 `controllerAs` 구문은 `전형적인 $scope 구문` 보다 더 자바스크립트의 생성자와 흡사하게 작동합니다. - - - *이유*: 이는 뷰에서 해당 변수를 사용할 때 점 접근자를 이용한 속성에 대한 바인딩의 사용을 조장하게 됩니다.(e.g. `name` 대신 `customer.name` ) 이는 후에 점 접근자를 사용하지 않아서 발생할 수 있는 참조 오류를 피할 수 있게 해주며 문맥상으로 훨씬 이해가 쉬운 코드를 작성하게 도와줍니다. - - - *이유*: 이는 네스티드 컨트롤러의 뷰에서 `$parent` 호출의 사용을 피할 수 있게 해줍니다. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax -###### [Style [Y031](#style-y031)] - - - `전형적인 컨트롤러 `$scope` 구문 대신 `controllerAs` 구문을 사용하세요. - - - `controllerAs` 구문은 `$scope` 에 바인딩 하기위해 컨트롤러 안에서 `this`를 사용합니다. - - *이유*: `controllerAs`는 `$scope` 보다 통어적인 장점입니다. 이를 이용하더라도 계속해서 view 에 바인드 할 수 있고 `$scoep`에도 접근이 가능합니다.. - - *이유*: 이는 컨트롤러 안에서 더 나은 방법을 사용하거나, factory에 메서드를 옮기고 컨트롤러에서 factory를 참조하는 것이 더 나은 방법임에도 불구하고 `$scope` 메서드를 사용하게되는 유혹을 피할 수 있도록 도와줍니다. 컨트롤러 내에서 `$scope`을 사용하는 것은 꼭 필요할 때만 하도록 하세요. 예를 들어 퍼블리싱과 섭스크라이빙 이벤트는 [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), 또는 [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on)를 사용하세요. - - ```javascript - /* avoid */ - function CustomerController($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs with vm -###### [Style [Y032](#style-y032)] - - - `controllerAs` 구문을 사용할 때는 `this`를 이용해 capture 변수를 사용하세요. - - *이유*: `this` 예약어는 구문 변수로 컨트롤러 안의 함수에서 사용될 때 그 값이 변경될 수 있기 때문입니다. `this`를 다른 변수에 캡쳐한 후 사용하면 이 문제를 피할 수 있습니다. - - - ```javascript - /* avoid */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function CustomerController() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - 주의: 특정 코드의 바로 윗줄에 특정 코맨트를 추가함으로서 [jshint](http://www.jshint.com/) 경고를 무시하게 할 수 있습니다. 하지만 함수의 이름이 대문자일 경우에는 불필요합니다. 이 경우 함수는 생성자로 여겨지고 그 자체가 Angular에서 컨트롤러이기 때문입니다. - - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - 주의: `controller as`를 이용하여 watch를 만들 때는 아래의 구문을 이용하여 `vm.*` 에 대한 watch를 할 수 있습니다. (watch는 추가적인 실행 사이클을 필요로 할 수 있기 때문에 사용할 때는 주의를 기울이세요.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - - 주의: 대량의 코드에 대한 작업을 할 경우에, 인식과 검색에 대한 오버해드를 줄이는데 도움을 줄 수 있도록 서술적인 이름을 사용하도록 하세요. 그렇다고 부담될 정도의 긴 이름은 피해주세요. - - ```html - - - ``` - - ```html - - - ``` - -### Bindable Members Up Top -###### [Style [Y033](#style-y033)] - - - 바인딩이 가능한 맴버들을 가장 위쪽으로 올리세요. 알파벳 순서로 정렬하세요. 코드 전체에 선언 부분을 섞어서 정렬하지 마세요. - - *이유*: 바인딩할 맴버를 위쪽에 올려두면 뷰에서 어떤 맴버를 사용하는지 즉각적으로 구분하는데 도움이 되고 코드 읽기가 쉬워집니다. - - *이유*: 인라인 익명함수의 세팅이 쉬워집니다. 하지만 이런 함수들의 코드 길이가 1줄을 넘어가면 읽기가 어려워 집니다. 함수 선언은 바인딩 맴버들 아래쪽에 하세요. (함수들은 끌어올려질 거에요) 함수 정의는 아래쪽에 하세요. 바인더블 맴버들을 위쪽에 두세요. 그러면 코드 읽기가 쉬워집니다. - - ```javascript - /* avoid */ - function SessionsController() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - } - ``` - - !["Above the Fold"를 사용하는 컨트롤러](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - 주의: 만약 코드 가독성에 영향을 주지않고 1줄이라면 그냥 위쪽에 두어도 됩니다. - - ```javascript - /* avoid */ - function SessionsController(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - -### Function Declarations to Hide Implementation Details -###### [Style [Y034](#style-y034)] - - - 함수 선언문을 사용하여 구체적인 구현내용을 숨기세요. 바인딩 맴버들은 위쪽에 두세요. 컨트롤러 안에서 함수를 바인딩 하려면 파일 아래쪽에 위치한 함수 선언문을 참조하도록 하세요. 이렇게 하면 바인더블 섹션에 직접적으로 묶여지게 됩니다. 좀 더 구체적인 정보는 여기를 참고하세요 [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *이유*: 바인딩할 맴버를 위쪽에 올려두면 뷰에서 어떤 맴버를 사용하는지 즉각적으로 구분하는데 도움이 되고 코드 읽기가 쉬워집니다. - - *이유*: 구체적인 함수 내용을 파일 아래쪽에 옮겨두면 뷰의 복잡성을 줄여줍니다. 그래서 중요한 내용을 상단에서 볼 수 있습니다. - - 왜: 함수 선언은 인터프리터에 의해서 나중에 위쪽으로 올려집니다. 그래서 아래서 선언된 함수를 위쪽에서 참조하는 것은 문제가 없습니다. (함수 선언문을 사용하는 것과 마찬가지 효과 입니다.) - - 왜: 함수 선언문을 사용할 경우 함수의 참조 순서에 의해서 코드가 실행중단 되는 것을 걱정하지 않아도 됩니다. 함수 표현의 경우 `var a`에서 `var b`를 참조할 경우 코드는 런타임 오류로 멈추게 된다. - - *이유*: 함수 표현에서는 순서가 아주 중요합니다. - - ```javascript - /** - * avoid - * Using function expressions. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - 다음 예제에는 중요한 것들이 두루 포함되어 있습니다. 아래의 예제에서, 중요한 것들은 위쪽에 두었습니다. `vm.avengers` 나 `vm.title` 같은 컨트롤러 바인딩 맴버들을 의미합니다. 구체적인 구현은 아래쪽에 두었습니다. 이렇게 하면 코드 읽기가 쉬워집니다. - - ```javascript - /* - * recommend - * Using function declarations - * and bindable members up top. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Defer Controller Logic to Services -###### [Style [Y035](#style-y035)] - - - 컨트롤러에서 service 와 factory 로 로직을 넘겨서 처리하도록 하세요. - - *이유*: 함수로 노출하는 서비스에 로직을 넣을 경우 다양한 컨트롤러에서 참조하여 재활용이 가능하기 때문입니다. - - *이유*: 서비스에 넣어진 로직은 유닛 테스트용으로 분리가 쉽게 됩니다. 컨트롤러 안에서 로직을 호출하는 것도 쉽게 흉내낼 수 있습니다. - - *이유*: 디펜던시를 없애고 구체적 구현을 컨트롤러로 부터 감출 수 있습니다. - - *이유*: 컨트롤러를 슬림하고 간결하고 포커스 되어있도록 유지할 수 있습니다. - - ```javascript - - /* avoid */ - function OrderController($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recommended */ - function OrderController(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showError); - }; - } - ``` - -### Keep Controllers Focused -###### [Style [Y037](#style-y037)] - - - 한 뷰를 위해 한 컨트롤러를 정의하세요. 다른 뷰를 위해 컨트롤러를 재사용하지마세요. 대신 재사용 가능한 로직을 팩토리에 넣고 컨트롤러를 간단하고 뷰에 포커스 되도록 유지하세요. - - *이유*: 한 컨트롤러를 여러 뷰에서 사용하는 것은 불안정합니다. 좋은 end-to-end (e2w) 테스트 시험 범위는 큰 어플리케이션 전반적인 안정성을 요구합니다. - -### Assigning Controllers -###### [Style [Y038](#style-y038)] - - - 한 컨트롤러가 한 뷰와 쌍을 이루어야 할 때 그리고 컴포넌트가 다른 컨트롤러나 뷰에서 재사용 되어야 할 때, 라우트에서 컨트롤러를 정의하세요. - - 주의: 만약 뷰가 라우트가 아닌 다른 방법으로 로딩 되었을 때는 `ng-controller="Avengers as vm" 구문을 사용하세요. - - *이유*: 라우트에서 컨트롤러를 엮는 방법은 컨트롤러와 뷰를 엮을 때 다른 라우트를 사용할 수 있도록 합니다. 컨트롤러의 할당이 [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) 이 태그를 이용한 것이라면, 그 뷰는 항상 동일한 컨트롤러와 작동하게 됩니다. - - ```javascript - /* avoid - when using with a route and dynamic pairing is desired */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommended */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - 서비스는 `new` 키워드를 통해 인스턴스화 됩니다. `this`를 사용하여 public 메소드와 변수에 접근합니다. 이는 팩토리와 흡사하기 때문에 일관성 있게 팩토리를 사용하도록 하세요. - - 주의: [모든 Angular 서비스는 싱글톤](https://docs.angularjs.org/guide/services). 인젝터 당 주어진 서비스의 인스턴스는 하나만 존재한다는 뜻입니다. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Back to top](#table-of-contents)** - -## Factories - -### Single Responsibility -###### [Style [Y050](#style-y050)] - - - 팩토리는 캡슐화 되어 단 [하나의 책임](http://en.wikipedia.org/wiki/Single_responsibility_principle)만 가져야 합니다. 팩토리가 단일 목적을 넘어 사용되게 된다면 다른 팩토리를 만들어야 합니다. - -### Singletons -###### [Style [Y051](#style-y051)] - - - 팩토리는 싱글톤이고 서비스의 맴버들을 가진 오브젝트를 리턴합니다. - - 주의: [모든 Angular 서비스들은 싱글톤](https://docs.angularjs.org/guide/services). - -### Accessible Members Up Top -###### [Style [Y052](#style-y052)] - - - 노출하고 싶은 호출 가능한 맴버(인터페이스)들은 서비스의 위쪽에 위치시키세요. 여기 링크에서 제시하는 방식을 사용하세요. [모듈 패턴 파해치기](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *이유*: 호출 가능한 맴버들을 상단에 배치하는 것은 가독성을 높여주고 어떤 맴버들이 외부로 노출되고 호출 될 수 있는지 그리고 단위 테스트를 해야하는지 순식간에 파악가능하도록 해줍니다. - - *이유*: 특히 파일이 길어져서 노출 함수나 변수를 알아차리기 위해 스크롤 해야하는 것을 방지하게 됩니다. - - *이유*: 코드 작성을 진행하면서 바로 함수를 세팅하는게 쉬울 수 있습니다. 하지만 그 함수들이 한줄 이상이 되면 가독성이 떨어지고 스크롤을 많이 사용해야 합니다. 리턴되는 서비스에 호출 가능한 인터페이스를 정의하는 것은 구현 부분을 아래로 배치되도록 합니다. 위쪽에 호출 가능한 인터페이스를 배치하는 것은 읽기 쉬운 코드를 만들어 줍니다. - - ```javascript - /* avoid */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommended */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - 호스트 오브젝트 내에서 이런 식의 바인딩이 반영이 되어서, 원시 값들은 모듈 패턴 노출 방식으로 업데이트 되지 않습니다. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Function Declarations to Hide Implementation Details -###### [Style [Y053](#style-y053)] - - - 함수 정의를 사용하여 구체적 구현내용을 숨기세요. 노출시키고 싶은 맴버들을 상단에 배치하세요. 상단의 맴버에서 아래쪽에 정의된 함수들을 할당하세요. 더 자세한 정보는 여기를 보세요. [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *이유*: 상단에 접근 가능한 맴버들을 배치함으로서 읽기 쉬운 코드를 만들어주고 외부에서 접근 가능한 팩토리 함수를 즉각적으로 알아볼 수 있도록 도와줍니다. - - *이유*: 구체적 함수 정의 부분을 파일의 아래쪽에 배치함으로 뷰의 복잡성을 이동시키고 중요한 것들을 상단에서 바로 볼 수 있습니다. - - *이유*: 함수 정의 부분은 아래쪽에 두더라도 인터프리터에 의해서 상단으로 끌어올려집니다. 그래서 정의되기 전에 호출 또는 참조되어도 문제가 발생하지 않아요. (함수 표현으로 할 경우 문제가 될 수 있죠.) - - *이유*: 함수 정의로 할 경우 순서에 상관이 없기 때문에 아무 위치에서 호출해도 걱정할 필요가 없습니다. - - *이유*: 함수 표현으로 할 경우 순서에 의해서 코드가 깨질 수 있기 때문에 중요합니다. - - ```javascript - /** - * avoid - * Using function expressions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommended - * Using function declarations - * and accessible members up top. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [Y060](#style-y060)] - - - 데이터 통신 부분은 팩토리에게 부여하세요. 데이터 서비스들이 XHR 호출, 로컬 저장소, 메모리 저장 등 모든 데이터 조작을 책임지도록 하세요. - - *이유*: 컨트롤러의 책무는 정보를 모아서 뷰를 통해 화면을 그리는 것입니다. 컨트롤러가 데이터를 가져오는 과정에 개입하는 것은 좋지 않아요. 다만 어느 구성요소에게 요구해야 할지만 알면 됩니다. 데이터 서비스로 데이터를 어떻게 가져올지 분리하고 컨트롤러는 간단하게 하고 뷰에 더욱 집중하도록 하세요. - - *이유*: 데이터 서비스를 사용하는 컨트롤러를 테스트할 때 이렇게 하면 (mock or real) 테스트가 모두 쉬워집니다. - - *이유*: 데이터 서비스 구현 부분은 아주 구체적, 특정적인 코드일 가능성일 수 있습니다. 이는 헤더, 통신 방식 또는 `$http` 같은 다른 서비스를 이용할 수도 있습니다. 컨트롤러 같은 실제 사용자들에게서 이를 분리하는 것은 구현 방식을 나중에 변경할 때에도 좋습니다. - - ```javascript - /* recommended */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - 중요: 아래처럼, 컨트롤러 같은, 호출 부에서 데이터 서비스를 호출하여 사용하는 것은 구현 코드를 감추어 줍니다. - - ```javascript - /* recommended */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['dataservice', 'logger']; - - function AvengersController(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Return a Promise from Data Calls -###### [Style [Y061](#style-y061)] - - - `$http` 처럼 프라미스를 리턴하는 데이터 서비스를 호출 할 때는, 호출 받는 함수에서 프라미스를 리턴하세요. - - *이유*: 프라미스를 되받게 되면 프라미스에 체인 호출을 하여 계속해서 데이터 호출 완료 후의 흐름을 작성할 수 있습니다. - - ```javascript - /* recommended */ - - activate(); - - function activate() { - /** - * Step 1 - * Ask the getAvengers function for the - * avenger data and wait for the promise - */ - return getAvengers().then(function() { - /** - * Step 4 - * Perform an action on resolve of final promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Ask the data service for the data and wait - * for the promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * set the data and resolve the promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Back to top](#table-of-contents)** - -## Directives -### Limit 1 Per File -###### [Style [Y070](#style-y070)] - - - 파일 하나당 하나의 디렉티브만 정의하세요. 파일 이름을 디렉티브 이름으로 저장하세요. - - *이유*: 하나의 파일에 모든 디렉티브 정의를 넣으면 쉬울 수 있습니다. 하지만 앱 전체, 특정 모듈들 혹은 한 모듈에서 재사용되는 부분만 꺼내어 활용하는 것은 너무 어렵게 됩니다. - - *이유*: 하나의 파일에 하나의 디렉티브를 넣으면 관리가 쉬워집니다. - - > 주의: "**베스트 프랙티스**: 디렉티브들은 자기 일이 끝난 후에 청소를 해야 합니다. `element.on('$destroy', ...)` 또는 `scope.$on('$destroy', ...)`를 사용해서 디렉티브가 제거되었을 경우에 청소할 수 있습니다." ... Angular 문서에서 발췌 - - ```javascript - /* avoid */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order directive that is specific to the order module */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales directive that can be used anywhere across the sales app */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner directive that can be used anywhere across apps */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* implementation details */ - } - - function salesCustomerInfo() { - /* implementation details */ - } - - function sharedSpinner() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* calendar-range.directive.js */ - - /** - * @desc order directive that is specific to the order module at a company named Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* customer-info.directive.js */ - - /** - * @desc sales directive that can be used anywhere across the sales app at a company named Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* spinner.directive.js */ - - /** - * @desc spinner directive that can be used anywhere across apps at a company named Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* implementation details */ - } - ``` - - 주의: 디렉티브는 다양한 범위에서 사용이 가능하기 때문에, 이름을 지을 때 선택지가 너무 많습니다. 디렉티브와 디렉티브 파일명이 명확하고 선명한 것을 고르세요. 아래 몇몇 예제를 보시고 더욱 다양한 추천방식은 여기를 참고하세요 [Naming](#naming). - -### Manipulate DOM in a Directive -###### [Style [Y072](#style-y072)] - - - DOM을 직접 다루게 되었다면, 디렉티브를 사용하세요. 만약 CSS나 [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) 등의 방식을 사용할 수 있다면 이를 사용하세요. 예를 들, 만약 디렉티브의 기능이 간단히 보여줌, 숨김 기능만 있다면 ngHide/ngShow를 사용하세요. - - *이유*: DOM을 다루는 코드는 테스트, 수정이 어렵고 대부분 더 나은 구현 방법이 존재합니다. (e.g. CSS, 애니매이션, 템플릿) - -### Provide a Unique Directive Prefix -###### [Style [Y073](#style-y073)] - - - `acmeSalesCustomerInfo` 가 HTML에서 `acme-sales-customer-info`로 정의 되는 것 처럼 유일하고 짧고 설명적인 접두어를 사용하세요. - - *이유*: 유일하고 짧은 접두어는 디렉티브의 문맥과 출신을 파악하게 해줍니다. 예를 들어 `cc-`는 아마 CodeCamper 앱을 지칭하려고 사용되었을 수 있습니다. 또한 `acme-`는 Acme company에서 사용된 디렉티브를 지칭할 수 있습니다. - - 주의: `ng-`는 Angular 디렉티브로 예약되어 있기 때문에 사용하지 마세요. 넓게 검색한 후에 충돌이 없는지 확인하고 사용하세요. `ion-` 같은 경우 [Ionic Framework](http://ionicframework.com/) 프로젝트의 접두어로 사용되고 있으니까요. - -### Restrict to Elements and Attributes -###### [Style [Y074](#style-y074)] - - - 혼자서 사용될 수 있다고 판단되는 디렉티브를 만들 때는 restrict `E` (custom element)를 사용하세요. 어트리뷰트를 만들려먼 restrict `A` (custom attribute)를 사용하세요. 대부분, 자신이 컨트롤 하려면 `E`가 적당합니다. 일반적 가이드라인은 `EA` 이지만 홀로 사용되는 엘레멘트 이거나 이미 존재하는 DOM 엘레멘트를 향상시키기 위해서 속성을 변경하는 경우에는 따로 나누어 구현하세요. - - *이유*: 그게 적합하기 때문입니다. - - *이유*: 우리가 디렉티브가 클래스로 사용되도록 할 수도 있지만, 만약 디렉티브가 진짜 엘레멘트나 어트리뷰트 처럼 작동하고 그럴 때 더 적합하다고 생각된다면 말입니다. - - 주의: Angular 1.3+에서 기본값은 EA입니다. - - ```html - -
- ``` - - ```javascript - /* avoid */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommended */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives and ControllerAs -###### [Style [Y075](#style-y075)] - - - 일관되게 컨트롤러와 뷰를 연결시킬 때 `controller as` 구문을 사용하세요. - - *이유*: 어렵지도 않고 그게 적합하기 때문이죠. - - 주의: 아래 있는 디렉티브는 controllerAs를 사용해서 링크나 디렉티브 컨트롤러 안에서 스콥을 사용하는 방법을 명시하고 있습니다. 한 곳에서 다 보여주기 위해서 인라인 템플릿을 사용했습니다. - - 주의: 종속 인젝션을 고려할 때는 여기를 참고하세요. [수동 종속 명시](#manual-annotating-for-dependency-injection). - - 주의: 디랙티브의 컨트롤러는 디렉티브 클로져의 외부에 존재한다는 것을 기억하세요. 이 방식은 `return` 이후에 injection이 만들어지고 접근이 불가하게 되는 문제를 막아줍니다. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - // note: This would be 'ExampleController' (the exported controller name, as string) - // if referring to a defined controller in its separate file. - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injecting $scope just for comparison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - 주의: 링크 함수로 컨트롤러를 인젝트 할 때 컨트롤러의 이름을 정할 수 있습니다. 그리고 그 컨트롤러의 프로퍼티 형식으로 디렉티브 어트리뷰트에 접근이 가능합니다. - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - 디렉티브에서 `controller as`를 사용할 때 `bindToController = true`를 사용하면 컨트롤러의 스콥을 외부 스콥에 바인딩할 수 있습니다. - - *이유*: 외부 스콥을 쉽게 디렉티브의 컨트롤러 스콥에 바인딩할 수 있습니다. - - 주의: `bindToController`는 Angular 1.3.0 부터 사용이 가능합니다. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Back to top](#table-of-contents)** - -## Resolving Promises for a Controller -### Controller Activation Promises -###### [Style [Y080](#style-y080)] - - - 컨트롤러의 시작-준비 로직을 `activate` 함수 안에 넣으세요. - - *이유*: 시작-준비 로직을 컨트롤러 안의 일관된 부분에 위치 시키세요. 이는 일관성 있는 테스트를 하게 해주고, 시작-준비 코드가 군데군데 퍼지는 것을 막아줍니다. - - *이유*: `activate` 함수를 만들어두면 컨트롤러/뷰를 새로고침하는 곳에서 재사용하기가 편리합니다. 그리고 필요한 코드들이 함께 있도록 해주고, 사용자가 뷰에 빠르게 도달하도록 해줍니다. `ng-view` 나 `ui-view`에서 애니매이션을 만들기가 쉬워지고 사용자들이 인터페이스가 즉각적이라고 느껴지게 해줍니다. - - 주의: 만약 컨트롤러를 사용하기 전 시점에서 라우팅을 조건적으로 취소해야 한다면 [route resolve](#style-y081)를 대신 사용하세요. - - ```javascript - /* avoid */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Route Resolve Promises -###### [Style [Y081](#style-y081)] - - - 컨트롤러가 활성화되기 전에 프라미스 값이 구해져야 한다면, 컨트롤러 로직이 실행되기 전에 `$routeProvider`에서 종속 값들을 다 구하도록 하세요. 만약 컨트롤러가 활성화되기 전에 라우트를 조건적으로 중단해야 한다면, 라우트 resolver를 이용하세요. - - - - 화면의 변화가 일어나기 전에 라우틀를 취소해야 할지 말지 결정해야 한다면 라우트 resolve를 이용하세요. - - *이유*: 컨트롤러가 로드되기 전에 데이터를 필요로 할 수도 있기 때문입니다. 그 데이터는 특정 팩토리나 [$http](https://docs.angularjs.org/api/ng/service/$http)를 통해서 프라미스부터 올 수 있습니다. [라우트 resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)는 컨트롤러 로직이 실행되기 전 프라미스를 구할 수 있도록 해줍니다. 그래서 프라미스를 구한 후 특정 액션을 취할 수 있습니다. - - *이유*: 코드는 라우트와 컨트롤러의 준비-시작 함수 다음 실행이 됩니다. 뷰는 바로 로드가 시작됩니다. 데이터 바인딩은 활성 프라미스가 구해지면 시작됩니다. "busy" 애니매이션은 뷰의 변경 중 보여질 수 있습니다. (`ng-view` or `ui-view`를 통해서) - - 주의: 코드는 프라미스를 통해서 라우트 전에 실행됩니다. 프라미스를 거절하면 라우팅을 중단합니다. Resolve는 라우트가 값을 가져오기 전까지 다음 화면이 기다리게 합니다. "busy" 애니매이션은 화면 변경 중, resolve 전에 보여질 수 있습니다. 뷰에 좀 더 빨리 도달하기를 원하고, 뷰에 갈 수 있는지 체크포인트가 필요하다면, [컨트롤러 `activate` 테크닉](#style-y080)를 대신 고려하세요. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('AvengersController', AvengersController); - - function AvengersController(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - 주의: 아래 예제는, 라우트 resolve 가 함수 정의를 가르키고 있음을 보여줍니다. 이는 디버깅과 디펜던시 인젝션을 핸들 하기 쉽게 해줍니다. - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - 주의: 예제의 `movieService` 의존성은 코드 최소화에 안전하지 않습니다. 코드 최소화에 안전하도록 만들려면 여기를 참고하세요. [dependency injection](#manual-annotating-for-dependency-injection), [minification and annotation](#minification-and-annotation). - -**[Back to top](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### UnSafe from Minification -###### [Style [Y090](#style-y090)] - - - 최소화-안전 접근법을 사용하지 않으면서 의존성 정의 단축 문법을 사용하지 않도록 하세요. - - *이유*: 컨트롤러나 팩토리 같은 컴포넌트에 전달되는 파라미터들은 최소화된 이름으로 변경되어 버립니다. 예를 들어 `common` 과 `dataservice`는 `a` 또는 `b` 처럼 Angular에서 발견될 수 없게 되어버립니다. - - ```javascript - /* avoid - not minification-safe*/ - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController(common, dataservice) { - } - ``` - - 이 코드는 최소화된 변수명으로 런타임 에러를 발생시킬 수 있습니다. - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('DashboardController', d);function d(a, b) { } - ``` - -### Manually Identify Dependencies -###### [Style [Y091](#style-y091)] - - - `$inject`를 사용하여 수동적으로 Angular 의존성을 정의하세요. - - *이유*: 이 방법은 [`ng-annotate`](https://github.com/olov/ng-annotate)에서 이용된 기술을 반영합니다. 또한 최소화 안전 의존성코드를 자동으로 최소화 시킬 때 추천합니다. 만약 `ng-annotate`이 이미 인젝션이 된 것을 탐지하면 반복해서 인잭션 하지 않게 됩니다. - - *이유*: 이 방법은 파라미터이 이름이 최소화 재생성 되었을 때 망가지기 쉬운 문제로 부터 보호해줍니다. `common` 와 `dataservice`는 `a` or `b`로 변경될 것이고 이는 Angular에서 찾을 수 없는 키워드입니다. - - *이유*: 어레이 형식의 한 줄 의존성 정의는 읽기가 어려우므로 피해야 합니다. 그리고 마지막 항목은 함수로 넘겨질 경우 앞쪽의 스트링으로 넘겨진 것들과 헷갈리기 쉽습니다. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('DashboardController', DashboardController); - - DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function DashboardController($location, $routeParams, common, dataservice) { - } - ``` - - 주의: `$inject` 실행 부분이 return 명령어 아래쪽에 위치할 경우, 실행되지 않게 됩니다. (이는 디렉티브에서 발생할 수 있습니다.) 이를 해결하기 위해서는 컨트롤러를 디렉티브의 바깥쪽에 위치시키면됩니다. - - ```javascript - /* avoid */ - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommended */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Manually Identify Route Resolver Dependencies -###### [Style [Y092](#style-y092)] - - - `$inject`를 사용하면 수동으로 Angular 컴포넌트의 라우트 resolver 의존성을 구분하게 합니다. - - *이유*: 이 방법은 익명 함수를 라우트 resolver로부터 분리하여 읽기 쉬운 코드로 만들어줍니다. - - *이유*: `$inject` 구문은 의존성 최소화 안전화를 다루기 위해 쉽게 resolver로 진행할 수 있습니다. - - ```javascript - /* recommended */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com)를 사용할 때 [ng-annotate](//github.com/olov/ng-annotate)를 사용하세요. 그리고 `/* @ngInject */`이 코맨트 함수를 사용하여 자동화된 의존성 인젝션을 사용하세요. - - *이유*: 최소화-안전 방식을 사용하지 않는 의존성 정의를 지켜줍니다. - - *이유*: [`ng-min`](https://github.com/btford/ngmin)는 제외 되었습니다. - - >나는 Gulp를 선호합니다. 작성하고 읽고 디버깅하기 쉽다고 생각합니다. - - 아래 코드는 최소화 안전 의존성코드를 사용하지 않습니다. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - ``` - - When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. - 위의 코드가 포함된 코드가 ng-annotate를 거치게 되면 `$inject` 부분을 생성하게 되어 최소화 안전 코드가 됩니다. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - - AvengersController.$inject = ['storage', 'avengerService']; - ``` - - 주의: 만약 `ng-annotate` 가 이미 인잭션 코드가 있음을 발견하면 이를 재생성하지 않습니다 (`@ngInject`를 발견하면). - - 주의: 라우트 resolver를 사용할 경우 그 함수에도 `/* @ngInject */`를 사용할 수 있습니다. 이것은 적당한 구문을 생성하여 최소화 안전 의존성을 지켜줍니다. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > 주의: Angular 1.3 부터는 [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) 디랙티브의 `ngStrictDi` 파라미터를 사용하면 잠재적으로 빠뜨려진 최소화 안전 코드를 찾아낼 수 있습니다. 이를 사용하면 "strict-di" 모드에서 만들어진 인젝터는 어플리케이션이 명시적 함수 구문을 사용하지 않는 함수를 호출하면 실행 실패를 유발 할 수 있습니다 (이는 최소화 안전하지 않습니다). 디버깅 시 이를 발생시킨 코드를 추적할 수 있는 정보를 콘솔에 뿌려줍니다. 나는 디버깅 시에만 `ng-strict-di`를 사용하는 것을 선호합니다. - `` - -### Use Gulp or Grunt for ng-annotate -###### [Style [Y101](#style-y101)] - - - [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate)를 사용하여 자동화 코드생성을 하세요. 의존성을 가지고 있는 모든 함수의 앞부분에 `/* @ngInject */`를 넣으세요. - - *이유*: ng-annotate는 대부분의 의존성을 잡아냅니다. 하지만 가끔 `/* @ngInject */`를 이용한 힌트가 필요할 수 있습니다. - - 아래 코드는 ngAnnotate를 이용하는 걸프 작업의 예입니다. - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Back to top](#table-of-contents)** - -## Exception Handling - -### decorators -###### [Style [Y110](#style-y110)] - - - 설정하는 부분에서 [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) 서비스를 사용할 때 [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator)를 사용하여 [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) 서비스를 이용하여 예외가 발생했을 때 대처하세요. - - *이유*: 개발기간과 실행시간에 발견되지 않은 Angular 예외를 일관되게 처리하도록 합니다. - - 주의: 다른 방법으로는 decorator를 사용하지 않고 기존 서비스를 덮어쓰기 하는 것입니다. 이 방법은 나름 좋습니다. 하지만 기본적인 방법을 사용하면서 확장하고 싶다면 decorator를 추천합니다. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Could add the error to a service's collection, - * add errors to $rootScope, log errors to remote web server, - * or log locally. Or throw hard. It is entirely up to you. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Catchers -###### [Style [Y111](#style-y111)] - - - 예외를 우아하게 처리하고 싶다면 팩토리를 만들어서 인터페이스를 노출하는 식으로 사용하세요. - - *이유*: 예를 들어 XHR 호출이나 프라미스 실패 시, 좀 더 일관적인 방식으로 코드에서 발생한 예외를 잡아줍니다. - - 주의: 예외 캐쳐는 당신이 예상했던 호출에서 특정 예외가 발생했을 때 그것을 잡아내고 대처하는데 좋습ㄴ디ㅏ. 예를 들어 원격 웹 서비스에 접속해서 데이터를 가져오는 XHR 호출을 만들 때 그 서비스로 부터 예외를 받아서 특정한 방식으로 대처할 수 있습니다. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Errors -###### [Style [Y112](#style-y112)] - - - [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError)를 사용하여 라우팅 에러를 다루고 기록할 수 있습니다. - - *이유*: 라우팅 에러를 일관적인 방법으로 처리할 수 있습니다. - - *이유*: 잠재적으로 더 나은 사용자 경험을 제공합니다. 사용자가 라우팅 에러를 마주하게 되면 좀 더 친숙한 페이지로 안내하거나 복구 선택도 가능하게 할 수 있습니다. - - ```javascript - /* recommended */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionally log using a custom service or $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - - /** - * On routing error, go to another route/state. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [Y120](#style-y120)] - - - 다음의 패턴을 참고하여 모든 컴포넌트 파일 이름을 만드세요. 컴포넌트의 기능을 설명하는 이름에 (선택적으로) 그 것의 타입을 더 하면 됩니다. 제가 추천하는 이름은 `feature.type.js` 이런 식입니다. 대부분 다음과 같은 2 가지의 이름을 가지게 됩니다: - * 파일 이름 (`avengers.controller.js`) - * Angular 에 등록되는 컴포넌트 이름 (`AvengersController`) - - *이유*: 이름 짓는 규칙을 가지게 되면 금방 원하는 정보의 위치를 찾을 수 있습니다. - 프로젝트를 일관되게 하는 것은 필수적입니다. 팀 전체가 일관되는 것은 중요합니다. 회사 전체가 일관된 것은 어마한 효과를 가져옵니다. - - *이유*: 이름 규칙을 가지게 되면 일단 코드를 찾고 이해하는 데 매우 도움이 됩니다. - -### Feature File Names -###### [Style [Y121](#style-y121)] - - - 다음의 패턴을 참고하여 모든 컴포넌트 파일이름을 만드세요. 컴포넌트의 기능을 설명하는 이름에 (선택적으로) 그것의 타입을 더 하면 됩니다. 제가 추천하는 이름은 `feature.type.js` 이런 식입니다. - - *이유*: 컴포넌트를 찾는데 매우 일관적인 빠른 방법을 제공합니다. - - *이유*: 자동화 업무를할 때 파일 선택의 패턴을 동일하게 사용할 수 있습니다. - - ```javascript - /** - * common options - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommended - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - 주의: 또 다른 컨트롤러를 이름 짓는 통용 관습으로는 `controller`라는 단어를 제외하는 것입니다. `avengers.controller.js`가 아닌 `avengers.js` 처럼 말이죠. 다른 대부분의 컴포넌트들은 접미사를 그대로 사용하게 둡니다. 컨트롤러가 제일 흔하게 사용되는 컴포넌트 타입이기 때문에 controller라고 명시하지 않아도 쉽게 구분할 수 있습니다. 이 중 한 가지 관습을 선택한 다음 전체 팀이 모두 같은 관습을 사용하도록 하기를 권장합니다. 제가 선호하는 방식은 `avengers.controller.js` 입니다. - - ```javascript - /** - * recommended - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test File Names -###### [Style [Y122](#style-y122)] - - - 테스트 명세파일을 이름 지을 때는 추가 접미사 `spec`를 붙여서 만듭니다. - - *이유*: 일관적으로 빠르게 해당 컴포넌트를 찾을 수 있습니다. - - *이유*: [karma](http://karma-runner.github.io/)나 다른 테스트 러너에게 패턴 매칭으로 테스트 파일을 선택할 수 있게 할 수 있습니다. - - ```javascript - /** - * recommended - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller Names -###### [Style [Y123](#style-y123)] - - - 컨트롤러 이름을 지을 때는 그의 기능을 따서 일관된 이름을 짓도록 합니다. 생성자를 이름 지을 때는 대문자 캐멀 케이스를 사용하세요. - - *이유*: 일관된 방식으로 찾아내고 참조할 수 있도록 합니다. - - *이유*: 대문자 캐멀 케이스는 생성자로 구별되는 오브젝트를 구별하는 방식입니다. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Controller Name Suffix -###### [Style [Y124](#style-y124)] - - - 컨트롤러 이름에 `Controller`라는 접미사를 붙여주세요. - - *이유*: `Controller`접미사를 사용하는 것은 좀 더 일반적이고 또한 좀 더 명시적으로 설명적이기 때문입니다. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Factory and Service Names -###### [Style [Y125](#style-y125)] - - - 각각의 기능에 따라 팩토리와 서비스의 이름을 일관적으로 지어주세요. 대문자 캐멀 케이싱을 사용하세요. `$`를 접두어로 사용하는 것은 피하세요. 예를 들어 이름이 명사이거나 다른 이유로 이름 자체가 뭘 하는지 모호한 경우에만 접미사로 `Service`를 사용하세요. - - *이유*: 일관된 방식으로 팩토리를 찾아내고 참조할 수 있도록 합니다. - - *이유*: `$` 접두어가 붙어있는 내장된 팩토리와 서비스들과 이름 충돌이 일어나지 않도록 하세요. - - *이유*: `logger`같은 서비스는 굉장히 목적이 뚜렷하기 때문에 접미어가 필요하지 않습니다. - - *이유*: `avengers`같은 서비스 이름은 명사이고 확실한 기능이 떠오르지 않기 때문에 `avengersService`처럼 접미어를 붙여야 합니다. - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - - ```javascript - /** - * recommended - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // customer.service.js - angular - .module - .service('customerService', customerService); - - function customerService() { } - ``` - -### Directive Component Names -###### [Style [Y126](#style-y126)] - - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - - 캐멀 캐이스를 이용해서 디렉티브 이름을 일관적으로 지어주세요. 짧은 접두어를 사용하여 이 디렉티브가 어떤 프로젝트 혹은 회사에 소속되어 있는지 알려주세요. - - *이유*: 일관된 방식으로 컴포넌트를 찾아내고 참조할 수 있도록 합니다. - - ```javascript - /** - * recommended - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - 여러개의 모듈이 존재할 경우, 중심이 되는 모듈은 `app.module.js` 로 이름을 짓고, 다른 의존 모듈들은 각자의 대표 기능을 따서 이름을 지으면 됩니다. 예를 들어 어드민 모듈은 `admin.module.js`으로 말이죠. 각가의 등록된 이름은 `app`과 `admin`이 될 겁니다. - - *이유*: 큰 어플리케이션으로 확장하기 위해서 여러 모듈을 사용할 때 일관성을 유지할 수 있습니다. - - *이유*: 자동화 작업을 위해 모듈 정의를 모두 로드하고, (바인딩을 위한) 모든 다른 angular 파일을 로드하는 것을 쉽게 만들어 줍니다. - -### Configuration -###### [Style [Y128](#style-y128)] - - - 모듈의 이름을 따서 만든 파일에 설정 부분을 분리해서 넣도록 합니다. 중심 모듈인 `app`모듈의 설정파일은 `app.config.js`가 될 겁니다. (또는 간단히 `config.js`) `admin.module.js` 의 설정 파일은 `admin.config.js`이 됩니다. - - *이유*: 설정 파일을 모듈 정의, 컴포넌트, 작동 코드로 부터 분리합니다. - - *이유*: 모듈 설정 부분을 구분할 수 있는 곳을 제공합니다. - -### Routes -###### [Style [Y129](#style-y129)] - - - 라우팅 설정 부분을 따로 만들어서 저장합니다. 예를 들어 `app.route.js`는 중심 모듈의 라우팅, `admin.route.js`는 `admin`모듈의 라우팅 설정이 됩니다. 비록 작은 앱이라도 저는 분리해서 저장하는 것을 선호합니다. - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - 앱을 구조적으로 만들게 되면 당신은 코드를 빠르게 `L`위치 추적 할 수 있고, 한눈에 `I`구분할 수 있고, `F`단순한 구조를 유지할 수 있고, DRY(반복하지 마세요)를 `T`유지할 수 있습니다. 구조는 다음 4 가지 가이드라인을 따르면 됩니다. - - LIFT *이유*: 규모를 잘 조절할 수 있는 일관된 구조를 제공합니다. 코드를 쉽게 찾을 수 있으므로 개발자의 효율을 쉽게 증대시킵니다. 다음 질문을 함으로서 앱이 구조가 잘 갖추어 져 있는지 확인할 수 있습니다. - - 만약 다음 가이드라인에 적합하지 않는 부분이 있다고 느껴지면, 위로 돌아가서 LIFT 가이드라인을 다시 살펴보세요. - - 1. `L`코드를 쉽게 찾아낼 수 있음 - 2. `I`첫눈에 구분할 수 있음 - 3. `F`단순한 구조를 유지할 수 있음 - 4. `T`반복작업을 피할 수 있음 - -### Locate -###### [Style [Y141](#style-y141)] - - - 코드를 찾아내는 방법을 간단하고, 직관적이고 빠르게 해야 합니다. - - *이유*: 저는 이 부분이 어마어마하게 중요하다는 것을 알게 되었습니다. 팀 사람들이 그들이 필요한 파일을 빨리 찾을 수 없다면, 최대의 효율을 내서 일할 수 없습니다. 구조는 변경되어야 합니다. 간혹 당신은 파일명을 알 수 없고 어떤 파일과 관련이 있는지 알 수 없습니다. 그래서 최고의 직관적인 위치나 그 근처의 위치에 파일을 두는 것은 어마어마한 시간을 아껴줍니다. 설명적인 폴더 구조는 이를 도와줄 수 있습니다. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - 파일명을 보면 즉시 그 안에 뭐가 있고 어떤 기능을 담고 있는지 알 수 있어야 합니다. - - *이유*: 파일을 찾으러 다니면서 소비되는 시간이 줄어들수록 업무의 효율은 증가합니다. 만약 그러다가 파일명이 길어진다면, 그냥 길게 쓰세요. 파일명이 충분히 설명적이어도 되고 그 안의 내용은 그 설명에 부합하는 1개의 컴포넌트 기능이어야 합니다. 한 파일 안에 여러 컨트롤러나 여러 서비스 또는 그 두 가지 들이 여럿 들어있으면 안 됩니다. 가끔 1파일 규칙을 어기게 되는 경우가 있습니다. 여러 가지의 작은 기능들인데 서로 관련되어 있는 경우입니다. 하지만 그렇게 하더라고 쉽게 구별할 수 있기 때문입니다. - -### Flat -###### [Style [Y143](#style-y143)] - - - 최대한 수평적 구조를 갖도록 하세요. 7개 이상의 파일을 갖게 되면, 나눌 수 있도록 고려해보세요. - - *이유*: 7층의 폴더구조를 오가며 파일을 찾는 일은 쉽지가 않습니다. 웹사이트의 메뉴를 생각해보세요 ... 2단계 이상이 된다면 심각하게 재고를 해야 합니다. 폴더 구조에서는 그런 엄격한 룰은 존재하지 않지만, 한 폴더 안에 7-10개의 파일이 존재한다면, 하위 폴더를 만들 적절한 시기일 수 있습니다. 자신이 편하게 느끼는 정도로 말이죠. 명백한 이유가 있다면 하위 폴더를 만들되 그전까지는 나머지 LIFT를 지키기 위해 폴더를 수평적으로 유지하세요. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - 반복작업 하지마세요. 그렇다고 너무 오버해서 가독성을 희생하지도 마세요. - - *이유*: 반복작업을 하지 않는 것은 중요합니다. 하지만 다른 LIFT 요소를 희생해야 한다면 그리 과하게 DRY를 지킬 필요는 없습니다. 그래서 저는 T-DRY 라고 명명합니다. (try-DRY) 나는 session-view.html 이라고 view를 위해 이름짓고 싶지 않습니다. 왜냐면 명백히 이것은 view 이기 때문이죠. 만약 이것이 명백하지 않고 다른 관습에 의한 게 아니라면 저는 그렇게 이름을 지을 겁니다. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [Y150](#style-y150)] - - - 구현을 위한 생각을 가지되, 멀리 보고 고려하세요. 달리 말하자면, 작게 시작하지만 앞으로 얼마나 커질 수 있을지 항상 기억하세요. 모든 앱 코드는 `app`이라는 최상위 폴더에 들어갈 겁니다. 한 파일 안에는 한 가지 기능만 들어갑니다. 컨트롤러, 서비스, 모듈, 뷰는 각각의 파일을 가지고 그안에 넣습니다. 제 3의 외부 코드들은 `app`폴더가 아닌 다른 상위 폴더를 가지게 하세요. 내가 작성한 코드가 아니니 그 것들이 제 앱을 어지럽히기 원치 않으니까요 (`bower_components`, `scripts`, `lib`). - 주의: 구조를 만드는 것에 좀 더 구체적인 이유를 알고 싶으면 여기를 참고하세요. [this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - 어플리케이션의 전체 레이아웃을 정의하는 컴포넌트는 `layout`이라는 폴더 안에 넣어주세요. 이는 쉘 뷰나 컨트롤러를 포함할 수 있고 앱, 네비게이션, 메뉴, 내용 부분, 그리고 다른 부분을 감사는 컨테이너로 역할을 할수 있습니다. - - *이유*: 레이아웃과 관련된 모든 것들을 한 곳에 넣어두고 전체 어플리케이션에서 재사용을 하도록 정리하세요. - -### Folders-by-Feature Structure -###### [Style [Y152](#style-y152)] - - - 폴더를 만들 때는 그 기능을 따서 이름을 지으세요. 폴더가 점점 커져서 7개 파일 이상 가지게 되면 따로 폴더를 만들기를 고려하세요. 기준은 사람마다 다를 수 있습니다. 필요에 따라 조절하면서 진행하세요. - - *이유*: 개발자는 한눈에 각 코드의 위치를 파악하고 어떤 용도로 필요한지 구별할 수 있습니다. 최대한 폴더 구조가 수평적일수록 반복적이고 불필요한 이름이 적게 사용되게 됩니다. - - *이유*: LIFT 가이드 라인에 다 만족됩니다. - - *이유*: 코드 내용들을 LIFT 가이드라인을 다 만족하도록 정리하면 복잡하게 어질러질 가능성을 줄여줍니다. - - *이유*: 만약 많은 파일을 위치시켜야 한다면 일관적인 구조라면 쉽게 위치시킬 수 있지만 수평적 구조에서는 좀 더 어려울 수 있습니다. - - ```javascript - /** - * recommended - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![심플한 앱 구조](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - 주의: 타입별로 묶어서 폴더를 만드는 식으로 앱 구조를 만들지 마세요. 한 가지 기능에 대해서 일하려면 여러 폴더를 옮겨 다녀야 합니다. 이는 앱이 5, 10 또는 25 이상의 뷰와 컨트롤러(또는 다른 기능들)를 갖게되면 기능으로 나뉜 폴더일 경우보다 금방 거추장스러워 집니다. - - - ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [Y160](#style-y160)] - - - 한 가지에 대해서 책임을 캡슐화하는 작은 모듈들을 만드세요. - - *이유*: 모듈화된 어플리케이션은 끼우고 사용하는 방법을 쉽게 합니다. 어플리케이션의 수직적 조각을 만들도록 도와주는데 이는 점차 역할을 합니다. 이것은 우리가 몇 가지 기능을 개발하는 동시에 조립해서 사용할 수 있다는 뜻입니다. - -### Create an App Module -###### [Style [Y161](#style-y161)] - - - 중심적 역할을 하는 근본 모듈을 만들어서 모든 나머지 모듈과 기능들을 조직하고 협업되도록 하세요. 이 모듈의 이름은 전체 어플리케이션의 이름을 따서 지으세요. - - *이유*: Angular는 모듈화와 분리 패턴을 권장합니다. 근본 모듈을 만들어서 이 모듈이 다른 모듈들을 사용하게 함으로써 전체 모듈에 새로운 모듈을 추가하는 등의 작업을 직관적으로 만들어줍니다. - -### Keep the App Module Thin -###### [Style [Y162](#style-y162)] - - - 어플리케이션 모듈에는 조직하는 로직만 넣도록 합니다. 기능들은 각각의 모듈에 들어가게 합니다. - - *이유*: 어플리케이션을 구성하는 로직 외, 원격 데이터를 가져오거나 뷰를 보여주거나 등의 로직을 근본 모듈에 넣게 되면 기능 모듈들을 재사용 어렵게 하고 기능을 잠시 꺼두기 어렵게 합니다. - - *이유*: 근본 모듈은 이 앱을 정의하는데 어떤 모듈들이 도움을 주는지 설명하는 목록이 됩니다. - -### Feature Areas are Modules -###### [Style [Y163](#style-y163)] - - - 레이아웃 같은 기능 구역을 나타내는 모듈들, 재사용 가능하고 공유가 가능한 서비스, 대시보드, 특화된 기능들을 모듈로 만드세요 (예를 들어 고객, 어드민, 판매). - - *이유*: 자극 자족적인 모듈들은 어플리케이션에 아주 작은 충돌 혹은 아무 충돌 없이 추가될 수 있습니다. - - *이유*: 스프린트나 이터레이션은 기능 구역에 집중될 수 있고 마지막 부분에 켜질 수 있습니다. - - *이유*: 기능 구역을 구분하여 모듈화 하는것은 고립되고 재사용 코드로서 모듈 테스트에서 사용하기 쉽게 합니다. - -### Reusable Blocks are Modules -###### [Style [Y164](#style-y164)] - - - 예외처리, 로깅, 진단, 보안, 그리고 로컬 데이터 저장 등의 일반적인 서비스들은 재사용 가능한 어플리케이션 블럭으로 모듈화 될 수 있습니다. - - *이유*: 이런 성격의 기능들은 많은 어플리케이션에서 사용될 수 있습니다. 이런 부분들을 분리하여 각각 모듈로 만들어두면 다른 어플리케이션에서 일반적인 부분으로 두루 사용될 수 있습니다. - -### Module Dependencies -###### [Style [Y165](#style-y165)] - - - 어플리케이션의 루트 모듈은 앱 특화된 기능모듈 그리고 재사용되고 공유된 모듈들에 의존하게 됩니다. - - ![모듈화 그리고 의존성](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *이유*: 주요 앱 모듈은 어플리케이션의 기능들을 빠르게 구분할 수 있는 목록을 가지고 있습니다. - - *이유*: 각각의 기능 구역은 자신이 의존하고 있는 모듈의 목록을 가지고 있습니다. 이는 다른 어플리케이션에서 작동될 때 그 모듈을 참조함으로 문제없이 작동할 수 있습니다. - - *이유*: 공유 데이터 서비스 같은 인트라-앱 기능은 `app.core`(자신이 선호하는 이름으로 지으세요)라는 모듈에 넣어두면 찾기도 쉽고 공유하기도 쉽습니다. - - 주의: 이것은 일관성을 위한 전략입니다. 다른 많은 좋은 옵션들도 있습니다. 어떤 것이든 일관되고 Angular의 의존성 규칙을 따르고, 유지보수가 쉽고 확장성이 있다면 선택해도 됩니다. - - > 내가 사용하는 구조는 프로젝트마다 조금씩 다릅니다. 하지만 모두 이 구조화, 모듈화에 대한 가이드라인을 지킵니다. 실제 구현방식은 팀마다 조금씩 다를 수 있습니다. 다르게 표현하자면, 완전히 똑같은 구조를 사용하려고 애쓰다가 전체를 거부하지 마세요. 일관성과 유지보수 그리고 효율을 마음에 두면서 자신의 구조를 직접 판단해보세요. - - > 또한 작은 앱들에서는 공유되는 의존 모듈들을 앱 모듈 폴더에 넣는 것도 고려하세요. 단 기능 모듈들이 직접 의존하지 않는 경우에 입니다. 이렇게 하면 작은 앱들은 유지하기가 쉽습니다. 하지만 다른 어플리케이션에서 이 모듈을 재사용하는것은 어려워 집니다. - -**[Back to top](#table-of-contents)** - -## Startup Logic - -### Configuration -###### [Style [Y170](#style-y170)] - - - Angular 앱을 실행하기 전에 인젝트 코드는 [모듈 설정](https://docs.angularjs.org/guide/module#module-loading-dependencies)에 설정되어 있어야 합니다. 이상적인 위치는 프로바이더와 상수 입니다. - - *이유*: 이는 설정하는 부분을 간결하게 합니다. 설정이 여기저기 퍼져있으면 유지보수하기 힘들고 헷갈리수 있습니다. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run Blocks -###### [Style [Y171](#style-y171)] - - - 어떠한 코드든 어플리케이션이 실행 될 때 실행되어야 한다면, 팩토리로 정의되어서 함수로 노출이 되고 [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies)에 인젝트 되어야 합니다. - - *이유*: 실행 구역에 직접 작성된 코드는 테스트하기 어렵습니다. 팩토리에 넣어두면 추상화하고 흉내내기가 쉽습니다. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document and $window -###### [Style [Y180](#style-y180)] - - - `document` 와 `window` 대신 [`$document`](https://docs.angularjs.org/api/ng/service/$document) 와 [`$window`](https://docs.angularjs.org/api/ng/service/$window)를 사용하세요. - - *이유*: 이 서비스들은 Angular에 의해서 감추어집니다. 그리고 window와 document를 사용해서 테스트 하는 것 보다 테스트를 쉽게 해줍니다. 또한 document와 window를 흉내 내도록 코드를 만들어야 하는것을 피하게 해줍니다. - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - `setTimeout` 와 `setInterval` 대신 [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) 와 [`$interval`](https://docs.angularjs.org/api/ng/service/$interval)를 사용하세요. - - *이유*: 이 서비스들은 Angular에 의해서 감추어집니다. 데이터 바인딩을 유지시켜주기 때문에 테스트하기 좋고 Angular의 실행 주기를 다루는데 더 쉽습니다. - -**[Back to top](#table-of-contents)** - -## Testing -단위 테스팅은 깨끗한 코드를 유지하도록 도와줍니다. 여기에 단위 테스팅을 위한 약간의 권장 사항을 링크와 함께 제공하려고 합니다. - -### Write Tests with Stories -###### [Style [Y190](#style-y190)] - - - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. - - 모든 각각의 스토리를 위해서 테스트 세트를 작성하세요. 빈 테스트로 시작을 하고 스토리에 맞추어 코드를 작성하면서 채워나가세요. - - *이유*: 테스트 설명을 작성함으로 스스로의 스토리가 어떻게 작동해야 하고 어떻게 작동하지 말아야 하는지 투명하게 정의할 수 있습니다. 그리고 성공을 어떻게 측정해야 하는지도 포함됩니다. - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Testing Library -###### [Style [Y191](#style-y191)] - - - [Jasmine](http://jasmine.github.io/) 또는 [Mocha](http://mochajs.org)를 사용하여 단위 테스트를 하세요. - - *이유*: Jasmine과 Mocha 모두 Angular 커뮤니티에서 두루 사용되고 있습니다. 둘다 안정적이고 잘 유지보수 되고 있으며 확실하게 기능 테스트를 할 수 있습니다. - - 주의: 모카를 사용할 경우 [Chai](http://chaijs.com)와 같은 assert 라이브러리를 선택하세요. 저는 개인적으로 Mocha를 선호합니다. - -### Test Runner -###### [Style [Y192](#style-y192)] - - - [Karma](http://karma-runner.github.io)를 테스트 실행자로 사용하세요. - - *이유*: Karma는 설정하기 쉽고 그냥 한 번 실행할 수도 있고 코드가 변경되면 자동으로 실행할 수 도 있습니다. - - *이유*: Karma는 그 자체로 또는 Grunt나 Gulp를 이용하여 지속적인 통합을 연결해 진행하기 쉽도록 해줍니다. - - *이유*: 어떤 IDE들은 Karma와 통합하기 시작했습니다. [WebStorm](http://www.jetbrains.com/webstorm/), [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) - - *이유*: [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://www.gulpjs.com)와 같은 자동화 선두주자들과 협업이 아주 좋습니다. Gulp를 사용한다면 [Karma](https://github.com/karma-runner/karma)를 플러그인을 사용하지 않고 직접적으로 API를 호출할 수 있습니다. - - ```javascript - /* recommended */ - - // Gulp example with Karma directly - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### Stubbing and Spying -###### [Style [Y193](#style-y193)] - - - 스터빙이나 스파잉을 위해서는 [Sinon](http://sinonjs.org/)를 사용하세요. - - *이유*: Sinon은 Jasmine과 Mocha 둘다와 잘 작동하고 스터빙과 스파잉 기능을 이용해 확장할 수 있습니다. - - *이유*: Sinon은 Jasmine과 Mocha 사이에서 왔다 갔다 하기 쉽게 해줍니다. 만약 둘다 시도해보고 싶은 경우에 말이죠. - - *이유*: 만약 테스트가 실패하면 Sinon은 설명적인 메시지를 보여줍니다. - -### Headless Browser -###### [Style [Y194](#style-y194)] - - - 테스트를 서버에서 실행하고 싶다면 [PhantomJS](http://phantomjs.org/)를 이용하세요. - - *이유*: PhantomJS는 "시각적" 브라우저를 필요로 하지 않고 테스트를 브라우저에서 실행할 수 있도록 도와줍니다. 크롬이나 사파리 IE 또는 어떠한 브라우저도 설치할 필요가 없습니다. - - 주의: 대상 고객을 위해서 자신의 설치환경에서 모든 브라우저를 테스트해야 합니다. PhantomJS 테스트를 했다고 안심하면 안된다는 말입니다. - -### Code Analysis -###### [Style [Y195](#style-y195)] - - - 테스트에 JSHint를 사용하세요. - - *이유*: 테스트역시 코드입니다. JSHint는 테스트 코드의 질을 보증하고 테스트가 부정확하게 진행되는 것을 막아줍니다. - -### Alleviate Globals for JSHint Rules on Tests -###### [Style [Y196](#style-y196)] - - - `describe`와 `expect`같은 일반적인 글로벌 함수를 통과시키기 위해서 JSHint규칙을 완화하세요. Mocha 가 사용하는 표현식에 대한 규칙도 완화하세요. - - *이유*: 테스트 코드들도 실서버에 사용될 코드와 같은 주의와 질을 요구합니다. 하지만 테스트 코드에서 사용된 테스팅 프레임워크의 글로벌 변수같은 경우 아래 코드를 테스트 스팩에 포함하여 룰을 완화할 수 있습니다. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Or you can add the following to your JSHint Options file. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![테스팅 도구들](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - - -### Organizing Tests -###### [Style [Y197](#style-y197)] - - - 단위 테스트 파일들을 자신의 클라이언트 코드와 쌍으로 띄워두세요. 서버 통합을 확인하거나 여러 컴포넌트의 테스트 요건을 분리된 `tests` 폴더에 넣어두세요. - - *이유*: 단위 테스트는 소스 코드의 특정 컴포넌트와 파일들과 직접적인 상호작용을 합니다. - - *이유*: 항상 눈에 보여지게 됨으로 최신으로 유지하기가 쉽습ㄴ디ㅏ. TDD 또는 개발중 테스트 또는 개발 후 테스트 중 어떤 것을 사용하든 테스트 스팩은 나란히 보여지고 눈에서 멀어지기 어렵고 마음에서도 멀어지기 어렵습니다. 그러니 코드를 테스팅하는 코드도 유지보수하기 쉬워집니다. - - *이유*: 소스코드를 수정하게 될 때 테스트도 동시에 수정하기가 매우 쉽습니다. 한폴더에 있고 보여지니까요. - - *이유*: 나중에 소스코드를 옮기게 되어도 코드와 나란히 있기 때문에 함께 옮기기도 쉽고 편합니다. - - *이유*: 테스트 스팩을 곁에 두는 것은 코드를 파악하려는 사람에게 컴포넌트가 어떻게 동작하게 만들어졌는지 한계는 무엇인지 파악하기 쉽게 합니다. - - *이유*: Grunt나 gulp를 이용하면 배포 서버에 올려지는 코드에서 테스트 스팩코드를 분리하는게 어렵지 않습니다. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Back to top](#table-of-contents)** - -## Animations - -### Usage -###### [Style [Y210](#style-y210)] - - - 자연스러운 [Angular 애니매이션](https://docs.angularjs.org/guide/animations)을 사용하여 화면의 상태와 주요한 시각적 요소에 변화를 주세요. [ngAnimate 모듈](https://docs.angularjs.org/api/ngAnimate)를 포함하세요. 3 가지 중요 포인트는 자연스럽게, 부드럽게, 끊김없이 입니다. - - *이유*: 적합하게 사용된 자연스러운 애니매이션은 사용자 경험을 향상시킵니다. - - *이유*: 자연스러운 애니매이션은 티날정도로 화면의 변화를 향상시킵니다. - -### Sub Second -###### [Style [Y211](#style-y211)] - - - 애니매이션은 짧게 사용하세요. 저는 보통 300ms 로 시작해서 적당하다 싶게 조절합니다. - - *이유*: 긴 애니메이션은 유저 경험에 반대 영향을 줄 수 있고 늦게 반응하는 어플리케이션이라는 시각효과를 줌으로서 인지 성능에 안좋은 영향을 줍니다. - -### animate.css -###### [Style [Y212](#style-y212)] - - - 평범한 애니메이션을 위해서는 [animate.css](http://daneden.github.io/animate.css/)를 사용하세요. - - *이유*: animate.css이 제공하는 애니메이션들은 빠르고 부드럽고 쉽게 사용할 수 있습니다. - - *이유*: 애니매이션이 일관되게 사용되도록 해줍니다. - - *이유*: animate.css 널리 사용되고 있고 테스트 되었습니다. - - 주의: [Angular 애니메이션에 대한 좋은 포스팅 by Matias Niemelä](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) 여기를 참고하세요. - -**[Back to top](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-y220)] - - - 프로그램 문서를 만들고자 한다면, [`jsDoc`](http://usejsdoc.org/) 구문을 사용하여 함수이름 설명, 파라미터 리턴값 등을 명세하세요. `@namespace` 와 `@memberOf`를 사용하여 앱 구조와 맞추세요. - - *이유*: 문서를 처음부터 다 작성하지말고 코드로 부터 문서를 만들거나 재생성 할 수 있습니다. - - *이유*: 널리알려진 산업 툴을 사용함으로 일관성을 유지할수있습니다. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Back to top](#table-of-contents)** - -## JS Hint - -### Use an Options File -###### [Style [Y230](#style-y230)] - - - JS Hint를 사용하여 JavaScript 코드를 청소하세요. JS Hint 옵션 파일을 수정하고 소스 컨트롤에 추가하세요. 구체적인 옵션은 여기를 참고하세요 [JS Hint docs](http://www.jshint.com/docs/). - - *이유*: 소스 컨트롤에 코드를 전송하기 전에 경고를 받을 수 있습니다. - - *이유*: 팀 전체적으로 일관성을 유지할 수있습니다. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Back to top](#table-of-contents)** - -## JSCS - -### Use an Options File -###### [Style [Y235](#style-y235)] - - - JSCS를 사용하여 JavaScript의 코딩 스타일을 체크하세요. JSCS 옵션파일을 수정하고 소스 컨트롤에 함께 넣어두세요. 더 많은 옵션 정보를 위해서는 여기를 확인하세요 [JSCS docs](http://www.jscs.info). - - *이유*: 소스 컨트롤에 코드를 전송하기 전에 경고를 받을 수 있습니다. - - *이유*: 팀 전체적으로 일관성을 유지할 수있습니다. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "jsDoc": { - "checkAnnotations": true, - "checkParamNames": true, - "requireParamTypes": true, - "checkReturnTypes": true, - "checkTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Back to top](#table-of-contents)** - -## Constants - -### Vendor Globals -###### [Style [Y240](#style-y240)] - - - 외부 라이브러리의 글로벌 변수를 위해서 Angular 상수를 생성하세요. - - *이유*: 외부 라이브러리를 글로벌 변수에 만들지 않고 인젝트 하는 방법을 제공합니다. 이렇게 하면 의존성 컴포넌트알게 되기 때문에 코드의 테스트 용이성이 높아집니다. (누설된 추상화를 방지) 또한 필요한 부분에 의존 컴포넌트들을 흉내내기 쉽게 해줍니다. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - 변하지 않고 다른 서비스로부터 오지 않는 값들은 불변 상수를 이용하세요. 다양한 어플리케이션에서 재사용될 수 있는 모듈 내에서 사용되는 불변상 수들은 모듈의 이름을 딴 상수 파일을 만들어서 넣어두세요. 이 작업이 필요하기 전까지는 불변 상수는 메인 모듈의 `constants.js` 파일에 넣어두면 됩니다. - - *이유*: 자주 변하지 않더라도 변할 가능성이 있는 값들은 서비스로부터 받아서 사용해야 소스코드를 변경하지 않아도 되게 됩니다. 예를 들어 데이터를 받아오는 url 값은 상수로 저장해서 사용할 수도 있지만, 더 좋은 곳은 웹서비스로 부터 받아오는 것입니다. - - *이유*: 불변 상수는 브로바이더를 포함한 어떤 angular 컴포넌트에도 인젝트 될 수 있습니다. - - *이유*: 어떤 어플리케이션이 모듈화되어서 분리되어있다면 다른 어플리케이션에서 재사용 될 수 있습니다. 각각의 독립적인 모듈은 각각의 의존 상수를 불러옴으로 작동할 수 있어야 합니다. - - ```javascript - // Constants used by the entire app - angular - .module('app.core') - .constant('moment', moment); - - // Constants used only by the sales module - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. -파일 템플릿이나 스니펫을 사용하면 일관적인 스타일과 패턴을 지킬수 있습니다. 웹 개발용 에디터와 IDE들에서 사용 가능한 템플릿과 스니펫을 알려 드리겠습니다. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. - - - Download the [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) - - Place it in your Packages folder - - Restart Sublime - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Angular 파일 템플릿은 이 스타일과 가이드라인을 따릅니다 [SideWaffle](http://www.sidewaffle.com). - - - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) - - Run the vsix file - - Restart Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Angular 라이브 템플릿들은 이 스타일과 가이드라인을 따릅니다. - - - Download the [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) - - Place it in your [templates folder](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) - - Restart WebStorm - - In a JavaScript file type these commands followed by a `TAB`: - - ```javascript - // These are full file snippets containing an IIFE - ngapp // creates an Angular module setter - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngfilter // creates an Angular filter - ngservice // creates an Angular service - - // These are partial snippets intended to be chained - ngconfig // defines a configuration phase function - ngmodule // creates an Angular module getter - ngroute // defines an Angular ngRoute 'when' definition - ngrun // defines a run phase function - ngstate // creates an Angular UI Router state definition - ``` - - *각각의 템플릿 또한 여기서 다운받을 수 있습니다. [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true)* - -### Atom -###### [Style [Y253](#style-y253)] - - - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. - ``` - apm install angularjs-styleguide-snippets - ``` - or - - Open Atom, then open the Package Manager (Packages -> Settings View -> Install Packages/Themes) - - Search for the package 'angularjs-styleguide-snippets' - - Click 'Install' to install the package - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. - - - Download the [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) - - Brackets Extension manager ( File > Extension manager ) - - Install ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Click the light bulb in brackets' right gutter - - Click `Settings` and then `Import` - - Choose the file and select to skip or override - - Click `Start Import` - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngapp // creates an Angular module setter - ngservice // creates an Angular service - ngfilter // creates an Angular filter - - // These are partial snippets intended to chained - ngmodule // creates an Angular module getter - ngstate // creates an Angular UI Router state definition - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngwhen // defines an Angular ngRoute 'when' definition - ngtranslate // uses $translate service with its promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. - - - Download the [vim Angular snippets](assets/vim-angular-snippets?raw=true) - - set [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - copy snippets to snippet directory - - - vim UltiSnips snippets that follow these styles and guidelines. - - - Download the [vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) - - set [UltiSnips](https://github.com/SirVer/ultisnips) - - copy snippets to UltiSnips directory - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio Code - -###### [Style [Y256](#style-y256)] - - - [Visual Studio Code](http://code.visualstudio.com) 스니팻은 이 스타일과 가이드라인을 따릅니다. - - - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) - - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ``` - -**[Back to top](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -[HotTowel yeoman generator](http://jpapa.me/yohottowel)를 이용하여 앱을 만들면 이 스타일 가이드를 따르는 앱의 시작 부분을 생성하여 줍니다. - -1. Install generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Create a new folder and change directory to it - - ``` - mkdir myapp - cd myapp - ``` - -3. Run the generator - - ``` - yo hottowel helloWorld - ``` - -**[Back to top](#table-of-contents)** - -## Routing -클라이언트-사이드 라우팅은 화면과 작은 템플릿과 디렉티브들로 구성된 합성 뷰 사이의 네비게이션 흐름을 생성하는데 중요합니다. - -###### [Style [Y270](#style-y270)] - - - 클라이언트-사이드 라우팅을 위해서는 [AngularUI 라우터](http://angular-ui.github.io/ui-router/)를 사용하세요. - - *이유*: UI 라우터는 Angular 라우터의 모든 기능을 제공하고 추가적으로 내포 라우터, 상태를 제공합니다. - - *이유*: 구문법은 Angular 라우터와 매우 흡사하고 UI Router로 갈아타기 쉽게 되어있습니다. - - - 주의: `routerHelperProvider` 와 같은 프로바이더를 사용하여 진행 단계간에 파일들의 전체적인 설정을 도울 수 있습니다. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - 뷰를 위한 라우팅 값을 모듈 안에 넣어두세요. 각각의 모듈은 뷰를 위한 라우팅 정보를 모듈 안에 두어야 합니다. - - *이유*: 각각의 모듈은 단독적으로 실행될 수 있어야 합니다. - - *이유*: 모듈을 제거하거나 추가할 때, 앱은 존재하는 뷰에 대해서만 라우팅 포인트를 가지고 있어야 합니다. - - *이유*: 홀로 남겨진 라우트가 생길 염려없이 어플리케이션의 부분을 활성화 비활성화 하기 쉽도록 해줍니다. - -**[Back to top](#table-of-contents)** - -## Task Automation -[Gulp](http://gulpjs.com) 또는 [Grunt](http://gruntjs.com)를 사용하여 자동화 처리를 사용하세요. Gult는 설정보다는 코드 자체에 무게를 더 주는 반면 Grunt는 설정을 더 중요하게 생각합니다. 개인적으로는 읽고 작성하기가 쉬워서 Gulp를 선호합니다. 하지만 둘다 정말 멋집니다. - -> 여기를 참고하여 gulp 자동화 업무 패턴을 배우세요 [Gulp Pluralsight course](http://jpapa.me/gulpps). - -###### [Style [Y400](#style-y400)] - - - 업무 자동화를 이용하여 `*.module.js` 패턴의 모듈 정의 파일을 리스팅 하세요. - - *이유*: Angular는 모듈이 등록되어 사용되기 전에 모듈 정의가 필요합니다. - - *이유*: `*.module.js`와 같은 패턴을 이용하여 모듈 이름을 지어놨기 때문에 찾아내기 쉽습니다. - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Back to top](#table-of-contents)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - 복잡한 오브젝트 그래프의 모든 프로퍼티를 스캔하기 위한 필터 사용을 자제하세요. 프로퍼티를 선택하기 위해 필터를 사용하세요. - - *이유*: 현명하게 사용되지 못한 필터는 남용될 수 있고 성능에 부정적 영향을 줄 수 있습니다. 예를 들어 크고 깊은 오브젝트 그래프를 필터로 걸르면 안 됩니다. - -**[Back to top](#table-of-contents)** - -## Angular docs -나머지 부분, API 참고는 [Angular 문서](//docs.angularjs.org/api)여기로 가시면 됩니다. - -## Contributing - -수정과 추가를 위해서는 이슈를 먼저 발행하시기 바랍니다. 이 가이드에 질문이 있으면 리파지토리에 이슈를 남겨주세요. 오타를 발견하면 pull request를 만들어주세요. 이렇게 하는 이유는 github의 기능을 최대한 사용해서 이슈와 PR이 어떻게 이루어 졌는지를 알려주기 위함입니다. 이런 정보는 구글로 검색도 가능합니다. 왜냐구요? 이상하게도 당신이 질문이 있다면 다른사람들도 그런 질문을 가지기 때문입니다. 여기서 어떻게 기여할 수 있는지 배울 수 있습니다. - -*이 저장소에 기여함으로서 당신의 콘텐츠가 이 저장소의 라이센트의 대상이 됨을 동의합니다.* - -### Process - 1. Discuss the changes in a GitHub issue. - 2. Open a Pull Request, reference the issue, and explain the change and why it adds value. - 3. The Pull Request will be evaluated and either merged or declined. - -## License - -_tldr; Use this guide. Attributions are appreciated._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Back to top](#table-of-contents)** diff --git a/a1/i18n/mk-MK.md b/a1/i18n/mk-MK.md deleted file mode 100644 index 3ad49ea8..00000000 --- a/a1/i18n/mk-MK.md +++ /dev/null @@ -1,2929 +0,0 @@ -# Angular водич на кодирање - -*Своеволен Angular водич на кодирање за тимови од [@john_papa](//twitter.com/john_papa)* - -Доколку барате своеволен стил на кодирање за синтакса, конвенции и структурирање на Angular апликации, тогаш сте на правилното место. Овие стилови се базирани на моето искуство во развој на [Angular](//angular.org), презентации, [Pluralsight тренинг курсеви](http://pluralsight.com/training/Authors/Details/john-papa) и работа во тимови. - -Целта на овој водич на кодирање е да овозможи насока во развој на Angular апликации преку конвенциите што јас ги користам, и уште поважно, зошто ги користам. - ->Ако ви се допаѓа овој водич, тогаш проверете ми го курсот [Angular Patterns: Clean Code](http://jpapa.me/ngclean) на Pluralsight кој е придружник на овој водич. - - [![Angular Шаблон: Чист Код](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Величествена заедница и заслуга -Никогаш не работи во вакуум. Јас сметам дека Angular заедницата е неверојатна група кои се страсни за споделување искуство. Како резултат, јас и мојот пријател кој е Angular експерт, Todd Motto соработувавме со многу стилови и конвенции. Се согласуваме на повеќето, додека на останатите се разликуваме. Ве охрабрувам да ги погледнете на [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) со цел да добиете осет за неговиот пристап и како се споредува. - -Многу од моите водичи произлегоа од многу сесии во програмирање во пар со [Ward Bell](http://twitter.com/wardbell). Mојот пријател Ward секако влијаеше во последната еволуција на овој водич. - -## Погледнете ги водичите во пробната апликација -Иако овој водич ги објаснува "што", "зошто" и "како", јас сметам дека е полезно да ги запазиме во практика. Овој водич е придружен од пробна апликација која ги следи овие стилови и модели. Можете да ја најдете [пробната апликација (наречена modular) тука](https://github.com/johnpapa/ng-demos) во папката 'modular'. Не се колебајте да ја земете, да ја клонирате и форкувате. [Инструкции за да ја започнете се во своете readme](https://github.com/johnpapa/ng-demos/tree/master/modular) - -##Преводи -[Преводи од овој Angular водич на кодирање](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) се одржувани од заедницата и можете да ги најдете тука. - -## Table of contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Правило од 1 -###### [Style [Y001](#style-y001)] - - - Дефинирај 1 компонента во датотека. - - Следниот пример го дефинира `app` модулот и неговите зависности, дефинира контролер, и дефинира фабрика, сé во една датотека - - ```javascript - /* избегнувајте */ - angular - .module('app', ['ngRoute']) - .controller('someController', someController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Истите компоненти сега се разделени во свои датотеки. - - ```javascript - /* препорачано */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* препорачано */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* препорачано */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Врати се на содржина](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - Вгнездете ги Angular компоненти во Immediately Invoked Function Expression (IIFE). - - *Зошто?*: IIFE не ги покажува променливите на глобално ниво. Ова помага при спречување да променливите и декларациите на функциите да живеат подолго од очекуваното на глобалното ниво, што исто така помага во избегнување на судири на променливи со исто име. - - *Зошто?*: Кога вашиот код е минифициран и поставен во една датотека за да биде ажуриран на продукцискиот сервер тогаш може да се појават судири на локални и глобални променливи. IIFE ве заштитува од двата случаи овозможувајќи ниво на променливи во една датотека. - - ```javascript - /* избегнувајте */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger функцијата е додадена како глобална променлива - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage функцијата е додадена како глобална променлива - function storage() { } - ``` - - - ```javascript - /** - * препорачано - * - * нема заборавени глобални променливи - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Забелешка: Со цел да се скрати кодот, остатокот од примерите во овој водич нема да го прикажат IIFE - - - Забелешка: IIFE's го спречува кодот за тестирање од пристап до приватните членови како регуларни изрази или помошни функции кои потребни се да бидат директно тестирани во изолација. Сепак можете да ги тестирате овие преку членови кои можат да се пристапат или да ги изложите преку своја компонента. На пример, со поставување тие помошни функции, регуларни изрази или константи во своја фабрика или константа. - -**[Назад кон содржината](#table-of-contents)** - -## Modules - -### Избегнувајте судири во именување -###### [Style [Y020](#style-Y020)] - - Користите уникатни шаблони на именување со разделувачи за под-модулите. - - *Зошто?*: Со уникатни имиња избегнувате судири на модули со исто име. Разделувачи ги одредуваат модулите и нивната хиерархија на подмодули. На пример `app` е главниот модул додека `app.dashboard` и `app.users` се модули кои можно е да се зависни од `app`. - -### Дефинирање (познати како Setters) -###### [Style [Y021](#style-Y021)] - - Декларирај модули без променлива користејќи ја setter синтаксата. - - *Зошто?*: Со 1 компонента во датотека, реткост е да поставиш нова променлива во модулот. - - ```javascript - /* избегнувајте*/ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Наместо тоа, употреби ја едноставната setter синтакса. - - ```javascript - /* препорачано */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-Y022)] - - Кога употребувате модул, избегнувајте користење на променливa. Наместо тоа употребете врзување со getter синтакса. - - *Зошто?* : Ова создава читлив код и избегнува судири на променливи или протекувања. - - ```javascript - /* избегнувајте */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* препорачано */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-Y023)] - - Само еднаш создади и земај го за сите други инстанци. - - *Зошто?*: Модул треба еднаш да биде создаден, и од тој момент да биде само превземен. - - - Употреби `angular.module('app', []);` за да создадеш модул. - - Употреби `angular.module('app');` за да го превземеш тој модул. - -### Именувани vs Анонимни функции -###### [Style [Y024](#style-Y024)] - - Употреби именувани функции наместо да проследиш анонимни функции како повратни повици (callback). - - *Зошто?*: Ова создава читлив код, кој е полесен за дебагирање и го намалува бројот на вгнездени повратни повици. - - ```javascript - /* избегнувајте */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* препорачано */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Controllers - -### controllerAs синтакса во Изглед -###### [Style [Y030](#style-Y030)] - - Употреби ја [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) синтаксата наместо `класичниот контролер со $scope` синтакса. - - *Зошто?*: Контролерите се создаваат, се ажурираат и потоа се пристапуваат преку единствена нова инстанца. `controllerAs` синтаксата е поблиску до JavaScript конструктор наспроти `класичната $scope синтакса`. - - *Зошто?*: Го промовира користењето на "dotted" објект во Прегледот (на пример `customer.name` наместо `name`), што е поконтекстуално, полесно за читање и избегнува проблеми со референцирање кои може да се појават без "dotting". - - *Зошто?*: Помага при избегнување на `$parent` повици во Прегледи со вгнездени контролери. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs синтакса во контролери -###### [Style [Y031](#style-Y031)] - - Употреби `controllerAs` наместо `класичен контролер со $scope` синтакса. - - - `controllerAs` синтакса користи `this` во контролерите што се поврзува со `$scope` - - *Зошто?*: `controllerAs` е синтаксички поубав од `$scope`. Вие сеуште може да се поврзете со Прегледот и да ги пристапите `$scope` методите. - - *Зошто?*: Ви помага да избегнете употреба на `$scope` методи во контролерот кога е подобро да ги избегнете или преместите во фабрика. `$scope` може да употребите во фабрика или во контролер само кога ви е потребен. На пример, кога објавувате/пријавувате настани со употреба на [`$emit`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$broadcast), или [`$on`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$on) подобро е да ги преместите во фабрика и да ја повикате неа во контролерот. - - ```javascript - /* избегнувајте */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* препорачано - но провери ја следната секција */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs со vm -###### [Style [Y032](#style-Y032)] - - Употребете нова променлива за `this` кога употребувате `controllerAs` синтакса. Одлучете се за постојано име на променлива како на пример `vm`, што е кратеница за ViewModel. - - *Зошто?*: `this` зборот е контекстуален и може да ја промени својата состојба кога се употребува во функција која е во контролерот. Со употреба на нова променлива за `this`, се избегнува овој проблем. - - ```javascript - /* избегнувајте */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* препорачано */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - Забелешка: Можете да ги избегнете предупредувањата од [jshint](http://www.jshint.com/) со поставување коментар над таа линија код. Но, немате потреба доколку функцијата е именувана со ГолемиБукви, што значи дека е конструктор функција т.е. контролер во Ангулар. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Забелешка: Кога создавате watches во контролерот кога ја користите `controller as` синтаксата, тогаш можете да го надгледувате `vm.*` членот со следната синтакса. (Создавање watches претпазливо бидејќи можат да додадат товар на digest циклусот.) - - ```javascript - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - ``` - -### Поврзување на членовите на почеток -###### [Style [Y033](#style-Y033)] - - Постави ги членовите кои ќе се поврзат според алфабетски редослед. Наместо да бидат поставени низ кодот во контролерот. - - *Зошто?*: Поставување на поврзувањата на почетокот го прави кодот полесен за читање и помага за да го најдеш кој член од контролерот е поврзан и употребуван во Прегледот. - - *Зошто?*: Поставување на анонимни функции во ист ред е лесно, но доколку тие функции се подолги од 1 линија код тогаш може да ја намалат читливоста. Со дефинирање на функциите под поврзаните членови (функциите ќе бидат земени) горе и нивните имплементациите доле го прави кодот полесен за читање. - - ```javascript - /* избегнувајте*/ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* препорачано */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Забелешка: Ако функцијата е 1 линија код, можете да ја поставите горе се додека читливоста не се наруши. - - ```javascript - /* избегнувајте */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * линии - * на - * код - * нарушуваат - * читливост - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* препорачано */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 линија е ОК - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Декларација на функции за да ја скрие имплементацијата -###### [Style [Y034](#style-Y034)] - - Употребу декларации на функции за да ги скриеш нивните имплементации. Нека членовите за поврзување останат горе. Кога треба да поврзеш функција во контролерот, посочи ја кон декларацијата на таа функција која се појавува подоле во датотеката. Ова е директно поврзано со секцијата Поврзување на членови на почеток. За повеќе детали, проверете го [овој пост](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Зошто?*: Поставување членови за поврзување на почеток го прави кодот полесен за читање и лесно да забележиме кој член од контролерот може да биде повзан и искористен во Прегледот. (Исто како погоре.) - - *Зошто?*: Поставување на имплементацијата на функцијата подоле во датотеката ја поместува комплексноста надвор од преглед со цел да ги забележиме поважните работи на почетокот. - - *Зошто?*: Декларации на функции се превземени со цел да не се грижиме дали функцијата е декларирана пред да е дефинирана. (како што е со изрази на функции). - - *Зошто?*: Нема потреба да се грижиме дали нашиот код ќе се скрши доколку ги приместиме `var a` пред `var b` доколку `a` зависи од `b`. - - *Зошто?*: Редоследот е значаен со функциски изрази. - - ```javascript - /** - * избегнувајте - * употреба на функциски изрази - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Забележете дека значајните работи се распрснати во претходниот пример. Во следниот пример, забележете дека важните работи се на почетокот. На пример, членовите во контролерот како `vm.avengers` и `vm.title`. Имплементацијата е подоле. Ова е полесно за читање. - - ```javascript - /* - * препорачано - * употреба на декларација на функции - * и членови на поврзување на почеток. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Одлагање Логика во Контролерот до Сервисите -###### [Style [Y035](#style-Y035)] - - Одлагајте логика на во контролерот со делегирање до сервиси и фабрики. - - *Зошто?*: Логиката може да биде повторно искористена кога е во сервис, од повеќе контролери и изложена преку функција. - - *Зошто?*: Логиката во сервис е полесно изолирана во тестирањето, додека повикаување на истата во тој контролер лесно може да се лажира. - - *Зошто?*: Се ослободува од зависностите и ја крие имплементацијата од контролерот. - - ```javascript - /* избегнувајте */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Земете го URL-то на сервисот за кредит од config датотеката - // Поставете ги потребните заглавија - // Спремете го потребното URL барање или потребниот податочен објект за побарување на податоците - // Додадете инфо за идентификација на корисникот за сервисот да добие точниот лимит на кредитот за овој корисник - // Употребете JSONP за овој пребарувач доколку не подржува CORS - return $http.get(settings) - .then(function(data) { - // Отпакувајте го JSON податокот во одговорот - // за да го најдете maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Интерпретирајте ја грешката - // Спремете се со истекување на време? пробување пак? друг сервис? - // Повторно одбиете за корисникот да ја забележи соодветната порака - }); - }; - } - ``` - - ```javascript - /* препорачано */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - }; - } - ``` - -### Контролерите треба да бидат фокусирани -###### [Style [Y037](#style-Y037)] - - Дефинирајте контролер за преглед, и пробајте да не го искористите истиот за други прегледи. Наместо тоа, преместете ја повторно употребливата логика во фабрики и поставете го тој контролер фокусиран за својот преглед. - - *Why?*: Повторно искористување на контролери со повеќе прегледи е лесно кршливо и тешко за одржување стабилност низ повеќе апликации во е2е тестирање. - -### Назначување Контролери -###### [Style [Y038](#style-Y038)] - - Кога контролерот е во пар со прегледот и кога било кој од нив треба да биде повторно искористен од други контролери или прегледи тогаш дефинирај ги контролерите со нивните рута. - - Забелешка: Доколку Прегледот е вчитан преку други начини наместо рути, тогаш искористете ја `ng-controller="Avengers as vm"` синтаксата. - - *Зошто?*: Преку поставување на контролерот во пар во рутата се овозможува други рути да започнат други парови од контролери и прегледи. Кога контролерите се назначени со прегледот со [`ng-controller`](https://docs.angular.org/api/ng/directive/ngController), тогаш тој преглед е секогаш поврзан со истиот контролер. - - ```javascript - /* избегнувајте - кога потребна е употреба на рути и динамички пар*/ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* препорачано */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Назад кон содржината](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-Y040)] - - Сервиси се инстанцирани со `new` зборот, и се употребуваат со `this` за јавни методи и променливи. Бидејќи се слични со фабрики, користете фабрика за конзистентност. - - Забелешка: [Сите Angular сервиси се singletons](https://docs.angular.org/guide/services). Тоа значи дека има само една инстанца од сервис за injector. - - ```javascript - // сервис - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // фабрика - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Factories - -### Единствена одговорност -###### [Style [Y050](#style-Y050)] - - Фабрики треба да имаат [единствена одговорност](http://en.wikipedia.org/wiki/Single_responsibility_principle), што е обопштена од својот контекст. Чим фабриката ја надмине единствената цел, тогаш нова фабрика треба да биде создадена. - -### Singletons -###### [Style [Y051](#style-Y051)] - - Фабрики се singletons и враќаат објект што ги содржини членовите од тој сервис. - - Забелешка: [Сите Angular сервиси се singletons](https://docs.angular.org/guide/services). - -### Членовите за пристап на почеток -###### [Style [Y052](#style-Y052)] - - Изложи ги членовите на сервисот кои треба да се повикаат (неговиот интерфејс) на почетокот, користејќи ја техниката [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Зошто?*: Поставување на повикувачките членови на почетокот го прави кодот полесен за читање и помага при лесно забележување на кои членови можат да се повикаат и мораат да бидат тестирани (и/или излажани при тестирање). - - *Зошто?*: Ова е посебно значајно кога датотеката станува подолга и ја намалува потребата од scrolling за да забележиме што е изложено. - - *Зошто?*: Поставување функции во редослед е лесно, но кога тие се повеќе од 1 линија код тогаш може да ја нарушат читливоста и создадат повеќе scrolling. Дефинирање на повикувачкиот интерфејс со враќање на сервисот ја сокрива имплементацијата, го поставува интерфејсот на почеток што го прави кодот лесен за читање. - - ```javascript - /* избегнувајте */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* препорачано */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - На овој начин поврзувањата се пресликуваат низ објектот, примитивните вредности не можат да се ажурираат самостојно со употреба на Revealing шаблонот на модули. - - ![Фабрики искористуваат "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Декларации на функции ја кријат имплементацијата -###### [Style [Y053](#style-Y053)] - - Употребете декларации на функции за да ја сокриете имплементацијата. Нека пристапните членови на фабриката бидат поставени на почеток. Додека имплементацијата на декларациите на функциите биде поставена подоле во датотеката. За повеќе детали, посочете со до [овој пост](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Зошто?*: Поставување на пристапните членови на почеток е лесно за читање и помага при лесно забележување на кои функции на фабриката можат да се пристапат од надвор. - - *Зошто?*: Поставување на имплементацијата подоле во датотеката ја крие комплексноста со цел да може полесно да се забележат најважните работи на почетокот. - - *Зошто?*: Декларации на функции се поврзани така што нема потреба од грижа доколку се користи функцијата пред да биде дефинирана (како што е примерот со функциски изрази). - - *Зошто?*: Никогаш нема да има потреба од грижа со декларации на функции и дали поместување на `var a` пред `var b` ќе го скрши кодот доколку `a` зависи од `b`. - - *Зошто?*: Редоследот е значаен за функциски изрази. - - ```javascript - /** - * избегнувајте - * употреба на функциски изрази - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // имплементација - }; - - var getAvengerCount = function() { - // имплементација - }; - - var getAvengersCast = function() { - // имплементација - }; - - var prime = function() { - // имплементација - }; - - var ready = function(nextPromises) { - // имплементација - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * препорачано - * употреба на функциски декларации - * и пристапни членови на почеток. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // имплементација - } - - function getAvengerCount() { - // имплементација - } - - function getAvengersCast() { - // имплементација - } - - function prime() { - // имплементација - } - - function ready(nextPromises) { - // имплементација - } - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Data Services - -### Одделени податочни повици -###### [Style [Y060](#style-Y060)] - - Рефакторирај ја логиката со податочни операции и интеракција на податоците во фабриката. Податочни сервиси се одговорни за XHR повици, локално складирање, поставување во меморија и останати податочни операции. - - *Зошто?*: Одговорноста на контролерот е во презентацијата и собирање информации за прегледот. Нема потреба да се засега како ги добива податоците, само да знае кого да праша за нив. Одделување на податочните сервиси ја преместува логиката за пристап на податоците на податочниот сервис, додека контролерот е поедноставен и фокусиран на прегледот. - - *Зошто?*: Полесно тестирање (вистинско или лажно) на податочни повици кога се тестира контролер кој употребува податочен сервис. - - *Зошто?*: Имплементацијата на податочен сервис може да биде конкретен на справување со податочното складиште. Ова може да вклучува заглавија, како да зборува со податоците, или други сервиси како $http. Одделување на логиката во податочен сервис ја енкапсулира оваа логика во единечно место, криејќи ја имплементацијата од надворешните потрошувачи (на пример, контролер) и со тоа се поедноставува промената на имплементацијата. - - ```javascript - /* препорачано */ - - // фабрика на податочен сервис - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Забелешка: Податочниот сервис е повикан од потрошувачите како контролери, криејќи ја имплементацијата од нив, како што е покажано подоле. - - ```javascript - /* препорачано */ - - // контролер ја повикува фабриката за податочниот сервис - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Вратете Promise од податочни повици -###### [Style [Y061](#style-Y061)] - - Кога повикувате податочен сервис кој враќа promise како што е `$http`, вратете promise во вашата повикувачка функција. - - *Зошто?*: Можете да врзете повеќе promise заедно со повеќе акции над податоците откако податочниот повик заврши и го прифати или одбие promise-от. - - ```javascript - /* препорачано */ - - activate(); - - function activate() { - /** - * Чекор 1 - * Прашај ја getAvengers функцијата за - * avenger податокот и чекај на promise - */ - return getAvengers().then(function() { - /** - * Чекор 4 - * Извршете акција на последниот promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Чекор 2 - * Повикај го податочниот сервис за податоците и чекан - * на promise-от - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Чекор 3 - * постави го податокот и реши го promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - **[Назад кон содржината](#table-of-contents)** - -## Directives -### Ограничувајте се на 1 на датотека -###### [Style [Y070](#style-Y070)] - - Создадете една директива по датотека. Именувајте ја според директивата. - - *Зошто?*: Полесно е да ги споите повеќе директиви во една датотека, но потешко да ги разделите тие со цел да бидат споделени низ апликации, модули или само еден модул. - - *Зошто?*: Една директива по датотека е полесна за одржување. - - ```javascript - /* избегнувајте */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order директива која е специфична за order модулот */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales директива која може да се користи низ sales апликацијата */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner директива која може да се користи низ повеќе апликации */ - .directive('sharedSpinner', sharedSpinner); - - - function orderCalendarRange() { - /* имплементација */ - } - - function salesCustomerInfo() { - /* имплементација */ - } - - function sharedSpinner() { - /* имплементација */ - } - ``` - - ```javascript - /* препорачано */ - /* calendarRange.directive.js */ - - /** - * @desc order директива која е специфична за модулот во компанијата Acme - * @пример
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* имплементација */ - } - ``` - - ```javascript - /* препорачано */ - /* customerInfo.directive.js */ - - /** - * @desc sales директива која може да се користи низ апликацијата во компанијата Acme - * @пример
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* препорачано */ - /* spinner.directive.js */ - - /** - * @desc spinner директива која може да се користи низ повеќе апликации во компанијата Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* имплементација */ - } - ``` - - Забелешка: Постојат повеќе начини на именување на директиви, посебно што можат да се искористат во широк или краток обем. Одлучете се за еден кој ја прави директивата и нејзиното име јасно и различно. Има неколку примери подоле, но посоветувајте се до секцијата за [именување](#naming). - -### Манипулирајте DOM во директивата -###### [Style [Y072](#style-Y072)] - - Кога манипулирате директно со DOM, употребете директива. Ако можат да се употребат други начини, како CSS за стилови или [анимациски сервиси](https://docs.angular.org/api/ngAnimate), Angular темплејти, [`ngShow`](https://docs.angular.org/api/ng/directive/ngShow) или [`ngHide`](https://docs.angular.org/api/ng/directive/ngHide), тогаш употребете ги тие. На пример, ако директивата само се појавува/исчезнува, тогаш употребете ngHide/ngShow. - - *Зошто?*: Манипулација на DOM е тешка да се тестира, дебагира и притоа постојат подобри начини. (на пример CSS, анимации, темплејти) - -### Обезбедете уникатен префикс на директивата -###### [Style [Y073](#style-Y073)] - - Обезбедете краток, уникатен и описен префикс на директивата како `acmeSalesCustomerInfo` што е декларирана во HTML како `acme-sales-customer-info`. - - *Зошто?*: Уникатниот краток префикс ја идентификува смислата на директивата и нејзиното потекло. На пример, префиксот `cc-` може да укажува дека директивата дел од CodeCamper апликацијата додека `acme-` може да укажува дека директивата е за компанијата Acme. - - Забелешка: Избегнувајте `ng-` бидејќи тие се резервирани за директивите на Angular. Проучете најчесто употребувани директиви со цел да избегнувате судири со имињата, како `ion-` за [Ionic Framework](http://ionicframework.com/). - -### Ограничете се на Елементи и Атрибути -###### [Style [Y074](#style-Y074)] - - Кога создавате директива која има смисла како единечен елемент, тогаш ограничете ја на `E` (сопствен елемент) и како опција `A` (сопствен атрибут). Обично, ако треба да биде сопствена контролна единка, тогаш `A` е препорачливо. Најчести водичи дозволуваат `EA`, но насочете се кон имплементација како елемент кога е самостоен и како атрибут доколку го подобрува постоечкиот DOM елемент. - - *Зошто?*: Има смисла. - - *Зошто?*: Иако овозможуваме директивите да се користат како класи, доколку навистина директивата се употребува како елемент тогаш има повеќе смисла да се користи како елемент или во најмал случај, како атрибут. - - Забелешка: EA е стандардно за Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* избегнувајте */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* препорачано */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Директиви и ControllerAs -###### [Style [Y075](#style-Y075)] - - Употребете `controller as` синтакса со директива доколку сакате да бидете во согласност со `controller as` на прегледот и неговиот контролер. - - *Зошто?*: Има смисла, и не е тешко.. - - Забелешка: Директивата подоле прикажува некои од начините кои можете да употребите scope во link функцијата и контролерите на директивата. Јас го внесов темплејтот со цел да го прикажам примерот. - - Забелешка: Што се однесува до dependency injection, проверете [Рачна Идентификација на Зависности](#manual-annotating-for-dependency-injection). - - Забелешка: Контролерот на директивата е надвор од директивата. Овој стил ги оневозможува грешките каде кодот за вметнување на зависностите не се извршува поради поставеност после `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller : ExampleController, - controllerAs: 'vm' - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Внесување $scope за споредба - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Забелешка: Можете исто така да го именувате Контролерот кога ќе го вметнете во link функција и пристапите до атрибутите на директивата како својства на контролерот. - - ```javascript - // Алтернатива за примерот горе - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - - -###### [Style [Y076](#style-y076)] - - - Употребете `bindToController = true` кога употребувате `controller as` синтакса со директива чиј scope на контролерот ќе биде поврзан со надворешниот scope. - - *Зошто?*: Полесно е да се поврзе надворешниот scope со тој на контролерот во директивата. - - Забелешка: `bindToController` беше воведен во Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Назад кон содржината](#table-of-contents)** - -## Resolving Promises for a Controller - -### Активација на Promises во контролерот -###### [Style [Y080](#style-Y080)] - - Решете се со логиката за започнување на контролерот во `activate` функцијата. - - *Зошто?*: Поставување на почетна логика во согласно место во контролерот е полесно за лоцирање, полесно за тестирање и го оневозможува распределувањето на почетната логика низ целиот контролер. - - *Зошто?*: Функцијата `activate` во контролерот е погодна за повторно да се искористи логиката за освежување на контролер/Преглед, ја држи логиката на едно место, побрзо го прикажува Прегледот, ги олеснува анимациите на `ng-view` или `ui-view` и е елегантно за корисникот. - - Забелешка: Доколку условно требате да го прекинете решавањето на патеката пред да го користите контролерот, тогаш разгледајте го [route resolve](#style-y081). - - ```javascript - /* избегнувајте */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* препорачано */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Решавање на патеката преку Promises -###### [Style [Y081](#style-Y081)] - - Кога контролер зависи на решавање на promise, решете ги сите зависности во `$routeProvider` пред контролерот да биде активиран. Ако треба опционално да ја спречите патеката пред да биде контролерот активиран, решете ја патеката. - - - Употребете решавање на патеката кога сакате да ја прекинете патеката пред да преминете на Прегледот. - - *Зошто?*: Контролерот може да зависи од податоци пред да се изврши. Овој податок може да дојде преку promise од сопствена фабрика или [$http](https://docs.angular.org/api/ng/service/$http). Со употреба на [route resolve](https://docs.angular.org/api/ngRoute/provider/$routeProvider) ќе овозможиме promise да се реши пред логиката на контролерот да биде извршена, така што може да зависи од акција во податокот. - - *Зошто?*: Кодот се извршува после патеката и во activate функцијата во контролерот. Прегледот започнува одма да се вчитува. Поврзување со податоците се вклучува кога ќе се реши promise-от во activate функцијата. "Зафатена" анимација ќе се појави додека преминувате на Прегледот. (со `ng-view` или `ui-view`) - - Забелешка: Кодот се извршува пред патеката со promise. Со одбивање на promise се спречува патеката. Со решавање, се чека на новиот преглед да заврши. "Зафатена" анимација може да се прикаже пред да се реши патеката и во преминот на Прегледот. Доколку сакате побрзо да стигнете до Прегледот, а не ви е потребен checkpoint доколку сакате да стигнете до Прегледот, тогаш разгледајте го [controller `activate` technique](#style-y080). - - ```javascript - /* избегнувајте */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* подобро */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Забелешка: Примерот подоле покажува како решавање на патеката покажува кон именувана функција, која е полесна за дебагирање и полесно справување со вклучување на зависностите. - - ```javascript - /* уште подобро */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviePrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Забелешка: Примерот на `movieService` не е безбеден за минификација. За детали како да го направите безбеден за минификација, прегледајте ги секциите за [dependency injection](#manual-annotating-for-dependency-injection) и [minification and annotation](#minification-and-annotation). - -**[Назад кон содржината](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### Опасно за минифкација -###### [Style [Y090](#style-Y090)] - - Избегнувајте употреба на кратенки за декларација на зависности без употреба на безбеден пристап за минификација. - - *Зошто?*: Параметрите на компонентата (e.g. контролер, фабрика, итн) ќе бидат претворени во исчезнати променливи. На пример, `common` и `dataservice` може да постанат `a` или `b` и да не бидат најдени од Angular. - - ```javascript - /* избегнувајте - опасно за минифкација*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Овој код може да произведе исчезнати променливи и кога ќе биде минифициран да создаде грешки при извршување. - - ```javascript - /* избегнувајте - опасно за минификација*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Рачна идентификација на зависности -###### [Style [Y091](#style-Y091)] - - Употребете `$inject` за рачна идентификација на вашиоте зависности во Angular компонентите. - - *Зошто?*: Оваа техника се користи во [`ng-annotate`](https://github.com/olov/ng-annotate), што ја препорачувам за автоматизација на создавање на безбедни зависности при минификација. Доколку `ng-annotate` забележи зависност доколку постои, нема да ја повтори. - - *Зошто?*: Ова ги заштитува вашите зависности од можноста да бидат изгубени при минификација. На пример, `common` и `dataservice` можат да постанат `a` or `b` и да не можат да бидат најдени од Angular. - - *Зошто?*: Избегнувајте вметнување зависности во иста линија се додека тешко се читаат во листата. Исто така може да биде збунувачко дека низата се повеќе зборови во иста линија додека крајниот член е функција. - - ```javascript - /* избегнувајте */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* избегнувајте */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* препорачано */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Забелешка: Кога вашата функција е под return линијата, тогаш `$inject` може да биде недостижна (ова е возможно да се случи во директива). Можете да го решите ова со преместување на Контролерот надвор од директивата. - - ```javascript - // избегнувајте - // во дефиницијата на директивата - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Недостижно - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* препорачано */ - // надвор од дефиницијата на директивата - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Рачна идентификација на зависностите преку решавање на патеки -###### [Style [Y092](#style-Y092)] - - Употребете `$inject` за рачна идентификација на зависностите преку решавање на патеки во Angular компоненти. - - *Зошто?*: Оваа техника ги разделува анонимните функции за решавање на патеката, кое е полесно за читање. - - *Зошто?*: `$inject` линијата може лесно да го претходи решавачот, подобрувајќи ја безбедноста при минификација. - - ```javascript - /* препорачано */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-Y100)] - - Употребете [ng-annotate](//github.com/olov/ng-annotate) за [Gulp](http://gulpjs.com) или [Grunt](http://gruntjs.com) и поставете го коментарот `/** @ngInject */` над функциите кои им се потребни автоматизиран dependency injection. - - *Зошто?*: Го заштитува вашиот код од загуба на зависности при минификација. - - *Зошто?*: Употреба на [`ng-min`](https://github.com/btford/ngmin) е застарено. - - >Јас преферирам Gulp бидејќи сметам е полесен за пишување, читање и дебагирање. - - Следниот код користи заштитни мерки при минификација на зависности. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Кога следниот код ќе помине низ ng-annotate, ќе го произведе следниот излез со `$inject` анотација и ќе постане безбеден при минификација. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Забелешка: Доколку `ng-annotate` забележи дека постои injection (на пример `@ngInject`), нема да ја повтори `$inject` линијата на кодот погоре. - - Забелешка: Кога употребувате решавање на патеки, може да поставите префикс на функцијата за решавање, на пример `/* @ngInject */`, и ќе произведе правилно анотиран код со безбедни зависности при минификација. - - ```javascript - // Со употреба на @ngInject анотација - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Забелешка: Со почеток на Angular 1.3 можете да го употребете параметарот `ngStrictDi` на [`ngApp`](https://docs.angular.org/api/ng/directive/ngApp) директивата да детектирате потенцијални недостатоци при минификација на зависностите. Доколку е достапен, injector-от ќе биде создаден во "strict-di" мод оневозможувајќи ја апликацијата да започне и да ги повика функциите кои не користат експлицитна анотација. (овие не се безбедни од минификација). Со логирање на информациите во конзола од дебагирање ќе ви помогне да ги најдете тие функции. Преферирам да го употребуваме `ng-strict-di` само за дебагирање. - `` - -### Употребете Gulp или Grunt за ng-annotate -###### [Style [Y101](#style-Y101)] - - Употребете [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) или [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) во автоматизиран build task. Внесете `/* @ngInject */` пред функцијата која има зависности. - - *Зошто?*: ng-annotate ќе ги фати повеќето зависности, но понекогаш потребни се навестувања во својата `/* @ngInject */` синтакса. - - Следниот код е припер за gulp задача која употребува ngAnnotate - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Анотација пред uglify за правилна минификација. - .pipe(ngAnnotate({ - // true помага за @ngInject да знае каде не се користи. - // Не работи со resolve, така да мора експлицитно да се постави - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Exception Handling - -### Декоратори -###### [Style [Y110](#style-Y110)] - - Употребете [decorator](https://docs.angular.org/api/auto/service/$provide#decorator), при конфигурација со употреба на [`$provide`](https://docs.angular.org/api/auto/service/$provide) сервис, на [`$exceptionHandler`](https://docs.angular.org/api/ng/service/$exceptionHandler) сервисот за да извршите лични акции кога ќе се случи исклучок. - - *Зошто?*: Овозможува постојан начин да се справи со исклучоци кои Angular не може да ги фати во development-time или run-time. - - Забелешка: Друга опција е да се прескокне сервисот наместо да се користи декоратор. Ова е добра опција, но доколку сакате да го задржите стандардното однесување и проширите, тогаш препорачливо е да користите декоратор. - - ```javascript - /* препорачано */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Може да ја додадете грешката на колекцијата на сервиси, - * додади грешки на $rootScope, логирај грешки на далечен веб сервер, - * или логирај локално. Или фрлете го. Од вас зависи. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Ловци на исклучоци -###### [Style [Y111](#style-Y111)] - - Создадете фабрика која изложува интерфејс за да улови и грациозно да се справи со исклучокот. - - *Зошто?*: Обезбедува самостоен начин да ги улови исклучоците кои може да бидат фрлени во вашиот код.(на пример при XHR повици или пад на Promise). - - Забелешка: Ловецот на исклучоци е добар за ловење и реагирање на специфични исклучоци од повици кои можат да фрлат таков исклучок. На пример, кога правите XHR повик за да ги примите податоците од далечен веб сервии и сакате да фатите било какви исклучоци од тој сервер и да реагирате различно за секој од нив. - - ```javascript - /* препорачано */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Грешки при рутирање -###### [Style [Y112](#style-Y112)] - - Справете се и логирајте сите рутирачки грешки со употреба на [`$routeChangeError`](https://docs.angular.org/api/ngRoute/service/$route#$routeChangeError). - - *Зошто?*: Обезбедува постојан начин да се справи со сите рутирачки грешки. - - *Зошто?*: Обезбедува подобро искуство за корисникот доколку се случи грешка при рутирање и вие ги пренесете на пријателска патека каде можат да забележат повеќе детали како да се вратат на минатата акција без грешка. - - ```javascript - /* препорачано */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Прекин на патека: - * Доколку настане грешка, оди на контролната страна. - * Обезбеди излез доколку не успее по втор пат. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - /** - * Опционално логирајте со рачно изработен сервис или $log. - * (Не заборавајте да го вклучите рачно изработениот сервис) - */ - logger.warning(msg, [current]); - - /** - * При грешка во рутирање, одете на друга рута/состојба. - */ - $location.path('/'); - } - ); - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Naming - -### Водич во именување -###### [Style [Y120](#style-Y120)] - - Употребете самостојни имиња за сите компоненти следејќи го шаблонот кој ја опишува функцијата и после (опционално) неговиот тип. Мојот препорачан шаблон е `feature.type.js`. Постојат две имиња за повеќето датотеки: - * името на датотеката (`avengers.controller.js`) - * регистрираната компонента во Angular (`AvengersController`) - - *Зошто?*: Конвенциите при именување им помага на тимот за конзистентен начин да ги најдат компонентите при прв поглед. Конзистентност во проект е значајно. Конзистентност во тимот е важно. Конзистентност низ компанијата обезбедува огромна ефикасност. - - *Зошто?*: Конвенцијата при именување треба едностанво да ви помогне побрзо да го најдете кодот и полесно да го разберете. - -### Имиња на датотеки по функција -###### [Style [Y121](#style-Y121)] - - Употребете конзистентни имиња за сите компоненти следејќи шаблон што ја опишува функцијата на компонентата и после (опционално) нејзиниот тип. Мојата препорака е `feature.type.js`. - - *Зошто?*: Обезбедува конзистентен начин за лесна идентификација на компоненти. - - *Зошто?*: Обезбедува шаблон за совпаѓање на автоматизирани задачи. - - ```javascript - /** - * чести опции - */ - - // Контролери - avengers.js - avengers.controller.js - avengersController.js - - // Сервиси/Фабрики - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * препорачано - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // Сервиси/Фабрики - logger.service.js - logger.service.spec.js - - // Константи - constants.js - - // Дефиниција на модули - avengers.module.js - - // Рути - avengers.routes.js - avengers.routes.spec.js - - // Конфигурација - avengers.config.js - - // Директиви - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Забелешка: Други чести конвенции е именување на контролерот датотеките без зборот `controller` во датотеката како `avengers.js` наместо `avengers.controller.js`. Сите други конвенции покажуваат употреба на суфикс на типот. Контролерите се најчестиот тип компонента така што заштедува на пишување и сеуште лесно препознатливо. Препорачувам да се одлучите со 1 конвенција и бидете конзистентен со тимот. Моја преференца е `avengers.controller.js`. - - ```javascript - /** - * препорачано - */ - // Контролери - avengers.js - avengers.spec.js - ``` - -### Имиња на датотеки за тестови -###### [Style [Y122](#style-Y122)] - - Имињата на тестовите се слични со компонентата што ја тестираат со додавање на суфикс `spec`. - - *Зошто?*: Обезбедува конзистентен начин за лесно препознавање компоненти. - - *Зошто?*: Обезбедува шаблонско совпаѓање за [karma](http://karma-runner.github.io/) или други тест фрејморкови. - - ```javascript - /** - * препорачано - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Имиња на контролер -###### [Style [Y123](#style-Y123)] - - Употребете конзистентни имиња за сите контролери именувани по нивните функции. Користете UpperCamelCase за контролери, бидејќи се контруктори. - - *Зошто?*: Обезбедува конзистентен начин за лесно препознавање и референцирање контролери. - - *Зошто?*: UpperCamelCase е конвенција за препознавање објекти кои можат да се инстанцираат со конструктор. - - ```javascript - /** - * препорачано - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController(){ } - ``` - -### Суфикс на името на контролерот -###### [Style [Y124](#style-Y124)] - - Додадете суфикс `Controller` на името на Контролерот. - - *Зошто?*: `Controller` суфиксот е почесто користен и експлицитно описен. - - ```javascript - /** - * препорачано - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -### Имиња на фабрики -###### [Style [Y125](#style-Y125)] - - Употребете конзистентни имиња за сите фабрики според нивната функција. Употребете camel-casing за сервиси и фабрики. Избегнувајте префикси на фабрики и сервиси со `$`. - - *Зошто?*: Обезбедува конзистентен начин за брзо препознавање и референцирање фабрики. - - *Зошто?*: Избегнува судири со имиња на вградени фабрики и сервиси кои го користат `$` префиксот. - - ```javascript - /** - * препорачано - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - -### Имиња на директиви -###### [Style [Y126](#style-Y126)] - - Употребете конзистентни имиња за сите директиви со употреба на camel-case. Употребете краток префикс за да опише во која зона припаѓа директивата. (неколку примери се компаниски префикси или проект префикс). - - *Зошто?*: Обезбедува конзистентен начин за брзо препознавање и референцирање на компоненти. - - ```javascript - /** - * препорачано - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile(){ } - ``` - -### Модули -###### [Style [Y127](#style-Y127)] - - Кога постојат повеќе модули, името на главниот модул е `app.module.js` додека другите зависни модули се именувани според тоа што претставуваат. На пример, админ модул е именувам `admin.module.js`. Соодветните регистрирани имиња на модули би биле `app` и `admin`. - - *Зошто?*: Обезбедува конзистентност за повеќе модуларни апликации како и проширување до огромни апликации. - - *Зошто?*: Обезбедува лесен начин да се автоматизираат задачите за создавање на дефинициите на модулите, па потоа сите останати angular датотеки (пакување на датотеки). - -### Конфигурација -###### [Style [Y128](#style-Y128)] - - Разделете ја конфигурацијата по модул во своја датотека, именувана по модулот. На пример, конфигурациската датотека за модулот `app` е именувана `app.config.js`. Конфигурација за модул `admin.module.js` е `admin.config.js`. - - *Зошто?*: Ја разделува конфигурацијата од дефиницијата на модулот, компонентите и имплементацијата. - - *Зошто?*: Обезбедува лесна идентификација на конфигурацијата на модулот. - -### Патеки -###### [Style [Y129](#style-Y129)] - - Разделете ги конфигурациите на патеките во посебна датотека. Примери како `app.route.js` за главниот модул и `admin.route.js` за `admin` модулот. Дури во помали апликации, преферирам да останат разделени од останатата конфигурација. - -**[Назад кон содржината](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-Y140)] - - Поставете ја пликацијата така што `L` (брзо лоцирање на кодот), `I` (идентификација на кодот со поглед), `F` (најрамна структура што можете) и `T` (обидете се да останете DRY). Структурата треба да ги задоволува овие 4 основни водичи. - - *Зошто LIFT?*: Обезбедува конзистентна структура која лесно се скалира, која е модуларна, и лесно може да ја зголеми ефикасноста на програмерите преку брзо лоцирање на кодот. Друг начин да ја проверите вашата структура е да се запрашате: Колку брзо можете да ги отворите и работите во сите датотека со одредена функција? - - Кога забележувам дека мојата структура е незадоволителна, се навраќам на LIFT водичите - - 1. `L` (лоцирање на вашиот код е лесно) - 2. `I` (идентифицирање на кодот со поглед) - 3. `F` (рамна структура колку што можеме) - 4. `T` (обидете се да останеме DRY (Не Се Повторувај) или T-DRY) - -### Лоцирање -###### [Style [Y141](#style-Y141)] - - Лоцирање на вашиот код треба да биде интуитивно, едноставно и брзо. - - *Зошто?*: Сметам дека е најважно за проект. Доколку тимот не може да брзо ги пронајде датотеките кои им се потребни да работат, тогаш не можат да работат ефикасно и структурата треба да се промени. Можете да го знаете името на датотеката или каде се наоѓаат сродните датотеки, така што доколку ги поставите на најинтуитивното место во блискост меѓу себе би се заштедило многу време. Описна структура на папки може да помогне. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Идентификација -###### [Style [Y142](#style-Y142)] - - Кога ќе погледнете во датотека, би требало одма да знаете што содржи и претставува. - - *Зошто?*: Ќе поминете помалку време на пребарување на кодот и со тоа поефикасен. Доколку тоа значи подолги имиња, нека биде така. Бидете описни со имињата на датотеките и нека содржината биде во една компонента. Избегнувајте датотеки со повеќе контролери, сервиси или мешавина од нив. Постои исклучок на 1 компонента по датотека кога постојат мали функционалности поврзани една со друга кои сеуште можат лесно да се идентифицираат. - -### Рамна структура -###### [Style [Y143](#style-Y143)] - - Одржувајте рамна структура на папки колку што можете повеќе. Кога ќе се појават 7+ датотеки, започнете со поделба. - - *Зошто?*: Никој не би сакал да пребарува низ 7 нивоа од папки за да пронајде датотека. Размислете за мениа на веб страни... било што подлабоко од 2, треба да се размисли подобро во разделба. Во структурата не постои правило за број на папки, но кога папка има 7-10 датотеки, време е да се создадат подпапки. Нека биде поставена како што ви е удобно. Користете порамна структура се додека не е очигледно (со помош на LIFT) да создадете нова папка. - -### T-DRY (Обидете се да останете DRY) -###### [Style [Y144](#style-Y144)] - - Бидете, но се во нормални граници без загуба на читливост. - - *Зошто?*: Да останете DRY е важно, но не е значајно доколку жртвувате на другите водичи во LIFT и зошто го нарекувам T-DRY. Не би сакал да пишувам session-view.html за преглед бидејќи очигледно е дека е преглед. Доколку не е очигледно под конвенција, тогаш ќе го именувам. - -**[Назад кон содржината](#table-of-contents)** - -## Application Structure - -### Целокупните водиши -###### [Style [Y150](#style-Y150)] - - Имајте блиска и широка визија на вашата имплементација. Со други зборови, започнете со мали компоненти и запазете каде се насочува апликацијата во иднината. Целокупниот код на апликацијата е во корен-папката наречена `app`. Целата содржина е 1 датотека по функција. Секој контролер, сервис, модул, преглед е во своја датотека. Сите 3rd party скрипти поставете ги во друга папка, не во `app` папката. Јас ги немам напишано нив и не би сакал да имам неред во мојата апликација (`bower_components`, `scripts`, `lib`). - - Забелешка: Најдете повеќе детали и расудувања за структурата во [овој оригинален пост за структура на апликација](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Изглед -###### [Style [Y151](#style-Y151)] - - Поставете компоненти која го отсликуваат целокупниот изглед на апликацијата во папката `layout`. Овде може да имаме поделеност на посебни секции од прегледот и контролер кој ќе биде контејнер за неговата апликација, навигација, мениа, области со содржина и други региони. - - *Зошто?*: Го организира целиот изглед во единствено место кое може да се употребува низ целата апликација. - -### Папки-По-Функција структура -###### [Style [Y152](#style-Y152)] - - Создадете папки според нивната функција. Кога папката ќе порасне 7+ датотеки, почнете да размислувате за нови папки. Вашиот праг може да биде различен, така да поставете како што е препорачано. - - *Зошто?*: Програмерот може да го лоцира кодот, идентификува секоја датотека што претставува, структурата останува рамна и не постојат повторувачки или непотребни имиња. - - *Зошто?*: LIFT водичите се покриени. - - *Зошто?*: Помага при нередот во апликацијата со оганизација на содржината со помош на LIFT водичите. - - *Зошто?*: Кога постојат многу датотеки, полесно е да ги лоцирате со конзистентна структура на папки и потешко со рамна структура на истите папки. - - ```javascript - /** - * препорачано - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Пробна апликација пример](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Забелешка: Не употребувајте структура со папка-по-тип. Со ова ќе се движите низ повеќе папки кога работите на функционалност што станува потешко како што апликацијата има повеќе од 5, 10 или 25 прегледи и контролери (за други функционалности), а со тоа и потешко за лоцирање на датотеките на таа функционалност. - - ```javascript - /* - * избегнувајте - * алтернативен папка-по-тип - * Препорачаувам "папка-по-функционалност" - */ - - app/ - app.module.js - app.config.js - app.routes.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Modularity - -### Многу мали, само содржани модулу -###### [Style [Y160](#style-Y160)] - - Создадете мали модули кои содржат една одговорност. - - *Зошто?*: Модуларни апликации се полесни за внесување на зависности во нив и овозможува за тимовите да ја прошират апликацијата вертикално во инкременти. Ова значи дека може да внесуваме нови функционалности како што ги развиваме. - -### Создадете модул за апликацијата -###### [Style [Y161](#style-Y161)] - - Создадете корен модул за апликацијата која улога е да ги содржи сите модули и функционалности на вашата апликација. Именувајте го според вашето име на апликација. - - *Зошто?*: Angular охрабруба модуларност и поделба на грижи. Со создавање на корен модул на вашата апликација чија улога е да се поврзе со другите модули овозможува јасен начин за вклучување и исклучување модули од вашата апликација. - -### Нека апликацискиот модул остане лесен -###### [Style [Y162](#style-Y162)] - - Внесете логика за поврзување на апликацијата во корен модулот. Нека функционалноста биде во останатите модули. - - *Зошто?*: Со додавање на додатни улоги на корен модулот на апликацијата за земање на податоци, прикажување изгледи и други логики кои не се поврзани со поврзување на модулите во апликацијата, се отежнува процесот на повторно искористување на функционалности како и нивно вклучување/исклучување. - - *Зошто?*: Модулот на апликацијата постанува манифест кој опишува кои модули ќе ја дефинираат апликацијата. - -### Областите за функционалност се Модулите -###### [Style [Y163](#style-Y163)] - - Создадете модули кои ги претставуваат областите на функционалности, како изгледот, повторно искористливи и заеднички сервиси, контролни табли и функционалности специфични за апликацијата (на пример, корисници, админ, продажба). - - *Зошто?*: Само содржани модули можат да се вклучат/исклучат без многу проблеми. - - *Зошто?*: Спринтови или итерации може да се фокусираат на функционалности и нивните области, а да се вклучат во апликацијата на крајот од спринтот или итерацијата. - - *Зошто?*: Со разделба на областите на функционалности во модули овозможува полесно тестирање на модулите во изолација и повторно искористување на кодот. - -### Повторно употребливи блокови се Модули -###### [Style [Y164](#style-Y164)] - - Создадете модули кои претставуваат повторно реискористливи блокови од апликацијата за заеднички сервиси како справување со исклучоци, логирање, дијагностика, безбедност и локално зачувување на податоци. - - *Зошто?*: Овие типови на функционалности се потребни во многу апликации, така што со одделување во нивните модули овозможува да бидат генерични и повторно употребливи во други апликации. - -### Зависности на модули -###### [Style [Y165](#style-Y165)] - - Корен модулот на апликацијата зависи од функционалните модули на апликацијата како и било кои заеднички или реискористливи модули. - - ![Модуларност и Зависности](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Зошто?*: Главниот модул на апликацијата содржи манифест од брзо идентифицирани функционалности на апликацијата. - - *Зошто?*: Секоја функционалност е во околина која содржи манифест од сите нејзини зависности, која може да биде поставена како зависност во други апликации и да работи. - - *Зошто?*: Интра-Апликациски функционалности како податочни сервиси кои се споделени се лесни за лоцирање и споделување во `app.core` (изберете го вашето омилено име за овој модул). - - Забелешка: Ова е стратегија за конзистентност. Постојат многу добри опции. Одберете една што е конзистентна, што ги следи Angular правилата за зависности, а лесно за одржување и скалабилност. - - > Моите структури се разликуваат малку низ проекти, но сите ги запазуваат правилата за структура и модуларност. Имплементацијата може да се разликува во зависност од функционалностите и тимот. Со други зборови, не се засегајте на буквалната структура се додека ја оправдува вашата структура за конзистентност, одржливост и ефикасност. - - > Во мали апликации можете да ги поставите сите заеднички зависности во апликацискиот модул каде функционалните зависности немаат директни зависности. Ова овозможува полесно одржување кај мали апликации, но станува потешко да се реискористуват тие модули надвор од оваа апликација. - -**[Назад кон содржината](#table-of-contents)** - -## Startup Logic - -### Конфигурација -###### [Style [Y170](#style-Y170)] - - Внесете го кодот во [конфигурацијата на модулот](https://docs.angular.org/guide/module#module-loading-dependencies) што мора да биде извршен пред почетокот на апликацијата. Идеални кандидати се провајдери и константи. - - *Зошто?*: Имаме помалку места за конфигурација на апликацијата. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Извршувачки блокови -###### [Style [Y171](#style-Y171)] - - Било каков код што треба да се изврши кога ќе започне апликацијата треба да биде поставен во фабрика, изложен преку функција или вгнезден во [извршувачки блок](https://docs.angular.org/guide/module#module-loading-dependencies). - - *Зошто?*: Код во извршувачки блок е тежок за тестирање. Поставување на истиот во фабрика е полесен за абстракција и лажење во тестирање. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document и $window -###### [Style [Y180](#style-Y180)] - - Употребете [`$document`](https://docs.angular.org/api/ng/service/$document) и [`$window`](https://docs.angular.org/api/ng/service/$window) наместо `document` и `window`. - - *Зошто?*: Овие сервиси се завиткани од Angular и полесни за тестирање него со употреба на document и window во тестовите. Ова помага да се излажат document и window во самите тестови. - -### $timeout и $interval -###### [Style [Y181](#style-Y181)] - - Употребете [`$timeout`](https://docs.angular.org/api/ng/service/$timeout) и [`$interval`](https://docs.angular.org/api/ng/service/$interval) наместо `setTimeout` и `setInterval` . - - *Зошто?*: Овие сервиси се завиткани од Angular и полесни се за тестирање. Исто така се ракува со Angular digest циклусот што овозможува полесно ажурирање на поврзаните податоци. - -**[Назад кон содржината](#table-of-contents)** - -## Testing -Тестирање на единки се справува со чистење на кодот и затоа внесов неколку препораки во основи на тестирање во линковите подоле кои содржат повеќе информации за нив. - -### Напишете тестови со Сценарија -###### [Style [Y190](#style-Y190)] - - Напишете неколку тестови за секое сценарио. Започнете со празен тест и внесете код како што пишувате код за сценариото. - - *Зошто?*: Со пишување на описи за тестовите се дефинира што ќе биде сценариото, што ќе прави и не прави, а и како ќе одреди дали е успешно. - - ```javascript - it('should have Avengers controller', function() { - //TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - //TODO - }); - - it('should have 10 Avengers', function() { - //TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - //TODO ($httpBackend?) - }); - - // and so on - ``` - -### Библиотеки за тестирање -###### [Style [Y191](#style-Y191)] - - Употребете [Jasmine](http://jasmine.github.io/) или [Mocha](http://mochajs.org) за тестирање на единки. - - *Зошто?*: И Jasmine и Mocha се широко употребувани во Angular заедницата. И двете се стабилни, добро одржувани и овозможуваат робустни функции за тестирање. - - Забелешка: Кога користите Mocha, исто така не заборавајте да употребите assert библиотека како што е [Chai](http://chaijs.com). Јас преферирам Mocha. - -### Извршувач на тестови -###### [Style [Y192](#style-Y192)] - - Употребете [Karma](http://karma-runner.github.io) како извршувач на тестови. - - *Зошто?*: Karma е лесна за подесување за еднаш или автоматски кога ќе го промениш кодот. - - *Зошто?*: Karma се поврзува во вашиот Continuous Integration процес само по себе или со помош од Grunt или Gulp. - - *Зошто?*: Некои IDE почнуваат да го вградуваат Karma во нив, како [WebStorm](http://www.jetbrains.com/webstorm/) и [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Зошто?*: Karma работи добро со автоматизација на задачите кои водечки алатки се [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) или [Gulp](http://www.gulpjs.com) (со [gulp-karma](https://github.com/lazd/gulp-karma)). - -### Stubbing и Spying -###### [Style [Y193](#style-Y193)] - - Употребете [Sinon](http://sinonjs.org/) за stubbing и spying. - - *Зошто?*: Sinon работи добро со Jasmine и Mocha и ги проширува нивните stubbing и spying функционалности. - - *Зошто?*: Sinon овозможува полесно менување меѓу Jasmine и Mocha, доколку сакате да ги пробате двете. - - *Зошто?*: Sinon има описни пораки кога тестовите ќе паднат на проверка. - -### Без Пребарувач -###### [Style [Y194](#style-Y194)] - - Употребете [PhantomJS](http://phantomjs.org/) за да ги извршувате тестовите на вашиот сервер. - - *Зошто?*: PhantomJS е пребарувач кој ги започнува тестовите без потреба од кориснички интерфејс. Така што нема потреба да инсталирате Chrome, Safari, IE, или други пребарувачи на вашиот сервер. - - Забелешка: Сепак потребно е да ги извршите тестовите на сите пребарувачи во вашата околина како и вашата целна група. - -### Анализа на код -###### [Style [Y195](#style-Y195)] - - Извршете JSHint на вашиот код. - - *Зошто?*: Тестовите се код. JSHint лесно ги прикажува грешките во квалитетот на кодот што може да предизвика неправилно извршување на тестовите. - -### Изменете ги променливите на JSHint за правила на тестови -###### [Style [Y196](#style-Y196)] - - Олабавете ги правилата за вашиот тест код со цел да ви дозволи да користите глобални променливи како `describe` и `expect`. Релаксирајте ги правилата за expressions. бидејќи Mocha ги користи овие. - - *Зошто?*: Вашите тестови се код и им е потребно истото внимание како вашиот продукциски код. Сепак, глобални променливи, како тие подоле, кои се користат од фрејмворкот за тестирање можат да бидат исклучени да не се проверуваат во вашите тестови. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Или можете да го додадете следното во вашата JSHint датотека за опции - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Алатки за тестирање](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - - ### Организирање на тестови -###### [Style [Y197](#style-y197)] - - - Поставете ги датотеките за unit тестови (спецификација) една поред друга во вашиот клиентски код. Поставете ги спецификациите кои ја покриваат интеграцијата со серверот или тестираат повеќе компоненти во посебна `tests` папка. - - *Зошто?*: Unit тестови имаат директна поврзаност со специфична компонента и датотека во изворниот код. - - *Зошто?*: Полесно е временски да ги ажурирате бидејќи секогаш се забележителни. Кога кодирате, дали TDD или тестирате пред или после развојот, специкациите се секогаш една поред друга и никогаш нема да се изгубат, што значи дека треба да се одржуваат притоа подобрувајќи ја покриеноста на кодот. - - *Зошто?*: Кога го ажурирате изворниот код полесно е да ги ажурирате и тестовите во исто време. - - *Зошто?*: Поставувајќи ги една поред друга овозможува полесно пронаоѓање и преместување заедно со изворниот код доколку го преместите. - - *Зошто?*: Разделување на спецификациите така што не се во дистрибуиран build е полесно со grunt или gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Animations - -### Употреба -###### [Style [Y210](#style-Y210)] - - Употребете суптилни [анимации со Angular](https://docs.angular.org/guide/animations) да преминете низ состојбите на прегледите и главните визуелни елементи. Вклучете го [ngAnimate модулот](https://docs.angular.org/api/ngAnimate). Трите главни точки се суптилност, глаткост и беспрекорност. - - *Зошто?*: Суптилни анимации го подобруваат искуството на корисникот кога се употребуваат правилно. - - *Зошто?*: Суптилни анимации ги подобруваат перформансите како што имаме премин низ прегледи. - -### Втора подточка -###### [Style [Y211](#style-Y211)] - - Употребете кратки анимации. Јас најчесто започнувам со 300ms и го изменувам се додека не е соодветно. - - *Зошто?*: Долги анимации може да имаат обратен ефект на искуството на корисникот со тоа што прикажуваме дека нашата апликација е со бавни перформанси. - -### animate.css -###### [Style [Y212](#style-Y212)] - - Употребете [animate.css](http://daneden.github.io/animate.css/) за конвенционални анимации. - - *Зошто?*: Анимациите на animate.css се први, глатки и лесни да се додадат во вашата апликација. - - *Зошто?*: Обезбедува конзистентност во вашите анимации. - - *Зошто?*: animate.css се широко употребувани и тестирани. - - Забелешка: Проверете те го [овој одличчен пост од Matias Niemelä за Angular анимации](http://www.yearofmoo.com/2013/08/remastered-animation-in-angular-1-2.html) - -**[Назад кон содржината](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-Y220)] - - Доколку планирате да направите документација, употребете го [`jsDoc`](http://usejsdoc.org/) со чија синтакса ќе документирате функциски имиња, нивен опис, нивни параметри и што враќа. Употребете `@namespace` и `@memberOf` за да се израмни со вашата апликациска структура. - - *Зошто?*: Можете да генерирате (и регенерирате) документација од вашиот код, наместо да ја пишувате од почеток. - - *Зошто?*: Обезбедува конзистентност со употреба на често употребувана алатка во софтерската индустрија. - - ```javascript - /** - * Фабрика за логирање - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Назад кон содржината](#table-of-contents)** - -## JS Hint - -### Употребете датотека на можните опции -###### [Style [Y230](#style-Y230)] - - Употребете JS Hint за проверување на вашиот JavaScript код и не заборавајте да ја измените JS Hint датотеката на можни опции која мора да биде вклучена во вашиот управувач на изворниот код. Погледнете ја [JS Hint документација](http://www.jshint.com/docs/) за детали на можните опции. - - *Зошто?*: Обезбедува прво предупредување пред да го пратите кодот до управувачот на изворниот код. - - *Зошто?*: Обезбедува конзистентност во вашиот тим. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Назад кон содржината](#table-of-contents)** - -## JSCS - -### Употребете датотека за опциите -###### [Style [Y235](#style-y235)] - - - Употребете JSCS за проверка на вашиот кодерски стил во JavaScript и бидете сигурни да ја прилагодите JSCS датотеката за опции и да ја вклучите во контролата на изворниот код. Погледнете ја [JSCS документација](http://www.jscs.info) за детали околу опциите. - - *Зошто?*: Обезбедува прво предупредување пред да го пратите кодот до управувачот на изворниот код. - - *Зошто?*: Обезбедува конзистентност во вашиот тим. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowMultipleLineStrings": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -*[Назад кон содржината](#table-of-contents)** - -## Constants - -### Глобални од продавачот -###### [Style [Y240](#style-Y240)] - - Создадете Angular константа за глобалните променливи од библиотеки кои не се ваши. - - *Зошто?*: Обезбедува начин да ги внесете библиотеките кои се глобални. Ова го подобрува тестирањето на кодот што овозможува лесно да забележите кои зависности се во вашите компоненти (се справува со протекување на абстракции). Исто така овозможува да ги излажирате овие зависности, каде има смисла да направите. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-Y241)] - - Употребете константи за вредности кои не се променуваат и не доаѓаат од друг сервис. Кога константи се употребени само за модул кој може повторно да биде искористен во повеќе апликации, потавете ги константите во една датотека за модул именувана како модулот. Се додека ова не е потребно, нека останат константите во главниот модул, во `constants.js` датотеката. - - *Зошто?*: Вредност која може да се промени, дури и поретко, треба да биде превземена од сервис со цел да не го менувате изворниот код. На пример, линк до податочниот сервис може да биде поставен во константите, но подобро е да се превземе од веб сервис. - - *Зошто?*: Константи можат да бидат вметнати во било која ангулар компонента, вклучувајќи ги провајдерите. - - *Зошто?*: Кога апликацијата е поделена во модули кои можат да бидат повторно искористени во други апликации, секој поединечен модул треба да се извршува самостојно вклучувајќи било какви зависни константи. - -```javascript - // Константи употребени во целата апликација -angular - .module('app.core') - .constant('moment', moment); - -// Константи употребени само во sales модулот -angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); -``` - -**[Назад кон содржината](#table-of-contents)** - -## File Templates and Snippets -Употребете датотечни шаблони и кратки кодови за да следите конзистентен стил и шаблон во вашиот код. Евен неколку шаблони и/или кратки кодови од IDE и едитори за веб развој. - -### Sublime Text -###### [Style [Y250](#style-Y250)] - - Angular кратки кодови кои ги следат овие водичи и стилови на код. - - - Симнете ги [Sublime Angular кратки кодови](assets/sublime-angular-snippets?raw=true) - - Поставете ги во вашата Packages папка - - Рестартирајте го Sublime - - Во JavaScript датотека напишете ја следната команда и потоа кликнете на `TAB` - - ```javascript - ngcontroller // создава Angular контролер - ngdirective // создава Angular директива - ngfactory // создава Angular фабрика - ngmodule // создава Angular модул - ngdirective // создава Angular директива - ngfactory // создава Angular фабрика - ngmodule // создава Angular модул - ngservice // создава Angular сервис - ngfilter // создава Angular филтер - ``` - -### Visual Studio -###### [Style [Y251](#style-Y251)] - - Angular датотечни шаблони што ги следат овие стилови и водичи на код можат да бидат најдени на [SideWaffle](http://www.sidewaffle.com) - - - Симнете ја [SideWaffle](http://www.sidewaffle.com) Visual Studio екстензија (vsix file) - - Извршете ја vsix датотека - - Рестартирајте го Visual Studio - -### WebStorm -###### [Style [Y252](#style-Y252)] - - Angular кратки кодови и датотечни шаблони кои ги следат овие стилови и водичи на код. Можете да ги внесете во вашите WebStorm подесувања: - - - Симнете ги [WebStorm Angular датотечни шаблони и кратки кодови](../assets/webstorm-angular-file-template.settings.jar?raw=true) - - Отворете го WebStorm и одберете го `File` менито - - Одберете го `Import Settings` - - Одберете ја датотеката и кликнете `OK` - - Во JavaScript датотека пишете ги следните команди и потоа кликнете на `TAB`: - - ```javascript - ng-c // создава Angular контролер - ng-f // создава Angular фабрика - ng-m // создава Angular модул - ``` - -**[Назад кон содржината](#table-of-contents)** - -+### Atom -###### [Style [Y253](#style-y253)] - - - Angular кратки кодови кои ги следат овие стилови и водичи на код. - ``` - apm install angular-styleguide-snippets - ``` - или - - Отворете го Atom, потоа отворете го Package Manager (Packages -> Settings View -> Install Packages/Themes) - - Побарајте го пакетот 'angular-styleguide-snippets' - - Кликнете на 'Install' за да го инсталирате пакетот - - - Во JavaScript датотека напишете ги следните команди и потоа кликнете `TAB` - - ```javascript - ngcontroller // создава Angular контролер - ngdirective // создава Angular директива - ngfactory // создава Angular фабрика - ngmodule // создава Angular модул - ngservice // создава Angular сервис - ngfilter // создава Angular филтер - ``` - -**[Назад кон содржината](#table-of-contents)** - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular програмки кои ги следат овие стилови и водичи. - - - Симнете [Brackets Angular програмки](assets/brackets-angular-snippets.yaml?raw=true) - - Brackets менаџер за екстензии ( File > Extension manager ) - - Инсталирајте ['Brackets Snippets (од edc)'](https://github.com/chuyik/brackets-snippets) - - Кликнете на сијалицата во десниот дел од brackets - - Кликнете на `Settings` и потоа `Import` - - Одберете датотека и одберете skip или override - - Кликнете `Start Import` - - - Во JavaScript датотека напишете ги овие команди следејќи го `TAB` - - ```javascript - // Овие се целосни програмки кои содржат IIFE - ngcontroller // создава Angular контролер - ngdirective // создава Angular директива - ngfactory // создава Angular фабрика - ngapp // создава Angular поставувач за модул - ngservice // создава Angular сервис - ngfilter // создава Angular филтер - - // Овие се парцијални програмки наменети за врзување - ngmodule // создава Angular земач на модули - ngstate // создава Angular UI Router дефиниција на состојба - ngconfig // дефинира configuration phase функција - ngrun // дефинира run phase функција - ngroute // дефинира Angular ngRoute 'when' дефиниција - ngtranslate // употребува $translate сервисот заедно со неговиот promise - ``` - - **[Назад кон содржината](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -Можете да го употребите [HotTowel yeoman генераторот](http://jpapa.me/yohottowel) да создадете апликација која ќе биде почетна за Angular следејќи го овој водич. - -1. Инсталирајте generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Создадете нова папка и сменете ја моменталната папка до неа - - ``` - mkdir myapp - cd myapp - ``` - -3. Започнете го генераторот - - ``` - yo hottowel helloWorld - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Routing -Рутирање од клиентска страна е значајно за создавање на тек во навигација помеѓу прегледи и прегледи составени од помали шаблони и директиви. - -###### [Style [Y270](#style-y270)] - - - Употребете го [AngularUI Router](http://angular-ui.github.io/ui-router/) за рутирање од клиентска страна. - - *Зошто?*: UI Router ги овозможува сите функционалности на Angular рутерот плус некои додатни функционалности како вклучување вгнездени рути и состојби. - - *Зошто?*: Синтаксата е прилично слична со Angular рутерот и лесна за миграција на UI Router. - -###### [Style [Y271](#style-y271)] - - - Дефинирајте рути за прегледи во модулот каде постојат. Секој модул треба да содржи рути за прегледите во модулот. - - *Зошто?*: Секој модул треба да си биде независен. - - *Зошто?*: Кога бришеме или додаваме модул, апликација ќе ги содржи рутите кои посочуваат кон постоечките прегледи. - - *Зошто?*: Ова го олеснува вклучување или исклучување на делови од апликацијата без грижи околу рути сирачиња. - -**[Назад кон содржината](#table-of-contents)** - -## Task Automation -Употребете [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) за создавање автоматизирани задачи. - -> Gulp се потпира на код наместо конфигурација додека Grunt се потпира на конфигурација наместо код. Јас лично преферирам Gulp бидејќи чувствувам е полесно да се пишува и чита, но и двата се одлични. - -> Научете повеќе за gulp и шаблони при task automation во мојот [Gulp Pluralsight курс](http://jpapa.me/gulpps) - -###### [Style [Y400](#style-y400)] - - - Употребете автоматизирање на тестови за вклучување на сите `*.module.js` датотеки пред останатите JavaScript датотеки од апликацијата. - - *Зошто?*: На Angular му требаат дефинициите на модулите да бидат регистрирани пред да бидат употребени. - - *Зошто?*: Именување на модули со слична шема како `*.module.js` е полесно за нивно превземање со glob и да ги постави први. - - ```javascript - var clientApp = './src/client/app/'; - - // Секогаш превземете ги датотеките со модули први - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Назад кон содржината](#table-of-contents)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - Избегнувајте употреба на филтери за скенирање на сите својства на комплексен објект граф. Употребувајте филтери за селекција на својства. - - *Зошто?*: Филтерите можат лесно да бидат злоупотребени и да имаат негативен ефект на перформанси ако не се употребени мудро, на пример кога филтер удира на голем и длабок објект граф. - -**[Назад кон содржината](#table-of-contents)** - -## Angular Docs -За се останато, референцирајте се до неговото API во [Angular документацијата](//docs.angular.org/api). - -## Contributing - -Отворете Issue прво за да дискутираме за можни промени/додатоци. Доколку имате прашања со водичот, слободно отворете Issue во складиштето. Доколку најдете пропуст, создадете Pull Request. Идеата е да ја одржуваме содржината ажурирана и со функционалноста на github да си помогнеме во проширување на приказната со Issue и PR, кои може да се пронајдат од Google. Зошто? Бидејќи шансите се доколку имате прашање, може и некој друг да го има истото! Можете да научите повеќе за начинот на кој придонесуваме. - -*Со придонесување до ова складиште, се придржувате вашата содржина да подлежи на лиценцата на ова складиште.* - -### Процес - 1. Дискусирајте за промените во Github Issue. - 2. Отворете Pull Request, поставете референца до Issue и објаснете ја промената и како додава на вредност. - 3. Pull Request ќе биде оценето и биде или споено или одбиено. - -## License - -_tldr; Use this guide. Attributions are appreciated._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[Назад кон содржината](#table-of-contents)** diff --git a/a1/i18n/pt-BR.md b/a1/i18n/pt-BR.md deleted file mode 100644 index 2f961aeb..00000000 --- a/a1/i18n/pt-BR.md +++ /dev/null @@ -1,2516 +0,0 @@ -# Guia de Estilo AngularJS - -*Guia de Estilo opinativo de Angular para times. Por [@john_papa](//twitter.com/john_papa)* - -Se você procura por um guia de estilo opinativo para sintaxe, convenções e estruturação de aplicações AngularJS, então siga em frente! Estes estilos são baseados em minha experiência com desenvolvimento com [AngularJS](//angularjs.org), apresentações, [cursos de treinamento na Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) e trabalhando em equipe. - - - -> Se você gostar deste guia, confira meu curso [Angular Patterns: Clean Code](http://jpapa.me/ngclean) na Pluralsight. - -A proposta deste guia de estilo é fornecer uma direção na construção de aplicações Angular mostrando convenções que eu uso, e o mais importante, porque eu as escolhi. - -## A Importância da Comunidade e Créditos - -Nunca trabalhe sozinho. Acho que a comunidade Angular é um grupo incrível, apaixonado em compartilhar experiências. Dessa forma, Todd Motto, um amigo e expert em Angular e eu temos colaborado com vários estilos e convenções. Nós concordamos na maioria deles, e discordamos em alguns. Eu encorajo você a conferir o [guia do Todd](https://github.com/toddmotto/angularjs-styleguide) para ter uma noção sobre sua abordagem e como ela se compara a esta. - -Vários de meus estilos vieram de várias sessões de pair-programming (programação pareada) que [Ward Bell](http://twitter.com/wardbell) e eu tivemos. Embora não concordemos sempre, meu amigo Ward certamente me ajudou influenciando na última evolução deste guia. - -## Veja os estilos em um aplicativo de exemplo - -Embora este guia explique o **o quê**, **porque** e **como**, acho útil ver tudo isso em prática. Este guia é acompanhado de uma aplicação de exemplo que segue estes estilos e padrões. Você pode encontrar a [aplicação de exemplo (chamada "modular") aqui](https://github.com/johnpapa/ng-demos) na pasta `modular`. Sinta-se livre para pegá-la, cloná-la e *forká-la*. [Instruções de como rodar o aplicativo estão em seu README](https://github.com/johnpapa/ng-demos/tree/master/modular). - -> **Nota de tradução**: Os títulos originais de cada seção serão mantidos, pois caso você queira buscar mais sobre estes assuntos futuramente, fazendo tal busca em inglês será obtido um resultado **imensamente** melhor. -> -> Após o título, estará a tradução auxiliar, quando necessária, visto que alguns termos são mais facilmente entendidos quando não traduzidos, por fazerem parte do núcleo do estudo em questão. -> -> Para eventuais erros de digitação e/ou tradução, favor enviar um pull-request! - -## Tabela de Conteúdo - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Angular Docs](#angularjs-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility -ou *Responsabilidade Única* - -### Regra nº 1 - - - Defina um componente por arquivo. - - O exemplo seguinte define um módulo `app` e suas dependências, define um controller e define uma factory, todos no mesmo arquivo. - - ```javascript - /* evite */ - angular - .module('app', ['ngRoute']) - .controller('SomeController' , SomeController) - .factory('someFactory' , someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Os mesmos componentes agora estão separados em seus próprios arquivos. - - ```javascript - /* recomendado */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recomendado */ - - // someController.js - angular - .module('app') - .controller('SomeController' , SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recomendado */ - - // someFactory.js - angular - .module('app') - .factory('someFactory' , someFactory); - - function someFactory() { } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## IIFE -### JavaScript Closures - - - Envolva os componentes Angular em uma *Immediately Invoked Function Expression (IIFE - Expressão de função imediatamente invocada)*. - - **Por que?** Uma IIFE remove as variáveis do escopo global. Isso ajuda a prevenir declarações de variáveis e funções de viverem por mais tempo que o esperado no escopo global, que também auxilia evitar colisões de variáveis. - - **Por que?** Quando seu código é minificado e empacotado dentro de um único arquivo para *deployment* no servidor de produção, você pode ter conflitos de variáveis e muitas variáveis globais. Uma IIFE o protege em todos estes aspectos provendo escopo de variável para cada arquivo. - - ```javascript - /* evite */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // função logger é adicionada como uma variável global - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // função storage é adicionada como uma variável global - function storage() { } - ``` - - - ```javascript - /** - * recomendado - * - * nada global é deixado para trás - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - **Nota**: Apenas para agilizar, o resto dos exemplos neste guia omitirão a sintaxe IIFE. - - - **Nota**: IIFE impede que códigos de teste alcancem membros privados como expressões regulares ou funções auxiliares que são frequentemente boas para testes unitários. Entretanto, você pode testá-las através de membros acessíveis ou expondo-os pelo próprio componente. Por exemplo, colocando funções auxiliares, expressões regulares ou constantes em sua própria *factory* ou constante. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Modules -ou *Módulos* - -### Avoid Naming Collisions -ou *Evitando Colisão de Nomes* - - - Use uma única convenção de nomes com separadores para sub-módulos. - - **Por que?** Nomes únicos ajudam a evitar colisão de nomes no módulo. Separadores ajudam a definir a hierarquia de módulos e submódulos. Por exemplo, `app` pode ser seu módulo raiz, enquanto `app.dashboard` e `app.users` podem ser módulos que são usados como dependências de `app`. - -### Definições (*aka Setters*) - -> ps: **aka** é o acrônimo de **A**lso **K**nown** **A**s, de forma traduzida, **também conhecido como**. - - - Declare os módulos sem uma variável usando a sintaxe *setter*. - - **Por que?** Com 1 componente por arquivo, raramente será necessário criar uma variável para o módulo. - - ```javascript - /* evite */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Ao invés, use a simples sintaxe *setter*. - - ```javascript - /* recomendado */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### *Getters* - - - Ao usar um módulo, evite usar uma variável. Em vez disso, use encadeamento com a sintaxe *getter*. - - **Por que?** Isso produz um código mais legível e evita colisão de variáveis ou vazamentos. - - ```javascript - /* evite */ - var app = angular.module('app'); - app.controller('SomeController' , SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recomendado */ - angular - .module('app') - .controller('SomeController' , SomeController); - - function SomeController() { } - ``` - -### *Setting* vs *Getting* -ou *Definindo* vs *Obtendo* - - - Apenas *set* (configure) uma vez e *get* (receba) em todas as outras instâncias. - - **Por que?** Um módulo deve ser criado somente uma vez, então recupere-o deste ponto em diante. - - - Use `angular.module('app', []);` para definir (*set*) um módulo. - - Use `angular.module('app');` para pegar (*get*) este módulo. - -### Named vs Anonymous Functions -ou *Funções Nomeadas vs Funções Anônimas* - - - Use funções nomeadas ao invés de passar uma função anônima como um callback. - - **Por que?** Isso produz um código mais legível, é muito fácil de *debugar*, e reduz a quantidade de callbacks aninhados no código. - - ```javascript - /* evite */ - angular - .module('app') - .controller('Dashboard', function() { }); - .factory('logger', function() { }); - ``` - - ```javascript - /* recomendado */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Controllers -ou *Controladores* - -### controllerAs View Syntax - - - Utilize a sintaxe [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) ao invés da sintaxe `clássica controller com $scope`. - - **Por que?** Controllers são construídos, "iniciados", e fornecem um nova instância única, e a sintaxe `controllerAs` é mais próxima de um construtor JavaScript do que a `sintaxe clássica do $scope`. - - **Por que?** Isso promove o uso do binding de um objeto "pontuado", ou seja, com propriedades na View (ex. `customer.name` ao invés de `name`), que é mais contextual, legível, e evita qualquer problema com referências que podem ocorrer sem a "pontuação" - - **Por que?** Ajuda a evitar o uso de chamadas ao `$parent` nas Views com controllers aninhados. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax - - - Utilize a sintaxe `controllerAs` ao invés da sintaxe `clássica controller com $scope`. - - - A sintaxe `controllerAs` usa o `this` dentro dos controllers que fica ligado ao `$scope`. - - **Por que?** O `controllerAs` é uma forma mais simples de lidar com o `$scope`. Você ainda poderá fazer o bind para a View e ainda poderá acessar os métodos do `$scope`. - - **Por que?** Ajuda a evitar a tentação de usar os métodos do `$scope` dentro de um controller quando seria melhor evitá-los ou movê-los para um factory. Considere utilizar o `$scope` em um factory, ou em um controller apenas quando necessário. Por exemplo, quando publicar e subscrever eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), ou [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considere mover estes casos para um factory e invocá-los a partir do controller. - - ```javascript - /* evite */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recomendado - mas veja a próxima sessão */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs with vm - - - Utilize uma variável de captura para o `this` quando usar a sintaxe `controllerAs`. Escolha um nome de variável consistente como `vm`, que representa o ViewModel. - - **Por que?** A palavra-chave `this` é contextual e quando usada em uma função dentro de um controller pode mudar seu contexto. Capturando o contexto do `this` evita a ocorrência deste problema. - - ```javascript - /* evite */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recomendado */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Nota: Você pode evitar qualquer [jshint](http://www.jshint.com/) warnings colocando o comentário abaixo acima da linha de código. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Nota: Quando watches são criados no controller utilizando o `controller as`, você pode observar o objeto `vm.*` utilizando a seguinte sintaxe. (Crie watches com cuidado pois eles deixam o ciclo de digest mais "carregado".) - - ```javascript - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - ``` - -### Bindable Members Up Top - - - Coloque os objetos que precisam de bind no início do controller, em ordem alfabética, e não espalhados através do código do controller. - - **Por que?** Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. - - **Por que?** Setar funções anônimas pode ser fácil, mas quando essas funções possuem mais de 1 linha do código elas podem dificultar a legibilidade. Definir as funções abaixo dos objetos que necessitam de bind (as funções serão elevadas pelo JavaScript Hoisting) move os detalhes de implementação para o final do controller, mantém os objetos que necessitam de bind no topo, e deixa o código mais fácil de se ler. - - ```javascript - /* evite */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recomendado */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) - - Nota: Se a função possuir apenas 1 linha considere mantê-la no topo, desde que a legibilidade não seja afetada. - - ```javascript - /* evite */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * linhas - * de - * código - * afetam - * a - * legibilidade - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recomendado */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 linha está OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Function Declarations to Hide Implementation Details - - - Utilize declarações de funções para esconder detalhes de implementação. Mantenha seus objetos que necessitam de bind no topo. Quando você precisar fazer o bind de uma função no controller, aponte ela para a declaração de função que aparece no final do arquivo. Ela está ligada diretamente aos objetos que precisam de bind no início do arquivo. Para mais detalhes veja [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - **Por que?** Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. (Mesmo do item anterior.) - - **Por que?** Colocar os detalhes de implementação de uma função no final do arquivo coloca a complexidade fora do foco, logo, você pode focar nas coisas importantes no topo. - - **Por que?** Declarações de funções são içadas, logo, não existe problema de se utilizar uma função antes dela ser definida (como haveria com expressões de função). - - **Por que?** Você nunca precisará se preocupar com declarações de funções quebrarem seu código por colocar `var a` antes de `var b` por que `a` depende de `b`. - - **Por que?** A ordenação é crítica em expressões de função. - - ```javascript - /** - * evite - * Usar expressões de funções. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Note-se que as coisas importantes estão espalhadas no exemplo anterior. No exemplo abaixo, nota-se que as coisas importantes do javascript estão logo no topo. Por exemplo, os objetos que precisam de bind no controller como `vm.avengers` e `vm.title`. Os detalhes de implementação estão abaixo. Isto é mais fácil de ler. - - ```javascript - /* - * recomendado - * Usar declarações de funções - * e objetos que precisam de bind no topo. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Defer Controller Logic - - - Remova a lógica do controller delegando ela a services e factories. - - **Por que?** A lógica pode ser reutilizada em múltiplos controllers quando colocada em um service e exposta através de uma função. - - **Por que?** A lógica em um serviço pode ser mais facilmente isolada em um teste unitário, enquanto a lógica feita no controlador pode ser facilmente [mockada](http://www.thoughtworks.com/pt/insights/blog/mockists-are-dead-long-live-classicists). - - **Por que?** Remove as dependências e esconde os detalhes de implementação do controlador. - - ```javascript - /* evite */ - function Order($http, $q) { - var vm = this; - vm.checkCredit = checkCredit; - vm.total = 0; - - function checkCredit() { - var orderTotal = vm.total; - return $http.get('api/creditcheck').then(function(data) { - var remaining = data.remaining; - return $q.when(!!(remaining > orderTotal)); - }); - }; - } - ``` - - ```javascript - /* recomendado */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.total = 0; - - function checkCredit() { - return creditService.check(); - }; - } - ``` - -### Keep Controllers Focused - - - Defina um controller para a view e tente não reutilizar o controller para outras views. Ao invés disso, coloque as lógicas reaproveitáveis em factories e mantenha o controller simples e focado em sua view. - - **Por que?** Reutilizar controllers em várias views é arriscado e um boa cobertura de testes end to end (e2e) é obrigatório para se garantir estabilidade em grandes aplicações. - -### Assigning Controllers - - - Quando um controller deve ser pareado com sua view e algum componente pode ser reutilizado por outros controllers ou views, defina controllers juntamente de suas rotas. - - Nota: Se uma View é carregada de outra forma que não seja através de uma rota, então utilize a sintaxe `ng-controller="Avengers as vm"`. - - **Por que?** Parear os controllers nas rotas permite diferentes rotas invocarem diferentes pares de controllers e views. Quando um controller é utilizado na view usando a sintaxe [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), esta view sempre será associada ao mesmo controller. - - ```javascript - /* evite - quando utilizar com uma rota e emparelhamento dinâmico é desejado */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recomendado */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Services -ou *Serviços* - -### Singletons - - - Services são instanciados com a palavra-chave `new`, use `this` para métodos públicos e variáveis. Services são bastante similares a factories, use um factory para consistência. - - Nota: [Todos services em Angular são singletons](https://docs.angularjs.org/guide/services). Isso significa que há apenas uma instância do serviço para cada injetor. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Factories -ou *Fábricas* - -### Single Responsibility -ou *Responsabilidade Única* - - - Factories devem ter [responsabilidade única](http://en.wikipedia.org/wiki/Single_responsibility_principle), que é encapsulado pelo seu contexto. Assim que uma factory começa a exceder a proposta de singularidade, uma nova factory deve ser criada. - -### Singletons - - - Factories são singletons e retornam um objeto que contém os membros do serviço. - - Nota: [Todos services em Angular são singletons](https://docs.angularjs.org/guide/services). - -### Accessible Members Up Top -ou *Membros acessíveis no topo* - - - Exponha os membros que podem ser invocados no serviço (a interface) no topo, utilizando uma técnica derivada do [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - **Por que?** Colocando no topo os membros que podem ser invocados da factory, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser invocados e testados através de teste unitário (e/ou mock). - - **Por que?** É especialmente útil quando o arquivo torna-se muito longo e ajuda a evitar a necessidade de rolagem para ver o que é exposto. - - **Por que?** Definir as funções conforme você escreve o código pode ser fácil, mas quando essas funções tem mais que 1 linha de código, elas podem reduzir a leitura e causar rolagem. Definir a interface no topo do que pode ser invocado da factory, torna a leitura mais fácil e mantém os detalhes de implementação mais abaixo. - - ```javascript - /* evite */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recomendado */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - Dessa forma, os bindings são espelhados através do objeto da interface da factory e os valores primitivos não podem ser atualizados sozinhos utilizando o revealing module pattern - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) - -### Function Declarations to Hide Implementation Details -ou *Declarações de função para esconder detalhes de implementação* - - - Use function declarations (declarações de função) para esconder detalhes de implementação. Mantenha os membros acessíveis da factory no topo. Aponte as function declarations que aparecem posteriormente no arquivo. Para mais detalhes leia [esse post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - **Por que?** Colocando os membros acessíveis no topo, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser acessados externamente. - - **Por que?** Colocando os detalhes de implementação da função posteriormente no arquivo move a complexidade para fora da visão, permitindo que você veja as coisas mais importantes no topo. - - **Por que?** Function declarations (declarações de função) são içadas (hoisted) para que não hajam preocupações em utilizar uma função antes que ela seja definida (como haveria com function expressions (expressões de função)). - - **Por que?** Você nunca deve se preocupar com function declaration (declarações de função) onde `var a` está antes de `var b` vai ou não quebrar o seu código porque `a` depende de `b`. - - **Por que?** A ordem é crítica com function expressions (expressões de função) - - ```javascript - /** - * evite - * Utilizando function expressions (expressões de função) - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // detalhes de implementação - }; - - var getAvengerCount = function() { - // detalhes de implementação - }; - - var getAvengersCast = function() { - // detalhes de implementação - }; - - var prime = function() { - // detalhes de implementação - }; - - var ready = function(nextPromises) { - // detalhes de implementação - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recomendado - * Utilizando function declarations (declarações de função) - * e membros acessíveis no topo. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // detalhes de implementação - } - - function getAvengerCount() { - // detalhes de implementação - } - - function getAvengersCast() { - // detalhes de implementação - } - - function prime() { - // detalhes de implementação - } - - function ready(nextPromises) { - // detalhes de implementação - } - } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Data Services -ou *Serviços de dados* - -### Separate Data Calls -ou *Chamadas de dados separadas* - - - A lógica de refatoração (refactor) para operações com dados e interação com dados na factory. Faça serviços de dados responsáveis por chamadas XHR, armazenamento local (local storage), armazenamento em memória (stashing) ou outras operações com dados. - - **Por que?** A responsabilidade dos controladores (controllers) é para a apresentação e coleta de informações da view. Eles não devem se importar como os dados são adquiridos, somente como "perguntar" por eles. Separar os serviços de dados (data services), move a lógica de como adquiri-los para o serviço e deixa o controlador (controller) mais simples e focado na view. - - **Por que?** Isso torna mais fácil testar (mock ou real) as chamadas de dados quando estiver testando um controlador (controller) que utiliza um serviço de dados (data service). - - **Por que?** A implementação de um serviço de dados (data service) pode ter um código bem específico para lidar com o repositório de dados. Isso pode incluir cabeçalhos (headers), como comunicar com os dados ou outros serviços, como $http. Separando a lógica de dados em um serviço, coloca toda a lógica somente em um local e esconde a implementação de consumidores de fora (talvez um controlador (controller)), tornado mais fácil mudar a implementação. - - ```javascript - /* recomendado */ - - // factory de serviço de dados (data service factory) - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Nota: O serviço de dados (data service) é chamado pelos consumidores, como um controlador (controller), escondendo a implementação dos consumidores, como mostrado abaixo. - - ```javascript - /* recomendado */ - - // controlador chamando uma factory de serviço de dados - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Return a Promise from Data Calls -ou *Retorne uma promessa de chamadas de dados* - - - Quando chamar um serviço de dados (data service) que retorna uma promessa (promise), como o $http, retorne uma promessa (promise) na função que está chamando também. - - **Por que?** Você pode encandear as promessas (promises) juntas e definir ações após a promessa (promise) da chamada do dado ser completada, resolvendo ou rejeitando a promessa (promise). - - ```javascript - /* recomendado */ - - activate(); - - function activate() { - /** - * Passo 1 - * Chame a função getAvengers para os dados - * dos vingadores (avengers) e espere pela promessa (promise) - */ - return getAvengers().then(function() { - /** - * Passo 4 - * Faça uma ação resolvendo a promessa (promise) finalizada - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Passo 2 - * Chame o serviço de dados (data service) e espere - * pela promessa (promise) - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Passo 3 - * Atribua os dados e resolva a promessa (promise) - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - **[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Directives -ou *Diretivas* - -### Limit 1 Per File -ou *Limite 1 por arquivo* - - - Crie uma diretiva (directive) por arquivo. Nomeie o arquivo pela diretiva. - - **Por que?** É fácil misturar todas as diretivas em um arquivo, mas é difícil depois separá-las, já que algumas são compartilhadas entre aplicativos, outras pelos módulos (modules) e algumas somente para um módulo. - - **Por que?** Uma diretiva (directive) por arquivo é mais fácil de dar manutenção. - - ```javascript - /* evite */ - /* directives.js */ - - angular - .module('app.widgets') - - /* diretiva de pedido (order) que é específica para o módulo de pedido */ - .directive('orderCalendarRange', orderCalendarRange) - - /* diretiva de vendas (sales) que pode ser usada em qualquer lugar do aplicativo de vendas */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* diretiva de spinner que pode ser usada em qualquer lugar dos aplicativos */ - .directive('sharedSpinner', sharedSpinner); - - - function orderCalendarRange() { - /* detalhes de implementação */ - } - - function salesCustomerInfo() { - /* detalhes de implementação */ - } - - function sharedSpinner() { - /* detalhes de implementação */ - } - ``` - - ```javascript - /* recomendado */ - /* calendarRange.directive.js */ - - /** - * @desc diretiva de pedido (order) que é específica para o módulo de pedido em uma companhia chamada Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* detalhes de implementação */ - } - ``` - - ```javascript - /* recomendado */ - /* customerInfo.directive.js */ - - /** - * @desc diretiva de spinner que pode ser usada em qualquer lugar de um aplicativo de vendas em uma companhia chamada Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* detalhes de implementação */ - } - ``` - - ```javascript - /* recomendado */ - /* spinner.directive.js */ - - /** - * @desc diretiva de spinner que pode ser usada em qualquer lugar de aplicativo em uma companhia chamada Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* detalhes de implementação */ - } - ``` - - Nota: Há diferentes opções de nomear diretivas (directives), especialmente quando elas podem ser usadas em escopos (scopes) variados. Escolha uma que faça a diretiva e o nome do arquivo distinto e simples. Alguns exemplos são mostrados abaixo, mas veja a seção de nomeação para mais recomendações. - -### Limit DOM Manipulation -ou *Limite a manipulação do DOM* - - - Quando estiver manipulando o DOM diretamente, utilize uma diretiva (directive). Se formas alternativas podem ser utilizadas, como: utilizar CSS para setar estilos ou [serviços de animação (animation services)](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ou [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), então prefira utilizá-los. Por exemplo, se uma diretiva simplesmente esconde ou mostra um elemento, use ngHide/ngShow. - - **Por que?** A manipulação do DOM pode ser difícil de testar, debugar, e há melhores maneiras (ex: CSS, animações (animations), templates). - -### Provide a Unique Directive Prefix -ou *Forneça um prefixo único para as diretivas* - - - Forneça um curto, único e descritivo prefixo para a diretiva, como `acmeSalesCustomerInfo`, que é declarado no HTML como `acme-sales-customer-info`. - - **Por que?** Um prefixo curto e único identifica o contexto e a origem da diretiva. Por exemplo, o prefixo `cc-` pode indicar que a diretiva é parte de um aplicativo da CodeCamper, enquanto a diretiva `acme-` pode indicar uma diretiva para a companhia Acme. - - Nota: Evite `ng-`, pois são reservadas para as diretivas do AngularJS. Pesquise largamente as diretivas utilizadas para evitar conflitos de nomes, como `ion-` que são utilizadas para o [Ionic Framework](http://ionicframework.com/). - -### Restrict to Elements and Attributes -ou *Restringir para elementos e atributos* - - - Quando criar uma diretiva que faça sentido por si só como um elemento, utilize restrição `E` (elemento personalizado) e opcionalmente `A` (atributo personalizado). Geralmente, se ela pode ter o seu próprio controlador (controller), `E` é o apropriado. Em linhas gerais, `EA` é permitido, mas atente para a implementação como elemento quando faz sentido por si só e como atributo quando estende algo existente no DOM. - - **Por que?** Faz sentido. - - **Por que?** Nós podemos utilizar uma diretiva como uma classe (class), mas se a diretiva está realmente agindo como um elemento, faz mais sentido utilizar como um elemento, ou pelo menos como um atributo. - - Nota: EA é o padrão para o Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* evite */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recomendado */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives and ControllerAs -ou *Diretivas e "ControladorComo"* - - - Utilize a sintaxe `controller as` com uma diretiva para consistência com o uso de `controller as` com os pares view e controlador (controller). - - **Por que?** Faz sentido e não é difícil. - - Nota: A diretiva (directive) abaixo demonstra algumas maneiras que você pode utilizar escopos (scopes) dentro de link e controller de uma diretiva, utilizando controllerAs. Eu coloquei o template somente para manter tudo em um mesmo local. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller : ExampleController, - controllerAs: 'vm' - }; - return directive; - - ExampleController.$inject = ['$scope']; - function ExampleController($scope) { - // Injetando $scope somente para comparação - /* jshint validthis:true */ - var vm = this; - - vm.min = 3; - vm.max = $scope.max; - console.log('CTRL: $scope.max = %i', $scope.max); - console.log('CTRL: vm.min = %i', vm.min); - console.log('CTRL: vm.max = %i', vm.max); - } - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.max = %i', scope.max); - console.log('LINK: scope.vm.min = %i', scope.vm.min); - console.log('LINK: scope.vm.max = %i', scope.vm.max); - } - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Resolving Promises for a Controller -ou *Resolvendo promessas para um controlador* - -### Controller Activation Promises -ou *Ativação de promessas no controlador* - - - Resolva a lógica de inicialização no controlador (controller) em uma função `iniciar`. - - **Por que?** Colocando a lógica de inicialização em um lugar consistente no controlador (controller), torna mais fácil de localizar, mais consistente para testar e ajuda a evitar o espalhamento da lógica de inicialização pelo controlador (controller). - - Nota: Se você precisa cancelar a rota condicionalmente antes de utilizar o controlador (controller), utilize uma resolução de rota (route resolve). - - ```javascript - /* evite */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recomendado */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - iniciar(); - - //////////// - - function iniciar() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Route Resolve Promises -ou *Resolução de promessas na rota* - - - Quando o controlador (controller) depende de uma promessa ser resolvida, resolva as dependências no `$routeProvider` antes da lógica do controlador (controller) ser executada. Se você precisa cancelar a rota condicionalmente antes do controlador (controller) ser ativado, utilize uma resolução de rota (route resolve). - - **Por que?** Um controlador (controller) pode precisar de dados antes de ser carregado. Esses dados podem vir de uma promessa (promise) através de uma factory personalizada ou [$http](https://docs.angularjs.org/api/ng/service/$http). Utilizar [resolução de rota (route resolve)](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite as promessas (promises) serem resolvidas antes da lógica do controlador (controller) ser executada, então ele pode executar ações através dos dados dessa promessa (promise). - - ```javascript - /* evite */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // não resolvida - vm.movies; - // resolvida assíncrona - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* melhor */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - /* jshint validthis:true */ - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Nota: As dependências no código de exemplos do `movieService` não estão seguras para minificação. Para mais detalhes de como fazer o código seguro para minificação, veja as seções [injeção de dependência (dependency injection)](#manual-annotating-for-dependency-injection) e [minificação e anotação (minification and annotation)](#minification-and-annotation). - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Manual Annotating for Dependency Injection -ou *Anotação Manual para Injeção de Dependência* - -### UnSafe for Minification -ou *Não seguro para Minificação* - - - Evite usar o atalho de sintaxe de declarar dependências sem usar uma abordagem segura para minificação. - - **Por que?** Os parâmetros do componente (por ex. controller, factory, etc) serão convertidos em variáveis encurtadas. Por exemplo, `common` e `dataservice` podem virar `a` ou `b` e não serem encontrados pelo AngularJS. - - ```javascript - /* evite - não seguro para minificação*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Este código pode produzir variáveis encurtadas quando minificado e, assim, causar erro em tempo de execução. - - ```javascript - /* evite - não seguro para minificação*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Manually Identify Dependencies -ou *Identifique Dependências Manualmente* - - - Use `$inject` para identificar manualmente suas dependências de componentes do AngularJS. - - **Por que?** Esta técnica espelha a técnica usada por [`ng-annotate`](https://github.com/olov/ng-annotate), a qual eu recomendo para automatizar a criação de dependências seguras para minificação. Se `ng-annotate` detectar que a injeção já foi feita, ela não será duplicada. - - **Por que?** Isto salvaguarda suas dependências de serem vulneráveis a problemas de minificação quando parâmetros podem ser encurtados. Por exemplo, `common` e `dataservice` podem se tornar `a` ou `b` e não serem encontrados pelo AngularJS. - - **Por que?** Evite criar dependências in-line pois listas longas podem ser difíceis de ler no array. Além disso, pode ser confuso o array ser uma série de strings enquanto o último item é a função do componente. - - ```javascript - /* evite */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* evite */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recomendado */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Nota: Quando sua função estiver abaixo de um return o $inject pode ficar inacessível (isso pode acontecer em uma diretiva). Você pode resolver isso movendo o $inject para acima do return ou usando a sintaxe alternativa de injeção de array. - - Nota: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) introduziu um comportamento em que ele move o `$inject` para onde ele possa ser acessado. - - ```javascript - // dentro da definição de diretiva - function outer() { - return { - controller: DashboardPanel, - }; - - DashboardPanel.$inject = ['logger']; // inacessível - function DashboardPanel(logger) { - } - } - ``` - - ```javascript - // dentro da definição de diretiva - function outer() { - DashboardPanel.$inject = ['logger']; // acessível - return { - controller: DashboardPanel, - }; - - function DashboardPanel(logger) { - } - } - ``` - -### Manually Identify Route Resolver Dependencies -ou *Identifique Dependências do Resolvedor de Rotas Manualmente* - - - Use $inject para identificar manualmente as dependências do seu resolvedor de rotas para componentes do AngularJS. - - **Por que?** Esta técnica separa a função anônima do resolvedor de rota, tornando-a mais fácil de ler. - - **Por que?** Uma chamada a `$inject` pode facilmente preceder o resolvedor para fazer qualquer dependência segura para minificação. - - ```javascript - /* recomendado */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Minification and Annotation -ou *Minificação e Anotação* - -### ng-annotate - - - Use [ng-annotate](//github.com/olov/ng-annotate) para [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) e comente as funções que precisam de injeção de dependência automatizada usando `/** @ngInject */` - - **Por que?** Isso protege seu código de qualquer dependência que pode não estar usando práticas seguras para minificação. - - **Por que?** [`ng-min`](https://github.com/btford/ngmin) está deprecated. - - > Eu prefiro Gulp pois sinto que é mais fácil de escrever, de ler, e de debugar. - - O código a seguir não está usando dependências seguras para minificação. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Quando o código acima é executado através de ng-annotate produzirá a seguinte saída com a anotação `$inject` e se tornará seguro para minificação. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Nota: Se `ng-annotate` detectar que a injeção já foi feita (por ex. `@ngInject` foi detectado), o código do `$inject` não será duplicado. - - Nota: Quando usar um resolvedor de rotas você pode prefixar a função do resolvedor com `/* @ngInject */` o que produzirá código devidamente anotado, mantendo qualquer dependência injetada segura para minificação. - - ```javascript - // Usando anotação @ngInject - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Nota: A partir do Angular 1.3 use o parâmetro `ngStrictDi` da diretiva [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp). Quando presente, o injetor será criado no modo "strict-di" fazendo com que a aplicação falhe ao tentar invocar funções que não usem anotação explícita de função (elas podem não ser seguras para minificação). Informação de debug será logada no console para ajudar a rastrear o código ofensivo. - `` - -### Utilize Gulp ou Grunt para o ng-annotate - - - Utilize [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ou [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) para tarefas de build automatizadas. Injete `/* @ngInject */` antes de qualquer função que tenha dependências. - - **Por que?** ng-annotate vai capturar todas as dependências, mas as vezes requer dicas utilizando a sintaxe `/* @ngInject */` . - - O código abaixo é um exemplo de uma task Gulp utilizando ngAnnotate - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Exception Handling -ou *Tratamento de exceção* - -### decorators -ou *decoradores* - - - Utilize um [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), no seu config utilizando o serviço [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), no serviço [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) para realizar ações customizadas quando um erro ocorrer. - - **Por que?** Fornece um caminho consistente para manipular erros não tratados pelo Angular em tempo de desenvolvimento ou execução (run-time). - - Nota: Outra opção é sobrescrever o serviço ao invés de utilizar um decorator. Esta é uma boa opção, mas se você quer manter o comportamento padrão e estender, o decorator é recomendado. - - ```javascript - /* recomendado */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Pode adicionar o erro para um serviço de coleções, - * adicionar os erros no $rootScope, logar os erros em um servidor remoto - * ou logar localmente. Ou lançar a exceção. Isso cabe interiamente à você. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Catchers -ou *Coletores de exceção* - - - Criar um factory que expõe uma interface para capturar e tratar adequadamente as exceções. - - **Por que?** Fornece uma forma consistente de coletar exceções que podem ser lançadas no seu código (ex. durante uma chamada XHR ou uma promessa (promise) que falhou). - - Nota: O coletor de exceção é bom para coletar e reagir às exceções específicas das chamadas que você sabe que podem ser lançadas. Por exemplo, quando realizar uma chamada XHR que retorna dados de um serviço remoto e você quer coletar qualquer exceção desse serviço, reagindo de uma maneira única. - - ```javascript - /* recomendado */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Errors - - - Gerencie e log todos os erros de routing utilizando o [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - **Por que?** Fornece uma maneira consistente de gerenciar erros relacionados a routing. - - **Por que?** Potencialmente fornece uma melhor experiência de usuário se um erro de routing ocorrer e você redirecionar o usuário para uma tela amigável com mais detalhes ou opções de recuperação. - - ```javascript - /* recomendado */ - function handleRoutingErrors() { - /** - * Cancelamento de rota: - * Quando houver erro no roteamento, vá para o dashboard. - * Forneça uma cláusula de saída se ele tentar fazer isso 2 vezes. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - var destination = (current && (current.title || current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + (rejection.msg || ''); - /** - * Opcionalmente log usando um serviço customizado ou $log. - * (Não se esqueça de injetar o serviço customizado) - */ - logger.warning(msg, [current]); - } - ); - } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Naming -ou *Nomenclatura* - -### Diretrizes de Nomenclatura - - - Use nomes consistentes para todos os componentes seguindo um padrão que descreva a funcionalidade do componente e (opcionalmente) seu tipo. Meu padrão recomendado é `característica.tipo.js`. Existem dois nomes para a maioria dos componentes: - * o nome do arquivo (`avengers.controllers.js`) - * o nome de componente registrado pelo Angular (`AvengersController`) - - **Por que?** As convenções de nomenclatura ajudam a fornecer uma maneira consistente de encontrar algo à primeira vista. Consistência dentro do projeto é vital. Consistência dentro de um time é importante. Consistência em toda a empresa proporciona uma enorme eficiência. - - **Por que?** As convenções de nomenclatura deveriam simplesmente te ajudar a encontrar trechos do seu código mais rápido e torná-lo mais fácil de se entender. - -### Feature File Names -ou *Nome para funcionalidades* - - - Use nomes consistentes para todos os componentes seguindo um padrão que descreve a funcionalidade do componente e, em seguida, (opcionalmente) o seu tipo. Meu padrão recomendado é `feature.type.js`. - - **Por que?** Fornece uma maneira consistente para identificar componentes mais rapidamente. - - **Por que?** Fornece um padrão apropriado pra qualquer tarefa automatizada. - - ```javascript - /** - * opções comuns - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recomendado - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Nota: Outra convenção comum é nomear arquivos dos controllers sem a palavra `controller` no nome do arquivo como` avengers.js` em vez de `avengers.controller.js`. Todas as outras convenções ainda mantêm o uso de um sufixo do tipo. Controllers são o tipo mais comum de componente, portanto isso só economiza digitação e ainda é facilmente identificável. Eu recomendo que você escolha uma convenção que seja mais coerente para sua equipe. - - ```javascript - /** - * recomendado - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test File Names -ou *Nome para aquivos de testes* - - - Nomeie as especificações de testes de forma similar aos componentes que elas testam, com o sufixo `spec`. - - **Por que?** Fornece um modo consistente para identificar rapidamente os componentes. - - **Por que?** Fornece padrões de correspondência para o [karma](http://karma-runner.github.io/) ou outros test runners. - - ```javascript - /** - * recomendado - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller Names -ou *Nomes para controller* - - - Use nomes consistentes para todos os controllers nomeados após as sua funcionalidade. Use UpperCamelCase para os controllers, assim como para seus construtores. - - **Por que?** Fornece um modo consistente para identificar e referenciar os controllers. - - **Por que?** O UpperCamelCase é o modo mais comum para identificar objetos que serão instanciados através de construtores. - - ```javascript - /** - * recomendado - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengers', HeroAvengers); - - function HeroAvengers(){ } - ``` - -### Controller Name Suffix -ou *sufixo "Controllers"* - - - Complemente o nome do controller com ou sem o sufixo `Controller`. Escolha uma opção, não ambas. - - **Por que?** O sufixo `Controller` é mais usado e mais descritivo. - - ```javascript - /** - * recomendado: Opção 1 - */ - - // avengers.controller.js - angular - .module - .controller('Avengers', Avengers); - - function Avengers(){ } - ``` - - ```javascript - /** - * recomendado: Opção 2 - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -### Factory Names -ou *Nomes para factory* - - - Use nomes consistentes para todas as factories nomeadas após sua funcionalidade. Use a conveção camelCase para services e factories. Evite prefixos com `$`. - - **Por que?** Fornece um modo consistente de identificar e referenciar rapidamente as factories. - - **Por que?** Evite colisão de nomes com factories e services pré-programados que usam o prefixo `$`. - - ```javascript - /** - * recomendado - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - -### Directive Component Names -ou *Nomes para directive* - - - Use nomes consistentes para todas as directives usando a convenção camelCase. Use um prefixo curto para descrever a área a qual a directive pertence (como prefixo da compania ou do projeto). - - **Por que?** Fornece um modo consistente de identificar e referenciar rapidamente os componentes. - - ```javascript - /** - * recomendado - */ - - // avenger.profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile(){ } - ``` - -### Modules -ou *Módulos* - - - Quando há vários módulos, o arquivo principal deste módulo é nomeado `app.module.js`, enquanto os módulos dependentes são nomeados de acordo com o que eles representam. Por exemplo, um módulo admin é nomeado `admin.module.js`. Os nomes dos respectivos módulos registrados seriam `app` e `admin`. - - **Por que?** Fornece consistência para múltiplos módulos, e para expansão para grandes aplicações. - - **Por que?** Fornece um modo fácil para automação de tarefas, a fim de carregar todos as definições dos módulos em primeiro lugar, então os demais arquivos (empacotamento). - -### Configuration -ou *Configuração* - - - Separe a configuração do módulo em seu próprio arquivo, nomeado após o módulo. Um arquivo de configuração para o módulo principal `app` é nomeado `app.config.js` (ou simplesmente `config.js`). Uma configuração para o módulo `admin.module.js` é nomeada `admin.config.js`. - - **Por que?** Separa a configuração do módulo da definição, dos componentes e do código ativo. - - **Por que?** Fornece um local identificável para definir as configurações de um módulo. - -### Routes -ou *Rotas* - - - Separe as configurações das rotas em seus próprios arquivos. Os exemplos podem ser `app.route.js` para o módulo princial, e `admin.route.js` para o módulo `admin`. Mesmo nas menores aplicações, prefiro esta separação das demais configurações. Uma alternativa é um nome mais longo, como `admin.config.route.js`. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Application Structure LIFT Principle -ou *Princípio da estrutura LIFT na aplicação* - -### LIFT - - - Estruture a sua aplicação de um modo onde você possa: `L`ocate (Localizar) seu código rapidamente, `I`dentify (Identificar) o código facilmente, manter a estrutura a mais `F`lattest (Plana) que você conseguir, e `T`ry (Tentar) seguir o conceito de DRY (Don't Repeat Yourself - Não repita a si mesmo). A estrutura deve seguir essas 4 regras básicas. - - **Por que LIFT?**: Fornece uma estrutura consistente que escala bem, é modular, e torna mais fácil para aumentar a eficiência ao desenvolver, pois encontra-se o código rapidamente. Outra forma de verificar a estrutura da sua aplicação é se perguntar: Quão rápido é para você abrir e trabalhar em todos os arquivos relacionados com uma funcionalidade? - - Quando estou sentindo que não estou confortável com a minha estrutura, eu volto e revisito as regras do LIFT - - 1. `L`ocating (Localizar) nosso código é fácil - 2. `I`dentify (Identificar) o código rapidamente - 3. `F`lat (Plano) - Deixar a estrutura a mais plana que conseguirmos - 4. `T`ry (Tentar) se manter DRY (Don’t Repeat Yourself - Não repita a si mesmo) ou T-DRY - -### Locate -ou *Localizar* - - - Torne a localização do seu código: intuitiva, simples e rápida. - - **Por que?** Acho que isso é super importante para um projeto. Se a equipe não pode encontrar rapidamente os arquivos que precisam para trabalhar, eles não serão capazes de trabalhar da forma mais eficiente possível, e a estrutura precisa mudar. Você pode não saber o nome do arquivo ou onde os arquivos relacionados estão, por isso, colocando-os nos locais mais intuitivos e próximos uns dos outros, economiza uma boa parcela de tempo. Uma pasta descrevendo a estrutura pode ajudá-lo. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -ou *Identificar* - - - Quando você olhar para um arquivo, prontamente você deve saber o que ele contém e o que representa. - - **Por que?** Você gasta menos tempo caçando e procurando por código, e torna-se mais eficiente. Se isso significa nomes de arquivos mais longos, então que assim seja. Seja descritivo nos nomes de arquivos e mantenha o conteúdo do arquivo somente com 1 componente. Evite arquivos com vários controladores (controllers), múltiplos serviços (services), ou uma mistura. Existem exceções de 1 regra por arquivo quando eu tenho um conjunto de recursos muito pequenos que estão todos relacionados uns aos outros, eles ainda são facilmente identificáveis. - -### Flat -ou *Plano* - - - Mantenha uma estrutura plana de pastas o máximo que for possível. Quando você tiver 7 arquivos ou mais, comece a considerar separá-los. - - **Por que?** Ninguém quer pesquisar 7 níveis de pastas para encontrar um arquivo. Pense sobre menus em web sites - nada mais profundo do que 2 níveis deve ser levado a sério. Em uma estrutura de pastas não há nenhum número mágico, mas quando uma pasta tem 7-10 arquivos, pode ser a hora de criar subpastas. Baseie-se no seu nível de conforto. Use uma estrutura mais plana até que haja um valor óbvio (para ajudar o resto do LIFT) na criação de uma nova pasta. - -### T-DRY (Try to Stick to DRY) -ou *Tente manter-se em DRY - Não repita a si mesmo* - - - Mantenha-se DRY, mas não fique louco e sacrifique a legibilidade. - - **Por que?** Não ficar se repetindo é importante, mas não é crucial se acabar sacrificando os outros itens do LIFT, por isso eu chamo de T-DRY (Tente não ficar se repetindo). Eu não quero escrever session-view.html para uma view, porque obviamente é uma view. Se não é óbvio ou uma convenção, então eu renomeio. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Application Structure -ou *Estrutura da aplicação* - -### Overall Guidelines -ou *Orientações gerais* - - - Tenha uma visão de curto prazo da implementação e uma visão de longo prazo. Em outras palavras, comece pequeno, mas tenha em mente o caminho que o aplicativo pode tomar. Todo o código do aplicativo vai em uma pasta raiz chamada `app`. Todo o conteúdo é feito com um recurso por arquivo. Cada controlador (controller), serviço (service), módulo (module), visão (view) está em seu próprio arquivo. Todos os scripts de terceiros são armazenados em uma outra pasta raiz e não na pasta `app`. Não fui eu quem escreveu esses scripts, então eu não quero que eles baguncem meu aplicativo (`bower_components`,` scripts`, `lib`). - - Nota: Encontre mais detalhes sobre essa estrutura em [esse post original sobre a estrutura da aplicação](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout - - - Coloque os componentes que definem o layout geral do aplicativo em uma pasta chamada `layout`. Eles podem incluir uma view e um controller que agem como recipiente para o app, navegação, menus, áreas de conteúdo, e outras regiões. - - **Por que?** Organiza todos os layouts em um único lugar reutilizado em toda a aplicação. - -### Folders-by-Feature Structure -ou *Estrutura de Pastas-por-Recurso* - - - Crie pastas nomeadas para cada recurso que elas representam. Quando uma pasta cresce ao ponto de conter mais de 7 arquivos, comece considerar a criação de uma pasta para eles. O seu limite pode ser diferente, por isso, ajuste conforme necessário. - - **Por que?** O desenvolvedor pode localizar o código, identificar o que cada arquivo representa em resumo, a estrutura é plana como deve ser, e não há nenhum nome repetido ou redundante. - - **Por que?** As orientações LIFT estão todas sendo respeitadas. - - **Por que?** Através da organização do conteúdo, ajuda a reduzir o app de tornar-se desordenado e mantêm alinhado com as diretrizes LIFT. - - **Por que?** Quando há um grande número de arquivos (10+) localizá-los é mais fácil com estruturas de pastas consistentes e mais difícil em estruturas planas. - - ```javascript - /** - * recomendado - */ - - app/ - app.module.js - app.config.js - app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - session-detail.html - session-detail.controller.js - ``` - - ![Exemplo de estrutura na aplicação](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) - - Nota: Não estruture seu aplicativo usando pastas-por-tipo. Isto requer alternar entre várias pastas ao trabalhar em um recurso e fica difícil de manejar quando o aplicativo cresce rapidamente para 5, 10 ou 25+ views e controllers (e outros recursos), o que torna mais difícil do que pasta-por-recurso para localizar arquivos. - - ```javascript - /* - * evite - * Alternativa pastas-por-tipo. - * Eu recomendo "pastas-por-recurso". - */ - - app/ - app.module.js - app.config.js - app.routes.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Modularity -ou *Modularidade* - -### Many Small, Self Contained Modules -ou *Muitos módulos pequenos e independentes* - - - Crie pequenos módulos que encapsulem uma única responsabilidade. - - **Por que?** Aplicações modulares tornam fácil o acoplamento, pois permitem que as equipes de desenvolvimento construam fatias verticais das aplicações e juntem tudo de forma incremental. Isto significa que podemos acoplar novos recursos enquanto os desenvolvemos. - -### Create an App Module -ou *Crie um módulo da aplicação* - -- Crie um módulo raiz para a aplicação, cujo papel é: reunir todos os outros módulos e funcionalidades da sua aplicação. Nomeie ele de acordo com a sua aplicação. - - **Por que?** Angular incentiva padrões de modularidade e de separação. Criando um módulo raiz da aplicação cujo papel é o de amarrar os outros módulos juntos, fica muito simples de adicionar ou remover módulos na sua aplicação. - -### Keep the App Module Thin -ou *Mantenha o módulo da aplicação leve* - - - Somente coloque a lógica para reunir o aplicativo no módulo da aplicação. Deixe os recursos em seus próprios módulos. - - **Por que?** Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. - **Por que?** Colocar funções adicionais na raiz da aplicação para obter dados remoto, modos de exibição, ou outra lógica não relacionada com o acoplamento do aplicativo, torna mais difícil reutilizar os recursos ou mesmo, desligá-los. - - **Por que?** O módulo da aplicação torna-se um manifesto que descreve os módulos que ajudam a definir a aplicação. - -### Feature Areas are Modules -ou *Áreas de recursos são módulos* - - - Crie módulos que representem áreas de recursos, como: layout, serviços compartilhados e reutilizados, dashboards e recursos específicos do aplicativo (por exemplo, clientes, administrativo, vendas). - - **Por que?** Módulos independentes podem ser adicionados na aplicação com pouco ou nenhum esforço. - - **Por que?** Sprints ou iterações podem focar em áreas de recursos e acoplá-los na aplicação ao fim da sprint ou iteração. - - **Por que²**: Separando as áreas de recursos em módulos, fica fácil de testar os módulos em isolamento e de reutilizar o código. - -### Reusable Blocks are Modules -ou *Blocos reutilizáveis são módulos* - -- Crie módulos que representam blocos reutilizáveis da aplicação para serviços comuns, como: tratamento de exceção, log, diagnósticos, segurança e armazenamento local. - - **Por que?** Esses tipos de recursos são necessários em muitas aplicações, então mantê-los separados em seus próprios módulos os torna genéricos e assim, podem ser reutilizados em diferentes aplicações. - -### Module Dependencies -ou *Dependências do módulo* - - - O módulo raiz da aplicação depende de módulos de recursos específicos do aplicativo e de qualquer módulo compartilhado ou reutilizado. - - ![Moduluaridade e Dependências](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - **Por que?** O módulo principal do aplicativo contém um rápido manifesto para identificar os recursos da aplicação. - - **Por que?** Cada área de recurso contém um manifesto mostrando as suas dependências, assim, ela pode ser colocada como uma dependência em outras aplicação e ainda continuar funcionando. - - **Por que?** Recursos intra-aplicação como serviços compartilhados de dados, tornam-se muito mais fácil de localizar e compartilhar atráves do `app.core` (escolha o seu nome favorito para esse módulo). - - Nota: Essa é uma estratégia para consistência. Existem muitas outras boas opções. Escolha uma que seja consistente, que siga as regras de dependência do Angular, e que seja fácil de manter e escalar. - - > As minhas estruturas podem variar ligeiramente entre os projetos, mas todas elas seguem estas diretrizes para estrutura e modularidade. A implementação pode variar dependendo dos recursos e do time. Em outras palavras, não fique preso somente a uma estrutura mas justifique sua estrutura usando consistência, facilidade de manutenção e eficiência em mente. - - > Em um aplicativo pequeno, você também pode considerar colocar todas as dependẽncias compartilhadas no módulo principal do aplicativo, onde os módulos de recursos não tem dependências diretas. Isso torna mais fácil de manter a aplicação, mas torna mais difícil de reutilizar os módulos fora dessa aplicação. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Angular $ Wrapper Services - -### $document e $window - - - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) e [`$window`](https://docs.angularjs.org/api/ng/service/$window) ao invés de `document` e `window`. - - **Por que?** Estes services são encapsulados pelo Angular e mais fáceis de testar do que o uso de document e window. Isso ajuda a evitar que você tenha que mockar document e window por sua própria conta. - -### $timeout e $interval - - - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) e [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) ao invés de `setTimeout` e `setInterval` . - - **Por que?** Estes services são encapsulados pelo Angular e mais fáceis de testar e lidar com o ciclo do AngularJS's mantendo o data binding em sincronismo. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Testing -Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas recomendações de fundamentos para testes unitários com links para mais informações. - -### Escreva testes com Histórias (Stories) - - - Escreva um grupo de testes para cada história. Comece com um teste em branco e preencha-o conforme você for escrevendo o código para a história. - - **Por que?** Escrevendo uma descrição de teste te ajudará a definir claramente o que a sua história vai fazer ou não vai fazer e como você poderá mensurar o sucesso. - - ```javascript - it('should have Avengers controller', function() { - //TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - //TODO - }); - - it('should have 10 Avengers', function() {} - //TODO (mock data?) - }); - - it('should return Avengers via XHR', function() {} - //TODO ($httpBackend?) - }); - - // and so on - ``` - -### Frameworks para Testes - - - Para teste unitários use [Jasmine](http://jasmine.github.io/) ou [Mocha](http://visionmedia.github.io/mocha/). - - **Por que?** Ambos, Jasmine e Mocha são amplamente utilizados na comunidade AngularJS. Ambos são estáveis, são mantidos e provém features de teste robustas. - - Nota: Se escolher Mocha, também considere a escolha de uma Assert Library como [Chai](http://chaijs.com). - -### Test Runner - - - Use [Karma](http://karma-runner.github.io) como seu test runner. - - **Por que?** Karma é fácil de configurar para executar apenas uma vez ou automaticamente enquanto você altera seu código. - - **Por que?** Karma se integra facilmente com seu processo de Integração Contínua ou através do Grunt ou Gulp. - - **Por que?** Algumas IDE's estão começando a se integrar com o Karma, como [WebStorm](http://www.jetbrains.com/webstorm/) e [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - **Por que?** Karma funciona muito bem com os líderes de automação de tarefas, como [Grunt](http://www.gruntjs.com) (com [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com) (com [gulp-karma](https://github.com/lazd/gulp-karma)). - -### Stubbing e Spying - - - Utilize Sinon para stubbing e spying. - - **Por que?** Sinon funciona bem tanto com Jasmine quanto com Mocha e amplia as features de stubbing e spying que eles oferecem. - - **Por que?** Sinon faz ficar mais fácil alternar entre Jasmine e Mocha, se você quiser tentar ambos. - -### Headless Browser - - - Use [PhantomJS](http://phantomjs.org/) para executar seus testes no servidor. - - **Por que?** PhantomJS é um headless browser que executa os testes sem um navegador "visual". Ou seja, você não precisa instalar Chrome, Safari, IE ou outros navegadores no seu servidor. - - Nota: Você deve continuar testando em todos os navegadores em seu ambiente, conforme apropriado para seu público alvo. - -### Análise de Código - - - Execute JSHint no seus testes. - - **Por que?** Testes são códigos. O JSHint ajuda a identificar problemas de qualidade de código que podem fazer com que o teste execute de maneira errada. - -### Ignore algumas regras globais do JSHint no seus testes - - - Faça com que as regras de teste permitam globais comuns, tais como `describe` e `expect`. - - **Por que?** Seus testes são codigos e como tal necessitam da mesma atenção e regras de qualidade que todo o seu código de produção. No entanto, as variáveis globais usadas pelo framework de teste, por exemplo, podem ser ignoradas para que você as utilize em seus testes. - - ```javascript - /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/testing-tools.png) - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Animations -ou *Animações* - -### Utilização - - - Use [animações com AngularJS](https://docs.angularjs.org/guide/animations) para transitar suavemente entre a visualização de views e elementos visuais primários. Inclúa o módulo [ngAnimate](https://docs.angularjs.org/api/ngAnimate). Os três princípios são sutilidade, suavidade e sem remendos. - - **Por que?** Animações sutis podem melhorar a Experiência de Usuário (UX) quando usadas adequadamente. - - **Por que?** Animações sutis podem aumentar a sensação de performance durante a alteração entre views. - -### Sub Second - - - Use animações de curta duração. Eu geralmente começo com 300ms e ajusto conforme necessário. - - **Por que?** Animações de longa duração podem impactar negativamente na experiência do usuário e em sua percepção de desempenho, dando a impressão de ser uma aplicação lenta. - -### animate.css - - - Use [animate.css](http://daneden.github.io/animate.css/) para animações convencionais. - - **Por que?** As animações fornecidas por animate.css são rápidas, suaves e fáceis de adicionar à sua aplicação. - - **Por que?** Provê consistência em suas animações. - - **Por que?** animate.css amplamente utilizado e testado. - - Nota: Leia este [excelente post do Matias Niemelä sobre Angular Animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Comments -ou *Comentários* - -### jsDoc - - - Se você planeja produzir documentação, use a sintaxe [`jsDoc`](http://usejsdoc.org/) para documentar nomes, descrições, parâmetros e retornos de funções. Use `@namespace` e `@memberOf` para adequar à estrutura de sua aplicação. - - **Por que?** Você pode gerar (e regerar) documentação a partir do seu código ao invés de escrever do zero. - - **Por que?** Fornece consistência utilizando uma ferramenta comum no mercado. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## JS Hint - -### Use um arquivo de Options - - - Use JS Hint para inspecionar seu JavaScript e não se esqueça de customizar o arquivo de configurações e versioná-lo no controle de versão. Veja [JS Hint docs](http://www.jshint.com/docs/) para detalhes a respeito das opções. - - **Por que?** Fornece um primeiro alerta antes de commitar qualquer código ao controle de versão. - - **Por que?** Provê consistência a seu time. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Constants - -*ou Constantes* - -### Globais de terceiros (*vendors*) - - - Cria uma *Constant* no Angular para variáveis globais de bibliotecas de terceiros. - - **Por que?** Fornece uma forma de injetar bibliotecas de terceiros que de outra forma seriam globais. Isso melhora a testabilidade do código permitindo a você conhecer mais facilmente quais dependências os seus componentes têm (evita vazamento de abstrações). Também permite que você simule estas dependências, o que faz sentido. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## File Templates and Snippets -*ou Arquivos de Templates e Fragmentos* - -Use arquivos de templates ou fragmentos para auxiliar na consistência de estilos e padrões. Aqui estão alguns templates e/ou snippets(fragmentos) para algumas IDEs e editores de texto para desenvolvimento web. - -### Sublime Text - - - Angular snippets que seguem esses estilos e diretrizes. - - - Download [Sublime Angular snippets](assets/sublime-angular-snippets.zip) - - Coloque-os na pasta de pacotes - - Reinicie o Sublime Text - - Em um arquivo JavaScript, use os comandos abaixo seguidos de `TAB` - - ```javascript - ngcontroller // cria um Angular controller - ngdirective // cria uma Angular directive - ngfactory // cria uma Angular factory - ngmodule // cria um Angular module - ``` - -### Visual Studio - - - Angular snippets que seguem esses estilos e diretrizes podem ser encontrados em [SideWaffle](http://www.sidewaffle.com) - - - Download [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (arquivo vsix) - - Execute o arquivo vsix - - Reinicie o Visual Studio - -### WebStorm - - - Angular snippets que seguem esses estilos e diretrizes. Você pode importa-los na sua configuração do WebStorm: - - - Download [WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar) - - Abra o WebStorm e selecione a opção `File` no menu - - Selecione a opção `Import Settings` no menu - - Localize o arquivo e selecione `OK` - - Em um arquivo JavaScript, use os comandos abaixo seguidos de `TAB`: - - ```javascript - ng-c // cria um Angular controller - ng-f // cria uma Angular factory - ng-m // cria um Angular module - ``` - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** - -## Angular docs -*ou Documentação Angular* - -Para qualquer outra coisa e documentação da API, visite a [documentação do Angular](//docs.angularjs.org/api). - -## Contribuindo - -Primeiro, abra uma issue para discutir potenciais alterações / adições. Se você tiver dúvidas com o guia, sinta-se livre para abrir uma issue. Se você encontrar um erro de digitação, crie um pull request. A idéia é manter o conteúdo atualizado e usar o recurso nativo do github para ajudar a contar a história com issues e pull requests, que são pesquisáveis pelo Google. Por quê? Porque as probabilidades são de que se você tem uma dúvida, alguém também tem! Você pode saber mais aqui sobre como contribuir. - -*Contribuindo com este repositório você aceita disponibilizar sua contribuição sob os termos de licenciamento deste repositório.* - -### Processo - 1. Discuta as alterações em uma issue. - 1. Abra uma Pull Request, referencie a issue, eplique a alteração e como ela agrega valor. - 1. A Pull Request será avaliada e poderá ser aceita ou descartada. - -## Licença - -_tldr; Use este guia. Atribuições(menções) são apreciadas._ - -### (The MIT License) - -Copyright (c) 2014 [John Papa](http://johnpapa.net) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[De volta ao topo](#tabela-de-conte%C3%BAdo)** diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md deleted file mode 100644 index bd8a3f50..00000000 --- a/a1/i18n/ru-RU.md +++ /dev/null @@ -1,2774 +0,0 @@ -# Руководство по стилям для AngularJS - -*Angular соглашения по стилям для команд разработчиков, предложенные [@john_papa](//twitter.com/john_papa)* - -Если вам нужны стандарты написания кода, соглашения и руководства структурирования приложений AngularJS, то вы находитесь в правильном месте. Эти соглашения основаны на моем опыте программирования на [AngularJS](//angularjs.org), на моих презентациях [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa), а также на совместной работе в командах разработчиков. - -Главной целью этого документа является желание предоставить вам наиболее полные инструкции для построения приложений AngularJS. Рекомендуя данные соглашения, я стараюсь акцентировать ваше внимание на цели и причины, зачем их нужно придерживаться. - ->Если это руководство вам понравится, то вы можете также оценить мой курс [Angular Patterns: Clean Code](http://jpapa.me/ngclean), который размещен на сайте Pluralsight. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Признательность сообществу и коллегам -Никогда не работайте в вакууме. Я считаю AngularJS-сообщество невероятно открытым, которое активно обменивается опытом и заботится об этом. Также как и мой друг Todd Motto (отличный Angular эксперт), я работал со многими стилями и соглашениями. Мы с ним сходимся во многом, но иногда и противоречим друг другу. Я предлагаю вам ознакомиться с курсом [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) дабы почувствовать разницу подходов. - -Многие из моих стилей взяты в ходе моих программистских сессий с [Ward Bell](http://twitter.com/wardbell). А так как мы не всегда были согласны друг с другом, то мой друг Ward оказал очень сильное влияние на эволюцию и окончательную редакцию этого документа. - -## Смотрите стили и шаблоны в приложении-примере -Пока данное руководство объясняет *что*, *почему* и *как*, я сразу же показываю, как это работает на практике. Все, что я предлагаю и описываю сопровождается примером-приложения, которое соблюдает и демонстрирует приведенные стили и шаблоны. Вы можете найти [пример приложения (имя modular) здесь](https://github.com/johnpapa/ng-demos) в папке `modular`. Свободно скачивайте, клонируйте и перемещайте эти примеры в свои хранилища. [Инструкциии по запуску примеров находятся в файлах readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -## Переводы -[Переводы данного руководства по Angular-стилям](https://github.com/johnpapa/angularjs-styleguide/tree/master/i18n) поддерживаются соообществом разработчиков и могут быть найдены здесь. - -## Table of Contents - - 1. [Единственная обязанность (Single Responsibility)](#single-responsibility) - 1. [IIFE](#iife) - 1. [Модули (Modules)](#modules) - 1. [Контроллеры (Controllers)](#controllers) - 1. [Сервисы (Services)](#services) - 1. [Фабрики (Factories)](#factories) - 1. [Сервисы данных (Data Services)](#data-services) - 1. [Директивы (Directives)](#directives) - 1. [Resolving Promises for a Controller (Работа с Объектами Promise в Контроллерe)](#resolving-promises-for-a-controller) - 1. [Manual Annotating for Dependency Injection (Аннотация для Внедренной Зависимости)](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation (Минификация и аннотация)](#minification-and-annotation) - 1. [Exception Handling (Обработка Исключений)](#exception-handling) - 1. [Naming (Именования)](#naming) - 1. [Application Structure LIFT Principle (Структура Приложения по Приниципу LIFT)](#application-structure-lift-principle) - 1. [Application Structure (Структура Приложения)](#application-structure) - 1. [Modularity (Модульность)](#modularity) - 1. [Startup Logic (Логика Запуска Приложения)](#startup-logic) - 1. [Angular $ Wrapper Services (Angular и Интерфейсные Сервисы)](#angular--wrapper-services) - 1. [Testing (Тестирование)](#testing) - 1. [Animations (Анимации)](#animations) - 1. [Comments (Комментарии)](#comments) - 1. [JSHint](#js-hint) - 1. [Constants (Константы)](#constants) - 1. [File Templates and Snippets (Шаблоны Файлов и Сниппеты)](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing (Маршрутизация)](#routing) - 1. [Task Automation (Автоматизация)](#task-automation) - 1. [Angular Docs (Angular документация)](#angularjs-docs) - 1. [Contributing (Сотрудничество)](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Правило 1 -###### [Style [Y001](#style-y001)] - - - Определяйте 1 компонент в одном файле. - - В следующем примере в одном и том же файле определяется модуль(module) `app` вместе с его зависимостями, определяется контроллер(controller), а также сервис(factory). - - ```javascript - /* избегайте этого */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - А теперь каждый компонент разнесен в свой отдельный файл. - - ```javascript - /* рекомендовано */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* рекомендовано */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* рекомендовано */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[К Содержанию](#table-of-contents)** - -## IIFE -### Замыкания JavaScript -###### [Style [Y010](#style-y010)] - - - Оборачивайте компоненты Angular в Немедленно Исполняемые Функции(IIFE - Immediately Invoked Function Expression). - - *Зачем?*: IIFE удаляют переменные из глобальной области видимости. Этот прием не дает существовать переменным и функциям дольше, чем это необходимо в глобальной области видимости. Иначе это может вызвать непредсказуемые коллизии во время исполнения всего приложения. - - *Зачем?*: Когда ваш код будет сжат и упакован (bundled and minified) в один файл для размещения его на рабочем сервере, то коллизий станет намного больше, чем их было до минификации. IIFE защитит ваш код, обеспечивая область видимости переменных только в немедленно исполняемых функциях(IIFE), которые оборачивают ваш код. - - ```javascript - /* избегайте этого */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // функция logger добавлена как глобальная переменная - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // функция storage добавлена как глобальная переменная - function storage() { } - ``` - - ```javascript - /** - * рекомендовано - * - * больше нет глобальных переменных - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Замечание: Только для краткости, в остальных примерах мы не будем прописывать синтаксис с функциями IIFE. - - - Замечание: IIFE не дает тестировать приватный код, так как предотвращает доступ к нему, например, регулярные выражения или вспомогательные функции, которые нужно оттестировать в модульных тестах (unit test) напрямую. Как выход, вы можете тестировать через специальные методы, используемые только в тестах (accessible members) или выставить нужные внутренние функции в собственном компоненте. Например, поместите вспомогательные функции, регулярные выражения или константы в собственную фабрику(factory) или константу(constant). - -**[К Содержанию](#table-of-contents)** - -## Modules - -### Избегайте коллизий имен -###### [Style [Y020](#style-y020)] - - - Используйте конвенцию уникальных имен с разделителями для подмодулей. - - *Почему?*: Уникальные имена помогают избежать коллизий в именах модулей. Разделители определяет сам модуль и его подмодульную иерархию. Например, `app` может быть вашим корневым модулем, а модули `app.dashboard` и `app.users` могут использоваться как имена модулей, зависимые от `app`. - -### Сеттеры (Setters) -###### [Style [Y021](#style-y021)] - - - Объявляйте модули без переменных, используйте сеттеры (setters). - - *Почему?*: Так как обычно в файле 1 компонент, поэтому вряд ли потребуется вводить переменную для модуля. - - ```javascript - /* избегайте этого */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Используйте простой синтаксис сеттера. - - ```javascript - /* рекомендовано */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Геттеры (Getters) -###### [Style [Y022](#style-y022)] - - - Когда нужно использовать модуль, избегайте использования переменных, а используйте вместо этого цепочку геттеров. - - *Почему?*: Так вы производите более читаемый код, а также избегаете утечек и коллизий переменных. - - ```javascript - /* избегайте этого */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* рекомендовано */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Определение и получение модулей -###### [Style [Y023](#style-y023)] - - - Определите модуль один раз и получайте его во всех других сущностях. - - *Почему?*: Модуль должен быть определен только один раз, а потом используйте его во всех остальных местах. - - - Используйте `angular.module('app', []);` для определения модуля. - - Используйте `angular.module('app');` чтобы получить модуль. - -### Именованные или Анонимные Функции -###### [Style [Y024](#style-y024)] - - - Используйте именованные функции, не передавайте анонимные функции обратного вызова в качестве параметров. - - *Почему?*: Так вы производите более читаемый код, его легче отлаживать, и это уменьшает число вложенных функций обратного вызова. - - ```javascript - /* избегайте этого */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* рекомендовано */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[К Содержанию](#table-of-contents)** - -## Controllers - -### Синтаксис controllerAs в представлении -###### [Style [Y030](#style-y030)] - - - Используйте синтаксис [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/), который работает поверх синтаксиса `классический контроллер со $scope`. - - *Почему?*: Контроллер создается, как JavaScript-объект с ключевым словом "new" и затем предоставляется этот единственный экземпляр объекта. То есть синтаксис `controllerAs` намного ближе и похоже на конструктор языка JavaScript, чем `классический синтаксис $scope`. - - *Почему?*: Это позволяет использовать в представлении связывание на свойство объекта "через точку" (например вместо `name` будет `customer.name`), что является более концептуальным, проще для чтения, и помогает избегать многих ссылочных проблем, которые могут возникнуть без использования "точки". - - *Почему?*: Помогает избежать использование вызовов `$parent` в представлениях с вложенными контроллерами. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### Синтаксис controllerAs в контроллере -###### [Style [Y031](#style-y031)] - - - Используйте синтаксис `controllerAs` поверх синтаксиса `классический контроллер со $scope`. - - - Синтаксис `controllerAs` использует внутри контроллеров ключевую переменную `this`, которая привязывается к `$scope`. - - *Почему?*: `controllerAs` - это только синтаксический сахар поверх `$scope`. Вы все равно можете использовать связывание в представлениях и все равно имеете доступ к методам `$scope`. - - *Почему?*: Избавляет от искушения использования методов `$scope` внутри контроллера, когда это не требуется явно, и это позволяет перенести методы в фабрику(factory). Предпочтительнее использовать `$scope` в фабрике, а в контроллере только если это действительно необходимо. Например, когда мы подписываемся и рассылаем события с помощью [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), or [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) перенесите эти сервисы в фабрику, и вызывайте методы фабрики в контроллере. - - ```javascript - /* избегайте этого */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* рекомендовано - но изучите следующую секцию */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### Синтаксис controllerAs с переменной vm -###### [Style [Y032](#style-y032)] - - - Сохраните `this` в переменную, когда используете синтаксис `controllerAs`. Выберите постоянное имя для переменной, такое как `vm`, что будет значить ViewModel. - - *Почему?*: Ключевое слово `this` контекстное, и когда его потребуется использовать внутри любой другой функции контроллера, то его содержимое будет другим. Сохранение `this` в переменной `vm` позволит избежать этих проблем. - - ```javascript - /* избегайте этого */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* рекомендовано */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Замечание: Вы можете избежать любые [jshint](http://www.jshint.com/) предупреждения, если разместите над строкой кода комментарий, как в приведенном ниже примере. Это не требуется, если функция названа с помощью ВерхнегоРегистра(UpperCasing), так как согласно этой конвенциии, это означает, что функция является конструктором контроллера Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Замечание: Когда создаете наблюдающие функции(watcher) в контроллерах типа `controller as`, вы можете наблюдать за переменной следующего вида `vm.*`. (Создавайте наблюдающие функции с предупреждением, что они создают дополнительную нагрузку на цикл digest.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Привязываемые Члены Сверху -###### [Style [Y033](#style-y033)] - - - Помещайте привязываемые члены в верхней части контроллера, в алфавитном порядке, и не раскидывайте их в коде контроллера где попало. - - *Почему?*: Размещение привязываемых членов наверху, упрощает чтение и позволяет мгновенно определить, какие члены контроллера привязаны и используются в представлении. - - *Почему?*: Написание анонимных функций по месту использования может конечно и проще, но когда такие функции содержат много строк кода, то это значительно снижает читабельность кода. Определяйте функции ниже привязываемых членов, тем самым вы перенесете детали реализации вниз отдельно. Функции определяйте как hoisted, то есть они будут подняты наверх области видимости. А привязываемые члены наверху повысят читабельность кода. - - ```javascript - /* избегайте этого */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* рекомендовано */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Замечание: Если функция однострочная, то разместите и ее наверху, но так чтобы это не усложняло читабельность. - - ```javascript - /* избегайте этого */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * эти - * строки - * кода - * ухудшают - * читабельность - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* рекомендовано */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // одна строка, все OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Определения Функций Для Скрытия Деталей Реализации -###### [Style [Y034](#style-y034)] - - - Используйте определения функций для скрытия деталей реализации. Держите свои привязываемые члены наверху. Если нужно в контроллере сделать функцию привязываемой, укажите это в группе привязываемых членов и ссылайтесь на данную функцию, которая реализована ниже. Это подробно описано в секции Привязываемые Члены Сверху. Подробнее смотрите [здесь](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Почему?*: Размещение привязываемых членов наверху делает код читабельнее, и позволяет мгновенно определить, какие члены привязаны и используются в представлении. (Выше описано тоже самое.) - - *Почему?*: Размещение деталей реализации функции внизу скрывает эту сложность ниже и таким образом все важные вещи находятся на видном месте сверху. - - *Почему?*: Функции определены как hoisted (определены в самом верху области видимости), поэтому не надо заботиться об их использовании перед объявлением, так как это было бы с объявлениями выражений функций (function expressions). - - *Почему?*: Вам не надо волноваться, о том в каком порядке объявлены функции. Также как и изменение порядка функций не будет ломать код из-за зависимостей. - - *Почему?*: С выражениями функций(function expressions) порядок будет критичен. - - ```javascript - /** - * избегайте этого - * Использование выражений функций (function expressions). - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Заметьте, в предыдущем примере важный материал размещен в контроллере в разных местах. А в примере ниже, все важные моменты размещены сверху, в нашем случае, это члены, привязанные к контроллеру такие как `vm.avengers` и `vm.title`. Подробности реализации смещены вниз. Такой код проще читать. - - ```javascript - /* - * рекомендовано - * Объявления функций и - * привязанные члены смещены вверх. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Логика Контроллера Отдельно -###### [Style [Y035](#style-y035)] - - - Переносите логику контроллера в сервисы и фабрики. - - *Почему?*: Логика может использоваться несколькими контроллерами, если она помещена в сервис и выставлена в виде функции. - - *Почему?*: Вся логика в сервисе может быть легко изолирована в модульном тесте, а вызовы этой логики в контроллере могут быть фиктивно реализованы (mocked). - - *Почему?*: Из контроллера удаляются зависимости и скрываются подробности реализации. - - ```javascript - - /* избегайте этого */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* рекомендовано */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -### Один Контроллер - Одно Представление -###### [Style [Y037](#style-y037)] - - - Определяйте контроллер для одного представления, и не пытайтесь использовать этот контроллер для других представлений. Вместо этого, выносите повторно используемую логику в фабрики. Старайтесь держать контроллер простым и сфокусированным только на свое представление. - - *Почему?*: Использование контроллера с несколькими представлениями хрупко и ненадежно. А для больших приложений требуется хорошее покрытие тестами end to end (e2e) для проверки стабильности. - -### Получение Контроллеров -###### [Style [Y038](#style-y038)] - - - Когда контроллер и его представление уже создано, и если нужно что-то повторно использовать (контроллер и представление), определяйте экземпляр контроллера вместе с его маршрутом (route). - - Замечание: Если представление загружается не через маршрут, тогда используйте синтаксис `ng-controller="Avengers as vm"`. - - *Почему?*: Указание экземпляра контроллера в определении маршрута позволяет различным маршрутам вызывать различные пары контроллеров и представлений. А когда контроллеры указаны в представлении с помощью [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), то представление будет всегда ассоциировано с одним и тем же контроллером. - - ```javascript - /* избегайте этого - когда используется маршрут и необходимо динамическое назначение контроллера и представления */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* рекомендовано */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[К Содержанию](#table-of-contents)** - -## Services - -### Синглтоны -###### [Style [Y040](#style-y040)] - - - Сервисы создаются с помощью ключевого слова `new`. Используйте `this` для публичных методов и переменных. Так как они очень похожи на фабрики, то используйте фабрики для согласованности. - - Замечание: [Все Angular сервисы являются синглтонами](https://docs.angularjs.org/guide/services). Это значит, что создается только один экземпляр сервиса на один инжектор. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Factories - -### Единственная Обязанность (Single Responsibility) -###### [Style [Y050](#style-y050)] - - - Фабрики должны иметь [единственную обязанность](http://en.wikipedia.org/wiki/Single_responsibility_principle), это следует из их контекста. Если фабрика начинает превышать ту цель для которой она создана, то нужно создать другую фабрику. - -### Синглтон -###### [Style [Y051](#style-y051)] - - - Фабрики это синглтоны, которые возвращают объект, содержащий свойства и методы сервиса. - - Замечание: [Все Angular сервисы являются синглтонами](https://docs.angularjs.org/guide/services). - -### Доступные Члены Наверх -###### [Style [Y052](#style-y052)] - - - Помещайте вызываемые члены сервиса (интерфейс) наверху, используя технику [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и дает вам возможность быстро определить, какие члены сервиса могут быть вызваны и должны быть модульно оттестированы (либо фиктивно имплементированы - mocked). - - *Почему?*: Это особенно полезно, когда файл становится очень длинным, и вынуждает прокручивать текст кода, чтобы посмотреть, какие методы или свойства сервис предоставляет. - - *Почему?*: Размещение функций в обычном прямом порядке может быть и проще, но когда эти функции становятся многострочными, это снижает читабельность и вынуждает много скроллить. Определяя вызываемый интерфейс(в виде возвращаемого сервиса), вы убираете детали реализации вниз. А размещенный сверху интерфейс улучшает читабельность. - - ```javascript - /* избегайте этого */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* рекомендовано */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - -Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна Revealing Module. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Определения Функций для Скрытия Деталей Реализации -###### [Style [Y053](#style-y053)] - - - Используйте определения функций, чтобы скрыть детали реализации. Держите вызываемые члены фабрики в верхней части. Свяжите их с определениями функций, которые расположены ниже в файле. Подробную информацию смотрите [здесь](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и помогает быстро определить, какие функции фабрики могут быть вызваны извне. - - *Почему?*: Размещение функций с деталями реализации в нижней части файла выносит сложные вещи из поля зрения, так что только важные детали вы видите в верхней части файла. - - *Почему?*: Функции определены в самом верху области видимости (hoisted), то есть их можно вызывать до их определения. (что было бы не возможно с выражениями функций) - - *Почему?*: Вам не надо беспокоиться о порядке определений функций. Перестановка зависимых друг от друга функций не ломает код. - - *Почему?*: А для выражений функций порядок критичен. - - ```javascript - /** - * избегайте этого - * Использование выражений функций - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // детали реализации здесь - }; - - var getAvengerCount = function() { - // детали реализации здесь - }; - - var getAvengersCast = function() { - // детали реализации здесь - }; - - var prime = function() { - // детали реализации здесь - }; - - var ready = function(nextPromises) { - // детали реализации здесь - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * рекомендовано - * Использование определений функций - * и вызываемые члены расположены в верхней части. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // детали реализации здесь - } - - function getAvengerCount() { - // детали реализации здесь - } - - function getAvengersCast() { - // детали реализации здесь - } - - function prime() { - // детали реализации здесь - } - - function ready(nextPromises) { - // детали реализации здесь - } - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Data Services - -### Отделите вызовы данных -###### [Style [Y060](#style-y060)] - - - Делайте рефакторинг логики работы с данными и взаимодействия этих данных с фабрикой. Создавайте сервисы данных, ответственных за вызовы XHR, локальное хранилище(local storage), работу с памятью или за любые другие операции с данными. - - *Почему?*: Ответственность контроллера - это предоставление или сбор информации для представления. Он не должен заботиться о том, как работать с данными, он только должен знать кого попросить об этом. В сервисы для данных переносится вся логика работы с данными. Это делает контроллер более простым и более сфокусированным для работы с представлением. - - *Почему?*: Это позволяет более проще тестировать (фиктивно или реально) вызовы данных в контроллере, который использует сервис для данных. - - *Почему?*: Реализация сервиса данных может иметь очень специфичный код для операций с хранилищем данных. Могут использоваться заголовки для взаимодействовия с данными, или другие сервисы типа `$http`. Логика в сервисе данных инкапсулируется в одном месте и скрывает реализацию для внешних потребителей (контроллеров), также будет гораздо проще изменить реализацию в случае необходимости. - - ```javascript - /* рекомендовано */ - - // фабрика сервиса данных - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Замечание: Сервис данных вызывается потребителем (контроллером), и скрывает реализацию от потребителя. Это показано ниже. - - ```javascript - /* рекомендовано */ - - // контроллер вызывает фабрику сервиса данных - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Возвращение Объекта Promise для Контроллера -###### [Style [Y061](#style-y061)] - - - Если сервис данных возвращает promise типа $http, то в вызывающей функции возвращайте promise тоже. - - *Почему?*: Вы можете объединить в цепочку объекты promise, и после того как вызов данных закончится, выполнить дальнейшие действия для принятия или отклонения объекта promise. - - ```javascript - /* рекомендовано */ - - activate(); - - function activate() { - /** - * Шаг 1 - * Запрашиваем функцию getAvengers function - * для получения данных и ждем promise - */ - return getAvengers().then(function() { - /** - * Шаг 4 - * Выполняем действие принятия финального объекта promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Шаг 2 - * Запрашиваем сервис для данных - * и ждем promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Шаг 3 - * инициализируем данные и принимаем promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Directives -### Одна Директива - Один Файл -###### [Style [Y070](#style-y070)] - - - Создавайте только одну директиву в файле. Называйте файл именем директивы. - - *Почему?*: Конечно можно поместить директивы в один файл. Но потом их трудно будет разделить по приложениям, и по модулям. Например, нужна будет только одна из директив для определенного модуля. - - *Почему?*: Одну директиву в файле легче поддерживать. - - ```javascript - /* избегайте этого */ - /* directives.js */ - - angular - .module('app.widgets') - - /* директива для заказа, которая специфична для модуля заказов */ - .directive('orderCalendarRange', orderCalendarRange) - - /* директива продажи, которая может быть использована везде в модуле продаж */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* директива крутилки (spinner), которая может быть использована во всех модулях */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* детали реализации */ - } - - function salesCustomerInfo() { - /* детали реализации */ - } - - function sharedSpinner() { - /* детали реализации */ - } - ``` - - ```javascript - /* рекомендовано */ - /* calendarRange.directive.js */ - - /** - * @desc директива заказа, которая специфична модулю заказов в компании Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* детали реализации */ - } - ``` - - ```javascript - /* рекомендовано */ - /* customerInfo.directive.js */ - - /** - * @desc директива продажи, которая может быть использована везде в модуле продаж компании Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* рекомендовано */ - /* spinner.directive.js */ - - /** - * @desc директива крутилки (spinner), которая может быть использована во всех модулях компании Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* детали реализации */ - } - ``` - - Замечание: Существует много способов для именования директив, особенно это зависит от широты области использования (локально или глобально). Выбирайте тот способ, который определяет директиву и ее файл четко и ясно. Несколько примеров будет ниже, но в основном смотрите рекомендации в секции именований. - -### Манипулирование Элементами DOM в Директиве -###### [Style [Y072](#style-y072)] - - - Используйте директивы, если нужно манипулировать элементами DOM напрямую. Но если существуют альтернативные способы, то используйте лучше их. Например, для изменения стилей используйте CSS, для эффектов [сервисы анимации](https://docs.angularjs.org/api/ngAnimate), для управления видимостью используйте [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) и [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide). - - *Почему?*: Манипулирование элементами DOM тяжело тестить, отлаживать, и зачастую существуют более лучшие способы для реализации поставленной задачи (например CSS, анимации, шаблоны) - -### Добавляйте Директивам Уникальный Префикс -###### [Style [Y073](#style-y073)] - - - Добавляйте директивам короткий, уникальный, пояснительный префикс, такой как `acmeSalesCustomerInfo`, директива будет объявлена в HTML как `acme-sales-customer-info`. - - *Почему?*: Уникальный короткий префикс говорит о контексте и происхождении директивы. Например, префикс `cc-` может рассказать нам, что директива является частью приложения CodeCamper, а `acme-` это директива для компании Acme. - - Замечание: Не используйте префикс `ng-` для своих директив, так как он зарезервирован для директив AngularJS. Также исследуйте префиксы широко используемых директив для избежания конфликтов имен, например, префикс `ion-` используется для директив [Ionic Framework](http://ionicframework.com/). - -### Ограничивайте Элементы и Атрибуты -###### [Style [Y074](#style-y074)] - - - При создании директивы, которая планируется как самостоятельный элемент, применяйте ограничение `E` (разработано, как элемент) или по необходимости ограничение `A` (разработано, как атрибут). В основном, если директива разрабатывается как элемент, ограничения `E` вполне достаточно. Хотя Angular позволяет использовать `EA`, но все же лучше определится как реализовывать директиву, либо как самостоятельный отдельный элемент, либо как атрибут для улучшения функциональности существующего DOM-элемента. - - *Почему?*: Это имееет смысл. - - *Почему?*: Конечно мы можем использовать директиву в атрибуте class, но если директива действует как элемент, то лучше объявлять ее как элемент, ну или по крайней мере как атрибут. - - Замечание: EA используется по умолчанию для Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* избегайте этого */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* рекомендовано */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Директивы и ControllerAs -###### [Style [Y075](#style-y075)] - - - Используйте синтактис`controller as` в директиве, чтобы директива была согласована с использованием синтаксиса `controller as` в паре контроллера и представлении. - - *Почему?*: Это имет смысл и не так сложно. - - Замечание: Директива ниже демонстрирует некоторые способы, при которых вы можете использовать объект $scope внутри ссылки и контроллере директивы. Я сделал инлайновый шаблон для того, чтобы держать все в одном месте. - - Замечание: Что касается внедренной зависимости, смотрите [Определение зависимостей вручную](#manual-annotating-for-dependency-injection). - - Замечание: Заметьте, что контроллер директивы находится снаружи самой директивы. Такой подход исключает проблемы, когда инжектор создается в недосягаемом кодe после, например, 'return'. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - controllerAs: 'vm' - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.max = %i', scope.max); - console.log('LINK: scope.vm.min = %i', scope.vm.min); - console.log('LINK: scope.vm.max = %i', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Внедрение $scope сразу для параметра сравнения - var vm = this; - - vm.min = 3; - vm.max = $scope.max; - console.log('CTRL: $scope.max = %i', $scope.max); - console.log('CTRL: vm.min = %i', vm.min); - console.log('CTRL: vm.max = %i', vm.max); - } - ``` - - ```html - /* example.directive.html */ -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[К Содержанию](#table-of-contents)** - -## Resolving Promises for a Controller - -### Активация объектов promise в контроллере -###### [Style [Y080](#style-y080)] - - - Размещайте стартовую начальную логику для контроллера в функции `activate`. - - *Почему?*: Размещение стартовой логики в согласованном месте контроллера позволяет ее быстрее находить, более согласованно тестировать и позволит не расбрасывать по всему контроллеру логику активации. - - *Почему?*: Функция `activate` делает удобным повторное использование логики для обновления контроллера/представления, держит всю логику вместе, делает более быстрой работу пользователя с представлением, делает анимацию более простой с директивами `ng-view` и `ui-view`, ну и делает ориентирование разработчика в коде более энергичным и быстрым. - - Замечание: Если вам нужно при каком-то условии отменить маршрут перед началом использования контроллера, используйте для этого [route resolve](#style-y081). - - ```javascript - /* избегайте этого */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* рекомендовано */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Работа с Объектами Promise в Маршрутах -###### [Style [Y081](#style-y081)] - - - Если контроллер зависит от объекта promise, от результата которого зависит активация контроллера, то разрешайте/получайте эти зависимости в `$routeProvider`, перед тем как логика контроллера будет выполена. Если вам нужно по какому-то условию отменить маршрут перед активацией контроллера, то используйте обработчик маршрутов. - - - Используйте обработчик маршрутов, если вы решили в последствии отменить маршут до перехода к представлению. - - *Почему?*: Контроллер может потребовать данные перед своей загрузкой. Эти данные могут прийти из promise объекта через пользовательскую фабрику или [$http](https://docs.angularjs.org/api/ng/service/$http). Использование [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) дает возможность объекту promise разрешиться перед тем, как логика контроллера выполнится, тогда мы сможем выполнить действие, зависящее от результата объекта promise. - - *Почему?*: Код выполняется после маршрута и в активационной функции контроллера. После этого, сразу же начинает загружаться представление. Связывание данных начинается, когда активационнный объект promisе разрешился/выполнился. Анимация "прогресса" может быть показана время передачи представления (via ng-view or ui-view). - - Замечание: Код запускается перед маршрутом через объект promise. Отклонение promise отменяет маршрут. Разрешение заставляет ожидать новое представление, когда маршрут разрешится. Анимация “прогресса” может быть показана перед разрешением и через передачу представления. Если вам нужно получить представление быстрее и вам не нужна точка решения, получать ли представление, используйте тогда [controller `activate` technique](#style-y080). - - ```javascript - /* избегайте этого */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // не определено - vm.movies; - // определено асинхронно - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* это лучше */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Замечание: В примере ниже показано, как разрешение маршрута указывает на именованную функцию, которую легче отлаживать и легче оперировать встроенной зависимостью. - - ```javascript - /* еще лучше */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviePrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Замечание: Пример кода зависимости от `movieService` не безопасен для минификации. Подробности от том как сделать этот код безопасным для минификации, смотрите секции [dependency injection](#manual-annotating-for-dependency-injection) и [minification and annotation](#minification-and-annotation). - -**[К Содержанию](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### Уязвимости от Минификации -###### [Style [Y090](#style-y090)] - - - Избегайте объявления зависимостей без использования безопасного для минификации подхода. - - *Почему?*: Параметры для компонент (типа контроллер, фабрика и т.п.) будут преобразованы в усеченные переменные. Например, `common` и `dataservice` превратятся `a` или `b` и не будут найдены средой AngularJS. - - ```javascript - /* избегайте этого - не безопасно для минификации */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - После минификации будут производится усеченные переменные и это будет вызывать ошибки выполнения. - - ```javascript - /* избегайте этого- не безопасно для минификации */ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Определяйте Зависимости Вручную -###### [Style [Y091](#style-y091)] - - - Используйте `$inject` для ручного определения ваших зависимостей для AngularJS. - - *Почему?*: Этот прием отражает технику использованную в [`ng-annotate`](https://github.com/olov/ng-annotate), которую я рекомендую для автоматического создания зависимостей, безопасных для минификации. Если `ng-annotate` обнаруживает вставку(injection), которая уже была, то она не будет продублирована. - - *Почему?*: Это гарантирует вашим зависимостям защиту от повреждений, которую может нанести минификация, путем урезания и укорачивания переменных. Например, `common` и `dataservice` превратятся `a` и `b`, и не будут найдены средой AngularJS. - - *Почему?*: Избегайте создания однострочных зависимостей в виде длинных списков, которые трудно читаемы в массиве. Еще вводят в заблуждение то, что такие массивы состоят из элементов строк, а последний элемент - это функция. - - ```javascript - /* избегайте этого */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* избегайте этого */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* рекомендовано */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Замечание: Если функция снизу является возвращаемым значением, то $inject может быть недостижим (это может случится в директиве). Это можно решить перемещением $inject выше, чем возращаемое значение, либо использовать альтернативный синтаксис массива вставок. - - Замечание: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) ввело возможность, когда `$inject` переносится туда, где оно доступно. - - ```javascript - // внутри определения директивы - function outer() { - return { - controller: DashboardPanel, - }; - - DashboardPanel.$inject = ['logger']; // Недоступный код - function DashboardPanel(logger) { - } - } - ``` - - ```javascript - // внутри определения директивы - function outer() { - DashboardPanel.$inject = ['logger']; // Доступный код - return { - controller: DashboardPanel, - }; - - function DashboardPanel(logger) { - } - } - ``` - -### Определяйте Маршрутные Обработчики Зависимостей Вручную -###### [Style [Y092](#style-y092)] - - - Используйте $inject, чтобы вручную определить ваш маршрутный обработчик зависимостей для компонентов AngularJS. - - *Почему?*: Эта техника убирает анонимные функции для маршрутных обработчиков, делая чтение такого кода проще. - - *Почему?*: Оператор $inject` может предшествовать обработчику для того, чтобы сделать любую минификацию зависимостей безопасной. - - ```javascript - /* рекомендовано */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Используйте [ng-annotate](//github.com/olov/ng-annotate) для [Gulp](http://gulpjs.com) или [Grunt](http://gruntjs.com) и комментируйте функции, которые нуждаются в автоматической вставке зависимостей, используйте `/** @ngInject */`. - - *Почему?*: Это гарантирует, что в вашем коде нет зависимостей, которые не используют защиту для повреждений от минификации. - - *Почему?*: [`ng-min`](https://github.com/btford/ngmin) не рекомендуется для применения, выводится из употребления - - >Я предпочитаю Gulp, так как для меня он проще для чтения, написания кода и отладки. - - Следующий код не использует защиту зависимостей от минификации. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Если код выше запустить через ng-annotate, то будет произведен код с аннотацией `$inject`, и код станет устойчив к минификации. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Замечание: Если `ng-annotate` обнаруживает вставки которые уже сделаны (например `@ngInject` был обнаружен), он не будет дублирован в коде `$inject`. - - Замечание: Если используется маршрутный обработчик, то вы можете перед встраиваемой функцией подставить `/* @ngInject */` и это будет производить корректный аннотационный код, делающий каждую вставленную зависимость безопасной для минификации. - - ```javascript - // Используем @ngInject аннотацию - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Замечание: Начиная с Angular 1.3 используйте [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) директивный параметр `ngStrictDi`. При наличии инжектора будет создан режим "strict-di", который не даст приложению работать, если обнаружит функции, которые не используют явные аннотации (например, для защиты от минификации). Отладочная информация будет отображаться в консоли, чтобы помочь разработчику выявить код, ломающий приложение. - `` - -### Используйте Gulp или Grunt для ng-annotate -###### [Style [Y101](#style-y101)] - - - Используйте [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) или [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) для автоматических билдов. Вставляйте `/* @ngInject */` перед любой функцией, которая имеет зависимости. - - *Почему?*: ng-annotate будет отлавливать большинство зависимостей, но иногда требуется вставлять подсказки в виде синтаксиса `/* @ngInject */`. - - Следующий код является примером gulp-задания с использованием ngAnnotate. - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[К Содержанию](#table-of-contents)** - -## Exception Handling - -### декораторы -###### [Style [Y110](#style-y110)] - - - Используйте [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), во время конфигурации, применяя сервис [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), пользовательские действия будут происходить в сервисе [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler), если произойдут исключения. - - *Почему?*: Это дает постоянный надежный способ обработки необработанных исключений Angular во время разработки и во время выполнения. - - Замечание: Другим способом является переопределение сервиса, вместо использования декоратора. Это прекрасный способ, но если вы хотите сохранить поведение по умолчанию, и просто дополнить это поведение, то декоратор крайне рекомендуем. - - ```javascript - /* рекомендовано */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Здесь можно добавить ошибку в сервисную коллекцию, - * добавить ошибки в $rootScope, логировать ошибки на удаленный сервер - * или записывать их локально. Или просто бросить ошибку дальше. Это полностью зависит от вас. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Обработчики Исключений -###### [Style [Y111](#style-y111)] - - - Создайте фабрику, которая предоставит интерфейс для перехвата и изящной обработки исключений. - - *Почему?*: Это дает постоянный и надежный способ для перехвата исключений, которые могут возникнуть в вашем коде (например, во время вызовов объекта XHR или сбоев в работе объектов promise). - - Замечание: Перехватчик исключений хорош для отлавливания и реагирования на специфические исключения для вызовов, про которые вы точно знаете, что они могут бросить только одно исключение. Например, вы делаете вызов XHR для получения данных с удаленного сервера и хотите поймать все исключения этого сервиса и обработать их индивидуально. - - ```javascript - /* рекомендовано */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Маршрутные ошибки -###### [Style [Y112](#style-y112)] - - - Обрабатывайте и логгируйте все маршрутные ошибки используя [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Почему?*: Это дает постоянный и устойчивый способ обработки всех маршрутных ошибок. - - *Почему?*: Потенциально мы получим лучший способ обработки маршрутных ошибок и научимся перенаправлять их на более дружественный экран описания ошибки с большими подробностями и указаниями к восстановлению до нормальной ситуации. - - ```javascript - /* рекомендовано */ - function handleRoutingErrors() { - /** - * Отмена маршрута: - * Во время маршрутной ошибки, мы переходим на информационную панель. - * Не забудьте реализовать выход, если происходит попытка перехода дважды. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - var destination = (current && (current.title || current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + (rejection.msg || ''); - /** - * Опционально мы можем записать логи, используя пользовательский сервис или $log. - * (Не забудьте инжектировать пользовательский сервис) - */ - logger.warning(msg, [current]); - } - ); - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Naming - -### Рекомендации для именований -###### [Style [Y120](#style-y120)] - - - Используйте согласованные имена для всех компонентов по шаблону, который описывает особенность(feature) компонента, а затем (опционально) его тип. Я рекомендую шаблон - `feature.type.js`. Существует два типа имен для большинства случаев: - * имя файла (`avengers.controller.js`) - * имя компонента, которое зарегистрировано Angular (`AvengersController`) - - *Почему?*: Соглашения об именованиях дает постояннный надежный способ поиска содержимого быстрым беглым взглядом. Согласованность в проекте жизненно важна. Согласованность в команде очень важна. Согласованность между компаниями дает огромную эффективность. - - *Почему?*: Соглашения об именованиях просто должны помочь найти вам свой код быстрее, и сделать его проще для понимания. -### Характерные Имена Файлов -###### [Style [Y121](#style-y121)] - - - Используйте согласованные имена для всех компонентов используя шаблон, который описывает компонентную особенность, затем опционально его тип. Я рекомендую шаблон - `feature.type.js`. - - *Почему?*: Это надежный способ для быстрой идентификации компонентов. - - *Почему?*: Автоматизирует рабочий процесс. - - ```javascript - /** - * общие настройки - */ - - // Контроллеры - avengers.js - avengers.controller.js - avengersController.js - - // Сервисы/Фабрики - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * рекомендовано - */ - - // контроллеры - avengers.controller.js - avengers.controller.spec.js - - // сервисы/фабрики - logger.service.js - logger.service.spec.js - - // константы - constants.js - - // определение модуля - avengers.module.js - - // маршруты - avengers.routes.js - avengers.routes.spec.js - - // конфигурация - avengers.config.js - - // директивы - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Замечание: Другим общим соглашением является именование файлов контроллера без слова `controller`, например, называем файл `avengers.js` вместо `avengers.controller.js`. Все остальные соглашения все же должны содержать суффикс типа. Просто контроллеры наиболее общий тип компонентов, и таким образом, мы экономим время печатая имя, но контроллеры все равно прекрасно идентифицируются. Я рекомендую выбрать один тип соглашения и быть на одной волне со своей командой. - - ```javascript - /** - * рекомендовано - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Имена Тестовых Файлов -###### [Style [Y122](#style-y122)] - - - Имя тестовой спецификации подобно имени компонента, которая его тестит, только к ней еще добавляется суффикс `spec`. - - *Почему?*: Это надежный способ для быстрой идентификации компонентов. - - *Why?*: Это шаблон соответствующий [karma](http://karma-runner.github.io/) или другим движкам для запуска тестов. - - ```javascript - /** - * рекомендовано - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Имена Контроллеров -###### [Style [Y123](#style-y123)] - - - Используйте согласованные имена для всех контроллеров, именованных по их характерной особенности. Используйте UpperCamelCase (ВерхнийВерблюжийРегистр) для контроллеров, так как они являются конструкторами. - - *Почему?*: Это дает надежный и понятный способ для быстрой идентификации и применения контроллеров. - - *Почему?*: UpperCamelCase является традиционным форматом для идентификации объектов, которые могут быть созданы с помощью конструктора. - - ```javascript - /** - * рекомендовано - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengers', HeroAvengers); - - function HeroAvengers() { } - ``` - -### Суффикс Имени Контроллера -###### [Style [Y124](#style-y124)] - - - Добавляйте к имени контроллера суффикс `Controller` или не добавляйте. Выберите одно правило и придерживайтесь его везде. - - *Почему?*: Суффикс `Controller` более общеупотребим и наиболее явно описателен. - - *Почему?*: Если не указывать суффикс, то получатся более краткие имена, но контроллеры все равно будут легко идентифицируемы, даже без суффикса. - - ```javascript - /** - * рекомендовано: Вариант 1 - */ - - // avengers.controller.js - angular - .module - .controller('Avengers', Avengers); - - function Avengers() { } - ``` - - ```javascript - /** - * рекомендовано: Вариант 2 - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Имена Фабрик -###### [Style [Y125](#style-y125)] - - - Используйте согласованные имена для всех фабрик, именуйте их по характерной особенности. Используйте camel-casing для сервисов и фабрик. - - *Почему?*: Это дает надежный и понятный способ для быстрой идентификации и применения фабрик. - - ```javascript - /** - * рекомендовано - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - -### Имена Директивных Компонент -###### [Style [Y126](#style-y126)] - - - Используйте согласованные имена для всех директив, применяя camel-case. Добавляйте короткий префикс для описания области, которой эти директивы принадлежат (иногда это может быть префикс компании, иногда префикс проекта). - - *Почему?*: Это дает надежный и понятный способ для быстрой идентификации и применения компонент. - - ```javascript - /** - * рекомендовано - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // применять так - - function xxAvengerProfile() { } - ``` - -### Модули -###### [Style [Y127](#style-y127)] - - - Если разрабатываются несколько модулей, файл главного модуля будет называться `app.module.js`, а другие модули получат свое название по своему назначению (то что они представляют). Например, модуль администрирования будет назван `admin.module.js`. Соответствующие зарегистрированные имена модулей будут `app` и `admin`. - - *Почему?*: Это дает согласованность для многих модулей приложения, а также позволяет расширяться в огромные приложения. - *Почему?*: Получаем простой способ автоматизировать работу для первоначальной загрузки всех модульных определений, а затем уже остальных файлов angular. - -### Конфигурация -###### [Style [Y128](#style-y128)] - - - Отделяйте конфигурационную информацию от модуля в отдельном файле, называйте такой файл по названию модульного файла. Конфигурационный файл для главного `app` модуля называем `app.config.js` (или просто `config.js`). Конфигурацию для модуля `admin.module.js` называем соответственно `admin.config.js`. - - *Почему?*: Конфигурация отделяется от определения модуля, компонентов и активного кода. - - *Почему?*: Мы получаем идентифицируемое место для установки конфигурации модуля. - -### Маршруты -###### [Style [Y129](#style-y129)] - - - Выделяйте конфигурацию маршрута в свой собственный файл. Примеры могут быть такими: `app.route.js` для главного модуля и `admin.route.js` для модуля `admin`. Даже в маленьких приложениях я предпочитаю такое разделение от остальной конфигурации. - -**[К Содержанию](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Структура вашего приложения должна быть построена таким образом, чтобы вы могли: `L` - размещать ваш код быстро (`L`ocate your code quickly), `I` - идентифицировать код практически с первого взгляда (`I`dentify the code at a glance), `F` - держать структуру плоской, насколько это возможно (keep the `F`lattest structure you can), и стараться оставаться DRY (Don’t Repeat Yourself) - Не Повторяйте Себя - (`T`ry to stay DRY - Don’t Repeat Yourself). Структура должна придерживаться этим основным 4 правилам. - - *Почему LIFT?*: Получаем согласованную структуру, которая хорошо масштабируется, разбита на модули, и легко позволяет увеличить эффективность разработчика, путем быстрого нахождения кода. Другой способ проверить структуру вашего приложения - это спросить себя: Как быстро я могу открыть все сооответствующие файлы, чтобы работать над нужной функциональностью? - - Когда я чувствую, что работать с моей структурой некомфортно, я возвращаюсь к принципам LIFT. - - 1. `L`Размещать наш код легко (`L`ocating our code is easy) - 2. `I`Идентифицировать код быстро (`I`dentify code at a glance) - 3. `F`Держать структуру ровной как можно дольше (`F`lat structure as long as we can) - 4. `T`Старайтесь оставаться DRY или T-DRY (Don’t Repeat Yourself) - Не Повторяйте Себя (`T`ry to stay DRY - Don’t Repeat Yourself) - -### Размещение -###### [Style [Y141](#style-y141)] - - - Размещайте свой код интуитивно, просто и быстро. - - *Почему?*: Я считаю, это должно быть супер важно для проекта. Если команда не может быстро найти файлы, с которыми нужно работать, то команда не будет работать настолько эффективно, насколько это возможно. Такая структура должна быть изменена. Если не можете найти файл по имени, а также сопутствующие файлы, тогда поместите их в наиболее интуитивно подходящее место, рядом друг другом, и это сэкономит кучу времени. Описательная структура папок может помочь с этим. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Идентификация -###### [Style [Y142](#style-y142)] - - - Когда вы смотрите на файл вы должны мгновенно знать и понимать, что он содержит и представляет. - - *Почему?*: Вы потратите меньше времени на охоту и клевание кода, и станете более эффективным. Если для этого потребуется более длинные имена файлов, то пусть будет так. Делайте имена файлов описательными, и держите в файле только один компонент. Не размещайте в файле несколько контроллеров, несколько сервисов или вообще смесь всего. Могут быть конечно отклонения от правила одного компонента в файле, напрмер, когда я прописываю в одном файле очень маленькие сущности, которые связаны друг с другом, но они все равно очень легко идентифицируемы. - -### Плоская структура -###### [Style [Y143](#style-y143)] - - - Держите структуру папок плоской, как можно дольше. Когда у вас больше 7 файлов, начните думать о разделении. - - *Почему?*: Никто не хочет искать файл в семи уровнях папок. Вспомните о меню на веб-сайтах … все что глубже второго уровня требует серьезного размышления. В организации структуры папок нет жестких правил, но если папка содержит 7-10 файлов, нужно делать подпапку. Основывайтесь на уровне вашего комфорта. Используйте плоскую структуру, пока не станет точно очевидно, что нужно создать новую папку (и это поможет соблюсти принципы LIFT). - -### T-DRY (Try to Stick to DRY) T-DRY (Старайтесь придерживаться принципов DRY) -###### [Style [Y144](#style-y144)] - - - Примечание переводчика: Аббревиатура DRY значит Don`t Repeat Yourself (Не повторяйте себя). - - - Придерживайтесь DRY, но не сходите с ума и не жертвуйте читабельностью. - - *Почему?*: Быть DRY - это важно, но не критично, если в жертву приносятся другие принципы LIFT. Поэтому я и назвал это T-DRY (Try DRY - попытайтесь быть DRY). Напрмер, я не хочу печатать session-view.html для представления, потому что и так понятно, что это представление(view). Но если это не очевидно или это определено соглашением, тогда нужно давать полное имя. - -**[К Содержанию](#table-of-contents)** - -## Application Structure - -### Общее руководство -###### [Style [Y150](#style-y150)] - - - Имейте короткую перспективу реализации и долгосрочное видение проекта. Другими словами, начинайте с малого, но держите всегда в голове, куда развивается ваше приложение. Весь код приложения идет в корневую папку `app`. Все содержимое распределяется, как один компонент на один файл. Каждый контроллер, сервис, модуль, представление - каждый в своем файле. Все скрипты сторонних производителей помещаются в другую корневую папку, а не в папку `app`. Я их не писал и я не хочу, чтобы они загромождали мое приложение (`bower_components`, `scripts`, `lib`). - - Замечание: Найти более подробную информацию и объяснение структуры: [тут оригинальная статья о структуре приложения](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Общие элементы -###### [Style [Y151](#style-y151)] - - - Размещайте компоненты, которые определяют общий каркас приложения в папке `layout`. Там могут быть представление-оболочка и контроллер, которые являются контейнером для приложения, навигация, меню, регионы содержимого (content areas) и другие общие элементы. - - *Почему?*: Так организуются все элементы общего назначения, которые размещаются в одном месте и используются во всем приложении. - -### Структура Папки-по-Функциональностям (Folders-by-Feature) -###### [Style [Y152](#style-y152)] - - - Создавайте папки и называйте их по функциональным особенностям, которые они представляют и реализуют. Если папка растет и превышает семь файлов, то начинайте рассматривать возможность создания новой папки. Ваш порог может быть разный, так что регулируйте такую структуру по необходимости. - - *Почему?*: Разработчик легко ориентируется в коде, быстро определяет, что каждый файл реализует. В приложении поддерживается плоская структура, насколько это возможно. Повторяющиеся и избыточные имена отсутствуют. - - *Почему?*: Правила LIFT выполнены все. - - *Почему?*: Помогает уменьшить приложение, путем устранения неразберихи. Содержимое организуется понятно и соблюдаются принципы LIFT. - - *Почему?*: Когда набралось много файлов (более 10), то их размещение в структуре последовательных согласованных папок намного проще, чем в плоской структуре . - - ```javascript - /** - * рекомендовано - */ - - app/ - app.module.js - app.config.js - app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - session-detail.html - session-detail.controller.js - ``` - - ![Пример Структуры Приложения](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Замечание: Не используйте структуру папки-по-типу. Так файлы одной функциональности разбрасываются по нескольким папкам, и далее все быстро становится очень громоздким. Как только в приложении создаются 5, 10, или 25+ представлений и контроллеров (и других компонентов), то работа становится очень сложной, в отличиии от структуры папки-по-функциональностям. - - ```javascript - /* - * избегайте этого - * Альтернативный способ "папки-по-типу". - * Я рекомендую "папки-по-функциональностям". - */ - - app/ - app.module.js - app.config.js - app.routes.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.j - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[К Содержанию](#table-of-contents)** - -## Modularity - -### Много Маленьких Самодостаточных Модулей -###### [Style [Y160](#style-y160)] - - - Создавайте небольшие модули, которые включают в себя одну ответственность. - - *Почему?*: Модульные приложения позволяют легко подключать и отключать модули, это дает командам разработчиков строить вертикальные срезы приложения и раскатывать их постепенно. Это значит, мы можем добавлять новые модули в приложение по мере их готовности. - -### Создайте Модуль Приложения -###### [Style [Y161](#style-y161)] - - - Создайте корневой модуль приложения, который будет собирать вместе все модули и функциональности вашего приложения. Назовите этот модуль именем вашего приложения. - - *Почему?*: Angular специально разработан для поддержки модульности и принципов разделения сущностей. А создание корневого модуля приложения, который связывает все ваши остальные модули вместе, предоставляет очень простой способ добавления и удаления модулей из приложения. - -### Держите Модуль Приложения Тонким -###### [Style [Y162](#style-y162)] - - - Поместите в модуль приложения только логику сборки приложения. Функциональности оставте в их собственных модулях. - - *Почему?*: Добавление дополнительных ролей в корень приложения, типа получения или удаления данных, отображение представлений или другой подобной логики, не связанной со сборкой частей приложения вместе, загрязняет модуль приложения, а сами добавленные функциональности будет труднее повторно использовать или отключить. - - *Почему?*: Модуль приложения становится манифестом, который описывает какие модули подключаются к приложению. - -### Функциональные Области и Модули -###### [Style [Y163](#style-y163)] - - - Создавайте модули, которые представляют функциональные области, такие как папка общей компоновки (layout), повторно используемые и общие сервисы, информационные панели, и специфические функциональности приложения (типа клиенты, администратор, продажи). - - *Почему?*: Автономные модули могут быть добавлены с небольшиими доработками или вообще без них. - - *Почему?*: Спринты или итерации могут сосредоточиться на функциональных областях, и включить их по завершению спринта или итерации. - - *Почему?*: Оформление функциональных областей в модули позволяет проще их тестировать в изолированном или повторно используемом коде. - -### Повторно Используемые Блоки и Модули -###### [Style [Y164](#style-y164)] - - - Создавайте модули, которые представляют из себя повторно используемые блоки приложения для общих сервисов таких как обработка исключений, логгирование, диагностика, безопасность и локальная работа с данными. - - *Почему?*: Эти типы функциональностей нужны во многих приложениях. Поэтому держите их отдельно в своих собственных модулях, применяйте универсально и повторно используйте во многих приложениях. - -### Зависимости модулей -###### [Style [Y165](#style-y165)] - - - Корневой модуль приложения зависит от специфичных функциональных модулей и от любых общих или повторно используемых модулей. - - ![Модульность и Зависимости](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Почему?*: Главный модуль приложения содержит хорошо читаемый манифест функциональностей приложения. - - *Почему?*: Каждая функциональная область содержит манифест, поясняющий от чего она зависит, таким образом эта область может быть подключена к любому приложению вместе с ее зависимостями и все будет работать. - - *Почему?*: Функциональности приложения для внутреннего пользования (intra-app) такие как общие сервисы данных становится проще размещать и делится из модуля `app.core` (выберите это имя для такого модуля). - - Замечание: Это стратегия для согласованности. Здесь очень много хороших вариантов. Выберите тот, который следует правилам зависимостей AngularJS, и его проще поддерживать и масштабировать. - - > Мои структуры слегка отличаются от проекта к проекту, но они всегда следовали этим руководствам структуры и модульности. Реализация может менятся в зависимости от особенностей проекта и команды. Другими словами, не зацикливайтесь на точных воспроизведениях структуры. Регулируйте свою структуру учитывая согласованность, способность поддерживать код и эффективность. - - > В небольшом приложении вы можете конечно объединить все общие зависимости в модуль приложения, где функциональные модули не имеют прямых зависимостей. Это проще поддерживать в маленьких приложениях, использовать это вне приложения будет очень затруднительно. - -**[К Содержанию](#table-of-contents)** - -## Startup Logic - -### Конфигурация -###### [Style [Y170](#style-y170)] - - - Вставьте код в [конфигурацию модуля](https://docs.angularjs.org/guide/module#module-loading-dependencies), который должен быть сконфигурирован перед запуском angular-приложения. Идеальные кандидаты для этого - провайдеры и константы. - - *Почему?*: Чем меньше мест для конфигурации, тем лучше. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Блоки Run -###### [Style [Y171](#style-y171)] - - - Весь код, который должен запуститься, во время старта приложения, должен быть объявлен в фабрике, предоставлен в виде функции, и вставлен в [блок run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Почему?*: Если код поместить сразу в блок run, то его будет тяжело тестить. Размещение кода в фабрике облегчает абстрагирование и использование фиктивных объектов для тестов. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document and $window -###### [Style [Y180](#style-y180)] - - - Используйте [`$document`](https://docs.angularjs.org/api/ng/service/$document) и [`$window`](https://docs.angularjs.org/api/ng/service/$window) вместо `document` и `window`. - - *Почему?*: Эти сервисы являются оберткой среды Angular, они более проще тестируются, чем объекты document и window. Это дает вам возможность не создавать самим фиктивные объекты document and window. - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - Используйте [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) и [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) вместо `setTimeout` и `setInterval` . - - *Почему?*: Эти сервисы являются оберткой среды Angular, они более легко тестируемы и еще они обрабатываются циклом digest среды AngularJS, таким образом синхронизируя данные. - -**[К Содержанию](#table-of-contents)** - -## Testing -Модульное тестирование помогает поддерживать чистый код. Я включил некоторые мои рекомендации по основам модульного тестирования в виде ссылок для более подробной информации. - -### Пишите Тесты с Историями -###### [Style [Y190](#style-y190)] - - - Пишите набор тестов для каждой истории. Начните пустой тест и заполняйте его по мере написания кода для истории. - - *Почему?*: Описания тестов помогают ясно определить, что ваша история будет делать, чего не будет, и как вы можете оценить успешность тестов. - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Библиотеки для тестирования -###### [Style [Y191](#style-y191)] - - - Используйте [Jasmine](http://jasmine.github.io/) или [Mocha](http://mochajs.org) для модульного тестирования. - - *Почему?*: И Jasmine и Mocha широко распространены в сообществе AngularJS. Обе они стабильны, хорошо поддерживаются, и предоставляют отличные возможности для тестирования. - - Замечание: Если используется Mocha, то дополнительно нужно использовать assert-библиотеку, например [Chai](http://chaijs.com). - -### Движок Запуска Тестов -###### [Style [Y192](#style-y192)] - - - Используйте [Karma](http://karma-runner.github.io) в качестве движка для запуска тестов. - - *Почему?*: Karma просто конфигурируется, она просто запускается вручную или автоматически (как только вы измените код). - - *Почему?*: Karma просто внедряется в ваш процесс Continuous Integration, как самостоятельно, так и через Grunt или Gulp. - - *Почему?*: Некоторые средства разработки (IDE) начали интегрировать в себя библиотеку Karma, это - [WebStorm](http://www.jetbrains.com/webstorm/) и [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Почему?*: Karma очень хорошо работает с такими лидерами автоматизации как [Grunt](http://www.gruntjs.com) (вместе [grunt-karma](https://github.com/karma-runner/grunt-karma)) и [Gulp](http://www.gulpjs.com) (вместе [gulp-karma](https://github.com/lazd/gulp-karma)). - -### Stubbing и Spying -###### [Style [Y193](#style-y193)] - - - Используйте [Sinon](http://sinonjs.org/) для stubbing и spying. - - *Почему?*: Sinon хорошо работает и с Jasmine и с Mocha. Он расширяет возможности stubbing и spying, которые предлагают Jasmine и Mocha. - - *Почему?*: Sinon просто переключается между Jasmine и Mocha, это если вы хотите использовать сразу обе библиотеки. - -### Невизуальный Браузер -###### [Style [Y194](#style-y194)] - - - Используйте [PhantomJS](http://phantomjs.org/) для запуска тестов на сервере. - - *Почему?*: PhantomJS - невизуальный браузер, который помогает запускать тесты, не используя обычные визуальные браузеры. Таким образом, вам не нужно устанавливать на ваш сервер Chrome, Safari, IE, или другие браузеры. - - Замечание: Все же проведите тесты на всех браузерах, особенно на браузерах вашей целевой аудитории. - -### Анализ Кода -###### [Style [Y195](#style-y195)] - - - Запустите JSHint на ваших тестах. - - *Почему?*: Тесты это код. JSHint помогает идентифицировать проблемы качества кода, которые могут причиной некорректной работы тестов. - -### Разрешите Глобальные Переменные для Правил JSHint на Тестах -###### [Style [Y196](#style-y196)] - - - Смягчите правила для кода ваших тестов, чтобы разрешить общие глобальные переменные такие как `describe` и `expect`. - - *Почему?*: Ваши тесты - это код и он требует того же самого внимания и соблюдения правил качества, как и весь ваш рабочий код. Все же, глобальные переменные используются средой тестирования, и правила для них нужно ослабить в спецификации тестов. - - ```javascript - /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ - ``` - - ![Средства Тестирования](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Организация Тестов -###### [Style [Y197](#style-y197)] - - - Размещайте файлы модульных тестов (specs) рядом со своим клиентским кодом. А тестовые файлы (specs), которые покрывают интеграцию с сервером или тестируют сразу несколько компонентов, в отдельной папке `tests`. - - *Почему?*: Модульные тесты имеют прямое отношение к определенному компоненту и файлу с исходным кодом. - - *Почему?*: Так легче держать тесты актуальными, потому что они всегда на виду. Во время написания кода, применяете ли вы технику TDD или тестируете во время разработки или тестируете после разработки, тестовые файлы (specs) всегда рядом, они никогда не пропадают не из вида не из мыслей. Таким образом, тестам будет всегда уделено внимание, а это поможет поддерживать покрытие кода тестами. - - *Почему?*: Когда вы изменяете исходный код, всегда проще сразу обновить тесты. - - *Почему?*: Размещение файла кода и теста рядом упрощает их поиск и в случае необходимости, перенос в другое место обоих файлов не составляет большого труда. - - *Почему?*: Отделить тестовые файлы(specs), так чтобы они не попали в рабочую сборку можно с помощью grunt или gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[К Содержанию](#table-of-contents)** - -## Animations - -### Применение -###### [Style [Y210](#style-y210)] - - - Используйте subtle [анимации AngularJS](https://docs.angularjs.org/guide/animations) чтобы перемещать состояния представлений и первичные визуальные элементы. Подключите [модуль ngAnimate](https://docs.angularjs.org/api/ngAnimate). Есть три ключа - тонкий (subtle), плавный (smooth), цельный (seamless). - - *Почему?*: При правильном использовании тонкая анимация может улучшить удобство работы. - - *Почему?*: Тонкие анимации могут улучшить воспринимаемую эффективность при изменениии представлений. - -### Длительность Анимаций -###### [Style [Y211](#style-y211)] - - - Используйте короткую длительность анимаций. Я в основном начинаю с 300 миллисекунд и регулирую до нужного состояния. - *Почему?*: Долгие анимации могут иметь обратный эффект для пользователя, приложение будет восприниматься как медленно работающее. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Используйте [animate.css](http://daneden.github.io/animate.css/) для обычных анимаций. - - *Почему?*: Анимации, предоставляемые animate.css, быстрые, плавные и их легко добавить в приложение. - - *Почему?*: Это дает согласованность ваших анимаций. - - *Почему?*: animate.css широко используем и оттестирован. - - Замечание: Посмотрите эту [замечательную статью от Matias Niemelä об анимациях AngularJS](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[К Содержанию](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-y220)] - - - Если планируется производство документации, используйте синтаксис [`jsDoc`](http://usejsdoc.org/) для документирования имен функций, описаний, параметров и возвращаемых значений. Используйте `@namespace` и `@memberOf` для соответствия структуре приложения. - - *Почему?*: Вы можете сгенерировать (и перегенерировать) документацию из вашего кода, чтобы не писать ее с нуля. - - *Почему?*: С помощью общеиспользуемого промышленного инстумента вы получаете согласованность. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[К Содержанию](#table-of-contents)** - -## JS Hint - -### Используйте Файл Настроек -###### [Style [Y230](#style-y230)] - - - Используйте JS Hint для проверки вашего кода JavaScript и проверьте файл настроек самого JS Hint, а также включите этот файл с систему управления исходным кодом (source control). Смотрите [JS Hint docs](http://www.jshint.com/docs/) для точного описания настроек. - - *Почему?*: Если код некорректен, то получаем предупреждения, перед тем, как отправить изменения в систему управления исходным кодом. - - *Почему?*: Обеспечивается согласованность для всей команды. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[К Содержанию](#table-of-contents)** - -## Constants - -### Глобальные Переменные Сторонних Производителей (Vendors) -###### [Style [Y240](#style-y240)] - - - Создайте константы Angular для глобальных переменных из библиотек сторонних производителей. - - *Почему?*: Предоставляет способ подключить сторонние библиотеки, которые являются глобальными переменными. Это улучшает тестируемость кода, позволяя вам проще узнать, какие зависимости есть у ваших компонентов. Это также позволит вам, создать фиктивные объекты этих зависимостей, если это нужно. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Используйте константы для значений, которые не изменяются и не приходят из другого сервиса. Если константы используются в модуле, который может быть использован в нескольких приложениях, то поместите константу в файле, названному по имени модуля. В противном случае держите константы в главном модуле в файле `constants.js`. - - *Почему?*: Значение может измениться, возможно это редкая ситуация, но допустим сервис возвращает нам значение, и таким образом мы не будем менять наш рабочий код, использующий этот сервис. Напрмер, url для сервиса данных мог бы быть помещен в константы, но лучше загружать его из веб сервиса. - - *Почему?*: Константы могут быть инжектированы в любой angular-компонент, включая провайдеры. - - *Почему?*: Когда приложение разделено на модули, которые могут быть использованы в других приложениях, каждый отдельный модуль должен быть способен оперировать своими собственными зависимыми константами. - - ```javascript - // Константы используются во всем приложении - angular - .module('app.core') - .constant('moment', moment); - - // Константы используются в модуле продаж - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[К Содержанию](#table-of-contents)** - -## File Templates and Snippets -Используйте шаблоны файлов и сниппеты, чтобы соблюсти согласованность стилей и инструкций. Здесь есть шаблоны и/или сниппеты для некоторых веб-редакторов и интегрированных средств разработки (IDE). - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular сниппеты, которые соблюдают приведенные здесь стили и руководства. - - - Скачайте [Sublime Angular сниппеты](assets/sublime-angular-snippets.zip?raw=true) - - Поместите все в вашу папку Packages - - Перезапустите Sublime - - В файле JavaScript напечатайте следующие команды после клавиши `TAB` - - ```javascript - ngcontroller // создает контроллер Angular - ngdirective // создает директиву Angular - ngfactory // создает фабрику Angular - ngmodule // создает модуль Angular - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Шаблоны файлов AngularJS, которые соблюдают приведенные здесь стили и руководства, можно найти на [SideWaffle](http://www.sidewaffle.com) - - - Скачайте [SideWaffle](http://www.sidewaffle.com) это расширение к Visual Studio (файл vsix) - - Запустите файл vsix - - Перезапустите Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Angular сниппеты и шаблоны файлов, которые соблюдают приведенные здесь стили и руководства. Вы можете импортировать их в свои настройки WebStorm: - - - Скачайте [WebStorm Angular шаблоны файлов и сниппетов](../assets/webstorm-angular-file-template.settings.jar?raw=true) - - Откройте WebStorm и перейдите в меню `File` - - Выберите пункт меню `Import Settings` - - Выберите файл и нажмите `OK` - - В файле JavaScript напечатайте следующие команды после клавиши `TAB`: - - ```javascript - ng-c // создает контроллер Angular - ng-f // создает фабрику Angular - ng-m // создает модуль Angular - ``` - -**[К Содержанию](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -Вы можете использовать [HotTowel yeoman generator](http://jpapa.me/yohottowel) для создания приложений Angular, которые являются стартовой точкой разработки и соблюдают правила данного руководства. - -1. Установите generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Создайте новую папку и перейдите в нее. - - ``` - mkdir myapp - cd myapp - ``` - -3. Запустите генератор - - ``` - yo hottowel helloWorld - ``` - -**[К Содержанию](#table-of-contents)** - -## Routing -Клиентская маршрутизация важна для создания навигации между представлениями, а также компоновкой представлений, которые состоят из небольших маленьких шаблонов и директив. - -###### [Style [Y270](#style-y270)] - - - Используйте [AngularUI Router](http://angular-ui.github.io/ui-router/) для клиентской маршрутизации. - - *Почему?*: UI Router реализует все возможности маршрутизатора Angular, плюс предлагает дополнительную функциональность, включая вложенные маршруты и состояния. - - *Почему?*: Синтаксис почти такой же, как и у маршрутизатора Angular, и несложно мигрировать на UI Router. - -###### [Style [Y271](#style-y271)] - - - Определяйте маршруты для всех представлений в модуле, где они есть. Каждый модуль должен содержать маршруты для всех своих представлений. - - *Почему?*: Каждый модуль должен быть независим. - - *Почему?*: Когда мы удаляем или добавляем модуль, то приложение должно содержать только те маршруты, которые указывают на существующие представления. - - *Почему?*: Так мы можем включать или исключать части приложения, не заботясь о том, что у нас останутся маршруты на несуществующие представления. - -**[К Содержанию](#table-of-contents)** - -## Task Automation -Используйте [Gulp](http://gulpjs.com) или [Grunt](http://gruntjs.com) для создания автоматизированных процессов. Gulp работает по принципу код главнее конфигурации, а Grunt наоборот конфигурация главнее кода. Я лично предпочитаю Gulp, так как мне кажется его проще читать и писать, но на самом деле они оба отличные инструменты автоматизации. - -###### [Style [Y400](#style-y400)] - - - Используйте автоматизацию для сборки всех файлов с определениями модуля `*.module.js` перед всеми остальными JavaScript-файлами приложения. - - *Почему?*: Angular должен зарегистрировать все определения модулей, перед тем как их использовать. - - *Почему?*: Именование модулей по специальному шаблону `*.module.js` упрощает их поиск и сборку в единую группу, для того чтобы подключить их первыми. - - ```javascript - var clientApp = './src/client/app/'; - - // Всегда собираем файлы модулей первыми - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[К Содержанию](#table-of-contents)** - -## Angular Docs -Для дополнительной информации, описания API, смотрите [документацию Angular](//docs.angularjs.org/api). - -## Contributing - -Сначала откройте issue и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда дежать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. - -*Добавляя материал в данное хранилище вы согласны с тем, ваше содержимое будет доступно согласно приведенной ниже лицензии.* - -### Процесс - 1. Обсудите изменения в Issue. - 2. Откройте Pull Request, сделайте ссылку на issue, объясните изменения и их ценность. - 3. Pull Request будет проверен и далее принят или отклонен. - -## License - -_tldr; Use this guide. Attributions are appreciated._ - -### (The MIT License) - -Copyright (c) 2014 [John Papa](http://johnpapa.net) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[К Содержанию](#table-of-contents)** diff --git a/a1/i18n/tr-TR.md b/a1/i18n/tr-TR.md deleted file mode 100644 index bcc73b64..00000000 --- a/a1/i18n/tr-TR.md +++ /dev/null @@ -1,3233 +0,0 @@ -# Angular Stil Rehberi - -## Angular Ekibinden Destek -Angular takım lideri Igor Minar'a, rehberimi incelediği, geri bildirimde bulunduğu ve rehber olma görevini bana emanet ettiği için özellikle teşekkür ederim. - -##Amaç -*[@john_papa](//twitter.com/john_papa)'dan Takımlar için seçeneklendirilmiş stil rehberi* - -Eğer Angular projeleriniz için seçeneklendirilmiş bir sintaks, yöntem ve yapılandırma rehberi arıyorsanız, buyrun gelin. Bu stiller benim [Angular](//angularjs.org) sunumlarım, [Pluralsight eğitim kurslarım](http://pluralsight.com/training/Authors/Details/john-papa) ve takım çalışmalarımdan edindiğim deneyimlerle oluşturulmuştur. - -Bu rehberin amacı, kullandığım yöntemleri göstererek, hatta daha önemlisi neden bu yöntemleri seçtiğimi açıklayarak, Angular uygulamalarınızı geliştirirken size yol göstermektir. - ->Eğer bu rehberi beğendiyseniz, [Angular Patterns: Clean Code](http://jpapa.me/ngclean) isimli kursuma Pluralsight sitesinden bir bakın. Bu rehberle pekiltirici olacaktır. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Topluluğun Aşmışlığı ve Referanslar -Asla izole olarak çalışmayın. Angular topluluğunu, deneyimlerini paylaşma konusunda tutukulu buluyorum. Örneğin, arkadaşım ve bir Angular uzmanı Todd Motto ile birçok stil ve yöntem üzerinde işbirliği yaptık. Birçoğunda hemfikir olduk, birkaçında farklı yollar izledik. [Todd'un rehberi'ni](https://github.com/toddmotto/angularjs-styleguide) de onun yaklaşımını anlamak ve karşılaştırma yapmak için incelemenizi öneririm - -Bir çok yöntem [Ward Bell](http://twitter.com/wardbell) ile yaptığımız eşli programlama seanslarında ortaya çıktı. Arkadaşım Ward bu rehberin nihai evrimine büyük katkılarda bulundu. - -## Örnek uygulama üzerinde yöntemler -Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deneyimlemenin yardımcı olacaığını düşünüyorum. Bu rehbere, bu rehberdeki yöntemleri ve tasarım desenlerini kullanan örnek bir uygulama eşlik ediyor. Bu uygulamayı [burada](https://github.com/johnpapa/ng-demos), `modular` klasörünün altında bulabilirsiniz. Üzerinde denemeler yapmaktan çekinmeyin. [Çalıştırma talimatları readme dosyasındadır](https://github.com/johnpapa/ng-demos/tree/master/modular). - -##Çeviriler -[Bu Angular rehberinin çevirileri](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) gönüllü yardımcılar tarafından sağlanmaktadır - -## İçerik Listesi - - 1. [Tek İşlevsellik](#tek-islevsellik) - 1. [IIFE](#iife) - 1. [Modüller](#moduller) - 1. [Controller'lar](#controllerlar) - 1. [Servisler](#servisler) - 1. [Factory'ler](#factoryler) - 1. [Veri Servisleri](#veri-servisleri) - 1. [Directive'ler](#directiveler) - 1. [Promise'leri Controller'lar İçin Çözümlemek](#promiseleri-controllerlar-icin-cozumlemek) - 1. [Dependency Injection ve Manuel Annotation](#dependency-injection-ve-manuel-annotation) - 1. [Minification ve Annotation](#minification-ve-annotation) - 1. [Exception Yakalama](#exception-yakalama) - 1. [İsimlendirme](#isimlendirme) - 1. [Uygulama Yapısı ve LIFT Prensibi](#uygulama-yapisi-ve-lift-prensibi) - 1. [Uygulama Yapısı](#uygulama-yapisi) - 1. [Modülerlik](#modulerlik) - 1. [Başlangıç Mantığı](#baslangic-mantigi) - 1. [Angular Servisleri](#angular-servisleri) - 1. [Testler](#testler) - 1. [Animasyonlar](#animasyonlar) - 1. [Yorumlar](#yorumlar) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constant'lar](#constantlar) - 1. [Dosya Şablonları ve Snippetler](#dosya-sablonlari-ve-snippetler) - 1. [Yeoman Anayapı Üreticisi](#yeoman-anayapı-ureticisi) - 1. [Routing](#routing) - 1. [Görev Otomasyonu](#gorev-otomasyonu) - 1. [Filtreler](#filtreler) - 1. [Angular Dökümantasyonu](#angular-dokumantasyonu) - 1. [Katkıda Bulunmak](#katkida-bulunmak) - 1. [Lisans](#lisans) - -## Tek İşlevsellik - -### Kural 1 -###### [Stil [Y001](#style-y001)] - - - Her dosyaya yalnızca bir component tanımlayın. - Göreceğimiz örnek `app` modülünü ve bağımlılıklarını, conroller'ını ve factory'sini aynı dosyada tanımlıyor. - - ```javascript - /* sakınılacak stil */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Bu örnekte ise aynı component'lar farklı dosyalara ayrılmış durumdalar - - ```javascript - /* önerilen stil */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* önerilen stil */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* önerilen stil */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## IIFE -### JavaScript Kapsamları(Closures) -###### [Stil [Y010](#style-y010)] - - Angular component'larınızı Hemen Çalışan Fonksiyon İfadeleri (HÇFİ) ile kapsayın - > *Not: Hemen Çalışan Fonksiyon İfadeleri İngilizcede (Immediately Invoked Function Expression) olarak geçer. Bu fonksiyon bloğu içerisinde kalan kısım, tanımlanmasının ardından hemen çalıştırılır, fonksiyonun çağrılmasını beklemez* - - *Neden?*: HÇFİ değişkenleri global olarak tanımlanmaktan çıkarır. Bu yöntem değişkenlerin ve fonksiyonların global olarak beklenenden daha uzun tanımlı kalmasını ve aynı isimde olan değişken ve fonksiyonlarla çakışmasını engeller. - - *Neden?*: Kodunuz sıkıştırıldığı zaman ve üretim ortamın için tek bir javascript dosyası halinde paketlendiğinde, birçok yerel ve global değişken için çakışma hataları alabilirsiniz. HÇFİ sizi bu çakışmalara karşı korur ve her dosya için kendi değişken kapsamını tanımlar. - - ```javascript - /* sakınılacak stil */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger fonksiyonu global olarak tanımlanıyor - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage fonksiyonu global olarak tanımlanıyor - function storage() { } - ``` - - ```javascript - /** - * önerilen stil - * - * global tanımlamamız yok - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Not: Dökümanın sadeliğini korumak namına, bundan sonraki örneklerin HÇFİ fonksiyonları içinde tanımlandığını farzedin. - - - Not: HÇFİ'ler test kodunuzun fonksiyona özel değişkenlere erişmenizi engeller (Regular Expression, Yardımcı fonksiyonlar gibi). O yüzden bu fonksiyonları kendi başlarına test etmek daha iyidir. Ama yine de bu özel fonksiyonları component dışından erişilebilir kılarak test edebilirsiniz. - -**[İçerik Listesi](#icerik-listesi)** - -## Modüller - -### İsim Çakışmalarından Kaçının -###### [Stil [Y020](#style-y020)] - - - Alt modüller için eşsiz isimlendirme yöntemleri kullanın. - - *Neden?*: Eşsiz isimler modül isimlerinin çakışmasını engeller. Ayraçlar, modüller ve alt modüller arasındaki hiyerarşiyi kurmaya yardımcı olur. Örneğin `app` sizin ana modülünüz olsun. `app.dashboard` ve `app.users` modülleri alt modülleriniz olur ve `app` modülüne bağımlılık olarak eklenirler. - -### Modül Tanımlama Yöntemi (Setters) -###### [Stil [Y021](#style-y021)] - - - Modüllerinizi bir değişkene atama yapmadan setter sintaksını kullanarak tanımlayın. - - *Neden?*: Her component için bir dosya yöntemi ile, nadiren modülünüzü bir değişkene atama ihtiyacı hissedersiniz. - - ```javascript - /* kaçınılacak stil */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Setter sintaksı kullanımı - - ```javascript - /* önerilen stil */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Modüle Ulaşma Yöntemi -###### [Stil [Y022](#style-y022)] - - - Modülünüze ulaşıp kullanırken değişkene atamak yerine getter sintaksını zincirleyerek kullanın. - - *Neden?*: Bu yöntem kodunuzu daha okunabilir kılar ve değişken çakışmalarını ve sızıntılarını engeller. - - ```javascript - /* kaçınılacak stil */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* önerilen stil */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Yaratma ve Kullanma -###### [Stil [Y023](#style-y023)] - - - Modülünüzü sadece bir kere yaratın ve diğer durumlar için getter sintaksını kullanın. - - *Neden?*: Modül sadece birkere yaratılmalıdır. Sonrasında bu yaratılan modül kullanılırç - - ```javascript - /* önerilen stil */ - - // modül yaratılır - angular.module('app', []); - - // modül kullanılır - angular.module('app'); - ``` - -### İsimli ve Anonoim Fonksiyonlar -###### [Style [Y024](#style-y024)] - - - Modülünüzün component'lerinin fonksiyonlarını isimli fonksiyonlar olarak tanımlayın. - - *Neden?*: Bu yöntem kodunuzu daha okunabilir kılar ve hata ayıklamak için kolaylık sağlar. Ayrcıa iç içe geçmiş fonksiyon bloklarının önüne geçcer. - - ```javascript - /* kaçınılacak stil */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* önerilen stil */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Controller'lar - -### controllerAs View Sintaksı -###### [Stil [Y030](#style-y030)] - - - [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) sintaksını klasik $scope'lu controller sintaksına tercih edin. - - *Neden?*: Controller'lar `new` kelimesi ile yaratılır ve uygulamanız içerisinde sadece bir örneği bulunur. `controllerAs` yöntemi JavaScript'in constructor yapısına daha yakındır. - - *Neden?*: View katmanında noktalı notasyonun kullanımını teşvik eder. (örneğin `customer.name` yerine `name`). Bu yöntem daha kolay okunur ve referans problemlerinin oluşmasını engeller. - - *Neden?*: İçiçe olan controller'larda veriye ulaşırken `$parent` kullanmanızı engeller. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Sintaksı -###### [Stil [Y031](#style-y031)] - - - `controllerAs` sintaksını klasik $scope'lu controller sintaksına tercih edin. - - - `controllerAs` sintaksı controller içerisinde `this` kelimesini kullanır ve $scope'a bağlanırç - - *Neden?*: `controllerAs` `$scope` için bir sintaks süslemedir. Hala View'a bağlayabilir ve `$scope` fonksiyonlarına ulaşabilirsiniz. - - *Neden?*: `$scope` metodlarının bir Factory içerisinde tanımlanıp controller içerisinde çağrılmasındansa, controller içerisinde direk kullanılması eğilimine engel olur. `$scope`'u controller içerisinde sadece ihtiyaç olduğunda kullanın. Örneğin [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast) veya [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) kullanırkenö bunları bir factory içine taşıyıp, controller içerisinden çağırın. - - ```javascript - /* kaçınılan stil */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* önerilen stil - ama bir sonraki bölüme bakın */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### vm ile controllerAs -###### [Stil [Y032](#style-y032)] - - - `controllerAs` sintaksını kullanırken `this` kelimesi için yakalayıcı bir değişken kullanın. `vm` gibi tutarlı bir isim seçin. `vm`, ViewModel'in kısaltılmışıdır. - - *Neden?*: `this` kelimesi kullanıldığı yere göre içeriğini değiştirebilir. Baştan yakalayıcı bir değişkene atayarak hep aynı içeriği tutması sağlanır. - - ```javascript - /* kaçınılan stil */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* önerilen stil */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Not: [jshint](http://www.jshint.com/) uyarılarını kodun üstüne yorum ekleyerek engelleyebilirsiniz. Eğer fonksiyonunu UpperCasing yöntemi ile isimlendirdiyse buna ihtiyaç olmaz. Çünkü bu yöntem bu fonksiyonun bir constructor fonksiyonu olduğunu belirtir, ki Angular controller'ları de bir constructor fonksiyonudur. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Not: vm değişkenlerini izlerken ($watch) aşağıdaki sintaksı kullanabilirsiniz. ($watch yaratırken dikkatli olunmalıdır. Çümkü digest cycle'a yük bindrir.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Bağlanacaklar Yukarı -###### [Style [Y033](#style-y033)] - - - Bağlanacak olan değişkenleri controller fonksiyonuzda aflabetik sıralanmış olarak tepeye koyun. Kod içerisinde dağılmış olarak bırakmayın. - - *Neden?*: Bağlancak değişkenleri tepeye koymak kod okunabilirliğini arttırır ve bir bakışta View'a hangi değişkenlerin bağlanacağını görebilirsiniz. - - *Neden?*: Anonim fonksiyonlar yaratmak hızlı ve kolaydır, ama bu fonksiyonlar bir satırdan fazla olurlarsa okunabilirliği düşürürler. Fonksiyonları bağlanabilir değişkenlerin altında tanımlarsanız (JavaScript'in hoisting özelliği ile yukarı taşınacaklardır) kodun okunması daha kolay olur. - - ```javascript - /* kaçınılacak stil */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* önerilen stil */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - Not: Eğer fonksiyon bir satıra sığıyorsa, okunabilirlik etkilenmez ve bağlanacak değişkenlerle beraber tutulabilir. - - ```javascript - /* kaçınılacak stil */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* önerilen stil */ - function Sessions(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Fonksiyon Tanımlamaları ve İmplementasyon Detaylarının Saklanması -###### [Stil [Y034](#style-y034)] - - - Fonksiyon tanımlamalarınızı implementasyon detaylarını saklamak için kullanın. View'a bağlanacak öğeleri yukarıda tanımlayın. Controller'ınızda bir fonksiyonu bağlama ihtiyacı hissettiğinizde, bu öğeyi bir fonksiyon tanımlamasına eşitleyin. Fonksiyonun implementasyon detaylarını kodun ileriki satırlarında yapın. Bu direk olarak "Bağlanacaklar Yukarı" başlığı ile ilintili. Daha fazla detay için bu [makaleme](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) bakabilirsiniz. - - *Neden?*: Bağlanacak öğeleri yukarı taşımak okumayı kolaylaştırır ve controlller içerisinde hangi öğelerin View'a bağlandığını anında görmemizi sağlar. - - *Neden?*: Fonksiyonun implementasyonunu dosya içerisinde daha aşağılara taşımak kompleks kısımları göz önünden uzak tutar ve asıl önemli olan kısma odaklanmayı sağlarç - - *Neden?*: Fonksiyon tanımlamaları(declerations) JavaScript'in *hoisting* özelliğinden faydalandığı için fonksiyonun tanımlamasından önce çağrılmasından endişe duymaya gerek yoktur. (Fonksiyon eşitlemeleri(expression) için bu durum geçerli değildir) - - *Neden?*: Fonksiyon tanımlamaları ile değişkenlerin yerlerini değiştirirken kodunuz kırılır mı diye endişe duymaya gerek yoktur. - - *Neden?*: Fonksiyon eşitlemelerinde sıra önemlidir. - - ```javascript - /** - * kaçınılacak stil - * Fonksiyon eşitlemeleri kullanmak. - */ - function Avengers(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Bir önceki örnekte önemli olan noktaların kod içerisinde nasıl dağıldığına dikkat edin. Aşağıdaki örnekte, önemli olan kısım yukarıda toplanmıştır. Örneğin, controller'a bağlı `vm.avengers` ve `vm.title` öğeleri. İmplementasyonun detayları aşağıda yer alıyor. Kodu okumak böyle daha kolay. - - ```javascript - /* - * önerilen stil - * Fonksiyon tanımlamaları kullanarak - * bağlanacakları yukarı taşımak. - */ - function Avengers(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Mantıksal kodu Controller'lardan Servislere Kaydırın -###### [Style [Y035](#style-y035)] - - - Controller içerisindeki mantıksal kodu servisler ve factory'ler aracılığıyle yönetin. - - *Neden?*: Mantıksal kod servislere taşınırsa farklı controller'larda tekrar terkrar kullanılabilir. - - *Neden?*: Servise taşınmış mantıksal kod daha kolay test edilebilir ve controller içerisinde kolayca taklit edilebilir(mocking) - - *Neden?*: Controller'dan bağımlılıkları kaldırır ve implementasyon detaylarını gizler. - - *Neden?*: Controller'ı kısa ve öz tutar. - - ```javascript - - /* kaçınılacak stil */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Konfigürasyonlardan URL'yi al - // Gerekli header'ları belirler - // URL'yi gerekli parametrelerle hazırla - // Kullanıcıyı belirleyecek veriyi ekle ki bu kullanıcı için doğru limit bulunsun - // CORS desteklemeyen tarayıcılar için JSONP kullan - return $http.get(settings) - .then(function(data) { - // Response ile gelen JSON objesinden maksimum kalan tutarı al - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Hatayı işlet - // Tekrar dene? Başka bir servisi dene? - // Kullanıcıya anlayabileceği uygun bir hata göster - }); - }; - } - ``` - - ```javascript - /* önerilen stil */ - function Order(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showError); - }; - } - ``` - -### Controller'ın Odağını Koruyun -###### [Stil [Y037](#style-y037)] - - - Bir controller'ı bir view tanımlayın ve başka view'lar için kullanmaya çalışmayın. Onun yerine tekrar kullanılabilir mantıksal kodu farcory'lere taşıyıp, controller'ı sade ve view'a odaklı bırakın. - - *Neden?*: Controller'ları değişik view'larla birlikte kullanmak kodu kırılgan yapar ve iyi bir uçtan uca test kapsamı için kararlılık gereklidir. - -### Controller Atamaları -###### [Stil [Y038](#style-y038)] - - - Eğer bir controller bir view ile eşleşmek zorunda ise ve o view başka controller'lar tarafından da kullanılıyorsa, o zaman controller'ı router serviyesinde tanımlayın. - - Not: Eğer view router dışında başka biryerden yükleniyorsa, view içerisinde `ng-controller="Avengers as vm"` sintaksını kullanın. - - *Neden?*: Controller'ı router ile eşlemek, farklı route'ların farklı controller ve view eşlerini çağırmasına olanak sağlar. Eğer controller [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) yöntemi kullanılarak view ile eşlendiyse, o view hep o controller'ı kullanacaktır. - - ```javascript - /* kaçınılacak stil */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* önerilen stil */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Servisler - -### Singleton -###### [Stil [Y040](#style-y040)] - - - Servisler `new` kelimesi ile yaratılır, Paylaşımlı metod ve değişkenler için `this` kelimesini kullanın. Servisler Factory'lere çok benzedikleri için, tutarlılık açısından servisler yerine factory kullanın. - - Not: [Bütün Angular servisleri singleton yapıdadır](https://docs.angularjs.org/guide/services). Bu yaratılan servisin aynı anda tek bir örneğinin injectörler tarafından kullanıldığı anlamına gelir. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Factory'ler - -### Tek Sorumluluk -###### [Stil [Y050](#style-y050)] - - - Factory'lerin tek sorumluluğu olmalıdır [single responsibility](http://en.wikipedia.org/wiki/Single_responsibility_principle), ve kendi içeriğini kapsamalıdır. Factory sorumluluğunun dışına taşmaya başlarsa, bu yeni sorumluluk için ayrı bir factory yaratılmalıdır. - -### Singleton -###### [Stil [Y051](#style-y051)] - - - Factoryler singleton yapıdadır ve servisin metodları ve değişkenlerinin bulunduğu objeleri dönerler. - - Not: [Bütün Angular servisleri singleton yapıdadır](https://docs.angularjs.org/guide/services). - -### Ulaşılabilirler Yukarı! -###### [Style [Y052](#style-y052)] - - - Servisin çağrılabilen metodlarını [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) yapısını kullanarak kodun tepesinde tanımlayın. - - *Neden?*: Çağrılabilen metodları kodun tepesinde tanımlamak okunabilirliği arttırır ve bir bakışta bu servisin hangi metodlarının dışarıdan çağırılabileceğini anlamamıza yardımcı olur. Ayrıca hangi metodların unit testlerinin yazılması gerektiği hakkında fikir verir. - - *Neden?*: Bu yöntem özellikle dosya uzamaya başladığında daha da yardımcı olur. Hangi metodların çağrılabilir olduğunu görmek için aşağıya kadar kaymamızı engeller. - - *Neden?*: Fonksiyonları olduğu yerde tanımlamak kolay olabilir, ama fonksiyonlar bir satırdan daha uzun olmaya başladıklarında okunabilirliği azaltırlar ve aşağı doğru daha fazla kaydırma yapmanıza sebep olurlar. Çağrılabilecek metodları tepede tanımlayıp implementasyon detaylarını aşağıda yapmak okunabilirliği arttırır. - - ```javascript - /* kaçınılacak stil */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* önerilen stil */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - Primitif değerler revealing module pattern yöntemi kullanıldığında güncellenemezler. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -### Fonksiyon Tanımlamaları ve İmplementasyon Detaylarının Saklanması -###### [Stil [Y053](#style-y053)] - - - Fonksiyon tanımlamalarınızı implementasyon detaylarını saklamak için kullanın. View'a bağlanacak öğeleri yukarıda tanımlayın. Controller'ınızda bir fonksiyonu bağlama ihtiyacı hissettiğinizde, bu öğeyi bir fonksiyon tanımlamasına eşitleyin. Fonksiyonun implementasyon detaylarını kodun ileriki satırlarında yapın. Bu direk olarak "Bağlanacaklar Yukarı" başlığı ile ilintili. Daha fazla detay için bu [makaleme](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) bakabilirsiniz. - - *Neden?*: Bağlanacak öğeleri yukarı taşımak okumayı kolaylaştırır ve controller içerisinde hangi öğelerin View'a bağlandığını anında görmemizi sağlar. - - *Neden?*: Fonksiyonun implementasyonunu dosya içerisinde daha aşağılara taşımak kompleks kısımları göz önünden uzak tutar ve asıl önemli olan kısma odaklanmayı sağlarç - - *Neden?*: Fonksiyon tanımlamaları(declerations) JavaScript'in *hoisting* özelliğinden faydalandığı için fonksiyonun tanımlamasından önce çağrılmasından endişe duymaya gerek yoktur. (Fonksiyon eşitlemeleri(expression) için bu durum geçerli değildir) - - *Neden?*: Fonksiyon tanımlamaları ile değişkenlerin yerlerini değiştirirken kodunuz kırılır mı diye endişe duymaya gerek yoktur. - - *Neden?*: Fonksiyon eşitlemelerinde sıra önemlidir. - - ```javascript - /** - * kaçınılacak stil - * Fonksiyon eşitlemelerini kullanarak - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementasyon detayları - }; - - var getAvengerCount = function() { - // implementasyon detayları - }; - - var getAvengersCast = function() { - // implementasyon detayları - }; - - var prime = function() { - // implementasyon detayları - }; - - var ready = function(nextPromises) { - // implementasyon detayları - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * önerilen stil - * Fonksiyon tanımlamaları kullanarak - * ve ulaşılabilir metodları yukarıda tanımlayarak. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementasyon detayları - } - - function getAvengerCount() { - // implementasyon detayları - } - - function getAvengersCast() { - // implementasyon detayları - } - - function prime() { - // implementasyon detayları - } - - function ready(nextPromises) { - // implementasyon detayları - } - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Veri Servisleri - -### Veri İsteklerinizi Ayırın -###### [Style [Y060](#style-y060)] - - - Veri işlemlerinizi ve işleme mantığınızı bir factory servisine alarak kodunuzu düzenleyin. Veri servislerini sadece ajax çağrıları, verileri lokal depo ya da bellekte saklama, veya diğer veri işlemlerinden sorumlu olacak şekilde tasarlayın. - - *Neden?*: Controller'ın görevi sadece view için gerekli verileri toplamaktır. Bu verilerin nasıl edinildiği ile ilgilenmez, sadece bu verileri nereden temin edeceğini bilir. Veri servislerini ayırmak, veri işleme mantığını servise taşır ve controller'ın daha basit kalmasını ve sadece view'a odaklı kalmasını sağlar. - - *Neden?*: Bu yöntemle veri servisi kullanan controller'ların test edilebilmesini kolaylaştırır. - - *Neden?*: Veri servisi implementasyonu veri havuzlarını yönetmek için çok belirgin bir kod yapısına sahiptir. Bu, veri ile nasıl iletişilebileceğini anlatan header'lar yada `$http` gibi başka servisler içerebilir. Veri işleme mantığını ayırıp bir veri servisinde toplamak bu işlemlerin tek bir yerden yönetilmesini ve implementasyonun bu servisi kullananlardan (örneğin kontolörler) saklanmasını sağlar. Ayrıca implementasyonu değiştirmek kolaylaşır. - - ```javascript - /* önerilen stil */ - - // veri servisi factory'si - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. - Not: Veri servisi controller gibi onu kullanan yerlerden çağrılır ve aşağıdaki örnekteki gibi implementasyon detaylarını kullanılan yerlerden saklar. - - ```javascript - /* önerilen stil */ - - // veri servisi factroy'sini çağıran controller - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Veri Çağrılarının Sonucunda bir Promise Döndürün -###### [Style [Y061](#style-y061)] - - - Promise döndüren bir veri servisini çağırdığınızda, siz de çağrıyı yapan fonksiyona bir Promise döndürün. - - *Neden?*: Böylece Promise'lerinizi zincirleyebilirsiniz ve veri çağrısı bitip çöczümlendiğinde sonuca göre aksiyon alabilirsiniz. - - ```javascript - /* önerilen stil */ - - activate(); - - function activate() { - /** - * 1. Adım - * getAvengers fonksiyonundan avenger - * verisini isteyin ve promise'i bekleyin - */ - return getAvengers().then(function() { - /** - * 4. Adım - * Son promise'in sonunda bir aksiyon al - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * 2. Adım - * Veri servisinden veriyi iste ve - * promise'i bekle - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * 3. Adım - * Veriyi kaydet ve promise'i çözümle - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Directive'ler -### Bir Dosyaya Bir Directive -###### [Stil [Y070](#style-y070)] - - - Her bir directive için ayrı bir dosya yaratın ve dosyanın adını directive'in adı ile aynı tutun. - - *Neden?*: Bütün directive'leri bir dosya içerisinde toplamak kolaydır, ancak daha sonra bu directive'leri ayırıp farklı uygulamalarda, modüllerde kullanmak zorlaşır. - - *Neden?*: Her dosyada bir directive'in olması sürdürülebilirliği kolaylaştırır. - - > Not: "**En iyi uygulama**: Directive'ler kendilerini temizlemelilerdir. `element.on('$destroy', ...)` ya da `scope.$on('$destroy', ...)` kullanarak directive kaldırıldığında bir temizlik fonksiyonu çalıştırabilirsiniz" ... Angular dökümantasyonundan. - - ```javascript - /* sakınılacak stil */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order modülüne özel directive */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales uygulamasının heryerinde kullanılabilecek bir directive */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* bütün uygulamalarda kullanılabilecek bir directive */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* İmplementasyon detayları */ - } - - function salesCustomerInfo() { - /* İmplementasyon detayları */ - } - - function sharedSpinner() { - /* İmplementasyon detayları */ - } - ``` - - ```javascript - /* önerilen stil */ - /* calendarRange.directive.js */ - - /** - * @desc order modülüne özel directive - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* İmplementasyon detayları */ - } - ``` - - ```javascript - /* önerilen stil */ - /* customerInfo.directive.js */ - - /** - * @desc uygulama içerisinde heryede kullanılabilecek sales directive'i - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* İmplementasyon detayları */ - } - ``` - - ```javascript - /* önerilen stil */ - /* spinner.directive.js */ - - /** - * @desc bütün uygulamalarda kullanılabilecek spinner directive'i - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* İmplementasyon detayları */ - } - ``` - - Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. - - Not: Directive'ler için birçok isimlendirme seçeneği mevcut, özellikle dar ya da geniş kapsamda kullanılanlar için. Directive'i ve dosya ismini belirgin ve açık ifade edecek isimler seçin. Aşağıda bazı örnekler bulabilirsiniz, ama daha fazla tavsiye için [İsimlendirme](#naming) bölümüne bakın. - -### Directive İçerisinde DOM Değişiklikleri -###### [Stil [Y072](#style-y072)] - - - DOM'a direk olark müdahele etmek için directive kullanın. Eğer CSS kullanmak ya da [animasyon servisleri](https://docs.angularjs.org/api/ngAnimate), Angular şablonlandırma, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ya da [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) ile amacınıza ulaşabiliyorsanız bu yöntemleri tercih edin. Örneğin eğer bir directive sadece bir elemanı saklayıp gösteriyorsa ngHide/ngShow kullanın. - - *Neden?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) - *Neden?*: DOM değişikliklerini test ve debug etmek güç olabilir, ve genellikle daha iyi bir yöntem bulabilirsiniz (örneğin CSS, animasyon, şablonlar) - -### Eşsiz Bir Directive Ön eki Kullanın -###### [Stil [Y073](#style-y073)] - - - Eşsiz, kısa ve tanımlayıcı bir ön ek kullanın. Örneğin `acmeSalesCustomerInfo`. HTML'de `acme-sales-customer-info` şeklinde tanımlanır. - - *Neden?*: Eşsiz ön ek directive'in kapsamını ve orijinini ifade eder. Örneğin `cc-` directive'in CodeCamper uygulamasına ait olduğunu ifade ederken, `acme-` bu directive'in Acme firmasına ait olduğunu ifade edevilir - - Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). - - Not: `ng-` Angular tafafından kullanıldığı için bu ön eki kullanmaktan kaçının. Ön ekinizi belirlemeden önce çakışmaların önüne geçmek için iyice araştırın. Örneğin `ion-` ön eki [Ionic Framework](http://ionicframework.com/) tarafından kullanılmaktadır. - -### Directive'inizin Yazım Türünü Element ve Attribute Olarak Sınırlayın -###### [Stil [Y074](#style-y074)] - - - When creating a directive that makes sense as a stand-alone element, allow restrict `E` (custom element) and optionally restrict `A` (custom attribute). Generally, if it could be its own control, `E` is appropriate. General guideline is allow `EA` but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element. - - - Kendi başına element olarak anlamlı bir directive yaratırken restrict `E` (özel element) ve tercihen restrict `A` (özel attribute) kullanın. Genellikle, eğer kendi kendini kontrol eden bir directive ise `E` uygun olur. Genel olarak `EA` kullanmaya izin verilir ama eğer directive tek başına bir element ise element(E) olarak, hazırda var olan bir element'i iyileştiren bir directive'se attribute(A) olarak sınırlamaya yönelin. - - *Neden?*: Çünkü mantıklı. - - *Neden?*: Directive'i class olarak da kullanmaya olanak sağlansa da, eğer directive gerçekten kendi başına bir element olarak davranıyorsa element(E) olarak sınırlamak ya da en azından attribute(A) olarak sınırlamak mantıklı olur. - - Not: EA, Angular 1.3 + için varsayılan sınırlandırma seçeneğidir. - - ```html - -
- ``` - - ```javascript - /* sakınılacak stil */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* önerilen stil */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directive'ler ve controllerAs -###### [Stil [Y075](#style-y075)] - - - Tutarlı olmak için directive'le birlikte `controller as` sintaksını kullanın. - - *Neden?*: Mantıklı ve zor değil. - - Not: Aşağıdaki örnek scope'u link ve directive controller'ı içerisinde controllerAs yöntemi ile nasıl kullanılacağını gösterir. Örneğin bölünmemesi amacı ile HTML şablonunu directive içerisinde tuttum. - - Not: Bağımlılık Enjeksiyonu (Dependency Injection) ile ilgili olarak , [Manuel Olarak Bağımlılıkları Belirlemek](#manual-annotating-for-dependency-injection) kısmına bakın. - - Not: Directive'in controller'ının directive'in kapsamının(closure) dışında olduğunu unutmayın. Bu stil `return`'den sonra enjeksiyonların ulaşılamaz şekilde yaratılması probleminin önüne geçer. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Kıyaslama yapmak için $scope enjecte ediliyor - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Not: Ayrıca controller'ı link fonksiyonuna enjekte ederken isimlendirebilirsiniz ve böylece directive attribute'larına controller'ın elemanları olarak erişebilirsiniz. - - ```javascript - // Yukarıdaki örneğe alternatif - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Stil [Y076](#style-y076)] - - - `controller as` sintaksını kullanırken `bindToController = true` seçeneğini kullanın. Bu dış $scope'u directive'in controller $scope'una bağlamanızı sağlar. - - *Neden?*: Dış $scope'u directive'in controller'ın $scope'una bağlamayı kolaylaştırır. - - Not: `bindToController` özelliği Angular 1.3.0 ile birlikte gelmiştir. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Promise'leri Controller'lar İçin Çözümlemek -### Controller Aktifleştirme Promise'leri -###### [Stil [Y080](#style-y080)] - - - Controller'ın başlangıç mantığını `activate` fonksiyonu içerisinde çözün. - - *Neden?*: Başlangıç mantığını controller içerisinde tutarlı bir yerde tutmak yerini bulmayı kolaylaştırır, test için tutarlı hale getirir ve başlangıç mantığını controller içerisinde dağıtmaya yardımcı olur. - - *Neden?*: `activate` fonksiyonu başlangıç mantığını controller/View baştan başlatılmak istendiğinde tekrar kullanmaya elverişli hale getirir, mantığı bir arada tutar, kullanıcıyı View'a daha hızlı ulaştırır, `ng-view` ya da `ui-view` için animasyonları kolaylaştırır ve kullanıcıya daha hızlı hissettirir. - - Not: Eğer durumsal olarak route'u controller başlamadan önce iptal etmek istiyorsanız [route resolve](#style-y081) kullanın. - - ```javascript - /* sakınılacak stil */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* önerilen stil */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Promise'leri Route için Çözümlemek -###### [Stil [Y081](#style-y081)] - - - Bir controller aktive olmadan önce bir promise'in çözülmesine bağlı ise, o bağımlılıkları `$routeProvider` içerisinde controller çalışmaya başlamadan önce çözün. Eğer durumsal olarak bir route'un controller çalışmadan önce iptal olmasını istiyorsanız, route resolver kullanın. - - - View'a geçiş yapmadan önce geçişi iptal etmek istiyorsanız route resolver kullanın. - - *Neden?*: Bir controller yüklenmeden önce veri yüklenmesi ihtiyacında olabilir. Bu veri bir bir factory aracılığı ile promise'den ya da [$http](https://docs.angularjs.org/api/ng/service/$http)'den gelebilir. [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) kullanmak promise'in controller çalıştırılmadan önce çözümlenmesini sağlar, böylece controller promise'den gelen veriye göre aksiyon alabilir. - - *Neden?*: Kod route'dan ve controller'ın aktivasyon fonksiyonundan sonra çalıştırılır. View hemen yüklenir. Data binding aktivasyon promise'i çözümlendikten hemen sonra yapılır. Bir “meşgul” animasyonu view geçişi esnasında gösterilebilir (`ng-view` veya `ui-view`) - - Note: Kod route'dan önce promise aracılığı ile çalıştırılır. Reject olan promise route'u iptal eder. Resolve olması view'ın route promise'inin çözülmesini bekletir. Bir meşgul” animasyonu resolve'dan önce ve view geçişi süresince gösterilebilir. Eğer view'ın daha hızlı yüklenmesini istiyorsanız ve view'ın yüklenebilir olup olmadığını kontrol ettiğiniz bir nokta yok ise [controller `activate` tekniği](#style-y080)'ni kullanın. - - ```javascript - /* sakınılacak stil */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* daha iyi stil */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Not: Aşağıdaki örnek route resolve'un bir isimli fonksiyonuna işaret ettiğini gösterir, debug etmesi ve dependency injection kontrolü daha kolaydır. - - ```javascript - /* çok daha iyi stil */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Note: Örnek kodun `movieService` bağımlılığı minification işlemi için uygun değildir. Minification uyumlu hale getirme detayları için [dependency injection](#manual-annotating-for-dependency-injection) ve [minification and annotation](#minification-and-annotation) bölümlerine bakın. - -**[İçerik Listesi](#icerik-listesi)** - -## Dependency Injection ve Manuel Annotation - -### Minification Uyumluluk -###### [Stil [Y090](#style-y090)] - - - Bağımlılıkları belirlerken kısayol sintaksını kullanmaktan kaçının. - - *Neden?*: Component'e aid değişkenler (controller, factory, etc) minification işlemi sonrası karıştırılmış değişkenlere çevrilecektir. Örneğin, `common` ve `dataservice`, `a` ve `b` değişkenlerine dönüşebilir ve Angular tarafından bulunamayabilir. - - ```javascript - /* sakınılacak stil - minification uyumlu değil*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Bu kod minificationdan sonra karışık değişkenler üretebilir ve runtime hataları ile karşılaşabilirsiniz. - - ```javascript - /* sakınılacak stil - minification uyumlu değil*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Manuel Olarak Bağımlılıkları Tanımlamak -###### [Stil [Y091](#style-y091)] - - - Angular component'lerinize manuel olarak bağımlılıklarınızı tanımlamak için `$inject` kullanın. - - *Neden?*: Bu teknik [`ng-annotate`](https://github.com/olov/ng-annotate) tarafından kullanılan tekniği taklit eder, ki benim minification uyumlu bağımlılıkları otomatik olarak yaratmak için önerdiğim yöntemdir. Eğer `ng-annotate` bir bağımlılığın daha önceden eklendiğini farkederse, bu bağımlılığı çoklamaz. - - *Neden?*: Bu bağımlılıklarınızın minification sürecinde değiştirilirken hataya açık hale gelmelerini engeller. `common` ve `dataservice`, `a` ve `b` haline gelebilir ve Angular tarafından bulunamayabilir. - - *Neden?*: Uzun array listelerini okumak zor olacağından satıriçinde bağımlılık tanımlamaktan kaçının. Ayrıca array'iniz string serilerinden oluşurken son elemanının bir fonksiyon olması kafa karıştırıcı olabilir. - - ```javascript - /* kaçınılacak stil */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* kaçınılacak stil */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* önerilen stil */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Not: Eğer fonksiyonunuz bir return statement'ının altındaysa `$inject` ulaşılmaz hale gelebilir (directive içerisinde bu durum gerçekleşebilir). Bunu controller'ı directive dışarısına taşıyarak çözebilirsiniz. - - ```javascript - /* sakınılacak stil */ - // directive tanımlaması içerisinde - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* önerilen stil */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Manuel Olarak Route Resolver Bağımlılıklarını Tanımlamak -###### [Stil [Y092](#style-y092)] - - - Angular component'lerinize manuel olarak bağımlılıklarınızı tanımlamak için `$inject` kullanın. - - *Neden?*: Bu teknik route resolver için anonim fonksiyonu kırar ve okunmasını kolaylaştırır. - - *Neden?*: `$inject` kolayca resolver öncesine konulabilir ve bağımlılıkları minification için uygun hale getiirir. - - ```javascript - /* önerilen stil */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Minification ve Annotation - -### ng-annotate -###### [Stil [Y100](#style-y100)] - - - [Gulp](http://gulpjs.com) ya da [Grunt](http://gruntjs.com) ile birlikte [ng-annotate](//github.com/olov/ng-annotate) kullanın ve otomatik dependency injection'a ihtiyacı olan fonksiyonları `/* @ngInject */` ile yorumlayın - - *Neden?*: Bu kodunuzu minification uyumlu yazılması unutulmuş bağımlılıklara karşı korur. - - *Neden?*: [`ng-min`](https://github.com/btford/ngmin) artık kullanılmıyor - - >Ben kişisel olarak Gulp kullanmayı tercih ediyorum. Yazması, okuması ve debug etmesi çok daha kolay. - - Aşağıdaki kod minification uyumlu bağımlılıklar içermemektedir. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - ``` - - Yukarıdaki kod ng-annotate ile çalıştırıldığı zaman ürettiği kod `$inject` annotation'unu içerecek ve minification uyumlu hale gelecek. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - - Avengers.$inject = ['storage', 'avengerService']; - ``` - - Not: Eğer `ng-annotate` bağımlılığın daha önceden eklendiğini anlarsa, `$inject` kodunu çoğaltmayacaktır. - - Not: Yukarıdaki kod ng-annotate ile çalıştırıldığı zaman ürettiği kod `$inject` annotation'unu içerecek ve minification uyumlu hale gelecek. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note: Angular 1.3'den itibaren [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) directive'inin `ngStrictDi` parametresini minification uyumlu olmayan bağımlılıkları yakalamak için kullanabilirsiniz. Bu parametre aktif olduğu zaman injector "strict-di" modunda yaratılacak ve uygulamanın açık annotation kullanmayan fonksiyonların çalışmamasını sağlayacak. Debbuging bilgisi konsola yazılacak ve problemi yaratan fonksiyonu bulacaktır. Ben `ng-strict-di` parametresini sadece debugging için kullanmayı tercih ediyorum. - `` - -### ng-annotate için Gulp ya da Grunt Kullanın -###### [Stil [Y101](#style-y101)] - - - Otomatik derleme görevleriniz için [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ya da [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) kullanın. Bağımlılığı olan fonksiyonların başına `/* @ngInject */` satırını koyun. - - *Neden?*: ng-annotate çoğu bağımlılığı yakalayacaktır, ama bazen `/* @ngInject */` sintaksı ile done vermenizi bekler. - - Takip eden kod gulp ve ngAnnotate kullanımına örnektir - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // uglify etmeden önce annotate edin ki kod düzgün minified olsun. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. - // Resolve ile birlikte çalışmaz. Orası için kesin olarak belirtilmelidir - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Exception Yakalama - -### decorator'ler -###### [Stil [Y110](#style-y110)] - - - [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) servisinin config aşamasında [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator) kullanın, [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) servisinde exception'u yakaladığınız zaman alacağınız özel aksiyonları tanımlayın. - - *Neden?*: Geliştirme ve çalıştırma esnasında Angular tarafından yakalanamayan exception'ları yakalamak için tutarlı bir yol sağlar. - - Note: Diğer bir yöntem ise decorator kullanmak yerine servisi override etmektir. Bu da iyi bir seçenektir, ancak eğer varsayılan davranışı korumak istiyorsanız. Davranışı değiştirmek istiyorsanız decorator tavsiye edilir. - - ```javascript - /* önerilen stil */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - - /** - * Hatayı service'in kolleksiyonuna ekleyebilirsiniz, - * $rootScope'a ekleyebilirsiniz, uzaktaki bir sunucuya yazabilirsiniz - * ya da lokal olarak loglayabilirsiniz. Ya da direk console'a yazdırabilirsinz. Tamamen size kalmış. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Yakalayıcılar -###### [Style [Y111](#style-y111)] - - - Exceptionları yakalamak ve yönetmek için bir interface döndüren factory servisi yazın. - - *Neden?*: Kodunuzda fırlatılan exceptionların yakalanması için tutarlı bir yol sağlar (e.g. during XHR calls or promise failures). - - Not: Exception yakalayıcı çağrılarınızda fırlatılabilecek exceptionları yakalamak ve aksiyon almak için iyi bir yöntemdir. Örneğin, uzaktaki bir web servisinden veri almak için bir XHR çağrısı yaparken, o sunucudan fırlatılan exceptionları yakalamak ve buna göre aksiyon almak isteyebilirsiniz. - - ```javascript - /* önerilen stil */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Hataları -###### [Style [Y112](#style-y112)] - - - Bütün routing hatalarını [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError) kullanarak yakalayın ve loglayın. - - *Neden?*: Bütün routing hatalarını yakalamak için tutarlı bir yöntem. - - *Neden?*: Potensiyel olarak bir routing hatası yakalandığında kullanıcıları hatanın detayları ve nereye yönlenebileceklerini gördüğü bir sayfaya yönlendirmek daha iyi bir kullanıcı deneyimi sağlar. - - ```javascript - /* önerilen stil */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route iptali: - * Hata olduğunda dashboard ekranına git - * Eğer iki kere denenirse bir çıkış yapısı sun. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Tercih olarak özel bir servis ya da $log kullanarak logla. - * (Özel servisi inject etmeyi unutma) - */ - logger.warning(msg, [current]); - - /** - * Routing hatası aldığında başka bir sayfaya yönlendir. - */ - $location.path('/'); - - } - ); - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## İsimlendirme - -### İsimlendirme Rehberi -###### [Stil [Y120](#style-y120)] - - - Bütün componentler için, tipini ve içeriğini belirten tutarlı isimler kullanın. Benim tavsiye ettiğim şablon `içerik.tip.js`. Çoğu asset için genelde 2 isim vardır: - * dosya adı (`avengers.controller.js`) - * Angular'a kayıt olan component ismi (`AvengersController`) - - *Neden?*: İsimlendirme gelenekleri arananın bir bakışta bulunması için tutarlı bir yol sunar. Proje genelinde tutarlılık hayatidir. Takım içerisinde tutartlılık önemlidir. Şirket içersinde tutartlılık ise inanılmaz bir verim sağlar. - - *Neden?*: İsimlendirme geleneği, aradığınız kodu basitçe bulmanızı sağlar ve anlaşılmasını kolaylaştırır. - -### İçerik Dosya İsimleri -###### [Stil [Y121](#style-y121)] - - - Bütün componentler için, tipini ve içeriğini belirten tutarlı isimler kullanın. - - *Neden?*: Component'leri hızlı bir şekilde tanımlamak için tutarlı bir yol sunar. - - *Neden?*: Otomatik görevlerde dosya isimleri için pattern matching sağlar. - - ```javascript - /** - * yaygın seçenekler - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * önerilen stil - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Note: Another common convention is naming controller files without the word `controller` in the file name such as `avengers.js` instead of `avengers.controller.js`. All other conventions still hold using a suffix of the type. Controllers are the most common type of component so this just saves typing and is still easily identifiable. I recommend you choose 1 convention and be consistent for your team. My preference is `avengers.controller.js`. - - Note: Controller dosyalarını isimlendirirken yaygın olan bir diğer gelenek ise `controller` kelimesini dosya isminden çıkarıp `avengers.controller.js` yerine `avengers.js` olarak bırakmaktır. Diğer bütün componentler için son ek tutulur. Controller'lar en yaygın kullanılan component tipidir, bu yüzden `controller` kelimesini çıkartmak bizi fazladan yazı yazmaktan kurtarır ve hala kolayca component'in ne olduğunu anlamamızı sağlar. Benim tavsiyen tek bir geleneğe sadık kalın ve takımınız içerisinde tutarlı olun. Benim tercihim `avengers.controller.js`. - - ```javascript - /** - * önerilen stil - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test Dosyası İsimleri -###### [Style [Y122](#style-y122)] - - - Test dosyalarını, test ettikleri component'in ismi ve `spec` son eki ile isimlendirin. - - *Neden?*: Component'leri hızlı bir şekilde tanımlamak için tutarlı bir yol sunar. - - *Neden?*: [karma](http://karma-runner.github.io/) ya da diğer test araçları için bir pattern matching yapısı sunar. - - ```javascript - /** - * önerilen stil - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller İsimleri -###### [Style [Y123](#style-y123)] - - - Bütün controller'lar için içeriklerini ifade eden tutarlı isimlar kullanın. Contructor oldukları için UpperCamelCase yöntemini kullanın. - - *Neden?*: Controller'ları hızlıca ilişkilendirmek için tutarlı bir yol sunar. - - *Neden?*: UpperCamelCase isimlendirme yöntemi constructor kullanarak yaratılan objeler için gelenekseldir. - - ```javascript - /** - * önerilen stil - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Controller İsmi Son Eki -###### [Style [Y124](#style-y124)] - - - Controller ismine `Controller` son eki ekleyin. - - *Neden?*: `Controller` son eki daha yaygın kullanılıyor ve daha açıklayıcı. - - ```javascript - /** - * önerilen stil - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Factory ve Service İsimleri -###### [Style [Y125](#style-y125)] - - - Bütün Service'ler ve Factory'ler için içeriklerini ifade eden tutarlı isimler kullanın. İsimlendirme yöntemi olarak camel-case kullanın. `$` ön ekini kullanmaktan kaçının. Ne oldukları tam olarak anlaşılmıyor ise `Service` son ekini kullanın. - - *Neden?*: Factory'leri hızlıca ilişkilendirmek için tutarlı bir yol sunar. - - *Neden?*: Angular ile birlikte gelen `$` ön ekini kullanan servisler ile çakışmayı önler. - - *Neden?*: Ne olduğu açık olan `logger` gibi servis isimleri son eke ihtiyaç duymaz. - - *Neden?*: `avengers` gibi servis isimleri isimdir ve son eke ihtiyaç duyar. `avengersService` olarak isimlendirilmelidir. - - ```javascript - /** - * önerilen stil - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - - ```javascript - /** - * önerilen stil - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // customer.service.js - angular - .module - .service('customersService', customersService); - - function customersService() { } - ``` - -### Directive İsimleri -###### [Stil [Y126](#style-y126)] - - - Bütün directive'leri camel-case yöntemini kullanarak tutarlı bir şekilde isimlendirin. Directive'in nereye ait olduğunu belirten kısa ön ekler kullanın (firma ya da proje isimlerinin kısaltılmış hali örnek olabilir). - - *Neden?*: Component'leri hızlıca ilişkilendirmek için tutarlı bir yol sunar. - - ```javascript - /** - * önerilen stil - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Modüller -###### [Stil [Y127](#style-y127)] - - - Eğer birden fazla modülünüz varsa, ana modül ismi `app.module.js` olarak isimlendirilir ve diğer tüm bağımlılık modülleri sundukları özellikleri ifade edecek şekilde isimlendirlir. Örneğin, admin modülü `admin.module.js` olarak isimlendirilir. Kayıtlı modül isimlerimi `app` ve `admin` olur. - - *Neden?*: Birçok modülü olan uygulamalar ya da genişleyen büyük uygulamalar için tutarlılık sağlar. - - *Neden?*: Otomatik görevler içerisinde bütün dosyaları birleştiriyorsanız, önce modülleri daha sonra diğer angular dosyalarını sıralamanıza yardımcı olur. - -### Konfigürasyonlar -###### [Style [Y128](#style-y128)] - - - Separate configuration for a module into its own file named after the module. A configuration file for the main `app` module is named `app.config.js` (or simply `config.js`). A configuration for a module named `admin.module.js` is named `admin.config.js`. - - - Modül konfigüreasyonunu ayrı bir dosyada modülün ismi ile bulundurun. `app` modülünün konfigürasyon dosyasının adı `app.config.js` olmalıdır (ya da basitçe `config.js`). `admin.module.js`'nün konfigürasyon dosyası `admin.config.js` olarak isimlendirilir. - - *Neden?*: Konfigürasyonları modül tanımlamalarından, component'lerden ve çalışan koddan ayırır. - - *Neden?*: Modül konfigürasyonlarının yerinin anlaşılmasını kolaylaştırır. - -### Route'lar -###### [Stil [Y129](#style-y129)] - - - Separate route configuration into its own file. Examples might be `app.route.js` for the main module and `admin.route.js` for the `admin` module. Even in smaller apps I prefer this separation from the rest of the configuration. - - - Route konfigürasyonlarını ayrı bir dosyaya alın. Ana modül için `app.route.js` ve `admin` modülü için `admin.route.js` şeklinde. Küçük uygulamalar için bile ben bu ayrıma gitmeyi tercih ediyorum. - -**[İçerik Listesi](#icerik-listesi)** - -## Uygulama Yapısı ve LIFT Prensibi -### LIFT -###### [Stil [Y140](#style-y140)] - - - Uygulamanızı, kodunuzu kolayca bulup (`L`ocate), bir bakışta tanımlayıp (`I`dentify), en az dallanmış selikde klasörlediğiniz (`F`lattest structure) ve kendinizi tekrar etmediğiniz (`T`ry to stay DRY) bir şekilde yapılandırın. Yapnınız bu 4 temel prensibi izlemeli. - - *Neden LIFT?*: Kolaylıkla büyüyebilen tutarlı bir yapı sunar, modülerdir ve kodlara kolayca ulaşmayı sağlayarak yazılımcı verimini arttırır. Yapınızı kontrol etmenin bir başka yöntemi ise kendinize: Bir özellik eklemek için ihtiyacım olan dosyaları kolayca bulup açabiliyor muyum? diye sormanızdır - - Yapımdan memnun olmadığım zamanlarda geri dönüp LIFT prensibinin kurallarını tekrar gözden geçiriyorum - - 1. Kodunuzu kolayca bulabiliyor musunuz? (`L`ocating our code is easy) - 2. Bir bakışta ne iş yaptığını tanımlayabiliyor musunuz? (`I`dentify code at a glance) - 3. Çok dallanmamış bir klasör yapınız mı var? (`F`lat structure as long as we can) - 4. Bir işi tekrar tekrar yapıyor musunuz? (`T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY) - -### Yer Tespiti -###### [Stil [Y141](#style-y141)] - - - Kodunuzun kolayca tahmin edilebilir bir yerde ve hızlıca ulaşılabilir olduğundan emin olun. - - *Neden?*: Bunun bir proje için çok önemli olduğunu düşünüyorum. Eğer takımınız çalışacağı dosyaları kolayca bulamıyorlarsa, verimli bir şekilde çalışamıyorlarsa yapınız değişmeli demektir. Bir dosyanın ve onunla ilintili olan dosyaların nerede olduğunu bilmiyor olabilirsiniz. Dosyları ve ilintili olan dosyaları kolayca tahmin edilebilecek bir klasöre koymak çok zaman kazanmanızı sağlar. Betimleyici bir klasör yapısı size bunu sağlar. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Tanımlama -###### [Stil [Y142](#style-y142)] - - - Bir dosyaya baktğınız anda onun ne içerdiğini ve temsil ettiğini anlamalısınız. - - *Neden?*: Kodun ne yaptığını anlamak için daha az didiklersiniz ve daha verimli olursunuz. Eğer bunun için daha uzun dosya isimlerine ihtiyaç duyarsanız varsın öyle olsun. Dosya isimleriniz açıklayıcı olsun ve içerisinde sadece bir component bulundursun. Birçok servis ve controller içeren dosyalardan uzak durun. Her dosyada tek bir component kuralının istisnası ancak çok küçük ve ilintili component'lerin aynı dosyada bulunması olabilir. - -### Düz Klasör Yapısı -###### [Stil [Y143](#style-y143)] - - - Olabildiğince dallanmayan bir klasör yapısı kurun. 7'den fazla dosya bir klasörde oluyorsa alt klasörlere bölmeyi düşünün. - - *Neden?*: Hiçkimse bir dosya bulmak için yedi kat klasör gezmek istemez. Websitelerindeki menüleri düşünün… 2 kademeden derin bir menü yapmadan önce iyice düşünmek gerekir. Klasör yapılarında böyle kesin bir sayı yoktur, ancak bir klasörde 7-10 dosya olursa, alt klasörlere bölmeyi düşünmeye başlayabiliriz. Bu sizin rahat hissetmeniz ile ilgilidir. Gerçekten anlamlı olmadıkça alt klasör oluşturmayın ve düz klasör yapısını koruyun. - -### Kendini Tekrar Etmemeye Çalışma (Try to Stick to DRY) -###### [Stil [Y144](#style-y144)] - - - Kendinizi tekrar etmeyin, ama abartıp okunabilirliği azaltmayın. - - *Neden?*: Kendini tekrar etmemek önemlidir, ama LIFT'in diğer prensiplerini bozuyorsa önemini yitirir, o yüzden kendinizi tekrar etmemeye çalışın diyorum. Mesela session-view.html diye isimlendirmek istemiyorum çünkü bir view dosyası olduğu çok açık. Eğer açık değilse ya da geleneğiniz buysa o zaman böyle isimlendirin. - -**[İçerik Tablosu](#icerik-listesi)** - -## Uygulama Yapısı - -### Genel -###### [Stil [Y150](#style-y150)] - - - Kısa vadeli düşünerek implementasyon yapın ama vizyonunuzu uzun vadeli tutun. Diğer bir deyişle, küçük parçalarla başlayın ama uygulamanın nereye doğru gittiğini aklınızda tutun. Uygulamanın bütün kodu `app` adlı bir klasör altında duracak. Bütün içerik bir dosyaya bir özellik şeklinde olacak. Her controller, servis, modül ve view kendi dosyalarında olacaklar. Bütün 3. parti kütüphaneler başka bir klasör altında toplanmalı, `app` klasörü altında değil. O kodları ben yazmadım ve benim uygulamamı karmaşıklaştırmasını istemiyorum (ör. `bower_components`, `scripts`, `lib`). - - Not: Bu yapının hakkında daha fazla bilgi istiyorsanız: [uygulama yapısı hakkındaki orjinal makalem](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout (yerleşim) -###### [Stil [Y151](#style-y151)] - - - Genel yerleşimi belirleyen bütün component'leri `layout` klasörü altına koyun. Buna ana view ve controller dosyası , navigasyon, menüler, içerik alanları ve diğer alanlar dahil olabilir. - - *Nden?*: Büyün yerleşim öğelerinizi tek bir yerde toplar ve bütün uygulama içerisinde tekrar kullanabilirsiniz. - -### Özelliklere Göre Klasör Yapısı -###### [Stil [Y152](#style-y152)] - - - İçerdikleri özelliğe göre klasör isimleri verin. Eğer bir klasör 7'den fazla dosya içermeye başlarsa bir alt klasör yaratmayı düşünün. Alt klasöre bölme limitiniz farklı olabilir, kendinize göre ayarlayın. - - *Neden?*: Bir yazılımcı kodun yerini bulabilir, tek bakışta ne iş yaptığını anlayabilir, klasör yapısı olabildiğince düz olur ve tekrar eden ve gereksiz isimler olmaz. - - *Neden?*: LIFT prensiplerinin hepsini kapsar. - - *Neden?*: İçeriği organize ederek uygulamanın karmaşıklaşmasını azaltır ve LIFT prensipleri ile örtüştürür. - - *Neden?*: Eğer 10'dan fazla dosya varsa bunların yerini tespit etmek tutarlı bir klasör yapısı ile düz klasör yapısına göre daha kolay olur. - - ```javascript - /** - * önerilen stil - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Örnek Uygulama Yapısı](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - Not: Uygulamanızı tipe göre klasör yapısı ile oluşturmayın. Bu yüzden bir özellik üzerinde çalışırken birden çok klasör içerisinde çalışmanız gerekir ve dosya sayısı arttıkça sizi hantallaştırır. Özelliğe göre klasör yapısına göre dosyaları bulmak daha zordur. - - ```javascript - /* - * sakınılacak stil - * Tipe Göre Klasör yöntemi. - * Bunun yerine ben İçeriğe Göre Klasörleme yöntemini tercih ediyorum. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Modülerlik - -### Küçük, Kendi Başına Yeterli Modüller -###### [Stil [Y160](#style-y160)] - - - Tek işlevsellik içeren küçük modüller yaratın. - - *Neden?*: Modüler uygulamalar ile "ekle çalıştır" yapmak daha kolaydır. Takım olarak geliştirmeye olanak sağlar ve dikey kesitlerde uygulamayı beraber geliştirebilirsiniz. Bu sayede yeni bir özelliik geliştirildiğinde uygulamaya hemen ekleyebiliriz. - -### Bir App Modülü Yaratın -###### [Stil [Y161](#style-y161)] - - - Bir ana uygulama modülü yaratın ve bunun rolü diğer bütün modülleri ve özellikleri bir araya getirmek olsun. Bu modülü uygulamanızın adı ile isimlendirin. - - *Neden?*: Angular modülerliği ve ayrıştırma desenlerini destekler. Bütün modüllerinizi bir arada tutan ana bir modül yaratmak çok basit bir şekilde bu modüllere ekleme yada bazılarını çıkarmanıza olanak sağlar. - - -### App Modülünü Sade Tutun -###### [Stil [Y162](#style-y162)] - - - Sadece bütün modülleri bir araya getirme mantığını buraya koyun. Özellikleri modüller kendi içlerinde tutsunlar. - - *Neden?*: App modülüne diğer modülleri birbirine bağlamak dışında veri alma, view gösterme ya da başka işleyiş mantıkları koymanız App modülünüzü bulandırır ve özellik setlerinin tekrar kullanılmasını yada kapsam dışına alınmasını zorlaştırır. - - *Neden?*: App modülü bu uygulamanın hangi modüllerden oluştuğunu gösteren bir manifesto haline gelir. - -### Özellik Kapsamları ve Modüller -###### [Stil [Y163](#style-y163)] - - - Özellik kapsamlarını ifade eden modüller yaratın; yerleşim, tekrar kullanılan ve paylaşılan servisler, dashboard ve uygulamaya özel özellikler gibi (Ör. kullanıcılar, admin, satış). - - *Neden?*: Kendi başına yeterli modüller uygulamaya çok küçük pürüzlerle eklenir. - - *Neden?*: Sprint'ler ve iterasyonlar özellik geliştirmeye odaklanabilir ve sprint ya da iterasyon sonunda özellik uygulamaya eklenebilir. - - *Neden?*: Özellik kapsamlarını modüllere ayırmak onları izole şekilde daha kolay test edilebilir hale getirir ve tekrar kullanılmalarını kolaylaştırır. - -### Tekrar Kullanılabilir Bloklar Modüllerdir -###### [Stil [Y164](#style-y164)] - - - Tekrar kullanılabilir uygulama blokları için modüller yaratın. Örneğin exception yakalam, loglama, güvenlik, sistem kontrolü, lokal veri depolama gibi. - - *Neden?*: Bu tarz özelliklere birçok uygulamada ihtiyaç duyulur. Bunları ayrı kendi modüllerinde ayrı tutmak uygulamalar arasında tekrar kullanılmalarına olanak sağlar. - -### Modül Bağımlılıkları -###### [Stil [Y165](#style-y165)] - - - Ana uygulama modülü uygulamaya özel modüllere ve paylaşılan, tekrar kullanılabilen modüllere bağlıdır. - - ![Modülerlik ve Bağımlılık](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *Neden?*: Uygulama ana modülü uygulamanın özelliklerini içeren kolayca tanımlanabilen bir manifestodur. - - *Neden?*: Her özellik kapsamı nelere bağımlı olduğunu gösteren bir manifesto içerir, böylece başka uygulamalar içerisine bağımlılık olarak eklendiğinda hala çalışmaya devam eder. - - *Neden?*: Paylaşılan veri servisleri gibi uygulamalar arası özellikler kolay bulunabilir ve `app.core`'dan kolay bir şekilde paylaşılabilir (Bu modül için favori isminizi seçin). - - Not: Bu tutarlılığı sağlamak için bi stratejidir. Bunun için birçok iyi seçenek mevcut. Angular'ın bağımlılık kurallarını izleyen, kolay yönetilebilen ve genişleyebilen tutarlı olan birtanesini seçin. - - > Benim yapılarım projelerim arasında pek fazla değişmez ama hepsi yapı ve modülerlik için bu rehberi izler. İmplementasyon özelliklere ve takıma göre değişkenlik gösterebilir. Bir başka deyişle, kelimesi kelimesine bir yapıya tutunmayın ama tutarlılığı, yönetilebilirliği ve verimi göz önünde bulundurarak yapınızı ayaralayın. - - > Küçük bir uygulamada, özelliklerin direk bağımlılığı olmadığı, paylaşılan bütün bağımlılıkları ana modüle koymayı düşünebilirsiniz. Bu küçük uygulamaları daha kolay yönetilebilir hale getirir, ama bu uygulamada kullanılan modülleri bu uygulama dışında kullanmayı zorlaştırır. - -**[İçerik Listesi](#icerik-listesi)** - -## Başlangıç Mantığı - -### Konfigürasyon -###### [Stil [Y170](#style-y170)] - - - Kodunuzu, uygulamanız başlamadan önce çalışacak olan [modül konfigürasyonu](https://docs.angularjs.org/guide/module#module-loading-dependencies) içine koyun. Buraya koyulması beklenenler provider'lar ve constant'lardır. - - *Neden?*: Bu yöntem konfigürasyonların yapılacağı yerleri azaltmayı sağlar. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run Blokları -###### [Stil [Y171](#style-y171)] - - - Uygulama başladığında çalışması gereken her kod bloğu bir fonksiyon ile dışarı açılan factory içerisinde tanımlanmalıdır, ve [run bloğuna](https://docs.angularjs.org/guide/module#module-loading-dependencies) inject edilmelidir. - - *Neden?*: Run bloğu içerisinde boşta duran bir kod zor test edilir. Bir factory içerisine koymak soyutlaştırmayı ve mock'lamayı kolaylaştırır. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Angular Servisleri - -### $document ve $window -###### [Stil [Y180](#style-y180)] - - - `document` ve `window` yerine [`$document`](https://docs.angularjs.org/api/ng/service/$document) ve [`$window`](https://docs.angularjs.org/api/ng/service/$window) kullanın. - - *Neden?*: Bu servisler Angular tarafından yaratılmıştır ve document ve window'a göre daha kolay test edilebilirler. Bu kendiniz için mock document ve window oluşturmanızı engeller. - -### $timeout and $interval -###### [Stil [Y181](#style-y181)] - - - `setTimeout` and `setInterval` yerine [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) ve [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) kullanın. - - *Neden?*: Bu servisler Angular tarafından yaratılmıştır, daha kolay test edilebilirler ve Angular'ın digest cycle'ını yönetebilir ve veriyi senkronize tutabilirler. - -**[İçerik Listesi](#icerik-listesi)** - -## Test - -Unit Test yapmak temiz kodu yönetmeye yardımcı olur, bu yüzden benim unit test temelleri için önerilerimi linkler ve daha fazla detay ile paylaşıyorum. - -### Testlerinizi Senaryolar İle Yazın -###### [Stil [Y190](#style-y190)] - - - Her senaryo için test kümeleri yazın. Boş bir test ile başlayın ve kodu yazarken içini doldurun. - - *Neden?*: Test tanımlamalarını yazmak senaryonuzun ne yapacağını, ne yapmayacağını ve başarısını nasıl ölçeceğinizi tanımlamaya yardımcı olur. - - ```javascript - it('Avengers controller`ı tanımlı olmalı', function() { - // TODO - }); - - it('İsme göre filtrelendiğinde 1 Avenger bulmalı', function() { - // TODO - }); - - it('10 Avenger olmalı', function() { - // TODO (mock veri?) - }); - - it('Avengerları XHR ile dönmeli', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Test Kütüphanesi -###### [Stil [Y191](#style-y191)] - - - Unit testleriniz için [Jasmine](http://jasmine.github.io/) ya da [Mocha](http://mochajs.org) kullanın. - - *Neden?*: Jasmine ve Mocha, ikisi de Angular topluluğu tarafından yaygınca kullanılmaktadır. İkisi de istikrarlı, iyi yönetilir, ve sağlam test özellikleri sunuyorlar. - - Not: Mocha kullanırken ayrıca [Chai](http://chaijs.com) gibi bir assert kütüphanesi kullanmayı gözönünde bulundurun. Ben Mocha'yı tercih ediyorum. - -### Test Çalıştırıcı -###### [Stil [Y192](#style-y192)] - - - Test çalıştırıcısı olarak [Karma](http://karma-runner.github.io)'yı kullanın. - - *Neden?*: Karma kolay ayarlanabilir ve kodunuzu değiştirdiğinizde otomatik olarak ya da birkereliğine çalışabilir bir araçtır. - - *Neden?*: Karma kendi başına ya da Grunt veya Gulp aracılığı ile Continuous Integration sisteminize kolaylıkla dahil olabilir. - - *Neden?*: [WebStorm](http://www.jetbrains.com/webstorm/) ve [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) gibi bazı IDE'ler Karma ile entegre olmaya başladılar. - - *Neden?*: Karma, [Grunt](http://www.gruntjs.com) ([grunt-karma](https://github.com/karma-runner/grunt-karma) ile) ve [Gulp](http://www.gulpjs.com) gibi otomatik görev yönetici devleri ile uyumlu çalışır. Gulp kullanırken [Karma](https://github.com/karma-runner/karma)'yı direk olarak API'si ile kullanabilirsiniz. - - ```javascript - /* önerilen stil */ - - // Karman'nın direk kullanıldığı Gulp örneği - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### Stubbing ve Spying -###### [Stil [Y193](#style-y193)] - - - Stubbing ve spying [Sinon](http://sinonjs.org/) kullanın. - - *Neden?*: Sinon, Jasmine ve Mocha ile uyumlu çalışır ve sundukları stubbing ve spying özelliklerini genişletir. - - *Neden?*: Sinon, Jasmine ve Karma arasında kolay geçiş sağlar, eğer ikisini de kullanmak istiyorsanız. - - *Neden?*: Sinon, assert'ler hata verdiğin güzel açıklamalı hata mesajları üretir. - -### Head'siz Tarayıcı -###### [Stil [Y194](#style-y194)] - - - Testlerinizi sunucu üzerinde çalıştırmak için [PhantomJS](http://phantomjs.org/) kullanın. - - *Neden?*: PhantomJS head'siz bir tarayıcıdır ve testlerinizi görsel bir tarayıcıya ihtiyaç duymadan çalıştırabilir. Böylece sunucunuza Chrome, Safari, IE ya da başka bir tarayıcı yüklemek zorunda kalmazsınız. - - Not: Siz yine de hedef kitlenizin kullanacağı tarayıcıları kendi ortamınızda test etmelisiniz. - -### Kod Analizi -###### [Stil [Y195](#style-y195)] - - - Run JSHint on your tests. - - Testlerinizde JSHint kullanın. - - *Neden?*: Testler de koddur. JSHint kodun hatılı çalışmasına sebep olan kod kalitesi problemlerini denetler. - -### JSHint Kurallarını Testler İçin Hafifletmek -###### [Stil [Y196](#style-y196)] - - - `describe` ve `expect` gibi yaygın kullanılan global'lere izin vermesi için kurallarınızı hafifletin. Expression'ları Mocha kullandığı için onları kontrol eden kuralları da hafifletin. - - *Neden?*: Testleriniz de birer koddur ve üretim ortamındaki kodunuz gibi onlar da aynı ilgi ve kod kalitesini hakeder. Ancak, test için test frameworkleri tarafından kullanılan global değişkenler için bu kurallar hafifletilmelidir. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Or you can add the following to your JSHint Options file. - Ya da aşağıdaki ayarları JSHint konfigürasyon dosyanıza ekleyebilirsiniz - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Test Araçları](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### Testleri Organize Etmek -###### [Stil [Y197](#style-y197)] - - - Test dosyalarınızı(specs) test ettiğiniz dosyalarla yanyana koyun. Birçok component'i ve sunucu entegrasyonunu test eden kodları `tests` klasörüne koyun. - - *Neden?*: Unit testlerin kaynak kodundaki component ile doğrudan ilişkisi vardır. - - *Neden?*: Sürekli gözönünde olduklarından güncel tutmak kolay olacaktır. TDD de yapsanız, kod yazarken ya da yazdıktan sonra da test yazsanız, kaynak kodu dosyanızla yanyana olan test dosyası gözünüzden ve aklınızdan kaçmaz, ve böylece güncellenme olasılığı ve kodunuzun test kapsamı artar. - - *Neden?*: Kaynak kodunu güncellediğinizde test kodunu da güncellemeniz kolaylaşır. - - *Neden?*: Kaynak kodu ile yanyana koymak test kodlarını bulmayı kolaylaştırır ve taşırken onunla birlikte gitmesini sağlar. - - *Neden?*: Testi yakınlarda tutmak, kaynak kodu okuyan kişinin component'in ne yaptığını ve limitlerini keşfetmesi için testi de okumasını kolaylaştırır. - - *Neden?*: Yayına çıkacak kodun içerisinde testlerin bulunmamasını sağlamak grunt ve gulp ile kolay olur. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Animasyonlar - -### Kullanım -###### [Stil [Y210](#style-y210)] - - - [Angular ile animasyonlar](https://docs.angularjs.org/guide/animations) state'ler arası ve birincil görsel elemanlar için kullanılıyor ise hafif olmalı. [ngAnimate module](https://docs.angularjs.org/api/ngAnimate) modülünü projenize dahil edin. 3 anahtar özellik: hafif, pürüzsüz, teklemeyen. - - *Neden?*: Uygun kullanıldığında hafif animasyonlar kullanıcı deneyimini arttırır. - - *Neden?*: Hafif animasyonlar view geçişleri sırasında hissedilen performansı arttırır. - -### Saniyenin Altında -###### [Stil [Y211](#style-y211)] - - - Animasyonlarınızı kısa sürelerde tamamlayın. Ben genellikle 300ms ile başlarım ve duruma göre ayarlarım. - - *Neden?*: Uzun animasyonlar kullanıcı deneyimi ve hissedilen performans üzerinde yavaş bir uygulamaymış gibi algılanıp ters etki yaratabilir. - -### animate.css -###### [Stil [Y212](#style-y212)] - - - Geleneksel animasyonlar için [animate.css](http://daneden.github.io/animate.css/) kullanın. - - *Neden?*: animate.css'in sunduğı animasyonlar hızlı, akıcı ve uygulamanıza kolay eklenebilir. - - *Neden?*: Animasyonlarınızda tutarlılık sağlar. - - *Neden?*: animate.css yaygınca kullanılıyor ve test edilmiş. - - Not: Bu müthiş makaleye göz gezdirin: [Matias Niemelä Angular animations anlatıyor](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[İçerik Listesi to top](#icerik-listesi)** - -## Yorumlar - -### jsDoc -###### [Stil [Y220](#style-y220)] - - - Kodunuz için dökümantasyon yaratmayı planlıyorsanız, [`jsDoc`](http://usejsdoc.org/) sintaksını kullanın. Fonksiyon isimleri, açıklamaları, parametreleri ve ne döndürdüklerini listeleyebilirsiniz. `@namespace` ve `@memberOf` anahtar kelimelerini uygulamanızın yapısı ile eşleştirmek için kullanın. - - *Neden?*: Sıfırdan yazmak yere kodunuz üzerinden dökümantasyonunuzu yaratabilirsiniz. - - *Neden?*: Yaygın bir araç kullanmak tutarlılık sağlar. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Uygulama geneli için logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Loglanacak mesaj - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## JS Hint - -### Bir Seçenek Dosyası Kullanın -###### [Stil [Y230](#style-y230)] - - - JavaScript Kod denetleyicisi olarak JS Hint kullanın ve JS Hint seçeneklerini kendinize göre ayarlamayı unutmayın. Seçeneklerin detayları için [JS Hint dökümantasyonuna](http://www.jshint.com/docs/) bakın. - - *Neden?*: Kodunuzu versiyon kontrol sistemine göndermeden önce size bir ilk uyarı verir. - - *Neden?*: Takımınız içinde tutarlılık oluşturur. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## JSCS - -### Bir Seçenek Dosyası Kullanın -###### [Stil [Y235](#style-y235)] - - - Kod yazım stilinizi kontrol etmek için JSCS kullanın ve JSCS seçeneklerini kendinize göre ayarlamayı unutmayın. Seçeneklerin detayları için [JSCS dökümantasyonuna](http://www.jscs.info) bakın. - - *Neden?*: Kodunuzu versiyon kontrol sistemine göndermeden önce size bir ilk uyarı verir. - - *Neden?*: Takımınız içinde tutarlılık oluşturur. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "jsDoc": { - "checkAnnotations": true, - "checkParamNames": true, - "requireParamTypes": true, - "checkReturnTypes": true, - "checkTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Constant'lar - -### 3. Parti Kütüphane Global'leri -###### [Stil [Y240](#style-y240)] - - - 3. parti kütüphanelerin global değişkenleri için birer Angular constant'ı tanımlayın. - - *Neden?*: 3. parti kütüphaneleri kodunuza inject etmek için bir yol yaratır. Component'lerinizin bağımlılıklarını daha kolay anlamanızı sağlayarak kodunuzun test edilebilirliğini arttırır. (abstraction sızıntılarını engeller). Ayrıca bu bağımlılıkları mock'layabilmenizi sağlar. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Stil [Y241](#style-y241)] - - - Değişmeyen ve başka servislerden gelmeyen değerler için constants kullanın. Eğer bu constant'lar birden fazla uygulamada kullanılabilecek bir modüle için yaratılıyor ise, her modül için constant'ları modül ile aynı isme sahip bir dosyaya koyun. Bu durum gerekli olmadığı takdirde, constant'ları ana modül içerisinde `constants.js` dosyasında tutun. - - *Neden?*: Değişme olasılığı olan, çok nadir de olsa, değerler bir servisten alınmalıdır, çünkü bu sayede kaynak kodunuzu değiştirmek zorunda kalmazsınız. Örneğin, bir veri servisi için url adresi constant içine yerleştirilebilir, ama daha iyi bir seçenek bunu bir web servisten yüklemek olacaktır. - - *Neden?*: Constant'lar angular component'lerine inject edilebilir, buna provider'lar dahildir. - - *Neden?*: Bir uygulama başka uygulamalarda kullanılabilecek modüllere ayrıldığında, her biri kendi başına bağımlı olduğu constant'lar ile birlikte çalışabilecek durumda olmalıdır. - - ```javascript - // Bütün uygulama tarafından kullanılan constant'lar - angular - .module('app.core') - .constant('moment', moment); - - // Sadece satış modülünün kullandığı constant'lar - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Dosya Şablonları ve Snippet'ler - -Tutarlı pattern'ler ve stiller için dosya şablonları ve snippet'ler kullanın. Burada bazı web geliştirme editörleri ve IDE'leri için şablon ve snippet'leri bulabilirsiniz. - -### Sublime Text -###### [Stil [Y250](#style-y250)] - - - Bu stil ve rehberleri izleyen Angular snippet'leri - - - [Sublime Angular snippet'leri](assets/sublime-angular-snippets?raw=true) indirin - - Packages klasörünün içine koyun - - Sublime'ı baştan başlatın - - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın - - ```javascript - ngcontroller // Angular controller'ı yaratır - ngdirective // Angular directive'i yaratır - ngfactory // Angular factory'si yaratır - ngmodule // Angular modülü yaratır - ngservice // Angular service'i yaratır - ngfilter // Angular filtresi yaratır - ``` - -### Visual Studio -###### [Stil [Y251](#style-y251)] - - - Bu stil ve rehberleri izleyen Angular snippet'leri [SideWaffle](http://www.sidewaffle.com) adresinde bulunabilir - - - [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix dosyası) indirin - - vsix dosyasını çalıştırın - - Visual Studio'yu baştan başlatın - -### WebStorm -###### [Stil [Y252](#style-y252)] - - - Bu stil ve rehberleri izleyen Angular snippet'leri. Webstorm ayarlarınızdan içeri aktarabilirsiniz: - - - [WebStorm Angular dosya şablonları ve snippet'leri](assets/webstorm-angular-file-template.settings.jar?raw=true)'ni indirin - - WebStorm'u açın ve `File` menüsüne gidin - - `Import Settings` seçeneğini seçin - - Dosyayı seçin ve `OK` tuşuna basın - - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın - - ```javascript - ng-c // creates an Angular controller - ng-f // creates an Angular factory - ng-m // creates an Angular module - ``` - -### Atom -###### [Stil [Y253](#style-y253)] - - - Bu stil ve rehberleri izleyen Angular snippet'leri. - ``` - apm install angularjs-styleguide-snippets - ``` - ya da - - Atom'u açın, Pakey yöneticisini açın (Packages -> Settings View -> Install Packages/Themes) - - 'angularjs-styleguide-snippets' paketini aratın - - 'Install' tuşuna basıp paketi yükleyin - - - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın - - ```javascript - ngcontroller // Angular controller'ı yaratır - ngdirective // Angular directive'i yaratır - ngfactory // Angular factory'si yaratır - ngmodule // Angular modülü yaratır - ngservice // Angular service'i yaratır - ngfilter // Angular filtresi yaratır - ``` - -### Brackets -###### [Stil [Y254](#style-y254)] - - - Bu stil ve rehberleri izleyen Angular snippet'leri. - - [Brackets Angular snippet'leri](assets/brackets-angular-snippets.yaml?raw=true)'ni indirin - - Brackets Extension Yöneticisini açın ( File > Extension manager ) - - ['Brackets Snippet'leri (by edc)'](https://github.com/chuyik/brackets-snippets)'ni yükleyin - - brackets'in sağ tarafındaki ampule tıklayın - - `Settings`'e tıklayın ve `Import` seçeneğini seçin - - Dosyayı seçin - - `Start Import` seçeneğini seçin - - - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // Angular controller'ı yaratır - ngdirective // Angular directive'i yaratır - ngfactory // Angular factory'si yaratır - ngapp // Angular modülü yaratır - ngservice // Angular service'i yaratır - ngfilter // Angular filtresi yaratır - - // These are partial snippets intended to chained - ngmodule // Angular module getter'i yaratır - ngstate // Angular UI Router tanımlayıcısı yaratır - ngconfig // konfigürasyon tanımlama fonksiyonu yaratır - ngrun // run bloğu tanımlama fonksiyonu yaratır - ngroute // Angular ngRoute 'when' tanımlama fonksiyonu yaratır - ngtranslate // $translate servisini promise'ile kullanan şablon yaratır - ``` - -### vim -###### [Stil [Y255](#style-y255)] - - - Bu stil ve rehberleri izleyen Angular vim snippet'leri. - - - [vim Angular snippet'leri](assets/vim-angular-snippets?raw=true)'ni indirin - - [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) dosyasını indirin - - snippet'leri snippet klasörüne kopyalayın - - - - Bu stil ve rehberleri izleyen Angular vim UltiSnips snippet'leri. - - - [vim Angular UltiSnips snippet'leri](assets/vim-angular-ultisnips?raw=true)'ni indirin - - [UltiSnips](https://github.com/SirVer/ultisnips) dosyasını indirin - - snippet'leri UltiSnips klasörüne kopyalayın - - ```javascript - ngcontroller // Angular controller'ı yaratır - ngdirective // Angular directive'i yaratır - ngfactory // Angular factory'si yaratır - ngmodule // Angular modülü yaratır - ngservice // Angular service'i yaratır - ngfilter // Angular filtresi yaratır - ``` - -### Visual Studio Code - -###### [Stil [Y256](#style-y256)] - - - [Visual Studio Code](http://code.visualstudio.com) stil ve rehberleri izleyen Angular vim snippet'leri - - - [VS Code Angular snippet'leri](assets/vscode-snippets/javascript.json?raw=true)'ni indirin - - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones - - snippet'leri snippet klasörüne kopyalayın, alternatif olarak snippetleri şu andaki snippetlerinizin olduğu yere kopyala/yapıştır yapın - - ```javascript - ngcontroller // Angular controller'ı yaratır - ngdirective // Angular directive'i yaratır - ngfactory // Angular factory'si yaratır - ngmodule // Angular modülü yaratır - ngservice // Angular service'i yaratır - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Yeoman Generator -###### [Stil [Y260](#style-y260)] - -[HotTowel yeoman üreticisi](http://jpapa.me/yohottowel)'ni Angular uygulamanız için bir başlangıç noktası yaratmak için ve stilleri takip etmek için kullanabilirsiniz. - -1. generator-hottowel'ı yükleyin - - ``` - npm install -g generator-hottowel - ``` - -2. Yeni bir klasör yaratın ve o klasörün içine girin - - ``` - mkdir myapp - cd myapp - ``` - -3. Üreticiyi çalıştırın - - ``` - yo hottowel helloWorld - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Routing - -Kullanıcı tarafında, küçük şablonlar ve directive'lerden oluşan view geçişleri için yapılan routing önemlidir. - -###### [Stil [Y270](#style-y270)] - - - Kullanıcı tarafındaki routing için [AngularUI Router](http://angular-ui.github.io/ui-router/)'ı kullanın. - - *Neden?*: UI Router Angular router'ın sağladığı bütün özellikleri sağlar ayrıca iç içe route ve state'ler için olanak sağlar. - - *Neden?*: Angular router sintaksına çok benzer ve UI Router'a geçiş yapmak kolaydır. - - - Not: `routerHelperProvider` gibi bir provider kullanarak dosyalar arası state konfigürasyonlarını ayarlama yapabilirsiniz. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Stil [Y271](#style-y271)] - - - View'lar için route'ları modül içerisinde tanımlayın. Her modül kend view'ları için gerekli olan route'ları barındırmalıdır. - - *Neden?*: Her modül kendi başına yeterli olmalıdır. - - *Neden?*: Bir modülü kaldırırken ya da eklerken, uygulama sadece var olan view'lara giden route'ları içermelidir. - - *Neden?*: Bu uygulamanın bazu bölümlerini açıp kaparken etrafta ulaşılamayacak route'lar kalmasını engeller. - -**[İçerik Listesi](#icerik-listesi)** - -## Görev Otomasyonu -Otomatik görevler yaratmak için [Gulp](http://gulpjs.com) ya da [Grunt](http://gruntjs.com) kullanın. Gulp convention over configuration paradigmasını benimserken, Grunt configuration over code prensibini benimser. Ben şahsen Gulp'ı tercih ediyorum. Yazması ve okuması daha kolay geliyor, ama ikisi de çok başarılılar. - -> [Gulp Pluralsight kursu](http://jpapa.me/gulpps)mda gulp ve otomatik görevler hakkında daha çok şey öğrenebilirsiniz. - -###### [Stil [Y400](#style-y400)] - - - Modül tanımlayıcı dosyalarını `*.module.js` diğer bütün uygulama JavaScript dosyalarından önce listelemek için otomatik görevleri kullanın. - - *Neden?*: Angular modüllerin kullanılmadan önce kayıt olmasını bekler. - - *Neden?*: Modülleri `*.module.js` gibi belirgin bir tarz ile isimlendirmeniz onları yakalamanızı ve listelemenizi kolaylaştırır. - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[İçerik Listesi](#icerik-listesi)** - -## Filtreler - -###### [Stil [Y420](#style-y420)] - - - Komplek objelerin bütün property'lerini taramak için filtreleri kullanmaktan kaçının. Filtreleri property'leri seçmek için kullanın. - - *Neden?*: Filtrelerin kullanımı kolaylıkla suistimal ediliyor ve uygulamanın performansını kötü etkiliyor. Örneğin bir filtrenin büyük ve derin bir objeyi taraması. - -**[İçerik Listesi](#icerik-listesi)** - -## Angular dökümantasyonu - -Burada anlatılanların dışındaki herşey için ve API referansı için [Angular dökümantasyonu](//docs.angularjs.org/api)'na bakın. - -## Katkıda bulunma - -Potensiyel değişiklik/eklemeler için öncelikle bir issue açın. Eğer rehber hakkında sorularınız varsa, onları da issue olarak açabilirsiniz. Eğer bir yazım hatası bulursanız pull request açın. Anafikir içeriği güncel tutmak ve github'ın kendi özelliklerini kullanarak bu işi issuelar ve pull request'lerle ilerletmek, bunların hepsi google tarafından indeksleniyor ve aramalar da çıkıyor. Neden? Çünkü bir sorunuz var ise, aynı soruyu başkası da soruyor olabilir! Burada nasıl katkıda bulunabileceğinize dair daha fazla bigi bulabilirsiniz. - -*Bu repository'ye katkıda bulunarak içeriğinizin bu repository'nin lisansına dahil olduğunu kabul etmiş bulunursunuz.* - -### Süreç - 1. Değişiklikleri bir issue ile tartışın. - 2. Bir Pull Request açın, issue'yu referans gösterin, ve değişikliği açıklayın. Bu değişiklik nasıl bir değer katıyor? - 3. Pull Requestiniz incelenecektir ve ya merge edilecek ya da geri çevrilecektir. - -## Lisans - -_uzun lafın kısası; Bu rehberi kullanın. Katkılarınız makbule geçecektir._ - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[İçerik Listesi](#icerik-listesi)** diff --git a/a1/i18n/zh-CN.md b/a1/i18n/zh-CN.md deleted file mode 100644 index e369cb42..00000000 --- a/a1/i18n/zh-CN.md +++ /dev/null @@ -1,3102 +0,0 @@ -# Angular规范 - -## Angular Team Endorsed -非常感谢领导Angular团队的Igor Minar对本指南做出的审查和贡献,并且委托我继续打理本指南。 - -## 目的 -*Angular规范[@john_papa](//twitter.com/john_papa)* - -如果你正在寻找一些关于语法、约定和结构化的Angular应用的一个有建设性的规范,那么你来对地方了。这里所包含的内容是基于我在团队中使用[Angular](//angularjs.org)的一些经验、一些演讲和[Pluralsight培训课程](http://pluralsight.com/training/Authors/Details/john-papa)。 - -这个规范的目的是为构建Angular应用提供指导,当然更加重要的是让大家知道我为什么要选择它们。 - ->如果你喜欢这个规范,请在Pluralsight看看[Angular Patterns: Clean Code](http://jpapa.me/ngclean)。 - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Community Awesomeness and Credit -Angular社区是一个热衷于分享经验的令人难以置信的社区,尽管Todd Motto(他是我的一个朋友,也是Angular专家)和我合作了多种规范和惯例,但是我们也存在着一些分歧。我鼓励你去看看[Todd的指南](https://github.com/toddmotto/angularjs-styleguide),在那里你能看到我们之间的区别。 - -我的许多规范都是从大量的程序会话[Ward Bell](http://twitter.com/wardbell)和我所拥有的而来的,我的好友Ward也影响了本规范的最终演变。 - -## 在示例App中了解这些规范 -看示例代码有助于你更好地理解,你可以在`modular`文件夹下找到[命名为modular的示例应用程序](https://github.com/johnpapa/ng-demos),随便克隆。 - -##翻译 -[Angular规范翻译版本](https://github.com/johnpapa/angular-styleguide/tree/master/i18n)。 - -##目录 - 1. [单一职责](#单一职责) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [解决Controller的Promises](#解决controller的promises) - 1. [手动依赖注入](#手动依赖注入) - 1. [压缩和注释](#压缩和注释) - 1. [异常处理](#异常处理) - 1. [命名](#命名) - 1. [应用程序结构的LIFT准则](#应用程序结构的lift准则) - 1. [应用程序结构](#应用程序结构) - 1. [模块化](#模块化) - 1. [启动逻辑](#启动逻辑) - 1. [Angular $包装服务](#angular-包装服务) - 1. [测试](#测试) - 1. [动画](#动画) - 1. [注释](#注释) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [常量](#常量) - 1. [文件模板和片段](#文件模板和片段) - 1. [Yeoman Generator](#yeoman-generator) - 1. [路由](#路由) - 1. [任务自动化](#任务自动化) - 1. [Filters](#filters) - 1. [Angular文档](#angularjs文档) - 1. [贡献](#贡献) - 1. [许可](#许可) - -## 单一职责 -###规则一 -###### [Style [Y001](#style-y001)] - - - 一个文件只定义一个组件。 - - 下面的例子在同一个文件中定义了一个`app`的module和它的一些依赖、一个controller和一个factory。 - - ```javascript - /* avoid */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - 推荐以下面的方式来做,把上面相同的组件分割成单独的文件。 - ```javascript - /* recommended */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommended */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[返回顶部](#目录)** - -## IIFE -### JavaScript闭包 -###### [Style [Y010](#style-y010)] - - - 把Angular组件包装到一个立即调用函数表达式中(IIFE)。 - - *为什么?*:把变量从全局作用域中删除了,这有助于防止变量和函数声明比预期在全局作用域中有更长的生命周期,也有助于避免变量冲突。 - - *为什么?*:当你的代码为了发布而压缩了并且被合并到同一个文件中时,可能会有很多变量发生冲突,使用了IIFE(给每个文件提供了一个独立的作用域),你就不用担心这个了。 - - ```javascript - /* avoid */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger function会被当作一个全局变量 - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage function会被当作一个全局变量 - function storage() { } - ``` - - ```javascript - /** - * recommended - * - * 再也不存在全局变量了 - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - 注:为了简洁起见,本规范余下的示例中将会省略IIFE语法。 - - - 注:IIFE阻止了测试代码访问私有成员(正则表达式、helper函数等),这对于自身测试是非常友好的。然而你可以把这些私有成员暴露到可访问成员中进行测试,例如把私有成员(正则表达式、helper函数等)放到factory或是constant中。 - -**[返回顶部](#目录)** - -## Modules - -###避免命名冲突 -###### [Style [Y020](#style-y020)] - - - 每一个独立子模块使用唯一的命名约定。 - - *为什么*:避免冲突,每个模块也可以方便定义子模块。 - -###定义(aka Setters) -###### [Style [Y021](#style-y021)] - - - 不使用任何一个使用了setter语法的变量来定义modules。 - - *为什么?*:在一个文件只有一个组件的条件下,完全不需要为一个模块引入一个变量。 - - ```javascript - /* avoid */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - 你只需要用简单的setter语法来代替。 - - ```javascript - /* recommended */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -###Getters -###### [Style [Y022](#style-y022)] - - - 使用module的时候,避免直接用一个变量,而是使用getter的链式语法。 - - *为什么?*:这将产生更加易读的代码,并且可以避免变量冲突和泄漏。 - - ```javascript - /* avoid */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -###Setting vs Getting -###### [Style [Y023](#style-y023)] - - - 只能设置一次。 - - *为什么?*:一个module只能被创建一次,创建之后才能被检索到。 - - - 设置module,`angular.module('app', []);`。 - - 获取module,`angular.module('app');`。 - -###命名函数 vs 匿名函数 -###### [Style [Y024](#style-y024)] - - - 回调函数使用命名函数,不要用匿名函数。 - - *为什么?*:易读,方便调试,减少嵌套回调函数的数量。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard () { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger () { } - ``` - -**[回到顶部](#目录)** - -## Controllers - -###controllerAs在View中的语法 -###### [Style [Y030](#style-y030)] - - - 使用[`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) 语法代替直接用经典的$scope定义的controller的方式。 - - *为什么?*:controller被构建的时候,就会有一个新的实例,`controllerAs` 的语法比`经典的$scope语法`更接近JavaScript构造函数。 - - *为什么?*:这促进在View中对绑定到“有修饰”的对象的使用(例如用`customer.name` 代替`name`),这将更有语境、更容易阅读,也避免了任何没有“修饰”而产生的引用问题。 - - *为什么?*:有助于避免在有嵌套的controllers的Views中调用 `$parent`。 - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -###controllerAs在controller中的语法 -###### [Style [Y031](#style-y031)] - - - 使用 `controllerAs` 语法代替 `经典的$scope语法` 语法。 - - - 使用`controllerAs` 时,controller中的`$scope`被绑定到了`this`上。 - - *为什么?*:`controllerAs` 是`$scope`的语法修饰,你仍然可以绑定到View上并且访问 `$scope`的方法。 - - *为什么?*:避免在controller中使用 `$scope`,最好不用它们或是把它们移到一个factory中。factory中可以考虑使用`$scope`,controller中只在需要时候才使用`$scope`,例如当使用[`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast),或者 [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on)。 - - ```javascript - /* avoid */ - function Customer ($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function Customer () { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -###controllerAs with vm -###### [Style [Y032](#style-y032)] - - - 使用`controllerAs`语法时把`this` 赋值给一个可捕获的变量,选择一个有代表性的名称,例如`vm`代表ViewModel。 - - *为什么?*:`this`在不同的地方有不同的语义(就是作用域不同),在controller中的一个函数内部使用`this`时可能会改变它的上下文。用一个变量来捕获`this`的上下文从而可以避免遇到这样的坑。 - - ```javascript - /* avoid */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function Customer () { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - - 注:你可以参照下面的做法来避免 [jshint](http://www.jshint.com/)的警告。但是构造函数(函数名首字母大写)是不需要这个的. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - 注:在controller中用`controller as`创建了一个watch时,可以用下面的语法监测`vm.*`的成员。(创建watch时要谨慎,因为它会增加更多的负载) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -###置顶绑定成员 - -###### [Style [Y033](#style-y033)] - - - 把可绑定的成员放到controller的顶部,按字母排序,并且不要通过controller的代码传播。 - - *为什么?*:虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员置顶,这会提高代码的可读性。 - - ```javascript - /* avoid */ - function Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - function Sessions() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) - - 注:如果一个函数就是一行,那么只要不影响可读性就把它放到顶部。 - - ```javascript - /* avoid */ - function Sessions(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -###函数声明隐藏实现细节 -###### [Style [Y034](#style-y034)] - - - 使用函数声明来隐藏实现细节,置顶绑定成员,当你需要在controller中绑定一个函数时,把它指向一个在文件的后面会出现函数声明。更多详情请看[这里](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code)。 - - *为什么?*:易读,易识别哪些成员可以在View中绑定和使用。 - - *为什么?*:把函数的实现细节放到后面,你可以更清楚地看到重要的东西。 - - *为什么?*:由于函数声明会被置顶,所以没有必要担心在声明它之前就使用函数的问题。 - - *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 - - *为什么?*:函数表达式中顺序是至关重要的。 - - ```javascript - /** - * avoid - * Using function expressions. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - 注意这里重要的代码分散在前面的例子中。 - 下面的示例中,可以看到重要的代码都放到了顶部。实现的详细细节都在下方,显然这样的代码更易读。 - - ```javascript - /* - * recommend - * Using function declarations - * and bindable members up top. - */ - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -###把Controller中的逻辑延迟到Service中 -###### [Style [Y035](#style-y035)] - - - 通过委派到service和factory中来延迟controller中的逻辑。 - - *为什么?*:把逻辑放到service中,并通过一个function暴露,就可以被多个controller重用。 - - *为什么?*:把逻辑放到service中将会使单元测试的时候更加容易地把它们分离,相反,如果在controller中调用逻辑就显得很二了。 - - *为什么?*:保持controller的简洁。 - - *为什么?*:从controller中删除依赖关系并且隐藏实现细节。 - - ```javascript - /* avoid */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit () { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recommended */ - function Order (creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit () { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); - }; - } - ``` - -###保持Controller的专一性 -###### [Style [Y037](#style-y037)] - - - 一个view定义一个controller,尽量不要在其它view中使用这个controller。把可重用的逻辑放到factory中,保证controller只服务于当前视图。 - - *为什么?*:不同的view用同一个controller是非常不科学的,良好的端对端测试覆盖率对于保证大型应用稳定性是必需的。 - -###分配Controller -###### [Style [Y038](#style-y038)] - - - 当一个controller必须匹配一个view时或者任何一个组件可能被其它controller或是view重用时,连同controller的route一起定义。 - - 注:如果一个view是通过route外的其它形式加载的,那么就用`ng-controller="Avengers as vm"`语法。 - - *为什么?*:在route中匹配controller允许不同的路由调用不同的相匹配的controller和view,当在view中通过[`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController)分配controller时,这个view总是和相同的controller相关联。 - - ```javascript - /* avoid - when using with a route and dynamic pairing is desired */ - - // route-config.js - angular - .module('app') - .config(config); - - function config ($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommended */ - - // route-config.js - angular - .module('app') - .config(config); - - function config ($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[返回顶部](#目录)** - -## Services - -###单例 -###### [Style [Y040](#style-y040)] - - - 用`new`实例化service,用`this`实例化公共方法和变量,由于这和factory是类似的,所以为了保持统一,推荐用facotry来代替。 - - 注意:[所有的Angular services都是单例](https://docs.angularjs.org/guide/services),这意味着每个injector都只有一个实例化的service。 - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger () { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger () { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[返回顶部](#目录)** - -## Factories - -###单一职责 -###### [Style [Y051](#style-y051)] - - - factory应该是[单一职责](http://en.wikipedia.org/wiki/Single_responsibility_principle),这是由其上下文进行封装的。一旦一个factory将要处理超过单一的目的时,就应该创建一个新的factory。 - -###单例 -###### [Style [Y051](#style-y051)] - - - facotry是一个单例,它返回一个包含service成员的对象。 - - 注:[所有的Angular services都是单例](https://docs.angularjs.org/guide/services),这意味着每个injector都只有一个实例化的service。 - -###可访问的成员置顶### -###### [Style [Y052](#style-y052)] - - - 使用从[显露模块模式](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript)派生出来的技术把service(它的接口)中可调用的成员暴露到顶部, - - *为什么?*:易读,并且让你可以立即识别service中的哪些成员可以被调用,哪些成员必须进行单元测试(或者被别人嘲笑)。 - - *为什么?*:当文件内容很长时,这可以避免需要滚动才能看到暴露了哪些东西。 - - *为什么?*:虽然你可以随意写一个函数,但当函数代码超过一行时就会降低可读性并造成滚动。通过把实现细节放下面、把可调用接口置顶的形式返回service的方式来定义可调用的接口,从而使代码更加易读。 - - ```javascript - /* avoid */ - function dataService () { - var someValue = ''; - function save () { - /* */ - }; - function validate () { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommended */ - function dataService () { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save () { - /* */ - }; - - function validate () { - /* */ - }; - } - ``` - - 这种绑定方式复制了宿主对象,原始值不会随着暴露模块模式的使用而更新。 - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) - -###函数声明隐藏实现细节 -###### [Style [Y053](#style-y053)] - - - 函数声明隐藏实现细节,置顶绑定成员,当你需要在controller中绑定一个函数时,把它指向一个函数声明,这个函数声明在文件的后面会出现。 - - *为什么?*:易读,易识别哪些成员可以在View中绑定和使用。 - - *为什么?*:把函数的实现细节放到后面,你可以更清楚地看到重要的东西。 - - *为什么?*:由于函数声明会被置顶,所以没有必要担心在声明它之前就使用函数的问题。 - - *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 - - *为什么?*:函数表达式中顺序是至关重要的。 - - ```javascript - /** - * avoid - * Using function expressions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommended - * Using function declarations - * and accessible members up top. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[返回顶部](#目录)** - -## Data Services - -###独立的数据调用 -###### [Style [Y060](#style-y060)] - - - 把进行数据操作和数据交互的逻辑放到factory中,数据服务负责XHR请求、本地存储、内存存储和其它任何数据操作。 - - *为什么?*:controller的作用是查看视图和收集视图的信息,它不应该关心如何取得数据,只需要知道哪里需要用到数据。把取数据的逻辑放到数据服务中能够让controller更简单、更专注于对view的控制。 - - *为什么?*:方便测试。 - - *为什么?*:数据服务的实现可能有非常明确的代码来处理数据仓库,这可能包含headers、如何与数据交互或是其它service,例如`$http`。把逻辑封装到单独的数据服务中,这隐藏了外部调用者(例如controller)对数据的直接操作,这样更加容易执行变更。 - - ```javascript - /* recommended */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - 注意:数据服务被调用时(例如controller),隐藏调用的直接行为,如下所示。 - - ```javascript - /* recommended */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('Avengers', Avengers); - - Avengers.$inject = ['dataservice', 'logger']; - - function Avengers(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -###数据调用返回一个Promise -###### [Style [Y061](#style-y061)] - - - 就像`$http`一样,调用数据时返回一个promise,在你的调用函数中也返回一个promise。 - - *为什么?*:你可以把promise链接到一起,在数据调用完成并且resolve或是reject这个promise后采取进一步的行为。 - - ```javascript - /* recommended */ - - activate(); - - function activate() { - /** - * Step 1 - * Ask the getAvengers function for the - * avenger data and wait for the promise - */ - return getAvengers().then(function() { - /** - * Step 4 - * Perform an action on resolve of final promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Ask the data service for the data and wait - * for the promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * set the data and resolve the promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[返回顶部](#目录)** - -## Directives -###一个directive一个文件 -###### [Style [Y070](#style-y070)] - - - 一个文件中只创建一个directive,并依照directive来命名文件。 - - *为什么?*:虽然把所有directive放到一个文件中很简单,但是当一些directive是跨应用的,一些是跨模块的,一些仅仅在一个模块中使用时,想把它们独立出来就非常困难了。 - - *为什么?*:一个文件一个directive也更加容易维护。 - - > 注: "**最佳实践**:Angular文档中有提过,directive应该自动回收,当directive被移除后,你可以使用`element.on('$destroy', ...)`或者`scope.$on('$destroy', ...)`来执行一个clean-up函数。" - - ```javascript - /* avoid */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order directive仅仅会被order module用到 */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales directive可以在sales app的任意地方使用 */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner directive可以在任意apps中使用 */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* implementation details */ - } - - function salesCustomerInfo() { - /* implementation details */ - } - - function sharedSpinner() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* calendarRange.directive.js */ - - /** - * @desc order directive that is specific to the order module at a company named Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* customerInfo.directive.js */ - - /** - * @desc sales directive that can be used anywhere across the sales app at a company named Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* spinner.directive.js */ - - /** - * @desc spinner directive that can be used anywhere across apps at a company named Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* implementation details */ - } - ``` - - 注:由于directive使用条件比较广,所以命名就存在很多的选项。选择一个让directive和它的文件名都清楚分明的名字。下面有一些例子,不过更多的建议去看[命名](#命名)章节。 - -###在directive中操作DOM -###### [Style [Y072](#style-y072)] - - - 当需要直接操作DOM的时候,使用directive。如果有替代方法可以使用,例如:使用CSS来设置样式、[animation services](https://docs.angularjs.org/api/ngAnimate)、Angular模板、[`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow)或者[`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide),那么就直接用这些即可。例如,如果一个directive只是想控制显示和隐藏,用ngHide/ngShow即可。 - - *为什么?*:DOM操作的测试和调试是很困难的,通常会有更好的方法(CSS、animations、templates)。 - -###提供一个唯一的Directive前缀 -###### [Style [Y073](#style-y073)] - - - 提供一个短小、唯一、具有描述性的directive前缀,例如`acmeSalesCustomerInfo`在HTML中声明为`acme-sales-customer-info`。 - - *为什么?*:方便快速识别directive的内容和起源,例如`acme-`可能预示着这个directive是服务于Acme company。 - - 注:避免使用`ng-`为前缀,研究一下其它广泛使用的directive避免命名冲突,例如[Ionic Framework](http://ionicframework.com/)的`ion-`。 - -###限制元素和属性 -###### [Style [Y074](#style-y074)] - - - 当创建一个directive需要作为一个独立元素时,restrict值设置为`E`(自定义元素),也可以设置可选值`A`(自定义属性)。一般来说,如果它就是为了独立存在,用`E`是合适的做法。一般原则是允许`EA`,但是当它是独立的时候这更倾向于作为一个元素来实施,当它是为了增强已存在的DOM元素时则更倾向于作为一个属性来实施。 - - *为什么?*:这很有意义! - - *为什么?*:虽然我们允许directive被当作一个class来使用,但如果这个directive的行为确实像一个元素的话,那么把directive当作元素或者属性是更有意义的。 - - 注:Angular 1.3 +默认使用EA。 - - ```html - -
- ``` - - ```javascript - /* avoid */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange () { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommended */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange () { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -###Directives和ControllerAs -###### [Style [Y075](#style-y075)] - - - directive使用`controller as`语法,和view使用`controller as`保持一致。 - - *为什么?*:因为不难且有必要这样做。 - - 注意:下面的directive演示了一些你可以在link和directive控制器中使用scope的方法,用controllerAs。这里把template放在行内是为了在一个地方写出这些代码。 - - 注意:关于依赖注入的内容,请看[手动依赖注入](#手动依赖注入)。 - - 注意:directive的控制器是在directive外部的,这种风格避免了由于注入造成的`return`之后的代码无法访问的情况。 - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller : ExampleController, - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injecting $scope just for comparison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - 注意:当你把controller注入到link的函数或可访问的directive的attributes时,你可以把它命名为控制器的属性。 - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { // 和上面例子的区别在于把vm直接传递进来 - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - 当directive中使用了`controller as`语法时,如果你想把父级作用域绑定到directive的controller作用域时,使用`bindToController = true`。 - - *为什么?*:这使得把外部作用域绑定到directive controller中变得更加简单。 - - 注意:Angular 1.3.0才介绍了`bindToController`。 - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[返回顶部](#目录)** - -## 解决Controller的Promises -###Controller Activation Promises -###### [Style [Y080](#style-y080)] - - - 在`activate`函数中解决controller的启动逻辑。 - - *为什么?*:把启动逻辑放在一个controller中固定的位置可以方便定位、有利于保持测试的一致性,并能够避免controller中到处都是激活逻辑。 - - *为什么?*:`activate`这个controller使得重用刷新视图的逻辑变得很方便,把所有的逻辑都放到了一起,可以让用户更快地看到视图,可以很轻松地对`ng-view` 或 `ui-view`使用动画,用户体验更好。 - - 注意:如果你需要在开始使用controller之前有条件地取消路由,那么就用[route resolve](#style-y081)来代替。 - - ```javascript - /* avoid */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -###Route Resolve Promises -###### [Style [Y081](#style-y081)] - - - 当一个controller在激活之前,需要依赖一个promise的完成时,那么就在controller的逻辑执行之前在`$routeProvider`中解决这些依赖。如果你需要在controller被激活之前有条件地取消一个路由,那么就用route resolver。 - - - 当你决定在过渡到视图之前取消路由时,使用route resolve。 - - *为什么?*:controller在加载前可能需要一些数据,这些数据可能是从一个通过自定义factory或是[$http](https://docs.angularjs.org/api/ng/service/$http)的promise而来的。[route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)允许promise在controller的逻辑执行之前解决,因此它可能对从promise中来的数据做一些处理。 - - *为什么?*:这段代码将在路由后的controller的激活函数中执行,视图立即加载,promise resolve的时候将会开始进行数据绑定,可以(通过`ng-view`或`ui-view`)在视图的过渡之间加个loading状态的动画。 - - 注意:这段代码将在路由之前通过一个promise来执行,拒绝了承诺就会取消路由,接受了就会等待路由跳转到新视图。如果你想更快地进入视图,并且无需验证是否可以进入视图,你可以考虑用[控制器 `activate` 技术](#style-y080)。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers (movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config ($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers (moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - 注意:下面这个例子展示了命名函数的路由解决,这种方式对于调试和处理依赖注入更加方便。 - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('Avengers', Avengers); - - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - 注意:示例代码中的`movieService`不符合安全压缩的做法,可以到[手动依赖注入](#手动依赖注入)和[压缩和注释](#压缩和注释)部分学习如何安全压缩。 - -**[返回顶部](#目录)** - -## 手动依赖注入 - -### 压缩的不安全性 -###### [Style [Y090](#style-y090)] - - - 声明依赖时避免使用缩写语法。 - - *为什么?*:组件的参数(例如controller、factory等等)将会被转换成各种乱七八糟错误的变量。例如,`common`和`dataservice`可能会变成`a`或者`b`,但是这些转换后的变量在Angular中是找不到的。 - - ```javascript - /* avoid - not minification-safe*/ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - 这一段代码在压缩时会产生错误的变量,因此在运行时就会报错。 - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -###手动添加依赖 -###### [Style [Y091](#style-y091)] - - - 用`$inject`手动添加Angular组件所需的依赖。 - - *为什么?*:这种技术反映了使用[`ng-annotate`](https://github.com/olov/ng-annotate)的技术,这就是我推荐的对依赖关系进行自动化创建安全压缩的方式,如果`ng-annotate`检测到已经有了注入,那么它就不会再次重复执行。 - - *为什么?*:可以避免依赖变成其它Angular找不到的变量,例如,`common`和`dataservice`可能会变成`a`或者`b`。 - - *为什么?*:避免创建内嵌的依赖,因为一个数组太长不利于阅读,此外,内嵌的方式也会让人感到困惑,比如数组是一系列的字符串,但是最后一个却是组件的function。 - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - 注意:当你的函数处于return语句后面,那么`$inject`是无法访问的(这会在directive中发生),你可以通过把Controller移到directive外面来解决这个问题。 - - ```javascript - /* avoid */ - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommended */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -###手动确定路由解析器依赖 -###### [Style [Y092](#style-y092)] - - - 用`$inject`手动给Angular组件添加路由解析器依赖。 - - *为什么?*:这种技术打破了路由解析的匿名函数的形式,易读。 - - *为什么?*:`$inject`语句可以让任何依赖都可以安全压缩。 - - ```javascript - /* recommended */ - function config ($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[返回顶部](#目录)** - -## 压缩和注释 - -###ng-annotate -###### [Style [Y100](#style-y100)] - - - 在[Gulp](http://gulpjs.com)或[Grunt](http://gruntjs.com)中使用[ng-annotate](//github.com/olov/ng-annotate),用`/** @ngInject */`对需要自动依赖注入的function进行注释。 - - *为什么?*:可以避免代码中的依赖使用到任何不安全的写法。 - - *为什么?*:不推荐用[`ng-min`](https://github.com/btford/ngmin)。 - - >我更喜欢Gulp,因为我觉得它是易写易读易调试的。 - - 下面的代码没有注入依赖,显然压缩是不安全的。 - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers (storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - 当上面的代码通过ng-annotate运行时,就会产生如下的带有`$inject`注释的输出结果,这样的话压缩就会安全了。 - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers (storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - 注意:如果`ng-annotate`检测到已经有注入了(例如发现了`@ngInject`),就不会重复生成`$inject`代码了。 - - 注意:路由的函数前面也可以用`/* @ngInject */` - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > 注意:从Angular 1.3开始,你就可以用[`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)指令的 `ngStrictDi`参数来检测任何可能失去依赖的地方,当以“strict-di”模式创建injector时,会导致应用程序无法调用不使用显示函数注释的函数(这也许无法安全压缩)。记录在控制台的调试信息可以帮助追踪出问题的代码。我只在需要调试的时候才会用到`ng-strict-di`。 - `` - -###使用Gulp或Grunt结合ng-annotate -###### [Style [Y101](#style-y101)] - - - 在自动化任务中使用[gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate)或[grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate),把`/* @ngInject */`注入到任何有依赖关系函数的前面。 - - *为什么?*:ng-annotate会捕获大部分的依赖关系,但是有时候需要借助于`/* @ngInject */`语法提示。 - - 下面的代码是gulp任务使用ngAnnotate的例子。 - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[返回顶部](#目录)** - -## 异常处理 - -###修饰符 -###### [Style [Y110](#style-y110)] - - - 使用一个[decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator),在配置的时候用[`$provide`](https://docs.angularjs.org/api/auto/service/$provide)服务,当发生异常时,在[`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler)服务中执行自定义的处理方法。 - - *为什么?*:在开发时和运行时提供了一种统一的方式来处理未被捕获的Angular异常。 - - 注:另一个选项是用来覆盖service的,这个可以代替decorator,这是一个非常nice的选项,但是如果你想保持默认行为,那么推荐你扩展一个decorator。 - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Could add the error to a service's collection, - * add errors to $rootScope, log errors to remote web server, - * or log locally. Or throw hard. It is entirely up to you. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -###异常捕获器 -###### [Style [Y111](#style-y111)] - - - 创建一个暴露了一个接口的factory来捕获异常并以合适方式处理异常。 - - *为什么?*:提供了一个统一的方法来捕获代码中抛出的异常。 - - 注:异常捕获器对特殊异常的捕获和反应是非常友好的,例如,使用XHR从远程服务获取数据时,你想要捕获所有异常并做出不同的反应。 - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -###路由错误 -###### [Style [Y112](#style-y112)] - - - 用[`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError)来处理并打印出所有的路由错误信息。 - - *为什么?*:提供一个统一的方式来处理所有的路由错误。 - - *为什么?*:当一个路由发生错误的时候,可以给展示一个提示信息,提高用户体验。 - - ```javascript - /* recommended */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionally log using a custom service or $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - - /** - * On routing error, go to another route/state. - */ - $location.path('/'); - - } - ); - } - ``` - -**[返回顶部](#目录)** - -## 命名 - -###命名原则 -###### [Style [Y120](#style-y120)] - - - 遵循以描述组件功能,然后是类型(可选)的方式来给所有的组件提供统一的命名,我推荐的做法是`feature.type.js`。大多数文件都有2个名字。 - * 文件名 (`avengers.controller.js`) - * 带有Angular的注册组件名 (`AvengersController`) - - *为什么?*:命名约定有助于为一目了然地找到内容提供一个统一的方式,在项目中和团队中保持统一性是非常重要的,保持统一性对于跨公司来说提供了巨大的效率。 - - *为什么?*:命名约定应该只为代码的检索和沟通提供方便。 - -###功能文件命名 -###### [Style [Y121](#style-y121)] - - - 遵循以“描述组件功能.类型(可选)”的方式来给所有的组件提供统一的命名,我推荐的做法是`feature.type.js`。 - - *为什么?*:为快速识别组件提供了统一的方式。 - - *为什么?*:为任何自动化的任务提供模式匹配。 - - ```javascript - /** - * common options - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommended - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - 注意:另外一种常见的约定就是不要用`controller`这个词来给controller文件命名,例如不要用`avengers.controller.js`,而是用`avengers.js`。所有其它的约定都坚持使用类型作为后缀,但是controller是组件中最为常用的类型,因此这种做法的好处貌似仅仅是节省了打字,但是仍然很容易识别。我建议你为你的团队选择一种约定,并且要保持统一性。我喜欢的命名方式是`avengers.controller.js`。 - - ```javascript - /** - * recommended - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -###测试文件命名 -###### [Style [Y122](#style-y122)] - - - 和组件命名差不多,带上一个`spec`后缀。 - - *为什么?*:为快速识别组件提供统一的方式。 - - *为什么?*:为[karma](http://karma-runner.github.io/)或是其它测试运行器提供模式匹配。 - - ```javascript - /** - * recommended - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -###Controller命名 -###### [Style [Y123](#style-y123)] - - - 为所有controller提供统一的名称,先特征后名字,鉴于controller是构造函数,所以要采用UpperCamelCase(每个单词首字母大写)的方式。 - - *为什么?*:为快速识别和引用controller提供统一的方式。 - - *为什么?*:UpperCamelCase是常规的识别一个可以用构造函数来实例化的对象的方式。 - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengers(){ } - ``` - -###Controller命名后缀 -###### [Style [Y124](#style-y124)] - - - 使用`Controller`。 - - *为什么?*:`Controller`使用更广泛、更明确、更具有描述性。 - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -###Factory命名 -###### [Style [Y125](#style-y125)] - - - 一样要统一,对service和factory使用camel-casing(驼峰式,第一个单词首字母小写,后面单词首字母大写)方式。避免使用`$`前缀。 - - *为什么?*:可以快速识别和引用factory。 - - *为什么?*:避免与内部使用`$`前缀的服务发生冲突。 - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - -###Directive组件命名 -###### [Style [Y126](#style-y126)] - - - 使用camel-case方式,用一个短的前缀来描述directive在哪个区域使用(一些例子中是使用公司前缀或是项目前缀)。 - - *为什么?*:可以快速识别和引用controller。 - - ```javascript - /** - * recommended - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile(){ } - ``` - -###模块 -###### [Style [Y127](#style-y127)] - - - 当有很多的模块时,主模块文件命名成`app.module.js`,其它依赖模块以它们代表的内容来命名。例如,一个管理员模块命名成`admin.module.js`,它们各自的注册模块名字就是`app`和`admin`。 - - *为什么?*:给多模块的应用提供统一的方式,这也是为了扩展大型应用。 - - *为什么?*:对使用任务来自动化加载所有模块的定义(先)和其它所有的angular文件(后)提供了一种简单的方式。 - -###配置 -###### [Style [Y128](#style-y128)] - - - 把一个模块的配置独立到它自己的文件中,以这个模块为基础命名。`app`模块的配置文件命名成`app.config.js`(或是`config.js`),`admin.module.js`的配置文件命名成`admin.config.js`。 - - *为什么?*:把配置从模块定义、组件和活跃代码中分离出来。 - - *为什么?*:为设置模块的配置提供了一个可识别的地方。 - -###路由 -###### [Style [Y129](#style-y129)] - - - 把路由的配置独立到单独的文件。主模块的路由可能是`app.route.js`,`admin`模块的路由可能是`admin.route.js`。即使是在很小的应用中,我也喜欢把路由的配置从其余的配置中分离出来。 - -**[返回顶部](#目录)** - -## 应用程序结构的LIFT准则 -###LIFT -###### [Style [Y140](#style-y140)] - - - 构建一个可以快速定位(`L`ocate)代码、一目了然地识别(`I`dentify)代码、拥有一个平直(`F`lattest)的结构、尽量(`T`ry)坚持DRY(Don’t Repeat Yourself)的应用程序,其结构应该遵循这4项基本准则。 - - *为什么是LIFT?*: 提供一个有良好扩展的结构,并且是模块化的,更快的找到代码能够帮助开发者提高效率。另一种检查你的app结构的方法就是问你自己:你能多块地打开涉及到一个功能的所有相关文件并开始工作? - - 当我发现我的的代码结构很恶心的时候,我就重新看看LIFT准则。 - - 1. 轻松定位代码(L) - 2. 一眼识别代码(I) - 3. 平直的代码结构(层级不要太多)(F) - 4. 尽量保持不要写重复代码(T) - -###Locate -###### [Style [Y141](#style-y141)] - - - 更直观、更简单、更快捷地定位代码 - - *为什么?*:我发现这对于一个项目是非常重要的,如果一个团队不能快速找到他们需要工作的文件,这将不能使团队足够高效地工作,那么这个代码结构就得改变。你可能不知道文件名或是相关的文件放在了哪里,那么就把他们放在最直观的地方,放在一起会节省大量的时间。下面是一个参考目录结构。 - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -###Identify -###### [Style [Y142](#style-y142)] - - - 当你看到一个文件时你应该能够立即知道它包含了什么、代表了什么。 - - *为什么?*:你花费更少的时间来了解代码代表了什么,并且变得更加高效。如果这意味着你需要更长的名字,那么就这么干吧。文件名一定要具有描述性,保持和文件内容互为一体。避免文件中有多个controller,多个service,甚至是混合的。 - -###Flat -###### [Style [Y143](#style-y143)] - - - 尽可能长时间地保持一个平直的文件夹结构,如果你的文件夹层级超过7+,那么就开始考虑分离。 - - *为什么?*:没有谁想在一个7级文件夹中寻找一个文件,你可以考虑一下网页导航栏有那么多层。文件夹结构没有硬性规则,但是当一个文件夹下的文件有7-10个,那么就是时候创建子文件夹了,文件夹的层级一定要把握好。一直使用一个平直的结构,直到确实有必要(帮助其它的LIFT)创建一个新的文件夹。 - -###T-DRY(尽量坚持DRY) -###### [Style [Y144](#style-y144)] - - - 坚持DRY,但是不要疯了一样的做却牺牲了可读性。 - - *为什么?*:保持DRY很重要,但是如果牺牲了其它LIFT,那么它就没那么重要了,这就是为什么说尽量坚持DRY。 - -**[返回顶部](#目录)** - -## 应用程序结构 - -###总规范 -###### [Style [Y150](#style-y150)] - - - 有实施的短期看法和长远的目标,换句话说,从小处入手,但是要记住app的走向。app的所有代码都在一个叫做`app`的根目录下,所有的内容都遵循一个功能一个文件,每一个controller、service、module、view都是独立的文件。第三方脚本存放在另外的根文件夹中(`bower_components`、`scripts`、`lib`)。 - - 注:了解实例结构的具体信息看[Angular应用结构](http://www.johnpapa.net/angular-app-structuring-guidelines/)。 - -###Layout -###### [Style [Y151](#style-y151)] - - - 把定义应用程序总体布局的组件放到`layout`文件夹中,如导航、内容区等等。 - - *为什么?*:复用。 - -###按功能划分文件夹结构 -###### [Style [Y152](#style-y152)] - - - 按照它们代表的功能来给创建的文件夹命名,当文件夹包含的文件超过7个(根据需要自行调整数量限制),就考虑新建文件夹。 - - *为什么?*:开发者可以快速定位代码、快速识别文件代表的意思,结构尽可能平直,没有重复,没有多余名字。 - - *为什么?*:LIFT规范都包括在内。 - - *为什么?*:通过组织内容和让它们保持和LIFT指导准则一致,帮助降低应用程序变得混乱的可能性。 - - *为什么?*:超过10个文件时,在一个一致性的文件夹中很容易定位,但是在一个平直的文件夹结构中确实很难定位。 - - ```javascript - /** - * recommended - */ - - app/ - app.module.js - app.config.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![实例App结构](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) - - 注意:不要使用按类型划分文件夹结构,因为如果这样的话,当做一个功能时,需要在多个文件夹中来回切换。当应用程序有5个、10个,甚至是25个以上的view、controller(或其他feature)时,这种方式将迅速变得不实用,这就使得它定位文件比按功能分文件夹的方式要困难的多。 - - ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[返回顶部](#目录)** - -## 模块化 - -###许多小的、独立的模块 -###### [Style [Y160](#style-y160)] - - - 创建只封装一个职责的小模块。 - - *为什么?*:模块化的应用程序很容易添加新的功能。 - -###创建一个App Module -###### [Style [Y161](#style-y161)] - - - 创建一个应用程序的根模块,它的职责是把应用程序中所有的模块和功能都放到一起。 - - *为什么?*:Angular鼓励模块化和分离模式。创建根模块的作用是把其它模块都绑定到一起,这为增加或是删除一个模块提供了非常简单的方法。 - - *为什么?*:应用程序模块变成了一个描述哪些模块有助于定义应用程序的清单。 - -###保持App Module的精简 -###### [Style [Y162](#style-y162)] - - - app module中只放聚合其它模块的逻辑,具体功能在它们自己的模块中实现。 - - *为什么?*:添加额外的代码(获取数据、展现视图、其它和聚合模块无关的代码)到app module中使app module变得很糟糕,也使得模块难以重用和关闭。 - -###功能区域就是模块 -###### [Style [Y163](#style-y163)] - - - 创建代表功能区的模块,例如布局、可重用、共享服务、仪表盘和app的特殊功能(例如客户、管理、销售)。 - - *为什么?*:自包含的模块可以无缝地被添加到应用程序中。 - - *为什么?*:项目进行功能迭代时,可以专注于功能,在开发完成启用它们即可。 - - *为什么?*:把功能拆分成不同模块方便测试。 - -###可重用的块就是模块 -###### [Style [Y164](#style-y164)] - - - 为通用service创建代表可重用的应用程序块的模块,例如异常处理、日志记录、诊断、安全性和本地数据储藏等模块。 - - *为什么?*:这些类型的功能在很多应用程序中都需要用到,所以把它们分离到自己的模块中,它们可以变成通用的应用程序,也能被跨应用地进行重用。 - -###模块依赖 -###### [Style [Y165](#style-y165)] - - - 应用程序根模块依赖于应用程序特定的功能模块、共享的和可复用的模块。 - - ![模块化和依赖](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) - - *为什么?*:主程序模块包含一个能快速识别应用程序功能的清单。 - - *为什么?*:每个功能区都包含一个它依赖了哪些模块的列表,因此其它应用可以把它当作一个依赖引入进来。 - - *为什么?*:程序内部的功能,如共享数据的服务变得容易定位,并且从`app.core`中共享。 - - 注意:这是保持一致性的一种策略,这里有很多不错的选择,选择一种统一的,遵循Angular依赖规则,这将易于维护和扩展。 - - > 我的不同项目间的结构略有不同,但是它们都遵循了这些结构和模块化的准则,具体的实施方案会根据功能和团队发生变化。也就是说,不要在一棵树上吊死,但是心中一定要记得保持一致性、可维护性和效率。 - - > 小项目中,你可以直接把所有依赖都放到app module中,这对于小项目来说比较容易维护,但是想在此项目外重用模块就比较难了。 - -**[返回顶部](#目录)** - -## 启动逻辑 - -### 配置 -###### [Style [Y170](#style-y170)] - - - 必须在angular应用启动前进行配置才能把代码注入到[模块配置](https://docs.angularjs.org/guide/module#module-loading-dependencies),理想的一些case应该包括providers和constants。 - - *为什么?*:这使得在更少的地方进行配置变得容易。 - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### 运行代码块 -###### [Style [Y171](#style-y171)] - - - 任何在应用程序启动时需要运行的代码都应该在factory中声明,通过一个function暴露出来,然后注入到[运行代码块](https://docs.angularjs.org/guide/module#module-loading-dependencies)中。 - - *为什么?*:直接在运行代码块处写代码将会使得测试变得很困难,相反,如果放到facotry则会使的抽象和模拟变得很简单。 - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[返回顶部](#目录)** - -##Angular $包装服务 - -###$document和$window -###### [Style [Y180](#style-y180)] - - - 用[`$document`](https://docs.angularjs.org/api/ng/service/$document)和[`$window`](https://docs.angularjs.org/api/ng/service/$window)代替`document`和`window`。 - - *为什么?*:使用内部包装服务将更容易测试,也避免了你自己去模拟document和window。 - -###$timeout和$interval -###### [Style [Y181](#style-y181)] - - - 用[`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout)和[`$interval`](https://docs.angularjs.org/api/ng/service/$interval)代替`setTimeout`和`setInterval` 。 - - *为什么?*:易于测试,处理Angular消化周期从而保证数据的同步绑定。 - -**[返回顶部](#目录)** - -## 测试 -单元测试有助于保持代码的清晰,因此我加入一些关于单元测试的基础和获取更多信息的链接。 - -###用故事来编写测试 -###### [Style [Y190](#style-y190)] - - - 给每一个故事都写一组测试,先创建一个空的测试,然后用你给这个故事写的代码来填充它。 - - *为什么?*:编写测试有助于明确规定你的故事要做什么、不做什么以及你如何判断是否成功。 - - ```javascript - it('should have Avengers controller', function() { - //TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - //TODO - }); - - it('should have 10 Avengers', function() { - //TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - //TODO ($httpBackend?) - }); - - // and so on - ``` - -###测试库 -###### [Style [Y191](#style-y191)] - - - 用[Jasmine](http://jasmine.github.io/)或者[Mocha](http://mochajs.org)进行单元测试。 - - *为什么?*:Angular社区中Jasmine和Mocha都用的很广,两者都很稳定,可维护性好,提供强大的测试功能。 - - 注意:使用Mocha时你可以考虑选择一个类似[Chai](http://chaijs.com)的提示库。 - -###测试运行器 -###### [Style [Y192](#style-y192)] - - - [Karma](http://karma-runner.github.io)。 - - *为什么?*:Karma容易配置,代码发生修改时自动运行。 - - *为什么?*:可以通过自身或是Grunt、Gulp方便地钩入持续集成的进程。 - - *为什么?*:一些IDE已经开始集成Karma了,如[WebStorm](http://www.jetbrains.com/webstorm/)和[Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225)。 - - *为什么?*:Karma可以很好的和自动化任务工具如[Grunt](http://www.gruntjs.com)(带有[grunt-karma](https://github.com/karma-runner/grunt-karma))和[Gulp](http://www.gulpjs.com)(带有[gulp-karma](https://github.com/lazd/gulp-karma))合作。 - -###Stubbing和Spying -###### [Style [Y193](#style-y193)] - - - 用[Sinon](http://sinonjs.org/)。 - - *为什么?*:Sinon可以和Jasmine和Mocha合作良好,并且可以扩展它们提供的stubbing和spying。 - - *为什么?*:如果你想试试Jasmine和Mocha,用Sinon在它们中间来回切换是很方便的。我更喜欢Mocha。 - - *为什么?*:测试失败Sinon有一个具有描述性的信息。 - -###Headless Browser -###### [Style [Y194](#style-y194)] - - - 在服务器上使用[PhantomJS](http://phantomjs.org/)来运行你的测试。 - - *为什么?*:PhantomJS是一个headless browser,无需一个“可视”的浏览器来帮助你运行测试。因此你的服务器上不需要安装Chrome、Safari、IE或是其它浏览器。 - - 注意:你仍然需要在你的环境下测试所有浏览器,来满足用户的需求。 - -###代码分析 -###### [Style [Y195](#style-y195)] - - -在你的测试上运行JSHint。 - - *为什么?*:测试也是代码,JSHint能够帮你识别代码中可能导致测试无法正常工作的的质量问题。 - -###对测试降低全局JSHint规则 -###### [Style [Y196](#style-y196) - - - 对你的测试代码放宽规则,这样可以允许使用`describe`和`expect`等类似通用的全局方法。对表达式放宽规则,就行Mocha一样。 - - *为什么?*:测试也是代码,因此要和对待其它生产代码一样重视测试代码的质量。然而,测试框架中允许使用全局变量,例如,在你的测试单例中允许使用this。 - - ```javascript - /* jshint -W117, -W030 */ - ``` - 或者你也可以把下面的这几行加入到你的JSHint Options文件中。 - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![测试工具](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) - -### 组织测试 -###### [Style [Y197](#style-y197)] - - - 将单元测试文件(specs)同被测试客户端代码并列放在同一个文件夹下,将多个组件共用的测试文件以及服务器集成测试的文件放到一个单独的`tests`文件夹下。 - - *为什么?*:单元测试和源代码中的每一个组件和文件都有直接的相关性。 - - *为什么?*:这样它就会一直在你的视野中,很容易让它们保持在最新状态。编码的时候无论你做TDD还是在开发过程中测试,或者开发完成后测试,这些单测都不会脱离你的视线和脑海,这样就更容易维护,也有助于保持代码的覆盖率。 - - *为什么?*:更新源代码的时候可以更简单地在同一时间更新测试代码。 - - *为什么?*:方便源码阅读者了解组件如何使用,也便于发现其中的局限性。 - - *为什么?*:方便找。 - - *为什么?*:方便使用grunt或者gulp。 - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[返回顶部](#目录)** - -## 动画 - -###用法 -###### [Style [Y210](#style-y210)] - - - 在页面过渡时使用[Angular动画](https://docs.angularjs.org/guide/animations),包括[ngAnimate模块](https://docs.angularjs.org/api/ngAnimate)。三个关键点是细微、平滑、无缝。 - - *为什么?*:使用得当的话能够提高用户体验。 - - *为什么?*:当视图过渡时,微小的动画可以提高感知性。 - -###Sub Second -###### [Style [Y211](#style-y211)] - - - 使用短持续性的动画,我一般使用300ms,然后调整到合适的时间。 - - *为什么?*:长时间的动画容易造成用户认为程序性能太差的影响。 - -###animate.css -###### [Style [Y212](#style-y212)] - - - 传统动画使用[animate.css](http://daneden.github.io/animate.css/)。 - - *为什么?*:css提供的动画是快速的、流畅的、易于添加到应用程序中的。 - - *为什么?*:为动画提供一致性。 - - *为什么?*:animate.css被广泛使用和测试。 - - 注意:参阅[Matias Niemelä的关于Angular动画的文章](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[返回顶部](#目录)** - -## 注释 - -### jsDoc -###### [Style [Y220](#style-y220)] - - - 如果你准备做一个文档,那么就使用[`jsDoc`](http://usejsdoc.org/)的语法来记录函数名、描述、参数和返回值。使用`@namespace`和`@memberOf`来匹配应用程序结构。 - - *为什么?*:你可以从代码中生成(重新生成)文档,而不必从头开始编写文档。 - - *为什么?*:使用业内通用工具保持了统一性。 - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger ($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[返回顶部](#目录)** - -## JS Hint - -###使用一个Options文件 -###### [Style [Y230](#style-y230)] - - - 用JS Hint来分析你的JavaScript代码,确保你自定义了JS Hint选项文件并且包含在源控制里。详细信息:[JS Hint文档](http://www.jshint.com/docs/)。 - - *为什么?*:提交代码到原版本之前先发出警告。 - - *为什么?*:统一性。 - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[返回顶部](#目录)** - -## JSCS - -### 用一个Options文件 -###### [Style [Y235](#style-y235)] - - - 使用JSCS检查代码规范,确保你的代码控制中有定制的JSCS options文件,在这里[JSCS docs](http://www.jscs.info)查看更多信息。 - - *为什么?*:提交代码前第一时间提供一个预警。 - - *为什么?*:保持团队的一致性。 - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowMultipleLineStrings": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[返回顶部](#目录)** - -## 常量 - -###供应全局变量 -###### [Style [Y240](#style-y240)] - - - 为供应库中的全局变量创建一个Angular常量。 - - *为什么?*:提供一种注入到供应库的方法,否则就是全局变量。通过让你更容易地了解你的组件之间的依赖关系来提高代码的可测试性。这还允许你模拟这些依赖关系,这是很有意义的。 - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - 对于一些不需要变动,也不需要从其它service中获取的值,使用常量定义,当一些常量只是在一个模块中使用但是有可能会在其它应用中使用的话,把它们写到一个以当前的模块命名的文件中。把常量集合到一起是非常有必要的,你可以把它们写到`constants.js`的文件中。 - - *为什么?*:一个可能变化的值,即使变动的很少,也会从service中重新被检索,因此你不需要修改源代码。例如,一个数据服务的url可以被放到一个常量中,但是更好的的做法是把它放到一个web service中。 - - *为什么?*:常量可以被注入到任何angular组件中,包括providers。 - - *为什么?*:当一个应用程序被分割成很多可以在其它应用程序中复用的小模块时,每个独立的模块都应该可以操作它自己包含的相关常量。 - - ```javascript - // Constants used by the entire app - angular - .module('app.core') - .constant('moment', moment); - - // Constants used only by the sales module - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[返回顶部](#目录)** - -## 文件模板和片段 -为了遵循一致的规范和模式,使用文件模板和片段,这里有针对一些web开发的编辑器和IDE的模板和(或)片段。 - -###Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular片段遵循这些规范。 - - - 下载[Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) - - 把它放到Packages文件夹中 - - 重启Sublime - - 在JavaScript文件中输入下面的命令然后按下`TAB`键即可: - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -###Visual Studio -###### [Style [Y251](#style-y251)] - - - Angular文件遵循[SideWaffle](http://www.sidewaffle.com)所介绍的规范。 - - - 下载Visual Studio扩展文件[SideWaffle](http://www.sidewaffle.com) - - 运行下载的vsix文件 - - 重启Visual Studio - -###WebStorm -###### [Style [Y252](#style-y252)] - - - 你可以把它们导入到WebStorm设置中: - - - 下载[WebStorm Angular file templates and snippets](https://github.com/johnpapa/angular-styleguide/blob/master/assets/webstorm-angular-file-template.settings.jar?raw=true) - - 打开WebStorm点击`File`菜单 - - 选择`Import Settings`菜单选项 - - 选择文件点击`OK` - - 在JavaScript文件中输入下面的命令然后按下`TAB`键即可: - - ```javascript - ng-c // creates an Angular controller - ng-f // creates an Angular factory - ng-m // creates an Angular module - ``` - -### Atom -###### [Style [Y253](#style-y253)] - - - Angular片段遵循以下规范。 - ``` - apm install angularjs-styleguide-snippets - ``` - 或 - - 打开Atom,打开包管理器(Packages -> Settings View -> Install Packages/Themes) - - 搜索'angularjs-styleguide-snippets' - - 点击'Install' 进行安装 - - - JavaScript文件中输入以下命令后以`TAB`结束 - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular代码片段遵循以下规范。 - - - 下载[Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) - - 拓展管理器( File > Extension manager ) - - 安装['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Click the light bulb in brackets' right gutter - - Click `Settings` and then `Import` - - Choose the file and select to skip or override - - Click `Start Import` - - - JavaScript文件中输入以下命令后以`TAB`结束 - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngapp // creates an Angular module setter - ngservice // creates an Angular service - ngfilter // creates an Angular filter - - // These are partial snippets intended to chained - ngmodule // creates an Angular module getter - ngstate // creates an Angular UI Router state defintion - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngroute // creates an Angular routeProvider - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - vim代码片段遵循以下规范。 - - - 下载[vim Angular代码段](assets/vim-angular-snippets?raw=true) - - 设置[neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - 粘贴到snippet路径下 - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` -**[返回顶部](#目录)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -你可以使用[HotTowel yeoman generator](http://jpapa.me/yohottowel)来创建一个遵循本规范的Angular入门应用。 - -1. 安装generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. 创建一个新的文件夹并定位到它 - - ``` - mkdir myapp - cd myapp - ``` - -3. 运行生成器 - - ``` - yo hottowel helloWorld - ``` - -**[返回顶部](#目录)** - -## 路由 -客户端路由对于在视图和很多小模板和指令组成的构成视图中创建导航是非常重要的。 - -###### [Style [Y270](#style-y270)] - - - 用[AngularUI Router](http://angular-ui.github.io/ui-router/)来做路由控制。 - - *为什么?*:它包含了Angular路由的所有特性,并且增加了一些额外的特性,如嵌套路由和状态。 - - *为什么?*:语法和Angular路由很像,很容易迁移到UI Router。 - - - 注意:你可以在运行期间使用`routerHelperProvider`配置跨文件状态 - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - Define routes for views in the module where they exist,Each module should contain the routes for the views in the module. - - *为什么?*:每个模块应该是独立的。 - - *为什么?*:当删除或增加一个模块时,应用程序只包含指向现存视图的路由。(也就是说删除模块和增加模块都需更新路由) - - *为什么?*:这使得可以在不关心孤立的路由时很方便地启用或禁用应用程序的某些部分。 - -**[返回顶部](#目录)** - -## 任务自动化 -用[Gulp](http://gulpjs.com)或者[Grunt](http://gruntjs.com)来创建自动化任务。Gulp偏向于代码优先原则(code over configuration)而Grunt更倾向于配置优先原则(configuration over code)。我更倾向于使用gulp,因为gulp写起来比较简单。 - -> 可以在我的[Gulp Pluralsight course](http://jpapa.me/gulpps)了解更多gulp和自动化任务的信息 - -###### [Style [Y400](#style-y400)] - - - 用任务自动化在其它JavaScript文件之前列出所有模块的定义文件`*.module.js`。 - - *为什么?*:Angular中,模块使用之前必须先注册。 - - *为什么?*:带有特殊规则的模块命名,例如`*.module.js`,会让你很轻松地识别它们。 - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[返回顶部](#目录)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - 避免使用filters扫描一个复杂对象的所有属性,应该用filters来筛选选择的属性。 - - *为什么?*:不恰当的使用会造成滥用并且会带来糟糕的性能问题,例如对一个复杂的对象使用过滤器。 - -**[返回顶部](#目录)** - -## Angular文档 -[Angular文档](//docs.angularjs.org/api)。 - -## 贡献 - -先打开一个问题讨论潜在的变化和增加。如果你对这篇规范有任何疑惑,随时在仓库中提出问题。如果你发现了一个错字,创建一个pull request。这样做是为了保持内容的更新,使用github的原生功能通过问题和PR来帮助讲述这个故事,具体做法可以google一下。为什么?因为如果你有问题,其他人可能有同样的问题,你在这里可以学到如何贡献。 - -*贡献代码到这个仓库就意味着你同意了本仓库的许可证内容* - -###过程 - 1. 在Github Issue中讨论这个问题。 - 2. 拉取一个pull request,引用这个问题,解释你做的修改和为什么要这样做。 - 3. pull request将会被进行评估,结果就是合并或是拒绝。 - -## 许可 - - - **tldr;** 如果可以的话,使用本规范的时候还是指明归属吧。 - -### Copyright - -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) - -### (The MIT License) -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**[返回顶部](#目录)** diff --git a/a2/README.md b/a2/README.md deleted file mode 100644 index a2635b56..00000000 --- a/a2/README.md +++ /dev/null @@ -1,423 +0,0 @@ -# Angular 2 Style Guide **D R A F T** -Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. - -My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definitely ask questions when someone, including me, publishes a guide :) -d -## Angular Team Endorsed -Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. - -## Purpose -*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* - -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. - -The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. - ->If you like this guide, check out my Angular 2 First Look course **coming soon** to Pluralsight. - -## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. - -##Translations -Translations of this Angular 2 style guide are maintained by the community. Due to the in flux nature of this guide, I will hold off on accepting contributions for the time being. But I hope to get the same amazing contributions we had for the v1 of the guide! - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [Modules](#modules) - 1. [Components](#components) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Angular CLI](#angular-cli) - 1. [Routing](#routing) - 1. [Angular Docs](#angular-docs) - -## Single Responsibility - -### Rule of 1 -###### [Style [A2-001](#style-a2-001)] - - - Define 1 component per file, recommended to be less than 500 lines of code. - - *Why?*: One component per file promotes easier unit testing and mocking. - - *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - - *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - - The following example defines the `app` module and its dependencies, defines a component, and defines a factory all in the same file. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Small Functions -###### [Style [A2-002](#style-a2-002)] - - - Define small functions, no more than 75 LOC (less is better). - - *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. - - *Why?*: Small functions promote reuse. - - *Why?*: Small functions are easier to read. - - *Why?*: Small functions are easier to maintain. - - *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [A2-020](#style-a2-020)] - - - Use unique naming conventions with separators for sub-modules. - - *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. - -### Definitions (aka Setters) -###### [Style [A2-021](#style-a2-021)] - -## Components - -### Bindable Members Up Top -###### [Style [A2-033](#style-a2-033)] - - - Place bindable members at the top of the component, alphabetized, and not spread through the component code. - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the component can be bound and used in the View. - - *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. - - **example coming soon** - -### Defer Logic to Services -###### [Style [A2-035](#style-a2-035)] - - - Defer logic in a component by delegating to services. - - *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. - - *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. - - *Why?*: Removes dependencies and hides implementation details from the component. - - *Why?*: Keeps the component slim, trim, and focused. - - **example coming soon** - -### Keep Components Focused -###### [Style [A2-037](#style-a2-037)] - - - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. - - *Why?*: Reusing components with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [A2-040](#style-a2-040)] - - - Services are singletons and should be used for sharing data and functionality. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Single Responsibility -###### [Style [A2-050](#style-a2-050)] - - - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Singletons -###### [Style [A2-051](#style-a2-051)] - - - Factories are singletons and return an object that contains the members of the service. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Accessible Members Up Top -###### [Style [A2-052](#style-a2-052)] - - - Expose the callable members of the service (its interface) at the top - - *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). - - *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. - - *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [A2-060](#style-a2-060)] - - - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. - - *Why?*: The component's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the component be simpler and more focused on the view. - - *Why?*: This makes it easier to test (mock or real) the data calls when testing a component that uses a data service. - - *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [A2-120](#style-a2-120)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: - * the file name (`avengers.component.ts`) - * the registered component name with Angular (`Component`) - - *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. - - *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Feature File Names -###### [Style [A2-121](#style-a2-121)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.ts`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for any automated tasks. - -### Test File Names -###### [Style [A2-122](#style-a2-122)] - - - Name test specifications similar to the component they test with a suffix of `spec`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Component Names -###### [Style [A2-123](#style-a2-123)] - - - Use consistent names for all components named after their feature. Use UpperCamelCase for components, as they are constructors. - - *Why?*: Provides a consistent way to quickly identify and reference components. - - *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Component Name Suffix -###### [Style [A2-124](#style-a2-124)] - - - Append the component name with the suffix `Component`. - - *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. - - ```typescript - /** - * recommended - */ - - // avengers.component.ts - export class AvengersComponent { } - ``` - -### Service Names -###### [Style [A2-125](#style-a2-125)] - - - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). - - *Why?*: Provides a consistent way to quickly identify and reference services. - - *Why?*: Clear service names such as `logger` do not require a suffix. - - *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `AvengersService`. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Directive Component Names -###### [Style [A2-126](#style-a2-126)] - - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - - *Why?*: Provides a consistent way to quickly identify and reference components. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Routes -###### [Style [A2-129](#style-a2-129)] - - - Separate route configuration into a routing component file. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. - - *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? - - When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines - - 1. `L`ocating our code is easy - 2. `I`dentify code at a glance - 3. `F`lat structure as long as we can - 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - Make locating your code intuitive, simple and fast. - - *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - When you look at a file you should instantly know what it contains and represents. - - *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. - -### Flat -###### [Style [Y143](#style-y143)] - - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. - - *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - Be DRY, but don't go nuts and sacrifice readability. - - *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [A2-150](#style-a2-150)] - - - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each component, service, pipe is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Layout -###### [Style [A2-151](#style-a2-151)] - - - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and component may act as the container for the app, navigation, menus, content areas, and other regions. - - *Why?*: Organizes all layout in a single place re-used throughout the application. - -### Folders-by-Feature Structure -###### [Style [A2-152](#style-a2-152)] - - - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. - - *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. - - *Why?*: The LIFT guidelines are all covered. - - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. - - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [A2-160](#style-a2-160)] - - - Create small modules that encapsulate one responsibility. - - *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. - -### Visual Studio Code - - - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - -**[Back to top](#table-of-contents)** - -## Angular CLI - -**[Back to top](#table-of-contents)** - -## Routing -Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. - -**[Back to top](#table-of-contents)** - -## Angular docs -For anything else, API reference, check the [Angular 2 documentation](//angular.io). - -**[Back to top](#table-of-contents)** diff --git a/a2/notes.md b/a2/notes.md deleted file mode 100644 index 393606b5..00000000 --- a/a2/notes.md +++ /dev/null @@ -1,632 +0,0 @@ -# Angular 2 Style Guide -Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. - -My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definitely ask questions when someone, including me, publishes a guide :) - -## Angular Team Endorsed -Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. - -## Purpose -*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* - -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. - -The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. - ->If you like this guide, check out my Angular 2 First Look course **coming soon** to Pluralsight. - -## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. - -##Translations -Translations of this Angular 2 style guide are maintained by the community. Due to the in flux nature of this guide, I will hold off on accepting contributions for the time being. But I hope to get the same amazing contributions we had for the v1 of the guide! - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Components](#components) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises](#resolving-promises) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Rule of 1 -###### [Style [A2-001](#style-a2-001)] - - - Define 1 component per file, recommended to be less than 500 lines of code. - - *Why?*: One component per file promotes easier unit testing and mocking. - - *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - - *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - - The following example defines the `app` module and its dependencies, defines a component, and defines a factory all in the same file. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Small Functions -###### [Style [A2-002](#style-a2-002)] - - - Define small functions, no more than 75 LOC (less is better). - - *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. - - *Why?*: Small functions promote reuse. - - *Why?*: Small functions are easier to read. - - *Why?*: Small functions are easier to maintain. - - *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [A2-020](#style-a2-020)] - - - Use unique naming conventions with separators for sub-modules. - - *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. - -### Definitions (aka Setters) -###### [Style [A2-021](#style-a2-021)] - -## Components - -### Bindable Members Up Top -###### [Style [A2-033](#style-a2-033)] - - - Place bindable members at the top of the component, alphabetized, and not spread through the component code. - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the component can be bound and used in the View. - - *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. - - **example coming soon** - -### Defer Logic to Services -###### [Style [A2-035](#style-a2-035)] - - - Defer logic in a component by delegating to services. - - *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. - - *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. - - *Why?*: Removes dependencies and hides implementation details from the component. - - *Why?*: Keeps the component slim, trim, and focused. - - **example coming soon** - -### Keep Components Focused -###### [Style [A2-037](#style-a2-037)] - - - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. - - *Why?*: Reusing components with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [A2-040](#style-a2-040)] - - - Services are singletons and should be used for sharing data and functionality. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Single Responsibility -###### [Style [A2-050](#style-a2-050)] - - - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Singletons -###### [Style [A2-051](#style-a2-051)] - - - Factories are singletons and return an object that contains the members of the service. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Accessible Members Up Top -###### [Style [A2-052](#style-a2-052)] - - - Expose the callable members of the service (its interface) at the top - - *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). - - *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. - - *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [A2-060](#style-a2-060)] - - - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. - - *Why?*: The component's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the component be simpler and more focused on the view. - - *Why?*: This makes it easier to test (mock or real) the data calls when testing a component that uses a data service. - - *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Return a Promise or Observable from Data Calls -###### [Style [A2-061](#style-a2-061)] - - - When calling a data service that returns a promise such as `http`, return a promise or Observable in your calling function too. - - *Why?*: You can chain the promises together and take further action after the data call completes and resolves or rejects the promise. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Directives -### Limit 1 Per File -###### [Style [A2-070](#style-a2-070)] - - - Create one directive per file. Name the file for the directive. - - *Why?*: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module. - - *Why?*: One directive per file is easy to maintain. - -### Manipulate DOM in a Structural Directive -###### [Style [A2-072](#style-a2-072)] - - - When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow. - - *Why?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) - -### Provide a Unique Directive Prefix -###### [Style [A2-073](#style-a2-073)] - - - Provide a short, unique and descriptive directive prefix such as `acmeSalesCustomerInfo` which would be declared in HTML as `acme-sales-customer-info`. - - *Why?*: The unique short prefix identifies the directive's context and origin. For example a prefix of `cc-` may indicate that the directive is part of a CodeCamper app while `acme-` may indicate a directive for the Acme company. - - Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Resolving Promises -### Component Activation Promises -###### [Style [A2-080](#style-a2-080)] - - - Resolve start-up logic for a component in an `activate` function. - - *Why?*: Placing start-up logic in a consistent place in the component makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the component. - - *Why?*: The component `activate` makes it convenient to re-use the logic for a refresh for the component/View, keeps the logic together, gets the user to the View faster, makes animations easy on the `ng-view` or `ui-view`, and feels snappier to the user. - - Note: If you need to conditionally cancel the route before you start using the component, use a [route resolve](A2-style-a2-081) instead. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Route Resolve Promises -###### [Style [A2-081](#style-a2-081)] - - - When a component depends on a promise to be resolved before the component is activated, resolve those dependencies in the `$routeProvider` before the component logic is executed. If you need to conditionally cancel a route before the component is activated, use a route resolver. - - - Use a route resolve when you want to decide to cancel the route before ever transitioning to the View. - - *Why?*: A component may require data before it loads. That data may come from a promise via a custom factory or [$http](https://docs.angularjs.org/api/ng/service/$http). Using a [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) allows the promise to resolve before the component logic executes, so it might take action based on that data from the promise. - - *Why?*: The code executes after the route and in the component’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via `ng-view` or `ui-view`) - - Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the [component `activate` technique](#style-aA2--080) instead. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Handling Exceptions with Promises -###### [Style [A2-082](#style-a2-082)] - - - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. - - - Always handle exceptions in services/factories. - - *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. - - *Why?*: To avoid swallowing errors and misinforming the user. - - Note: Consider putting any exception handling in a function in a shared module and service. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Exception Handling - -### Exception Catchers -###### [Style [A2-111](#style-a2-111)] - - - Create a service that exposes an interface to catch and gracefully handle exceptions. - - *Why?*: Provides a consistent way to catch exceptions that may be thrown in your code (e.g. during XHR calls or promise failures). - - Note: The exception catcher is good for catching and reacting to specific exceptions from calls that you know may throw one. For example, when making an XHR call to retrieve data from a remote web service and you want to catch any exceptions from that service and react uniquely. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [A2-120](#style-a2-120)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: - * the file name (`avengers.component.ts`) - * the registered component name with Angular (`Component`) - - *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. - - *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Feature File Names -###### [Style [A2-121](#style-a2-121)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.ts`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for any automated tasks. - -### Test File Names -###### [Style [A2-122](#style-a2-122)] - - - Name test specifications similar to the component they test with a suffix of `spec`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Component Names -###### [Style [A2-123](#style-a2-123)] - - - Use consistent names for all components named after their feature. Use UpperCamelCase for components, as they are constructors. - - *Why?*: Provides a consistent way to quickly identify and reference components. - - *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Component Name Suffix -###### [Style [A2-124](#style-a2-124)] - - - Append the component name with the suffix `Component`. - - *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. - - ```typescript - /** - * recommended - */ - - // avengers.component.ts - export class AvengersComponent { } - ``` - -### Service Names -###### [Style [A2-125](#style-a2-125)] - - - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). - - *Why?*: Provides a consistent way to quickly identify and reference services. - - *Why?*: Clear service names such as `logger` do not require a suffix. - - *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `AvengersService`. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Directive Component Names -###### [Style [A2-126](#style-a2-126)] - - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - - *Why?*: Provides a consistent way to quickly identify and reference components. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Routes -###### [Style [A2-129](#style-a2-129)] - - - Separate route configuration into a routing component file. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. - - *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? - - When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines - - 1. `L`ocating our code is easy - 2. `I`dentify code at a glance - 3. `F`lat structure as long as we can - 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - Make locating your code intuitive, simple and fast. - - *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - When you look at a file you should instantly know what it contains and represents. - - *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. - -### Flat -###### [Style [Y143](#style-y143)] - - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. - - *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - Be DRY, but don't go nuts and sacrifice readability. - - *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [A2-150](#style-a2-150)] - - - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each component, service, pipe is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Layout -###### [Style [A2-151](#style-a2-151)] - - - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and component may act as the container for the app, navigation, menus, content areas, and other regions. - - *Why?*: Organizes all layout in a single place re-used throughout the application. - -### Folders-by-Feature Structure -###### [Style [A2-152](#style-a2-152)] - - - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. - - *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. - - *Why?*: The LIFT guidelines are all covered. - - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. - - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [A2-160](#style-a2-160)] - - - Create small modules that encapsulate one responsibility. - - *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Startup Logic - -### Bootstrapping -###### [Style [A2-170](#style-a2-170)] - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## Testing -Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information. - -### Write Tests with Stories -###### [Style [A2-190](#style-a2-190)] - - - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. - - *Why?*: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success. - - ```javascript - it('should have Avengers component', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Testing Library -###### [Style [A2-191](#style-a2-191)] - - - Use [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) for unit testing. - - *Why?*: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features. - -### Test Runner -###### [Style [A2-192](#style-a2-192)] - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Organizing Tests -###### [Style [A2-197](#style-a2-197)] - - - Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate `tests` folder. - - *Why?*: Unit tests have a direct correlation to a specific component and file in source code. - - *Why?*: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage. - - *Why?*: When you update source code it is easier to go update the tests at the same time. - - *Why?*: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source. - - *Why?*: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations. - - *Why?*: Separating specs so they are not in a distributed build is easy with grunt or gulp. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. - -### Visual Studio Code - - - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - -**[Back to top](#table-of-contents)** - -## Angular CLI - -**[Back to top](#table-of-contents)** - -## Routing -Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. - -**[Back to top](#table-of-contents)** - -## Task Automation - -**[Back to top](#table-of-contents)** - -## Pipes - -###### [Style [A2-420](#style-a2-420)] - -**[Back to top](#table-of-contents)** - -## Angular docs -For anything else, API reference, check the [Angular 2 documentation](//angular.io). - -**[Back to top](#table-of-contents)** diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..758f7a3c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,10726 @@ +{ + "name": "webpack-demo", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", + "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "dev": true, + "requires": { + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-observable": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.2.0.tgz", + "integrity": "sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", + "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true, + "optional": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.3.tgz", + "integrity": "sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": true + }, + "atob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "dev": true + }, + "autoprefixer": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.4.1.tgz", + "integrity": "sha512-YqUclCBDXUT9Y7aQ8Xv+ja8yhTZYJoMsOD7WS++gZIJLCpCu+gPcKGDlhk6S3WxhLkTcNVdaMZAWys2nzZCH7g==", + "dev": true, + "requires": { + "browserslist": "^3.2.6", + "caniuse-lite": "^1.0.30000832", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.22", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "browserslist": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.6.tgz", + "integrity": "sha512-XCsMSg9V4S1VRdcp265dJ+8kBRjfuFXcavbisY7G6T9QI0H1Z24PP53vvs0WDYWqm38Mco1ILDtafcS8ZR4xiw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000830", + "electron-to-chromium": "^1.3.42" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", + "dev": true, + "optional": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz", + "integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==", + "dev": true, + "requires": { + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "babel-plugin-syntax-class-constructor-call": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", + "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-export-extensions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", + "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-class-constructor-call": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", + "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", + "dev": true, + "requires": { + "babel-plugin-syntax-class-constructor-call": "^6.18.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "dev": true, + "requires": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-export-extensions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", + "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", + "dev": true, + "requires": { + "babel-plugin-syntax-export-extensions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^2.1.2", + "invariant": "^2.2.2", + "semver": "^5.3.0" + }, + "dependencies": { + "browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" + } + } + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-preset-stage-1": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", + "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", + "dev": true, + "requires": { + "babel-plugin-transform-class-constructor-call": "^6.24.1", + "babel-plugin-transform-export-extensions": "^6.22.0", + "babel-preset-stage-2": "^6.24.1" + } + }, + "babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "7.0.0-beta.46", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.46.tgz", + "integrity": "sha512-WFJlg2WatdkXRFMpk7BN/Uzzkjkcjk+WaqnrSCpay+RYl4ypW9ZetZyT9kNt22IH/BQNst3M6PaaBn9IXsUNrg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "binaryextensions": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz", + "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==", + "dev": true + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "optional": true, + "requires": { + "hoek": "4.x.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", + "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "dev": true, + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-db": { + "version": "1.0.30000832", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000832.tgz", + "integrity": "sha1-r+NMn3xiE5/RxgfbKrcwi7tqUVg=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000832", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000832.tgz", + "integrity": "sha512-WMC2GiGTPxGywFL70h+CnP7GAYo6LM6JSI1sF13vAZfXCzOeunHzl20DpfbDGMdvtT2wpqvabY96MHEp/la+BQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "chokidar": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", + "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.0" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "chrome-trace-event": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", + "integrity": "sha512-sjndyZHrrWiu4RY7AkHgjn80GfAM2ZSzUkZLV/Js59Ldmh6JDThf0SUmOHU53rFu2rVxxfCzJ30Ukcfch3Gb/A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "requires": { + "chalk": "^1.1.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", + "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", + "dev": true, + "requires": { + "source-map": "0.5.x" + } + }, + "clean-webpack-plugin": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", + "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", + "dev": true, + "requires": { + "rimraf": "^2.6.1" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "requires": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "colors": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz", + "integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "compressible": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", + "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", + "dev": true, + "requires": { + "mime-db": ">= 1.33.0 < 2" + } + }, + "compression": { + "version": "1.7.2", + "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", + "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "bytes": "3.0.0", + "compressible": "~2.0.13", + "debug": "2.6.9", + "on-headers": "~1.0.1", + "safe-buffer": "5.1.1", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz", + "integrity": "sha512-OlTo6DYg0XfTKOF8eLf79wcHm4Ut10xU2cRBRPMW/NA5F9VMjZGTfRHWDIYC3s+1kObGYrBLshXWU1K0hILkNQ==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + } + } + }, + "core-js": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "create-ecdh": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.1.tgz", + "integrity": "sha512-iZvCCg8XqHQZ1ioNBTzXS/cQSkqkqcPs8xSX4upNB+DAk9Ht3uzQf2J32uAHNCne8LDmKr29AgZrEs4oIrwLuQ==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "optional": true, + "requires": { + "boom": "5.x.x" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "optional": true, + "requires": { + "hoek": "4.x.x" + } + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz", + "integrity": "sha1-c6TIHehdtmTU7mdPfUcIXjstVdw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "source-map": "^0.1.38", + "source-map-resolve": "^0.3.0", + "urix": "^0.1.0" + }, + "dependencies": { + "atob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", + "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "source-map-resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", + "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=", + "dev": true, + "requires": { + "atob": "~1.1.0", + "resolve-url": "~0.2.1", + "source-map-url": "~0.3.0", + "urix": "~0.1.0" + } + }, + "source-map-url": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", + "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=", + "dev": true + } + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + } + } + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "dev": true + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + }, + "dependencies": { + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + } + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "^0.10.9" + } + }, + "dargs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", + "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-fns": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", + "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-extend": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "^2.0.5", + "object-keys": "^1.0.8" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-conflict": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.1.tgz", + "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", + "dev": true, + "requires": { + "utila": "~0.3" + }, + "dependencies": { + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", + "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", + "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.44.tgz", + "integrity": "sha1-72sVCmDVIwgjiMra2ICF7NL9RoQ=", + "dev": true + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz", + "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + }, + "envinfo": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-4.4.2.tgz", + "integrity": "sha512-5rfRs+m+6pwoKRCFqpsA5+qsLngFms1aWPrxfKbrObCzQaPc3M3yPloZx+BL9UE3dK58cxw36XVQbFRSCCfGSQ==", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", + "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", + "dev": true, + "requires": { + "string-template": "~0.2.1", + "xtend": "~4.0.0" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", + "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, + "es5-ext": { + "version": "0.10.42", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", + "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true, + "requires": { + "original": ">=0.0.5" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true, + "optional": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-text-webpack-plugin": { + "version": "4.0.0-beta.0", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-4.0.0-beta.0.tgz", + "integrity": "sha512-Hypkn9jUTnFr0DpekNam53X47tXn3ucY08BQumv7kdGgeVUBLq3DJHJTi6HNxv4jl9W+Skxjz9+RnK0sJyqqjA==", + "dev": true, + "requires": { + "async": "^2.4.1", + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "dev": true, + "requires": { + "lodash": "^4.14.0" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-glob": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.1.tgz", + "integrity": "sha512-wSyW1TBK3ia5V+te0rGPXudeMHoUQW6O5Y9oATiaGhpENmEifPDlOdhpsnlj5HoG6ttIvGiY1DdCmI9X2xGMhg==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.1", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "flow-parser": { + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.71.0.tgz", + "integrity": "sha512-rXSvqSBLf8aRI6T3P99jMcUYvZoO1KZcKDkzGJmXvYdNAgRKu7sfGNtxEsn3cX4TgungBuJpX+K8aHRC9/B5MA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "follow-redirects": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", + "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "gh-got": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-6.0.0.tgz", + "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", + "dev": true, + "requires": { + "got": "^7.0.0", + "is-plain-obj": "^1.1.0" + }, + "dependencies": { + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "dev": true + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "github-username": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", + "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", + "dev": true, + "requires": { + "gh-got": "^6.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-all": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", + "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", + "dev": true, + "requires": { + "glob": "^7.0.5", + "yargs": "~1.2.6" + }, + "dependencies": { + "minimist": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", + "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=", + "dev": true + }, + "yargs": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", + "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", + "dev": true, + "requires": { + "minimist": "^0.1.0" + } + } + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "got": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.0.tgz", + "integrity": "sha512-kBNy/S2CGwrYgDSec5KTWGKUvupwkkTVAjIsVFF2shXO13xpZdFP4d4kxa//CLX2tN/rV0aYwK8vY6UKWGn2vQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "grouped-queue": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.3.tgz", + "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", + "dev": true, + "requires": { + "lodash": "^4.17.2" + } + }, + "handle-thing": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "optional": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "optional": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "optional": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + } + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "^1.0.2" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "optional": true, + "requires": { + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", + "dev": true + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", + "dev": true + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "html-minifier": { + "version": "3.5.15", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.15.tgz", + "integrity": "sha512-OZa4rfb6tZOZ3Z8Xf0jKxXkiDcFWldQePGYFDcgKqES2sXeWaEv9y6QQvWUtX3ySI3feApQi5uCsHLINQ6NoAw==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.1.x", + "commander": "2.15.x", + "he": "1.1.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.3.x" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-js": { + "version": "3.3.23", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.23.tgz", + "integrity": "sha512-Ks+KqLGDsYn4z+pU7JsKCzC0T3mPYl+rU+VcPZiQOazjE4Uqi4UCRY3qPMDbJi7ze37n1lDXj3biz1ik93vqvw==", + "dev": true, + "requires": { + "commander": "~2.15.0", + "source-map": "~0.6.1" + } + } + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "html-withimg-loader": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/html-withimg-loader/-/html-withimg-loader-0.1.16.tgz", + "integrity": "sha1-dv9bCDO0jkrso4WCwI+NCyk8g00=", + "dev": true, + "requires": { + "loader-utils": "^0.2.15", + "path": "^0.12.7" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" + }, + "dependencies": { + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", + "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "dev": true, + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "dev": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "ieee754": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", + "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", + "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", + "dev": true + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", + "dev": true, + "requires": { + "meow": "^3.3.0" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "dev": true, + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-observable": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-0.2.0.tgz", + "integrity": "sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI=", + "dev": true, + "requires": { + "symbol-observable": "^0.2.2" + }, + "dependencies": { + "symbol-observable": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-0.2.4.tgz", + "integrity": "sha1-lag9smGG1q9+ehjb2XYKL4bQj0A=", + "dev": true + } + } + }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-scoped": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", + "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", + "dev": true, + "requires": { + "scoped-regex": "^1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", + "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "optional": true + }, + "istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "requires": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + } + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "jquery": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + }, + "js-base64": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", + "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jscodeshift": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.5.0.tgz", + "integrity": "sha512-JAcQINNMFpdzzpKJN8k5xXjF3XDuckB1/48uScSzcnNyK199iWEc9AxKL9OoX5144M2w5zEx9Qs4/E/eBZZUlw==", + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^7.0.0-beta.30", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", + "neo-async": "^2.5.0", + "node-dir": "0.1.8", + "nomnom": "^1.8.1", + "recast": "^0.14.1", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + } + } + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true, + "optional": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", + "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/less/-/less-3.0.2.tgz", + "integrity": "sha512-konnFwWXpUQwzuwyN3Zfw/2Ziah2BKzqTfGoHBZjJdQWCmR+yrjmIG3QLwnlXNFWz27QetOmhGNSbHgGRdqhYQ==", + "dev": true, + "requires": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "^0.5.3" + } + }, + "less-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", + "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + } + } + }, + "listr": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.13.0.tgz", + "integrity": "sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "figures": "^1.7.0", + "indent-string": "^2.1.0", + "is-observable": "^0.2.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.4.0", + "listr-verbose-renderer": "^0.4.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "ora": "^0.2.3", + "p-map": "^1.1.1", + "rxjs": "^5.4.2", + "stream-to-observable": "^0.2.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz", + "integrity": "sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "log-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", + "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "dev": true, + "requires": { + "ansi-escapes": "^1.0.0", + "cli-cursor": "^1.0.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + } + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "dev": true, + "requires": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "^3.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", + "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "macaddress": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", + "dev": true + }, + "make-dir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", + "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mem-fs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.3.tgz", + "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", + "dev": true, + "requires": { + "through2": "^2.0.0", + "vinyl": "^1.1.0", + "vinyl-file": "^2.0.0" + } + }, + "mem-fs-editor": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-4.0.1.tgz", + "integrity": "sha512-54fptqhSZX1sSYsVVInG2qzUWPPrEv/6qYxHAwXJZQfzDcviJcL+7p/wmupg8SdAOi42m/vilMBemx3D6Sz22g==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "deep-extend": "^0.5.1", + "ejs": "^2.5.9", + "glob": "^7.0.3", + "globby": "^8.0.0", + "isbinaryfile": "^3.0.2", + "mkdirp": "^0.5.0", + "multimatch": "^2.0.0", + "rimraf": "^2.2.8", + "through2": "^2.0.0", + "vinyl": "^2.0.1" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "globby": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", + "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge2": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.1.tgz", + "integrity": "sha512-wUqcG5pxrAcaFI1lkqkMnk3Q7nUxV/NWfpAFSeWUwG9TRODnBDCUHa75mi3o3vLWQ5N4CQERWCauSlP0I3ZqUg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", + "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "neo-async": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", + "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", + "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-dir": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz", + "integrity": "sha1-VfuN62mQcHB/tn+RpGDwRIKUx30=", + "dev": true + }, + "node-forge": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", + "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", + "dev": true + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "nomnom": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", + "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", + "dev": true, + "requires": { + "chalk": "~0.4.0", + "underscore": "~1.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "dev": true, + "requires": { + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" + } + }, + "strip-ansi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dev": true, + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "original": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "dev": true, + "requires": { + "url-parse": "1.0.x" + }, + "dependencies": { + "url-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", + "dev": true, + "requires": { + "querystringify": "0.0.x", + "requires-port": "1.0.x" + } + } + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "dev": true + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, + "p-lazy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-lazy/-/p-lazy-1.0.0.tgz", + "integrity": "sha1-7FPIAvLuOsKPFmzILQsrAt4nqDU=", + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "dev": true, + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true, + "optional": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "requires": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "uniqid": "^4.0.0" + } + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + } + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + } + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + } + }, + "postcss-loader": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.4.tgz", + "integrity": "sha512-L2p654oK945B/gDFUGgOhh7uzj19RWoY1SVMeJVoKno1H2MdbQ0RppR/28JGju4pMb22iRC7BJ9aDzbxXSLf4A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^1.2.0", + "schema-utils": "^0.4.0" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", + "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + } + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prettier": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.12.1.tgz", + "integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU=", + "dev": true + }, + "pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.6.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", + "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "dev": true, + "requires": { + "duplexify": "^3.5.3", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "dev": true + }, + "purify-css": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/purify-css/-/purify-css-1.2.5.tgz", + "integrity": "sha512-Vy4jRnV2w/kUjTyxzQOKbFkqwUe6RNLuZgIWR/IRQ8nCqRwiFgwC9XiO9+8poq5KL053uWAQnCSbsfihq77zPg==", + "dev": true, + "requires": { + "clean-css": "^4.0.12", + "glob": "^7.1.1", + "rework": "^1.0.1", + "uglify-js": "^3.0.6", + "yargs": "^8.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "uglify-js": { + "version": "3.3.23", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.23.tgz", + "integrity": "sha512-Ks+KqLGDsYn4z+pU7JsKCzC0T3mPYl+rU+VcPZiQOazjE4Uqi4UCRY3qPMDbJi7ze37n1lDXj3biz1ik93vqvw==", + "dev": true, + "requires": { + "commander": "~2.15.0", + "source-map": "~0.6.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "purifycss-webpack": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/purifycss-webpack/-/purifycss-webpack-0.7.0.tgz", + "integrity": "sha1-B8nOeYj2CPGSgQLtP/GReM448OA=", + "dev": true, + "requires": { + "ajv": "^4.11.2", + "webpack-sources": "^0.1.4" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.5.3" + } + } + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + } + } + }, + "read-chunk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz", + "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", + "dev": true, + "requires": { + "pify": "^3.0.0", + "safe-buffer": "^5.1.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "recast": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.14.7.tgz", + "integrity": "sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A==", + "dev": true, + "requires": { + "ast-types": "0.11.3", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "regenerate": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "~0.1", + "htmlparser2": "~3.3.0", + "strip-ansi": "^3.0.0", + "utila": "~0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "request": { + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", + "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + } + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "*" + } + }, + "rxjs": { + "version": "5.5.10", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz", + "integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "scoped-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", + "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=", + "dev": true + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", + "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", + "dev": true, + "requires": { + "node-forge": "0.7.1" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.1.tgz", + "integrity": "sha512-YA/iYtZpzFe5HyWVGrb02FjPxc4EMCfpoU/Phg9fQoyMC72u9598OUBrsU8IrtwAKG0tO8IYaqbaLIw+k3IRGA==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "dev": true, + "optional": true, + "requires": { + "hoek": "4.x.x" + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "dev": true, + "requires": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + }, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "dev": true, + "requires": { + "atob": "^2.0.0", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, + "spdy": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "dev": true, + "requires": { + "debug": "^2.6.8", + "handle-thing": "^1.2.5", + "http-deceiver": "^1.2.7", + "safe-buffer": "^5.0.1", + "select-hose": "^2.0.0", + "spdy-transport": "^2.0.18" + } + }, + "spdy-transport": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", + "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "detect-node": "^2.0.3", + "hpack.js": "^2.1.6", + "obuf": "^1.1.1", + "readable-stream": "^2.2.9", + "safe-buffer": "^5.0.1", + "wbuf": "^1.7.2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "dev": true, + "optional": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.1.tgz", + "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "stream-to-observable": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.2.0.tgz", + "integrity": "sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA=", + "dev": true, + "requires": { + "any-observable": "^0.2.0" + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", + "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + } + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "tapable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", + "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "dev": true + }, + "temp": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "dev": true, + "requires": { + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" + }, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "textextensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.2.0.tgz", + "integrity": "sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", + "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "optional": true, + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + } + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz", + "integrity": "sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqid": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", + "dev": true, + "requires": { + "macaddress": "^0.2.8" + } + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "untildify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz", + "integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E=", + "dev": true + }, + "upath": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.5.tgz", + "integrity": "sha512-qbKn90aDQ0YEwvXoLqj0oiuUYroLX2lVHZ+b+xwjozFasAOC4GneDq5+OaIG5Zj+jFmbz/uO+f7a9qxjktJQww==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-join": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", + "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", + "dev": true + }, + "url-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.0.1.tgz", + "integrity": "sha512-rAonpHy7231fmweBKUFe0bYnlGDty77E+fm53NZdij7j/YOpyGzc7ttqG1nAXl3aRs0k41o0PC3TvGXQiw2Zvw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^0.4.3" + }, + "dependencies": { + "mime": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.0.tgz", + "integrity": "sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + }, + "dependencies": { + "querystringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", + "dev": true + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true + }, + "use": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true + }, + "v8-compile-cache": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz", + "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", + "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.6.0.tgz", + "integrity": "sha512-Fu/k/3fZeGtIhuFkiYpIy1UDHhMiGKjG4FFPVuvG+5Os2lWA1ttWpmi9Qnn6AgfZqj9MvhZW/rmj/ip+nHr06g==", + "dev": true, + "requires": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^0.1.1", + "enhanced-resolve": "^4.0.0", + "eslint-scope": "^3.7.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-addons": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/webpack-addons/-/webpack-addons-1.1.5.tgz", + "integrity": "sha512-MGO0nVniCLFAQz1qv22zM02QPjcpAoJdy7ED0i3Zy7SY1IecgXCm460ib7H/Wq7e9oL5VL6S2BxaObxwIcag0g==", + "dev": true, + "requires": { + "jscodeshift": "^0.4.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "ast-types": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", + "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==", + "dev": true + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "jscodeshift": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.4.1.tgz", + "integrity": "sha512-iOX6If+hsw0q99V3n31t4f5VlD1TQZddH08xbT65ZqA7T4Vkx68emrDZMUOLVvCEAJ6NpAk7DECe3fjC/t52AQ==", + "dev": true, + "requires": { + "async": "^1.5.0", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^6.17.3", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", + "node-dir": "0.1.8", + "nomnom": "^1.8.1", + "recast": "^0.12.5", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "recast": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", + "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", + "dev": true, + "requires": { + "ast-types": "0.10.1", + "core-js": "^2.4.1", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-2.1.2.tgz", + "integrity": "sha512-2C6bs9gORlzCSgkNZTnj8hnXMxe3g2v+yqiUdB+1l/I3sI36ND4zZStV00yq0eGjE5CNu0eqOQr7YYe+42H2Yw==", + "dev": true, + "requires": { + "chalk": "^2.3.2", + "cross-spawn": "^6.0.5", + "diff": "^3.5.0", + "enhanced-resolve": "^4.0.0", + "envinfo": "^4.4.2", + "glob-all": "^3.1.0", + "global-modules": "^1.0.0", + "got": "^8.2.0", + "import-local": "^1.0.0", + "inquirer": "^5.1.0", + "interpret": "^1.0.4", + "jscodeshift": "^0.5.0", + "listr": "^0.13.0", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "log-symbols": "^2.2.0", + "mkdirp": "^0.5.1", + "p-each-series": "^1.0.0", + "p-lazy": "^1.0.0", + "prettier": "^1.5.3", + "supports-color": "^5.3.0", + "v8-compile-cache": "^1.1.2", + "webpack-addons": "^1.1.5", + "yargs": "^11.1.0", + "yeoman-environment": "^2.0.0", + "yeoman-generator": "^2.0.4" + } + }, + "webpack-dev-middleware": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.1.2.tgz", + "integrity": "sha512-Z11Zp3GTvCe6mGbbtma+lMB9xRfJcNtupXfmvFBujyXqLNms6onDnSi9f/Cb2rw6KkD5kgibOfxhN7npZwTiGA==", + "dev": true, + "requires": { + "loud-rejection": "^1.6.0", + "memory-fs": "~0.4.1", + "mime": "^2.1.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "url-join": "^4.0.0", + "webpack-log": "^1.0.1" + }, + "dependencies": { + "mime": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.3.tgz", + "integrity": "sha512-UXfgQIPpdw2rByoUnCrMAIXCS7IJJMp5N0MDQNk9CuQvirCkuWlu7gQpCS8Kaiz4kogC4TdAQHG3jzh/DdqEWg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^1.0.0", + "internal-ip": "1.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.1.4", + "spdy": "^3.4.1", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "webpack-dev-middleware": "3.1.2", + "webpack-log": "^1.1.2", + "yargs": "11.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", + "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + } + } + }, + "webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "dev": true, + "requires": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + } + }, + "webpack-sources": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", + "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + }, + "yeoman-environment": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.0.6.tgz", + "integrity": "sha512-jzHBTTy8EPI4ImV8dpUMt+Q5zELkSU5xvGpndHcHudQ4tqN6YgIWaCGmRFl+HDchwRUkcgyjQ+n6/w5zlJBCPg==", + "dev": true, + "requires": { + "chalk": "^2.1.0", + "debug": "^3.1.0", + "diff": "^3.3.1", + "escape-string-regexp": "^1.0.2", + "globby": "^6.1.0", + "grouped-queue": "^0.3.3", + "inquirer": "^3.3.0", + "is-scoped": "^1.0.0", + "lodash": "^4.17.4", + "log-symbols": "^2.1.0", + "mem-fs": "^1.1.0", + "text-table": "^0.2.0", + "untildify": "^3.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + } + } + }, + "yeoman-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-2.0.5.tgz", + "integrity": "sha512-rV6tJ8oYzm4mmdF2T3wjY+Q42jKF2YiiD0VKfJ8/0ZYwmhCKC9Xs2346HVLPj/xE13i68psnFJv7iS6gWRkeAg==", + "dev": true, + "requires": { + "async": "^2.6.0", + "chalk": "^2.3.0", + "cli-table": "^0.3.1", + "cross-spawn": "^6.0.5", + "dargs": "^5.1.0", + "dateformat": "^3.0.3", + "debug": "^3.1.0", + "detect-conflict": "^1.0.0", + "error": "^7.0.2", + "find-up": "^2.1.0", + "github-username": "^4.0.0", + "istextorbinary": "^2.2.1", + "lodash": "^4.17.10", + "make-dir": "^1.1.0", + "mem-fs-editor": "^4.0.0", + "minimist": "^1.2.0", + "pretty-bytes": "^4.0.2", + "read-chunk": "^2.1.0", + "read-pkg-up": "^3.0.0", + "rimraf": "^2.6.2", + "run-async": "^2.0.0", + "shelljs": "^0.8.0", + "text-table": "^0.2.0", + "through2": "^2.0.0", + "yeoman-environment": "^2.0.5" + }, + "dependencies": { + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "dev": true, + "requires": { + "lodash": "^4.14.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..5c762de8 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "webpack-demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --mode production", + "client": "webpack-dev-server --mode development" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "autoprefixer": "^8.4.1", + "babel-core": "^6.26.3", + "babel-loader": "^7.1.4", + "babel-preset-env": "^1.6.1", + "clean-webpack-plugin": "^0.1.19", + "copy-webpack-plugin": "^4.5.1", + "css-loader": "^0.28.11", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.11", + "glob": "^7.1.2", + "html-webpack-plugin": "^3.2.0", + "html-withimg-loader": "^0.1.16", + "less": "^3.0.2", + "less-loader": "^4.1.0", + "postcss-loader": "^2.1.4", + "purify-css": "^1.2.5", + "purifycss-webpack": "^0.7.0", + "style-loader": "^0.21.0", + "uglifyjs-webpack-plugin": "^1.2.5", + "url-loader": "^1.0.1", + "webpack": "^4.6.0", + "webpack-cli": "^2.1.2", + "webpack-dev-server": "^3.1.3" + }, + "dependencies": { + "jquery": "^3.3.1" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..47098786 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require("autoprefixer") + ] +} \ No newline at end of file diff --git a/src/assets/readme.txt b/src/assets/readme.txt new file mode 100644 index 00000000..e1af9459 --- /dev/null +++ b/src/assets/readme.txt @@ -0,0 +1 @@ +˵ĵ \ No newline at end of file diff --git a/src/css/a.css b/src/css/a.css new file mode 100644 index 00000000..48ab3085 --- /dev/null +++ b/src/css/a.css @@ -0,0 +1,27 @@ +body{ + background: greenyellow; +} +div{ + border: 5px solid green; + font-size: 20px; + background: url(../images/edit.png) repeat; +} +img{ + transform: rotate(45deg); +} + +#head{ + height: 100px; +} +.head{ + display: block; +} +.add{ + display: block; +} +.anthor{ + display: block; +} +.a{ + color: #fff; +} \ No newline at end of file diff --git a/src/images/edit.png b/src/images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..fa6cf08328ff960a388f292f2ff02cdd5578b858 GIT binary patch literal 3037 zcmdT`YdF+f7vKLGGovBbK`|Jm$^DjFNk%i889GuScMdAIGKe^HWGE7043TSuk~#iD}rwQ~is1U;2px)x_XUb3L{1%sD#r3J-c zo!V|<;0T!c7yB6J-u7R$FXe3r;X-FBF;$Q?LqL zYu%{q`aw`+_7^1uo=eJGpGhk$fQ8J8(h+M<{BpE#d$I1?r(Qg*w0|_zbzx`K>zH4q zLar9BzbDl=qV|ZUy5G5u-XD5<76#mIa$zeVD`}1NsgQe3HSiH*P0?Fta-s)K{tALa z#<*0HAn~^@&TlRcF5MDh1 zHv?t;_NRlI{Xi8Xv)%jITc$1X*ysIK9r+z=I1cR;J=q;<$= zVTXmW#O|#1hLE5S*fJrTInDu&d;Moh&!oRp-ot&ok3u$4_`W>q!{xTGWJ@o`I(#;4 z40Gwh?UbR99tdfVit`VR7fkljlqNZpUo++$tt2qn9v`AT-Lt412(Jiy3NxzSt7m-N zIw+SCwXTukX$ZKN&mpx_mN9)!80Nko8Ow9(JtYNcm_Bs^4^8Kl0N?3t{%Y7K9b-`J z)>5xdhfe@zm}7n0WjQ|6`-@pWy)WzV;}miR`+cAD&;AJLPp;V20paFr{8nA@FciZ! zFmL4$p3hVq_`%QQq_7BhG^JFrB39L<@>z`_el}UT zlB^O04N$TNlg8|<_$95|TWS*%`HPh~xyGW5P6!t?pKiY*{TGDubu@M}(u{=@CLPuC zx;5kA1a@suuC){YZe6u@vZtfHbjiH%P}#7-d6;qA_g%Q=LBDJI<4p!c;M-QRdITij zs}~S6r5l(?&^{eD1HMlxL(5Rf^iTmY`9uRFG;KRL46t4NGpn)2_I`pm^#hhyAez8# zX+n&*Wq>$MKomoWDY5icqD{*xGp1SP6N%CL*8rs4=)HRsU}KReT&%bM5Pn?cs18uo za3vbT$dAk+it5JgKx8r-4r8(Cy=i5o{{r^IINC#@Goq9*>_- ziGCVmyYF$Z#JG+03XcZPWWBe%#UN6Fzs{WC8DrCt^lDzA zal0nx&H2&D49>#SY?0T|faLg(%fy8#A@Mcbhx`_4BPe+|+_@E-5aMjsFh1il=MjD*rmWV2PDj)CBmyc>)&iA@=W7xFE zW8G(MbqzV?j&#WX3<6&v*FhIPx>LZ#wZ?jg|El-M5 zT`x{Zzo8+2fxEu!=bn4C{ZK~c)jG~_<2Gf*e$>LuP4l{O1pZXq!IDtTy?|heE^XUc z@gZtyKPDFr+a1`-vTCH`pN$Hwy6D3DUefn1)P$p>a69j=ad(cGKqNBx{d-{t8?}w%cpiCz@F6wfEbAjjEAtxzFkl_=d+G7l0II663xhjCf zG_?=xMG=59)yv-ifdltvN|dL7AVb+Vpz;9_Ak?>}dt(8_R8FcKz%W33Mkebv90yp% zwGG)YG4O8b3@eE5#?pUcf0obc5Mlawa~|2n;`MOP`-S0YThf!>0Rh~vuU~euDz4HA zXZp_I!%iI}PflBGIDzRWaYoFeX`F?Kh@e|GPhk(qa!aeSRb2eP7Z|COWB1RePgd0xX#r)qtW@Cqq zX>f*qn00&DE}14I-~+ zb?-5Fn3A;e2Tl&P_FFHN4y+d~N$suH&g-*81fpVP>EsyM3NUH2?U z`|sTNZeoc1S*8+VTZZdT*9(sbC@pm^>4KE4fNr z@ZKkPyFPBR)IjE$j^_4mj@&9CHz}3b3sEQ*73L5T4dM%bw@KhPS{LVJX*UxFJbm`0 zd!B+ot&yafUNK + + + + page1 + + +
pomelott template
+
less test
+
sass test
+ + + \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..ac49df84 --- /dev/null +++ b/src/index.js @@ -0,0 +1,13 @@ +import "./css/a.css" +import "./less/test.less" +// import "./sass/test.scss" + + +import {a, b, Person} from "./js/test.js" +console.log(a); +console.log(b); +document.querySelector("div").innerHTML = "after bundle1"; +$("div").eq(1).css({ + "width": "400px", + "heihgt": "400px" +}) \ No newline at end of file diff --git a/src/index2.html b/src/index2.html new file mode 100644 index 00000000..97081be2 --- /dev/null +++ b/src/index2.html @@ -0,0 +1,10 @@ + + + + + page2 + + + + + \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js new file mode 100644 index 00000000..c47211da --- /dev/null +++ b/src/js/index.js @@ -0,0 +1,14 @@ +import "../css/a.css" +import "../less/test.less" +// import "./sass/test.scss" + + +import {a, b, Person} from "./test.js" +console.log(a); +console.log(b); +$("div").eq(0).css({ + width:"800px", + height: "800px", + background: "#ccc" +}); +document.querySelector("div").innerHTML = "after bundle123456"; \ No newline at end of file diff --git a/src/js/index2.js b/src/js/index2.js new file mode 100644 index 00000000..9657b223 --- /dev/null +++ b/src/js/index2.js @@ -0,0 +1 @@ +document.querySelector("div").style.background = "red"; \ No newline at end of file diff --git a/src/js/test.js b/src/js/test.js new file mode 100644 index 00000000..53472e5a --- /dev/null +++ b/src/js/test.js @@ -0,0 +1,3 @@ +export const a = 10; +export const b = 15; + diff --git a/src/less/test.less b/src/less/test.less new file mode 100644 index 00000000..36182ae3 --- /dev/null +++ b/src/less/test.less @@ -0,0 +1,4 @@ +@color:red; +#less{ + color :@color; +} \ No newline at end of file diff --git a/src/sass/test.scss b/src/sass/test.scss new file mode 100644 index 00000000..5d4cb3e2 --- /dev/null +++ b/src/sass/test.scss @@ -0,0 +1,5 @@ +$color: green; + +#sass{ + color: $color; +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..61fc9085 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,44 @@ +const path = require('path'); +const pluginsConfig = require("./webpack.plguins.js"); +const rulesConfig = require("./webpack.rules.js"); + +module.exports = { + entry: { + // 多入口文件 + a: './src/js/index.js', + b: './src/js/index2.js', + jquery: 'jquery' + }, + output: { + path:path.resolve(__dirname, 'dist'), + // 打包多出口文件 + // 生成 a.bundle.js b.bundle.js jquery.bundle.js + filename: './js/[name].bundle.js' + }, + plugins: pluginsConfig, + devServer: { + contentBase: path.resolve(__dirname, "dist"), + host: "localhost", + port: "8090", + open: true, // 开启浏览器 + hot: false, // 开启热更新 + inline: true, + }, + // devtool: "source-map", // 开启调试模式 + module:{ + rules: rulesConfig + }, + // 提取js,lib1名字可改 + optimization: { + splitChunks: { + cacheGroups: { + lib1: { + chunks: "initial", + name: "jquery", + enforce: true + } + } + } + } + +} \ No newline at end of file diff --git a/webpack.plguins.js b/webpack.plguins.js new file mode 100644 index 00000000..f5d10aba --- /dev/null +++ b/webpack.plguins.js @@ -0,0 +1,50 @@ +const webpack = require("webpack"); +const path = require('path'); +const glob = require("glob"); +//消除冗余的css +const purifyCssWebpack = require("purifycss-webpack"); +// html模板 +const htmlWebpackPlugin = require("html-webpack-plugin"); +// 清除目录等 +const cleanWebpackPlugin = require("clean-webpack-plugin"); +//4.x之前用以压缩 +const uglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin"); +// 分离css +const extractTextPlugin = require("extract-text-webpack-plugin"); +//静态资源输出 +const copyWebpackPlugin = require("copy-webpack-plugin"); +module.exports = [ + new webpack.HotModuleReplacementPlugin(), + // 调用之前先清除 + new cleanWebpackPlugin(["dist"]), + // 4.x之前可用uglifyjs-webpack-plugin用以压缩文件,4.x可用--mode更改模式为production来压缩文件 + // new uglifyjsWebpackPlugin(), + new copyWebpackPlugin([{ + from: path.resolve(__dirname,"src/assets"), + to: './pulic' + }]), + // 分离css插件参数为提取出去的路径 + new extractTextPlugin("css/index.css"), + // 消除冗余的css代码 + new purifyCssWebpack({ + // glob为扫描模块,使用其同步方法 + paths: glob.sync(path.join(__dirname, "src/*.html")) + }), + // 全局暴露统一入口 + new webpack.ProvidePlugin({ + $: "jquery" + }), + // 自动生成html模板 + new htmlWebpackPlugin({ + filename: "index.html", + title: "xxxx", + chunks: ['a',"jquery"], // 按需引入对应名字的js文件 + template: "./src/index.html" + }), + new htmlWebpackPlugin({ + chunks: ['b'], + filename: "index2.html", + title: "page2", + template: "./src/index2.html" + }) +] \ No newline at end of file diff --git a/webpack.rules.js b/webpack.rules.js new file mode 100644 index 00000000..ee996ec4 --- /dev/null +++ b/webpack.rules.js @@ -0,0 +1,71 @@ +const extractTextPlugin = require("extract-text-webpack-plugin"); +module.exports = [ + { + test: /\.css$/, + // 不分离的写法 + // use: ["style-loader", "css-loader"] + // 使用postcss不分离的写法 + // use: ["style-loader", "css-loader", "postcss-loader"] + // 此处为分离css的写法 + /*use: extractTextPlugin.extract({ + fallback: "style-loader", + use: "css-loader", + // css中的基础路径 + publicPath: "../" + + })*/ + // 此处为使用postcss分离css的写法 + use: extractTextPlugin.extract({ + fallback: "style-loader", + use: ["css-loader", "postcss-loader"], + // css中的基础路径 + publicPath: "../" + + }) + }, + { + test: /\.js$/, + use: ["babel-loader"], + // 不检查node_modules下的js文件 + exclude: "/node_modules/" + }, + { + test: /\.(png|jpg|gif)$/, + use: [{ + // 需要下载file-loader和url-loader + loader: "url-loader", + options: { + limit: 50, + // 图片文件输出的文件夹 + outputPath: "images" + } + } + ] + }, + { + test: /\.html$/, + // html中的img标签 + use: ["html-withimg-loader"] + }, + { + test: /\.less$/, + // 三个loader的顺序不能变 + // 不分离的写法 + // use: ["style-loader", "css-loader", "less-loader"] + // 分离的写法 + use: extractTextPlugin.extract({ + fallback:"style-loader", + use: ["css-loader", "less-loader"] + }) + }, + { + test: /\.(scss|sass)$/, + // sass不分离的写法,顺序不能变 + // use: ["style-loader", "css-loader", "sass-loader"] + // 分离的写法 + use: extractTextPlugin.extract({ + fallback:"style-loader", + use: ["css-loader", "sass-loader"] + }) + } + ] \ No newline at end of file From 52e381f721db51d57a2835a8411ecc8929161b00 Mon Sep 17 00:00:00 2001 From: jamesliauw Date: Sat, 2 Jun 2018 11:42:02 +0800 Subject: [PATCH 02/10] feat: add common --- .gitignore | 1 + README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4331e8bf..30c56a0e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ yarn-error.log* *.ntvs* *.njsproj *.sln +.history diff --git a/README.md b/README.md index ceb47bf5..14383245 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # webpack4.x_Demo -基于webpack4.6.0的多页应用配置,包括常见的插件使用与配置。 -要点都有注释! -如有帮助,请赐我一颗小星星。 +1.add ts +2.add multi page entrance +3.add egg.js From 0049d06a7ed4d04cbef5611893a9d46f56bd49fe Mon Sep 17 00:00:00 2001 From: jamesliauw Date: Sat, 2 Jun 2018 15:47:04 +0800 Subject: [PATCH 03/10] feat: ts support --- .babelrc | 10 +- package-lock.json | 1566 ++++++++++++++++++++++++++++++++++----------- package.json | 13 +- src/js/index.ts | 17 + src/js/index2.ts | 17 + tsconfig.json | 11 + webpack.config.js | 9 +- webpack.rules.js | 16 +- 8 files changed, 1277 insertions(+), 382 deletions(-) create mode 100644 src/js/index.ts create mode 100644 src/js/index2.ts create mode 100644 tsconfig.json diff --git a/.babelrc b/.babelrc index 181cce65..5d352e00 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,7 @@ -{ - "presets": ["env"] -} \ No newline at end of file +{ "presets": [ + ["env", { + "targets": { + "browsers": ["last 2 versions", "safari >= 7"] + } + }] + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 758f7a3c..0744c7f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,238 @@ "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", "dev": true }, + "@webassemblyjs/ast": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fast/-/ast-1.5.9.tgz", + "integrity": "sha1-sncBgmeGkatJSdWTEFwV1AdP7bY=", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.9", + "@webassemblyjs/helper-wasm-bytecode": "1.5.9", + "@webassemblyjs/wast-parser": "1.5.9", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2ffloating-point-hex-parser/-/floating-point-hex-parser-1.5.9.tgz", + "integrity": "sha1-7lYkP2ujB4H/b5L+fxw3clUhmnw=", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-api-error/-/helper-api-error-1.5.9.tgz", + "integrity": "sha1-yA4gSv4a4QLCOwNX8ewlrrYQIqI=", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-buffer/-/helper-buffer-1.5.9.tgz", + "integrity": "sha1-kNma/LD9we4RvEA4lPOuN82SaoE=", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-code-frame/-/helper-code-frame-1.5.9.tgz", + "integrity": "sha1-tWrAajnD4c/vzEIa3h7kcac4pXA=", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.9" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-fsm/-/helper-fsm-1.5.9.tgz", + "integrity": "sha1-j5liaOsH7mcoEwqel/o6rDJ3JFQ=", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-module-context/-/helper-module-context-1.5.9.tgz", + "integrity": "sha1-aeLuoxD3VaC3ULhPivWfiQ8gRqw=", + "dev": true + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-wasm-bytecode/-/helper-wasm-bytecode-1.5.9.tgz", + "integrity": "sha1-Rnug+eTQ5KSL8cUQe59KvjyhFxo=", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fhelper-wasm-section/-/helper-wasm-section-1.5.9.tgz", + "integrity": "sha1-rslIarXVbjy1JSp+2Id3tnkqxiQ=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/helper-buffer": "1.5.9", + "@webassemblyjs/helper-wasm-bytecode": "1.5.9", + "@webassemblyjs/wasm-gen": "1.5.9", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fieee754/-/ieee754-1.5.9.tgz", + "integrity": "sha1-hGhW7OBAx969W1ZFsxnCZSNhO88=", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fleb128/-/leb128-1.5.9.tgz", + "integrity": "sha1-cklEOg/XV0p+PBw5UyU1xzU5C7w=", + "dev": true, + "requires": { + "leb": "^0.3.0" + } + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwasm-edit/-/wasm-edit-1.5.9.tgz", + "integrity": "sha1-m44FSy0wWn4FKAiFcclZBL1z30g=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/helper-buffer": "1.5.9", + "@webassemblyjs/helper-wasm-bytecode": "1.5.9", + "@webassemblyjs/helper-wasm-section": "1.5.9", + "@webassemblyjs/wasm-gen": "1.5.9", + "@webassemblyjs/wasm-opt": "1.5.9", + "@webassemblyjs/wasm-parser": "1.5.9", + "@webassemblyjs/wast-printer": "1.5.9", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwasm-gen/-/wasm-gen-1.5.9.tgz", + "integrity": "sha1-heB8BH+rkX4GsY3uTRY0Ki/TxZw=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/helper-wasm-bytecode": "1.5.9", + "@webassemblyjs/ieee754": "1.5.9", + "@webassemblyjs/leb128": "1.5.9" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwasm-opt/-/wasm-opt-1.5.9.tgz", + "integrity": "sha1-zKwXxBoETBZ7yV0+hkXPiJoTfOU=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/helper-buffer": "1.5.9", + "@webassemblyjs/wasm-gen": "1.5.9", + "@webassemblyjs/wasm-parser": "1.5.9", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwasm-parser/-/wasm-parser-1.5.9.tgz", + "integrity": "sha1-3auE2klXtkqvvGHkq3BsxmcILzI=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/helper-api-error": "1.5.9", + "@webassemblyjs/helper-wasm-bytecode": "1.5.9", + "@webassemblyjs/ieee754": "1.5.9", + "@webassemblyjs/leb128": "1.5.9", + "@webassemblyjs/wasm-parser": "1.5.9" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwast-parser/-/wast-parser-1.5.9.tgz", + "integrity": "sha1-GT0kzPR0Kl+PGRWTZoCrIxQBHfI=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/floating-point-hex-parser": "1.5.9", + "@webassemblyjs/helper-api-error": "1.5.9", + "@webassemblyjs/helper-code-frame": "1.5.9", + "@webassemblyjs/helper-fsm": "1.5.9", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.9", + "resolved": "http://192.168.31.62:4873/@webassemblyjs%2fwast-printer/-/wast-printer-1.5.9.tgz", + "integrity": "sha1-FmBdkKSBwBoTC3xO3rK85QN4frQ=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/wast-parser": "1.5.9", + "long": "^3.2.0" + } + }, "accepts": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "resolved": "http://192.168.31.62:4873/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { @@ -31,15 +260,15 @@ } }, "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "version": "5.6.1", + "resolved": "http://192.168.31.62:4873/acorn/-/acorn-5.6.1.tgz", + "integrity": "sha1-yeUMPjcXz4l/Gwcc6tu1Q7vAqNQ=", "dev": true }, "acorn-dynamic-import": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "resolved": "http://192.168.31.62:4873/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha1-kBzu5Mf6rvfgetKkfokGddpQong=", "dev": true, "requires": { "acorn": "^5.0.0" @@ -83,7 +312,7 @@ }, "ansi-html": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "resolved": "http://192.168.31.62:4873/ansi-html/-/ansi-html-0.0.7.tgz", "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", "dev": true }, @@ -110,8 +339,8 @@ }, "anymatch": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "resolved": "http://192.168.31.62:4873/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", "dev": true, "requires": { "micromatch": "^3.1.4", @@ -159,19 +388,19 @@ }, "array-find-index": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "resolved": "http://192.168.31.62:4873/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, "array-flatten": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", + "resolved": "http://192.168.31.62:4873/array-flatten/-/array-flatten-2.1.1.tgz", "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", "dev": true }, "array-includes": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "resolved": "http://192.168.31.62:4873/array-includes/-/array-includes-3.0.3.tgz", "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "dev": true, "requires": { @@ -222,8 +451,8 @@ }, "asn1.js": { "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "resolved": "http://192.168.31.62:4873/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha1-ucK/WAXx5kqt7tbfOiv6+1pz9aA=", "dev": true, "requires": { "bn.js": "^4.0.0", @@ -233,7 +462,7 @@ }, "assert": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "resolved": "http://192.168.31.62:4873/assert/-/assert-1.4.1.tgz", "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", "dev": true, "requires": { @@ -266,7 +495,7 @@ }, "async-each": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, @@ -340,6 +569,12 @@ "dev": true, "optional": true }, + "babel": { + "version": "6.23.0", + "resolved": "http://192.168.31.62:4873/babel/-/babel-6.23.0.tgz", + "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ=", + "dev": true + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -614,8 +849,8 @@ }, "babel-loader": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz", - "integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==", + "resolved": "http://192.168.31.62:4873/babel-loader/-/babel-loader-7.1.4.tgz", + "integrity": "sha1-40Y5OL1ObVXRwXTFSF1AahiO0BU=", "dev": true, "requires": { "find-cache-dir": "^1.0.0", @@ -1059,10 +1294,29 @@ "babel-types": "^6.24.1" } }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "http://192.168.31.62:4873/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "http://192.168.31.62:4873/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + }, "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "version": "1.7.0", + "resolved": "http://192.168.31.62:4873/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha1-3qefpOvriDzTXasH4mDBycBN93o=", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "^6.22.0", @@ -1092,20 +1346,32 @@ "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", "babel-plugin-transform-exponentiation-operator": "^6.22.0", "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^2.1.2", + "browserslist": "^3.2.6", "invariant": "^2.2.2", "semver": "^5.3.0" }, "dependencies": { "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "version": "3.2.8", + "resolved": "http://192.168.31.62:4873/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha1-sABTYdZHHw9ZUnl6dvyYXx+Xj8Y=", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" } + }, + "caniuse-lite": { + "version": "1.0.30000847", + "resolved": "http://192.168.31.62:4873/caniuse-lite/-/caniuse-lite-1.0.30000847.tgz", + "integrity": "sha1-vnf0Ob4pu8V64IAEseRwtlOx7B0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.48", + "resolved": "http://192.168.31.62:4873/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", + "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", + "dev": true } } }, @@ -1329,13 +1595,13 @@ }, "base64-js": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "resolved": "http://192.168.31.62:4873/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=", "dev": true }, "batch": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "resolved": "http://192.168.31.62:4873/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, @@ -1357,7 +1623,7 @@ }, "binary-extensions": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "resolved": "http://192.168.31.62:4873/binary-extensions/-/binary-extensions-1.11.0.tgz", "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, @@ -1375,13 +1641,13 @@ }, "bn.js": { "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "resolved": "http://192.168.31.62:4873/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=", "dev": true }, "body-parser": { "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "resolved": "http://192.168.31.62:4873/body-parser/-/body-parser-1.18.2.tgz", "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "dev": true, "requires": { @@ -1399,15 +1665,15 @@ "dependencies": { "iconv-lite": { "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "resolved": "http://192.168.31.62:4873/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=", "dev": true } } }, "bonjour": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "resolved": "http://192.168.31.62:4873/bonjour/-/bonjour-3.5.0.tgz", "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, "requires": { @@ -1476,14 +1742,14 @@ }, "brorand": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "resolved": "http://192.168.31.62:4873/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", "dev": true, "requires": { "buffer-xor": "^1.0.3", @@ -1496,8 +1762,8 @@ }, "browserify-cipher": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "resolved": "http://192.168.31.62:4873/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", "dev": true, "requires": { "browserify-aes": "^1.0.4", @@ -1507,8 +1773,8 @@ }, "browserify-des": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", - "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", + "resolved": "http://192.168.31.62:4873/browserify-des/-/browserify-des-1.0.1.tgz", + "integrity": "sha1-M0MSTbbXrVPiaogmMYcSvchFD5w=", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -1518,7 +1784,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://192.168.31.62:4873/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1528,7 +1794,7 @@ }, "browserify-sign": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "resolved": "http://192.168.31.62:4873/browserify-sign/-/browserify-sign-4.0.4.tgz", "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { @@ -1543,8 +1809,8 @@ }, "browserify-zlib": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "resolved": "http://192.168.31.62:4873/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", "dev": true, "requires": { "pako": "~1.0.5" @@ -1562,7 +1828,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://192.168.31.62:4873/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -1579,13 +1845,13 @@ }, "buffer-indexof": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "resolved": "http://192.168.31.62:4873/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha1-Uvq8xqYG0aADAoAmSO9o9jnaJow=", "dev": true }, "buffer-xor": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "resolved": "http://192.168.31.62:4873/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, @@ -1597,13 +1863,13 @@ }, "builtin-status-codes": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "resolved": "http://192.168.31.62:4873/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, "bytes": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "resolved": "http://192.168.31.62:4873/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, @@ -1692,7 +1958,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://192.168.31.62:4873/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -1702,7 +1968,7 @@ "dependencies": { "camelcase": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "resolved": "http://192.168.31.62:4873/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true } @@ -1758,13 +2024,14 @@ }, "chokidar": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", - "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", + "resolved": "http://192.168.31.62:4873/chokidar/-/chokidar-2.0.3.tgz", + "integrity": "sha1-3L1PbLsqVbR5m6ioQKxSfl9LEXY=", "dev": true, "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.0", "braces": "^2.3.0", + "fsevents": "^1.1.2", "glob-parent": "^3.1.0", "inherits": "^2.0.1", "is-binary-path": "^1.0.0", @@ -1783,14 +2050,14 @@ }, "chrome-trace-event": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", - "integrity": "sha512-sjndyZHrrWiu4RY7AkHgjn80GfAM2ZSzUkZLV/Js59Ldmh6JDThf0SUmOHU53rFu2rVxxfCzJ30Ukcfch3Gb/A==", + "resolved": "http://192.168.31.62:4873/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", + "integrity": "sha1-05WvLTHIe5CnFsgx/jJvaXaOwIQ=", "dev": true }, "cipher-base": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "resolved": "http://192.168.31.62:4873/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -2116,9 +2383,9 @@ } }, "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "version": "2.14.1", + "resolved": "http://192.168.31.62:4873/commander/-/commander-2.14.1.tgz", + "integrity": "sha1-IjUSPjevjKPGXfRbAm29NXsBuao=", "dev": true }, "commondir": { @@ -2135,7 +2402,7 @@ }, "compressible": { "version": "2.0.13", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", + "resolved": "http://192.168.31.62:4873/compressible/-/compressible-2.0.13.tgz", "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", "dev": true, "requires": { @@ -2144,7 +2411,7 @@ }, "compression": { "version": "1.7.2", - "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", + "resolved": "http://192.168.31.62:4873/compression/-/compression-1.7.2.tgz", "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", "dev": true, "requires": { @@ -2159,8 +2426,8 @@ "dependencies": { "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "resolved": "http://192.168.31.62:4873/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true } } @@ -2185,13 +2452,13 @@ }, "connect-history-api-fallback": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "resolved": "http://192.168.31.62:4873/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", "dev": true }, "console-browserify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { @@ -2200,20 +2467,20 @@ }, "constants-browserify": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, "content-disposition": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "resolved": "http://192.168.31.62:4873/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", "dev": true }, "content-type": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "resolved": "http://192.168.31.62:4873/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "convert-source-map": { @@ -2224,13 +2491,13 @@ }, "cookie": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "resolved": "http://192.168.31.62:4873/cookie/-/cookie-0.3.1.tgz", "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, "cookie-signature": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "resolved": "http://192.168.31.62:4873/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, @@ -2331,9 +2598,9 @@ } }, "create-ecdh": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.1.tgz", - "integrity": "sha512-iZvCCg8XqHQZ1ioNBTzXS/cQSkqkqcPs8xSX4upNB+DAk9Ht3uzQf2J32uAHNCne8LDmKr29AgZrEs4oIrwLuQ==", + "version": "4.0.3", + "resolved": "http://192.168.31.62:4873/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha1-yREbbzMEXEaX8UR4f5JUzcd8Rf8=", "dev": true, "requires": { "bn.js": "^4.1.0", @@ -2342,8 +2609,8 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "resolved": "http://192.168.31.62:4873/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -2355,8 +2622,8 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "resolved": "http://192.168.31.62:4873/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", "dev": true, "requires": { "cipher-base": "^1.0.3", @@ -2404,8 +2671,8 @@ }, "crypto-browserify": { "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "resolved": "http://192.168.31.62:4873/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", "dev": true, "requires": { "browserify-cipher": "^1.0.0", @@ -2612,7 +2879,7 @@ }, "currently-unhandled": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "resolved": "http://192.168.31.62:4873/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { @@ -2627,7 +2894,7 @@ }, "d": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { @@ -2658,7 +2925,7 @@ }, "date-now": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "resolved": "http://192.168.31.62:4873/date-now/-/date-now-0.1.4.tgz", "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", "dev": true }, @@ -2700,7 +2967,7 @@ }, "deep-equal": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, @@ -2769,7 +3036,7 @@ }, "del": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "resolved": "http://192.168.31.62:4873/del/-/del-3.0.0.tgz", "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", "dev": true, "requires": { @@ -2789,13 +3056,13 @@ }, "depd": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "resolved": "http://192.168.31.62:4873/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, "des.js": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/des.js/-/des.js-1.0.0.tgz", "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { @@ -2805,7 +3072,7 @@ }, "destroy": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "resolved": "http://192.168.31.62:4873/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, @@ -2826,7 +3093,7 @@ }, "detect-node": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "resolved": "http://192.168.31.62:4873/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", "dev": true }, @@ -2838,8 +3105,8 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "resolved": "http://192.168.31.62:4873/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", "dev": true, "requires": { "bn.js": "^4.1.0", @@ -2859,14 +3126,14 @@ }, "dns-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/dns-equal/-/dns-equal-1.0.0.tgz", "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", "dev": true }, "dns-packet": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "resolved": "http://192.168.31.62:4873/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha1-EqpCaYEHW+UAuRDu3NC0fdfe2lo=", "dev": true, "requires": { "ip": "^1.1.0", @@ -2875,7 +3142,7 @@ }, "dns-txt": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "resolved": "http://192.168.31.62:4873/dns-txt/-/dns-txt-2.0.2.tgz", "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "dev": true, "requires": { @@ -2919,8 +3186,8 @@ }, "domain-browser": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "resolved": "http://192.168.31.62:4873/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", "dev": true }, "domelementtype": { @@ -2984,7 +3251,7 @@ }, "ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "resolved": "http://192.168.31.62:4873/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, @@ -3008,7 +3275,7 @@ }, "elliptic": { "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "resolved": "http://192.168.31.62:4873/elliptic/-/elliptic-6.4.0.tgz", "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "dev": true, "requires": { @@ -3029,7 +3296,7 @@ }, "encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "resolved": "http://192.168.31.62:4873/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, @@ -3118,9 +3385,9 @@ } }, "es5-ext": { - "version": "0.10.42", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", - "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "version": "0.10.45", + "resolved": "http://192.168.31.62:4873/es5-ext/-/es5-ext-0.10.45.tgz", + "integrity": "sha1-C/33tHPaWRnVrfO9Jc63VPzMNlM=", "dev": true, "requires": { "es6-iterator": "~2.0.3", @@ -3130,7 +3397,7 @@ }, "es6-iterator": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "resolved": "http://192.168.31.62:4873/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { @@ -3141,7 +3408,7 @@ }, "es6-symbol": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "resolved": "http://192.168.31.62:4873/es6-symbol/-/es6-symbol-3.1.1.tgz", "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { @@ -3151,7 +3418,7 @@ }, "escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "resolved": "http://192.168.31.62:4873/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "dev": true }, @@ -3163,7 +3430,7 @@ }, "eslint-scope": { "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "resolved": "http://192.168.31.62:4873/eslint-scope/-/eslint-scope-3.7.1.tgz", "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { @@ -3179,8 +3446,8 @@ }, "esrecurse": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "resolved": "http://192.168.31.62:4873/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -3188,7 +3455,7 @@ }, "estraverse": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "resolved": "http://192.168.31.62:4873/estraverse/-/estraverse-4.2.0.tgz", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, @@ -3200,25 +3467,25 @@ }, "etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "resolved": "http://192.168.31.62:4873/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, "eventemitter3": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "resolved": "http://192.168.31.62:4873/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://192.168.31.62:4873/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, "eventsource": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "resolved": "http://192.168.31.62:4873/eventsource/-/eventsource-0.1.6.tgz", "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "dev": true, "requires": { @@ -3227,8 +3494,8 @@ }, "evp_bytestokey": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "resolved": "http://192.168.31.62:4873/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", "dev": true, "requires": { "md5.js": "^1.3.4", @@ -3366,7 +3633,7 @@ }, "express": { "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "resolved": "http://192.168.31.62:4873/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "dev": true, "requires": { @@ -3404,14 +3671,14 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "http://192.168.31.62:4873/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "resolved": "http://192.168.31.62:4873/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true } } @@ -3582,7 +3849,7 @@ }, "faye-websocket": { "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "resolved": "http://192.168.31.62:4873/faye-websocket/-/faye-websocket-0.10.0.tgz", "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { @@ -3639,8 +3906,8 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "resolved": "http://192.168.31.62:4873/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha1-7r9O2EAHnIP0JJA4ydcDAIMBsQU=", "dev": true, "requires": { "debug": "2.6.9", @@ -3704,9 +3971,9 @@ } }, "follow-redirects": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", - "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "version": "1.5.0", + "resolved": "http://192.168.31.62:4873/follow-redirects/-/follow-redirects-1.5.0.tgz", + "integrity": "sha1-I09Jz3cLfzW0DnkPY2zroMOgq3c=", "dev": true, "requires": { "debug": "^3.1.0" @@ -3714,8 +3981,8 @@ "dependencies": { "debug": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3765,7 +4032,7 @@ }, "forwarded": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "resolved": "http://192.168.31.62:4873/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, @@ -3780,7 +4047,7 @@ }, "fresh": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "resolved": "http://192.168.31.62:4873/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, @@ -3812,6 +4079,535 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.2.4", + "resolved": "http://192.168.31.62:4873/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha1-9B3LGvJYKvNpLaNvxVy9jhBBxCY=", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3826,7 +4622,7 @@ }, "get-stdin": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "resolved": "http://192.168.31.62:4873/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, @@ -4122,7 +4918,7 @@ }, "handle-thing": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "resolved": "http://192.168.31.62:4873/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, @@ -4205,7 +5001,7 @@ }, "has-symbols": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/has-symbols/-/has-symbols-1.0.0.tgz", "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, @@ -4252,7 +5048,7 @@ }, "hash-base": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "resolved": "http://192.168.31.62:4873/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { @@ -4262,8 +5058,8 @@ }, "hash.js": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "resolved": "http://192.168.31.62:4873/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha1-NA3tvmKQGHFRweodd3o0SJNd+EY=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -4291,7 +5087,7 @@ }, "hmac-drbg": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { @@ -4333,7 +5129,7 @@ }, "hpack.js": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "resolved": "http://192.168.31.62:4873/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { @@ -4351,7 +5147,7 @@ }, "html-entities": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "resolved": "http://192.168.31.62:4873/html-entities/-/html-entities-1.2.1.tgz", "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", "dev": true }, @@ -4502,13 +5298,13 @@ }, "http-deceiver": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "resolved": "http://192.168.31.62:4873/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "dev": true }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://192.168.31.62:4873/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -4519,15 +5315,15 @@ } }, "http-parser-js": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", - "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", + "version": "0.4.13", + "resolved": "http://192.168.31.62:4873/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", "dev": true }, "http-proxy": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "resolved": "http://192.168.31.62:4873/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -4537,8 +5333,8 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "resolved": "http://192.168.31.62:4873/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha1-CYfmu1pWBuWmkWjY+WeofxXdiqs=", "dev": true, "requires": { "http-proxy": "^1.16.2", @@ -4561,7 +5357,7 @@ }, "https-browserify": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, @@ -4610,8 +5406,8 @@ }, "ieee754": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", - "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", + "resolved": "http://192.168.31.62:4873/ieee754/-/ieee754-1.1.11.tgz", + "integrity": "sha1-wWOE/+APW3g1gk5ntvK9RKUilFU=", "dev": true }, "iferr": { @@ -4666,7 +5462,7 @@ }, "indexof": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "resolved": "http://192.168.31.62:4873/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, @@ -4715,7 +5511,7 @@ }, "internal-ip": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "resolved": "http://192.168.31.62:4873/internal-ip/-/internal-ip-1.2.0.tgz", "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", "dev": true, "requires": { @@ -4755,13 +5551,13 @@ }, "ip": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "resolved": "http://192.168.31.62:4873/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, "ipaddr.js": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "resolved": "http://192.168.31.62:4873/ipaddr.js/-/ipaddr.js-1.6.0.tgz", "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", "dev": true }, @@ -4799,7 +5595,7 @@ }, "is-binary-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { @@ -4991,14 +5787,14 @@ }, "is-path-cwd": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/is-path-cwd/-/is-path-cwd-1.0.0.tgz", "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", "dev": true }, "is-path-in-cwd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "resolved": "http://192.168.31.62:4873/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", "dev": true, "requires": { "is-path-inside": "^1.0.0" @@ -5006,7 +5802,7 @@ }, "is-path-inside": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { @@ -5112,7 +5908,7 @@ }, "is-wsl": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, @@ -5373,7 +6169,7 @@ }, "json3": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "resolved": "http://192.168.31.62:4873/json3/-/json3-3.3.2.tgz", "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, @@ -5413,7 +6209,7 @@ }, "killable": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/killable/-/killable-1.0.0.tgz", "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", "dev": true }, @@ -5432,6 +6228,12 @@ "invert-kv": "^1.0.0" } }, + "leb": { + "version": "0.3.0", + "resolved": "http://192.168.31.62:4873/leb/-/leb-0.3.0.tgz", + "integrity": "sha1-Mr7p+tFoMo1q6oUi2DP0GA7tHaM=", + "dev": true + }, "less": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/less/-/less-3.0.2.tgz", @@ -5753,7 +6555,7 @@ }, "loader-runner": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "resolved": "http://192.168.31.62:4873/loader-runner/-/loader-runner-2.3.0.tgz", "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", "dev": true }, @@ -5856,20 +6658,26 @@ }, "loglevel": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "resolved": "http://192.168.31.62:4873/loglevel/-/loglevel-1.6.1.tgz", "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, "loglevelnext": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "resolved": "http://192.168.31.62:4873/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha1-NvxPWZbWZA9Tn/IDuoGWQWgNdaI=", "dev": true, "requires": { "es6-symbol": "^3.1.1", "object.assign": "^4.1.0" } }, + "long": { + "version": "3.2.0", + "resolved": "http://192.168.31.62:4873/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", @@ -5881,7 +6689,7 @@ }, "loud-rejection": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "resolved": "http://192.168.31.62:4873/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { @@ -5926,6 +6734,12 @@ "pify": "^3.0.0" } }, + "mamacro": { + "version": "0.0.3", + "resolved": "http://192.168.31.62:4873/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha1-rSyVdhl8nxq/MI0Hh4Zb2XWj8+Q=", + "dev": true + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5934,7 +6748,7 @@ }, "map-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, @@ -5955,7 +6769,7 @@ }, "md5.js": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "resolved": "http://192.168.31.62:4873/md5.js/-/md5.js-1.3.4.tgz", "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", "dev": true, "requires": { @@ -5965,7 +6779,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://192.168.31.62:4873/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -6069,7 +6883,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://192.168.31.62:4873/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -6087,7 +6901,7 @@ "dependencies": { "find-up": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "resolved": "http://192.168.31.62:4873/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { @@ -6097,7 +6911,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6110,13 +6924,13 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://192.168.31.62:4873/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, "parse-json": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "resolved": "http://192.168.31.62:4873/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { @@ -6125,7 +6939,7 @@ }, "path-exists": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "resolved": "http://192.168.31.62:4873/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { @@ -6134,7 +6948,7 @@ }, "path-type": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { @@ -6145,13 +6959,13 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://192.168.31.62:4873/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "read-pkg": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { @@ -6162,7 +6976,7 @@ }, "read-pkg-up": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { @@ -6174,7 +6988,7 @@ }, "merge-descriptors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, @@ -6186,7 +7000,7 @@ }, "methods": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "resolved": "http://192.168.31.62:4873/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, @@ -6213,8 +7027,8 @@ }, "miller-rabin": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "resolved": "http://192.168.31.62:4873/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", "dev": true, "requires": { "bn.js": "^4.0.0", @@ -6256,13 +7070,13 @@ }, "minimalistic-assert": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "resolved": "http://192.168.31.62:4873/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, @@ -6351,8 +7165,8 @@ }, "multicast-dns": { "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "resolved": "http://192.168.31.62:4873/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha1-oOx72QVcQoL3kMPIL04o2zsxsik=", "dev": true, "requires": { "dns-packet": "^1.3.1", @@ -6361,7 +7175,7 @@ }, "multicast-dns-service-types": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "resolved": "http://192.168.31.62:4873/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, @@ -6383,6 +7197,13 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "nan": { + "version": "2.10.0", + "resolved": "http://192.168.31.62:4873/nan/-/nan-2.10.0.tgz", + "integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8=", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -6405,7 +7226,7 @@ }, "negotiator": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "resolved": "http://192.168.31.62:4873/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, @@ -6417,7 +7238,7 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, @@ -6443,15 +7264,15 @@ "dev": true }, "node-forge": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", - "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", + "version": "0.7.5", + "resolved": "http://192.168.31.62:4873/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha1-bBUsNFzhHFL0ZcKr2VfoY5zWdN8=", "dev": true }, "node-libs-browser": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "resolved": "http://192.168.31.62:4873/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=", "dev": true, "requires": { "assert": "^1.1.1", @@ -6481,7 +7302,7 @@ "dependencies": { "punycode": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "resolved": "http://192.168.31.62:4873/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true } @@ -6651,8 +7472,8 @@ }, "object.assign": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "resolved": "http://192.168.31.62:4873/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -6692,13 +7513,13 @@ }, "obuf": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "resolved": "http://192.168.31.62:4873/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha1-Cb6jND1BhZ69RGKS0RydTbYZCE4=", "dev": true }, "on-finished": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "resolved": "http://192.168.31.62:4873/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "dev": true, "requires": { @@ -6707,7 +7528,7 @@ }, "on-headers": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/on-headers/-/on-headers-1.0.1.tgz", "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", "dev": true }, @@ -6731,8 +7552,8 @@ }, "opn": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "resolved": "http://192.168.31.62:4873/opn/-/opn-5.3.0.tgz", + "integrity": "sha1-ZIcVZchjh18FLP31PT48ta21Oxw=", "dev": true, "requires": { "is-wsl": "^1.1.0" @@ -6818,29 +7639,17 @@ } }, "original": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", - "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "version": "1.0.1", + "resolved": "http://192.168.31.62:4873/original/-/original-1.0.1.tgz", + "integrity": "sha1-sKU/9Cupl6jJzR+12q60K51pMZA=", "dev": true, "requires": { - "url-parse": "1.0.x" - }, - "dependencies": { - "url-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", - "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", - "dev": true, - "requires": { - "querystringify": "0.0.x", - "requires-port": "1.0.x" - } - } + "url-parse": "~1.4.0" } }, "os-browserify": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "resolved": "http://192.168.31.62:4873/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, @@ -6947,8 +7756,8 @@ }, "pako": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "resolved": "http://192.168.31.62:4873/pako/-/pako-1.0.6.tgz", + "integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg=", "dev": true }, "parallel-transform": { @@ -6973,8 +7782,8 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "resolved": "http://192.168.31.62:4873/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha1-9r8pOBgzK9DatU77Fgh3JHRebKg=", "dev": true, "requires": { "asn1.js": "^4.0.0", @@ -7031,7 +7840,7 @@ }, "parseurl": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "resolved": "http://192.168.31.62:4873/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, @@ -7053,7 +7862,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "http://192.168.31.62:4873/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -7077,7 +7886,7 @@ }, "path-is-inside": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "resolved": "http://192.168.31.62:4873/path-is-inside/-/path-is-inside-1.0.2.tgz", "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, @@ -7095,7 +7904,7 @@ }, "path-to-regexp": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "resolved": "http://192.168.31.62:4873/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, @@ -7110,8 +7919,8 @@ }, "pbkdf2": { "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "resolved": "http://192.168.31.62:4873/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha1-dAQgjsawG2LYW/g4U6gGT42cKlw=", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -7160,7 +7969,7 @@ }, "portfinder": { "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "resolved": "http://192.168.31.62:4873/portfinder/-/portfinder-1.0.13.tgz", "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "dev": true, "requires": { @@ -7817,8 +8626,8 @@ }, "proxy-addr": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "resolved": "http://192.168.31.62:4873/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha1-NV8mJQWmIWRrMTCnKOtkfiIFU0E=", "dev": true, "requires": { "forwarded": "~0.1.2", @@ -7839,8 +8648,8 @@ }, "public-encrypt": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "resolved": "http://192.168.31.62:4873/public-encrypt/-/public-encrypt-4.0.2.tgz", + "integrity": "sha1-RuuRByBr9zSJ+LhbadkTNMZhCZQ=", "dev": true, "requires": { "bn.js": "^4.1.0", @@ -8124,20 +8933,20 @@ }, "querystring": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "resolved": "http://192.168.31.62:4873/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true }, "querystring-es3": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "resolved": "http://192.168.31.62:4873/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, "querystringify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", - "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", + "version": "2.0.0", + "resolved": "http://192.168.31.62:4873/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha1-+j7W5o6xUVlFfImze8ZHKDMZV1U=", "dev": true }, "randomatic": { @@ -8163,8 +8972,8 @@ }, "randombytes": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "resolved": "http://192.168.31.62:4873/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha1-0wLFIpSFiISKjTAMkytEwkIx2oA=", "dev": true, "requires": { "safe-buffer": "^5.1.0" @@ -8172,8 +8981,8 @@ }, "randomfill": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "resolved": "http://192.168.31.62:4873/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", "dev": true, "requires": { "randombytes": "^2.0.5", @@ -8182,13 +8991,13 @@ }, "range-parser": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "resolved": "http://192.168.31.62:4873/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", "dev": true }, "raw-body": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "resolved": "http://192.168.31.62:4873/raw-body/-/raw-body-2.3.2.tgz", "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", "dev": true, "requires": { @@ -8200,13 +9009,13 @@ "dependencies": { "depd": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "resolved": "http://192.168.31.62:4873/depd/-/depd-1.1.1.tgz", "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", "dev": true }, "http-errors": { "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "resolved": "http://192.168.31.62:4873/http-errors/-/http-errors-1.6.2.tgz", "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "dev": true, "requires": { @@ -8218,13 +9027,13 @@ }, "iconv-lite": { "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "resolved": "http://192.168.31.62:4873/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=", "dev": true }, "setprototypeof": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "resolved": "http://192.168.31.62:4873/setprototypeof/-/setprototypeof-1.0.3.tgz", "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", "dev": true } @@ -8278,7 +9087,7 @@ }, "readdirp": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "resolved": "http://192.168.31.62:4873/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { @@ -8319,7 +9128,7 @@ }, "redent": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { @@ -8557,7 +9366,7 @@ }, "requires-port": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, @@ -8655,8 +9464,8 @@ }, "ripemd160": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "resolved": "http://192.168.31.62:4873/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -8750,17 +9559,17 @@ }, "select-hose": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "resolved": "http://192.168.31.62:4873/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, "selfsigned": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", - "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", + "version": "1.10.3", + "resolved": "http://192.168.31.62:4873/selfsigned/-/selfsigned-1.10.3.tgz", + "integrity": "sha1-1ijs+eNzX4TouvupNrPPhb6kOCM=", "dev": true, "requires": { - "node-forge": "0.7.1" + "node-forge": "0.7.5" } }, "semver": { @@ -8771,8 +9580,8 @@ }, "send": { "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "resolved": "http://192.168.31.62:4873/send/-/send-0.16.2.tgz", + "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=", "dev": true, "requires": { "debug": "2.6.9", @@ -8798,7 +9607,7 @@ }, "serve-index": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "resolved": "http://192.168.31.62:4873/serve-index/-/serve-index-1.9.1.tgz", "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { @@ -8813,8 +9622,8 @@ }, "serve-static": { "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "resolved": "http://192.168.31.62:4873/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=", "dev": true, "requires": { "encodeurl": "~1.0.2", @@ -8831,7 +9640,7 @@ }, "set-immediate-shim": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", "dev": true }, @@ -8860,20 +9669,20 @@ }, "setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "resolved": "http://192.168.31.62:4873/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "dev": true }, "setprototypeof": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "resolved": "http://192.168.31.62:4873/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "resolved": "http://192.168.31.62:4873/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -9049,8 +9858,8 @@ }, "sockjs": { "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "resolved": "http://192.168.31.62:4873/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha1-2Xa76ACve9IK4IWY1YI5NQiZPA0=", "dev": true, "requires": { "faye-websocket": "^0.10.0", @@ -9059,7 +9868,7 @@ }, "sockjs-client": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "resolved": "http://192.168.31.62:4873/sockjs-client/-/sockjs-client-1.1.4.tgz", "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", "dev": true, "requires": { @@ -9073,7 +9882,7 @@ "dependencies": { "faye-websocket": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "resolved": "http://192.168.31.62:4873/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "dev": true, "requires": { @@ -9165,7 +9974,7 @@ }, "spdy": { "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "resolved": "http://192.168.31.62:4873/spdy/-/spdy-3.4.7.tgz", "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "dev": true, "requires": { @@ -9179,8 +9988,8 @@ }, "spdy-transport": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", + "resolved": "http://192.168.31.62:4873/spdy-transport/-/spdy-transport-2.1.0.tgz", + "integrity": "sha1-S7sVqv/tC+791WrWHb3Iuj4st6E=", "dev": true, "requires": { "debug": "^2.6.8", @@ -9256,13 +10065,13 @@ }, "statuses": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "resolved": "http://192.168.31.62:4873/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=", "dev": true }, "stream-browserify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "http://192.168.31.62:4873/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -9281,14 +10090,14 @@ } }, "stream-http": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.1.tgz", - "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==", + "version": "2.8.3", + "resolved": "http://192.168.31.62:4873/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", "dev": true, "requires": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.1", - "readable-stream": "^2.3.3", + "readable-stream": "^2.3.6", "to-arraybuffer": "^1.0.0", "xtend": "^4.0.0" } @@ -9382,7 +10191,7 @@ }, "strip-indent": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { @@ -9491,7 +10300,7 @@ }, "thunky": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", + "resolved": "http://192.168.31.62:4873/thunky/-/thunky-1.0.2.tgz", "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", "dev": true }, @@ -9503,8 +10312,8 @@ }, "timers-browserify": { "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "resolved": "http://192.168.31.62:4873/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha1-HSjj0qrfHVpZlsTp+VYBzQU0gK4=", "dev": true, "requires": { "setimmediate": "^1.0.4" @@ -9521,7 +10330,7 @@ }, "to-arraybuffer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, @@ -9600,7 +10409,7 @@ }, "trim-newlines": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, @@ -9610,9 +10419,22 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "ts-loader": { + "version": "4.3.0", + "resolved": "http://192.168.31.62:4873/ts-loader/-/ts-loader-4.3.0.tgz", + "integrity": "sha1-Tjuhcng9ElbTojvfrd4BGnlfrp4=", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^3.1.4", + "semver": "^5.0.1" + } + }, "tty-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "http://192.168.31.62:4873/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -9635,8 +10457,8 @@ }, "type-is": { "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "resolved": "http://192.168.31.62:4873/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -9649,28 +10471,34 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "2.9.1", + "resolved": "http://192.168.31.62:4873/typescript/-/typescript-2.9.1.tgz", + "integrity": "sha1-/bGdLGehXRGZX9FWQONz4JqwmWE=", + "dev": true + }, "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "version": "3.3.10", + "resolved": "http://192.168.31.62:4873/uglify-es/-/uglify-es-3.3.10.tgz", + "integrity": "sha1-iwt5ks6+IO3CbeG/MlzveXuPP6U=", "dev": true, "requires": { - "commander": "~2.13.0", + "commander": "~2.14.1", "source-map": "~0.6.1" }, "dependencies": { "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "resolved": "http://192.168.31.62:4873/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } }, "uglifyjs-webpack-plugin": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz", - "integrity": "sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==", + "resolved": "http://192.168.31.62:4873/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz", + "integrity": "sha1-Lvg4fI8akD7F5E+jb588vc6mdkE=", "dev": true, "requires": { "cacache": "^10.0.4", @@ -9685,8 +10513,8 @@ "dependencies": { "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "resolved": "http://192.168.31.62:4873/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -9773,7 +10601,7 @@ }, "unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "resolved": "http://192.168.31.62:4873/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, @@ -9824,9 +10652,9 @@ "dev": true }, "upath": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.5.tgz", - "integrity": "sha512-qbKn90aDQ0YEwvXoLqj0oiuUYroLX2lVHZ+b+xwjozFasAOC4GneDq5+OaIG5Zj+jFmbz/uO+f7a9qxjktJQww==", + "version": "1.1.0", + "resolved": "http://192.168.31.62:4873/upath/-/upath-1.1.0.tgz", + "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", "dev": true }, "upper-case": { @@ -9852,7 +10680,7 @@ }, "url": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "resolved": "http://192.168.31.62:4873/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "dev": true, "requires": { @@ -9862,7 +10690,7 @@ "dependencies": { "punycode": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "resolved": "http://192.168.31.62:4873/punycode/-/punycode-1.3.2.tgz", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "dev": true } @@ -9870,7 +10698,7 @@ }, "url-join": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", + "resolved": "http://192.168.31.62:4873/url-join/-/url-join-4.0.0.tgz", "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", "dev": true }, @@ -9895,20 +10723,12 @@ }, "url-parse": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.0.tgz", - "integrity": "sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg==", + "resolved": "http://192.168.31.62:4873/url-parse/-/url-parse-1.4.0.tgz", + "integrity": "sha1-a/2q1gCYx/4G9iPkKyLeYt4NPXU=", "dev": true, "requires": { "querystringify": "^2.0.0", "requires-port": "^1.0.0" - }, - "dependencies": { - "querystringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", - "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", - "dev": true - } } }, "url-parse-lax": { @@ -9976,7 +10796,7 @@ }, "utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "resolved": "http://192.168.31.62:4873/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, @@ -10004,7 +10824,7 @@ }, "vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "resolved": "http://192.168.31.62:4873/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, @@ -10061,7 +10881,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "http://192.168.31.62:4873/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -10070,8 +10890,8 @@ }, "watchpack": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "resolved": "http://192.168.31.62:4873/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha1-S8EsLr6KonenHx0/FNaFx7RGzQA=", "dev": true, "requires": { "chokidar": "^2.0.2", @@ -10081,19 +10901,23 @@ }, "wbuf": { "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "resolved": "http://192.168.31.62:4873/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha1-wdjRSTFtPqhShIiVy2oL/oh7h98=", "dev": true, "requires": { "minimalistic-assert": "^1.0.0" } }, "webpack": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.6.0.tgz", - "integrity": "sha512-Fu/k/3fZeGtIhuFkiYpIy1UDHhMiGKjG4FFPVuvG+5Os2lWA1ttWpmi9Qnn6AgfZqj9MvhZW/rmj/ip+nHr06g==", + "version": "4.10.2", + "resolved": "http://192.168.31.62:4873/webpack/-/webpack-4.10.2.tgz", + "integrity": "sha1-1qTMPi+nSPlspipw+R6qqTnvOFg=", "dev": true, "requires": { + "@webassemblyjs/ast": "1.5.9", + "@webassemblyjs/wasm-edit": "1.5.9", + "@webassemblyjs/wasm-opt": "1.5.9", + "@webassemblyjs/wasm-parser": "1.5.9", "acorn": "^5.0.0", "acorn-dynamic-import": "^3.0.0", "ajv": "^6.1.0", @@ -10101,6 +10925,7 @@ "chrome-trace-event": "^0.1.1", "enhanced-resolve": "^4.0.0", "eslint-scope": "^3.7.1", + "json-parse-better-errors": "^1.0.2", "loader-runner": "^2.3.0", "loader-utils": "^1.1.0", "memory-fs": "~0.4.1", @@ -10304,9 +11129,9 @@ } }, "webpack-dev-middleware": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.1.2.tgz", - "integrity": "sha512-Z11Zp3GTvCe6mGbbtma+lMB9xRfJcNtupXfmvFBujyXqLNms6onDnSi9f/Cb2rw6KkD5kgibOfxhN7npZwTiGA==", + "version": "3.1.3", + "resolved": "http://192.168.31.62:4873/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz", + "integrity": "sha1-izKqQ9qa55Nowb8Rg/K2z14fOe0=", "dev": true, "requires": { "loud-rejection": "^1.6.0", @@ -10320,16 +11145,16 @@ "dependencies": { "mime": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "resolved": "http://192.168.31.62:4873/mime/-/mime-2.3.1.tgz", + "integrity": "sha1-sWIcVNY7l8R9PP5/chX31kUXw2k=", "dev": true } } }, "webpack-dev-server": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.3.tgz", - "integrity": "sha512-UXfgQIPpdw2rByoUnCrMAIXCS7IJJMp5N0MDQNk9CuQvirCkuWlu7gQpCS8Kaiz4kogC4TdAQHG3jzh/DdqEWg==", + "version": "3.1.4", + "resolved": "http://192.168.31.62:4873/webpack-dev-server/-/webpack-dev-server-3.1.4.tgz", + "integrity": "sha1-mgjRPErd0eO22KzhFuhnFQlK1bQ=", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -10357,21 +11182,21 @@ "spdy": "^3.4.1", "strip-ansi": "^3.0.0", "supports-color": "^5.1.0", - "webpack-dev-middleware": "3.1.2", + "webpack-dev-middleware": "3.1.3", "webpack-log": "^1.1.2", "yargs": "11.0.0" }, "dependencies": { "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": "http://192.168.31.62:4873/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "debug": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "resolved": "http://192.168.31.62:4873/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -10379,7 +11204,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://192.168.31.62:4873/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -10388,14 +11213,14 @@ }, "y18n": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "resolved": "http://192.168.31.62:4873/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yargs": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "resolved": "http://192.168.31.62:4873/yargs/-/yargs-11.0.0.tgz", + "integrity": "sha1-wFKTEAbF7udGEOX8A1S+39CKIBs=", "dev": true, "requires": { "cliui": "^4.0.0", @@ -10416,8 +11241,8 @@ }, "webpack-log": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "resolved": "http://192.168.31.62:4873/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha1-pLNM2msitRjbsKsy5WeWLVxypD0=", "dev": true, "requires": { "chalk": "^2.1.0", @@ -10426,6 +11251,15 @@ "uuid": "^3.1.0" } }, + "webpack-merge": { + "version": "4.1.2", + "resolved": "http://192.168.31.62:4873/webpack-merge/-/webpack-merge-4.1.2.tgz", + "integrity": "sha1-XTct3dPh5fiHT1v1qOkp2wn+shY=", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, "webpack-sources": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", @@ -10446,7 +11280,7 @@ }, "websocket-driver": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "resolved": "http://192.168.31.62:4873/websocket-driver/-/websocket-driver-0.7.0.tgz", "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { @@ -10456,8 +11290,8 @@ }, "websocket-extensions": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "resolved": "http://192.168.31.62:4873/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha1-XS/yKXcAPsaHpLhwc9+7rBRszyk=", "dev": true }, "whet.extend": { @@ -10483,8 +11317,8 @@ }, "worker-farm": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "resolved": "http://192.168.31.62:4873/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha1-rsxAWXb6talVJhgIRvDboojzpKA=", "dev": true, "requires": { "errno": "~0.1.7" diff --git a/package.json b/package.json index 5c762de8..c2028a9e 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,18 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --mode production", - "client": "webpack-dev-server --mode development" + "client": "webpack-dev-server --mode development --content-base" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "autoprefixer": "^8.4.1", + "babel": "^6.23.0", "babel-core": "^6.26.3", "babel-loader": "^7.1.4", - "babel-preset-env": "^1.6.1", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.7.0", "clean-webpack-plugin": "^0.1.19", "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.11", @@ -30,11 +32,14 @@ "purify-css": "^1.2.5", "purifycss-webpack": "^0.7.0", "style-loader": "^0.21.0", + "ts-loader": "^4.3.0", + "typescript": "^2.9.1", "uglifyjs-webpack-plugin": "^1.2.5", "url-loader": "^1.0.1", - "webpack": "^4.6.0", + "webpack": "^4.10.2", "webpack-cli": "^2.1.2", - "webpack-dev-server": "^3.1.3" + "webpack-dev-server": "^3.1.4", + "webpack-merge": "^4.1.2" }, "dependencies": { "jquery": "^3.3.1" diff --git a/src/js/index.ts b/src/js/index.ts new file mode 100644 index 00000000..4489edcc --- /dev/null +++ b/src/js/index.ts @@ -0,0 +1,17 @@ +class Shape { + area: number; + color: string; + constructor ( name: string, width: number, height: number ) { + this.area = width * height; + this.color = "pink"; + }; + + shoutout() { + return "I'm " + this.color + " with an area of " + this.area + " cm squared."; + } +} + +var square = new Shape("square", 30, 30); +console.log( square.shoutout() ); +console.log( 'Area of Shape: ' + square.area ); +console.log( 'Color of Shape: ' + square.color ); \ No newline at end of file diff --git a/src/js/index2.ts b/src/js/index2.ts new file mode 100644 index 00000000..632fe621 --- /dev/null +++ b/src/js/index2.ts @@ -0,0 +1,17 @@ +class Square { + area: number; + color: string; + constructor ( name: string, width: number, height: number ) { + this.area = width * height; + this.color = "pink"; + }; + + shoutout() { + return "I'm " + this.color + " with an area of " + this.area + " cm squared."; + } +} + +var square = new Square("square", 30, 30); +console.log( square.shoutout() ); +console.log( 'Area of Shape: ' + square.area ); +console.log( 'Color of Shape: ' + square.color ); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..8bb545ea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": true, + "module": "commonjs", + "target": "es5", + "jsx": "react", + "allowJs": true + } + } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 61fc9085..2aaf26b2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,8 +5,8 @@ const rulesConfig = require("./webpack.rules.js"); module.exports = { entry: { // 多入口文件 - a: './src/js/index.js', - b: './src/js/index2.js', + a: './src/js/index.ts', + b: './src/js/index2.ts', jquery: 'jquery' }, output: { @@ -24,10 +24,13 @@ module.exports = { hot: false, // 开启热更新 inline: true, }, - // devtool: "source-map", // 开启调试模式 + devtool: "inline-source-map", // 开启调试模式 module:{ rules: rulesConfig }, + resolve: { + extensions: [".tsx", ".ts", ".js",".json"] + }, // 提取js,lib1名字可改 optimization: { splitChunks: { diff --git a/webpack.rules.js b/webpack.rules.js index ee996ec4..0900fd8b 100644 --- a/webpack.rules.js +++ b/webpack.rules.js @@ -1,5 +1,10 @@ const extractTextPlugin = require("extract-text-webpack-plugin"); module.exports = [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/ + }, { test: /\.css$/, // 不分离的写法 @@ -23,12 +28,11 @@ module.exports = [ }) }, - { - test: /\.js$/, - use: ["babel-loader"], - // 不检查node_modules下的js文件 - exclude: "/node_modules/" - }, + // { + // test: /\.js$/, + // use: ["babel-loader"], + // exclude: "/node_modules/" + // }, { test: /\.(png|jpg|gif)$/, use: [{ From 5f564bf5ee5ffec783161b1f76cd826a88adc561 Mon Sep 17 00:00:00 2001 From: jamesliauw Date: Sat, 2 Jun 2018 15:50:18 +0800 Subject: [PATCH 04/10] feat: add dist folder --- README.md | 2 +- .../51c5583ca9a3e99eae06868afde226e0.png | Bin 0 -> 3037 bytes dist/index.html | 1 + dist/index2.html | 1 + dist/js/a.bundle.js | 2 + dist/js/b.bundle.js | 2 + dist/js/jquery.bundle.js | 40 ++++++++++++++++++ dist/pulic/readme.txt | 1 + 8 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 dist/images/51c5583ca9a3e99eae06868afde226e0.png create mode 100644 dist/index.html create mode 100644 dist/index2.html create mode 100644 dist/js/a.bundle.js create mode 100644 dist/js/b.bundle.js create mode 100644 dist/js/jquery.bundle.js create mode 100644 dist/pulic/readme.txt diff --git a/README.md b/README.md index 14383245..74f6f564 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # webpack4.x_Demo -1.add ts +1.add ts done 2.add multi page entrance 3.add egg.js diff --git a/dist/images/51c5583ca9a3e99eae06868afde226e0.png b/dist/images/51c5583ca9a3e99eae06868afde226e0.png new file mode 100644 index 0000000000000000000000000000000000000000..fa6cf08328ff960a388f292f2ff02cdd5578b858 GIT binary patch literal 3037 zcmdT`YdF+f7vKLGGovBbK`|Jm$^DjFNk%i889GuScMdAIGKe^HWGE7043TSuk~#iD}rwQ~is1U;2px)x_XUb3L{1%sD#r3J-c zo!V|<;0T!c7yB6J-u7R$FXe3r;X-FBF;$Q?LqL zYu%{q`aw`+_7^1uo=eJGpGhk$fQ8J8(h+M<{BpE#d$I1?r(Qg*w0|_zbzx`K>zH4q zLar9BzbDl=qV|ZUy5G5u-XD5<76#mIa$zeVD`}1NsgQe3HSiH*P0?Fta-s)K{tALa z#<*0HAn~^@&TlRcF5MDh1 zHv?t;_NRlI{Xi8Xv)%jITc$1X*ysIK9r+z=I1cR;J=q;<$= zVTXmW#O|#1hLE5S*fJrTInDu&d;Moh&!oRp-ot&ok3u$4_`W>q!{xTGWJ@o`I(#;4 z40Gwh?UbR99tdfVit`VR7fkljlqNZpUo++$tt2qn9v`AT-Lt412(Jiy3NxzSt7m-N zIw+SCwXTukX$ZKN&mpx_mN9)!80Nko8Ow9(JtYNcm_Bs^4^8Kl0N?3t{%Y7K9b-`J z)>5xdhfe@zm}7n0WjQ|6`-@pWy)WzV;}miR`+cAD&;AJLPp;V20paFr{8nA@FciZ! zFmL4$p3hVq_`%QQq_7BhG^JFrB39L<@>z`_el}UT zlB^O04N$TNlg8|<_$95|TWS*%`HPh~xyGW5P6!t?pKiY*{TGDubu@M}(u{=@CLPuC zx;5kA1a@suuC){YZe6u@vZtfHbjiH%P}#7-d6;qA_g%Q=LBDJI<4p!c;M-QRdITij zs}~S6r5l(?&^{eD1HMlxL(5Rf^iTmY`9uRFG;KRL46t4NGpn)2_I`pm^#hhyAez8# zX+n&*Wq>$MKomoWDY5icqD{*xGp1SP6N%CL*8rs4=)HRsU}KReT&%bM5Pn?cs18uo za3vbT$dAk+it5JgKx8r-4r8(Cy=i5o{{r^IINC#@Goq9*>_- ziGCVmyYF$Z#JG+03XcZPWWBe%#UN6Fzs{WC8DrCt^lDzA zal0nx&H2&D49>#SY?0T|faLg(%fy8#A@Mcbhx`_4BPe+|+_@E-5aMjsFh1il=MjD*rmWV2PDj)CBmyc>)&iA@=W7xFE zW8G(MbqzV?j&#WX3<6&v*FhIPx>LZ#wZ?jg|El-M5 zT`x{Zzo8+2fxEu!=bn4C{ZK~c)jG~_<2Gf*e$>LuP4l{O1pZXq!IDtTy?|heE^XUc z@gZtyKPDFr+a1`-vTCH`pN$Hwy6D3DUefn1)P$p>a69j=ad(cGKqNBx{d-{t8?}w%cpiCz@F6wfEbAjjEAtxzFkl_=d+G7l0II663xhjCf zG_?=xMG=59)yv-ifdltvN|dL7AVb+Vpz;9_Ak?>}dt(8_R8FcKz%W33Mkebv90yp% zwGG)YG4O8b3@eE5#?pUcf0obc5Mlawa~|2n;`MOP`-S0YThf!>0Rh~vuU~euDz4HA zXZp_I!%iI}PflBGIDzRWaYoFeX`F?Kh@e|GPhk(qa!aeSRb2eP7Z|COWB1RePgd0xX#r)qtW@Cqq zX>f*qn00&DE}14I-~+ zb?-5Fn3A;e2Tl&P_FFHN4y+d~N$suH&g-*81fpVP>EsyM3NUH2?U z`|sTNZeoc1S*8+VTZZdT*9(sbC@pm^>4KE4fNr z@ZKkPyFPBR)IjE$j^_4mj@&9CHz}3b3sEQ*73L5T4dM%bw@KhPS{LVJX*UxFJbm`0 zd!B+ot&yafUNK page1
pomelott template
less test
sass test
\ No newline at end of file diff --git a/dist/index2.html b/dist/index2.html new file mode 100644 index 00000000..4e4ceb39 --- /dev/null +++ b/dist/index2.html @@ -0,0 +1 @@ + page2 \ No newline at end of file diff --git a/dist/js/a.bundle.js b/dist/js/a.bundle.js new file mode 100644 index 00000000..5439f023 --- /dev/null +++ b/dist/js/a.bundle.js @@ -0,0 +1,2 @@ +!function(e){function r(r){for(var t,o,c=r[0],i=r[1],d=r[2],a=0,l=[];a0;){var o=t.pop(),i=o.id,d=o.chain;if((c=H[i])&&!c.hot._selfAccepted){if(c.hot._selfDeclined)return{type:"self-declined",chain:d,moduleId:i};if(c.hot._main)return{type:"unaccepted",chain:d,moduleId:i};for(var a=0;a ")),j.type){case"self-declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of self decline: "+j.moduleId+I));break;case"declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of declined dependency: "+j.moduleId+" in "+j.parentId+I));break;case"unaccepted":r.onUnaccepted&&r.onUnaccepted(j),r.ignoreUnaccepted||(D=new Error("Aborted because "+d+" is not accepted"+I));break;case"accepted":r.onAccepted&&r.onAccepted(j),E=!0;break;case"disposed":r.onDisposed&&r.onDisposed(j),P=!0;break;default:throw new Error("Unexception type "+j.type)}if(D)return f("abort"),Promise.reject(D);if(E)for(d in w[d]=v[d],s(b,j.outdatedModules),j.outdatedDependencies)Object.prototype.hasOwnProperty.call(j.outdatedDependencies,d)&&(h[d]||(h[d]=[]),s(h[d],j.outdatedDependencies[d]));P&&(s(b,[j.moduleId]),w[d]=m)}var M,A=[];for(t=0;t0;)if(d=q.pop(),c=H[d]){var T={},R=c.hot._disposeHandlers;for(o=0;o=0&&J.parents.splice(M,1))}}for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(c=H[d]))for(U=h[d],o=0;o=0&&c.children.splice(M,1);for(d in f("apply"),i=y,w)Object.prototype.hasOwnProperty.call(w,d)&&(e[d]=w[d]);var N=null;for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(c=H[d])){U=h[d];var L=[];for(t=0;t=0&&r._disposeHandlers.splice(n,1)},check:j,apply:P,status:function(e){if(!e)return u;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var r=s.indexOf(e);r>=0&&s.splice(r,1)},data:a[e]};return o=void 0,r}(r),parents:(p=l,l=[],p),children:[]};return e[r].call(n.exports,n,n.exports,function(e){var r=H[e];if(!r)return k;var n=function(n){return r.hot.active?(H[n]?-1===H[n].parents.indexOf(e)&&H[n].parents.push(e):(l=[e],o=n),-1===r.children.indexOf(n)&&r.children.push(n)):(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),l=[]),k(n)},t=function(e){return{configurable:!0,enumerable:!0,get:function(){return k[e]},set:function(r){k[e]=r}}};for(var c in k)Object.prototype.hasOwnProperty.call(k,c)&&"e"!==c&&Object.defineProperty(n,c,t(c));return n.e=function(e){return"ready"===u&&f("prepare"),w++,k.e(e).then(r,function(e){throw r(),e});function r(){w--,"prepare"===u&&(m[e]||D(e),0===w&&0===b&&E())}},n}(r)),n.l=!0,n.exports}k.m=e,k.c=H,k.d=function(e,r,n){k.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},k.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},k.t=function(e,r){if(1&r&&(e=k(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(k.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var t in e)k.d(n,t,function(r){return e[r]}.bind(null,t));return n},k.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return k.d(r,"a",r),r},k.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},k.p="",k.h=function(){return i};var M=window.webpackJsonp=window.webpackJsonp||[],A=M.push.bind(M);M.push=r,M=M.slice();for(var S=0;S0;){var o=t.pop(),i=o.id,d=o.chain;if((c=H[i])&&!c.hot._selfAccepted){if(c.hot._selfDeclined)return{type:"self-declined",chain:d,moduleId:i};if(c.hot._main)return{type:"unaccepted",chain:d,moduleId:i};for(var a=0;a ")),j.type){case"self-declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of self decline: "+j.moduleId+I));break;case"declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of declined dependency: "+j.moduleId+" in "+j.parentId+I));break;case"unaccepted":r.onUnaccepted&&r.onUnaccepted(j),r.ignoreUnaccepted||(D=new Error("Aborted because "+d+" is not accepted"+I));break;case"accepted":r.onAccepted&&r.onAccepted(j),E=!0;break;case"disposed":r.onDisposed&&r.onDisposed(j),P=!0;break;default:throw new Error("Unexception type "+j.type)}if(D)return f("abort"),Promise.reject(D);if(E)for(d in w[d]=v[d],s(b,j.outdatedModules),j.outdatedDependencies)Object.prototype.hasOwnProperty.call(j.outdatedDependencies,d)&&(h[d]||(h[d]=[]),s(h[d],j.outdatedDependencies[d]));P&&(s(b,[j.moduleId]),w[d]=m)}var M,A=[];for(t=0;t0;)if(d=q.pop(),c=H[d]){var T={},R=c.hot._disposeHandlers;for(o=0;o=0&&J.parents.splice(M,1))}}for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(c=H[d]))for(U=h[d],o=0;o=0&&c.children.splice(M,1);for(d in f("apply"),i=y,w)Object.prototype.hasOwnProperty.call(w,d)&&(e[d]=w[d]);var N=null;for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(c=H[d])){U=h[d];var L=[];for(t=0;t=0&&r._disposeHandlers.splice(n,1)},check:j,apply:P,status:function(e){if(!e)return u;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var r=s.indexOf(e);r>=0&&s.splice(r,1)},data:a[e]};return o=void 0,r}(r),parents:(p=l,l=[],p),children:[]};return e[r].call(n.exports,n,n.exports,function(e){var r=H[e];if(!r)return k;var n=function(n){return r.hot.active?(H[n]?-1===H[n].parents.indexOf(e)&&H[n].parents.push(e):(l=[e],o=n),-1===r.children.indexOf(n)&&r.children.push(n)):(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),l=[]),k(n)},t=function(e){return{configurable:!0,enumerable:!0,get:function(){return k[e]},set:function(r){k[e]=r}}};for(var c in k)Object.prototype.hasOwnProperty.call(k,c)&&"e"!==c&&Object.defineProperty(n,c,t(c));return n.e=function(e){return"ready"===u&&f("prepare"),w++,k.e(e).then(r,function(e){throw r(),e});function r(){w--,"prepare"===u&&(m[e]||D(e),0===w&&0===b&&E())}},n}(r)),n.l=!0,n.exports}k.m=e,k.c=H,k.d=function(e,r,n){k.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},k.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},k.t=function(e,r){if(1&r&&(e=k(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(k.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var t in e)k.d(n,t,function(r){return e[r]}.bind(null,t));return n},k.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return k.d(r,"a",r),r},k.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},k.p="",k.h=function(){return i};var M=window.webpackJsonp=window.webpackJsonp||[],A=M.push.bind(M);M.push=r,M=M.slice();for(var S=0;S0&&t-1 in e)}C.fn=C.prototype={jquery:"3.3.1",constructor:C,length:0,toArray:function(){return u.call(this)},get:function(e){return null==e?u.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=C.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return C.each(this,e)},map:function(e){return this.pushStack(C.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(u.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},oe=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function ie(e,t,r,o){var i,s,l,c,f,h,y,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!o&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=Q.exec(e)))if(i=f[1]){if(9===T){if(!(l=t.getElementById(i)))return r;if(l.id===i)return r.push(l),r}else if(m&&(l=m.getElementById(i))&&x(t,l)&&l.id===i)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((i=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(i)),r}if(n.qsa&&!S[e+" "]&&(!v||!v.test(e))){if(1!==T)m=t,y=e;else if("object"!==t.nodeName.toLowerCase()){for((c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;s--;)h[s]="#"+c+" "+ye(h[s]);y=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(y)try{return L.apply(r,m.querySelectorAll(y)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,o)}function ae(){var e=[];return function t(n,o){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=o}}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){for(var n=e.split("|"),o=n.length;o--;)r.attrHandle[n[o]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&oe(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function ge(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=ie.support={},i=ie.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=ie.setDocument=function(e){var t,o,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(h=(d=a).documentElement,g=!i(d),w!==d&&(o=d.defaultView)&&o.top!==o&&(o.addEventListener?o.addEventListener("unload",re,!1):o.attachEvent&&o.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=J.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,o,i=t.getElementById(e);if(i){if((n=i.getAttributeNode("id"))&&n.value===e)return[i];for(o=t.getElementsByName(e),r=0;i=o[r++];)if((n=i.getAttributeNode("id"))&&n.value===e)return[i]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=J.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="
",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(n.matchesSelector=J.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=J.test(h.compareDocumentPosition),x=t||J.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],s=[t];if(!o||!i)return e===d?-1:t===d?1:o?-1:i?1:c?O(c,e)-O(c,t):0;if(o===i)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},ie.matches=function(e,t){return ie(e,null,null,t)},ie.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!y||!y.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return ie(t,d,null,[e]).length>0},ie.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},ie.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var o=r.attrHandle[t.toLowerCase()],i=o&&N.call(r.attrHandle,t.toLowerCase())?o(e,t,!g):void 0;return void 0!==i?i:n.attributes||!g?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null},ie.escape=function(e){return(e+"").replace(te,ne)},ie.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ie.uniqueSort=function(e){var t,r=[],o=0,i=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){for(;t=e[i++];)t===e[i]&&(o=r.push(i));for(;o--;)e.splice(r[o],1)}return c=null,e},o=ie.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=o(t);return n},(r=ie.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ie.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ie.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var o=ie.attr(r,e);return null==o?"!="===t:!t||(o+="","="===t?o===n:"!="===t?o!==n:"^="===t?n&&0===o.indexOf(n):"*="===t?n&&o.indexOf(n)>-1:"$="===t?n&&o.slice(-n.length)===n:"~="===t?(" "+o.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(o===n||o.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=i!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(i){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(x=(d=(l=(c=(f=(p=v)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p!==t)););return(x-=o)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,o=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ie.error("unsupported pseudo: "+e);return o[b]?o(t):o.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){for(var r,i=o(e,t),a=i.length;a--;)e[r=O(e,i[a])]=!(n[r]=i[a])}):function(e){return o(e,0,n)}):o}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,o){for(var i,a=r(e,null,o,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return ie(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||ie.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function be(e,t,n,r,o){for(var i,a=[],s=0,u=e.length,l=null!=t;s-1&&(i[l]=!(a[l]=f))}}else y=be(y===a?y.splice(h,y.length):y),o?o(null,a,y,u):L.apply(a,y)})}function Te(e){for(var t,n,o,i=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var o=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,o}];u1&&xe(p),u>1&&ye(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,o=e.length>0,i=function(i,a,s,u,c){var f,h,v,y=0,m="0",x=i&&[],b=[],w=l,C=i||o&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(o&&f){for(h=0,a||f.ownerDocument===d||(p(f),s=!g);v=e[h++];)if(v(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!v&&f)&&y--,i&&x.push(f))}if(y+=m,n&&m!==y){for(h=0;v=t[h++];)v(x,b,a,s);if(i){if(y>0)for(;m--;)x[m]||b[m]||(b[m]=j.call(u));b=be(b)}L.apply(u,b),c&&!i&&b.length>0&&y+t.length>1&&ie.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(i):i}(i,o))).selector=e}return s},u=ie.select=function(e,t,n,o){var i,u,l,c,f,p="function"==typeof e&&e,d=!o&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(i=V.needsContext.test(e)?0:u.length;i--&&(l=u[i],!r.relative[c=l.type]);)if((f=r.find[c])&&(o=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(i,1),!(e=o.length&&ye(u)))return L.apply(n,o),n;break}}return(p||s(e,d))(o,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),ie}(n);C.find=S,C.expr=S.selectors,C.expr[":"]=C.expr.pseudos,C.uniqueSort=C.unique=S.uniqueSort,C.text=S.getText,C.isXMLDoc=S.isXML,C.contains=S.contains,C.escapeSelector=S.escape;var D=function(e,t,n){for(var r=[],o=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(o&&C(e).is(n))break;r.push(e)}return r},N=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},A=C.expr.match.needsContext;function j(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var q=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,t,n){return m(t)?C.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?C.grep(e,function(e){return e===t!==n}):"string"!=typeof t?C.grep(e,function(e){return f.call(t,e)>-1!==n}):C.filter(t,e,n)}C.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?C.find.matchesSelector(r,e)?[r]:[]:C.find.matches(e,C.grep(t,function(e){return 1===e.nodeType}))},C.fn.extend({find:function(e){var t,n,r=this.length,o=this;if("string"!=typeof e)return this.pushStack(C(e).filter(function(){for(t=0;t1?C.uniqueSort(n):n},filter:function(e){return this.pushStack(L(this,e||[],!1))},not:function(e){return this.pushStack(L(this,e||[],!0))},is:function(e){return!!L(this,"string"==typeof e&&A.test(e)?C(e):e||[],!1).length}});var H,O=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(C.fn.init=function(e,t,n){var r,o;if(!e)return this;if(n=n||H,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:O.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof C?t[0]:t,C.merge(this,C.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:a,!0)),q.test(r[1])&&C.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(o=a.getElementById(r[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(C):C.makeArray(e,this)}).prototype=C.fn,H=C(a);var P=/^(?:parents|prev(?:Until|All))/,M={children:!0,contents:!0,next:!0,prev:!0};function R(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}C.fn.extend({has:function(e){var t=C(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&C.find.matchesSelector(n,e))){i.push(n);break}return this.pushStack(i.length>1?C.uniqueSort(i):i)},index:function(e){return e?"string"==typeof e?f.call(C(e),this[0]):f.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(C.uniqueSort(C.merge(this.get(),C(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),C.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return D(e,"parentNode")},parentsUntil:function(e,t,n){return D(e,"parentNode",n)},next:function(e){return R(e,"nextSibling")},prev:function(e){return R(e,"previousSibling")},nextAll:function(e){return D(e,"nextSibling")},prevAll:function(e){return D(e,"previousSibling")},nextUntil:function(e,t,n){return D(e,"nextSibling",n)},prevUntil:function(e,t,n){return D(e,"previousSibling",n)},siblings:function(e){return N((e.parentNode||{}).firstChild,e)},children:function(e){return N(e.firstChild)},contents:function(e){return j(e,"iframe")?e.contentDocument:(j(e,"template")&&(e=e.content||e),C.merge([],e.childNodes))}},function(e,t){C.fn[e]=function(n,r){var o=C.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(o=C.filter(r,o)),this.length>1&&(M[e]||C.uniqueSort(o),P.test(e)&&o.reverse()),this.pushStack(o)}});var I=/[^\x20\t\r\n\f]+/g;function W(e){return e}function $(e){throw e}function B(e,t,n,r){var o;try{e&&m(o=e.promise)?o.call(e).done(t).fail(n):e&&m(o=e.then)?o.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}C.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return C.each(e.match(I)||[],function(e,n){t[n]=!0}),t}(e):C.extend({},e);var t,n,r,o,i=[],a=[],s=-1,u=function(){for(o=o||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)i.splice(n,1),n<=s&&s--}),this},has:function(e){return e?C.inArray(e,i)>-1:i.length>0},empty:function(){return i&&(i=[]),this},disable:function(){return o=a=[],i=n="",this},disabled:function(){return!i},lock:function(){return o=a=[],n||t||(i=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},C.extend({Deferred:function(e){var t=[["notify","progress",C.Callbacks("memory"),C.Callbacks("memory"),2],["resolve","done",C.Callbacks("once memory"),C.Callbacks("once memory"),0,"resolved"],["reject","fail",C.Callbacks("once memory"),C.Callbacks("once memory"),1,"rejected"]],r="pending",o={state:function(){return r},always:function(){return i.done(arguments).fail(arguments),this},catch:function(e){return o.then(null,e)},pipe:function(){var e=arguments;return C.Deferred(function(n){C.each(t,function(t,r){var o=m(e[r[4]])&&e[r[4]];i[r[1]](function(){var e=o&&o.apply(this,arguments);e&&m(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,o?[e]:arguments)})}),e=null}).promise()},then:function(e,r,o){var i=0;function a(e,t,r,o){return function(){var s=this,u=arguments,l=function(){var n,l;if(!(e=i&&(r!==$&&(s=void 0,u=[n]),t.rejectWith(s,u))}};e?c():(C.Deferred.getStackHook&&(c.stackTrace=C.Deferred.getStackHook()),n.setTimeout(c))}}return C.Deferred(function(n){t[0][3].add(a(0,n,m(o)?o:W,n.notifyWith)),t[1][3].add(a(0,n,m(e)?e:W)),t[2][3].add(a(0,n,m(r)?r:$))}).promise()},promise:function(e){return null!=e?C.extend(e,o):o}},i={};return C.each(t,function(e,n){var a=n[2],s=n[5];o[n[1]]=a.add,s&&a.add(function(){r=s},t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(n[3].fire),i[n[0]]=function(){return i[n[0]+"With"](this===i?void 0:this,arguments),this},i[n[0]+"With"]=a.fireWith}),o.promise(i),e&&e.call(i,i),i},when:function(e){var t=arguments.length,n=t,r=Array(n),o=u.call(arguments),i=C.Deferred(),a=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?u.call(arguments):n,--t||i.resolveWith(r,o)}};if(t<=1&&(B(e,i.done(a(n)).resolve,i.reject,!t),"pending"===i.state()||m(o[n]&&o[n].then)))return i.then();for(;n--;)B(o[n],a(n),i.reject);return i.promise()}});var F=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;C.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&F.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},C.readyException=function(e){n.setTimeout(function(){throw e})};var _=C.Deferred();function z(){a.removeEventListener("DOMContentLoaded",z),n.removeEventListener("load",z),C.ready()}C.fn.ready=function(e){return _.then(e).catch(function(e){C.readyException(e)}),this},C.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--C.readyWait:C.isReady)||(C.isReady=!0,!0!==e&&--C.readyWait>0||_.resolveWith(a,[C]))}}),C.ready.then=_.then,"complete"===a.readyState||"loading"!==a.readyState&&!a.documentElement.doScroll?n.setTimeout(C.ready):(a.addEventListener("DOMContentLoaded",z),n.addEventListener("load",z));var X=function(e,t,n,r,o,i,a){var s=0,u=e.length,l=null==n;if("object"===T(n))for(s in o=!0,n)X(e,t,s,n[s],!0,i,a);else if(void 0!==r&&(o=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(C(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){Z.remove(this,e)})}}),C.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,C.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=C.queue(e,t),r=n.length,o=n.shift(),i=C._queueHooks(e,t);"inprogress"===o&&(o=n.shift(),r--),o&&("fx"===t&&n.unshift("inprogress"),delete i.stop,o.call(e,function(){C.dequeue(e,t)},i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:C.Callbacks("once memory").add(function(){K.remove(e,[t+"queue",n])})})}}),C.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&j(e,t)?C.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n-1)o&&o.push(i);else if(l=C.contains(i.ownerDocument,i),a=ve(f.appendChild(i),"script"),l&&ye(a),n)for(c=0;i=a[c++];)he.test(i.type||"")&&n.push(i);return f}me=a.createDocumentFragment().appendChild(a.createElement("div")),(xe=a.createElement("input")).setAttribute("type","radio"),xe.setAttribute("checked","checked"),xe.setAttribute("name","t"),me.appendChild(xe),y.checkClone=me.cloneNode(!0).cloneNode(!0).lastChild.checked,me.innerHTML="",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=a.documentElement,Ce=/^key/,Ee=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ke=/^([^.]*)(?:\.(.+)|)/;function Se(){return!0}function De(){return!1}function Ne(){try{return a.activeElement}catch(e){}}function Ae(e,t,n,r,o,i){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],i);return e}if(null==r&&null==o?(o=n,r=n=void 0):null==o&&("string"==typeof n?(o=r,r=void 0):(o=r,r=n,n=void 0)),!1===o)o=De;else if(!o)return e;return 1===i&&(a=o,(o=function(e){return C().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=C.guid++)),e.each(function(){C.event.add(this,t,o,r,n)})}C.event={global:{},add:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,g,v=K.get(e);if(v)for(n.handler&&(n=(i=n).handler,o=i.selector),o&&C.find.matchesSelector(Te,o),n.guid||(n.guid=C.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==C&&C.event.triggered!==t.type?C.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(I)||[""]).length;l--;)d=g=(s=ke.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=C.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=C.event.special[d]||{},c=C.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:o,needsContext:o&&C.expr.match.needsContext.test(o),namespace:h.join(".")},i),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),o?p.splice(p.delegateCount++,0,c):p.push(c),C.event.global[d]=!0)},remove:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,g,v=K.hasData(e)&&K.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(I)||[""]).length;l--;)if(d=g=(s=ke.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=C.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=i=p.length;i--;)c=p[i],!o&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(i,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||C.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)C.event.remove(e,d+t[l],n,r,!0);C.isEmptyObject(u)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,r,o,i,a,s=C.event.fix(e),u=new Array(arguments.length),l=(K.get(this,"events")||{})[s.type]||[],c=C.event.special[s.type]||{};for(u[0]=s,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(i=[],a={},n=0;n-1:C.find(o,this,null,[l]).length),a[o]&&i.push(r);i.length&&s.push({elem:l,handlers:i})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return j(e,"table")&&j(11!==t.nodeType?t:t.firstChild,"tr")&&C(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Me(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Re(e,t){var n,r,o,i,a,s,u,l;if(1===t.nodeType){if(K.hasData(e)&&(i=K.access(e),a=K.set(t,i),l=i.events))for(o in delete a.handle,a.events={},l)for(n=0,r=l[o].length;n1&&"string"==typeof h&&!y.checkClone&&Le.test(h))return e.each(function(o){var i=e.eq(o);g&&(t[0]=h.call(this,o,i.html())),Ie(i,t,n,r)});if(p&&(i=(o=we(t,e[0].ownerDocument,!1,e,r)).firstChild,1===o.childNodes.length&&(o=i),i||r)){for(s=(a=C.map(ve(o,"script"),Pe)).length;f")},clone:function(e,t,n){var r,o,i,a,s,u,l,c=e.cloneNode(!0),f=C.contains(e.ownerDocument,e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||C.isXMLDoc(e)))for(a=ve(c),r=0,o=(i=ve(e)).length;r0&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,o=C.event.special,i=0;void 0!==(n=e[i]);i++)if(J(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)o[r]?C.event.remove(n,r):C.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[Z.expando]&&(n[Z.expando]=void 0)}}}),C.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return X(this,function(e){return void 0===e?C.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Ie(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)})},prepend:function(){return Ie(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(C.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return C.clone(this,e,t)})},html:function(e){return X(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!qe.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=C.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-i-u-s-.5))),u}function et(e,t,n){var r=Be(e),o=_e(e,t,r),i="border-box"===C.css(e,"boxSizing",!1,r),a=i;if($e.test(o)){if(!n)return o;o="auto"}return a=a&&(y.boxSizingReliable()||o===e.style[t]),("auto"===o||!parseFloat(o)&&"inline"===C.css(e,"display",!1,r))&&(o=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(o=parseFloat(o)||0)+Ze(e,t,n||(i?"border":"content"),a,r,o)+"px"}function tt(e,t,n,r,o){return new tt.prototype.init(e,t,n,r,o)}C.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=_e(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,i,a,s=Y(t),u=Ue.test(t),l=e.style;if(u||(t=Qe(s)),a=C.cssHooks[t]||C.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(o=a.get(e,!1,r))?o:l[t];"string"===(i=typeof n)&&(o=oe.exec(n))&&o[1]&&(n=ue(e,t,o),i="number"),null!=n&&n==n&&("number"===i&&(n+=o&&o[3]||(C.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var o,i,a,s=Y(t);return Ue.test(t)||(t=Qe(s)),(a=C.cssHooks[t]||C.cssHooks[s])&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=_e(e,t,r)),"normal"===o&&t in Ge&&(o=Ge[t]),""===n||n?(i=parseFloat(o),!0===n||isFinite(i)?i||0:o):o}}),C.each(["height","width"],function(e,t){C.cssHooks[t]={get:function(e,n,r){if(n)return!Xe.test(C.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ve,function(){return et(e,t,r)})},set:function(e,n,r){var o,i=Be(e),a="border-box"===C.css(e,"boxSizing",!1,i),s=r&&Ze(e,t,r,a,i);return a&&y.scrollboxSize()===i.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(i[t])-Ze(e,t,"border",!1,i)-.5)),s&&(o=oe.exec(n))&&"px"!==(o[3]||"px")&&(e.style[t]=n,n=C.css(e,t)),Ke(0,n,s)}}}),C.cssHooks.marginLeft=ze(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(_e(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),C.each({margin:"",padding:"",border:"Width"},function(e,t){C.cssHooks[e+t]={expand:function(n){for(var r=0,o={},i="string"==typeof n?n.split(" "):[n];r<4;r++)o[e+ie[r]+t]=i[r]||i[r-2]||i[0];return o}},"margin"!==e&&(C.cssHooks[e+t].set=Ke)}),C.fn.extend({css:function(e,t){return X(this,function(e,t,n){var r,o,i={},a=0;if(Array.isArray(t)){for(r=Be(e),o=t.length;a1)}}),C.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||C.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(C.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=C.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=C.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){C.fx.step[e.prop]?C.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[C.cssProps[e.prop]]&&!C.cssHooks[e.prop]?e.elem[e.prop]=e.now:C.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},C.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},C.fx=tt.prototype.init,C.fx.step={};var nt,rt,ot=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function at(){rt&&(!1===a.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(at):n.setTimeout(at,C.fx.interval),C.fx.tick())}function st(){return n.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,o={height:e};for(t=t?1:0;r<4;r+=2-t)o["margin"+(n=ie[r])]=o["padding"+n]=e;return t&&(o.opacity=o.width=e),o}function lt(e,t,n){for(var r,o=(ct.tweeners[t]||[]).concat(ct.tweeners["*"]),i=0,a=o.length;i1)},removeAttr:function(e){return this.each(function(){C.removeAttr(this,e)})}}),C.extend({attr:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return void 0===e.getAttribute?C.prop(e,t,n):(1===i&&C.isXMLDoc(e)||(o=C.attrHooks[t.toLowerCase()]||(C.expr.match.bool.test(t)?ft:void 0)),void 0!==n?null===n?void C.removeAttr(e,t):o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:(e.setAttribute(t,n+""),n):o&&"get"in o&&null!==(r=o.get(e,t))?r:null==(r=C.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&j(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,o=t&&t.match(I);if(o&&1===e.nodeType)for(;n=o[r++];)e.removeAttribute(n)}}),ft={set:function(e,t,n){return!1===t?C.removeAttr(e,n):e.setAttribute(n,n),n}},C.each(C.expr.match.bool.source.match(/\w+/g),function(e,t){var n=pt[t]||C.find.attr;pt[t]=function(e,t,r){var o,i,a=t.toLowerCase();return r||(i=pt[a],pt[a]=o,o=null!=n(e,t,r)?a:null,pt[a]=i),o}});var dt=/^(?:input|select|textarea|button)$/i,ht=/^(?:a|area)$/i;function gt(e){return(e.match(I)||[]).join(" ")}function vt(e){return e.getAttribute&&e.getAttribute("class")||""}function yt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(I)||[]}C.fn.extend({prop:function(e,t){return X(this,C.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[C.propFix[e]||e]})}}),C.extend({prop:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return 1===i&&C.isXMLDoc(e)||(t=C.propFix[t]||t,o=C.propHooks[t]),void 0!==n?o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:e[t]=n:o&&"get"in o&&null!==(r=o.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=C.find.attr(e,"tabindex");return t?parseInt(t,10):dt.test(e.nodeName)||ht.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),y.optSelected||(C.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),C.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){C.propFix[this.toLowerCase()]=this}),C.fn.extend({addClass:function(e){var t,n,r,o,i,a,s,u=0;if(m(e))return this.each(function(t){C(this).addClass(e.call(this,t,vt(this)))});if((t=yt(e)).length)for(;n=this[u++];)if(o=vt(n),r=1===n.nodeType&&" "+gt(o)+" "){for(a=0;i=t[a++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");o!==(s=gt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,o,i,a,s,u=0;if(m(e))return this.each(function(t){C(this).removeClass(e.call(this,t,vt(this)))});if(!arguments.length)return this.attr("class","");if((t=yt(e)).length)for(;n=this[u++];)if(o=vt(n),r=1===n.nodeType&&" "+gt(o)+" "){for(a=0;i=t[a++];)for(;r.indexOf(" "+i+" ")>-1;)r=r.replace(" "+i+" "," ");o!==(s=gt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):m(e)?this.each(function(n){C(this).toggleClass(e.call(this,n,vt(this),t),t)}):this.each(function(){var t,o,i,a;if(r)for(o=0,i=C(this),a=yt(e);t=a[o++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||((t=vt(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+gt(vt(n))+" ").indexOf(t)>-1)return!0;return!1}});var mt=/\r/g;C.fn.extend({val:function(e){var t,n,r,o=this[0];return arguments.length?(r=m(e),this.each(function(n){var o;1===this.nodeType&&(null==(o=r?e.call(this,n,C(this).val()):e)?o="":"number"==typeof o?o+="":Array.isArray(o)&&(o=C.map(o,function(e){return null==e?"":e+""})),(t=C.valHooks[this.type]||C.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,o,"value")||(this.value=o))})):o?(t=C.valHooks[o.type]||C.valHooks[o.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(o,"value"))?n:"string"==typeof(n=o.value)?n.replace(mt,""):null==n?"":n:void 0}}),C.extend({valHooks:{option:{get:function(e){var t=C.find.attr(e,"value");return null!=t?t:gt(C.text(e))}},select:{get:function(e){var t,n,r,o=e.options,i=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?i+1:o.length;for(r=i<0?u:a?i:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),i}}}}),C.each(["radio","checkbox"],function(){C.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=C.inArray(C(e).val(),t)>-1}},y.checkOn||(C.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in n;var xt=/^(?:focusinfocus|focusoutblur)$/,bt=function(e){e.stopPropagation()};C.extend(C.event,{trigger:function(e,t,r,o){var i,s,u,l,c,f,p,d,g=[r||a],v=h.call(e,"type")?e.type:e,y=h.call(e,"namespace")?e.namespace.split("."):[];if(s=d=u=r=r||a,3!==r.nodeType&&8!==r.nodeType&&!xt.test(v+C.event.triggered)&&(v.indexOf(".")>-1&&(v=(y=v.split(".")).shift(),y.sort()),c=v.indexOf(":")<0&&"on"+v,(e=e[C.expando]?e:new C.Event(v,"object"==typeof e&&e)).isTrigger=o?2:3,e.namespace=y.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+y.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:C.makeArray(t,[e]),p=C.event.special[v]||{},o||!p.trigger||!1!==p.trigger.apply(r,t))){if(!o&&!p.noBubble&&!x(r)){for(l=p.delegateType||v,xt.test(l+v)||(s=s.parentNode);s;s=s.parentNode)g.push(s),u=s;u===(r.ownerDocument||a)&&g.push(u.defaultView||u.parentWindow||n)}for(i=0;(s=g[i++])&&!e.isPropagationStopped();)d=s,e.type=i>1?l:p.bindType||v,(f=(K.get(s,"events")||{})[e.type]&&K.get(s,"handle"))&&f.apply(s,t),(f=c&&s[c])&&f.apply&&J(s)&&(e.result=f.apply(s,t),!1===e.result&&e.preventDefault());return e.type=v,o||e.isDefaultPrevented()||p._default&&!1!==p._default.apply(g.pop(),t)||!J(r)||c&&m(r[v])&&!x(r)&&((u=r[c])&&(r[c]=null),C.event.triggered=v,e.isPropagationStopped()&&d.addEventListener(v,bt),r[v](),e.isPropagationStopped()&&d.removeEventListener(v,bt),C.event.triggered=void 0,u&&(r[c]=u)),e.result}},simulate:function(e,t,n){var r=C.extend(new C.Event,n,{type:e,isSimulated:!0});C.event.trigger(r,null,t)}}),C.fn.extend({trigger:function(e,t){return this.each(function(){C.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return C.event.trigger(e,t,n,!0)}}),y.focusin||C.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){C.event.simulate(t,e.target,C.event.fix(e))};C.event.special[t]={setup:function(){var r=this.ownerDocument||this,o=K.access(r,t);o||r.addEventListener(e,n,!0),K.access(r,t,(o||0)+1)},teardown:function(){var r=this.ownerDocument||this,o=K.access(r,t)-1;o?K.access(r,t,o):(r.removeEventListener(e,n,!0),K.remove(r,t))}}});var wt=n.location,Tt=Date.now(),Ct=/\?/;C.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||C.error("Invalid XML: "+e),t};var Et=/\[\]$/,kt=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,Dt=/^(?:input|select|textarea|keygen)/i;function Nt(e,t,n,r){var o;if(Array.isArray(t))C.each(t,function(t,o){n||Et.test(e)?r(e,o):Nt(e+"["+("object"==typeof o&&null!=o?t:"")+"]",o,n,r)});else if(n||"object"!==T(t))r(e,t);else for(o in t)Nt(e+"["+o+"]",t[o],n,r)}C.param=function(e,t){var n,r=[],o=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!C.isPlainObject(e))C.each(e,function(){o(this.name,this.value)});else for(n in e)Nt(n,e[n],t,o);return r.join("&")},C.fn.extend({serialize:function(){return C.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=C.prop(this,"elements");return e?C.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!C(this).is(":disabled")&&Dt.test(this.nodeName)&&!St.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=C(this).val();return null==n?null:Array.isArray(n)?C.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var At=/%20/g,jt=/#.*$/,qt=/([?&])_=[^&]*/,Lt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ht=/^(?:GET|HEAD)$/,Ot=/^\/\//,Pt={},Mt={},Rt="*/".concat("*"),It=a.createElement("a");function Wt(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,o=0,i=t.toLowerCase().match(I)||[];if(m(n))for(;r=i[o++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function $t(e,t,n,r){var o={},i=e===Mt;function a(s){var u;return o[s]=!0,C.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||i||o[l]?i?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!o["*"]&&a("*")}function Bt(e,t){var n,r,o=C.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((o[n]?e:r||(r={}))[n]=t[n]);return r&&C.extend(!0,e,r),e}It.href=wt.href,C.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:wt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(wt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Rt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":C.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Bt(Bt(e,C.ajaxSettings),t):Bt(C.ajaxSettings,e)},ajaxPrefilter:Wt(Pt),ajaxTransport:Wt(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,o,i,s,u,l,c,f,p,d,h=C.ajaxSetup({},t),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?C(g):C.event,y=C.Deferred(),m=C.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=Lt.exec(i);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?i:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return r&&r.abort(t),k(0,t),this}};if(y.promise(E),h.url=((e||h.url||wt.href)+"").replace(Ot,wt.protocol+"//"),h.type=t.method||t.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(I)||[""],null==h.crossDomain){l=a.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=It.protocol+"//"+It.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=C.param(h.data,h.traditional)),$t(Pt,h,t,E),c)return E;for(p in(f=C.event&&h.global)&&0==C.active++&&C.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Ht.test(h.type),o=h.url.replace(jt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(At,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(Ct.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(qt,"$1"),d=(Ct.test(o)?"&":"?")+"_="+Tt+++d),h.url=o+d),h.ifModified&&(C.lastModified[o]&&E.setRequestHeader("If-Modified-Since",C.lastModified[o]),C.etag[o]&&E.setRequestHeader("If-None-Match",C.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||t.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+Rt+"; q=0.01":""):h.accepts["*"]),h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(T="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),r=$t(Mt,h,t,E)){if(E.readyState=1,f&&v.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=n.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,r.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(e,t,a,s){var l,p,d,b,w,T=t;c||(c=!0,u&&n.clearTimeout(u),r=void 0,i=s||"",E.readyState=e>0?4:0,l=e>=200&&e<300||304===e,a&&(b=function(e,t,n){for(var r,o,i,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(o in s)if(s[o]&&s[o].test(r)){u.unshift(o);break}if(u[0]in n)i=u[0];else{for(o in n){if(!u[0]||e.converters[o+" "+u[0]]){i=o;break}a||(a=o)}i=i||a}if(i)return i!==u[0]&&u.unshift(i),n[i]}(h,E,a)),b=function(e,t,n,r){var o,i,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(i=c.shift();i;)if(e.responseFields[i]&&(n[e.responseFields[i]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=i,i=c.shift())if("*"===i)i=u;else if("*"!==u&&u!==i){if(!(a=l[u+" "+i]||l["* "+i]))for(o in l)if((s=o.split(" "))[1]===i&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[o]:!0!==l[o]&&(i=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+i}}}return{state:"success",data:t}}(h,b,E,l),l?(h.ifModified&&((w=E.getResponseHeader("Last-Modified"))&&(C.lastModified[o]=w),(w=E.getResponseHeader("etag"))&&(C.etag[o]=w)),204===e||"HEAD"===h.type?T="nocontent":304===e?T="notmodified":(T=b.state,p=b.data,l=!(d=b.error))):(d=T,!e&&T||(T="error",e<0&&(e=0))),E.status=e,E.statusText=(t||T)+"",l?y.resolveWith(g,[p,T,E]):y.rejectWith(g,[E,T,d]),E.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,T]),f&&(v.trigger("ajaxComplete",[E,h]),--C.active||C.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return C.get(e,t,n,"json")},getScript:function(e,t){return C.get(e,void 0,t,"script")}}),C.each(["get","post"],function(e,t){C[t]=function(e,n,r,o){return m(n)&&(o=o||r,r=n,n=void 0),C.ajax(C.extend({url:e,type:t,dataType:o,data:n,success:r},C.isPlainObject(e)&&e))}}),C._evalUrl=function(e){return C.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},C.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=C(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return m(e)?this.each(function(t){C(this).wrapInner(e.call(this,t))}):this.each(function(){var t=C(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=m(e);return this.each(function(n){C(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){C(this).replaceWith(this.childNodes)}),this}}),C.expr.pseudos.hidden=function(e){return!C.expr.pseudos.visible(e)},C.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},C.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Ft={0:200,1223:204},_t=C.ajaxSettings.xhr();y.cors=!!_t&&"withCredentials"in _t,y.ajax=_t=!!_t,C.ajaxTransport(function(e){var t,r;if(y.cors||_t&&!e.crossDomain)return{send:function(o,i){var a,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(a in e.xhrFields)s[a]=e.xhrFields[a];for(a in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||o["X-Requested-With"]||(o["X-Requested-With"]="XMLHttpRequest"),o)s.setRequestHeader(a,o[a]);t=function(e){return function(){t&&(t=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?i(0,"error"):i(s.status,s.statusText):i(Ft[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),r=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout(function(){t&&r()})},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}}),C.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),C.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return C.globalEval(e),e}}}),C.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),C.ajaxTransport("script",function(e){var t,n;if(e.crossDomain)return{send:function(r,o){t=C(" \ No newline at end of file diff --git a/dist/index2.html b/dist/index2.html deleted file mode 100644 index 4e4ceb39..00000000 --- a/dist/index2.html +++ /dev/null @@ -1 +0,0 @@ - page2 \ No newline at end of file diff --git a/dist/js/a.bundle.js b/dist/js/a.bundle.js deleted file mode 100644 index f0a2dba6..00000000 --- a/dist/js/a.bundle.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e){function r(r){for(var t,o,i=r[0],c=r[1],d=r[2],a=0,l=[];a0;){var o=t.pop(),c=o.id,d=o.chain;if((i=H[c])&&!i.hot._selfAccepted){if(i.hot._selfDeclined)return{type:"self-declined",chain:d,moduleId:c};if(i.hot._main)return{type:"unaccepted",chain:d,moduleId:c};for(var a=0;a ")),j.type){case"self-declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of self decline: "+j.moduleId+I));break;case"declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of declined dependency: "+j.moduleId+" in "+j.parentId+I));break;case"unaccepted":r.onUnaccepted&&r.onUnaccepted(j),r.ignoreUnaccepted||(D=new Error("Aborted because "+d+" is not accepted"+I));break;case"accepted":r.onAccepted&&r.onAccepted(j),E=!0;break;case"disposed":r.onDisposed&&r.onDisposed(j),P=!0;break;default:throw new Error("Unexception type "+j.type)}if(D)return f("abort"),Promise.reject(D);if(E)for(d in w[d]=v[d],s(b,j.outdatedModules),j.outdatedDependencies)Object.prototype.hasOwnProperty.call(j.outdatedDependencies,d)&&(h[d]||(h[d]=[]),s(h[d],j.outdatedDependencies[d]));P&&(s(b,[j.moduleId]),w[d]=m)}var M,A=[];for(t=0;t0;)if(d=q.pop(),i=H[d]){var T={},R=i.hot._disposeHandlers;for(o=0;o=0&&J.parents.splice(M,1))}}for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(i=H[d]))for(U=h[d],o=0;o=0&&i.children.splice(M,1);for(d in f("apply"),c=y,w)Object.prototype.hasOwnProperty.call(w,d)&&(e[d]=w[d]);var N=null;for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(i=H[d])){U=h[d];var L=[];for(t=0;t=0&&r._disposeHandlers.splice(n,1)},check:j,apply:P,status:function(e){if(!e)return u;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var r=s.indexOf(e);r>=0&&s.splice(r,1)},data:a[e]};return o=void 0,r}(r),parents:(p=l,l=[],p),children:[]};return e[r].call(n.exports,n,n.exports,function(e){var r=H[e];if(!r)return k;var n=function(n){return r.hot.active?(H[n]?-1===H[n].parents.indexOf(e)&&H[n].parents.push(e):(l=[e],o=n),-1===r.children.indexOf(n)&&r.children.push(n)):(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),l=[]),k(n)},t=function(e){return{configurable:!0,enumerable:!0,get:function(){return k[e]},set:function(r){k[e]=r}}};for(var i in k)Object.prototype.hasOwnProperty.call(k,i)&&"e"!==i&&Object.defineProperty(n,i,t(i));return n.e=function(e){return"ready"===u&&f("prepare"),w++,k.e(e).then(r,function(e){throw r(),e});function r(){w--,"prepare"===u&&(m[e]||D(e),0===w&&0===b&&E())}},n}(r)),n.l=!0,n.exports}k.m=e,k.c=H,k.d=function(e,r,n){k.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},k.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},k.t=function(e,r){if(1&r&&(e=k(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(k.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var t in e)k.d(n,t,function(r){return e[r]}.bind(null,t));return n},k.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return k.d(r,"a",r),r},k.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},k.p="",k.h=function(){return c};var M=window.webpackJsonp=window.webpackJsonp||[],A=M.push.bind(M);M.push=r,M=M.slice();for(var S=0;S0;){var o=t.pop(),c=o.id,d=o.chain;if((i=H[c])&&!i.hot._selfAccepted){if(i.hot._selfDeclined)return{type:"self-declined",chain:d,moduleId:c};if(i.hot._main)return{type:"unaccepted",chain:d,moduleId:c};for(var a=0;a ")),j.type){case"self-declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of self decline: "+j.moduleId+I));break;case"declined":r.onDeclined&&r.onDeclined(j),r.ignoreDeclined||(D=new Error("Aborted because of declined dependency: "+j.moduleId+" in "+j.parentId+I));break;case"unaccepted":r.onUnaccepted&&r.onUnaccepted(j),r.ignoreUnaccepted||(D=new Error("Aborted because "+d+" is not accepted"+I));break;case"accepted":r.onAccepted&&r.onAccepted(j),E=!0;break;case"disposed":r.onDisposed&&r.onDisposed(j),P=!0;break;default:throw new Error("Unexception type "+j.type)}if(D)return f("abort"),Promise.reject(D);if(E)for(d in w[d]=v[d],s(b,j.outdatedModules),j.outdatedDependencies)Object.prototype.hasOwnProperty.call(j.outdatedDependencies,d)&&(h[d]||(h[d]=[]),s(h[d],j.outdatedDependencies[d]));P&&(s(b,[j.moduleId]),w[d]=m)}var M,A=[];for(t=0;t0;)if(d=q.pop(),i=H[d]){var T={},R=i.hot._disposeHandlers;for(o=0;o=0&&J.parents.splice(M,1))}}for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(i=H[d]))for(U=h[d],o=0;o=0&&i.children.splice(M,1);for(d in f("apply"),c=y,w)Object.prototype.hasOwnProperty.call(w,d)&&(e[d]=w[d]);var N=null;for(d in h)if(Object.prototype.hasOwnProperty.call(h,d)&&(i=H[d])){U=h[d];var L=[];for(t=0;t=0&&r._disposeHandlers.splice(n,1)},check:j,apply:P,status:function(e){if(!e)return u;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var r=s.indexOf(e);r>=0&&s.splice(r,1)},data:a[e]};return o=void 0,r}(r),parents:(p=l,l=[],p),children:[]};return e[r].call(n.exports,n,n.exports,function(e){var r=H[e];if(!r)return k;var n=function(n){return r.hot.active?(H[n]?-1===H[n].parents.indexOf(e)&&H[n].parents.push(e):(l=[e],o=n),-1===r.children.indexOf(n)&&r.children.push(n)):(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),l=[]),k(n)},t=function(e){return{configurable:!0,enumerable:!0,get:function(){return k[e]},set:function(r){k[e]=r}}};for(var i in k)Object.prototype.hasOwnProperty.call(k,i)&&"e"!==i&&Object.defineProperty(n,i,t(i));return n.e=function(e){return"ready"===u&&f("prepare"),w++,k.e(e).then(r,function(e){throw r(),e});function r(){w--,"prepare"===u&&(m[e]||D(e),0===w&&0===b&&E())}},n}(r)),n.l=!0,n.exports}k.m=e,k.c=H,k.d=function(e,r,n){k.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},k.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},k.t=function(e,r){if(1&r&&(e=k(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(k.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var t in e)k.d(n,t,function(r){return e[r]}.bind(null,t));return n},k.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return k.d(r,"a",r),r},k.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},k.p="",k.h=function(){return c};var M=window.webpackJsonp=window.webpackJsonp||[],A=M.push.bind(M);M.push=r,M=M.slice();for(var S=0;S0&&t-1 in e)}C.fn=C.prototype={jquery:"3.3.1",constructor:C,length:0,toArray:function(){return u.call(this)},get:function(e){return null==e?u.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=C.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return C.each(this,e)},map:function(e){return this.pushStack(C.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(u.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},oe=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function ie(e,t,r,o){var i,s,l,c,f,h,y,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!o&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=Q.exec(e)))if(i=f[1]){if(9===T){if(!(l=t.getElementById(i)))return r;if(l.id===i)return r.push(l),r}else if(m&&(l=m.getElementById(i))&&x(t,l)&&l.id===i)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((i=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(i)),r}if(n.qsa&&!S[e+" "]&&(!v||!v.test(e))){if(1!==T)m=t,y=e;else if("object"!==t.nodeName.toLowerCase()){for((c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;s--;)h[s]="#"+c+" "+ye(h[s]);y=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(y)try{return L.apply(r,m.querySelectorAll(y)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,o)}function ae(){var e=[];return function t(n,o){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=o}}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){for(var n=e.split("|"),o=n.length;o--;)r.attrHandle[n[o]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&oe(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function ge(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=ie.support={},i=ie.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=ie.setDocument=function(e){var t,o,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(h=(d=a).documentElement,g=!i(d),w!==d&&(o=d.defaultView)&&o.top!==o&&(o.addEventListener?o.addEventListener("unload",re,!1):o.attachEvent&&o.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=J.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,r,o,i=t.getElementById(e);if(i){if((n=i.getAttributeNode("id"))&&n.value===e)return[i];for(o=t.getElementsByName(e),r=0;i=o[r++];)if((n=i.getAttributeNode("id"))&&n.value===e)return[i]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=J.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(n.matchesSelector=J.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=J.test(h.compareDocumentPosition),x=t||J.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],s=[t];if(!o||!i)return e===d?-1:t===d?1:o?-1:i?1:c?O(c,e)-O(c,t):0;if(o===i)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},ie.matches=function(e,t){return ie(e,null,null,t)},ie.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!y||!y.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return ie(t,d,null,[e]).length>0},ie.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},ie.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var o=r.attrHandle[t.toLowerCase()],i=o&&N.call(r.attrHandle,t.toLowerCase())?o(e,t,!g):void 0;return void 0!==i?i:n.attributes||!g?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null},ie.escape=function(e){return(e+"").replace(te,ne)},ie.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ie.uniqueSort=function(e){var t,r=[],o=0,i=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){for(;t=e[i++];)t===e[i]&&(o=r.push(i));for(;o--;)e.splice(r[o],1)}return c=null,e},o=ie.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=o(t);return n},(r=ie.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ie.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ie.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var o=ie.attr(r,e);return null==o?"!="===t:!t||(o+="","="===t?o===n:"!="===t?o!==n:"^="===t?n&&0===o.indexOf(n):"*="===t?n&&o.indexOf(n)>-1:"$="===t?n&&o.slice(-n.length)===n:"~="===t?(" "+o.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(o===n||o.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=i!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(i){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(x=(d=(l=(c=(f=(p=v)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p!==t)););return(x-=o)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,o=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ie.error("unsupported pseudo: "+e);return o[b]?o(t):o.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){for(var r,i=o(e,t),a=i.length;a--;)e[r=O(e,i[a])]=!(n[r]=i[a])}):function(e){return o(e,0,n)}):o}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,o){for(var i,a=r(e,null,o,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return ie(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||ie.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function be(e,t,n,r,o){for(var i,a=[],s=0,u=e.length,l=null!=t;s-1&&(i[l]=!(a[l]=f))}}else y=be(y===a?y.splice(h,y.length):y),o?o(null,a,y,u):L.apply(a,y)})}function Te(e){for(var t,n,o,i=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var o=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,o}];u1&&xe(p),u>1&&ye(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,o=e.length>0,i=function(i,a,s,u,c){var f,h,v,y=0,m="0",x=i&&[],b=[],w=l,C=i||o&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(o&&f){for(h=0,a||f.ownerDocument===d||(p(f),s=!g);v=e[h++];)if(v(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!v&&f)&&y--,i&&x.push(f))}if(y+=m,n&&m!==y){for(h=0;v=t[h++];)v(x,b,a,s);if(i){if(y>0)for(;m--;)x[m]||b[m]||(b[m]=j.call(u));b=be(b)}L.apply(u,b),c&&!i&&b.length>0&&y+t.length>1&&ie.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(i):i}(i,o))).selector=e}return s},u=ie.select=function(e,t,n,o){var i,u,l,c,f,p="function"==typeof e&&e,d=!o&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(i=V.needsContext.test(e)?0:u.length;i--&&(l=u[i],!r.relative[c=l.type]);)if((f=r.find[c])&&(o=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(i,1),!(e=o.length&&ye(u)))return L.apply(n,o),n;break}}return(p||s(e,d))(o,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),ie}(n);C.find=S,C.expr=S.selectors,C.expr[":"]=C.expr.pseudos,C.uniqueSort=C.unique=S.uniqueSort,C.text=S.getText,C.isXMLDoc=S.isXML,C.contains=S.contains,C.escapeSelector=S.escape;var D=function(e,t,n){for(var r=[],o=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(o&&C(e).is(n))break;r.push(e)}return r},N=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},A=C.expr.match.needsContext;function j(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var q=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,t,n){return m(t)?C.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?C.grep(e,function(e){return e===t!==n}):"string"!=typeof t?C.grep(e,function(e){return f.call(t,e)>-1!==n}):C.filter(t,e,n)}C.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?C.find.matchesSelector(r,e)?[r]:[]:C.find.matches(e,C.grep(t,function(e){return 1===e.nodeType}))},C.fn.extend({find:function(e){var t,n,r=this.length,o=this;if("string"!=typeof e)return this.pushStack(C(e).filter(function(){for(t=0;t1?C.uniqueSort(n):n},filter:function(e){return this.pushStack(L(this,e||[],!1))},not:function(e){return this.pushStack(L(this,e||[],!0))},is:function(e){return!!L(this,"string"==typeof e&&A.test(e)?C(e):e||[],!1).length}});var H,O=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(C.fn.init=function(e,t,n){var r,o;if(!e)return this;if(n=n||H,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:O.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof C?t[0]:t,C.merge(this,C.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:a,!0)),q.test(r[1])&&C.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(o=a.getElementById(r[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(C):C.makeArray(e,this)}).prototype=C.fn,H=C(a);var P=/^(?:parents|prev(?:Until|All))/,M={children:!0,contents:!0,next:!0,prev:!0};function R(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}C.fn.extend({has:function(e){var t=C(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&C.find.matchesSelector(n,e))){i.push(n);break}return this.pushStack(i.length>1?C.uniqueSort(i):i)},index:function(e){return e?"string"==typeof e?f.call(C(e),this[0]):f.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(C.uniqueSort(C.merge(this.get(),C(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),C.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return D(e,"parentNode")},parentsUntil:function(e,t,n){return D(e,"parentNode",n)},next:function(e){return R(e,"nextSibling")},prev:function(e){return R(e,"previousSibling")},nextAll:function(e){return D(e,"nextSibling")},prevAll:function(e){return D(e,"previousSibling")},nextUntil:function(e,t,n){return D(e,"nextSibling",n)},prevUntil:function(e,t,n){return D(e,"previousSibling",n)},siblings:function(e){return N((e.parentNode||{}).firstChild,e)},children:function(e){return N(e.firstChild)},contents:function(e){return j(e,"iframe")?e.contentDocument:(j(e,"template")&&(e=e.content||e),C.merge([],e.childNodes))}},function(e,t){C.fn[e]=function(n,r){var o=C.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(o=C.filter(r,o)),this.length>1&&(M[e]||C.uniqueSort(o),P.test(e)&&o.reverse()),this.pushStack(o)}});var I=/[^\x20\t\r\n\f]+/g;function W(e){return e}function $(e){throw e}function B(e,t,n,r){var o;try{e&&m(o=e.promise)?o.call(e).done(t).fail(n):e&&m(o=e.then)?o.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}C.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return C.each(e.match(I)||[],function(e,n){t[n]=!0}),t}(e):C.extend({},e);var t,n,r,o,i=[],a=[],s=-1,u=function(){for(o=o||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)i.splice(n,1),n<=s&&s--}),this},has:function(e){return e?C.inArray(e,i)>-1:i.length>0},empty:function(){return i&&(i=[]),this},disable:function(){return o=a=[],i=n="",this},disabled:function(){return!i},lock:function(){return o=a=[],n||t||(i=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},C.extend({Deferred:function(e){var t=[["notify","progress",C.Callbacks("memory"),C.Callbacks("memory"),2],["resolve","done",C.Callbacks("once memory"),C.Callbacks("once memory"),0,"resolved"],["reject","fail",C.Callbacks("once memory"),C.Callbacks("once memory"),1,"rejected"]],r="pending",o={state:function(){return r},always:function(){return i.done(arguments).fail(arguments),this},catch:function(e){return o.then(null,e)},pipe:function(){var e=arguments;return C.Deferred(function(n){C.each(t,function(t,r){var o=m(e[r[4]])&&e[r[4]];i[r[1]](function(){var e=o&&o.apply(this,arguments);e&&m(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,o?[e]:arguments)})}),e=null}).promise()},then:function(e,r,o){var i=0;function a(e,t,r,o){return function(){var s=this,u=arguments,l=function(){var n,l;if(!(e=i&&(r!==$&&(s=void 0,u=[n]),t.rejectWith(s,u))}};e?c():(C.Deferred.getStackHook&&(c.stackTrace=C.Deferred.getStackHook()),n.setTimeout(c))}}return C.Deferred(function(n){t[0][3].add(a(0,n,m(o)?o:W,n.notifyWith)),t[1][3].add(a(0,n,m(e)?e:W)),t[2][3].add(a(0,n,m(r)?r:$))}).promise()},promise:function(e){return null!=e?C.extend(e,o):o}},i={};return C.each(t,function(e,n){var a=n[2],s=n[5];o[n[1]]=a.add,s&&a.add(function(){r=s},t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(n[3].fire),i[n[0]]=function(){return i[n[0]+"With"](this===i?void 0:this,arguments),this},i[n[0]+"With"]=a.fireWith}),o.promise(i),e&&e.call(i,i),i},when:function(e){var t=arguments.length,n=t,r=Array(n),o=u.call(arguments),i=C.Deferred(),a=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?u.call(arguments):n,--t||i.resolveWith(r,o)}};if(t<=1&&(B(e,i.done(a(n)).resolve,i.reject,!t),"pending"===i.state()||m(o[n]&&o[n].then)))return i.then();for(;n--;)B(o[n],a(n),i.reject);return i.promise()}});var F=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;C.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&F.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},C.readyException=function(e){n.setTimeout(function(){throw e})};var _=C.Deferred();function z(){a.removeEventListener("DOMContentLoaded",z),n.removeEventListener("load",z),C.ready()}C.fn.ready=function(e){return _.then(e).catch(function(e){C.readyException(e)}),this},C.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--C.readyWait:C.isReady)||(C.isReady=!0,!0!==e&&--C.readyWait>0||_.resolveWith(a,[C]))}}),C.ready.then=_.then,"complete"===a.readyState||"loading"!==a.readyState&&!a.documentElement.doScroll?n.setTimeout(C.ready):(a.addEventListener("DOMContentLoaded",z),n.addEventListener("load",z));var X=function(e,t,n,r,o,i,a){var s=0,u=e.length,l=null==n;if("object"===T(n))for(s in o=!0,n)X(e,t,s,n[s],!0,i,a);else if(void 0!==r&&(o=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(C(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){Z.remove(this,e)})}}),C.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,C.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=C.queue(e,t),r=n.length,o=n.shift(),i=C._queueHooks(e,t);"inprogress"===o&&(o=n.shift(),r--),o&&("fx"===t&&n.unshift("inprogress"),delete i.stop,o.call(e,function(){C.dequeue(e,t)},i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:C.Callbacks("once memory").add(function(){K.remove(e,[t+"queue",n])})})}}),C.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&j(e,t)?C.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n-1)o&&o.push(i);else if(l=C.contains(i.ownerDocument,i),a=ve(f.appendChild(i),"script"),l&&ye(a),n)for(c=0;i=a[c++];)he.test(i.type||"")&&n.push(i);return f}me=a.createDocumentFragment().appendChild(a.createElement("div")),(xe=a.createElement("input")).setAttribute("type","radio"),xe.setAttribute("checked","checked"),xe.setAttribute("name","t"),me.appendChild(xe),y.checkClone=me.cloneNode(!0).cloneNode(!0).lastChild.checked,me.innerHTML="",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=a.documentElement,Ce=/^key/,Ee=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ke=/^([^.]*)(?:\.(.+)|)/;function Se(){return!0}function De(){return!1}function Ne(){try{return a.activeElement}catch(e){}}function Ae(e,t,n,r,o,i){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],i);return e}if(null==r&&null==o?(o=n,r=n=void 0):null==o&&("string"==typeof n?(o=r,r=void 0):(o=r,r=n,n=void 0)),!1===o)o=De;else if(!o)return e;return 1===i&&(a=o,(o=function(e){return C().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=C.guid++)),e.each(function(){C.event.add(this,t,o,r,n)})}C.event={global:{},add:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,g,v=K.get(e);if(v)for(n.handler&&(n=(i=n).handler,o=i.selector),o&&C.find.matchesSelector(Te,o),n.guid||(n.guid=C.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==C&&C.event.triggered!==t.type?C.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(I)||[""]).length;l--;)d=g=(s=ke.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=C.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=C.event.special[d]||{},c=C.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:o,needsContext:o&&C.expr.match.needsContext.test(o),namespace:h.join(".")},i),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),o?p.splice(p.delegateCount++,0,c):p.push(c),C.event.global[d]=!0)},remove:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,g,v=K.hasData(e)&&K.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(I)||[""]).length;l--;)if(d=g=(s=ke.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=C.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=i=p.length;i--;)c=p[i],!o&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(i,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||C.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)C.event.remove(e,d+t[l],n,r,!0);C.isEmptyObject(u)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,r,o,i,a,s=C.event.fix(e),u=new Array(arguments.length),l=(K.get(this,"events")||{})[s.type]||[],c=C.event.special[s.type]||{};for(u[0]=s,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(i=[],a={},n=0;n-1:C.find(o,this,null,[l]).length),a[o]&&i.push(r);i.length&&s.push({elem:l,handlers:i})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return j(e,"table")&&j(11!==t.nodeType?t:t.firstChild,"tr")&&C(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Me(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Re(e,t){var n,r,o,i,a,s,u,l;if(1===t.nodeType){if(K.hasData(e)&&(i=K.access(e),a=K.set(t,i),l=i.events))for(o in delete a.handle,a.events={},l)for(n=0,r=l[o].length;n1&&"string"==typeof h&&!y.checkClone&&Le.test(h))return e.each(function(o){var i=e.eq(o);g&&(t[0]=h.call(this,o,i.html())),Ie(i,t,n,r)});if(p&&(i=(o=we(t,e[0].ownerDocument,!1,e,r)).firstChild,1===o.childNodes.length&&(o=i),i||r)){for(s=(a=C.map(ve(o,"script"),Pe)).length;f")},clone:function(e,t,n){var r,o,i,a,s,u,l,c=e.cloneNode(!0),f=C.contains(e.ownerDocument,e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||C.isXMLDoc(e)))for(a=ve(c),r=0,o=(i=ve(e)).length;r0&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,o=C.event.special,i=0;void 0!==(n=e[i]);i++)if(J(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)o[r]?C.event.remove(n,r):C.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[Z.expando]&&(n[Z.expando]=void 0)}}}),C.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return X(this,function(e){return void 0===e?C.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Ie(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)})},prepend:function(){return Ie(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(C.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return C.clone(this,e,t)})},html:function(e){return X(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!qe.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=C.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-i-u-s-.5))),u}function et(e,t,n){var r=Be(e),o=_e(e,t,r),i="border-box"===C.css(e,"boxSizing",!1,r),a=i;if($e.test(o)){if(!n)return o;o="auto"}return a=a&&(y.boxSizingReliable()||o===e.style[t]),("auto"===o||!parseFloat(o)&&"inline"===C.css(e,"display",!1,r))&&(o=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(o=parseFloat(o)||0)+Ze(e,t,n||(i?"border":"content"),a,r,o)+"px"}function tt(e,t,n,r,o){return new tt.prototype.init(e,t,n,r,o)}C.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=_e(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,i,a,s=Y(t),u=Ue.test(t),l=e.style;if(u||(t=Qe(s)),a=C.cssHooks[t]||C.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(o=a.get(e,!1,r))?o:l[t];"string"===(i=typeof n)&&(o=oe.exec(n))&&o[1]&&(n=ue(e,t,o),i="number"),null!=n&&n==n&&("number"===i&&(n+=o&&o[3]||(C.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var o,i,a,s=Y(t);return Ue.test(t)||(t=Qe(s)),(a=C.cssHooks[t]||C.cssHooks[s])&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=_e(e,t,r)),"normal"===o&&t in Ge&&(o=Ge[t]),""===n||n?(i=parseFloat(o),!0===n||isFinite(i)?i||0:o):o}}),C.each(["height","width"],function(e,t){C.cssHooks[t]={get:function(e,n,r){if(n)return!Xe.test(C.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ve,function(){return et(e,t,r)})},set:function(e,n,r){var o,i=Be(e),a="border-box"===C.css(e,"boxSizing",!1,i),s=r&&Ze(e,t,r,a,i);return a&&y.scrollboxSize()===i.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(i[t])-Ze(e,t,"border",!1,i)-.5)),s&&(o=oe.exec(n))&&"px"!==(o[3]||"px")&&(e.style[t]=n,n=C.css(e,t)),Ke(0,n,s)}}}),C.cssHooks.marginLeft=ze(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(_e(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),C.each({margin:"",padding:"",border:"Width"},function(e,t){C.cssHooks[e+t]={expand:function(n){for(var r=0,o={},i="string"==typeof n?n.split(" "):[n];r<4;r++)o[e+ie[r]+t]=i[r]||i[r-2]||i[0];return o}},"margin"!==e&&(C.cssHooks[e+t].set=Ke)}),C.fn.extend({css:function(e,t){return X(this,function(e,t,n){var r,o,i={},a=0;if(Array.isArray(t)){for(r=Be(e),o=t.length;a1)}}),C.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||C.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(C.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=C.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=C.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){C.fx.step[e.prop]?C.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[C.cssProps[e.prop]]&&!C.cssHooks[e.prop]?e.elem[e.prop]=e.now:C.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},C.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},C.fx=tt.prototype.init,C.fx.step={};var nt,rt,ot=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function at(){rt&&(!1===a.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(at):n.setTimeout(at,C.fx.interval),C.fx.tick())}function st(){return n.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,o={height:e};for(t=t?1:0;r<4;r+=2-t)o["margin"+(n=ie[r])]=o["padding"+n]=e;return t&&(o.opacity=o.width=e),o}function lt(e,t,n){for(var r,o=(ct.tweeners[t]||[]).concat(ct.tweeners["*"]),i=0,a=o.length;i1)},removeAttr:function(e){return this.each(function(){C.removeAttr(this,e)})}}),C.extend({attr:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return void 0===e.getAttribute?C.prop(e,t,n):(1===i&&C.isXMLDoc(e)||(o=C.attrHooks[t.toLowerCase()]||(C.expr.match.bool.test(t)?ft:void 0)),void 0!==n?null===n?void C.removeAttr(e,t):o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:(e.setAttribute(t,n+""),n):o&&"get"in o&&null!==(r=o.get(e,t))?r:null==(r=C.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&j(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,o=t&&t.match(I);if(o&&1===e.nodeType)for(;n=o[r++];)e.removeAttribute(n)}}),ft={set:function(e,t,n){return!1===t?C.removeAttr(e,n):e.setAttribute(n,n),n}},C.each(C.expr.match.bool.source.match(/\w+/g),function(e,t){var n=pt[t]||C.find.attr;pt[t]=function(e,t,r){var o,i,a=t.toLowerCase();return r||(i=pt[a],pt[a]=o,o=null!=n(e,t,r)?a:null,pt[a]=i),o}});var dt=/^(?:input|select|textarea|button)$/i,ht=/^(?:a|area)$/i;function gt(e){return(e.match(I)||[]).join(" ")}function vt(e){return e.getAttribute&&e.getAttribute("class")||""}function yt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(I)||[]}C.fn.extend({prop:function(e,t){return X(this,C.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[C.propFix[e]||e]})}}),C.extend({prop:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return 1===i&&C.isXMLDoc(e)||(t=C.propFix[t]||t,o=C.propHooks[t]),void 0!==n?o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:e[t]=n:o&&"get"in o&&null!==(r=o.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=C.find.attr(e,"tabindex");return t?parseInt(t,10):dt.test(e.nodeName)||ht.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),y.optSelected||(C.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),C.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){C.propFix[this.toLowerCase()]=this}),C.fn.extend({addClass:function(e){var t,n,r,o,i,a,s,u=0;if(m(e))return this.each(function(t){C(this).addClass(e.call(this,t,vt(this)))});if((t=yt(e)).length)for(;n=this[u++];)if(o=vt(n),r=1===n.nodeType&&" "+gt(o)+" "){for(a=0;i=t[a++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");o!==(s=gt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,o,i,a,s,u=0;if(m(e))return this.each(function(t){C(this).removeClass(e.call(this,t,vt(this)))});if(!arguments.length)return this.attr("class","");if((t=yt(e)).length)for(;n=this[u++];)if(o=vt(n),r=1===n.nodeType&&" "+gt(o)+" "){for(a=0;i=t[a++];)for(;r.indexOf(" "+i+" ")>-1;)r=r.replace(" "+i+" "," ");o!==(s=gt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):m(e)?this.each(function(n){C(this).toggleClass(e.call(this,n,vt(this),t),t)}):this.each(function(){var t,o,i,a;if(r)for(o=0,i=C(this),a=yt(e);t=a[o++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||((t=vt(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+gt(vt(n))+" ").indexOf(t)>-1)return!0;return!1}});var mt=/\r/g;C.fn.extend({val:function(e){var t,n,r,o=this[0];return arguments.length?(r=m(e),this.each(function(n){var o;1===this.nodeType&&(null==(o=r?e.call(this,n,C(this).val()):e)?o="":"number"==typeof o?o+="":Array.isArray(o)&&(o=C.map(o,function(e){return null==e?"":e+""})),(t=C.valHooks[this.type]||C.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,o,"value")||(this.value=o))})):o?(t=C.valHooks[o.type]||C.valHooks[o.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(o,"value"))?n:"string"==typeof(n=o.value)?n.replace(mt,""):null==n?"":n:void 0}}),C.extend({valHooks:{option:{get:function(e){var t=C.find.attr(e,"value");return null!=t?t:gt(C.text(e))}},select:{get:function(e){var t,n,r,o=e.options,i=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?i+1:o.length;for(r=i<0?u:a?i:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),i}}}}),C.each(["radio","checkbox"],function(){C.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=C.inArray(C(e).val(),t)>-1}},y.checkOn||(C.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in n;var xt=/^(?:focusinfocus|focusoutblur)$/,bt=function(e){e.stopPropagation()};C.extend(C.event,{trigger:function(e,t,r,o){var i,s,u,l,c,f,p,d,g=[r||a],v=h.call(e,"type")?e.type:e,y=h.call(e,"namespace")?e.namespace.split("."):[];if(s=d=u=r=r||a,3!==r.nodeType&&8!==r.nodeType&&!xt.test(v+C.event.triggered)&&(v.indexOf(".")>-1&&(v=(y=v.split(".")).shift(),y.sort()),c=v.indexOf(":")<0&&"on"+v,(e=e[C.expando]?e:new C.Event(v,"object"==typeof e&&e)).isTrigger=o?2:3,e.namespace=y.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+y.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:C.makeArray(t,[e]),p=C.event.special[v]||{},o||!p.trigger||!1!==p.trigger.apply(r,t))){if(!o&&!p.noBubble&&!x(r)){for(l=p.delegateType||v,xt.test(l+v)||(s=s.parentNode);s;s=s.parentNode)g.push(s),u=s;u===(r.ownerDocument||a)&&g.push(u.defaultView||u.parentWindow||n)}for(i=0;(s=g[i++])&&!e.isPropagationStopped();)d=s,e.type=i>1?l:p.bindType||v,(f=(K.get(s,"events")||{})[e.type]&&K.get(s,"handle"))&&f.apply(s,t),(f=c&&s[c])&&f.apply&&J(s)&&(e.result=f.apply(s,t),!1===e.result&&e.preventDefault());return e.type=v,o||e.isDefaultPrevented()||p._default&&!1!==p._default.apply(g.pop(),t)||!J(r)||c&&m(r[v])&&!x(r)&&((u=r[c])&&(r[c]=null),C.event.triggered=v,e.isPropagationStopped()&&d.addEventListener(v,bt),r[v](),e.isPropagationStopped()&&d.removeEventListener(v,bt),C.event.triggered=void 0,u&&(r[c]=u)),e.result}},simulate:function(e,t,n){var r=C.extend(new C.Event,n,{type:e,isSimulated:!0});C.event.trigger(r,null,t)}}),C.fn.extend({trigger:function(e,t){return this.each(function(){C.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return C.event.trigger(e,t,n,!0)}}),y.focusin||C.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){C.event.simulate(t,e.target,C.event.fix(e))};C.event.special[t]={setup:function(){var r=this.ownerDocument||this,o=K.access(r,t);o||r.addEventListener(e,n,!0),K.access(r,t,(o||0)+1)},teardown:function(){var r=this.ownerDocument||this,o=K.access(r,t)-1;o?K.access(r,t,o):(r.removeEventListener(e,n,!0),K.remove(r,t))}}});var wt=n.location,Tt=Date.now(),Ct=/\?/;C.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||C.error("Invalid XML: "+e),t};var Et=/\[\]$/,kt=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,Dt=/^(?:input|select|textarea|keygen)/i;function Nt(e,t,n,r){var o;if(Array.isArray(t))C.each(t,function(t,o){n||Et.test(e)?r(e,o):Nt(e+"["+("object"==typeof o&&null!=o?t:"")+"]",o,n,r)});else if(n||"object"!==T(t))r(e,t);else for(o in t)Nt(e+"["+o+"]",t[o],n,r)}C.param=function(e,t){var n,r=[],o=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!C.isPlainObject(e))C.each(e,function(){o(this.name,this.value)});else for(n in e)Nt(n,e[n],t,o);return r.join("&")},C.fn.extend({serialize:function(){return C.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=C.prop(this,"elements");return e?C.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!C(this).is(":disabled")&&Dt.test(this.nodeName)&&!St.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=C(this).val();return null==n?null:Array.isArray(n)?C.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var At=/%20/g,jt=/#.*$/,qt=/([?&])_=[^&]*/,Lt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ht=/^(?:GET|HEAD)$/,Ot=/^\/\//,Pt={},Mt={},Rt="*/".concat("*"),It=a.createElement("a");function Wt(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,o=0,i=t.toLowerCase().match(I)||[];if(m(n))for(;r=i[o++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function $t(e,t,n,r){var o={},i=e===Mt;function a(s){var u;return o[s]=!0,C.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||i||o[l]?i?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!o["*"]&&a("*")}function Bt(e,t){var n,r,o=C.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((o[n]?e:r||(r={}))[n]=t[n]);return r&&C.extend(!0,e,r),e}It.href=wt.href,C.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:wt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(wt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Rt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":C.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Bt(Bt(e,C.ajaxSettings),t):Bt(C.ajaxSettings,e)},ajaxPrefilter:Wt(Pt),ajaxTransport:Wt(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,o,i,s,u,l,c,f,p,d,h=C.ajaxSetup({},t),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?C(g):C.event,y=C.Deferred(),m=C.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=Lt.exec(i);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?i:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return r&&r.abort(t),k(0,t),this}};if(y.promise(E),h.url=((e||h.url||wt.href)+"").replace(Ot,wt.protocol+"//"),h.type=t.method||t.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(I)||[""],null==h.crossDomain){l=a.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=It.protocol+"//"+It.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=C.param(h.data,h.traditional)),$t(Pt,h,t,E),c)return E;for(p in(f=C.event&&h.global)&&0==C.active++&&C.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Ht.test(h.type),o=h.url.replace(jt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(At,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(Ct.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(qt,"$1"),d=(Ct.test(o)?"&":"?")+"_="+Tt+++d),h.url=o+d),h.ifModified&&(C.lastModified[o]&&E.setRequestHeader("If-Modified-Since",C.lastModified[o]),C.etag[o]&&E.setRequestHeader("If-None-Match",C.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||t.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+Rt+"; q=0.01":""):h.accepts["*"]),h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(T="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),r=$t(Mt,h,t,E)){if(E.readyState=1,f&&v.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=n.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,r.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(e,t,a,s){var l,p,d,b,w,T=t;c||(c=!0,u&&n.clearTimeout(u),r=void 0,i=s||"",E.readyState=e>0?4:0,l=e>=200&&e<300||304===e,a&&(b=function(e,t,n){for(var r,o,i,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(o in s)if(s[o]&&s[o].test(r)){u.unshift(o);break}if(u[0]in n)i=u[0];else{for(o in n){if(!u[0]||e.converters[o+" "+u[0]]){i=o;break}a||(a=o)}i=i||a}if(i)return i!==u[0]&&u.unshift(i),n[i]}(h,E,a)),b=function(e,t,n,r){var o,i,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(i=c.shift();i;)if(e.responseFields[i]&&(n[e.responseFields[i]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=i,i=c.shift())if("*"===i)i=u;else if("*"!==u&&u!==i){if(!(a=l[u+" "+i]||l["* "+i]))for(o in l)if((s=o.split(" "))[1]===i&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[o]:!0!==l[o]&&(i=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+i}}}return{state:"success",data:t}}(h,b,E,l),l?(h.ifModified&&((w=E.getResponseHeader("Last-Modified"))&&(C.lastModified[o]=w),(w=E.getResponseHeader("etag"))&&(C.etag[o]=w)),204===e||"HEAD"===h.type?T="nocontent":304===e?T="notmodified":(T=b.state,p=b.data,l=!(d=b.error))):(d=T,!e&&T||(T="error",e<0&&(e=0))),E.status=e,E.statusText=(t||T)+"",l?y.resolveWith(g,[p,T,E]):y.rejectWith(g,[E,T,d]),E.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,T]),f&&(v.trigger("ajaxComplete",[E,h]),--C.active||C.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return C.get(e,t,n,"json")},getScript:function(e,t){return C.get(e,void 0,t,"script")}}),C.each(["get","post"],function(e,t){C[t]=function(e,n,r,o){return m(n)&&(o=o||r,r=n,n=void 0),C.ajax(C.extend({url:e,type:t,dataType:o,data:n,success:r},C.isPlainObject(e)&&e))}}),C._evalUrl=function(e){return C.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},C.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=C(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return m(e)?this.each(function(t){C(this).wrapInner(e.call(this,t))}):this.each(function(){var t=C(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=m(e);return this.each(function(n){C(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){C(this).replaceWith(this.childNodes)}),this}}),C.expr.pseudos.hidden=function(e){return!C.expr.pseudos.visible(e)},C.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},C.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Ft={0:200,1223:204},_t=C.ajaxSettings.xhr();y.cors=!!_t&&"withCredentials"in _t,y.ajax=_t=!!_t,C.ajaxTransport(function(e){var t,r;if(y.cors||_t&&!e.crossDomain)return{send:function(o,i){var a,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(a in e.xhrFields)s[a]=e.xhrFields[a];for(a in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||o["X-Requested-With"]||(o["X-Requested-With"]="XMLHttpRequest"),o)s.setRequestHeader(a,o[a]);t=function(e){return function(){t&&(t=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?i(0,"error"):i(s.status,s.statusText):i(Ft[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),r=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout(function(){t&&r()})},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}}),C.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),C.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return C.globalEval(e),e}}}),C.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),C.ajaxTransport("script",function(e){var t,n;if(e.crossDomain)return{send:function(r,o){t=C("