Skip to content

Instantly share code, notes, and snippets.

@pedrohugorm
Last active May 3, 2017 01:52
Show Gist options
  • Save pedrohugorm/aa6c5dc75a998aa727992e315d3a8829 to your computer and use it in GitHub Desktop.
Save pedrohugorm/aa6c5dc75a998aa727992e315d3a8829 to your computer and use it in GitHub Desktop.
Typescript Doubly Linked List
interface ISliderDirectiveScope {
readonly count;
register(sliderItem: ISliderItemDirectiveScope);
deregister(sliderItem: ISliderItemDirectiveScope);
next(): ISliderItemDirectiveScope;
prev(): ISliderItemDirectiveScope;
selectedItem(): ISliderItemDirectiveScope;
select(index: number): ISliderItemDirectiveScope;
each(callback: (sliderItem: ISliderItemDirectiveScope) => void);
}
class SliderDirective implements angular.IDirective {
restrict = 'E';
scope: {
};
controller = SliderController;
controllerAs = 'ctrl';
template = <string>require('./pager.html');
bindToController = true;
transclude = true;
link = ($scope: {ctrl: ISliderDirectiveScope} & ng.IScope, $element, $attrs, $ctrl) => {
}
static factory(): any {
return () => new SliderDirective();
}
}
class SliderController implements angular.IController, ISliderDirectiveScope {
private countRegistrations = 0;
list = new DoublyLinkedList<ISliderItemDirectiveScope>();
current: Node<ISliderItemDirectiveScope>;
constructor(){
}
get count(): number {
return this.countRegistrations;
}
register(sliderItem: ISliderItemDirectiveScope){
this.list.append(sliderItem);
this.countRegistrations++;
}
deregister(sliderItem: ISliderItemDirectiveScope){
this.list.popElement(sliderItem);
this.countRegistrations--;
}
next() {
if(!this.current) this.current = this.list.head;
else if(!this.current.next) this.current = this.list.head;
else this.current = this.current.next;
return this.current.data;
}
prev(){
if(!this.current) this.current = this.list.tail;
else if(!this.current.prev) this.current = this.list.tail;
else this.current = this.current.prev;
return this.current.data;
}
each(callback: (sliderItem: ISliderItemDirectiveScope) => void){
return this.list.each(n => callback(n.data));
}
select(index: number){
let result: Node<ISliderItemDirectiveScope> = undefined;
this.list.each((n, i) => {
if(i == index)
result = n;
});
this.current = result;
return result.data;
}
selectedItem(){
if(!this.current)
this.current = this.list.head;
return this.current.data;
}
static $inject: string[] = [
];
}
interface ISliderItemDirectiveScope {
show();
hide();
}
class SliderItemDirective implements angular.IDirective {
require = '^crmSlider';
restrict = 'E';
controller = SliderItemController;
controllerAs = 'ctrl';
scope: {
};
template = <string>require('./pager-page.html');
bindToController = true;
transclude = true;
link = ($scope: {ctrl: ISliderItemDirectiveScope} & ng.IScope, $element, $attrs, $ctrl: ISliderDirectiveScope, transclude: ng.ITranscludeFunction) => {
$ctrl.register($scope.ctrl);
$scope.$on('$destroy', () => $ctrl.deregister($scope.ctrl));
};
static factory(): any {
return () => new SliderItemDirective();
}
}
class SliderItemController implements angular.IDirective, ISliderItemDirectiveScope {
visible: boolean = false;
constructor(){
}
show(){
this.visible = true;
}
hide(){
this.visible = false;
}
static $inject: string[] = [
];
}
class SliderControlsDirective implements angular.IDirective {
require = '^crmSlider';
restrict = 'E';
scope: {
};
controller = SliderControlsController;
controllerAs = 'ctrl';
template = <string>require('./pager-controls.html');
bindToController = true;
link = ($scope: {ctrl: ISliderControlsScope} & ng.IScope, $element, $attrs, $ctrl: ISliderDirectiveScope) => {
$scope.ctrl.slider = $ctrl;
let killWatchFn = $scope.$watch(() => $ctrl.selectedItem(), (item) => {
if(!item) return;
$scope.ctrl.select(0);
killWatchFn();
})
};
static factory(): any {
return () => new SliderControlsDirective();
}
}
interface ISliderControlsScope {
slider: ISliderDirectiveScope;
selectedSliderItem: ISliderItemDirectiveScope;
next(): void;
prev(): void;
select(index: number);
}
class SliderControlsController implements angular.IDirective, ISliderControlsScope {
slider: ISliderDirectiveScope
selectedSliderItem: ISliderItemDirectiveScope;
constructor(){
}
private hideAll(){
this.slider.each(item => item.hide());
}
private updateSelectedItem(){
this.selectedSliderItem = this.slider.selectedItem();
}
next(){
this.hideAll();
this.slider.next().show();
this.updateSelectedItem();
}
prev(){
this.hideAll();
this.slider.prev().show();
this.updateSelectedItem();
}
select(index: number){
this.hideAll();
this.slider.select(index).show();
this.updateSelectedItem();
}
static $inject: string[] = [
];
}
angular.module('app.directives.pager', [])
.directive('crmSlider', SliderDirective.factory())
.directive('crmSliderItem', SliderItemDirective.factory())
.directive('crmSliderControls', SliderControlsDirective.factory());
type NodeCallback<T> = (node: Node<T>) => void;
type NodeIndexedCallback<T> = (node: Node<T>, index: number) => void;
class DoublyLinkedList<T> {
head: Node<T>;
tail: Node<T>;
append(data: T){
if (!this.head) return this.tail = this.head = new Node(data);
else return this.tail = this.tail.next = new Node(data, this.tail);
}
prepend(data: T){
if (!this.tail) return this.tail = this.head = new Node(data);
else return this.head = this.head.prev = new Node(data, null, this.head);
}
popBack(callback: NodeCallback<T>) {
var temp = this.tail;
(this.tail = this.tail.prev).next = null;
if (callback) callback(temp);
}
popFront(callback: NodeCallback<T>) {
var temp = this.head;
(this.head = this.head.next).prev = null;
if (callback) callback(temp);
}
popElement(data: T){
let node = this.find(data);
if(node)
node.prev.next = node.next;
return node;
}
find(data: T){
let current = this.head;
do {
if(current.data != data) continue;
return current;
} while(current.next);
return undefined;
}
each(callback: NodeIndexedCallback<T>){
let current = this.head;
let index = 0;
do {
callback(current, index);
index++;
} while(current = current.next);
}
toString() {
var list = [this.head], current = this.head;
while (current = current.next) list.push(current);
return list.map(n => n.toString()).join(', ');
}
}
class Node<T>{
constructor(
public data: T,
public prev?: Node<T>,
public next?: Node<T>
){}
toString(){
return this.data;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment