<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
    loading...
  </my-app>
  </body>

</html>
h1{text-align:center}
.centered{text-align:center;margin:0 auto}

smart-table{position:relative}
.smart-table-wrap{
    border-right: 1px solid #aaa;
    border-bottom: 1px solid #aaa;
    box-sizing: border-box;
    margin: 0 auto;
    max-width: 100%;
    overflow-x: auto;
    position: relative
}
.smart-table-wrap table{margin:0 auto}
.smart-table-wrap table th{
    background-color:#777;
    color:#fff;
    cursor:default;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    user-select: none;
}
.smart-table-wrap table td{
    border:1px solid #bbb;
    padding:5px;
    white-space: nowrap;
    background-color:#fff
}
.smart-table-wrap table tr.odd td{
    background-color:#eee;
    min-width:50px;
}
.smart-table-wrap .locked-table{border-right: 1px solid #aaa;position:fixed;z-index:2}
.smart-table-wrap .unlocked-table{margin-left:160px}
.smart-table-wrap table th {
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/blue-bar.png);
    background-repeat: no-repeat;
    height: 30px;
    position: relative;
    text-shadow: #012b4d 2px 2px 2px;
    text-align: center;
}
.smart-table-wrap table td img{
    height: 15px;
}
.smart-table-wrap table th.locked .lock{
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/indicators.png);
    background-position: 0 0;
    background-repeat: no-repeat;
    cursor:pointer;
    height:12px;
    position: absolute;
    right: 2px;
    top: 2px;
    width:12px;
}
.smart-table-wrap table th.unlocked .lock{
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/indicators.png);
    background-repeat: no-repeat;
    background-position: -12px 0;
    cursor:pointer;
    height:12px;
    position: absolute;
    right: 2px;
    top: 2px;
    width:12px;
}
.smart-table-wrap th.asc .sort{
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/indicators.png);
    background-repeat: no-repeat;
    width:12px;
    height:12px;
    background-position: -24px 0;
    position: absolute;
    bottom: 2px;
    right: 2px;
}
.smart-table-wrap th.des .sort{
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/indicators.png);
    background-repeat: no-repeat;
    width:12px;
    height:12px;
    background-position: -36px 0;
    position: absolute;
    bottom: 2px;
    right: 2px;
}
.smart-table-wrap th.not .sort{display:none}
.smart-table-wrap td.currency{text-align:right}
.smart-table-wrap th.attention,.smart-table-wrap td.attention{color:#f00}

pagination {
    display: block;
    clear: both;
    min-height: 28px;
    margin: 1px 0;
}
pagination .wrapper {
    box-sizing: border-box;
    font-size: 1em;
    position: relative;
    max-width: 100%;
    margin: 0 auto;
}

pagination .wrapper .next,
pagination .wrapper .prev,
pagination .wrapper .last,
pagination .wrapper .first {
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/blue-bar.png);
    background-repeat: no-repeat;
    border: 1px solid #ddd;
    border-radius: 3px;
    color:#fff;
    cursor:pointer;
    float:right;
    min-width:24px;
    padding: 5px;
    text-shadow: #012b4d 2px 2px 2px;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    user-select: none;
}

pagination .wrapper .current,
pagination .wrapper .reset-size {
    background-image: url(https://raw.githubusercontent.com/msalehisedeh/SmartTable/master/app/blue-bar.png);
    background-repeat: no-repeat;
    border: 1px solid #ddd;
    border-radius: 3px;
    color:#fff;
    min-width:100px;
    float:right;
    padding: 5px;
    text-shadow: #012b4d 2px 2px 2px;
    text-align: center;
}

pagination .wrapper .current input,
pagination .wrapper .reset-size input {
    padding: 0 3px;
    width: 50px;
    height: 14px;    
}
pagination .wrapper .disabled{
    color: #dbdbdb !important;
    opacity: 0.7;
}







### Angular Starter Plunker - Typescript
System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    
    '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
    
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.0.2/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';

platformBrowserDynamic().bootstrapModule(AppModule)

import {
	Component,
	ViewChild,
	ViewContainerRef,
	ElementRef,
	ComponentFactoryResolver,
	NgModule} from "@angular/core";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {BrowserModule} from "@angular/platform-browser";
import {HttpModule} from "@angular/http";
import {Observable} from "rxjs/Observable";

import 'rxjs/add/operator/map'

import {FormatPipe} from './format.pipe';
import {ContentManagementPipe,ContentManagementLangSetup} from './i18n.pipe';
import {SmartTable,SmartTableData,ORDER_BY} from './smart-table.component';
import {Pagination,PaginationInfo} from './pagination.component';


@Component({
    selector:'my-app',
    template: `<div lang="{{pl|isDefaultLanguage}}">
	             <h1>{{"sample.home.title" | i18n:pl}}</h1>
                 <p class="centered">{{"sample.home.description" | i18n:pl}}</p>
                 <p class="centered">{{"sample.home.click.lock" | i18n:pl}}</p>
                 <p class="centered">{{"sample.home.click.drag" | i18n:pl}}</p>
				 <smart-table [content]="tableData" (onbefrepagesizechange)="beforePageSizeChange(input)"  #theTable></smart-table>
				 <br/>
				 <div class="centered">
				 <input id="input" type="checkbox" #input (click)="sctivateReactComparison($event.target.checked)" /><label for="input">{{"sample.checkbox.compare" | i18n:pl}}</label>
			    </div>
			   </div>
			   `
})
export class SmartTableApp {
	@ViewChild('theTable') private theTable: SmartTable;

	private pl = "en_US";
	private tableData:SmartTableData = {};
	private intervals:number[] = [];

	constructor(private resolver:ComponentFactoryResolver){
		let data = [
			[24434,'Joe Black',54000.00,'not listed','cancer','additional information needed|https://www.google.com','lost contact|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24333,'Jeniffer Holand',154000.00,'listed','TB','information supplied|https://www.google.com','contact verified|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Fred Hampton',114000.00,'in transition','brain tumor','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Joe Apaya',104000.00,'listed','legament tumor','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Margaret Hays',164000.00,'in transition','malice','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Olav palyov',414000.00,'listed','delugional','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Mo Thacher',14000.00,'in transition','drunkeness','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24411,'Bob Blake',316000.00,'listed','bladder stone','information forwarded|https://www.google.com','midding contact info|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder'],
			[24545,'Morali Shaban',94000.00,'listed','kidney stone','additional data needed|https://www.google.com','contact verified|https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png','place holder','place holder','place holder','place holder','place holder']
		];
		this.tableData={
			id: "mySmartTable1",
			dragColumns:true,
			showEvenRows:true,
			headers:[
				{id:"col1",name:"id",format:"NUMB:0",lockable:true,sortable:true},
				{id:"col2",name:"name",format:"TEXT:50",lockable:true,sortable:true},
				{id:"col3",name:"income",format:"CURR:$",class:'currency',lockable:true,sortable:true},
				{id:"col4",name:"housing",format:"TEXT:50",class:'attention',sortable:true},
				{id:"col5",name:"illness",format:"TEXT:50",sortable:true},
				{id:"col6",name:"text 1",format:"LINK:_top"},
				{id:"col7",name:"text 2",format:"IMAG:50"},
				{id:"col8",name:"text 3",format:"TEXT:50"},
				{id:"col9",name:"text 4",format:"TEXT:50"},
				{id:"col10",name:"text 5",format:"TEXT:50"},
				{id:"col11",name:"text 6",format:"TEXT:50"},
				{id:"col12",name:"text 7",format:"TEXT:50"}
			],
			rows:data,
			pagination:{pageSize:8,contentSize:data.length,resetSize:true}
		};
	}
	beforePageSizeChange(input:Element){
		if(this.intervals.length){
			//this.sctivateReactComparison(false);
		}
	}
	sctivateReactComparison(checked:boolean){
		// see the perfomance of angular2 is just as good as ReactJS.
		if(checked){
			this.intervals.push(setInterval(()=>this.tableData.rows[0][0]=Math.floor(Math.random()*4000),500));
			this.intervals.push(setInterval(()=>this.tableData.rows[2][0]=Math.floor(Math.random()*5000),600));
			this.intervals.push(setInterval(()=>this.tableData.rows[4][0]=Math.floor(Math.random()*1000),700));
			this.intervals.push(setInterval(()=>this.tableData.rows[6][0]=Math.floor(Math.random()*6000),800));
			this.intervals.push(setInterval(()=>this.tableData.rows[7][0]=Math.floor(Math.random()*6000),800));

			this.intervals.push(setInterval(()=>this.tableData.rows[1][2]=Math.floor(Math.random()*40000),500));
			this.intervals.push(setInterval(()=>this.tableData.rows[3][2]=Math.floor(Math.random()*50000),600));
			this.intervals.push(setInterval(()=>this.tableData.rows[5][2]=Math.floor(Math.random()*10000),700));
			this.intervals.push(setInterval(()=>this.tableData.rows[6][2]=Math.floor(Math.random()*60000),800));
			this.intervals.push(setInterval(()=>this.tableData.rows[7][2]=Math.floor(Math.random()*60000),800));

			this.intervals.push(setInterval(()=>{
				this.tableData.rows[1][3]=Math.floor(Math.random()*10)>5 ? "in transit":"listed";
				this.tableData.rows[3][3]=Math.floor(Math.random()*10)>5 ? "in transit":"listed";
				this.tableData.rows[5][3]=Math.floor(Math.random()*10)>5 ? "in transit":"listed";
				this.tableData.rows[6][3]=Math.floor(Math.random()*10)>5 ? "in transit":"listed";
				this.tableData.rows[7][3]=Math.floor(Math.random()*10)>5 ? "in transit":"listed";
				this.theTable.resetBoundaries();
			},800));
		}else {
			for(let i:number=0;i<this.intervals.length;i++){
				clearInterval(this.intervals[i])
			}
			this.intervals=[];
		}
	}
}

 
@NgModule({
    declarations: [
		SmartTable,
		SmartTableApp,
		FormatPipe,
		Pagination,
		ContentManagementPipe,
		ContentManagementLangSetup
	],
    imports: [BrowserModule,HttpModule],
    bootstrap: [SmartTableApp]
})
export default class AppModule {

}

platformBrowserDynamic().bootstrapModule(AppModule);



import {
	Component,
	ComponentFactory, 
	ReflectiveInjector,
	ViewContainerRef,
	Input,
	Output,
	HostListener,
	EventEmitter,
	ViewChild,
	ElementRef} from "@angular/core";

import {Pagination,PaginationInfo} from './pagination.component';

export enum ORDER_BY {NOT,ASC,DES}

export interface SmartTableHeader {
	id?:string
	name:string,
	class?:string,
	format:string,
	lockable?:boolean,
	locked?:boolean,
	sortable?:boolean,
	sortorder?:ORDER_BY
}
export interface SmartTableData {
	id?:string
	pagination?:PaginationInfo,
	defaultSortIndex?:number,
	headers?:SmartTableHeader[],
	dragColumns?:boolean,
	showEvenRows?:boolean,
	rows?:any[]
}
export interface Pagination {
	from?:number
	to?:number,
	pageSize?:number,
	currentPage?:number
}

@Component({
    selector:'smart-table',
    template: `
		<pagination [info]="this.content.pagination" 
			(onready)="readyPagination($event)" (onchange)="updatePagination($event)" #pagination></pagination>
		<div class="smart-table-wrap">
		<table id="content.id" cellpadding="0" cellspacing="0" class="locked-table" #lockedTable>
	      <tbody>
		  <tr>
		   <template ngFor let-header [ngForOf]="content.headers">
		     <th *ngIf="header.locked" id="{{header.id}}" class="{{header.class}}"
			 	(click)="sort(header.id)"
				[class.asc]="header.sortable && header.sortorder==orderASC"
				[class.des]="header.sortable && header.sortorder==orderDES"
				[class.not]="header.sortable && header.sortorder==orderNOT"
			 	[class.locked]="header.lockable">
				 {{header.name}}
				 <a class="lock" (click)="lock(header.id,false)"></a>
				 <span class="sort"></span>
			 </th>
           </template>
		  </tr>
		  <template ngFor let-row [ngForOf]="content.rows" let-j="index">
		  <tr *ngIf="!content.pagination || (j>=content.pagination.from && j<=content.pagination.to)" [class.odd]="content.showEvenRows && j%2">
		   <template ngFor let-col [ngForOf]="row" let-i="index">
		    <td class="{{content.headers[i].class}}" *ngIf="content.headers[i].locked"><span [outerHTML]="col | format:content.headers[i].format"></span></td>
           </template>
		  </tr>
		  </template>
		  </tbody>
	    </table>
		<table id="content.id" cellpadding="0" cellspacing="0" class="unlocked-table" #unlockedTable>
	      <tbody>
		  <tr>
		   <template ngFor let-header [ngForOf]="content.headers">
		     <th *ngIf="!header.locked" id="{{header.id}}" class="{{header.class}}" 
			 	(click)="sort(header.id)"
				[class.asc]="header.sortable && header.sortorder==orderASC"
				[class.des]="header.sortable && header.sortorder==orderDES"
				[class.not]="header.sortable && header.sortorder==orderNOT"
			 	[class.unlocked]="header.lockable">
				 {{header.name}}
				 <a class="lock" (click)="lock(header.id,true)"></a>
				 <span class="sort"></span>
			 </th>
           </template>
		  </tr>
		  <template ngFor let-row [ngForOf]="content.rows" let-j="index">
		  <tr *ngIf="!content.pagination || (j>=content.pagination.from && j<=content.pagination.to)" [class.odd]="content.showEvenRows && j%2">
		   <template ngFor let-col [ngForOf]="row" let-i="index">
		    <td class="{{content.headers[i].class}}" *ngIf="!content.headers[i].locked"><span [outerHTML]="col | format:content.headers[i].format"></span></td>
           </template>
		  </tr>
		  </template>
		  </tbody>
	    </table>
		</div>`
})
export class SmartTable {
	@ViewChild('lockedTable', {read: ViewContainerRef}) private lockedTable: ViewContainerRef;
	@ViewChild('unlockedTable', {read: ViewContainerRef}) private unlockedTable: ViewContainerRef;
	@ViewChild('pagination') private pagination: Pagination;

	@Input("content")
	public content:SmartTableData ={};
	
	@Output('onbefrepagesizechange')
	private befrepagesizechange = new EventEmitter();

	private el:HTMLElement;
	private orderNOT:ORDER_BY = ORDER_BY.NOT;
	private orderASC:ORDER_BY = ORDER_BY.ASC;
	private orderDES:ORDER_BY = ORDER_BY.DES;

	constructor(el: ElementRef) {
		this.el = el.nativeElement;
    }

	@HostListener("window:scroll", [])
	onWindowScroll() {
		let lt = this.lockedTable.element.nativeElement;
		let ut = this.unlockedTable.element.nativeElement;
		let rect = ut.parentNode.getBoundingClientRect();
		lt.style.top = rect.top+"px";
		lt.style.left = (rect.left+2)+"px";
	}
	
	ngOnChanges(changes:any) {
		if(!this.content.defaultSortIndex){
			for(let i=0;i<this.content.headers.length;i++){
				let h = this.content.headers[i];
				if(h.sortable){
					this.content.defaultSortIndex = i;
					this.sort(h.id);
					break;
				}
			}
		}else {
			this.sort(this.content.headers[this.content.defaultSortIndex].id);
		}
		setTimeout(()=>this.calculateBoundaries(),20);
 		if(this.content.dragColumns){
			setTimeout(()=>this.enableDragging(),40);
		}
   }

	enableDragging(){
		let list = this.unlockedTable.element.nativeElement.children[0].children[0].children;
		for(let i=0;i<list.length;i++){
			this.registerDargEvents(list[i]);
		}
	}

	findColumnWithID(id:string){
		let list = this.unlockedTable.element.nativeElement.children[0].children[0].children;
		let column = null;
		for(let i=0;i<list.length;i++){
			if(list[i].getAttribute("id")==id){
				column = list[id];
				break;
			}
		}
		return column;
	}

	registerDargEvents(column:any){
		column.draggable = 'true';
		column.addEventListener('dragstart', this.dragStart);
		column.addEventListener('dragover', this.dragOver);
		column.parentNode.addEventListener('dragover', this.dragOver);
		column.addEventListener('dragend', this.dragEnd);	
		column.addEventListener('drop', this.dragDropped.bind(this));		  
	}
	unRegisterDargEvents(column:any){
		column.removeEventListener('dragstart', this.dragStart);
		column.removeEventListener('dragover', this.dragOver);
		column.parentNode.removeEventListener('dragover', this.dragOver);
		column.removeEventListener('dragend', this.dragEnd);	
		column.removeEventListener('drop', this.dragDropped);		  
	}
	dragStart(e:any){
		e.dataTransfer.setData('id', e.target.getAttribute("id"));
		e.dataTransfer.effectAllowed = 'move';
		return false;
	}
	dragOver(e:any){
		if (e.preventDefault) e.preventDefault();
		return false;
	}
	dragEnd(e:any){
		e.preventDefault();
	}
	dragDropped(e:any){
		if (e.stopPropagation) {
			e.stopPropagation(); // Stops some browsers from redirecting.
		}
		let sourceID = e.dataTransfer.getData('id');
		let destinationID = e.target.getAttribute("id");
		this.swapColumns(sourceID, destinationID);
		return false;
	}
	swapColumns(sourceID:string, destinationID:string){
		let srcIndex = this.getColumnIndex(sourceID);
		let desIndex = this.getColumnIndex(destinationID);
		let sobj = this.content.headers[srcIndex];
		this.content.headers[srcIndex] = this.content.headers[desIndex];
		this.content.headers[desIndex] = sobj;

		for(let i = 0;i<this.content.rows.length;i++){
			let row = this.content.rows[i];
			let sobj = row[srcIndex];
			row[srcIndex] = row[desIndex];
			row[desIndex] = sobj;
		}
	}
	getColumnIndex(id:string){
		let index = -1;
		for(let i = 0;i<this.content.headers.length;i++){
			if(this.content.headers[i].id==id){
				index = i;
				break;
			}
		}
		return index;
	}

	calculateBoundaries(){
		let lt = this.lockedTable.element.nativeElement;
		let ut = this.unlockedTable.element.nativeElement;
		let wrapper = ut.parentNode;
		let lrect = lt.getBoundingClientRect();
		let urect = ut.getBoundingClientRect();
		ut.style.marginLeft = (lrect.width-1)+"px";
		wrapper.style.width = ( urect.width+(lrect.width>10 ? lrect.width-7: lrect.width))+"px";
	}

	lock(id:string, flag:boolean){
		for(let i=0;i<this.content.headers.length;i++){
			let h = this.content.headers[i];
			if(h.id == id){
				if(flag){
					this.unRegisterDargEvents(this.findColumnWithID(id));
				}
				h.locked=flag;
				setTimeout(()=>{
					this.calculateBoundaries()
					if(!flag){
						this.registerDargEvents(this.findColumnWithID(id));
					}
				},20);
			}
		}
		setTimeout(()=>this.resetBoundaries(),2);
	}

	public resetBoundaries(){
		this.calculateBoundaries();
		this.readyPagination("");
	}

	sort(id:string){
		for(let i=0;i<this.content.headers.length;i++){
			let h = this.content.headers[i];
			if(h.id == id && h.sortable){
				if(h.sortorder===ORDER_BY.NOT || h.sortorder===ORDER_BY.ASC){
					h.sortorder=ORDER_BY.DES;
				}else {
					h.sortorder=ORDER_BY.ASC;
				}
				this.content.rows.sort((a,b)=>{
					if(h.sortorder==ORDER_BY.ASC){
					return a[i]>b[i] ? 1:-1;
					}
					return a[i]<b[i] ? 1:-1;
				});
				this.content.defaultSortIndex = i;
			}else {
				h.sortorder=ORDER_BY.NOT;
			}
		}
		setTimeout(()=>{
			this.calculateBoundaries();
			this.readyPagination("");
		},2);
	}

	readyPagination($event:any){
		let lt = this.lockedTable.element.nativeElement;
		let ut = this.unlockedTable.element.nativeElement;
		let wrapper = ut.parentNode;
		let lrect = lt.getBoundingClientRect();
		let urect = ut.getBoundingClientRect();

		this.pagination.setWidth( urect.width+(lrect.width>10 ? lrect.width-7: lrect.width));
	}
	updatePagination($event:any){
		setTimeout(()=>{
			this.befrepagesizechange.emit();
			this.resetBoundaries();
		},10);
	}
}




import {
	Component,
	ComponentFactory, 
	ReflectiveInjector,
	ViewContainerRef,
	Input,
	Output,
	HostListener,
	EventEmitter,
	ViewChild,
	ElementRef} from "@angular/core";


export interface PaginationInfo {
	contentSize:number,
	pageSize:number,
    maxWidth?:string,
	pages?:number,
	from?:number,
	to?:number,
	currentPage?:number,
    resetSize?:boolean
}

@Component({
    selector:'pagination',
    template: `
		<div *ngIf="canDisplayPagination" [style.width]="info.maxWidth" class="wrapper" #paginationWrapper>
        <div class="reset-size" *ngIf="info.resetSize">Page size: <input [value]="info.pageSize" (keydown.Enter)="changeSize(sizer)" #sizer/></div>
        <div class="last" (click)="selectLast()" [class.disabled]="info.currentPage==info.pages">Last</div>
        <div class="next" (click)="selectNext()" [class.disabled]="info.currentPage==info.pages">Next</div>
        <div class="current">page <input [value]="info.currentPage" (keydown.Enter)="changeCurrent(ranger)" #ranger/> of {{info.pages}}</div>
        <div class="prev" (click)="selectPrev()" [class.disabled]="info.currentPage==1">Previous</div>
		<div class="first" (click)="selectFirst()" [class.disabled]="info.currentPage==1">First</div>
		</div>`
})
export class Pagination {
	@ViewChild('paginationWrapper') private pager: Pagination;
	@ViewChild('paginationWrapper', {read: ViewContainerRef}) private root: ViewContainerRef;

    private canDisplayPagination:boolean = false;

	@Input("info")
    private info:PaginationInfo ={contentSize:0,pageSize:0,maxWidth:"0"};
    
	@Output('onchange')
	private change = new EventEmitter();
	
    @Output('onready')
	private ready = new EventEmitter();

	
	private el:HTMLElement;

	constructor(el: ElementRef) {
		this.el = el.nativeElement;
    }
	
	ngOnChanges(changes:any) {
		if(this.info && this.info.contentSize && this.info.pageSize){
			this.info.pages = Math.ceil(this.info.contentSize/this.info.pageSize);
            if(this.info.pages>1){
                this.canDisplayPagination = true;
            }
			this.info.from = 0;
			this.info.to = this.info.pageSize-1;
			this.info.currentPage = 1;
		    setTimeout(()=>this.onready(),20);
		}
    }

    public setWidth(width:number){
        this.info.maxWidth=width+"px";
    }

    onready(){this.ready.emit(this);this.change.emit(this.info);}
    selectFirst(){
        if(this.info.currentPage>1){
		    this.info.from = 0;
		    this.info.to = this.info.from+this.info.pageSize-1;
		    this.info.currentPage = 1;
            this.change.emit(this.info);
        }
   }
    selectNext(){
        if(this.info.currentPage<this.info.pages){
 		this.info.from = this.info.to+1;
		this.info.to = this.info.from+this.info.pageSize-1;
		this.info.currentPage++;
        this.change.emit(this.info);
        }
    }
    selectPrev(){
        if(this.info.currentPage>1){
 		    this.info.from -= this.info.pageSize;
		    this.info.to = this.info.from+this.info.pageSize-1;
		    this.info.currentPage--;
            this.change.emit(this.info);
        }
    }
    selectLast(){
        if(this.info.currentPage<this.info.pages){
		    this.info.from = this.info.pageSize*(this.info.pages-1);
		    this.info.to = this.info.from+this.info.pageSize-1;
		    this.info.currentPage = this.info.pages;
            this.change.emit(this.info);
        }
    }
    changeCurrent(ranger:any){
        let v = parseInt(ranger.value);
        if(this.info.currentPage<v && v>0 && v<this.info.pages){
		    this.info.from = v*(this.info.pageSize-1);
		    this.info.to = this.info.from+this.info.pageSize-1;
		    this.info.currentPage = v;
            this.change.emit(this.info);
        }else {
            ranger.value = this.info.currentPage;
        }
    }
    changeSize(sizer:any){
        let v = parseInt(sizer.value);
        if(this.info.contentSize>=v && v>1){
            this.info.pageSize = v;
 			this.info.pages = Math.ceil(this.info.contentSize/v);
            this.info.from = 0;
			this.info.to = this.info.pageSize-1;
			this.info.currentPage = 1;
            this.change.emit(this.info);
        }else {
            sizer.value = this.info.pageSize;
        }
    }
}



import { Pipe, PipeTransform } from '@angular/core';

var preferredLanguage="en_US";

const keys = {
  "en_US": {
      "sample.home.title": "Smart Table!!",
      "sample.home.description":  "Angular 2 fully configurable Smart Table with pagination.",
      "sample.home.click.lock":  "Click on lock icons to unlock/lock columns",
      "sample.home.click.drag":  "Drag unlocked columns to re-order them",
      "sample.checkbox.compare": "Activate table updater and see Angular2 is as fast as React"
  }
};

// need to make the calls everytime language is changed.
// setting pure to false will create a performance problem.
// the best way is to pass in the preffered language.
@Pipe({name:'i18n'})
export class ContentManagementPipe implements PipeTransform{
  transform(messageKey: string,args?:any[],lang?:string): any {
      return ContentManagementPipe.i18n(messageKey,args,lang);
  }

  static i18n(messageKey: string,args?:any[],lang?:string): any {
      let m = keys[lang ? lang:preferredLanguage][messageKey];
      if(m && args && args.length){
          for(let i=0;i<args.length;i++){m = m.replace("\{"+i+"\}",args[i])}
      }
      return (m ? m : messageKey);
  }
}

@Pipe({name:'isDefaultLanguage'})
export class ContentManagementLangSetup implements PipeTransform{
  transform(langKey: string): any {
      if(langKey && langKey.length==5 && langKey.indexOf("_")==2){
          preferredLanguage = langKey;
      }
      return preferredLanguage;
  }
}
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name:'format'})
export class FormatPipe implements PipeTransform{
  transform(message:any,type:string): any {
    let list = type.split(":");
    switch(list[0]){
      case "CURR" : return this.toCurrency(message,list);
      case "NUMB" : return this.toNumber(message,list);
      case "DATE" : return this.toDate(message,list);
      case "LINK" : return this.toLink(message,list);
      case "IMAG" : return this.toImage(message,list);
      default: return message;
    }
      
  }

  toImage(message:any,list:string[]){
    let text = message.split("|");
    let src = text.length>1 ? text[1]:text[0];
    let alt = text.length>1 ? text[0]:"";

    return "<img src='"+src+"' alt='"+alt+"' />";
  }
  toLink(message:any,list:string[]){
    let target = list.length && list[1].length ? list[1] : "";
    let text = message.split("|");
    let src = text.length>1 ? text[1]:text[0];
    let title = text.length>1 ? text[0]:"";
    return "<a href='"+src+"' target='"+target+"'>"+title+"</a>";
  }
  toDate(message:any,list:string[]){
    let format = list.length && list[1].length ? list[1] : "MM/dd/YYYY";
    let date = new Date(message);
    return message;//moment().format(format);
  }
  toNumber(message:any,list:string[]){
    let pre = list.length && list[1].length ? parseInt(list[1]) : 0;
    return message.toFixed(pre).replace(/./g, function(c:any, i:any, a:any) {
                return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
            });
  }
  toCurrency(message:any,list:string[]){
    return list[1]+message.toFixed(2).replace(/./g, function(c:any, i:any, a:any) {
                return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
            });
  }
}