├── .gitattributes ├── .gitignore ├── .jshintrc ├── README.md ├── config.js ├── css └── main.css ├── gulpfile.js ├── index.html ├── karma.conf.js ├── package.json ├── src ├── app.js ├── components │ └── main │ │ ├── flickr-gallery-directive-spec.js │ │ ├── flickr-gallery-directive.js │ │ ├── flickr-gallery.html │ │ ├── flickr-service-spec.js │ │ ├── flickr-service.js │ │ ├── main-controller.js │ │ ├── main.js │ │ ├── shaker-directive.js │ │ └── title-header-styler-filter.js └── config │ ├── config.js │ ├── constants.js │ └── decorators.js └── tasks ├── bundle.js ├── cover.js └── watch.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | jspm_packages/ 3 | bundle 4 | dist 5 | 6 | 7 | *.sublime-project +bundle 8 | *.sublime-workspace -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": false, 5 | "curly": false, 6 | "eqeqeq": true, 7 | "eqnull": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "undef": true, 13 | "strict": false, 14 | "globalstrict": true, 15 | "trailing": true, 16 | "smarttabs": true, 17 | "globals": { 18 | "window": false, 19 | "document": false, 20 | "angular": false, 21 | "System": false, 22 | "jasmine": false, 23 | "inject": false, 24 | "describe": false, 25 | "beforeEach": false, 26 | "expect": false, 27 | "it": false 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular ES6 2 | 3 | This example shows how to use ES6 with [AngularJS](https://angularjs.org/). 4 | 5 | The tools used are: 6 | * [NodeJS](http://nodejs.org/) as a general dependency 7 | * [Gulp](http://gulpjs.com/) for automation of the ES6 to ES5 transpilation as well as BrowserSync 8 | * [BrowserSync](http://gulpjs.com/) automatically refreshes your browser on js/html/css changes 9 | * [jspm](http://jspm.io/) modern Package Manager supporting ES6 Module Syntax 10 | * [BabelJS](https://babeljs.io/) for ES6 to ES5 transpilation 11 | * [isparta](https://github.com/douglasduteil/isparta) for ES6 code coverage 12 | 13 | ## Development 14 | All AngularJS Application files are located in the folder src/ 15 | Make sure to start gulp watch (see below for howto) before doing changes in order to get 16 | the source files automatically transpiled to the dist/ folder 17 | 18 | ## How to start 19 | 20 | In order to start the application do the following: 21 | 22 | 1. Make sure that [NodeJS](http://nodejs.org/) is installed. 23 | 2. Make sure that [Gulp](http://gulpjs.com/) is installed: `npm install -g gulp` 24 | 3. Make sure that [jspm](http://jspm.io/) is installed: `npm install -g jspm` 25 | 4. Go to the project folder 26 | 5. Execute the following command to install all node-dependencies: `npm install` 27 | 6. Now install all client-side dependencies with [jspm](http://jspm.io/): `jspm install` 28 | 7. Start the application with the gulp watch task: `gulp watch` 29 | 8. Open up your favorite Browser and navigate to [http://localhost:9000](http://localhost:9000) to see the app. 30 | 31 | ## Using decorators 32 | 33 | There is a base decorator called `@register` which performs generic component registrations. In order to save work 34 | you may use one of the following concrete implementations, which allow you to omit the type information 35 | 36 | ### Constants 37 | 38 | ``` 39 | import {constant} from './path/to/config/decorators'; 40 | 41 | @constant 42 | export default class MyConstant { 43 | constructor () { 44 | return 'my-constant'; 45 | } 46 | } 47 | ``` 48 | 49 | ### Values 50 | 51 | ``` 52 | import {value} from './path/to/config/decorators'; 53 | 54 | @value 55 | export default class MyValue { 56 | constructor () { 57 | return 'my-value'; 58 | } 59 | } 60 | ``` 61 | 62 | ### Factories 63 | 64 | ``` 65 | import {factory} from './path/to/config/decorators'; 66 | 67 | @factory 68 | export default class MyFactory { 69 | constructor (/* dependancies */) { } 70 | } 71 | ``` 72 | 73 | ### Services 74 | 75 | ``` 76 | import {service} from './path/to/config/decorators'; 77 | 78 | @service 79 | export default class MyService { 80 | constructor (/* dependancies */) { } 81 | } 82 | ``` 83 | 84 | ### Providers 85 | 86 | ``` 87 | import {provider} from './path/to/config/decorators'; 88 | 89 | @constant 90 | export default class MyProvider { 91 | constructor (/* dependancies */) { } 92 | } 93 | ``` 94 | 95 | ### Controllers 96 | 97 | ``` 98 | import {controller} from './path/to/config/decorators'; 99 | 100 | @controller 101 | export default class MyController { 102 | constructor (/* dependancies */) { } 103 | } 104 | ``` 105 | 106 | ### Directives 107 | 108 | ``` 109 | import {directive} from './path/to/config/decorators'; 110 | import {baseURL} from './path/to/config/constants'; 111 | 112 | @directive({ 113 | restrict: 'E', 114 | templateUrl: `${baseURL}/path/to/the/template.html` 115 | }) 116 | export default class MyController { 117 | constructor (/* dependancies */) { 118 | this.foo = 'bar'; 119 | } 120 | } 121 | 122 | // In template.html : 123 | 124 |

{{ ctrl.foo }} will display "bar"

125 | ``` 126 | 127 | ### Filters 128 | 129 | ``` 130 | import {filter} from './path/to/config/decorators'; 131 | 132 | @filter 133 | export default class MyFilter { 134 | constructor (/* dependancies */) { } 135 | filter (input) { 136 | return input.toUpperCase(); 137 | } 138 | } 139 | ``` 140 | 141 | ### Injections 142 | 143 | In order to inject existing components/services into your new component you can leverage the following decorator as 144 | depicted in the example below. 145 | 146 | ``` 147 | import {inject} from './path/to/config/decorators'; 148 | 149 | @controller 150 | @inject('$http', 'MyService') 151 | export default class MyController { 152 | constructor ($http, MyService) { } 153 | } 154 | ``` 155 | 156 | ### Injection as a property 157 | 158 | Let's say you want to inject a component/service but use it with a different property name. In order to do so use the 159 | `injectAs` decorator 160 | 161 | ``` 162 | import {inject, injectAs} from './path/to/config/decorators'; 163 | 164 | @controller 165 | export default class MyController { 166 | @inject $http = null; 167 | @inject MyService = null; 168 | @injectAs('$q') Promise = null; 169 | doSomething () { 170 | return this.Promise((resolve, reject) { 171 | $http.get(this.MyService.path) 172 | .success(data => resolve(data) 173 | .error(err => reject(err)); 174 | }); 175 | } 176 | } 177 | ``` 178 | 179 | 180 | ## Running Unit Tests 181 | 182 | In order to run the unit tests do all mentioned steps from above and the additional ones: 183 | 184 | 1. Make sure that [Karma](http://karma-runner.github.io/) CLI is installed: 185 | ```shell 186 | npm install -g karma-cli 187 | ``` 188 | 2. Start the Karma Runner with: 189 | ```shell 190 | karma start 191 | ``` 192 | 193 | ## Running code coverage 194 | 195 | To create a full code-coverage report execute the following command: 196 | ```shell 197 | gulp cover 198 | ``` 199 | 200 | This will result in a new folder called `coverage` in your project. It contains an index.html, which you can open with 201 | your browser to get a nice code-coverage-report 202 | 203 | 204 | ## Credits 205 | Special thanks goes to [Hadrien Lanneau](https://github.com/hadrienl) for his great contribution to this project -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | "transpiler": "babel", 3 | "babelOptions": { 4 | "optional": [ 5 | "es7.decorators", 6 | "es7.classProperties", 7 | "runtime" 8 | ] 9 | }, 10 | "paths": { 11 | "*": "*.js", 12 | "github:*": "jspm_packages/github/*.js", 13 | "npm:*": "jspm_packages/npm/*.js", 14 | "angular-es6/*": "lib/*.js" 15 | }, 16 | "baseUrl": "dist" 17 | }); 18 | 19 | System.config({ 20 | "map": { 21 | "angular": "github:angular/bower-angular@1.4.0", 22 | "angular-mocks": "github:angular/bower-angular-mocks@1.4.0", 23 | "babel": "npm:babel-core@5.5.7", 24 | "babel-runtime": "npm:babel-runtime@5.5.7", 25 | "bootstrap": "github:twbs/bootstrap@3.3.4", 26 | "core-js": "npm:core-js@0.9.16", 27 | "jquery": "github:components/jquery@2.1.3", 28 | "traceur": "github:jmcriffey/bower-traceur@0.0.88", 29 | "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.88", 30 | "github:angular/bower-angular-mocks@1.4.0": { 31 | "angular": "github:angular/bower-angular@1.4.0" 32 | }, 33 | "github:jspm/nodelibs-process@0.1.1": { 34 | "process": "npm:process@0.10.1" 35 | }, 36 | "github:twbs/bootstrap@3.3.4": { 37 | "jquery": "github:components/jquery@2.1.3" 38 | }, 39 | "npm:babel-runtime@5.5.7": { 40 | "process": "github:jspm/nodelibs-process@0.1.1" 41 | }, 42 | "npm:core-js@0.9.16": { 43 | "fs": "github:jspm/nodelibs-fs@0.1.2", 44 | "process": "github:jspm/nodelibs-process@0.1.1", 45 | "systemjs-json": "github:systemjs/plugin-json@0.1.0" 46 | } 47 | } 48 | }); 49 | 50 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | h4 { 2 | font-size: 14px; 3 | height: 40px; 4 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | path = { 3 | source: 'src', 4 | scripts: 'src/**/*.js', 5 | html: ['index.html', 'src/**/*.html'], 6 | output:'dist/', 7 | css: 'css/*.css', 8 | bundle: 'bundle/', 9 | appmodule: 'src/app' 10 | }, 11 | bundleTask = require('./tasks/bundle')(path), 12 | watchTask = require('./tasks/watch')(path), 13 | coverTask = require('./tasks/cover')(path); 14 | 15 | gulp.task('default', ['watch']); 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Flickr Photo Album

12 |
13 | 14 | 15 | 16 | Number of hits: {{ctrl.service.numberOfHits()}} 17 |
18 | 19 |
20 | 21 | 22 | 23 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Dec 05 2014 16:49:29 GMT-0500 (EST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jspm', 'jasmine', 'es6-shim'], 14 | 15 | jspm: { 16 | // Edit this to your needs 17 | loadFiles: [ 18 | 'jspm_packages/github/angular/bower-angular@1.4.1/angular.js', 19 | 'src/**/*.js', 20 | 'src/**/*.html' 21 | ] 22 | }, 23 | 24 | 25 | // list of files / patterns to load in the browser 26 | files: [ 27 | ], 28 | 29 | 30 | // list of files to exclude 31 | exclude: [], 32 | 33 | 34 | // preprocess matching files before serving them to the browser 35 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 36 | preprocessors: { 37 | 'test/**/*.js': ['babel'], 38 | 'src/**/*.js': ['babel'], 39 | '**/*.html': ['ng-html2js'] 40 | }, 41 | 'babelPreprocessor': { 42 | options: { 43 | sourceMap: 'inline', 44 | modules: 'system', 45 | moduleIds: false, 46 | optional: [ 47 | "es7.decorators", 48 | "es7.classProperties" 49 | ] 50 | } 51 | }, 52 | 53 | ngHtml2JsPreprocessor: { 54 | stripPrefix: 'src', 55 | prependPrefix: 'dist', 56 | moduleName: 'mocked-templates' 57 | }, 58 | 59 | // test results reporter to use 60 | // possible values: 'dots', 'progress' 61 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 62 | reporters: ['progress'], 63 | 64 | 65 | // web server port 66 | port: 9876, 67 | 68 | 69 | // enable / disable colors in the output (reporters and logs) 70 | colors: true, 71 | 72 | 73 | // level of logging 74 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 75 | logLevel: config.LOG_INFO, 76 | 77 | 78 | // enable / disable watching file and executing tests whenever any file changes 79 | autoWatch: true, 80 | 81 | 82 | // start these browsers 83 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 84 | browsers: ['PhantomJS'], 85 | 86 | 87 | // Continuous Integration mode 88 | // if true, Karma captures browsers, runs the tests and exits 89 | singleRun: false 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-es6", 3 | "version": "0.2.3", 4 | "description": "AngularJS with JSPM and Babel for ES6", 5 | "keywords": [ 6 | "angularjs", 7 | "jspm", 8 | "6to5" 9 | ], 10 | "license": "MIT", 11 | "author": "Vildan Softic ", 12 | "main": "index.js", 13 | "devDependencies": { 14 | "browser-sync": "^1.8.1", 15 | "del": "^1.2.0", 16 | "es5-shim": "^4.1.6", 17 | "es6-shim": "^0.28.1", 18 | "gulp": "^3.8.10", 19 | "gulp-babel": "^5.1.0", 20 | "gulp-changed": "^1.1.0", 21 | "gulp-exec": "^2.1.1", 22 | "gulp-istanbul": "^0.10.0", 23 | "gulp-jshint": "^1.9.0", 24 | "gulp-plumber": "^0.6.6", 25 | "gulp-preprocess": "^1.2.0", 26 | "gulp-sourcemaps": "^1.5.2", 27 | "gulp-useref": "^1.2.0", 28 | "isparta": "^3.0.3", 29 | "jasmine-core": "^2.1.3", 30 | "jshint-stylish": "^1.0.0", 31 | "karma": "^0.12.28", 32 | "karma-babel-preprocessor": "^5.0.0", 33 | "karma-chrome-launcher": "^0.1.7", 34 | "karma-es6-shim": "^0.1.3", 35 | "karma-jasmine": "^0.3.2", 36 | "karma-jspm": "^1.0.1", 37 | "karma-ng-html2js-preprocessor": "^0.1.2", 38 | "karma-phantomjs-launcher": "^0.2.0", 39 | "object.assign": "^1.0.3", 40 | "run-sequence": "^1.0.2", 41 | "unzip": "*" 42 | }, 43 | "jspm": { 44 | "dependencies": { 45 | "angular": "github:angular/bower-angular@^1.4.1", 46 | "angular-mocks": "github:angular/angular-mocks@^1.4.1", 47 | "bootstrap": "github:twbs/bootstrap@^3.3.1" 48 | }, 49 | "devDependencies": { 50 | "babel": "npm:babel-core@^5.5.6", 51 | "babel-runtime": "npm:babel-runtime@^5.5.6", 52 | "core-js": "npm:core-js@^0.9.4" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | import configModule from './config/config'; 4 | import {appName} from './config/constants'; 5 | 6 | import MainComponent from './components/main/main'; 7 | 8 | var app = angular.module(appName, [ 9 | configModule.name, 10 | MainComponent.name 11 | ]); 12 | 13 | // INFO: Manual Application Bootstrapping 14 | angular.element(document).ready(function() { 15 | angular.bootstrap(document, [appName]); 16 | }); 17 | 18 | // INFO: Export app as so others may require it 19 | export {app}; 20 | -------------------------------------------------------------------------------- /src/components/main/flickr-gallery-directive-spec.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | /* globals mocks: false */ 3 | /* jshint ignore:start */ 4 | import * as mocks from 'angular-mocks'; 5 | /* jshint ignore:end */ 6 | import {appName, baseURL} from '../../config/constants'; 7 | 8 | describe('', () => { 9 | 10 | let element, $scope, ctrl; 11 | beforeEach(() => { 12 | angular.mock.module('mocked-templates'); 13 | angular.mock.module(`${appName}.main`); 14 | angular.mock.module('decorators'); 15 | }); 16 | 17 | beforeEach(inject(($compile, $rootScope) => { 18 | $scope = $rootScope.$new(); 19 | $scope.images = [{ 20 | title: 'foo' 21 | }, { 22 | title: 'bar' 23 | }]; 24 | element = angular.element(''); 25 | element = $compile(element)($scope); 26 | $scope.$digest(); 27 | ctrl = element.controller('flickrGallery'); 28 | })); 29 | 30 | it ('should have a $scope', () => { 31 | expect(ctrl.$scope).toBe(element.isolateScope()); 32 | }); 33 | 34 | it('should get data', () => { 35 | expect(ctrl.getData()).toBe($scope.images); 36 | }); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /src/components/main/flickr-gallery-directive.js: -------------------------------------------------------------------------------- 1 | import {directive, inject} from '../../config/decorators'; 2 | import {baseURL} from '../../config/constants'; 3 | 4 | /* jshint ignore:start */ 5 | @directive({ 6 | restrict: 'E', 7 | scope: { 8 | data: '=' 9 | }, 10 | templateUrl: `${baseURL}components/main/flickr-gallery.html` 11 | }) 12 | @inject('$scope') 13 | /* jshint ignore:end */ 14 | export default class FlickrGallery { 15 | constructor ($scope) { 16 | this.$scope = $scope; 17 | } 18 | getData () { 19 | return this.data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/main/flickr-gallery.html: -------------------------------------------------------------------------------- 1 |
3 |
11 |

{{ image.title | titleHeaderStyler }}

12 | 14 | 17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/components/main/flickr-service-spec.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import * as mocks from 'angular-mocks'; 3 | import {appName} from '../../config/constants'; 4 | 5 | describe('FlickrService', () => { 6 | 7 | var service, $httpBackend; 8 | beforeEach(() => { angular.mock.module(appName); }); 9 | 10 | beforeEach(mocks.default.inject((_$httpBackend_, _FlickrService_) => { 11 | service = _FlickrService_; 12 | $httpBackend = _$httpBackend_; 13 | 14 | $httpBackend.whenJSONP(/http:\/\/api.flickr.com/) 15 | .respond({ 16 | "title": "Fake response", 17 | "link": "http://fakelink.com", 18 | "description": "", 19 | "modified": "1970-01-01T01:01:01Z", 20 | "generator": "FakeGenerator", 21 | "items": [ 22 | { 23 | "title": "TEST", 24 | "link": "http://FakeImageLing", 25 | "media": {"m":"http://fakeImage.jpg"}, 26 | "date_taken": "1970-01-01T12:05:34-08:00", 27 | "description": "FakeDescription", 28 | "published": "1970-01-01T22:59:45Z", 29 | "author": "fake@unittest.com", 30 | "author_id": "F@K3", 31 | "tags": "fake tags for testing" 32 | }] 33 | }); 34 | })); 35 | 36 | 37 | it("should perform a request against Flickr", () => { 38 | $httpBackend.expectJSONP(/http:\/\/api.flickr.com/); 39 | service.loadImages('AngularJS'); 40 | $httpBackend.flush(); 41 | }); 42 | 43 | it("should contain result with items", function() { 44 | $httpBackend.expectJSONP(/http:\/\/api.flickr.com/); 45 | service.loadImages('AngularJS').then(() => { 46 | expect(service.numberOfHits()).toBe(1); 47 | }); 48 | $httpBackend.flush(); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /src/components/main/flickr-service.js: -------------------------------------------------------------------------------- 1 | import {service, inject} from '../../config/decorators'; 2 | 3 | /* jshint ignore:start */ 4 | @service 5 | /* jshint ignore:end */ 6 | export default class FlickrService { 7 | /* jshint ignore:start */ 8 | @inject $http = null; 9 | images = []; 10 | /* jshint ignore:end */ 11 | 12 | numberOfHits() { 13 | return this.images.length; 14 | } 15 | 16 | loadImages(searchTag) { 17 | let URL = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${searchTag}&tagmode=any&format=json&jsoncallback=JSON_CALLBACK`; 18 | 19 | return this.$http 20 | .jsonp(URL) 21 | .then(response => { 22 | this.images = response.data.items; 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/main/main-controller.js: -------------------------------------------------------------------------------- 1 | import {controller, inject, injectAs} from '../../config/decorators'; 2 | 3 | /* jshint ignore:start */ 4 | @controller 5 | @inject('$scope') 6 | /* jshint ignore:end */ 7 | export class MainCtrl { 8 | /* jshint ignore:start */ 9 | @injectAs('FlickrService') service = null; 10 | /* jshint ignore:end */ 11 | constructor($scope) { 12 | this.searchTag = 'AngularJS'; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/main/main.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import {appName} from '../../config/constants'; 3 | import MainController from './main-controller'; 4 | import FlickrGallery from './flickr-gallery-directive'; 5 | import FlickrService from './flickr-service'; 6 | import Shaker from './shaker-directive'; 7 | import TitleHeaderStyler from './title-header-styler-filter'; 8 | 9 | let MainComponent = angular.module(`${appName}.main`, []); 10 | 11 | export default MainComponent; 12 | -------------------------------------------------------------------------------- /src/components/main/shaker-directive.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import {directive, inject} from '../../config/decorators'; 3 | 4 | // Add jQuery shake animation 5 | /* jshint ignore:start */ 6 | (function($) { 7 | $.fn.shake = function(o) { 8 | if (typeof o === 'function') 9 | o = {callback: o}; 10 | // Set options 11 | var o = $.extend({ 12 | direction: "left", 13 | distance: 20, 14 | times: 3, 15 | speed: 140, 16 | easing: "swing" 17 | }, o); 18 | 19 | return this.each(function() { 20 | 21 | // Create element 22 | var el = $(this), props = { 23 | position: el.css("position"), 24 | top: el.css("top"), 25 | bottom: el.css("bottom"), 26 | left: el.css("left"), 27 | right: el.css("right") 28 | }; 29 | 30 | el.css("position", "relative"); 31 | 32 | // Adjust 33 | var ref = (o.direction == "up" || o.direction == "down") ? "top" : "left"; 34 | var motion = (o.direction == "up" || o.direction == "left") ? "pos" : "neg"; 35 | 36 | // Animation 37 | var animation = {}, animation1 = {}, animation2 = {}; 38 | animation[ref] = (motion == "pos" ? "-=" : "+=") + o.distance; 39 | animation1[ref] = (motion == "pos" ? "+=" : "-=") + o.distance * 2; 40 | animation2[ref] = (motion == "pos" ? "-=" : "+=") + o.distance * 2; 41 | 42 | // Animate 43 | el.animate(animation, o.speed, o.easing); 44 | for (var i = 1; i < o.times; i++) { // Shakes 45 | el.animate(animation1, o.speed, o.easing).animate(animation2, o.speed, o.easing); 46 | }; 47 | el.animate(animation1, o.speed, o.easing). 48 | animate(animation, o.speed / 2, o.easing, function(){ // Last shake 49 | el.css(props); // Restore 50 | if(o.callback) o.callback.apply(this, arguments); // Callback 51 | }); 52 | }); 53 | }; 54 | })(jQuery); 55 | /* jshint ignore:end */ 56 | 57 | /* jshint ignore:start */ 58 | @directive({ 59 | scope: { 60 | count: '=', 61 | duration: '=', 62 | distance: '=' 63 | }, 64 | restrict: 'A' 65 | }) 66 | @inject('$element') 67 | /* jshint ignore:end */ 68 | export class Shaker { 69 | constructor ($element) { 70 | $element.on('click', () => { 71 | this.shake($element, this.count, this.distance, this.duration, this.direction); 72 | }); 73 | } 74 | 75 | shake(element, shakes, distance, duration, direction) { 76 | $(element).shake({ 77 | direction: direction, 78 | distance: distance, 79 | times: shakes, 80 | speed: duration 81 | }); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/components/main/title-header-styler-filter.js: -------------------------------------------------------------------------------- 1 | import {filter, inject} from '../../config/decorators'; 2 | 3 | /* jshint ignore:start */ 4 | @filter 5 | @inject('$http') 6 | /* jshint ignore:end */ 7 | export class TitleHeaderStyler { 8 | constructor ($http) { 9 | this.$http = $http; 10 | } 11 | 12 | filter(input) { 13 | return input.toUpperCase(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import {appName} from './constants'; 3 | 4 | import DecoratorsComponent from './decorators'; 5 | 6 | let configComponent = angular.module(`${appName}.core`, [ 7 | DecoratorsComponent.name 8 | ]); 9 | 10 | export default configComponent; 11 | -------------------------------------------------------------------------------- /src/config/constants.js: -------------------------------------------------------------------------------- 1 | export const appName = 'app'; 2 | export const baseURL = 'dist/'; 3 | -------------------------------------------------------------------------------- /src/config/decorators.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | let decoratorsModule = angular.module('decorators', []); 4 | export default decoratorsModule; 5 | 6 | let $injector; 7 | 8 | decoratorsModule.run(_$injector_ => { 9 | $injector = _$injector_; 10 | }); 11 | 12 | /** 13 | * @example 14 | * import {inject} from './decorators'; 15 | * 16 | * @inject('$scope', '$http') 17 | * class MyController { 18 | * constructor($scope, $http) { 19 | * this.$scope = $scope; 20 | * this.$http = $http; 21 | * } 22 | * } 23 | * 24 | * class MyOtherController { 25 | * @inject $http = null; 26 | * @inject MyService = null; 27 | * doSomething () { 28 | * this.MyService.doServiceTask(); 29 | * } 30 | * } 31 | */ 32 | export function inject (...components) { 33 | if (typeof components[0] === 'object') { 34 | let key = components[1]; 35 | 36 | return { 37 | get: () => { 38 | try { 39 | return $injector.get(key); 40 | } catch (err) { 41 | throw new Error(`${key} cannot be injected as a property. Inject it in the controller.`); 42 | } 43 | } 44 | }; 45 | } else { 46 | return function decorate (target, key, property) { 47 | target.$inject = components; 48 | }; 49 | } 50 | } 51 | 52 | /** 53 | * @example 54 | * import {injectAs} from './decorators'; 55 | * 56 | * class MyController { 57 | * @injectAs('MyService') service = null; 58 | * constructor() { 59 | * this.service.doSomething(); 60 | * } 61 | * } 62 | */ 63 | export function injectAs (dep) { 64 | return function decorate (target, key, descriptor) { 65 | return { 66 | get: () => { 67 | try { 68 | return $injector.get(dep); 69 | } catch (err) { 70 | throw new Error(`${name} cannot be injected as a property. Inject it in the controller.`); 71 | } 72 | } 73 | }; 74 | }; 75 | } 76 | 77 | /** 78 | * @example 79 | * import {directive, inject} from './decorators'; 80 | * import {baseUrl} from './constants'; 81 | * 82 | * @directive({ 83 | * priority: 42, 84 | * templateUrl: `${baseUrl}/components/myComponent/myView.html`, 85 | * restrict: 'E', 86 | * require: '^parentDirective', 87 | * // etc 88 | * }) 89 | * @inject('$scope', '$element', '$attrs') 90 | * class MyView { 91 | * constructor($scope, $element, '$attrs') { 92 | * $element.on('click', e => console.log('click')); 93 | * } 94 | * 95 | * // If you want to use link function : 96 | * static link (scope, element, attrs) { 97 | * element.on('click', e => console.log('click')); 98 | * } 99 | * } 100 | */ 101 | export function directive (opts) { 102 | return function decorate (Target) { 103 | let name = opts.name || getTargetName(Target); 104 | name = name.substring(0,1).toLowerCase() + name.substring(1); 105 | function factory(...deps) { 106 | let inject = Target.$inject || []; 107 | let directiveDefinitionObject = { 108 | priority: opts.priority, 109 | template: opts.template, 110 | templateUrl: opts.templateUrl, 111 | transclude: opts.transclude, 112 | restrict: opts.restrict, 113 | templateNamespace: opts.templateNamespace, 114 | scope: opts.scope, 115 | controller: [...inject, function (...deps) { 116 | return new Target(...deps); 117 | }], 118 | controllerAs: opts.controllerAs || 'ctrl', 119 | bindToController: opts.bindToController || true, 120 | require: opts.require 121 | }; 122 | if (Target.compile) { 123 | directiveDefinitionObject.compile = function compile(...args) { 124 | return Target.compile(...args); 125 | }; 126 | } 127 | if (Target.link) { 128 | directiveDefinitionObject.link = function link(...args) { 129 | return Target.link(...args); 130 | }; 131 | } 132 | return directiveDefinitionObject; 133 | } 134 | decoratorsModule.directive(name, factory); 135 | }; 136 | } 137 | 138 | 139 | /** 140 | * @example 141 | * import {register} from './decorators'; 142 | * 143 | * @register({ 144 | * type: 'controller' 145 | * }) 146 | * export default class MyController {} 147 | */ 148 | export function register (opts) { 149 | return function decorate(target) { 150 | if(opts.inject) { 151 | target.$inject = opts.inject; 152 | } 153 | 154 | let name = opts.name || getTargetName(target); 155 | decoratorsModule[opts.type](name, target); 156 | }; 157 | } 158 | 159 | /** 160 | * @example 161 | * import {controller} from './decorators'; 162 | * 163 | * @controller 164 | * export default class MyController {} 165 | */ 166 | export function controller (target) { 167 | return register({ type: 'controller' })(target); 168 | } 169 | /** 170 | * @example 171 | * import {filter, inject} from './decorators'; 172 | * 173 | * @filter 174 | * @inject('$http') 175 | * export default class MyFilter { 176 | * constructor($http) { 177 | * return this. 178 | * } 179 | * filter (input) { 180 | * return input.toUpperCase(); 181 | * } 182 | * } 183 | */ 184 | export function filter (Target) { 185 | let name = getTargetName(Target); 186 | name = name.substring(0,1).toLowerCase() + name.substring(1); 187 | let deps = Target.$inject || []; 188 | decoratorsModule.filter(name, [...deps, function (...deps) { 189 | let instance = new Target(); 190 | return function (...args) { return instance.filter(...args); }; 191 | }]); 192 | } 193 | /** 194 | * @example 195 | * import {constant} from './decorators'; 196 | * 197 | * @controller 198 | * export default class MyConstant { 199 | * constructor(...deps) { 200 | * return () => {}; 201 | * } 202 | * } 203 | */ 204 | export function constant (Target) { 205 | let name = getTargetName(Target); 206 | name = name.substring(0,1).toLowerCase() + name.substring(1); 207 | return register({ type: 'constant', name: name })(new Target()); 208 | } 209 | /** 210 | * @example 211 | * import {value} from './decorators'; 212 | * 213 | * @controller 214 | * export default class MyValue { 215 | * constructor(...deps) { 216 | * return () => {}; 217 | * } 218 | * } 219 | */ 220 | export function value (Target) { 221 | return register({ type: 'value', name: getTargetName(Target) })(new Target()); 222 | } 223 | /** 224 | * @example 225 | * import {factory} from './decorators'; 226 | * 227 | * @controller 228 | * export default class MyFactory {} 229 | */ 230 | export function factory (target) { 231 | return register({ type: 'factory' })(target); 232 | } 233 | /** 234 | * @example 235 | * import {service} from './decorators'; 236 | * 237 | * @controller 238 | * export default class MyService {} 239 | */ 240 | export function service (target) { 241 | return register({ type: 'service' })(target); 242 | } 243 | /** 244 | * @example 245 | * import {provider} from './decorators'; 246 | * 247 | * @controller 248 | * export default class Myprovider {} 249 | */ 250 | export function provider (target) { 251 | return register({ type: 'provider' })(target); 252 | } 253 | 254 | 255 | /** 256 | * Polyfill for IE to return Target.name 257 | */ 258 | function getTargetName (o) { 259 | if (o.name) { 260 | return o.name; 261 | } 262 | // if IE 263 | return o.toString().match(/function\s?(.*)\s?\(/)[1]; 264 | } 265 | -------------------------------------------------------------------------------- /tasks/bundle.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | runSequence = require('run-sequence'), 3 | del = require('del'), 4 | exec = require('gulp-exec'), 5 | useref = require('gulp-useref'), 6 | preprocess = require('gulp-preprocess'), 7 | NODE_ENV = process.env.NODE_ENV || 'development', 8 | path; 9 | 10 | module.exports = function (_path_) { 11 | path = _path_; 12 | }; 13 | 14 | gulp.task('clean-bundle', function (done) { 15 | del('bundle', done); 16 | }); 17 | 18 | gulp.task('bundle-app', function () { 19 | console.log('jspm bundle-sfx ' + path.appmodule + ' ' + path.bundle + 'app.js'); 20 | return gulp.src('src/app.js') 21 | .pipe(exec('jspm bundle-sfx ' + path.appmodule + ' ' + path.bundle + 'app.js')) 22 | .pipe(exec.reporter()); 23 | }); 24 | 25 | gulp.task('bundle-systemjs', function () { 26 | return gulp.src(['config.js', 'jspm_packages/system.js*', 'jspm_packages/es6-module-loader.js*']) 27 | .pipe(gulp.dest(path.bundle)); 28 | }); 29 | 30 | gulp.task('bundle-css', function () { 31 | return gulp.src(path.css) 32 | .pipe(gulp.dest(path.bundle + 'css')); 33 | }); 34 | 35 | gulp.task('bundle-templates', function () { 36 | return gulp.src(path.source + '/**/*.html') 37 | .pipe(gulp.dest(path.bundle + 'dist')); 38 | }); 39 | 40 | gulp.task('bundle-index', function () { 41 | var assets = useref.assets(); 42 | 43 | return gulp.src(['index.html']) 44 | .pipe(preprocess({ 45 | context: { 46 | NODE_ENV: 'production' 47 | } 48 | })) 49 | .pipe(assets) 50 | .pipe(assets.restore()) 51 | .pipe(useref()) 52 | .pipe(gulp.dest(path.bundle)); 53 | }); 54 | 55 | gulp.task('bundle-statics', function () { 56 | runSequence(['css', 'build-html'], ['bundle-templates', 'bundle-systemjs', 'bundle-index']); 57 | }); 58 | 59 | gulp.task('bundle', function () { 60 | runSequence('clean-bundle', 'bundle-app', 'bundle-statics'); 61 | }); 62 | -------------------------------------------------------------------------------- /tasks/cover.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | , istanbul = require('gulp-istanbul') 3 | , isparta = require('isparta'); 4 | 5 | 6 | var path; 7 | 8 | module.exports = function (_path_) { 9 | path = _path_; 10 | }; 11 | 12 | gulp.task('cover', function(cb) { 13 | gulp.src([path.scripts, '!src/**/*-spec.js']) 14 | .pipe(istanbul({ 15 | instrumenter: isparta.Instrumenter, 16 | includeUntested: true, 17 | babel: { stage: 0 } 18 | })) 19 | .pipe(istanbul.hookRequire()) 20 | .pipe(istanbul.writeReports({ 21 | dir: './coverage', 22 | reportOpts: {dir: './coverage'}, 23 | reporters: ['html'] 24 | })) 25 | .on('finish', cb) 26 | }); -------------------------------------------------------------------------------- /tasks/watch.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | runSequence = require('run-sequence'), 3 | to5 = require('gulp-babel'), 4 | jshint = require('gulp-jshint'), 5 | stylish = require('jshint-stylish'), 6 | assign = Object.assign || require('object.assign'), 7 | fs = require('fs'), 8 | browserSync = require('browser-sync'), 9 | changed = require('gulp-changed'), 10 | plumber = require('gulp-plumber'), 11 | reload = browserSync.reload, 12 | sourcemaps = require('gulp-sourcemaps'), 13 | del = require('del'), 14 | preprocess = require('gulp-preprocess'), 15 | NODE_ENV = process.env.NODE_ENV || 'development'; 16 | 17 | var path; 18 | 19 | module.exports = function (_path_) { 20 | path = _path_; 21 | }; 22 | 23 | var compilerOptions = { 24 | modules: 'system', 25 | moduleIds: false, 26 | stage: 2, 27 | optional: [ 28 | 'es7.decorators', 29 | 'es7.classProperties' 30 | ] 31 | }; 32 | 33 | var jshintConfig = {esnext:true}; 34 | 35 | gulp.task('build-system', function () { 36 | return gulp.src(path.scripts) 37 | .pipe(plumber()) 38 | .pipe(changed(path.output, {extension: '.js'})) 39 | .pipe(sourcemaps.init()) 40 | .pipe(to5(assign({}, compilerOptions, {modules:'system'}))) 41 | .pipe(sourcemaps.write(".")) 42 | .pipe(gulp.dest(path.output)) 43 | .pipe(browserSync.reload({ stream: true })); 44 | }); 45 | 46 | gulp.task('build-html', function () { 47 | return gulp.src(path.html) 48 | .pipe(changed(path.output, {extension: '.html'})) 49 | .pipe(preprocess({ 50 | context: { 51 | NODE_ENV: NODE_ENV 52 | } 53 | })) 54 | .pipe(gulp.dest(path.output)) 55 | .pipe(browserSync.reload({ stream: true })); 56 | }); 57 | 58 | gulp.task('lint', function() { 59 | return gulp.src(path.scripts) 60 | .pipe(jshint(jshintConfig)) 61 | .pipe(jshint.reporter(stylish)); 62 | }); 63 | 64 | gulp.task('clean-dist', function (done) { 65 | del('dist', done); 66 | }); 67 | 68 | gulp.task('build', function(callback) { 69 | return runSequence( 70 | ['build-system', 'build-html', 'css'], 71 | callback 72 | ); 73 | }); 74 | 75 | gulp.task('serve', ['build'], function(done) { 76 | browserSync({ 77 | open: false, 78 | port: 9000, 79 | files: { 80 | src: path.css 81 | }, 82 | server: { 83 | baseDir: ['.'], 84 | middleware: function (req, res, next) { 85 | if (req.url.match(/^(.(?!\..+))*$/) || 86 | req.url === 'index.html') { 87 | req.url = '/dist/index.html'; 88 | } 89 | res.setHeader('Access-Control-Allow-Origin', '*'); 90 | next(); 91 | } 92 | } 93 | }, done); 94 | }); 95 | 96 | gulp.task('css', function () { 97 | return gulp.src(path.css) 98 | .pipe(reload({stream:true})); 99 | }); 100 | 101 | gulp.task('watch', ['serve'], function() { 102 | var watcher = gulp.watch([path.scripts, path.html], ['build']); 103 | watcher.on('change', function(event) { 104 | console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); 105 | }); 106 | gulp.watch(path.css, ['css']); 107 | }); 108 | --------------------------------------------------------------------------------