├── .gitignore
├── .npmignore
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.MD
├── gulpfile.js
├── img-preview.gif
├── package-lock.json
├── package.json
├── src
├── image-preview
│ ├── image-preview.component.css
│ ├── image-preview.component.html
│ └── image-preview.component.ts
├── index.ts
├── package-lock.json
├── package.json
├── tsconfig.es5.json
└── tsconfig.spec.json
├── tools
└── gulp
│ └── inline-resources.js
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node
2 | node_modules/*
3 | npm-debug.log
4 |
5 | # TypeScript
6 | src/*.js
7 | src/*.map
8 | src/*.d.ts
9 |
10 | # JetBrains
11 | .idea
12 | .project
13 | .settings
14 | .idea/*
15 | *.iml
16 |
17 | # VS Code
18 | .vscode/*
19 |
20 | # Windows
21 | Thumbs.db
22 | Desktop.ini
23 |
24 | # Mac
25 | .DS_Store
26 | **/.DS_Store
27 |
28 | # Ngc generated files
29 | **/*.ngfactory.ts
30 |
31 | # Build files
32 | dist/*
33 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Node
2 | node_modules/*
3 | npm-debug.log
4 | docs/*
5 | # DO NOT IGNORE TYPESCRIPT FILES FOR NPM
6 | # TypeScript
7 | # *.js
8 | # *.map
9 | # *.d.ts
10 |
11 | # JetBrains
12 | .idea
13 | .project
14 | .settings
15 | .idea/*
16 | *.iml
17 |
18 | # VS Code
19 | .vscode/*
20 |
21 | # Windows
22 | Thumbs.db
23 | Desktop.ini
24 |
25 | # Mac
26 | .DS_Store
27 | **/.DS_Store
28 |
29 | # Ngc generated files
30 | **/*.ngfactory.ts
31 |
32 | # Library files
33 | src/*
34 | build/*
35 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - '4.2.1'
5 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-angular2-library": {
3 | "promptValues": {
4 | "gitRepositoryUrl": "https://github.com/Pluritech/repo"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Pluritech Brasil
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # Angular Image Preview
2 |
3 |
4 |
5 |
6 | This library was creating to abstract the [LQIP – Low Quality Image Placeholders](http://www.guypo.com/introducing-lqip-low-quality-image-placeholders/) technique used by Medium and Instagram to Angular apps. The code was implemented inspired by this awesome [codepen](https://codepen.io/jmperez/pen/yYjPER) made by [@JMPerez](https://github.com/JMPerez).
7 |
8 | And we used [RandomUser API](https://randomuser.me) to make our tests in development environment.
9 |
10 | ## How it works? (short explanation)
11 |
12 | First, we load the image with low quality and it is really fast. So, the user who have a slow internet can see a preview of what is coming.
13 | Then, we load the same image, but, with a high quality.
14 |
15 | It is a short explanation of how this technique works, if you want to know more, just visit the links above!
16 |
17 | ## Installation
18 |
19 | To install this library, run:
20 |
21 | ```bash
22 | $ npm install @pluritech/ng-image-preview --save
23 | ```
24 |
25 | ## Using the ng-image-preview
26 |
27 | ```typescript
28 | import { BrowserModule } from '@angular/platform-browser';
29 | import { NgModule } from '@angular/core';
30 |
31 | import { AppComponent } from './app.component';
32 |
33 | // Import the library
34 | import { NgImagePreviewModule } from '@pluritech/ng-image-preview';
35 |
36 | @NgModule({
37 | declarations: [
38 | AppComponent
39 | ],
40 | imports: [
41 | BrowserModule,
42 |
43 | // Specify the library as an import
44 | NgImagePreviewModule
45 | ],
46 | providers: [],
47 | bootstrap: [AppComponent]
48 | })
49 | export class AppModule { }
50 | ```
51 |
52 | Once the library is imported, you can use image-preview component in your Angular application:
53 |
54 | ```xml
55 |
56 |
62 |
63 |
64 | ```
65 |
66 | ## Parameters
67 | | Parameter | Description |
68 | | ------------- |:-------------:|
69 | | lowImage | low image url |
70 | | hqImage | high quality image url |
71 | | paddingBottom | a padding-bottom value, to prevent the reflow, default is 66.6% |
72 | | fallbackSrc | a path to fallback image the will replace the hqImage if occur an error on load |
73 | | bgPlaceholder | a background-color value to fill the placeholder 'container' |
74 | | filterLowImage | a filter value to image small like 'blur(10px)'. Default is 'blur(20px)' |
75 | | alt | a value 'alt' to both, low image and high image |
76 | | title | a value 'title' to both, low image and high image |
77 |
78 |
79 | ## To do
80 | * Manage images in cache
81 |
82 | Feel free to make your pull request.
83 |
84 | ## Fix Issues
85 | * 1.1.1 - Now you can use the operator Elvis to load data async, without the needed of use the *ngIf
86 |
87 | ## Authors
88 |
89 | * Lucas Corrẽa: https://github.com/lucascco
90 | * Luis Moreno: https://github.com/luishmcmoreno
91 | * Mateus Durâes: https://github.com/mateusduraes
92 |
93 | ## License
94 |
95 | MIT © [Lucas Correa & Mateus Durães & Luis Moreno](mailto:lucasccorrea@gmail.com)
96 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var gulp = require('gulp'),
3 | path = require('path'),
4 | ngc = require('@angular/compiler-cli/src/main').main,
5 | rollup = require('gulp-rollup'),
6 | rename = require('gulp-rename'),
7 | del = require('del'),
8 | runSequence = require('run-sequence'),
9 | inlineResources = require('./tools/gulp/inline-resources');
10 |
11 | const rootFolder = path.join(__dirname);
12 | const srcFolder = path.join(rootFolder, 'src');
13 | const tmpFolder = path.join(rootFolder, '.tmp');
14 | const buildFolder = path.join(rootFolder, 'build');
15 | const distFolder = path.join(rootFolder, 'dist');
16 |
17 | /**
18 | * 1. Delete /dist folder
19 | */
20 | gulp.task('clean:dist', function () {
21 |
22 | // Delete contents but not dist folder to avoid broken npm links
23 | // when dist directory is removed while npm link references it.
24 | return deleteFolders([distFolder + '/**', '!' + distFolder]);
25 | });
26 |
27 | /**
28 | * 2. Clone the /src folder into /.tmp. If an npm link inside /src has been made,
29 | * then it's likely that a node_modules folder exists. Ignore this folder
30 | * when copying to /.tmp.
31 | */
32 | gulp.task('copy:source', function () {
33 | return gulp.src([`${srcFolder}/**/*`, `!${srcFolder}/node_modules`])
34 | .pipe(gulp.dest(tmpFolder));
35 | });
36 |
37 | /**
38 | * 3. Inline template (.html) and style (.css) files into the the component .ts files.
39 | * We do this on the /.tmp folder to avoid editing the original /src files
40 | */
41 | gulp.task('inline-resources', function () {
42 | return Promise.resolve()
43 | .then(() => inlineResources(tmpFolder));
44 | });
45 |
46 |
47 | /**
48 | * 4. Run the Angular compiler, ngc, on the /.tmp folder. This will output all
49 | * compiled modules to the /build folder.
50 | */
51 | gulp.task('ngc', function () {
52 | return ngc({
53 | project: `${tmpFolder}/tsconfig.es5.json`
54 | })
55 | .then((exitCode) => {
56 | if (exitCode === 1) {
57 | // This error is caught in the 'compile' task by the runSequence method callback
58 | // so that when ngc fails to compile, the whole compile process stops running
59 | throw new Error('ngc compilation failed');
60 | }
61 | });
62 | });
63 |
64 | /**
65 | * 5. Run rollup inside the /build folder to generate our Flat ES module and place the
66 | * generated file into the /dist folder
67 | */
68 | gulp.task('rollup:fesm', function () {
69 | return gulp.src(`${buildFolder}/**/*.js`)
70 | // transform the files here.
71 | .pipe(rollup({
72 |
73 | // Bundle's entry point
74 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
75 | entry: `${buildFolder}/index.js`,
76 |
77 | // Allow mixing of hypothetical and actual files. "Actual" files can be files
78 | // accessed by Rollup or produced by plugins further down the chain.
79 | // This prevents errors like: 'path/file' does not exist in the hypothetical file system
80 | // when subdirectories are used in the `src` directory.
81 | allowRealFiles: true,
82 |
83 | // A list of IDs of modules that should remain external to the bundle
84 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
85 | external: [
86 | '@angular/core',
87 | '@angular/common'
88 | ],
89 |
90 | // Format of generated bundle
91 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
92 | format: 'es'
93 | }))
94 | .pipe(gulp.dest(distFolder));
95 | });
96 |
97 | /**
98 | * 6. Run rollup inside the /build folder to generate our UMD module and place the
99 | * generated file into the /dist folder
100 | */
101 | gulp.task('rollup:umd', function () {
102 | return gulp.src(`${buildFolder}/**/*.js`)
103 | // transform the files here.
104 | .pipe(rollup({
105 |
106 | // Bundle's entry point
107 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
108 | entry: `${buildFolder}/index.js`,
109 |
110 | // Allow mixing of hypothetical and actual files. "Actual" files can be files
111 | // accessed by Rollup or produced by plugins further down the chain.
112 | // This prevents errors like: 'path/file' does not exist in the hypothetical file system
113 | // when subdirectories are used in the `src` directory.
114 | allowRealFiles: true,
115 |
116 | // A list of IDs of modules that should remain external to the bundle
117 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
118 | external: [
119 | '@angular/core',
120 | '@angular/common'
121 | ],
122 |
123 | // Format of generated bundle
124 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
125 | format: 'umd',
126 |
127 | // Export mode to use
128 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#exports
129 | exports: 'named',
130 |
131 | // The name to use for the module for UMD/IIFE bundles
132 | // (required for bundles with exports)
133 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#modulename
134 | moduleName: 'ng2-image-preview',
135 |
136 | // See https://github.com/rollup/rollup/wiki/JavaScript-API#globals
137 | globals: {
138 | typescript: 'ts'
139 | }
140 |
141 | }))
142 | .pipe(rename('ng2-image-preview.umd.js'))
143 | .pipe(gulp.dest(distFolder));
144 | });
145 |
146 | /**
147 | * 7. Copy all the files from /build to /dist, except .js files. We ignore all .js from /build
148 | * because with don't need individual modules anymore, just the Flat ES module generated
149 | * on step 5.
150 | */
151 | gulp.task('copy:build', function () {
152 | return gulp.src([`${buildFolder}/**/*`, `!${buildFolder}/**/*.js`])
153 | .pipe(gulp.dest(distFolder));
154 | });
155 |
156 | /**
157 | * 8. Copy package.json from /src to /dist
158 | */
159 | gulp.task('copy:manifest', function () {
160 | return gulp.src([`${srcFolder}/package.json`])
161 | .pipe(gulp.dest(distFolder));
162 | });
163 |
164 | /**
165 | * 9. Copy README.md from / to /dist
166 | */
167 | gulp.task('copy:readme', function () {
168 | return gulp.src([path.join(rootFolder, 'README.MD')])
169 | .pipe(gulp.dest(distFolder));
170 | });
171 |
172 | /**
173 | * 10. Delete /.tmp folder
174 | */
175 | gulp.task('clean:tmp', function () {
176 | return deleteFolders([tmpFolder]);
177 | });
178 |
179 | /**
180 | * 11. Delete /build folder
181 | */
182 | gulp.task('clean:build', function () {
183 | return deleteFolders([buildFolder]);
184 | });
185 |
186 | gulp.task('compile', function () {
187 | runSequence(
188 | 'clean:dist',
189 | 'copy:source',
190 | 'inline-resources',
191 | 'ngc',
192 | 'rollup:fesm',
193 | 'rollup:umd',
194 | 'copy:build',
195 | 'copy:manifest',
196 | 'copy:readme',
197 | 'clean:build',
198 | 'clean:tmp',
199 | function (err) {
200 | if (err) {
201 | console.log('ERROR:', err.message);
202 | deleteFolders([distFolder, tmpFolder, buildFolder]);
203 | } else {
204 | console.log('Compilation finished succesfully');
205 | }
206 | });
207 | });
208 |
209 | /**
210 | * Watch for any change in the /src folder and compile files
211 | */
212 | gulp.task('watch', function () {
213 | gulp.watch(`${srcFolder}/**/*`, ['compile']);
214 | });
215 |
216 | gulp.task('clean', ['clean:dist', 'clean:tmp', 'clean:build']);
217 |
218 | gulp.task('build', ['clean', 'compile']);
219 | gulp.task('build:watch', ['build', 'watch']);
220 | gulp.task('default', ['build:watch']);
221 |
222 | /**
223 | * Deletes the specified folder
224 | */
225 | function deleteFolders(folders) {
226 | return del(folders);
227 | }
228 |
--------------------------------------------------------------------------------
/img-preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pluritech/ng-image-preview/6e8c63ce254894d72b0243a7d043cefb65f12e21/img-preview.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pluritech/ng-image-preview",
3 | "version": "1.2.0",
4 | "scripts": {
5 | "build": "gulp build",
6 | "build:watch": "gulp",
7 | "docs": "npm run docs:build",
8 | "docs:build": "compodoc -p tsconfig.json -n ng-image-preview -d docs --hideGenerator",
9 | "docs:serve": "npm run docs:build -- -s",
10 | "docs:watch": "npm run docs:build -- -s -w",
11 | "lint": "tslint --type-check --project tsconfig.json src/**/*.ts",
12 | "test": "tsc && karma start"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/Pluritech/ng-image-preview"
17 | },
18 | "author": {
19 | "name": "Lucas Correa & Mateus Durães",
20 | "email": "lucasccorrea@gmail.com"
21 | },
22 | "keywords": [
23 | "angular"
24 | ],
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/Pluritech/ng-image-preview/issues"
28 | },
29 | "devDependencies": {
30 | "@angular/common": "^4.0.0",
31 | "@angular/compiler": "^4.0.0",
32 | "@angular/compiler-cli": "^4.0.0",
33 | "@angular/core": "^4.0.0",
34 | "@angular/platform-browser": "^4.0.0",
35 | "@angular/platform-browser-dynamic": "^4.0.0",
36 | "@compodoc/compodoc": "^1.0.0-beta.10",
37 | "@types/jasmine": "2.5.38",
38 | "@types/node": "~6.0.60",
39 | "codelyzer": "~2.0.0",
40 | "core-js": "^2.4.1",
41 | "del": "^2.2.2",
42 | "gulp": "^3.9.1",
43 | "gulp-rename": "^1.2.2",
44 | "gulp-rollup": "^2.11.0",
45 | "jasmine-core": "~2.5.2",
46 | "jasmine-spec-reporter": "~3.2.0",
47 | "karma": "~1.4.1",
48 | "karma-chrome-launcher": "~2.0.0",
49 | "karma-cli": "~1.0.1",
50 | "karma-coverage-istanbul-reporter": "^0.2.0",
51 | "karma-jasmine": "~1.1.0",
52 | "karma-jasmine-html-reporter": "^0.2.2",
53 | "node-sass": "^4.5.2",
54 | "node-sass-tilde-importer": "^1.0.0",
55 | "node-watch": "^0.5.2",
56 | "protractor": "~5.1.0",
57 | "rollup": "^0.41.6",
58 | "run-sequence": "^1.2.2",
59 | "rxjs": "^5.1.0",
60 | "ts-node": "~2.0.0",
61 | "tslint": "~4.5.0",
62 | "typescript": "~2.2.0",
63 | "zone.js": "^0.8.4"
64 | },
65 | "engines": {
66 | "node": ">=6.0.0"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/image-preview/image-preview.component.css:
--------------------------------------------------------------------------------
1 | .placeholder {
2 | background-color: #f6f6f6;
3 | background-size: cover;
4 | background-repeat: no-repeat;
5 | position: relative;
6 | overflow: hidden;
7 | }
8 | .placeholder .image-item {
9 | position: absolute;
10 | opacity: 0;
11 | top: 0;
12 | left: 0;
13 | width: 100%;
14 | transition: opacity 1s linear;
15 | }
16 | .placeholder img.image-small {
17 | filter: blur(20px);
18 | /* this is needed so Safari keeps sharp edges */
19 | transform: scale(1);
20 | }
21 | .placeholder img.loaded {
22 | opacity: 1;
23 | }
24 | .placeholder .padding-bottom {
25 | padding-bottom: 66.6%;
26 | }
27 |
--------------------------------------------------------------------------------
/src/image-preview/image-preview.component.html:
--------------------------------------------------------------------------------
1 |
2 |
![]()
3 |
4 |
![]()
5 |
6 |
--------------------------------------------------------------------------------
/src/image-preview/image-preview.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'image-preview',
5 | templateUrl: './image-preview.component.html',
6 | styleUrls: ['./image-preview.component.css']
7 | })
8 | export class ImagePreviewComponent implements OnChanges {
9 |
10 | @Input() hqImage;
11 | @Input() lowImage;
12 | @Input() bgPlaceholder;
13 | @Input() filterLowImage;
14 | @Input() paddingBottom;
15 | @Input() fallbackSrc;
16 | @Input() title = '';
17 | @Input() alt = '';
18 |
19 | @ViewChild('placeholder') placeholder: ElementRef;
20 | @ViewChild('qualityImage') qualityImage: ElementRef;
21 |
22 | private classLoaded = 'loaded';
23 |
24 | constructor() {}
25 |
26 | private loadMetaData(): void {
27 | if (!this.alt && this.title) {
28 | this.alt = this.title;
29 | }
30 | if (this.alt && !this.title) {
31 | this.title = this.alt;
32 | }
33 | }
34 |
35 | private loadSmallImage(): void {
36 | const imgSmall = this.getSmallImageElement();
37 | imgSmall.src = this.lowImage;
38 | imgSmall.onload = () => {
39 | imgSmall.classList.add('loaded');
40 | };
41 | }
42 |
43 | private loadHqImage(): void {
44 | const imgLarge = new Image();
45 | imgLarge.src = this.hqImage;
46 | imgLarge.classList.add('image-item');
47 |
48 | imgLarge.onload = () => {
49 | this.qualityImage.nativeElement.classList.add(this.classLoaded);
50 | };
51 |
52 | imgLarge.onerror = () => {
53 | if (this.fallbackSrc) {
54 | this.qualityImage.nativeElement['src'] = this.fallbackSrc;
55 | this.qualityImage.nativeElement.classList.add(this.classLoaded);
56 | }
57 | };
58 | }
59 |
60 |
61 | private getSmallImageElement(): HTMLImageElement {
62 | return this.placeholder.nativeElement.querySelector('.image-small');
63 | }
64 |
65 | private resetPreview() {
66 | this.getSmallImageElement().classList.remove(this.classLoaded);
67 | this.qualityImage.nativeElement.classList.remove(this.classLoaded);
68 | }
69 |
70 | ngOnChanges() {
71 | this.resetPreview();
72 | this.loadMetaData();
73 | this.loadSmallImage();
74 | this.loadHqImage();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ImagePreviewComponent } from './image-preview/image-preview.component';
4 |
5 | export * from './image-preview/image-preview.component';
6 |
7 |
8 | @NgModule({
9 | imports: [
10 | CommonModule
11 | ],
12 | declarations: [
13 | ImagePreviewComponent
14 | ],
15 | exports: [
16 | ImagePreviewComponent
17 | ]
18 | })
19 | export class NgImagePreviewModule {
20 | }
21 |
--------------------------------------------------------------------------------
/src/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pluritech/ng-image-preview",
3 | "version": "1.2.0",
4 | "lockfileVersion": 1
5 | }
6 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pluritech/ng-image-preview",
3 | "version": "1.2.0",
4 | "repository": {
5 | "type": "git",
6 | "url": "https://github.com/Pluritech/ng-image-preview"
7 | },
8 | "author": {
9 | "name": "Lucas Correa & Mateus Durães",
10 | "email": "lucasccorrea@gmail.com"
11 | },
12 | "keywords": [
13 | "angular"
14 | ],
15 | "license": "MIT",
16 | "bugs": {
17 | "url": "https://github.com/Pluritech/ng-image-preview/issues"
18 | },
19 | "main": "ng-image-preview.umd.js",
20 | "module": "ng-image-preview.js",
21 | "jsnext:main": "ng-image-preview.js",
22 | "typings": "ng-image-preview.d.ts",
23 | "peerDependencies": {
24 | "@angular/core": "^4.0.0",
25 | "rxjs": "^5.1.0",
26 | "zone.js": "^0.8.4"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/tsconfig.es5.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "module": "es2015",
5 | "target": "es5",
6 | "baseUrl": ".",
7 | "stripInternal": true,
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "moduleResolution": "node",
11 | "outDir": "../build",
12 | "rootDir": ".",
13 | "lib": [
14 | "es2015",
15 | "dom"
16 | ],
17 | "skipLibCheck": true,
18 | "types": []
19 | },
20 | "angularCompilerOptions": {
21 | "annotateForClosureCompiler": true,
22 | "strictMetadataEmit": true,
23 | "skipTemplateCodegen": true,
24 | "flatModuleOutFile": "ng-image-preview.js",
25 | "flatModuleId": "@pluritech/ng-image-preview"
26 | },
27 | "files": [
28 | "./index.ts"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.es5.json",
3 | "compilerOptions": {
4 | "emitDecoratorMetadata": true,
5 | "experimentalDecorators": true,
6 | "outDir": "../out-tsc/spec",
7 | "module": "commonjs",
8 | "target": "es6",
9 | "baseUrl": "",
10 | "types": [
11 | "jest",
12 | "node"
13 | ]
14 | },
15 | "files": [
16 | "**/*.spec.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/tools/gulp/inline-resources.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // https://github.com/filipesilva/angular-quickstart-lib/blob/master/inline-resources.js
3 | 'use strict';
4 |
5 | const fs = require('fs');
6 | const path = require('path');
7 | const glob = require('glob');
8 | const sass = require('node-sass');
9 | const tildeImporter = require('node-sass-tilde-importer');
10 |
11 | /**
12 | * Simple Promiseify function that takes a Node API and return a version that supports promises.
13 | * We use promises instead of synchronized functions to make the process less I/O bound and
14 | * faster. It also simplifies the code.
15 | */
16 | function promiseify(fn) {
17 | return function () {
18 | const args = [].slice.call(arguments, 0);
19 | return new Promise((resolve, reject) => {
20 | fn.apply(this, args.concat([function (err, value) {
21 | if (err) {
22 | reject(err);
23 | } else {
24 | resolve(value);
25 | }
26 | }]));
27 | });
28 | };
29 | }
30 |
31 | const readFile = promiseify(fs.readFile);
32 | const writeFile = promiseify(fs.writeFile);
33 |
34 | /**
35 | * Inline resources in a tsc/ngc compilation.
36 | * @param projectPath {string} Path to the project.
37 | */
38 | function inlineResources(projectPath) {
39 |
40 | // Match only TypeScript files in projectPath.
41 | const files = glob.sync('**/*.ts', {cwd: projectPath});
42 |
43 | // For each file, inline the templates and styles under it and write the new file.
44 | return Promise.all(files.map(filePath => {
45 | const fullFilePath = path.join(projectPath, filePath);
46 | return readFile(fullFilePath, 'utf-8')
47 | .then(content => inlineResourcesFromString(content, url => {
48 | // Resolve the template url.
49 | return path.join(path.dirname(fullFilePath), url);
50 | }))
51 | .then(content => writeFile(fullFilePath, content))
52 | .catch(err => {
53 | console.error('An error occured: ', err);
54 | });
55 | }));
56 | }
57 |
58 | /**
59 | * Inline resources from a string content.
60 | * @param content {string} The source file's content.
61 | * @param urlResolver {Function} A resolver that takes a URL and return a path.
62 | * @returns {string} The content with resources inlined.
63 | */
64 | function inlineResourcesFromString(content, urlResolver) {
65 | // Curry through the inlining functions.
66 | return [
67 | inlineTemplate,
68 | inlineStyle,
69 | removeModuleId
70 | ].reduce((content, fn) => fn(content, urlResolver), content);
71 | }
72 |
73 | /**
74 | * Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and
75 | * replace with `template: ...` (with the content of the file included).
76 | * @param content {string} The source file's content.
77 | * @param urlResolver {Function} A resolver that takes a URL and return a path.
78 | * @return {string} The content with all templates inlined.
79 | */
80 | function inlineTemplate(content, urlResolver) {
81 | return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function (m, templateUrl) {
82 | const templateFile = urlResolver(templateUrl);
83 | const templateContent = fs.readFileSync(templateFile, 'utf-8');
84 | const shortenedTemplate = templateContent
85 | .replace(/([\n\r]\s*)+/gm, ' ')
86 | .replace(/"/g, '\\"');
87 | return `template: "${shortenedTemplate}"`;
88 | });
89 | }
90 |
91 |
92 | /**
93 | * Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and
94 | * replace with `styles: [...]` (with the content of the file included).
95 | * @param urlResolver {Function} A resolver that takes a URL and return a path.
96 | * @param content {string} The source file's content.
97 | * @return {string} The content with all styles inlined.
98 | */
99 | function inlineStyle(content, urlResolver) {
100 | return content.replace(/styleUrls\s*:\s*(\[[\s\S]*?\])/gm, function (m, styleUrls) {
101 | const urls = eval(styleUrls);
102 | return 'styles: ['
103 | + urls.map(styleUrl => {
104 | const styleFile = urlResolver(styleUrl);
105 | const originContent = fs.readFileSync(styleFile, 'utf-8');
106 | const styleContent = styleFile.endsWith('.scss') ? buildSass(originContent, styleFile) : originContent;
107 | const shortenedStyle = styleContent
108 | .replace(/([\n\r]\s*)+/gm, ' ')
109 | .replace(/"/g, '\\"');
110 | return `"${shortenedStyle}"`;
111 | })
112 | .join(',\n')
113 | + ']';
114 | });
115 | }
116 |
117 | /**
118 | * build sass content to css
119 | * @param content {string} the css content
120 | * @param sourceFile {string} the scss file sourceFile
121 | * @return {string} the generated css, empty string if error occured
122 | */
123 | function buildSass(content, sourceFile) {
124 | try {
125 | const result = sass.renderSync({
126 | data: content,
127 | file: sourceFile,
128 | importer: tildeImporter
129 | });
130 | return result.css.toString()
131 | } catch (e) {
132 | console.error('\x1b[41m');
133 | console.error('at ' + sourceFile + ':' + e.line + ":" + e.column);
134 | console.error(e.formatted);
135 | console.error('\x1b[0m');
136 | return "";
137 | }
138 | }
139 |
140 | /**
141 | * Remove every mention of `moduleId: module.id`.
142 | * @param content {string} The source file's content.
143 | * @returns {string} The content with all moduleId: mentions removed.
144 | */
145 | function removeModuleId(content) {
146 | return content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, '');
147 | }
148 |
149 | module.exports = inlineResources;
150 | module.exports.inlineResourcesFromString = inlineResourcesFromString;
151 |
152 | // Run inlineResources if module is being called directly from the CLI with arguments.
153 | if (require.main === module && process.argv.length > 2) {
154 | console.log('Inlining resources from project:', process.argv[2]);
155 | return inlineResources(process.argv[2]);
156 | }
157 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src",
4 | "experimentalDecorators": true,
5 | "moduleResolution": "node",
6 | "rootDir": "./src",
7 | "lib": [
8 | "es2015",
9 | "dom"
10 | ],
11 | "skipLibCheck": true,
12 | "types": []
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "class-name": true,
7 | "comment-format": [
8 | true,
9 | "check-space"
10 | ],
11 | "curly": true,
12 | "eofline": true,
13 | "forin": true,
14 | "indent": [
15 | true,
16 | "spaces"
17 | ],
18 | "label-position": true,
19 | "max-line-length": [
20 | true,
21 | 140
22 | ],
23 | "member-access": false,
24 | "member-ordering": [
25 | true,
26 | "static-before-instance",
27 | "variables-before-functions"
28 | ],
29 | "no-arg": true,
30 | "no-bitwise": true,
31 | "no-console": [
32 | true,
33 | "debug",
34 | "info",
35 | "time",
36 | "timeEnd",
37 | "trace"
38 | ],
39 | "no-construct": true,
40 | "no-debugger": true,
41 | "no-duplicate-variable": true,
42 | "no-empty": false,
43 | "no-eval": true,
44 | "no-inferrable-types": true,
45 | "no-shadowed-variable": true,
46 | "no-string-literal": false,
47 | "no-switch-case-fall-through": true,
48 | "no-trailing-whitespace": true,
49 | "no-unused-expression": true,
50 | "no-unused-variable": true,
51 | "no-use-before-declare": true,
52 | "no-var-keyword": true,
53 | "object-literal-sort-keys": false,
54 | "one-line": [
55 | true,
56 | "check-open-brace",
57 | "check-catch",
58 | "check-else",
59 | "check-whitespace"
60 | ],
61 | "quotemark": [
62 | true,
63 | "single"
64 | ],
65 | "radix": true,
66 | "semicolon": [
67 | "always"
68 | ],
69 | "triple-equals": [
70 | true,
71 | "allow-null-check"
72 | ],
73 | "typedef-whitespace": [
74 | true,
75 | {
76 | "call-signature": "nospace",
77 | "index-signature": "nospace",
78 | "parameter": "nospace",
79 | "property-declaration": "nospace",
80 | "variable-declaration": "nospace"
81 | }
82 | ],
83 | "variable-name": false,
84 | "whitespace": [
85 | true,
86 | "check-branch",
87 | "check-decl",
88 | "check-operator",
89 | "check-separator",
90 | "check-type"
91 | ],
92 | "directive-selector": [true, "attribute", "", "camelCase"],
93 | "component-selector": [true, "element", "", "kebab-case"],
94 | "use-input-property-decorator": true,
95 | "use-output-property-decorator": true,
96 | "use-host-property-decorator": true,
97 | "no-input-rename": true,
98 | "no-output-rename": true,
99 | "use-life-cycle-interface": true,
100 | "use-pipe-transform-interface": true,
101 | "component-class-suffix": true,
102 | "directive-class-suffix": true
103 | }
104 | }
105 |
--------------------------------------------------------------------------------