diff --git a/app/TypeScript/README.md b/app/TypeScript/README.md
new file mode 100644
index 0000000..cc380b7
--- /dev/null
+++ b/app/TypeScript/README.md
@@ -0,0 +1 @@
+TODO: Add TS instructions
diff --git a/app/TypeScript/angular-ui-tour-backdrop.ts b/app/TypeScript/angular-ui-tour-backdrop.ts
new file mode 100644
index 0000000..4e7bdc3
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-backdrop.ts
@@ -0,0 +1,206 @@
+module Tour {
+    export class TourBackdrop {
+        private $body: ng.IRootElementService;
+        private viewWindow: { top: ng.IRootElementService, bottom: ng.IRootElementService, left: ng.IRootElementService, right: ng.IRootElementService, target: ng.IRootElementService };
+
+        private preventDefault(e) {
+            e.preventDefault();
+        }
+
+        private preventScrolling() {
+            this.$body.addClass('no-scrolling');
+            this.$body.on('touchmove', this.preventDefault);
+        }
+
+        private allowScrolling() {
+            this.$body.removeClass('no-scrolling');
+            this.$body.off('touchmove', this.preventDefault);
+        }
+
+        private createNoScrollingClass() {
+            var name = '.no-scrolling',
+                rules = 'height: 100%; overflow: hidden;',
+                style = document.createElement('style');
+            style.type = 'text/css';
+            document.getElementsByTagName('head')[0].appendChild(style);
+
+            if (!style.sheet && !(<any>style.sheet).insertRule) {
+                ((<any>style).styleSheet || style.sheet).addRule(name, rules);
+            } else {
+                (<any>style.sheet).insertRule(name + '{' + rules + '}', 0);
+            }
+        }
+
+        private createBackdropComponent(backdrop) {
+            backdrop.addClass('tour-backdrop').addClass('not-shown').css({
+                //display: 'none',
+                zIndex: this.TourConfig.get('backdropZIndex')
+            });
+            this.$body.append(backdrop);
+        }
+
+        private showBackdrop() {
+            this.viewWindow.top.removeClass('hidden');
+            this.viewWindow.bottom.removeClass('hidden');
+            this.viewWindow.left.removeClass('hidden');
+            this.viewWindow.right.removeClass('hidden');
+
+            setTimeout(() => {
+                this.viewWindow.top.removeClass('not-shown');
+                this.viewWindow.bottom.removeClass('not-shown');
+                this.viewWindow.left.removeClass('not-shown');
+                this.viewWindow.right.removeClass('not-shown');
+            }, 33);
+        }
+
+        private hideBackdrop() {
+            this.viewWindow.top.addClass('not-shown');
+            this.viewWindow.bottom.addClass('not-shown');
+            this.viewWindow.left.addClass('not-shown');
+            this.viewWindow.right.addClass('not-shown');
+            this.hideTarget();
+
+            setTimeout(() => {
+                this.viewWindow.top.addClass('hidden');
+                this.viewWindow.bottom.addClass('hidden');
+                this.viewWindow.left.addClass('hidden');
+                this.viewWindow.right.addClass('hidden');
+            }, 250);
+        }
+
+        createForElement(element: ng.IRootElementService, shouldPreventScrolling: boolean, isFixedElement: boolean, padding: IPadding) {
+            var position,
+                viewportPosition,
+                bodyPosition;
+
+            if (shouldPreventScrolling) {
+                this.preventScrolling();
+            }
+
+            position = this.$uibPosition.offset(element);
+            viewportPosition = this.$uibPosition.viewportOffset(element);
+            bodyPosition = this.$uibPosition.offset(this.$body);
+
+            if (isFixedElement) {
+                angular.extend(position, viewportPosition);
+            }
+
+            padding = this._processPadding(padding);
+
+            var pTop = Math.floor(position.top) - padding.top;
+            var pLeft = Math.floor(position.left) - padding.left;
+            var pHeight = Math.floor(position.height) + padding.top + padding.bottom;
+            var pWidth = Math.floor(position.width) + padding.left + padding.right;
+
+            var bTop = Math.floor(bodyPosition.top);
+            var bLeft = Math.floor(bodyPosition.left);
+            var bHeight = Math.floor(bodyPosition.height);
+            var bWidth = Math.floor(bodyPosition.width);
+
+            this.viewWindow.top.css({
+                position: isFixedElement ? 'fixed' : 'absolute',
+                top: 0,
+                left: 0,
+                width: '100%',
+                height: (pTop) + 'px'
+            });
+            this.viewWindow.bottom.css({
+                position: isFixedElement ? 'fixed' : 'absolute',
+                left: 0,
+                width: '100%',
+                height: (bTop + bHeight - pTop - pHeight) + 'px',
+                top: (pTop + pHeight) + 'px'
+            });
+            this.viewWindow.left.css({
+                position: isFixedElement ? 'fixed' : 'absolute',
+                left: 0,
+                top: pTop + 'px',
+                width: (pLeft) + 'px',
+                height: pHeight + 'px'
+            });
+            this.viewWindow.right.css({
+                position: isFixedElement ? 'fixed' : 'absolute',
+                top: pTop + 'px',
+                width: (bLeft + bWidth - pLeft - pWidth) + 'px',
+                height: pHeight + 'px',
+                left: (pLeft + pWidth) + 'px'
+            });
+            this.viewWindow.target.css({
+                position: isFixedElement ? 'fixed' : 'absolute',
+                top: pTop + 'px',
+                width: pWidth + 'px',
+                height: pHeight + 'px',
+                left: pLeft + 'px'
+            });
+
+            this.showBackdrop();
+
+            if (shouldPreventScrolling) {
+                this.preventScrolling();
+            }
+        }
+
+        hide() {
+            this.hideBackdrop();
+            this.hideTarget();
+            this.allowScrolling();
+        }
+
+        hideTarget(removeNotShow = true) {
+            this.viewWindow.target.addClass('not-shown');
+
+            if (!removeNotShow)
+                return;
+
+            setTimeout(() => {
+                this.viewWindow.target.addClass('hidden');
+            }, 250);
+        }
+
+        showTarget() {
+            this.viewWindow.target.removeClass('hidden');
+
+            setTimeout(() => {
+                this.viewWindow.target.removeClass('not-shown');
+            }, 33);
+        }
+
+        constructor(private TourConfig: ITourConfig, private $document: ng.IDocumentService, private $uibPosition: angular.ui.bootstrap.IPositionService, private $window: ng.IWindowService) {
+            var service = this;
+            var document = <HTMLDocument>(<any>$document[0])
+            this.$body = angular.element(document.body);
+            this.viewWindow = {
+                top: angular.element(document.createElement('div')),
+                bottom: angular.element(document.createElement('div')),
+                left: angular.element(document.createElement('div')),
+                right: angular.element(document.createElement('div')),
+                target: angular.element(document.createElement('div'))
+            }
+
+            this.createNoScrollingClass();
+
+            this.createBackdropComponent(this.viewWindow.top);
+            this.createBackdropComponent(this.viewWindow.bottom);
+            this.createBackdropComponent(this.viewWindow.left);
+            this.createBackdropComponent(this.viewWindow.right);
+            this.createBackdropComponent(this.viewWindow.target);
+
+        }
+
+        static factory(TourConfig: ITourConfig, $document: ng.IDocumentService, $uibPosition: angular.ui.bootstrap.IPositionService, $window: ng.IWindowService) {
+            return new TourBackdrop(TourConfig, $document, $uibPosition, $window);
+        }
+
+        _processPadding(padding: IPadding) {
+            if (!padding)
+                padding = { top: 0, left: 0, right: 0, bottom: 0 }
+
+            padding.top = padding.top || 0;
+            padding.left = padding.left || 0;
+            padding.right = padding.right || 0;
+            padding.bottom = padding.bottom || 0;
+
+            return padding;
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-config-provider.ts b/app/TypeScript/angular-ui-tour-config-provider.ts
new file mode 100644
index 0000000..839a3fc
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-config-provider.ts
@@ -0,0 +1,57 @@
+module Tour {
+    export class TourConfigProvider {
+        config: ITourConfigProperties = {
+            placement: 'top',
+            animation: true,
+            popupDelay: 1,
+            closePopupDelay: 0,
+            enable: true,
+            appendToBody: false,
+            popupClass: '',
+            orphan: false,
+            backdrop: false,
+            backdropZIndex: 10000,
+            scrollOffset: 100,
+            scrollIntoView: true,
+            useUiRouter: false,
+            useHotkeys: false,
+
+            onStart: null,
+            onEnd: null,
+            onPause: null,
+            onResume: null,
+            onNext: null,
+            onPrev: null,
+            onShow: null,
+            onShown: null,
+            onHide: null,
+            onHidden: null
+        };
+
+        $get: [string, ($q: ng.IQService) => ITourConfig];
+
+        set(option, value) {
+            this.config[option] = value;
+        }
+        constructor() {
+            this.$get = ['$q', ($q) => {
+                angular.forEach(this.config, function (value, key) {
+                    if (key.indexOf('on') === 0 && angular.isFunction(value)) {
+                        this.config[key] = function () {
+                            return $q.resolve(value());
+                        };
+                    }
+                });
+
+                return {
+                    get: (option) => {
+                        return this.config[option];
+                    },
+                    getAll: () => {
+                        return angular.copy(this.config);
+                    }
+                };
+            }];
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-controller.ts b/app/TypeScript/angular-ui-tour-controller.ts
new file mode 100644
index 0000000..f1676ca
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-controller.ts
@@ -0,0 +1,570 @@
+module Tour {
+    export class TourController {
+        stepList: Array<ITourStep>
+        currentStep: ITourStep
+        resumeWhenFound: (step: ITourStep) => void;
+        tourStatus: number;
+        options: ITourConfigProperties;
+        initialized: boolean;
+        emit: (string, any?) => any;
+        once: (string, fn: () => void) => any;
+
+        statuses = {
+            OFF: 0,
+            ON: 1,
+            PAUSED: 2
+        }
+
+        constructor(private $timeout: ng.ITimeoutService, private $q: ng.IQService, private $filter: ng.IFilterService, TourConfig: ITourConfig, private uiTourBackdrop: TourBackdrop, private uiTourService: uiTourService, private EventEmitter, private hotkeys) {
+            this.tourStatus = this.statuses.OFF;
+            this.options = TourConfig.getAll();
+            this.stepList = [];
+            EventEmitter.mixin(this);
+        }
+
+        /**
+         * Closer to $evalAsync, just resolves a promise
+         * after the next digest cycle
+         *
+         * @returns {Promise}
+         */
+        digest() {
+            return this.$q((resolve) => {
+                this.$timeout(resolve);
+            });
+        }
+
+        /**
+         * return current step or null
+         * @returns {step}
+         */
+        getCurrentStep() {
+            return this.currentStep;
+        }
+
+        /**
+         * set the current step (doesnt do anything else)
+         * @param {step} step Current step
+         */
+        setCurrentStep(step) {
+            this.currentStep = step;
+        }
+
+        /**
+         * gets a step relative to current step
+         *
+         * @param {number} offset Positive integer to search right, negative to search left
+         * @returns {step}
+         */
+        getStepByOffset(offset) {
+            if (!this.getCurrentStep()) {
+                return null;
+            }
+            return this.stepList[this.stepList.indexOf(this.getCurrentStep()) + offset];
+        }
+
+        /**
+         * retrieves a step (if it exists in the step list) by index, ID, or identity
+         * Note: I realize ID is short for identity, but ID is really the step name here
+         *
+         * @param {string | number | step} stepOrStepIdOrIndex Step to retrieve
+         * @returns {step}
+         */
+        getStep(stepOrStepIdOrIndex) {
+            //index
+            if (angular.isNumber(stepOrStepIdOrIndex)) {
+                return this.stepList[stepOrStepIdOrIndex];
+            }
+
+            //ID string
+            if (angular.isString(stepOrStepIdOrIndex)) {
+                return this.stepList.filter((step) => step.stepId === stepOrStepIdOrIndex)[0];
+            }
+
+            //object
+            if (angular.isObject(stepOrStepIdOrIndex)) {
+                //step identity
+                if (~this.stepList.indexOf(stepOrStepIdOrIndex)) {
+                    return stepOrStepIdOrIndex;
+                }
+
+                //step copy
+                if (stepOrStepIdOrIndex.stepId) {
+                    return this.stepList.filter((step) => step.stepId === stepOrStepIdOrIndex.stepId)[0];
+                }
+            }
+
+            return null;
+        }
+
+        /**
+         * return next step or null
+         * @returns {step}
+         */
+        getNextStep() {
+            return this.getStepByOffset(+1);
+        }
+
+        /**
+         * return previous step or null
+         * @returns {step}
+         */
+        getPrevStep() {
+            return this.getStepByOffset(-1);
+        }
+
+        /**
+        * is there a next step
+        *
+        * @returns {boolean}
+        */
+        isNext() {
+            return !!(this.getNextStep() || this.getCurrentStep().nextPath);
+        }
+
+        /**
+         * is there a previous step
+         *
+         * @returns {boolean}
+         */
+        isPrev() {
+            return !!(this.getPrevStep() || this.getCurrentStep().prevPath);
+        }
+
+        /**
+         * Used by showStep and hideStep to trigger popover events
+         *
+         * @param step
+         * @param eventName
+         * @returns {*}
+         */
+        dispatchEvent(step, eventName) {
+            return this.$q((resolve) => {
+                step.element[0].dispatchEvent(new CustomEvent(eventName));
+                resolve();
+            });
+        }
+
+        /**
+         * A safe way to invoke a possibly null event handler
+         *
+         * @param handler
+         * @returns {*}
+         */
+        handleEvent(handler) {
+            return (handler || this.$q.resolve)();
+        }
+
+        /**
+         * Configures hot keys for controlling the tour with the keyboard
+         */
+        setHotKeys() {
+            this.hotkeys.add({
+                combo: 'esc',
+                description: 'End tour',
+                callback: () => {
+                    this.end();
+                }
+            });
+
+            this.hotkeys.add({
+                combo: 'right',
+                description: 'Go to next step',
+                callback: () => {
+                    if (this.isNext()) {
+                        this.next();
+                    }
+                }
+            });
+
+            this.hotkeys.add({
+                combo: 'left',
+                description: 'Go to previous step',
+                callback: () => {
+                    if (this.isPrev()) {
+                        this.prev();
+                    }
+                }
+            });
+        }
+
+        /**
+         * Turns off hot keys for when the tour isn't running
+         */
+        unsetHotKeys() {
+            this.hotkeys.del('esc');
+            this.hotkeys.del('right');
+            this.hotkeys.del('left');
+        }
+
+        //---------------- Protected API -------------------
+        /**
+         * Adds a step to the tour in order
+         *
+         * @param {object} step
+         */
+        addStep(step: ITourStep) {
+            if (~this.stepList.indexOf(step)) {
+                return;
+            }
+            this.stepList.push(step);
+            this.stepList = this.$filter('orderBy')(this.stepList, 'order');
+            this.emit('stepAdded', step);
+            if (this.resumeWhenFound) {
+                this.resumeWhenFound(step);
+            }
+        }
+
+        /**
+         * Removes a step from the tour
+         *
+         * @param step
+         */
+        removeStep(step: ITourStep) {
+            this.stepList.splice(this.stepList.indexOf(step), 1);
+            this.emit('stepRemoved', step);
+        }
+
+        /**
+         * if a step's order was changed, replace it in the list
+         * @param step
+         */
+        reorderStep(step: ITourStep) {
+            this.stepList = this.$filter('orderBy')(this.stepList, 'order');
+            this.emit('stepsReordered', step);
+        }
+
+        /**
+         * Checks to see if a step exists by ID, index, or identity
+         *
+         * @protected
+         * @param {string | number | step} stepOrStepIdOrIndex Step to check
+         * @returns {boolean}
+         */
+        protected hasStep(stepOrStepIdOrIndex) {
+            return !!this.getStep(stepOrStepIdOrIndex);
+        };
+
+        /**
+         * show supplied step
+         * @param step
+         * @returns {promise}
+         */
+        protected showStep(step: ITourStep) {
+            if (!step) {
+                return this.$q.reject('No step.');
+            }
+
+            return this.handleEvent(step.config('onShow')).then(() => {
+
+                if (!step.config('backdrop')) {
+                    return;
+                }
+
+                var delay = step.config('popupDelay');
+                if (step.config('animation') && delay < 100)
+                    delay = 250
+
+                return this.$q((resolve) => {
+                    this.$timeout(() => {
+                        this.uiTourBackdrop.createForElement(step.element, step.config('preventScrolling'), step.config('fixed'), step.config('backdropPadding'));
+                        this.uiTourBackdrop.hideTarget(false);
+                        resolve();
+                    }, delay);
+                })
+            }).then(() => {
+
+                step.element.addClass('ui-tour-active-step');
+                return this.dispatchEvent(step, 'uiTourShow');
+
+            }).then(() => {
+
+                return this.digest();
+
+            }).then(() => {
+
+                return this.handleEvent(step.config('onShown'));
+
+            }).then(() => {
+
+                this.emit('stepShown', step);
+                step.isNext = this.isNext();
+                step.isPrev = this.isPrev();
+
+            });
+        }
+
+        /**
+         * hides the supplied step
+         * @param step
+         * @returns {promise}
+         */
+        protected hideStep(step: ITourStep) {
+            if (!step) {
+                return this.$q.reject('No step.');
+            }
+
+            return this.handleEvent(step.config('onHide')).then(() => {
+
+                step.element.removeClass('ui-tour-active-step');
+                return this.dispatchEvent(step, 'uiTourHide');
+
+            }).then(() => {
+
+                return this.digest();
+
+            }).then(() => {
+
+                return this.handleEvent(step.config('onHidden'));
+
+            }).then(() => {
+
+                this.emit('stepHidden', step);
+
+            });
+        }
+
+        //------------------ end Protected API ------------------
+
+
+        //------------------ Public API ------------------
+
+        /**
+         * Returns the value for specified option
+         *
+         * @protected
+         * @param {string} option Name of option
+         * @returns {*}
+         */
+        public config(option) {
+            return this.options[option];
+        }
+
+        /**
+         * Tells the tour to pause while ngView loads
+         *
+         * @param waitForStep
+         */
+        waitFor(waitForStep) {
+            this.pause();
+            this.resumeWhenFound = (step) => {
+                if (step.stepId === waitForStep) {
+                    this.currentStep = this.stepList[this.stepList.indexOf(step)];
+                    this.resume();
+                    this.resumeWhenFound = null;
+                }
+            };
+        }
+
+        /**
+         * pass options from directive
+         * @param opts
+         */
+        init(opts) {
+            this.options = angular.extend(this.options, opts);
+            this.uiTourService._registerTour(this);
+            this.initialized = true;
+            this.emit('initialized');
+            return this;
+        }
+
+        /**
+         * Unregisters with the tour service when tour is destroyed
+         *
+         * @protected
+         */
+
+        destroy() {
+            this.uiTourService._unregisterTour(self);
+        }
+
+        /**
+         * starts the tour
+         */
+        start() {
+            return this.startAt(0);
+        }
+
+        /**
+         * starts the tour at a specified step, step index, or step ID
+         *
+         * @public
+         */
+        startAt(stepOrStepIdOrIndex) {
+            return this.handleEvent(this.options.onStart).then(() => {
+
+                var step = this.getStep(stepOrStepIdOrIndex);
+                this.setCurrentStep(step);
+                this.tourStatus = this.statuses.ON;
+                this.emit('started', step);
+                if (this.options.useHotkeys) {
+                    this.setHotKeys();
+                }
+                return this.showStep(this.getCurrentStep());
+
+            });
+        };
+
+        /**
+         * ends the tour
+         */
+        end() {
+            return this.handleEvent(this.options.onEnd).then(() => {
+                var step = this.getCurrentStep();
+                if (step) {
+                    this.uiTourBackdrop.hide();
+                    return this.hideStep(step);
+                }
+
+            }).then(() => {
+
+                this.setCurrentStep(null);
+                this.emit('ended');
+                this.tourStatus = this.statuses.OFF;
+
+                if (this.options.useHotkeys) {
+                    this.unsetHotKeys();
+                }
+
+            });
+        }
+
+        /**
+         * pauses the tour
+         */
+        pause() {
+            return this.handleEvent(this.options.onPause).then(() => {
+                this.tourStatus = this.statuses.PAUSED;
+                return this.hideStep(this.getCurrentStep());
+            }).then(() => {
+                this.emit('paused', this.getCurrentStep());
+            });
+        }
+
+        /**
+         * resumes a paused tour or starts it
+         */
+        resume() {
+            return this.handleEvent(this.options.onResume).then(() => {
+                this.tourStatus = this.statuses.ON;
+                this.emit('resumed', this.getCurrentStep());
+                return this.showStep(this.getCurrentStep());
+            });
+        }
+
+        /**
+         * move to next step
+         * @returns {promise}
+         */
+        next() {
+            return this.goTo('$next');
+        }
+
+        /**
+         * move to previous step
+         * @returns {promise}
+         */
+        prev() {
+            return this.goTo('$prev');
+        }
+
+        /**
+         * Jumps to the provided step, step ID, or step index
+         *
+         * @param {step | string | number} goTo Step object, step ID string, or step index to jump to
+         * @returns {promise} Promise that resolves once the step is shown
+         */
+        goTo(goTo) {
+            var currentStep = this.getCurrentStep(),
+                stepToShow = this.getStep(goTo),
+                actionMap = {
+                    $prev: {
+                        getStep: () => this.getPrevStep(),
+                        preEvent: 'onPrev',
+                        navCheck: 'prevStep'
+                    },
+                    $next: {
+                        getStep: () => this.getNextStep(),
+                        preEvent: 'onNext',
+                        navCheck: 'nextStep'
+                    }
+                };
+
+            if (goTo === '$prev' || goTo === '$next') {
+                //trigger either onNext or onPrev here
+                //if next or previous requires a redirect, it will happen here
+                //the tour will pause here until the next view loads and
+                //the next/prev step is found
+                return this.handleEvent(currentStep.config(actionMap[goTo].preEvent)).then(() => {
+                    currentStep.backdrop && this.uiTourBackdrop.showTarget();
+                    return this.hideStep(currentStep);
+
+                }).then(() => {
+
+                    //if the next/prev step does not have a backdrop, hide it
+                    if (this.getCurrentStep().config('backdrop') && !actionMap[goTo].getStep().config('backdrop')) {
+                        this.uiTourBackdrop.hide();
+                    }
+
+                    //if a redirect occurred during onNext or onPrev, getCurrentStep() !== currentStep
+                    //this will only be true if no redirect occurred, since the redirect sets current step
+                    if (!currentStep[actionMap[goTo].navCheck] || currentStep[actionMap[goTo].navCheck] !== this.getCurrentStep().stepId) {
+                        this.setCurrentStep(actionMap[goTo].getStep());
+                        this.emit('stepChanged', this.getCurrentStep());
+                    }
+
+                }).then(() => {
+
+                    if (this.getCurrentStep()) {
+                        return this.showStep(this.getCurrentStep());
+                    } else {
+                        this.end();
+                    }
+
+                });
+            }
+
+            //if no step found
+            if (!stepToShow) {
+                return this.$q.reject('No step.');
+            }
+
+            //take action
+            return this.hideStep(this.getCurrentStep())
+                .then(() => {
+                    //if the next/prev step does not have a backdrop, hide it
+                    if (this.getCurrentStep().config('backdrop') && !stepToShow.config('backdrop')) {
+                        this.uiTourBackdrop.hide();
+                    }
+                    this.setCurrentStep(stepToShow);
+                    this.emit('stepChanged', this.getCurrentStep());
+                    return this.showStep(stepToShow);
+                });
+        };
+
+
+        /**
+         * @typedef number TourStatus
+         */
+
+        /**
+         * Returns the current status of the tour
+         * @returns {TourStatus}
+         */
+        getStatus() {
+            return this.tourStatus;
+        }
+
+        status = this.statuses
+
+        //some debugging functions
+        private _getSteps() {
+            return this.stepList;
+        }
+        private _getStatus() {
+            return this.tourStatus;
+        }
+        private _getCurrentStep = this.getCurrentStep;
+        private _setCurrentStep = this.setCurrentStep;
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-helper.ts b/app/TypeScript/angular-ui-tour-helper.ts
new file mode 100644
index 0000000..f2551b2
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-helper.ts
@@ -0,0 +1,151 @@
+module Tour {
+    export class TourHelper {
+        $state
+
+        constructor(private $templateCache: ng.ITemplateCacheService, private $http: ng.IHttpService, private $compile: ng.ICompileService, private $location: ng.ILocationService, private TourConfig: ITourConfig, private $q: ng.IQService, private $injector) {
+            if ($injector.has('$state')) {
+                this.$state = $injector.get('$state');
+            }
+        }
+
+        /**
+        * Helper function that calls scope.$apply if a digest is not currently in progress
+        * Borrowed from: https://coderwall.com/p/ngisma
+        *
+        * @param {$rootScope.Scope} scope
+        * @param {Function} fn
+        */
+        safeApply(scope: ng.IScope, fn: () => any) {
+            var phase = scope.$$phase;
+            if (phase === '$apply' || phase === '$digest') {
+                if (fn && (typeof (fn) === 'function')) {
+                    fn();
+                }
+            } else {
+                scope.$apply(fn);
+            }
+        }
+
+        /**
+         * Converts a stringified boolean to a JS boolean
+         *
+         * @param string
+         * @returns {*}
+         */
+        stringToBoolean(string) {
+            if (string === 'true') {
+                return true;
+            } else if (string === 'false') {
+                return false;
+            }
+
+            return string;
+        }
+
+        /**
+         * This will attach the properties native to Angular UI Tooltips. If there is a tour-level value set
+         * for any of them, this passes that value along to the step
+         *
+         * @param {$rootScope.Scope} scope The tour step's scope
+         * @param {Attributes} attrs The tour step's Attributes
+         * @param {Object} step Represents the tour step object
+         * @param {Array} properties The list of Tooltip properties
+         */
+        attachTourConfigProperties(scope, attrs, step, properties) {
+            angular.forEach(properties, (property) => {
+                if (!attrs[this.getAttrName(property)] && angular.isDefined(step.config(property))) {
+                    attrs.$set(this.getAttrName(property), String(step.config(property)));
+                }
+            });
+        };
+
+        /**
+         * Helper function that attaches event handlers to options
+         *
+         * @param {$rootScope.Scope} scope
+         * @param {Attributes} attrs
+         * @param {Object} options represents the tour or step object
+         * @param {Array} events
+         * @param {boolean} prefix - used only by the tour directive
+         */
+        attachEventHandlers(scope, attrs, options, events, prefix?) {
+
+            angular.forEach(events, (eventName) => {
+                var attrName = this.getAttrName(eventName, prefix);
+                if (attrs[attrName]) {
+                    options[eventName] = () => {
+                        return this.$q((resolve) => {
+                            this.safeApply(scope, () => {
+                                resolve(scope.$eval(attrs[attrName]));
+                            });
+                        });
+                    };
+                }
+            });
+
+        };
+
+        /**
+         * Helper function that attaches observers to option attributes
+         *
+         * @param {Attributes} attrs
+         * @param {Object} options represents the tour or step object
+         * @param {Array} keys attribute names
+         * @param {boolean} prefix - used only by the tour directive
+         */
+        attachInterpolatedValues(attrs, options, keys, prefix?) {
+
+            angular.forEach(keys, (key) => {
+                var attrName = this.getAttrName(key, prefix);
+                if (attrs[attrName]) {
+                    options[key] = this.stringToBoolean(attrs[attrName]);
+                    attrs.$observe(attrName, (newValue) => {
+                        options[key] = this.stringToBoolean(newValue);
+                    });
+                }
+            });
+
+        };
+
+        /**
+         * sets up a redirect when the next or previous step is in a different view
+         *
+         * @param step - the current step (not the next or prev one)
+         * @param ctrl - the tour controller
+         * @param direction - enum (onPrev, onNext)
+         * @param path - the url that the next step is on (will use $location.path())
+         * @param targetName - the ID of the next or previous step
+         */
+        setRedirect(step, ctrl, direction, path, targetName) {
+            var oldHandler = step[direction];
+            step[direction] = (tour) => {
+                return this.$q((resolve) => {
+                    if (oldHandler) {
+                        oldHandler(tour);
+                    }
+                    ctrl.waitFor(targetName);
+                    if (step.config('useUiRouter')) {
+                        this.$state.transitionTo(path).then(resolve);
+                    } else {
+                        this.$location.path(path);
+                        resolve();
+                    }
+                });
+            };
+        };
+
+        /**
+         * Returns the attribute name for an option depending on the prefix
+         *
+         * @param {string} option - name of option
+         * @param {string} prefix - should only be used by tour directive and set to 'uiTour'
+         * @returns {string} potentially prefixed name of option, or just name of option
+         */
+        getAttrName(option, prefix?) {
+            return (prefix || 'tourStep') + option.charAt(0).toUpperCase() + option.substr(1);
+        };
+        static factory($templateCache: ng.ITemplateCacheService, $http: ng.IHttpService, $compile: ng.ICompileService, $location: ng.ILocationService, TourConfig: ITourConfig, $q: ng.IQService, $injector: ng.IInjectStatic) {
+            return new TourHelper($templateCache, $http, $compile, $location, TourConfig, $q, $injector);
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-interfaces.ts b/app/TypeScript/angular-ui-tour-interfaces.ts
new file mode 100644
index 0000000..f3800f3
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-interfaces.ts
@@ -0,0 +1,81 @@
+module Tour {
+    export interface IPadding {
+        top: number;
+        left: number;
+        bottom: number;
+        right: number;
+    }
+
+    export interface ITourScope extends ng.IScope {
+        tour: TourController;
+        tourStep: ITourStep;
+
+        originScope: () => ITourScope;
+        isOpen: () => boolean;
+    }
+
+    export interface ITourStep {
+        nextPath?;
+        prevPath?;
+        backdrop?;
+        stepId?;
+        trustedContent?;
+        content?: string;
+        order?: number;
+        templateUrl?: string;
+        element?: ng.IRootElementService;
+        enabled?: boolean;
+        preventScrolling?: boolean;
+        fixed?: boolean;
+        isNext?: boolean;
+        isPrev?: boolean;
+        redirectNext?: boolean;
+        redirectPrev?: boolean;
+        nextStep?: ITourStep;
+        prevStep?: ITourStep;
+        show?: () => PromiseLike<any>;
+        hide?: () => PromiseLike<any>;
+        onNext?: () => PromiseLike<any>;
+        onPrev?: () => PromiseLike<any>;
+        onShow?: () => PromiseLike<any>;
+        onHide?: () => PromiseLike<any>;
+        onShown?: () => PromiseLike<any>;
+        onHidden?: () => PromiseLike<any>;
+        config?: (string) => any;
+    }
+
+    export interface ITourConfig {
+        get: (option: string) => any;
+        getAll: () => ITourConfigProperties;
+    }
+
+    export interface ITourConfigProperties {
+        name?: string;
+        placement: string;
+        animation: boolean;
+        popupDelay: number;
+        closePopupDelay: number;
+        enable: boolean;
+        appendToBody: boolean;
+        popupClass: string;
+        orphan: boolean;
+        backdrop: boolean;
+        backdropZIndex: number;
+        scrollOffset: number;
+        scrollIntoView: boolean;
+        useUiRouter: boolean;
+        useHotkeys: boolean;
+
+        onStart: (any?) => any;
+        onEnd: (any?) => any;
+        onPause: (any?) => any;
+        onResume: (any?) => any;
+        onNext: (any?) => any;
+        onPrev: (any?) => any;
+        onShow: (any?) => any;
+        onShown: (any?) => any;
+        onHide: (any?) => any;
+        onHidden: (any?) => any;
+
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-service.ts b/app/TypeScript/angular-ui-tour-service.ts
new file mode 100644
index 0000000..dee20d5
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-service.ts
@@ -0,0 +1,82 @@
+
+module Tour {
+    export class uiTourService {
+        private tours: Array<TourController>
+
+        constructor(private $controller: ng.IControllerService) {
+            this.tours = [];
+        }
+
+        /**
+         * If there is only one tour, returns the tour
+         */
+        getTour() {
+            return this.tours[0];
+        }
+
+        /**
+         * Look up a specific tour by name
+         *
+         * @param {string} name Name of tour
+         */
+        getTourByName(name: string) {
+            return this.tours.filter((tour) => {
+                return tour.options.name === name;
+            })[0];
+        }
+
+        /**
+         * Finds the tour available to a specific element
+         *
+         * @param {jqLite | HTMLElement} element Element to use to look up tour
+         * @returns {*}
+         */
+        getTourByElement(element) {
+            return angular.element(element).controller('uiTour');
+        };
+
+        /**
+         * Creates a tour that is not attached to a DOM element (experimental)
+         *
+         * @param {string} name Name of the tour (required)
+         * @param {{}=} config Options to override defaults
+         */
+        createDetachedTour(name: string, config: ITourConfigProperties) {
+            if (!name) {
+                throw {
+                    name: 'ParameterMissingError',
+                    message: 'A unique tour name is required for creating a detached tour.'
+                };
+            }
+
+            config = config || <any>{};
+
+            config.name = name;
+            return (<any>this.$controller('uiTourController')).init(config);
+        };
+
+        /**
+         * Used by uiTourController to register a tour
+         *
+         * @protected
+         * @param tour
+         */
+        _registerTour(tour) {
+            this.tours.push(tour);
+        };
+
+        /**
+         * Used by uiTourController to remove a destroyed tour from the registry
+         *
+         * @protected
+         * @param tour
+         */
+        _unregisterTour(tour) {
+            this.tours.splice(this.tours.indexOf(tour), 1);
+        };
+
+        static factory($controller: ng.IControllerService) {
+            return new uiTourService($controller);
+        }
+    }
+}
diff --git a/app/TypeScript/angular-ui-tour-step-popup.ts b/app/TypeScript/angular-ui-tour-step-popup.ts
new file mode 100644
index 0000000..71a97d4
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-step-popup.ts
@@ -0,0 +1,74 @@
+module Tour {
+    export class TourStepPopupDirective {
+        public restrict = 'EA';
+        public replace = true;
+        public scope = { title: '@', uibTitle: '@uibTitle', content: '@', placement: '@', animation: '&', isOpen: '&', originScope: '&' };
+        public templateUrl = 'tour-step-popup.html';
+        public link: (scope, element: ng.IRootElementService, attrs, ctrl: Tour.TourController) => void;
+
+        constructor(TourConfig: Tour.ITourConfig, smoothScroll, ezComponentHelpers) {
+            TourStepPopupDirective.prototype.link = function (scope, element: ng.IRootElementService, attrs, ctrl: Tour.TourController) {
+                var step = scope.originScope().tourStep,
+                    ch = ezComponentHelpers.apply(null, arguments),
+                    scrollOffset = step.config('scrollOffset');
+
+                //UI Bootstrap name changed in 1.3.0
+                if (!scope.title && scope.uibTitle) {
+                    scope.title = scope.uibTitle;
+                }
+
+                //for arrow styles, unfortunately UI Bootstrap uses attributes for styling
+                attrs.$set('uib-popover-popup', 'uib-popover-popup');
+
+                element.css({
+                    zIndex: TourConfig.get('backdropZIndex') + 2,
+                    display: 'block'
+                });
+
+                element.addClass(step.config('popupClass'));
+
+                if (step.config('fixed')) {
+                    element.css('position', 'fixed');
+                }
+
+                if (step.config('orphan')) {
+                    ch.useStyles(
+                        `:scope {
+                               position: fixed;
+                               top: 50% !important;
+                               left: 50% !important;
+                               margin: 0 !important;
+                               -ms-transform: translateX(-50%) translateY(-50%);
+                               -moz-transform: translateX(-50%) translateY(-50%);
+                               -webkit-transform: translateX(-50%) translateY(-50%);
+                               transform: translateX(-50%) translateY(-50%);
+                            }
+                            
+                            .arrow
+                               display: none;
+                            }`
+                    );
+                }
+
+                scope.$watch('isOpen', (isOpen: () => boolean) => {
+                    if (isOpen() && !step.config('orphan') && step.config('scrollIntoView')) {
+                        smoothScroll(element[0], {
+                            offset: scrollOffset
+                        });
+                    }
+                });
+            };
+        }
+
+        public static Factory() {
+
+            var directive = (TourConfig: Tour.ITourConfig, smoothScroll, ezComponentHelpers) => {
+                return new TourStepPopupDirective(TourConfig, smoothScroll, ezComponentHelpers);
+            };
+
+            directive['$inject'] = ['TourConfig', 'smoothScroll', 'ezComponentHelpers'];
+
+            return directive;
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/TypeScript/angular-ui-tour-step.ts b/app/TypeScript/angular-ui-tour-step.ts
new file mode 100644
index 0000000..fcbf5f4
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour-step.ts
@@ -0,0 +1,226 @@
+module Tour {
+    export class TourStepCompiler {
+        private ctrl: TourController
+        private step: ITourStep
+        private events: Array<string>
+        private options: Array<string>
+        private tooltipAttrs: Array<string>
+        private orderWatch
+        private enabledWatch
+
+        public TourHelpers: TourHelper
+        public TourService: uiTourService
+        public $q: ng.IQService
+        public $sce: ng.ISCEService
+        public tourStepLinker
+
+        constructor(private scope: Tour.ITourScope, private element: ng.IRootElementService, private attrs: ng.IAttributes, private uiTourCtrl: Tour.TourController) {
+            this.initializeVariables();
+            if (attrs[this.TourHelpers.getAttrName('if')] !== undefined && attrs[this.TourHelpers.getAttrName('if')] === "false") {
+                return;
+            }
+
+            this.addWatches();
+            this.finalizeStep();
+            this.addStepToScope();
+            Object.defineProperties(this.step, {
+                element: {
+                    value: element
+                }
+            });
+
+            //clean up when element is destroyed
+            scope.$on('$destroy', function () {
+                this.ctrl.removeStep(this.step);
+                this.orderWatch();
+                this.enabledWatch();
+            });
+        }
+
+        public static Factory() {
+
+            var compiler = (scope: Tour.ITourScope, element: ng.IRootElementService, attrs: ng.IAttributes, uiTourCtrl: Tour.TourController) => {
+                return new TourStepCompiler(scope, element, attrs, uiTourCtrl);
+            };
+
+            compiler['$inject'] = ['scope', 'element', 'attrs', 'uiTourCtrl'];
+
+            return compiler;
+        }
+
+        private initializeVariables() {
+            this.ctrl = this.getCtrl(this.attrs, this.uiTourCtrl);
+            this.step = {
+                show: () => {
+                    this.element.triggerHandler('uiTourShow');
+                    return this.$q((resolve) => {
+                        this.element[0].dispatchEvent(new CustomEvent('uiTourShow'));
+                        resolve();
+                    });
+                },
+                hide: () => {
+                    return this.$q((resolve) => {
+                        this.element[0].dispatchEvent(new CustomEvent('uiTourHide'));
+                        resolve();
+                    });
+                },
+                stepId: (<any>this.attrs).tourStep,
+                enabled: true,
+                config: (option) => {
+                    if (angular.isDefined(this.step[option])) {
+                        return this.step[option];
+                    }
+                    return this.ctrl.config(option);
+                }
+            };
+            this.events = 'onShow onShown onHide onHidden onNext onPrev'.split(' ');
+            this.options = 'content title animation placement backdrop orphan popupDelay popupCloseDelay popupClass fixed preventScrolling scrollIntoView nextStep prevStep nextPath prevPath scrollOffset'.split(' ');
+            this.tooltipAttrs = 'animation appendToBody placement popupDelay popupCloseDelay'.split(' ');
+        }
+
+        private addWatches() {
+            this.TourHelpers.attachInterpolatedValues(this.attrs, this.step, this.options);
+            this.orderWatch = this.attrs.$observe(this.TourHelpers.getAttrName('order'), (order: number) => {
+                this.step.order = !isNaN(order * 1) ? order * 1 : 0;
+                this.ctrl.reorderStep(this.step);
+            });
+            this.enabledWatch = this.attrs.$observe(this.TourHelpers.getAttrName('enabled'), function (isEnabled) {
+                this.step.enabled = isEnabled !== 'false';
+                if (this.step.enabled) {
+                    this.ctrl.addStep(this.step);
+                } else {
+                    this.ctrl.removeStep(this.step);
+                }
+            });
+        }
+
+        private finalizeStep() {
+            //Attach event handlers
+            this.TourHelpers.attachEventHandlers(this.scope, this.attrs, this.step, this.events);
+
+            if (this.attrs[this.TourHelpers.getAttrName('templateUrl')]) {
+                this.step.templateUrl = this.scope.$eval(this.attrs[this.TourHelpers.getAttrName('templateUrl')]);
+            }
+
+            //If there is an options argument passed, just use that instead
+            if (this.attrs[this.TourHelpers.getAttrName('options')]) {
+                angular.extend(this.step, this.scope.$eval(this.attrs[this.TourHelpers.getAttrName('options')]));
+            }
+
+            //set up redirects
+            if (this.step.nextPath) {
+                this.step.redirectNext = true;
+                this.TourHelpers.setRedirect(this.step, this.ctrl, 'onNext', this.step.nextPath, this.step.nextStep);
+            }
+            if (this.step.prevPath) {
+                this.step.redirectPrev = true;
+                this.TourHelpers.setRedirect(this.step, this.ctrl, 'onPrev', this.step.prevPath, this.step.prevStep);
+            }
+
+            //for HTML content
+            this.step.trustedContent = this.$sce.trustAsHtml(this.step.content);
+        }
+
+        private addStepToScope() {
+
+            //Add step to tour
+            this.scope.tourStep = this.step;
+            this.scope.tour = this.scope.tour || this.ctrl;
+            if (this.ctrl.initialized) {
+                this.configureInheritedProperties();
+                this.ctrl.addStep(this.step);
+            } else {
+                this.ctrl.once('initialized', () => {
+                    this.configureInheritedProperties();
+                    this.ctrl.addStep(this.step);
+                });
+            }
+        }
+
+        private configureInheritedProperties() {
+            this.TourHelpers.attachTourConfigProperties(this.scope, this.attrs, this.step, this.tooltipAttrs/*, 'tourStep'*/);
+            this.tourStepLinker(this.scope, this.element, this.attrs);
+        }
+
+        private getCtrl(attrs, uiTourCtrl) {
+            var ctrl: Tour.TourController;
+
+            if (attrs[this.TourHelpers.getAttrName('belongsTo')]) {
+                ctrl = this.TourService.getTourByName(attrs[this.TourHelpers.getAttrName('belongsTo')]);
+            } else if (uiTourCtrl) {
+                ctrl = uiTourCtrl;
+            }
+
+            if (!ctrl) {
+                throw {
+                    name: 'DependencyMissingError',
+                    message: 'No tour provided for tour step.'
+                };
+            }
+
+            return ctrl;
+        }
+    }
+
+    export class TourStepDirective {
+        private tourStepDef;
+
+        public restrict: string;
+        public scope: boolean;
+        public require: string;
+        public compile: (element: ng.IAugmentedJQuery, attr: ng.IAttributes) => (...any) => TourStepCompiler;
+
+        constructor(private TourConfig: Tour.ITourConfig, private TourHelpers: Tour.TourHelper, private TourService: Tour.uiTourService, private $uibTooltip, private $q: ng.IQService, private $sce: ng.ISCEService) {
+            this.restrict = 'EA';
+            this.scope = true;
+            this.require = '?^uiTour';
+            this.tourStepDef = $uibTooltip('tourStep', 'tourStep', 'uiTourShow', {
+                popupDelay: 1 //needs to be non-zero for popping up after navigation
+            });
+
+            Tour.TourStepDirective.prototype.compile = (tElement, tAttrs) => {
+                TourStepCompiler.prototype.$q = $q;
+                TourStepCompiler.prototype.$sce = $sce;
+                TourStepCompiler.prototype.TourHelpers = TourHelpers;
+                TourStepCompiler.prototype.TourService = TourService;
+                TourStepCompiler.prototype.tourStepLinker = this.tourStepDef.compile(tElement, tAttrs);
+
+                if (!(<any>tAttrs).tourStep) {
+                    tAttrs.$set('tourStep', '\'PH\''); //a placeholder so popup will show
+                }
+
+                return TourStepCompiler.Factory();
+            }
+        }
+
+        public static Factory() {
+
+            var directive = (TourConfig: Tour.ITourConfig, TourHelpers: Tour.TourHelper, TourService: Tour.uiTourService, $uibTooltip, $q: ng.IQService, $sce: ng.ISCEService) => {
+                return new TourStepDirective(TourConfig, TourHelpers, TourService, $uibTooltip, $q, $sce);
+            };
+
+            directive['$inject'] = ['TourConfig', 'TourHelpers', 'uiTourService', '$uibTooltip', '$q', '$sce'];
+
+            return directive;
+        }
+
+        private getCtrl(attrs, uiTourCtrl) {
+            var ctrl: Tour.TourController;
+
+            if (attrs[this.TourHelpers.getAttrName('belongsTo')]) {
+                ctrl = this.TourService.getTourByName(attrs[this.TourHelpers.getAttrName('belongsTo')]);
+            } else if (uiTourCtrl) {
+                ctrl = uiTourCtrl;
+            }
+
+            if (!ctrl) {
+                throw {
+                    name: 'DependencyMissingError',
+                    message: 'No tour provided for tour step.'
+                };
+            }
+
+            return ctrl;
+        }
+    }
+}
diff --git a/app/TypeScript/angular-ui-tour.ts b/app/TypeScript/angular-ui-tour.ts
new file mode 100644
index 0000000..6195ac6
--- /dev/null
+++ b/app/TypeScript/angular-ui-tour.ts
@@ -0,0 +1,144 @@
+/// <reference path="../typings/jquery/jquery.d.ts" />
+/// <reference path="../typings/angularjs/angular.d.ts" />
+/// <reference path="../typings/angular-ui-bootstrap/angular-ui-bootstrap.d.ts" />
+/* global Tour: false */
+
+module Tour {
+    export class TourDirective {
+        public restrict = 'EA';
+        public scope = true;
+        public controller = 'uiTourController';
+        public link: (scope: Tour.ITourScope, element: ng.IRootElementService, attrs, ctrl: Tour.TourController) => void;
+
+        constructor(private TourHelpers: Tour.TourHelper) {
+            TourDirective.prototype.link = (scope: Tour.ITourScope, element: ng.IRootElementService, attrs, ctrl: Tour.TourController) => {
+                //Pass static options through or use defaults
+                var tour = {
+                    name: attrs.uiTour,
+                    templateUrl: null,
+                    onReady: null
+                }
+
+                this.interpolateValues(scope, attrs, tour);
+                this.finalizeTour(scope, attrs, tour);
+                this.finalizeScope(scope, tour, ctrl);
+            };
+        }
+
+        public static Factory() {
+
+            var directive = (TourHelpers: Tour.TourHelper) => {
+                return new TourDirective(TourHelpers);
+            };
+
+            directive['$inject'] = ['TourHelpers'];
+
+            return directive;
+        }
+
+        private interpolateValues(scope, attrs, tour) {
+            var events = 'onReady onStart onEnd onShow onShown onHide onHidden onNext onPrev onPause onResume'.split(' '),
+                properties = 'placement animation popupDelay closePopupDelay enable appendToBody popupClass orphan backdrop scrollOffset scrollIntoView useUiRouter useHotkeys'.split(' ');
+
+            //Pass interpolated values through
+            this.TourHelpers.attachInterpolatedValues(attrs, tour, properties, 'uiTour');
+
+            //Attach event handlers
+            this.TourHelpers.attachEventHandlers(scope, attrs, tour, events, 'uiTour');
+
+        }
+
+        private finalizeTour(scope, attrs, tour) {
+            //override the template url
+            if (attrs[this.TourHelpers.getAttrName('templateUrl', 'uiTour')]) {
+                tour.templateUrl = scope.$eval(attrs[this.TourHelpers.getAttrName('templateUrl', 'uiTour')]);
+            }
+
+            //If there is an options argument passed, just use that instead
+            if (attrs[this.TourHelpers.getAttrName('options')]) {
+                angular.extend(tour, scope.$eval(attrs[this.TourHelpers.getAttrName('options')]));
+            }
+        }
+
+        private finalizeScope(scope, tour, ctrl) {
+            //Initialize tour
+            scope.tour = ctrl.init(tour);
+            if (typeof tour.onReady === 'function') {
+                tour.onReady();
+            }
+
+            scope.$on('$destroy', function () {
+                ctrl.destroy();
+            });
+        }
+    }
+}
+
+((app: ng.IModule) => {
+    'use strict';
+
+    app.config(['$uibTooltipProvider', ($uibTooltipProvider: angular.ui.bootstrap.ITooltipProvider) => {
+        $uibTooltipProvider.setTriggers({
+            'uiTourShow': 'uiTourHide'
+        });
+    }]);
+
+})(angular.module('bm.uiTour', ['ngSanitize', 'ui.bootstrap', 'smoothScroll', 'ezNg', 'cfp.hotkeys']));
+
+(function (app: ng.IModule) {
+    'use strict';
+
+    app.factory('uiTourBackdrop', ['TourConfig', '$document', '$uibPosition', '$window', Tour.TourBackdrop.factory])
+        .factory('TourHelpers', ['$templateCache', '$http', '$compile', '$location', 'TourConfig', '$q', '$injector', Tour.TourHelper.factory])
+        .factory('uiTourService', ['$controller', Tour.uiTourService.factory])
+        .provider('TourConfig', [Tour.TourConfigProvider])
+        .controller('uiTourController', ['$timeout', '$q', '$filter', 'TourConfig', 'uiTourBackdrop', 'uiTourService', 'ezEventEmitter', 'hotkeys', Tour.TourController])
+        .directive('uiTour', ['TourHelpers', Tour.TourDirective.Factory()])
+        .directive('tourStepPopup', ['TourConfig', 'smoothScroll', 'ezComponentHelpers', Tour.TourStepPopupDirective.Factory()])
+        .directive('tourStep', ['TourConfig', 'TourHelpers', 'uiTourService', '$uibTooltip', '$q', '$sce', Tour.TourStepDirective.Factory()])
+        .run(['$templateCache', function ($templateCache) {
+            $templateCache.put("tour-step-popup.html",
+                `<div class="popover tour-step"
+                     tooltip-animation-class="fade"
+                     uib-tooltip-classes
+                     ng-class="{ in: isOpen() }">
+                    <div class="arrow"></div>
+                
+                    <div class="popover-inner tour-step-inner">
+                        <h3 class="popover-title tour-step-title" ng-bind="title" ng-if="title"></h3>
+                        <div class="popover-content tour-step-content"
+                             uib-tooltip-template-transclude="originScope().tourStep.config('templateUrl') || 'tour-step-template.html'"
+                             tooltip-template-transclude-scope="originScope()"></div>
+                    </div>
+                </div>
+                `);
+            $templateCache.put("tour-step-template.html",
+                `<div>
+                    <div class="popover-content tour-step-content" ng-bind-html="tourStep.trustedContent"></div>
+                    <div class="popover-navigation tour-step-navigation">
+                        <div class="btn-group">
+                            <button class="btn btn-sm btn-default" ng-if="tourStep.isPrev" ng-click="tour.prev()">&laquo; Prev</button>
+                            <button class="btn btn-sm btn-default" ng-if="tourStep.isNext" ng-click="tour.next()">Next &raquo;</button>
+                            <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause"
+                                    data-resume-text="Resume" ng-click="tour.pause()">Pause
+                            </button>
+                        </div>
+                        <button class="btn btn-sm btn-default" data-role="end" ng-click="tour.end()">End tour</button>
+                    </div>
+                </div>
+                `);
+        }]);
+} (angular.module('bm.uiTour')));
+
+(function (window) {
+    function CustomEvent(event, params) {
+        params = params || { bubbles: false, cancelable: false, detail: undefined };
+        var evt = document.createEvent('CustomEvent');
+        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
+        return evt;
+    }
+
+    CustomEvent.prototype = window.Event.prototype;
+
+    window.CustomEvent = CustomEvent;
+})(window);