"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = require('@angular/core');
var platform_browser_1 = require('@angular/platform-browser');
var animation_1 = require('../../animations/animation');
var transition_1 = require('../../transitions/transition');
var config_1 = require('../../config/config');
var util_1 = require('../../util/util');
var nav_params_1 = require('../nav/nav-params');
var view_controller_1 = require('../nav/view-controller');
var dom_1 = require('../../util/dom');
/**
 * @name Picker
 * @description
 *
 */
var Picker = (function (_super) {
    __extends(Picker, _super);
    function Picker(opts) {
        if (opts === void 0) { opts = {}; }
        opts.columns = opts.columns || [];
        opts.buttons = opts.buttons || [];
        opts.enableBackdropDismiss = util_1.isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
        _super.call(this, PickerDisplayCmp, opts);
        this.viewType = 'picker';
        this.isOverlay = true;
        this.change = new core_1.EventEmitter();
        // by default, pickers should not fire lifecycle events of other views
        // for example, when an picker enters, the current active view should
        // not fire its lifecycle events because it's not conceptually leaving
        this.fireOtherLifecycles = false;
        this.usePortal = true;
    }
    /**
    * @private
    */
    Picker.prototype.getTransitionName = function (direction) {
        var key = (direction === 'back' ? 'pickerLeave' : 'pickerEnter');
        return this._nav && this._nav.config.get(key);
    };
    /**
     * @param {any} button Picker toolbar button
     */
    Picker.prototype.addButton = function (button) {
        this.data.buttons.push(button);
    };
    /**
     * @param {any} button Picker toolbar button
     */
    Picker.prototype.addColumn = function (column) {
        this.data.columns.push(column);
    };
    Picker.prototype.getColumns = function () {
        return this.data.columns;
    };
    Picker.prototype.refresh = function () {
        this.instance.refresh && this.instance.refresh();
    };
    /**
     * @param {string} cssClass CSS class name to add to the picker's outer wrapper.
     */
    Picker.prototype.setCssClass = function (cssClass) {
        this.data.cssClass = cssClass;
    };
    Picker.create = function (opts) {
        if (opts === void 0) { opts = {}; }
        return new Picker(opts);
    };
    __decorate([
        core_1.Output(), 
        __metadata('design:type', core_1.EventEmitter)
    ], Picker.prototype, "change", void 0);
    return Picker;
}(view_controller_1.ViewController));
exports.Picker = Picker;
/**
 * @private
 */
var PickerColumnCmp = (function () {
    function PickerColumnCmp(config, _sanitizer) {
        this._sanitizer = _sanitizer;
        this.y = 0;
        this.pos = [];
        this.msPrv = 0;
        this.startY = null;
        this.change = new core_1.EventEmitter();
        this.rotateFactor = config.getNumber('pickerRotateFactor', 0);
    }
    PickerColumnCmp.prototype.ngAfterViewInit = function () {
        // get the scrollable element within the column
        var colEle = this.colEle.nativeElement;
        this.colHeight = colEle.clientHeight;
        // get the height of one option
        this.optHeight = (colEle.firstElementChild ? colEle.firstElementChild.clientHeight : 0);
        // set the scroll position for the selected option
        this.setSelected(this.col.selectedIndex, 0);
    };
    PickerColumnCmp.prototype.pointerStart = function (ev) {
        void 0;
        if (this.isPrevented(ev)) {
            // do not both with mouse events if a touch event already fired
            return;
        }
        // cancel any previous raf's that haven't fired yet
        dom_1.cancelRaf(this.rafId);
        // remember where the pointer started from`
        this.startY = dom_1.pointerCoord(ev).y;
        // reset everything
        this.velocity = 0;
        this.pos.length = 0;
        this.pos.push(this.startY, Date.now());
        var minY = this.col.options.length - 1;
        var maxY = 0;
        for (var i = 0; i < this.col.options.length; i++) {
            if (this.col.options[i].disabled) {
                continue;
            }
            if (i < minY) {
                minY = i;
            }
            if (i > maxY) {
                maxY = i;
            }
        }
        this.minY = (minY * this.optHeight * -1);
        this.maxY = (maxY * this.optHeight * -1);
    };
    PickerColumnCmp.prototype.pointerMove = function (ev) {
        ev.preventDefault();
        ev.stopPropagation();
        if (this.startY !== null) {
            if (this.isPrevented(ev)) {
                return;
            }
            var currentY = dom_1.pointerCoord(ev).y;
            this.pos.push(currentY, Date.now());
            // update the scroll position relative to pointer start position
            var y = this.y + (currentY - this.startY);
            if (y > this.minY) {
                // scrolling up higher than scroll area
                y = Math.pow(y, 0.8);
                this.bounceFrom = y;
            }
            else if (y < this.maxY) {
                // scrolling down below scroll area
                y += Math.pow(this.maxY - y, 0.9);
                this.bounceFrom = y;
            }
            else {
                this.bounceFrom = 0;
            }
            this.update(y, 0, false, false);
        }
    };
    PickerColumnCmp.prototype.pointerEnd = function (ev) {
        if (this.isPrevented(ev)) {
            return;
        }
        this.velocity = 0;
        if (this.bounceFrom > 0) {
            // bounce back up
            this.update(this.minY, 100, true, true);
        }
        else if (this.bounceFrom < 0) {
            // bounce back down
            this.update(this.maxY, 100, true, true);
        }
        else if (this.startY !== null) {
            var endY = dom_1.pointerCoord(ev).y;
            void 0;
            this.pos.push(endY, Date.now());
            var endPos = (this.pos.length - 1);
            var startPos = endPos;
            var timeRange = (Date.now() - 100);
            // move pointer to position measured 100ms ago
            for (var i = endPos; i > 0 && this.pos[i] > timeRange; i -= 2) {
                startPos = i;
            }
            if (startPos !== endPos) {
                // compute relative movement between these two points
                var timeOffset = (this.pos[endPos] - this.pos[startPos]);
                var movedTop = (this.pos[startPos - 1] - this.pos[endPos - 1]);
                // based on XXms compute the movement to apply for each render step
                this.velocity = ((movedTop / timeOffset) * FRAME_MS);
            }
            if (Math.abs(endY - this.startY) > 3) {
                ev.preventDefault();
                ev.stopPropagation();
                var y = this.y + (endY - this.startY);
                this.update(y, 0, true, true);
            }
        }
        this.startY = null;
        this.decelerate();
    };
    PickerColumnCmp.prototype.decelerate = function () {
        var y = 0;
        dom_1.cancelRaf(this.rafId);
        if (isNaN(this.y) || !this.optHeight) {
            // fallback in case numbers get outta wack
            this.update(y, 0, true, true);
        }
        else if (Math.abs(this.velocity) > 0) {
            // still decelerating
            this.velocity *= DECELERATION_FRICTION;
            // do not let it go slower than a velocity of 1
            this.velocity = (this.velocity > 0 ? Math.max(this.velocity, 1) : Math.min(this.velocity, -1));
            y = Math.round(this.y - this.velocity);
            if (y > this.minY) {
                // whoops, it's trying to scroll up farther than the options we have!
                y = this.minY;
                this.velocity = 0;
            }
            else if (y < this.maxY) {
                // gahh, it's trying to scroll down farther than we can!
                y = this.maxY;
                this.velocity = 0;
            }
            void 0;
            var notLockedIn = (y % this.optHeight !== 0 || Math.abs(this.velocity) > 1);
            this.update(y, 0, true, !notLockedIn);
            if (notLockedIn) {
                // isn't locked in yet, keep decelerating until it is
                this.rafId = dom_1.raf(this.decelerate.bind(this));
            }
        }
        else if (this.y % this.optHeight !== 0) {
            // needs to still get locked into a position so options line up
            var currentPos = Math.abs(this.y % this.optHeight);
            // create a velocity in the direction it needs to scroll
            this.velocity = (currentPos > (this.optHeight / 2) ? 1 : -1);
            this.decelerate();
        }
    };
    PickerColumnCmp.prototype.optClick = function (ev, index) {
        if (!this.velocity) {
            ev.preventDefault();
            ev.stopPropagation();
            this.setSelected(index, 150);
        }
    };
    PickerColumnCmp.prototype.setSelected = function (selectedIndex, duration) {
        // if there is a selected index, then figure out it's y position
        // if there isn't a selected index, then just use the top y position
        var y = (selectedIndex > -1) ? ((selectedIndex * this.optHeight) * -1) : 0;
        dom_1.cancelRaf(this.rafId);
        this.velocity = 0;
        // so what y position we're at
        this.update(y, duration, true, true);
    };
    PickerColumnCmp.prototype.update = function (y, duration, saveY, emitChange) {
        // ensure we've got a good round number :)
        y = Math.round(y);
        this.col.selectedIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0);
        for (var i = 0; i < this.col.options.length; i++) {
            var opt = this.col.options[i];
            var optTop = (i * this.optHeight);
            var optOffset = (optTop + y);
            var rotateX = (optOffset * this.rotateFactor);
            var translateX = 0;
            var translateY = 0;
            var translateZ = 0;
            if (this.rotateFactor !== 0) {
                translateX = 0;
                translateZ = 90;
                if (rotateX > 90 || rotateX < -90) {
                    translateX = -9999;
                    rotateX = 0;
                }
            }
            else {
                translateY = optOffset;
            }
            opt._trans = this._sanitizer.bypassSecurityTrustStyle("rotateX(" + rotateX + "deg) translate3d(" + translateX + "px," + translateY + "px," + translateZ + "px)");
            opt._dur = (duration > 0 ? duration + 'ms' : '');
        }
        if (saveY) {
            this.y = y;
        }
        if (emitChange) {
            if (this.lastIndex === undefined) {
                // have not set a last index yet
                this.lastIndex = this.col.selectedIndex;
            }
            else if (this.lastIndex !== this.col.selectedIndex) {
                // new selected index has changed from the last index
                // update the lastIndex and emit that it has changed
                this.lastIndex = this.col.selectedIndex;
                this.change.emit(this.col.options[this.col.selectedIndex]);
            }
        }
    };
    PickerColumnCmp.prototype.refresh = function () {
        var min = this.col.options.length - 1;
        var max = 0;
        for (var i = 0; i < this.col.options.length; i++) {
            var opt = this.col.options[i];
            if (!opt.disabled) {
                if (i < min) {
                    min = i;
                }
                if (i > max) {
                    max = i;
                }
            }
        }
        var selectedIndex = util_1.clamp(min, this.col.selectedIndex, max);
        if (selectedIndex !== this.col.selectedIndex) {
            var y = (selectedIndex * this.optHeight) * -1;
            this.update(y, 150, true, true);
        }
    };
    PickerColumnCmp.prototype.isPrevented = function (ev) {
        if (ev.type.indexOf('touch') > -1) {
            // this is a touch event, so prevent mouse events for a while
            this.msPrv = Date.now() + 2000;
        }
        else if (this.msPrv > Date.now() && ev.type.indexOf('mouse') > -1) {
            // this is a mouse event, and a touch event already happend recently
            // prevent the calling method from continuing
            ev.preventDefault();
            ev.stopPropagation();
            return true;
        }
    };
    __decorate([
        core_1.ViewChild('colEle'), 
        __metadata('design:type', core_1.ElementRef)
    ], PickerColumnCmp.prototype, "colEle", void 0);
    __decorate([
        core_1.Input(), 
        __metadata('design:type', Object)
    ], PickerColumnCmp.prototype, "col", void 0);
    __decorate([
        core_1.Output(), 
        __metadata('design:type', core_1.EventEmitter)
    ], PickerColumnCmp.prototype, "change", void 0);
    PickerColumnCmp = __decorate([
        core_1.Component({
            selector: '.picker-col',
            template: '<div *ngIf="col.prefix" class="picker-prefix" [style.width]="col.prefixWidth">{{col.prefix}}</div>' +
                '<div class="picker-opts" #colEle [style.width]="col.optionsWidth">' +
                '<button *ngFor="let o of col.options; let i=index" [style.transform]="o._trans" [style.transitionDuration]="o._dur" [class.picker-opt-selected]="col.selectedIndex === i" [class.picker-opt-disabled]="o.disabled" (click)="optClick($event, i)" type="button" category="picker-opt">' +
                '{{o.text}}' +
                '</button>' +
                '</div>' +
                '<div *ngIf="col.suffix" class="picker-suffix" [style.width]="col.suffixWidth">{{col.suffix}}</div>',
            host: {
                '[style.min-width]': 'col.columnWidth',
                '[class.picker-opts-left]': 'col.align=="left"',
                '[class.picker-opts-right]': 'col.align=="right"',
                '(touchstart)': 'pointerStart($event)',
                '(touchmove)': 'pointerMove($event)',
                '(touchend)': 'pointerEnd($event)',
                '(mousedown)': 'pointerStart($event)',
                '(mousemove)': 'pointerMove($event)',
                '(body:mouseup)': 'pointerEnd($event)',
            }
        }), 
        __metadata('design:paramtypes', [config_1.Config, platform_browser_1.DomSanitizationService])
    ], PickerColumnCmp);
    return PickerColumnCmp;
}());
/**
 * @private
 */
var PickerDisplayCmp = (function () {
    function PickerDisplayCmp(_viewCtrl, _elementRef, _config, params, renderer) {
        this._viewCtrl = _viewCtrl;
        this._elementRef = _elementRef;
        this._config = _config;
        this.d = params.data;
        if (this.d.cssClass) {
            this.d.cssClass.split(' ').forEach(function (cssClass) {
                renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
            });
        }
        this.id = (++pickerIds);
        this.created = Date.now();
        this.lastClick = 0;
    }
    PickerDisplayCmp.prototype.onPageLoaded = function () {
        // normalize the data
        var data = this.d;
        data.buttons = data.buttons.map(function (button) {
            if (util_1.isString(button)) {
                return { text: button };
            }
            if (button.role) {
                button.cssRole = "picker-toolbar-" + button.role;
            }
            return button;
        });
        // clean up dat data
        data.columns = data.columns.map(function (column) {
            if (!util_1.isPresent(column.columnWidth)) {
                column.columnWidth = (100 / data.columns.length) + '%';
            }
            if (!util_1.isPresent(column.options)) {
                column.options = [];
            }
            column.options = column.options.map(function (inputOpt) {
                var opt = {
                    text: '',
                    value: '',
                    disabled: inputOpt.disabled,
                };
                if (util_1.isPresent(inputOpt)) {
                    if (util_1.isString(inputOpt) || util_1.isNumber(inputOpt)) {
                        opt.text = inputOpt.toString();
                        opt.value = inputOpt;
                    }
                    else {
                        opt.text = util_1.isPresent(inputOpt.text) ? inputOpt.text : inputOpt.value;
                        opt.value = util_1.isPresent(inputOpt.value) ? inputOpt.value : inputOpt.text;
                    }
                }
                return opt;
            });
            return column;
        });
    };
    PickerDisplayCmp.prototype.refresh = function () {
        this._cols.forEach(function (column) {
            column.refresh();
        });
    };
    PickerDisplayCmp.prototype._colChange = function (selectedOption) {
        // one of the columns has changed its selected index
        var picker = this._viewCtrl;
        picker.change.emit(this.getSelected());
    };
    PickerDisplayCmp.prototype._keyUp = function (ev) {
        if (this.isEnabled() && this._viewCtrl.isLast()) {
            if (ev.keyCode === 13) {
                if (this.lastClick + 1000 < Date.now()) {
                    // do not fire this click if there recently was already a click
                    // this can happen when the button has focus and used the enter
                    // key to click the button. However, both the click handler and
                    // this keyup event will fire, so only allow one of them to go.
                    void 0;
                    var button = this.d.buttons[this.d.buttons.length - 1];
                    this.btnClick(button);
                }
            }
            else if (ev.keyCode === 27) {
                void 0;
                this.bdClick();
            }
        }
    };
    PickerDisplayCmp.prototype.onPageDidEnter = function () {
        var activeElement = document.activeElement;
        if (activeElement) {
            activeElement.blur();
        }
        var focusableEle = this._elementRef.nativeElement.querySelector('button');
        if (focusableEle) {
            focusableEle.focus();
        }
    };
    PickerDisplayCmp.prototype.btnClick = function (button, dismissDelay) {
        var _this = this;
        if (!this.isEnabled()) {
            return;
        }
        // keep the time of the most recent button click
        this.lastClick = Date.now();
        var shouldDismiss = true;
        if (button.handler) {
            // a handler has been provided, execute it
            // pass the handler the values from the inputs
            if (button.handler(this.getSelected()) === false) {
                // if the return value of the handler is false then do not dismiss
                shouldDismiss = false;
            }
        }
        if (shouldDismiss) {
            setTimeout(function () {
                _this.dismiss(button.role);
            }, dismissDelay || this._config.get('pageTransitionDelay'));
        }
    };
    PickerDisplayCmp.prototype.bdClick = function () {
        if (this.isEnabled() && this.d.enableBackdropDismiss) {
            this.dismiss('backdrop');
        }
    };
    PickerDisplayCmp.prototype.dismiss = function (role) {
        return this._viewCtrl.dismiss(this.getSelected(), role);
    };
    PickerDisplayCmp.prototype.getSelected = function () {
        var selected = {};
        this.d.columns.forEach(function (col, index) {
            var selectedColumn = col.options[col.selectedIndex];
            selected[col.name] = {
                text: selectedColumn ? selectedColumn.text : null,
                value: selectedColumn ? selectedColumn.value : null,
                columnIndex: index,
            };
        });
        return selected;
    };
    PickerDisplayCmp.prototype.isEnabled = function () {
        var tm = this._config.getNumber('overlayCreatedDiff', 750);
        return (this.created + tm < Date.now());
    };
    __decorate([
        core_1.ViewChildren(PickerColumnCmp), 
        __metadata('design:type', core_1.QueryList)
    ], PickerDisplayCmp.prototype, "_cols", void 0);
    __decorate([
        core_1.HostListener('body:keyup', ['$event']), 
        __metadata('design:type', Function), 
        __metadata('design:paramtypes', [KeyboardEvent]), 
        __metadata('design:returntype', void 0)
    ], PickerDisplayCmp.prototype, "_keyUp", null);
    PickerDisplayCmp = __decorate([
        core_1.Component({
            selector: 'ion-picker-cmp',
            template: '<div (click)="bdClick()" tappable disable-activated class="backdrop" role="presentation"></div>' +
                '<div class="picker-wrapper">' +
                '<div class="picker-toolbar">' +
                '<div *ngFor="let b of d.buttons" class="picker-toolbar-button" [ngClass]="b.cssRole">' +
                '<button (click)="btnClick(b)" [ngClass]="b.cssClass" class="picker-button" clear>' +
                '{{b.text}}' +
                '</button>' +
                '</div>' +
                '</div>' +
                '<div class="picker-columns">' +
                '<div class="picker-above-highlight"></div>' +
                '<div *ngFor="let c of d.columns" [col]="c" class="picker-col"> (change)="_colChange($event)"</div>' +
                '<div class="picker-below-highlight"></div>' +
                '</div>' +
                '</div>',
            host: {
                'role': 'dialog'
            },
            directives: [PickerColumnCmp],
            encapsulation: core_1.ViewEncapsulation.None,
        }), 
        __metadata('design:paramtypes', [view_controller_1.ViewController, core_1.ElementRef, config_1.Config, nav_params_1.NavParams, core_1.Renderer])
    ], PickerDisplayCmp);
    return PickerDisplayCmp;
}());
/**
 * Animations for pickers
 */
var PickerSlideIn = (function (_super) {
    __extends(PickerSlideIn, _super);
    function PickerSlideIn(enteringView, leavingView, opts) {
        _super.call(this, opts);
        var ele = enteringView.pageRef().nativeElement;
        var backdrop = new animation_1.Animation(ele.querySelector('.backdrop'));
        var wrapper = new animation_1.Animation(ele.querySelector('.picker-wrapper'));
        backdrop.fromTo('opacity', 0.01, 0.26);
        wrapper.fromTo('translateY', '100%', '0%');
        this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper);
    }
    return PickerSlideIn;
}(transition_1.Transition));
transition_1.Transition.register('picker-slide-in', PickerSlideIn);
var PickerSlideOut = (function (_super) {
    __extends(PickerSlideOut, _super);
    function PickerSlideOut(enteringView, leavingView, opts) {
        _super.call(this, opts);
        var ele = leavingView.pageRef().nativeElement;
        var backdrop = new animation_1.Animation(ele.querySelector('.backdrop'));
        var wrapper = new animation_1.Animation(ele.querySelector('.picker-wrapper'));
        backdrop.fromTo('opacity', 0.26, 0);
        wrapper.fromTo('translateY', '0%', '100%');
        this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper);
    }
    return PickerSlideOut;
}(transition_1.Transition));
transition_1.Transition.register('picker-slide-out', PickerSlideOut);
var pickerIds = -1;
var DECELERATION_FRICTION = 0.97;
var FRAME_MS = (1000 / 60);
