├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── preview-editable-table-antd.gif ├── src ├── .prettierrc ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── camp-euro.pipe.spec.ts │ ├── camp-euro.pipe.ts │ ├── editable-table │ │ ├── editable-table.component.html │ │ ├── editable-table.component.less │ │ ├── editable-table.component.spec.ts │ │ └── editable-table.component.ts │ ├── form-edit │ │ ├── form-edit.component.html │ │ ├── form-edit.component.less │ │ ├── form-edit.component.spec.ts │ │ └── form-edit.component.ts │ ├── freeclick-string.pipe.spec.ts │ ├── freeclick-string.pipe.ts │ ├── plista-firebase.service.spec.ts │ ├── plista-firebase.service.ts │ └── view-table │ │ ├── view-table.component.html │ │ └── view-table.component.ts ├── assets │ └── .gitkeep ├── browserslist ├── data │ ├── cleanTheData.ts │ └── plRawData.json ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json ├── tslint.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EditableTable-angular 2 | 3 | Editable table with [Angular 6](https://angular.io/) and [NG-ZORRO - Ant Design of Angular](https://ng.ant.design/docs/introduce/en). 4 | 5 | ## Table of Contents 6 | 7 | - [Preview](#preview) 8 | - [Installation](#installation) 9 | - [Support](#support) 10 | 11 | ## Preview 12 | 13 | URL: https://editabletable-angular.netlify.com/ 14 | ![](preview-editable-table-antd.gif) 15 | 16 | ## Installation 17 | 18 | Install [Node.js](https://nodejs.org/en/), [Git](https://git-scm.com/) and then: 19 | 20 | ```sh 21 | git clone https://github.com/antekai/ct-pl-editableTable-angular.git editableTable-angular 22 | cd editableTable-angular 23 | npm install 24 | ng serve -o 25 | ``` 26 | 27 | ## Features 28 | 29 | - Raw data preprocess and render to a table 30 | - Edit and save per record(row): number, checkbox, select, datePicker, timePicker, radio input for respective data type 31 | - UI-kit: [ant-design](https://ant.design/) 32 | - Netlify CD 33 | 34 | ## Support 35 | 36 | Please [open an issue](https://github.com/antekai/ct-pl-editableTable-react/issues/new) for support. 37 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "zorro-test": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/zorro-test", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "node_modules/ng-zorro-antd/src/ng-zorro-antd.min.css", 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true 48 | } 49 | } 50 | }, 51 | "serve": { 52 | "builder": "@angular-devkit/build-angular:dev-server", 53 | "options": { 54 | "browserTarget": "zorro-test:build" 55 | }, 56 | "configurations": { 57 | "production": { 58 | "browserTarget": "zorro-test:build:production" 59 | } 60 | } 61 | }, 62 | "extract-i18n": { 63 | "builder": "@angular-devkit/build-angular:extract-i18n", 64 | "options": { 65 | "browserTarget": "zorro-test:build" 66 | } 67 | }, 68 | "test": { 69 | "builder": "@angular-devkit/build-angular:karma", 70 | "options": { 71 | "main": "src/test.ts", 72 | "polyfills": "src/polyfills.ts", 73 | "tsConfig": "src/tsconfig.spec.json", 74 | "karmaConfig": "src/karma.conf.js", 75 | "styles": [ 76 | "node_modules/ng-zorro-antd/src/ng-zorro-antd.min.css", 77 | "src/styles.css" 78 | ], 79 | "scripts": [], 80 | "assets": [ 81 | "src/favicon.ico", 82 | "src/assets" 83 | ] 84 | } 85 | }, 86 | "lint": { 87 | "builder": "@angular-devkit/build-angular:tslint", 88 | "options": { 89 | "tsConfig": [ 90 | "src/tsconfig.app.json", 91 | "src/tsconfig.spec.json" 92 | ], 93 | "exclude": [ 94 | "**/node_modules/**" 95 | ] 96 | } 97 | } 98 | } 99 | }, 100 | "zorro-test-e2e": { 101 | "root": "e2e/", 102 | "projectType": "application", 103 | "architect": { 104 | "e2e": { 105 | "builder": "@angular-devkit/build-angular:protractor", 106 | "options": { 107 | "protractorConfig": "e2e/protractor.conf.js", 108 | "devServerTarget": "zorro-test:serve" 109 | }, 110 | "configurations": { 111 | "production": { 112 | "devServerTarget": "zorro-test:serve:production" 113 | } 114 | } 115 | }, 116 | "lint": { 117 | "builder": "@angular-devkit/build-angular:tslint", 118 | "options": { 119 | "tsConfig": "e2e/tsconfig.e2e.json", 120 | "exclude": [ 121 | "**/node_modules/**" 122 | ] 123 | } 124 | } 125 | } 126 | } 127 | }, 128 | "defaultProject": "zorro-test" 129 | } -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to zorro-test!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zorro-test", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.1.0", 15 | "@angular/common": "^6.1.0", 16 | "@angular/compiler": "^6.1.0", 17 | "@angular/core": "^6.1.0", 18 | "@angular/forms": "^6.1.0", 19 | "@angular/http": "^6.1.0", 20 | "@angular/platform-browser": "^6.1.0", 21 | "@angular/platform-browser-dynamic": "^6.1.0", 22 | "@angular/router": "^6.1.0", 23 | "core-js": "^2.5.4", 24 | "ng-zorro-antd": "^1.4.1", 25 | "rxjs": "^6.0.0", 26 | "zone.js": "~0.8.26" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "~0.7.0", 30 | "@angular/cli": "~6.1.5", 31 | "@angular/compiler-cli": "^6.1.0", 32 | "@angular/language-service": "^6.1.0", 33 | "@types/jasmine": "~2.8.6", 34 | "@types/jasminewd2": "~2.0.3", 35 | "@types/node": "~8.9.4", 36 | "codelyzer": "~4.2.1", 37 | "jasmine-core": "~2.99.1", 38 | "jasmine-spec-reporter": "~4.2.1", 39 | "karma": "~1.7.1", 40 | "karma-chrome-launcher": "~2.2.0", 41 | "karma-coverage-istanbul-reporter": "~2.0.0", 42 | "karma-jasmine": "~1.1.1", 43 | "karma-jasmine-html-reporter": "^0.2.2", 44 | "protractor": "~5.4.0", 45 | "ts-node": "~5.0.1", 46 | "tslint": "~5.9.1", 47 | "typescript": "~2.7.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /preview-editable-table-antd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekai/ct-pl-editableTable-angular/6afb5f54eb081bb9dca029c42cf911eea7a32211/preview-editable-table-antd.gif -------------------------------------------------------------------------------- /src/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { EditableTableComponent } from './editable-table/editable-table.component'; 4 | import { AppComponent } from './app.component'; 5 | import { ViewTableComponent } from './view-table/view-table.component'; 6 | import { FormEditComponent } from './form-edit/form-edit.component'; 7 | 8 | const appRoutes: Routes = [ 9 | { path: 'inline-edit', component: EditableTableComponent }, 10 | { 11 | path: 'view-table', 12 | component: ViewTableComponent, 13 | children: [{ path: 'edit/:key', component: FormEditComponent }] 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [ 19 | // RouterModule.forRoot(appRoutes, {useHash: true}) 20 | RouterModule.forRoot(appRoutes) 21 | ], 22 | exports: [RouterModule] 23 | }) 24 | export class AppRoutingModule {} 25 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekai/ct-pl-editableTable-angular/6afb5f54eb081bb9dca029c42cf911eea7a32211/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'zorro-test'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('zorro-test'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to zorro-test!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Response } from '@angular/http'; 3 | import { PlistaFirebaseService } from './plista-firebase.service'; 4 | import cleanTheData from '../data/cleanTheData'; 5 | import raw from '../data/plRawData.json'; 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'] 10 | }) 11 | export class AppComponent { 12 | constructor(private plistaFirebase: PlistaFirebaseService) {} 13 | 14 | plDataDefault = cleanTheData(raw); 15 | 16 | // onPost() { 17 | // this.plistaFirebase 18 | // .postPlistaData(this.plData) 19 | // .subscribe( 20 | // response => console.log(response), 21 | // error => console.log(error) 22 | // ); 23 | // } 24 | onPutDefaultData() { 25 | this.plistaFirebase 26 | .putPlistaData(this.plDataDefault) 27 | .subscribe( 28 | response => console.log(response), 29 | error => console.log(error) 30 | ); 31 | } 32 | onGet() { 33 | this.plistaFirebase 34 | .getPlistaData() 35 | .subscribe( 36 | response => console.log(response), 37 | error => console.log(error) 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | import { HttpModule } from '@angular/http'; 7 | import { registerLocaleData } from '@angular/common'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | import en from '@angular/common/locales/en'; 10 | import { NgZorroAntdModule, NZ_I18N, en_US } from 'ng-zorro-antd'; 11 | 12 | import { AppComponent } from './app.component'; 13 | import { EditableTableComponent } from './editable-table/editable-table.component'; 14 | import { ViewTableComponent } from './view-table/view-table.component'; 15 | // pipes,services,modules 16 | import { FreeclickStringPipe } from './freeclick-string.pipe'; 17 | import { CampEuroPipe } from './camp-euro.pipe'; 18 | import { PlistaFirebaseService } from './plista-firebase.service'; 19 | import { AppRoutingModule } from './app-routing.module'; 20 | import { FormEditComponent } from './form-edit/form-edit.component'; 21 | 22 | registerLocaleData(en); 23 | 24 | @NgModule({ 25 | declarations: [ 26 | AppComponent, 27 | EditableTableComponent, 28 | ViewTableComponent, 29 | FreeclickStringPipe, 30 | CampEuroPipe, 31 | FormEditComponent 32 | ], 33 | imports: [ 34 | BrowserModule, 35 | BrowserAnimationsModule, 36 | FormsModule, 37 | HttpClientModule, 38 | NgZorroAntdModule, 39 | HttpModule, 40 | AppRoutingModule, 41 | ReactiveFormsModule 42 | ], 43 | providers: [{ provide: NZ_I18N, useValue: en_US }, PlistaFirebaseService], 44 | bootstrap: [AppComponent] 45 | }) 46 | export class AppModule {} 47 | -------------------------------------------------------------------------------- /src/app/camp-euro.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { CampEuroPipe } from './camp-euro.pipe'; 2 | 3 | describe('CampEuroPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new CampEuroPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/camp-euro.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'campEuro' 5 | }) 6 | export class CampEuroPipe implements PipeTransform { 7 | 8 | transform(value: number, args?: any): any { 9 | return `€ ${value}`; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/app/editable-table/editable-table.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | camp_cpc 5 | date 6 | time 7 | freeclick 8 | network 9 | PlistaProduct 10 | Action 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{data.camp_cpc | campEuro}} 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | {{data.date | date:'dd.MM.yyyy'}} 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{data.date | date:'HH:mm'}} 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{data.freeclick | freeclickString}} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {{data.network}} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {{data.PlistaProduct}} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 | Edit 91 | 92 | 93 | Save 94 | 95 | Cancel 96 | 97 | 98 |
99 | 100 | 101 | 102 |
-------------------------------------------------------------------------------- /src/app/editable-table/editable-table.component.less: -------------------------------------------------------------------------------- 1 | 2 | .editable-row-operations a { 3 | margin-right: 8px; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/editable-table/editable-table.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { EditableTableComponent } from './editable-table.component'; 3 | 4 | describe('EditableTableComponent', () => { 5 | let component: EditableTableComponent; 6 | let fixture: ComponentFixture; 7 | 8 | beforeEach(fakeAsync(() => { 9 | TestBed.configureTestingModule({ 10 | declarations: [ EditableTableComponent ] 11 | }) 12 | .compileComponents(); 13 | 14 | fixture = TestBed.createComponent(EditableTableComponent); 15 | component = fixture.componentInstance; 16 | fixture.detectChanges(); 17 | })); 18 | 19 | it('should compile', () => { 20 | expect(component).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/editable-table/editable-table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnChanges, Input } from '@angular/core'; 2 | import raw from '../../data/plRawData.json'; 3 | import cleanTheData from '../../data/cleanTheData'; 4 | @Component({ 5 | selector: 'app-editable-table', 6 | templateUrl: './editable-table.component.html', 7 | styleUrls: ['./editable-table.component.less'] 8 | }) 9 | export class EditableTableComponent implements OnInit { 10 | i = 1; 11 | editCache = {}; 12 | raw = raw; 13 | dataSet = cleanTheData(raw); 14 | dateFormat = 'dd.MM.YYYY'; 15 | formatterEuro = value => `€ ${value}`; 16 | parserEuro = value => value.replace('€ ', ''); 17 | 18 | startEdit(key: string): void { 19 | this.editCache[key].edit = true; 20 | } 21 | 22 | cancelEdit(key: string): void { 23 | this.editCache[key].edit = false; 24 | } 25 | 26 | saveEdit(key: string): void { 27 | const index = this.dataSet.findIndex(item => item.key === key); 28 | this.dataSet[index] = this.editCache[key].data; 29 | this.editCache[key].edit = false; 30 | } 31 | 32 | updateEditCache(): void { 33 | this.dataSet.forEach(item => { 34 | if (!this.editCache[item.key]) { 35 | this.editCache[item.key] = { 36 | edit: false, 37 | data: item 38 | }; 39 | } 40 | }); 41 | } 42 | 43 | ngOnInit(): void { 44 | console.log(this.dataSet); 45 | this.updateEditCache(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/form-edit/form-edit.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | camp_cpc 5 | 6 | 7 | 8 | 9 | 10 | 11 | date 12 | 13 | 14 | 15 | 16 | 17 | 18 | time 19 | 20 | 21 | 22 | 23 | 24 | 25 | freeClick 26 | 27 | 28 | 29 | 30 | 31 | 32 | network 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | PlistaProduct 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | -------------------------------------------------------------------------------- /src/app/form-edit/form-edit.component.less: -------------------------------------------------------------------------------- 1 | [nz-form] { 2 | // max-width: 600px; 3 | background-color: #fafafa; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/form-edit/form-edit.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { FormEditComponent } from './form-edit.component'; 3 | 4 | describe('FormEditComponent', () => { 5 | let component: FormEditComponent; 6 | let fixture: ComponentFixture; 7 | 8 | beforeEach(fakeAsync(() => { 9 | TestBed.configureTestingModule({ 10 | declarations: [ FormEditComponent ] 11 | }) 12 | .compileComponents(); 13 | 14 | fixture = TestBed.createComponent(FormEditComponent); 15 | component = fixture.componentInstance; 16 | fixture.detectChanges(); 17 | })); 18 | 19 | it('should compile', () => { 20 | expect(component).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/form-edit/form-edit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { 3 | FormBuilder, 4 | FormControl, 5 | FormGroup, 6 | Validators 7 | } from '@angular/forms'; 8 | import { ActivatedRoute, Params } from '@angular/router'; 9 | import { Subscription } from 'rxjs'; 10 | import { PlistaFirebaseService } from '../plista-firebase.service'; 11 | @Component({ 12 | selector: 'app-form-edit', 13 | templateUrl: './form-edit.component.html', 14 | styleUrls: ['./form-edit.component.less'] 15 | }) 16 | export class FormEditComponent implements OnInit { 17 | validateForm: FormGroup; 18 | paramsSubscription: Subscription; 19 | key: string = null; 20 | record: { 21 | camp_cpc: number; 22 | date: string; 23 | freeClick: boolean; 24 | key: string; 25 | network: string; 26 | PlistaProduct: string; 27 | }; 28 | dataSet: { 29 | camp_cpc: number; 30 | date: string; 31 | freeClick: boolean; 32 | key: string; 33 | network: string; 34 | PlistaProduct: string; 35 | }[] = []; 36 | 37 | parserEuro = (value: string) => value.replace('€ ', ''); 38 | formatterEuro = (value: number) => `€ ${value}`; 39 | logRecord(): void { 40 | this.plistaFirebase 41 | .getPlistaDataRecord(this.key) 42 | .subscribe( 43 | response => console.log(response), 44 | error => console.log(error) 45 | ); 46 | } 47 | submitForm() { 48 | for (const i in this.validateForm.controls) { 49 | this.validateForm.controls[i].markAsDirty(); 50 | this.validateForm.controls[i].updateValueAndValidity(); 51 | } 52 | const { 53 | camp_cpc, 54 | date, 55 | time, 56 | freeClick, 57 | network, 58 | PlistaProduct, 59 | key 60 | } = this.validateForm.value; 61 | const dateClone = new Date(date); 62 | const timeClone = new Date(time); 63 | const dateString = dateClone.toISOString().replace(/T.*/, ''); 64 | const timeString = timeClone.toISOString().match(/(T.{8})/)[0]; 65 | const dateNorm = dateString + timeString; 66 | const normalizedData = { 67 | camp_cpc, 68 | date: dateNorm, 69 | freeClick, 70 | network, 71 | PlistaProduct, 72 | key: this.key 73 | }; 74 | const editIndex = this.dataSet.findIndex(x => x.key === this.key); 75 | const newData = [...this.dataSet]; 76 | newData[editIndex] = normalizedData; 77 | // return console.log(normalizedData, newData); 78 | return this.plistaFirebase 79 | .putPlistaData(newData) 80 | .subscribe( 81 | response => console.log(response), 82 | error => console.log(error) 83 | ); 84 | } 85 | 86 | constructor( 87 | private fb: FormBuilder, 88 | private route: ActivatedRoute, 89 | private plistaFirebase: PlistaFirebaseService 90 | ) {} 91 | 92 | ngOnInit(): void { 93 | this.validateForm = this.fb.group({ 94 | camp_cpc: [null, [Validators.required]], 95 | date: [null, [Validators.required]], 96 | time: [null, [Validators.required]], 97 | freeClick: [null, [Validators.required]], 98 | network: [null, [Validators.required]], 99 | PlistaProduct: [null, [Validators.required]] 100 | }); 101 | 102 | // this.key = this.route.snapshot.params['key']; 103 | this.paramsSubscription = this.route.params.subscribe((params: Params) => { 104 | this.key = params['key']; 105 | }); 106 | 107 | this.plistaFirebase 108 | .getPlistaData() 109 | .subscribe( 110 | response => (this.dataSet = response), 111 | error => console.log(error) 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/app/freeclick-string.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { FreeclickStringPipe } from './freeclick-string.pipe'; 2 | 3 | describe('FreeclickStringPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new FreeclickStringPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/freeclick-string.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'freeclickString' 5 | }) 6 | export class FreeclickStringPipe implements PipeTransform { 7 | transform(value: boolean): any { 8 | return value ? 'true' : 'false'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/plista-firebase.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { PlistaFirebaseService } from './plista-firebase.service'; 4 | 5 | describe('PlistaFirebaseService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [PlistaFirebaseService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([PlistaFirebaseService], (service: PlistaFirebaseService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/plista-firebase.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Headers, Http, Response } from '@angular/http'; 3 | import { map, catchError } from 'rxjs/operators'; 4 | import { Observable } from 'rxjs'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class PlistaFirebaseService { 10 | constructor(private http: Http) {} 11 | 12 | postPlistaData(plData: any[]) { 13 | const headers = new Headers({ 'Content-Type': 'application/json' }); 14 | return this.http.post( 15 | 'https://ct-plista.firebaseio.com/data.json', 16 | plData, 17 | { headers: headers } 18 | ); 19 | } 20 | putPlistaData(plData: any[]) { 21 | const headers = new Headers({ 'Content-Type': 'application/json' }); 22 | return this.http.put('https://ct-plista.firebaseio.com/data.json', plData, { 23 | headers: headers 24 | }); 25 | } 26 | // getPlistaRawData() { 27 | // return ( 28 | // this.http.get('https://ct-plista.firebaseio.com/data').pipe( 29 | // map((response: Response) => { 30 | // const data = response.json(); 31 | // // post-process data 32 | // // ...... 33 | // return data; 34 | // }) 35 | // ), 36 | // catchError((error: Response) => { 37 | // return Observable.throw('Something went wrong'); 38 | // }) 39 | // ); 40 | // } 41 | getPlistaData() { 42 | return this.http.get('https://ct-plista.firebaseio.com/data.json').pipe( 43 | map((response: Response) => { 44 | return response.json(); 45 | }) 46 | ); 47 | } 48 | getPlistaDataRecord(key: string) { 49 | return this.http.get('https://ct-plista.firebaseio.com/data.json').pipe( 50 | map((response: Response) => { 51 | const data = response.json(); 52 | const record = data.find(x => x.key === key); 53 | return record; 54 | }) 55 | ); 56 | } 57 | putPlistaDataRecord(key: string) {} 58 | } 59 | -------------------------------------------------------------------------------- /src/app/view-table/view-table.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | key 6 | camp_cpc 7 | date 8 | time 9 | freeclick 10 | network 11 | PlistaProduct 12 | Action 13 | 14 | 15 | 16 | 17 | {{data.key}} 18 | {{data.camp_cpc | campEuro}} 19 | {{data.date | date:'dd.MM.yyyy'}} 20 | {{data.date | date:'HH:mm'}} 21 | {{data.freeclick | freeclickString}} 22 | {{data.network}} 23 | {{data.PlistaProduct}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/app/view-table/view-table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import raw from '../../data/plRawData.json'; 3 | import cleanTheData from '../../data/cleanTheData'; 4 | import { PlistaFirebaseService } from '../plista-firebase.service'; 5 | import { Router, ActivatedRoute } from '@angular/router'; 6 | 7 | @Component({ 8 | selector: 'app-view-table', 9 | templateUrl: './view-table.component.html' 10 | }) 11 | export class ViewTableComponent implements OnInit { 12 | dataSet: { 13 | camp_cpc: number; 14 | date: string; 15 | freeClick: boolean; 16 | key: string; 17 | network: string; 18 | PlistaProduct: string; 19 | }[] = []; 20 | constructor( 21 | private plistaFirebase: PlistaFirebaseService, 22 | private router: Router, 23 | private route: ActivatedRoute 24 | ) {} 25 | // dataSet = cleanTheData(raw); 26 | 27 | // onEdit() { 28 | // this.router.navigate(['edit'], { relativeTo: this.route }); 29 | // } 30 | ngOnInit(): void { 31 | this.plistaFirebase 32 | .getPlistaData() 33 | .subscribe( 34 | response => (this.dataSet = response), 35 | error => console.log(error) 36 | ); 37 | // console.log(this.dataSet.filter(x => x.key == 0)); 38 | // console.log(this.dataSet.find(x => x.key == 0)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekai/ct-pl-editableTable-angular/6afb5f54eb081bb9dca029c42cf911eea7a32211/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/data/cleanTheData.ts: -------------------------------------------------------------------------------- 1 | const cleanTheData = data => 2 | data 3 | .map(({ a, campaignid, userid, frienddomainid, ...keepRest }) => keepRest) 4 | .map((obj, index) => { 5 | obj.key = index.toString(); 6 | return obj; 7 | }); 8 | 9 | export default cleanTheData; 10 | -------------------------------------------------------------------------------- /src/data/plRawData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "a": 8, 4 | "campaignid": "210a0e48ec0aea780bb747fe56a72d92", 5 | "userid": "16d798b3ae9d72408a91a8737bedb661", 6 | "camp_cpc": 0.271, 7 | "date": "2016-02-29T10:00:06", 8 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 9 | "freeclick": true, 10 | "network": "b", 11 | "PlistaProduct": "Product 8" 12 | }, 13 | { 14 | "a": 14, 15 | "campaignid": "9e7c0cd2c7a4f38f96085debffa70d9d", 16 | "userid": "e327be674a00de52245d6700078f37f0", 17 | "camp_cpc": 0.275, 18 | "date": "2016-01-29T12:00:12", 19 | "frienddomainid": "c677ec07c9679c2445e251ca89702d8f", 20 | "freeclick": false, 21 | "network": "a", 22 | "PlistaProduct": "Product 1" 23 | }, 24 | { 25 | "a": 15, 26 | "campaignid": "0f9f901474b82d051c6b6ca0a181ed2b", 27 | "userid": "c724b02cf291a2403b002182579ed36d", 28 | "camp_cpc": 0.165, 29 | "date": "2016-01-29T12:00:15", 30 | "frienddomainid": "c84f1f1c3914a66236542d1166b01780", 31 | "freeclick": false, 32 | "network": "a", 33 | "PlistaProduct": "Product 1" 34 | }, 35 | { 36 | "a": 19, 37 | "campaignid": "3af1c5a229be0c9a4901a7fc809792af", 38 | "userid": "e48041ab4a53eef90ba275ed6d8923dc", 39 | "camp_cpc": 1.1, 40 | "date": "2016-01-29T12:00:18", 41 | "frienddomainid": "59869eb209bd4d08a60fc30114855466", 42 | "freeclick": false, 43 | "network": "a", 44 | "PlistaProduct": "Product 1" 45 | }, 46 | { 47 | "a": 21, 48 | "campaignid": "7eac767e6a90f955ae20d75d0ed02123", 49 | "userid": "b47353b8eb275b7a468199c8ba9206c3", 50 | "camp_cpc": 0.02, 51 | "date": "2016-01-29T12:00:19", 52 | "frienddomainid": "4ef93030a385d1fb29b83ecce3644ec2", 53 | "freeclick": false, 54 | "network": "a", 55 | "PlistaProduct": "Product 1" 56 | }, 57 | { 58 | "a": 22, 59 | "campaignid": "210a0e48ec0aea780bb747fe56a72d92", 60 | "userid": "31ae72d4d7e96b531e1df085a5ece4dd", 61 | "camp_cpc": 0.271, 62 | "date": "2016-01-29T12:00:23", 63 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 64 | "freeclick": false, 65 | "network": "a", 66 | "PlistaProduct": "Product 1" 67 | }, 68 | { 69 | "a": 23, 70 | "campaignid": "510b8e231ef64f592150ac6106682d1a", 71 | "userid": "0adcd683f29c48c31328a86b1e77081d", 72 | "camp_cpc": 0.22, 73 | "date": "2016-01-29T12:00:24", 74 | "frienddomainid": "c7ea80155da6839ccc5a6ec630e7578c", 75 | "freeclick": false, 76 | "network": "a", 77 | "PlistaProduct": "Product 1" 78 | }, 79 | { 80 | "a": 24, 81 | "campaignid": "ee11c9826101526504fcf7583b53e3d7", 82 | "userid": "8532d40edc1bc45a78f90c0d890788a3", 83 | "camp_cpc": 0.012, 84 | "date": "2016-01-29T12:00:29", 85 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 86 | "freeclick": false, 87 | "network": "a", 88 | "PlistaProduct": "Product 1" 89 | }, 90 | { 91 | "a": 26, 92 | "campaignid": "bf4f4c23309b7d57ca74046e9ff0d7ce", 93 | "userid": "72499e7e68e81df70540284a30953115", 94 | "camp_cpc": 0.099, 95 | "date": "2016-01-29T12:00:30", 96 | "frienddomainid": "2563f41ff7c8f1b9ead213bbf8e1cb1e", 97 | "freeclick": false, 98 | "network": "a", 99 | "PlistaProduct": "Product 1" 100 | }, 101 | { 102 | "a": 30, 103 | "campaignid": "59c6515174a60b00e39655505d1d360e", 104 | "userid": "89453060d3ddd66221aa4092134388fa", 105 | "camp_cpc": 0.025, 106 | "date": "2016-01-29T12:00:37", 107 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 108 | "freeclick": false, 109 | "network": "a", 110 | "PlistaProduct": "Product 1" 111 | }, 112 | { 113 | "a": 32, 114 | "campaignid": "a4cc80e1f74a36122f4c2f2af5104057", 115 | "userid": "181ecf0c401cdd398a492d35f034f62b", 116 | "camp_cpc": 0.07, 117 | "date": "2016-01-29T12:00:37", 118 | "frienddomainid": "b76ed49b827b1f55082d74cc9b749ff8", 119 | "freeclick": false, 120 | "network": "a", 121 | "PlistaProduct": "Product 10" 122 | }, 123 | { 124 | "a": 37, 125 | "campaignid": "59c6515174a60b00e39655505d1d360e", 126 | "userid": "f7159abfe33ed904b488f4fc19e5b285", 127 | "camp_cpc": 0.025, 128 | "date": "2016-01-29T12:00:43", 129 | "frienddomainid": "7fc91ff77c2d67a6ceb83a4542afb346", 130 | "freeclick": false, 131 | "network": "a", 132 | "PlistaProduct": "Product 1" 133 | }, 134 | { 135 | "a": 38, 136 | "campaignid": "8115fe8942c37e49e3e016a060c7e2f9", 137 | "userid": "81f64565aaefce79015e588bc1efa433", 138 | "camp_cpc": 0.169, 139 | "date": "2016-01-29T12:00:43", 140 | "frienddomainid": "9a6e612aa4752d4967fd08f96c3f7424", 141 | "freeclick": false, 142 | "network": "a", 143 | "PlistaProduct": "Product 1" 144 | }, 145 | { 146 | "a": 39, 147 | "campaignid": "d96150cddc3ad967e0c9dd87304f1e94", 148 | "userid": "169c386e1961b894dd6bc1fafca5c148", 149 | "camp_cpc": 0.22, 150 | "date": "2016-01-29T12:00:45", 151 | "frienddomainid": "f1ce94556e4edc2c15a463ca093a05bc", 152 | "freeclick": false, 153 | "network": "a", 154 | "PlistaProduct": "Product 1" 155 | }, 156 | { 157 | "a": 41, 158 | "campaignid": "881a47aa16feffeed5b85456a53fe6a5", 159 | "userid": "29cacc45afc5007993c62dde4cc11225", 160 | "camp_cpc": 0.078, 161 | "date": "2016-01-29T12:00:46", 162 | "frienddomainid": "a96d6f7509d0ba36352ca9bb6c7a580f", 163 | "freeclick": false, 164 | "network": "a", 165 | "PlistaProduct": "Product 1" 166 | }, 167 | { 168 | "a": 42, 169 | "campaignid": "034825fd3b7fa3ac814b62593cb04125", 170 | "userid": "282851fa02e4ad8a057c48641c4c4179", 171 | "camp_cpc": 0.01, 172 | "date": "2016-01-29T12:00:47", 173 | "frienddomainid": "5ac9c3bd672d0e14247178d733ad0038", 174 | "freeclick": false, 175 | "network": "a", 176 | "PlistaProduct": "Product 1" 177 | }, 178 | { 179 | "a": 44, 180 | "campaignid": "96d5954c045c5226a4cae5ef0dd0efb8", 181 | "userid": "155b3bb66124d4b91f1827a39e04c592", 182 | "camp_cpc": 0.025, 183 | "date": "2016-01-29T12:00:47", 184 | "frienddomainid": "1f9c23217063fe2f9a1b98138e7f2276", 185 | "freeclick": false, 186 | "network": "a", 187 | "PlistaProduct": "Product 1" 188 | }, 189 | { 190 | "a": 45, 191 | "campaignid": "52b34fd99ee8836d8ebf86537ec5fc69", 192 | "userid": "c25b1f331406fcc37f56cabbe765d122", 193 | "camp_cpc": 0.275, 194 | "date": "2016-01-29T12:00:50", 195 | "frienddomainid": "1f8107db1348b9e5ddcf83489018a5d2", 196 | "freeclick": false, 197 | "network": "a", 198 | "PlistaProduct": "Product 1" 199 | }, 200 | { 201 | "a": 49, 202 | "campaignid": "59c6515174a60b00e39655505d1d360e", 203 | "userid": "06deb55c7a74ec300164e2c3f858ea93", 204 | "camp_cpc": 0.025, 205 | "date": "2016-01-29T12:00:58", 206 | "frienddomainid": "85d5f233371c9a0511187c1e3538dc06", 207 | "freeclick": false, 208 | "network": "a", 209 | "PlistaProduct": "Product 1" 210 | }, 211 | { 212 | "a": 53, 213 | "campaignid": "c4eb90fdc9fb2961fe28b5f6f587187b", 214 | "userid": "ba2e07874d990b59caa529d9a9f0814e", 215 | "camp_cpc": 0.02, 216 | "date": "2016-01-29T12:01:04", 217 | "frienddomainid": "bd7fb1f8bf0567760641b6d7a29c1609", 218 | "freeclick": false, 219 | "network": "a", 220 | "PlistaProduct": "Product 1" 221 | }, 222 | { 223 | "a": 60, 224 | "campaignid": "9f0f15b977bb62adc7357d7d8ae645f4", 225 | "userid": "edf03b170ef865a43fdd4757eb00c986", 226 | "camp_cpc": 0.44, 227 | "date": "2016-01-29T12:01:13", 228 | "frienddomainid": "5ac9c3bd672d0e14247178d733ad0038", 229 | "freeclick": false, 230 | "network": "a", 231 | "PlistaProduct": "Product 1" 232 | }, 233 | { 234 | "a": 69, 235 | "campaignid": "59c6515174a60b00e39655505d1d360e", 236 | "userid": "0272f7fd701e69927c2b59bc50b47550", 237 | "camp_cpc": 0.025, 238 | "date": "2016-01-29T12:01:26", 239 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 240 | "freeclick": false, 241 | "network": "a", 242 | "PlistaProduct": "Product 1" 243 | }, 244 | { 245 | "a": 73, 246 | "campaignid": "801a9705e4308f714b4adba6b90200c8", 247 | "userid": "f45d349797ecfd430456f79c3f710ce2", 248 | "camp_cpc": 0.03, 249 | "date": "2016-01-29T12:01:33", 250 | "frienddomainid": "ea180f1ea30b6bc86621e2b4d41d7605", 251 | "freeclick": false, 252 | "network": "a", 253 | "PlistaProduct": "Product 1" 254 | }, 255 | { 256 | "a": 75, 257 | "campaignid": "60ad9c594d33f1ffd75c2db49b6b238e", 258 | "userid": "6fabba96c171e566ec29985479b8d9c3", 259 | "camp_cpc": 0.002, 260 | "date": "2016-01-29T12:01:35", 261 | "frienddomainid": "ffd4a2bce01c12378a19a5127068d4c1", 262 | "freeclick": true, 263 | "network": "a", 264 | "PlistaProduct": "Product 1" 265 | }, 266 | { 267 | "a": 79, 268 | "campaignid": "3855280ae05920f6a6ea9cd8de4e3cc8", 269 | "userid": "dfe223ad95d0a503b8ef82cdba728c20", 270 | "camp_cpc": 0.032, 271 | "date": "2016-01-29T12:01:36", 272 | "frienddomainid": "b76fbc87f4889ccd1c4acad64670ccb2", 273 | "freeclick": false, 274 | "network": "a", 275 | "PlistaProduct": "Product 1" 276 | }, 277 | { 278 | "a": 80, 279 | "campaignid": "05b2882384c5f0480ff5672bd0fb5eb6", 280 | "userid": "330e6651a7e427a400d8e807e9daf9f4", 281 | "camp_cpc": 1.21, 282 | "date": "2016-01-29T12:01:37", 283 | "frienddomainid": "ab73535914798c3ad40ab8bea10188a4", 284 | "freeclick": false, 285 | "network": "a", 286 | "PlistaProduct": "Product 10" 287 | }, 288 | { 289 | "a": 81, 290 | "campaignid": "ee11c9826101526504fcf7583b53e3d7", 291 | "userid": "177d5f2579e68982b9b1f4d533cd6f85", 292 | "camp_cpc": 0.012, 293 | "date": "2016-01-29T12:01:37", 294 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 295 | "freeclick": false, 296 | "network": "a", 297 | "PlistaProduct": "Product 1" 298 | }, 299 | { 300 | "a": 82, 301 | "campaignid": "8a427942e5a35eab7750fb891671b955", 302 | "userid": "08eca482935176100e9fda8b4a9be611", 303 | "camp_cpc": 1.65, 304 | "date": "2016-01-29T12:01:41", 305 | "frienddomainid": "29afdbb94f80a2306c3816166cb68807", 306 | "freeclick": false, 307 | "network": "b", 308 | "PlistaProduct": "Product 1" 309 | }, 310 | { 311 | "a": 84, 312 | "campaignid": "89313ff9ffea9374ea791b4dbc3209ff", 313 | "userid": "134140f023a8631523ae5ae74acbcce8", 314 | "camp_cpc": 0.09, 315 | "date": "2016-01-29T12:01:42", 316 | "frienddomainid": "76021a7e1269f7ff36ff3d638aec49e9", 317 | "freeclick": false, 318 | "network": "a", 319 | "PlistaProduct": "Product 1" 320 | }, 321 | { 322 | "a": 85, 323 | "campaignid": "9e7c0cd2c7a4f38f96085debffa70d9d", 324 | "userid": "b5b0b3bb74835f3dba3f6664beef7fce", 325 | "camp_cpc": 0.275, 326 | "date": "2016-01-29T12:01:43", 327 | "frienddomainid": "8aeb76652f966400d5cac29f6bd6e442", 328 | "freeclick": false, 329 | "network": "a", 330 | "PlistaProduct": "Product 1" 331 | }, 332 | { 333 | "a": 89, 334 | "campaignid": "ac458b2e5a15abc596ab61bccf51dd5d", 335 | "userid": "af7beb57f30bb0c2e50f8860a840ef7b", 336 | "camp_cpc": 1.1, 337 | "date": "2016-01-29T12:01:50", 338 | "frienddomainid": "ad5f12b9539cca684ffefa884c430e4d", 339 | "freeclick": false, 340 | "network": "a", 341 | "PlistaProduct": "Product 10" 342 | }, 343 | { 344 | "a": 101, 345 | "campaignid": "8fe57c6ae091fd33688bc6ce76d74920", 346 | "userid": "037ebe831cb026a01462aac6cb2321f1", 347 | "camp_cpc": 0.275, 348 | "date": "2016-01-29T12:02:06", 349 | "frienddomainid": "36ac2b589744fa94bfe694b604971bf0", 350 | "freeclick": false, 351 | "network": "a", 352 | "PlistaProduct": "Product 1" 353 | }, 354 | { 355 | "a": 105, 356 | "campaignid": "66f1bb9ffc9bc65c12de585662ceed14", 357 | "userid": "495c30d014e0c4c73edb33ec2aea7414", 358 | "camp_cpc": 0.495, 359 | "date": "2016-01-29T12:02:11", 360 | "frienddomainid": "ea498e82579e76f2b96a4edc7a42ce05", 361 | "freeclick": false, 362 | "network": "a", 363 | "PlistaProduct": "Product 1" 364 | }, 365 | { 366 | "a": 106, 367 | "campaignid": "0f9f901474b82d051c6b6ca0a181ed2b", 368 | "userid": "58a6e8f6e8008d7151c89472324ecd7b", 369 | "camp_cpc": 0.165, 370 | "date": "2016-01-29T12:02:12", 371 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 372 | "freeclick": false, 373 | "network": "a", 374 | "PlistaProduct": "Product 1" 375 | }, 376 | { 377 | "a": 110, 378 | "campaignid": "39cf87661e3263138ff177765f78d20d", 379 | "userid": "23af33555171f604b5ab09b39dc5c2b4", 380 | "camp_cpc": 0.028, 381 | "date": "2016-01-29T12:02:17", 382 | "frienddomainid": "46fb6ac698d54a632d70b6b83fd6719a", 383 | "freeclick": false, 384 | "network": "a", 385 | "PlistaProduct": "Product 1" 386 | }, 387 | { 388 | "a": 117, 389 | "campaignid": "230cab36e9b7d8047e9771cd5a1b0e6a", 390 | "userid": "347b1ca07daf14bea74c5a2aa3488f41", 391 | "camp_cpc": 0.99, 392 | "date": "2016-01-29T12:02:19", 393 | "frienddomainid": "6ab5309c61d84b1386faaf1eb27aff0c", 394 | "freeclick": false, 395 | "network": "b", 396 | "PlistaProduct": "Product 1" 397 | }, 398 | { 399 | "a": 118, 400 | "campaignid": "9f0c619137a28052cfb491d36c48035a", 401 | "userid": "bc1f2ac5d94391a5836d0a838b3b3508", 402 | "camp_cpc": 0.015, 403 | "date": "2016-01-29T12:02:20", 404 | "frienddomainid": "6b6a6a1c554f9d6083451f68f9578d26", 405 | "freeclick": false, 406 | "network": "a", 407 | "PlistaProduct": "Product 1" 408 | }, 409 | { 410 | "a": 120, 411 | "campaignid": "8a12fd07b5f07f33fe6d9d057b546ae9", 412 | "userid": "2438bf855889ff283d9377a239fd2caa", 413 | "camp_cpc": 1.32, 414 | "date": "2016-01-29T12:02:20", 415 | "frienddomainid": "6cb7e3021d98159b9f4ce8b55786de04", 416 | "freeclick": false, 417 | "network": "a", 418 | "PlistaProduct": "Product 8" 419 | }, 420 | { 421 | "a": 126, 422 | "campaignid": "be0b9ef4ff59077e41c8932ac9b92185", 423 | "userid": "82184b542ba92898d3db5a06ce082463", 424 | "camp_cpc": 0.02, 425 | "date": "2016-01-29T12:02:27", 426 | "frienddomainid": "072278eca9b171854f70564769acae45", 427 | "freeclick": false, 428 | "network": "a", 429 | "PlistaProduct": "Product 1" 430 | }, 431 | { 432 | "a": 127, 433 | "campaignid": "c5c8fe6c49ad57ccc702685ce74e2c71", 434 | "userid": "cbb31bcf974b7755f23046f5ec655858", 435 | "camp_cpc": 0.77, 436 | "date": "2016-01-29T12:02:28", 437 | "frienddomainid": "a6a141f631618325c81115ed35c32ff5", 438 | "freeclick": false, 439 | "network": "a", 440 | "PlistaProduct": "Product 1" 441 | }, 442 | { 443 | "a": 135, 444 | "campaignid": "39cf87661e3263138ff177765f78d20d", 445 | "userid": "25fb0da84dc5e6dc9715d14ba03ce91b", 446 | "camp_cpc": 0.028, 447 | "date": "2016-01-29T12:02:35", 448 | "frienddomainid": "d069feadb7849044ecbcb68c2511a4f6", 449 | "freeclick": false, 450 | "network": "a", 451 | "PlistaProduct": "Product 1" 452 | }, 453 | { 454 | "a": 136, 455 | "campaignid": "5ef4dee06fb589b7325ac576079bb9f0", 456 | "userid": "e01394673c47b871b3b5c72f29b1392a", 457 | "camp_cpc": 0.55, 458 | "date": "2016-01-29T12:02:38", 459 | "frienddomainid": "131185c828857c1be44756bd59766432", 460 | "freeclick": false, 461 | "network": "a", 462 | "PlistaProduct": "Product 1" 463 | }, 464 | { 465 | "a": 139, 466 | "campaignid": "40321e0b975c073e89962c4aa7759b19", 467 | "userid": "94021a7fb7f002158011b5c5276af5e6", 468 | "camp_cpc": 0.33, 469 | "date": "2016-01-29T12:02:39", 470 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 471 | "freeclick": false, 472 | "network": "a", 473 | "PlistaProduct": "Product 1" 474 | }, 475 | { 476 | "a": 141, 477 | "campaignid": "a51bd7e30020038f294b1c0114f35150", 478 | "userid": "af7beb57f30bb0c2e50f8860a840ef7b", 479 | "camp_cpc": 0.12, 480 | "date": "2016-01-29T12:02:41", 481 | "frienddomainid": "ad5f12b9539cca684ffefa884c430e4d", 482 | "freeclick": false, 483 | "network": "a", 484 | "PlistaProduct": "Product 8" 485 | }, 486 | { 487 | "a": 144, 488 | "campaignid": "210a0e48ec0aea780bb747fe56a72d92", 489 | "userid": "c76b56947b3c71ae1d916fa544b74915", 490 | "camp_cpc": 0.271, 491 | "date": "2016-01-29T12:02:44", 492 | "frienddomainid": "3efeff9dbd6bfd7ccc698c0baf52740c", 493 | "freeclick": false, 494 | "network": "a", 495 | "PlistaProduct": "Product 1" 496 | }, 497 | { 498 | "a": 151, 499 | "campaignid": "881a47aa16feffeed5b85456a53fe6a5", 500 | "userid": "aa0dca1a46852900d174354b1fa566c5", 501 | "camp_cpc": 0.078, 502 | "date": "2016-01-29T12:02:48", 503 | "frienddomainid": "2be605048b41e243e01428d001c18388", 504 | "freeclick": false, 505 | "network": "a", 506 | "PlistaProduct": "Product 1" 507 | }, 508 | { 509 | "a": 154, 510 | "campaignid": "3bf8689417fec5bf95a3c356890e6e3d", 511 | "userid": "53b77cdafa3e09092c30664fdd174bd6", 512 | "camp_cpc": 0.33, 513 | "date": "2016-01-29T12:02:54", 514 | "frienddomainid": "2a58d32d2b0f0042a9d252cae6d0079c", 515 | "freeclick": false, 516 | "network": "a", 517 | "PlistaProduct": "Product 1" 518 | }, 519 | { 520 | "a": 165, 521 | "campaignid": "92d34d8765f0e40e5bed23101c6b3e16", 522 | "userid": "25fb0da84dc5e6dc9715d14ba03ce91b", 523 | "camp_cpc": 0.99, 524 | "date": "2016-01-29T12:03:07", 525 | "frienddomainid": "d069feadb7849044ecbcb68c2511a4f6", 526 | "freeclick": false, 527 | "network": "a", 528 | "PlistaProduct": "Product 1" 529 | }, 530 | { 531 | "a": 167, 532 | "campaignid": "4e45591105ef168381ef6122d58f3587", 533 | "userid": "10a6eb3cefd37345232b74fbb18fe5de", 534 | "camp_cpc": 0.02, 535 | "date": "2016-01-29T12:03:09", 536 | "frienddomainid": "1fcf4e6a7771f35daf0b98c69fb10528", 537 | "freeclick": false, 538 | "network": "a", 539 | "PlistaProduct": "Product 10" 540 | }, 541 | { 542 | "a": 170, 543 | "campaignid": "2fbb87e8c8d449b6a4df0baf8c1f617c", 544 | "userid": "fa173b6826f399251e873b4ccfd3aeab", 545 | "camp_cpc": 0.825, 546 | "date": "2016-01-29T12:03:11", 547 | "frienddomainid": "6dc4166c5aa3123f245a786381d0eb9c", 548 | "freeclick": false, 549 | "network": "a", 550 | "PlistaProduct": "Product 1" 551 | }, 552 | { 553 | "a": 171, 554 | "campaignid": "ee11c9826101526504fcf7583b53e3d7", 555 | "userid": "3064d65b5d536de84a9338dba23eaa00", 556 | "camp_cpc": 0.012, 557 | "date": "2016-01-29T12:03:13", 558 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 559 | "freeclick": false, 560 | "network": "a", 561 | "PlistaProduct": "Product 1" 562 | }, 563 | { 564 | "a": 172, 565 | "campaignid": "1c4c56e46fb745cf0ea2dc6b5451b6dd", 566 | "userid": "b83006f0ce9444904c6f140f842477f4", 567 | "camp_cpc": 1.265, 568 | "date": "2016-01-29T12:03:14", 569 | "frienddomainid": "c0b3cb0842f9f8148f618c587b48d5ba", 570 | "freeclick": false, 571 | "network": "a", 572 | "PlistaProduct": "Product 1" 573 | }, 574 | { 575 | "a": 173, 576 | "campaignid": "8cb358508e2e4ccb45b83e77d0120490", 577 | "userid": "fddddbd56fa7a00145759028f2b97c01", 578 | "camp_cpc": 0.05, 579 | "date": "2016-01-29T12:03:15", 580 | "frienddomainid": "85d5f233371c9a0511187c1e3538dc06", 581 | "freeclick": false, 582 | "network": "a", 583 | "PlistaProduct": "Product 1" 584 | }, 585 | { 586 | "a": 181, 587 | "campaignid": "230cab36e9b7d8047e9771cd5a1b0e6a", 588 | "userid": "fa196b8943c462e6a5c1286ee9544761", 589 | "camp_cpc": 0.99, 590 | "date": "2016-01-29T12:03:23", 591 | "frienddomainid": "c430b88284e83a68247b4785500d426c", 592 | "freeclick": false, 593 | "network": "a", 594 | "PlistaProduct": "Product 1" 595 | }, 596 | { 597 | "a": 183, 598 | "campaignid": "ab01567e6fb833c70d1bad248b5a7120", 599 | "userid": "86dfa98fb064f56bbb713c30f02e655e", 600 | "camp_cpc": 0.05, 601 | "date": "2016-01-29T12:03:24", 602 | "frienddomainid": "ad5f12b9539cca684ffefa884c430e4d", 603 | "freeclick": false, 604 | "network": "a", 605 | "PlistaProduct": "Product 1" 606 | }, 607 | { 608 | "a": 184, 609 | "campaignid": "5abf63f7f80defddd00db88537548b2e", 610 | "userid": "8e6b85234e0fcc1bb592ff394f0c4a47", 611 | "camp_cpc": 0.77, 612 | "date": "2016-01-29T12:03:25", 613 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 614 | "freeclick": false, 615 | "network": "a", 616 | "PlistaProduct": "Product 1" 617 | }, 618 | { 619 | "a": 187, 620 | "campaignid": "59c6515174a60b00e39655505d1d360e", 621 | "userid": "820e3949d87c3ac34dd266bd341b725d", 622 | "camp_cpc": 0.025, 623 | "date": "2016-01-29T12:03:28", 624 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 625 | "freeclick": false, 626 | "network": "a", 627 | "PlistaProduct": "Product 1" 628 | }, 629 | { 630 | "a": 189, 631 | "campaignid": "5ae8841a7c28782490936cc6312213a1", 632 | "userid": "4daaddc7bcf8dd7621455407d0fa98a8", 633 | "camp_cpc": 0.068, 634 | "date": "2016-01-29T12:03:30", 635 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 636 | "freeclick": false, 637 | "network": "a", 638 | "PlistaProduct": "Product 1" 639 | }, 640 | { 641 | "a": 193, 642 | "campaignid": "ae5fb5da59b0bfb43e4094da21511f64", 643 | "userid": "ca5fe6181c1223d5f0ed4594787fb9c0", 644 | "camp_cpc": 0.165, 645 | "date": "2016-01-29T12:03:36", 646 | "frienddomainid": "60dfd04289ecf518443e02289a0cd633", 647 | "freeclick": false, 648 | "network": "a", 649 | "PlistaProduct": "Product 1" 650 | }, 651 | { 652 | "a": 194, 653 | "campaignid": "0f9f901474b82d051c6b6ca0a181ed2b", 654 | "userid": "44db0a22ab6fcbfb31bef7b334c985b6", 655 | "camp_cpc": 0.165, 656 | "date": "2016-01-29T12:03:36", 657 | "frienddomainid": "c12771799157ed8ae819b9ce0a7b4c78", 658 | "freeclick": false, 659 | "network": "a", 660 | "PlistaProduct": "Product 1" 661 | }, 662 | { 663 | "a": 195, 664 | "campaignid": "0f9f901474b82d051c6b6ca0a181ed2b", 665 | "userid": "44db0a22ab6fcbfb31bef7b334c985b6", 666 | "camp_cpc": 0.165, 667 | "date": "2016-01-29T12:03:36", 668 | "frienddomainid": "c12771799157ed8ae819b9ce0a7b4c78", 669 | "freeclick": true, 670 | "network": "a", 671 | "PlistaProduct": "Product 1" 672 | }, 673 | { 674 | "a": 196, 675 | "campaignid": "9724f7ca6d4fe5dcd6ca445347e568e6", 676 | "userid": "e44389f19a54f2e88079f3b850d6e3a1", 677 | "camp_cpc": 0.77, 678 | "date": "2016-01-29T12:03:38", 679 | "frienddomainid": "e44956a0a9118d2bfe39f3483f57db37", 680 | "freeclick": false, 681 | "network": "a", 682 | "PlistaProduct": "Product 1" 683 | }, 684 | { 685 | "a": 199, 686 | "campaignid": "0b51647e8b40c558cf7d71efa88ef917", 687 | "userid": "95cf591030c1398c9a2762d1a27bc4bb", 688 | "camp_cpc": 0.33, 689 | "date": "2016-01-29T12:03:45", 690 | "frienddomainid": "c64350c9477e1ea35aa37a5a73d4e042", 691 | "freeclick": false, 692 | "network": "a", 693 | "PlistaProduct": "Product 1" 694 | }, 695 | { 696 | "a": 205, 697 | "campaignid": "30fb53c2e14a3a7709b6357fcb2b2982", 698 | "userid": "280111b75bda43da6d27bb2d6bf010bb", 699 | "camp_cpc": 0.03, 700 | "date": "2016-01-29T12:03:47", 701 | "frienddomainid": "a0cce36de7a1457b6802255fcec69081", 702 | "freeclick": false, 703 | "network": "a", 704 | "PlistaProduct": "Product 1" 705 | }, 706 | { 707 | "a": 206, 708 | "campaignid": "3977b94d96968652d71f8de20673828d", 709 | "userid": "a30babef0aafc5c335d707908494c39c", 710 | "camp_cpc": 0.253, 711 | "date": "2016-01-29T12:03:49", 712 | "frienddomainid": "a24d4d14365b82318d2f6c6df6b87907", 713 | "freeclick": false, 714 | "network": "a", 715 | "PlistaProduct": "Product 1" 716 | }, 717 | { 718 | "a": 207, 719 | "campaignid": "a169952962aac0046e1983fe798c565f", 720 | "userid": "17a1af164c3f646b8462c1ced8917a5b", 721 | "camp_cpc": 0.035, 722 | "date": "2016-01-29T12:03:50", 723 | "frienddomainid": "7fc91ff77c2d67a6ceb83a4542afb346", 724 | "freeclick": false, 725 | "network": "a", 726 | "PlistaProduct": "Product 1" 727 | }, 728 | { 729 | "a": 208, 730 | "campaignid": "5ef4dee06fb589b7325ac576079bb9f0", 731 | "userid": "dceca923851b760d613104dc6c654851", 732 | "camp_cpc": 0.55, 733 | "date": "2016-01-29T12:03:54", 734 | "frienddomainid": "131185c828857c1be44756bd59766432", 735 | "freeclick": false, 736 | "network": "a", 737 | "PlistaProduct": "Product 1" 738 | }, 739 | { 740 | "a": 209, 741 | "campaignid": "c81779aa7f1a0eafd914ba5aea36416c", 742 | "userid": "86a5d0fcc461d736eb567863a1e3802a", 743 | "camp_cpc": 0.715, 744 | "date": "2016-01-29T12:03:54", 745 | "frienddomainid": "e229e8f706916fc3170a2d4c5b3ce643", 746 | "freeclick": false, 747 | "network": "a", 748 | "PlistaProduct": "Product 1" 749 | }, 750 | { 751 | "a": 210, 752 | "campaignid": "be0b9ef4ff59077e41c8932ac9b92185", 753 | "userid": "aef54af24b68a3490200e218845ed653", 754 | "camp_cpc": 0.02, 755 | "date": "2016-01-29T12:03:54", 756 | "frienddomainid": "c64350c9477e1ea35aa37a5a73d4e042", 757 | "freeclick": false, 758 | "network": "a", 759 | "PlistaProduct": "Product 1" 760 | }, 761 | { 762 | "a": 212, 763 | "campaignid": "5ef4dee06fb589b7325ac576079bb9f0", 764 | "userid": "ade7bfe071d352f326efe565030f2b19", 765 | "camp_cpc": 0.55, 766 | "date": "2016-01-29T12:04:00", 767 | "frienddomainid": "8763d9a9825b0ca9e26bb80fe85f08d4", 768 | "freeclick": false, 769 | "network": "a", 770 | "PlistaProduct": "Product 1" 771 | }, 772 | { 773 | "a": 213, 774 | "campaignid": "59c6515174a60b00e39655505d1d360e", 775 | "userid": "69b7718edd324076b252dfeddd209492", 776 | "camp_cpc": 0.025, 777 | "date": "2016-01-29T12:04:00", 778 | "frienddomainid": "5ac9c3bd672d0e14247178d733ad0038", 779 | "freeclick": false, 780 | "network": "a", 781 | "PlistaProduct": "Product 1" 782 | }, 783 | { 784 | "a": 214, 785 | "campaignid": "8a12fd07b5f07f33fe6d9d057b546ae9", 786 | "userid": "106143f377a5830a9d66a616eb6559df", 787 | "camp_cpc": 1.32, 788 | "date": "2016-01-29T12:04:01", 789 | "frienddomainid": "ad5f12b9539cca684ffefa884c430e4d", 790 | "freeclick": false, 791 | "network": "a", 792 | "PlistaProduct": "Product 8" 793 | }, 794 | { 795 | "a": 217, 796 | "campaignid": "9f0c619137a28052cfb491d36c48035a", 797 | "userid": "1178b5c6f7f96b728a41ae2aae225bd2", 798 | "camp_cpc": 0.015, 799 | "date": "2016-01-29T12:04:04", 800 | "frienddomainid": "3049f7815aa6d0a717f6703971a6680a", 801 | "freeclick": false, 802 | "network": "a", 803 | "PlistaProduct": "Product 1" 804 | }, 805 | { 806 | "a": 222, 807 | "campaignid": "230cab36e9b7d8047e9771cd5a1b0e6a", 808 | "userid": "6911e4ecb976fd6b600a67f9db90085f", 809 | "camp_cpc": 0.99, 810 | "date": "2016-01-29T12:04:07", 811 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 812 | "freeclick": false, 813 | "network": "a", 814 | "PlistaProduct": "Product 1" 815 | }, 816 | { 817 | "a": 224, 818 | "campaignid": "5abf63f7f80defddd00db88537548b2e", 819 | "userid": "944c81192eb6032ff62de2d4cfe981d7", 820 | "camp_cpc": 0.77, 821 | "date": "2016-01-29T12:04:08", 822 | "frienddomainid": "9a8799e6ac57db61203d74b98d78e2ee", 823 | "freeclick": false, 824 | "network": "a", 825 | "PlistaProduct": "Product 1" 826 | }, 827 | { 828 | "a": 225, 829 | "campaignid": "4571c3ee84e9429ef5ae107789941c35", 830 | "userid": "006f74ebad04528e718ed7941a04dbe6", 831 | "camp_cpc": 1.32, 832 | "date": "2016-01-29T12:04:10", 833 | "frienddomainid": "2d2a041ccf81c1ea9ffd255c70a4f024", 834 | "freeclick": false, 835 | "network": "a", 836 | "PlistaProduct": "Product 1" 837 | }, 838 | { 839 | "a": 230, 840 | "campaignid": "69f71f2da7481441ee728ac78fe137cf", 841 | "userid": "b469f1b46c413bf6ab0e8c19a8b00d75", 842 | "camp_cpc": 0.096, 843 | "date": "2016-01-29T12:04:13", 844 | "frienddomainid": "131185c828857c1be44756bd59766432", 845 | "freeclick": false, 846 | "network": "a", 847 | "PlistaProduct": "Product 1" 848 | }, 849 | { 850 | "a": 234, 851 | "campaignid": "05b2882384c5f0480ff5672bd0fb5eb6", 852 | "userid": "7d43f97f23375db193b0a00af3f0898c", 853 | "camp_cpc": 1.21, 854 | "date": "2016-01-29T12:04:20", 855 | "frienddomainid": "e53da0660d5d695870b49aee039f9148", 856 | "freeclick": false, 857 | "network": "a", 858 | "PlistaProduct": "Product 10" 859 | }, 860 | { 861 | "a": 239, 862 | "campaignid": "ac458b2e5a15abc596ab61bccf51dd5d", 863 | "userid": "5d1647390056a484cfd289b9bea3403a", 864 | "camp_cpc": 1.1, 865 | "date": "2016-01-29T12:04:27", 866 | "frienddomainid": "1fcf4e6a7771f35daf0b98c69fb10528", 867 | "freeclick": false, 868 | "network": "a", 869 | "PlistaProduct": "Product 10" 870 | }, 871 | { 872 | "a": 242, 873 | "campaignid": "801a9705e4308f714b4adba6b90200c8", 874 | "userid": "1cb6012c7f02b075a92c955700722b3a", 875 | "camp_cpc": 0.03, 876 | "date": "2016-01-29T12:04:29", 877 | "frienddomainid": "d963c89a2d2f07bc26095f03c15b03b8", 878 | "freeclick": false, 879 | "network": "a", 880 | "PlistaProduct": "Product 1" 881 | }, 882 | { 883 | "a": 244, 884 | "campaignid": "9e7c0cd2c7a4f38f96085debffa70d9d", 885 | "userid": "2d34eb6eebdb4fc27f3260008e8dbb74", 886 | "camp_cpc": 0.275, 887 | "date": "2016-01-29T12:04:29", 888 | "frienddomainid": "059462857ce955b907bd79a9cf5a9e2c", 889 | "freeclick": false, 890 | "network": "a", 891 | "PlistaProduct": "Product 1" 892 | }, 893 | { 894 | "a": 246, 895 | "campaignid": "f8c586d98dcd82fae01306fa6978a256", 896 | "userid": "bc5046da607e6f1fd0bb2d995d946ead", 897 | "camp_cpc": 1.1, 898 | "date": "2016-01-29T12:04:31", 899 | "frienddomainid": "94cbfd54b262bae21ddeb6947d370996", 900 | "freeclick": false, 901 | "network": "a", 902 | "PlistaProduct": "Product 1" 903 | }, 904 | { 905 | "a": 251, 906 | "campaignid": "8a12fd07b5f07f33fe6d9d057b546ae9", 907 | "userid": "7cfd3f8da6cd23efbc3376ee8094e349", 908 | "camp_cpc": 1.32, 909 | "date": "2016-01-29T12:04:39", 910 | "frienddomainid": "6a1eb08082e599be20b641013fb8901c", 911 | "freeclick": false, 912 | "network": "a", 913 | "PlistaProduct": "Product 8" 914 | }, 915 | { 916 | "a": 255, 917 | "campaignid": "a51bd7e30020038f294b1c0114f35150", 918 | "userid": "64adf307791cffd8ca451d3e4bc8df04", 919 | "camp_cpc": 0.12, 920 | "date": "2016-01-29T12:04:42", 921 | "frienddomainid": "ab73535914798c3ad40ab8bea10188a4", 922 | "freeclick": false, 923 | "network": "a", 924 | "PlistaProduct": "Product 8" 925 | }, 926 | { 927 | "a": 256, 928 | "campaignid": "ecf5208ff66a38a9b607bead79c2152c", 929 | "userid": "1a0775276b2658dd7bc17eca3bb17928", 930 | "camp_cpc": 0.045, 931 | "date": "2016-01-29T12:04:43", 932 | "frienddomainid": "94a6cfcd986195c6d7dedc65b53dd94e", 933 | "freeclick": false, 934 | "network": "a", 935 | "PlistaProduct": "Product 1" 936 | }, 937 | { 938 | "a": 258, 939 | "campaignid": "579b99842048b589625fead03a423315", 940 | "userid": "696beb80b399c7e91d70e8cc62247bc6", 941 | "camp_cpc": 0.02, 942 | "date": "2016-01-29T12:04:46", 943 | "frienddomainid": "e5b10e2dfc76b52f3f707e43cb97bbf4", 944 | "freeclick": false, 945 | "network": "a", 946 | "PlistaProduct": "Product 1" 947 | }, 948 | { 949 | "a": 264, 950 | "campaignid": "f21cfe05f578ac1f9e3f05ccaafe9e19", 951 | "userid": "201ceff42f49ecfbd79cf1f645563e00", 952 | "camp_cpc": 0.825, 953 | "date": "2016-01-29T12:04:51", 954 | "frienddomainid": "16046607a0968b274c06a98fc1ed4215", 955 | "freeclick": false, 956 | "network": "a", 957 | "PlistaProduct": "Product 1" 958 | }, 959 | { 960 | "a": 266, 961 | "campaignid": "05b2882384c5f0480ff5672bd0fb5eb6", 962 | "userid": "acdf2cf44aa08f785979ab8cc9c00e85", 963 | "camp_cpc": 1.21, 964 | "date": "2016-01-29T12:04:52", 965 | "frienddomainid": "ab6c2776051fe76199341dc483bc5fcc", 966 | "freeclick": false, 967 | "network": "a", 968 | "PlistaProduct": "Product 10" 969 | }, 970 | { 971 | "a": 269, 972 | "campaignid": "a87d1663da8e21c5d3ce375903fec713", 973 | "userid": "4bfaea115903b4985329fa15850ca2f8", 974 | "camp_cpc": 0.88, 975 | "date": "2016-01-29T12:04:54", 976 | "frienddomainid": "e3187a861177924246b0361f7749bc7a", 977 | "freeclick": false, 978 | "network": "a", 979 | "PlistaProduct": "Product 1" 980 | }, 981 | { 982 | "a": 272, 983 | "campaignid": "c6a56d68185df217c9f8d3871ca69365", 984 | "userid": "2b2e09d68930df4d2d85e33a22177fe2", 985 | "camp_cpc": 0.026, 986 | "date": "2016-01-29T12:04:56", 987 | "frienddomainid": "e1091c68ec533c36a2e255e3a2393a59", 988 | "freeclick": false, 989 | "network": "a", 990 | "PlistaProduct": "Product 1" 991 | }, 992 | { 993 | "a": 273, 994 | "campaignid": "f1471bc23e88cb9d971953f99e020188", 995 | "userid": "474a2410bc7dd669699ce23c180aa6f1", 996 | "camp_cpc": 0.22, 997 | "date": "2016-01-29T12:04:56", 998 | "frienddomainid": "131185c828857c1be44756bd59766432", 999 | "freeclick": false, 1000 | "network": "a", 1001 | "PlistaProduct": "Product 1" 1002 | }, 1003 | { 1004 | "a": 275, 1005 | "campaignid": "4d8ea6a5740800b9423a1bcbe049f19e", 1006 | "userid": "aa513b873a075281bba7463128e74e3b", 1007 | "camp_cpc": 0.77, 1008 | "date": "2016-01-29T12:04:58", 1009 | "frienddomainid": "131185c828857c1be44756bd59766432", 1010 | "freeclick": false, 1011 | "network": "a", 1012 | "PlistaProduct": "Product 1" 1013 | }, 1014 | { 1015 | "a": 279, 1016 | "campaignid": "348e5daeacf16557c8cc671137d4b118", 1017 | "userid": "ec71999d3436fd6ba896dcd324372bfe", 1018 | "camp_cpc": 0.035, 1019 | "date": "2016-01-29T12:05:03", 1020 | "frienddomainid": "31c5624566b5580aa0a734cd9d2f63d1", 1021 | "freeclick": false, 1022 | "network": "a", 1023 | "PlistaProduct": "Product 1" 1024 | }, 1025 | { 1026 | "a": 280, 1027 | "campaignid": "66f1bb9ffc9bc65c12de585662ceed14", 1028 | "userid": "a99d323318335e250966c93f42e23041", 1029 | "camp_cpc": 0.495, 1030 | "date": "2016-01-29T12:05:04", 1031 | "frienddomainid": "94a6cfcd986195c6d7dedc65b53dd94e", 1032 | "freeclick": false, 1033 | "network": "a", 1034 | "PlistaProduct": "Product 1" 1035 | }, 1036 | { 1037 | "a": 281, 1038 | "campaignid": "5ef4dee06fb589b7325ac576079bb9f0", 1039 | "userid": "e3a29e6de5fa319f4dbe729ca6fa9f58", 1040 | "camp_cpc": 0.55, 1041 | "date": "2016-01-29T12:05:06", 1042 | "frienddomainid": "131185c828857c1be44756bd59766432", 1043 | "freeclick": true, 1044 | "network": "a", 1045 | "PlistaProduct": "Product 1" 1046 | }, 1047 | { 1048 | "a": 284, 1049 | "campaignid": "3977b94d96968652d71f8de20673828d", 1050 | "userid": "cf5fee0bcaf9979bc98477d88315ec06", 1051 | "camp_cpc": 0.253, 1052 | "date": "2016-01-29T12:05:13", 1053 | "frienddomainid": "de80eb2279587521fc59210ff024c7a9", 1054 | "freeclick": false, 1055 | "network": "a", 1056 | "PlistaProduct": "Product 1" 1057 | }, 1058 | { 1059 | "a": 287, 1060 | "campaignid": "230cab36e9b7d8047e9771cd5a1b0e6a", 1061 | "userid": "9a2b8c8859868405f3a0e83f78030370", 1062 | "camp_cpc": 0.99, 1063 | "date": "2016-01-29T12:05:14", 1064 | "frienddomainid": "e56e70587da39b4877edd181aa3b1857", 1065 | "freeclick": false, 1066 | "network": "a", 1067 | "PlistaProduct": "Product 1" 1068 | }, 1069 | { 1070 | "a": 292, 1071 | "campaignid": "230cab36e9b7d8047e9771cd5a1b0e6a", 1072 | "userid": "1b5619355ac9dcc1ee76cda9e5575451", 1073 | "camp_cpc": 0.99, 1074 | "date": "2016-01-29T12:05:19", 1075 | "frienddomainid": "7fc91ff77c2d67a6ceb83a4542afb346", 1076 | "freeclick": false, 1077 | "network": "a", 1078 | "PlistaProduct": "Product 1" 1079 | }, 1080 | { 1081 | "a": 298, 1082 | "campaignid": "ecf5208ff66a38a9b607bead79c2152c", 1083 | "userid": "ee9d5224ba81ff06191ef3a1fec9a2cb", 1084 | "camp_cpc": 0.045, 1085 | "date": "2016-01-29T12:05:24", 1086 | "frienddomainid": "827211f592276ddf0f47508c3dcb27a5", 1087 | "freeclick": false, 1088 | "network": "a", 1089 | "PlistaProduct": "Product 1" 1090 | } 1091 | ] 1092 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, for easier debugging, you can ignore zone related error 11 | * stack frames such as `zone.run`/`zoneDelegate.invokeTask` by importing the 12 | * below file. Don't forget to comment it out in production mode 13 | * because it will have a performance impact when errors are thrown 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antekai/ct-pl-editableTable-angular/6afb5f54eb081bb9dca029c42cf911eea7a32211/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EditableTable-Angular 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* allow import .json files */ 2 | declare module '*.json' { 3 | const value: any; 4 | export default value; 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------