Ox.Progressbar = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            paused: false,
            progress: 0,
            showCancelButton: false,
            showPauseButton: false,
            showPercent: false,
            showTime: false,
            width: 256
        })
        .options(options || {})
        .addClass('OxProgressbar')
        .css({width: self.options.width - 2 + 'px'});

    self.trackWidth = self.options.width
        - self.options.showPercent * 36
        - self.options.showTime * 60
        - self.options.showPauseButton * 16
        - self.options.showCancelButton * 16;

    self.$track = $('<div>')
        .addClass('OxTrack')
        .css({
            width: self.trackWidth - 2 + 'px'
        })
        .appendTo(that);

    self.$progress = $('<div>')
        .addClass('OxProgress')
        .appendTo(self.$track);

    if (self.options.showPercent) {
        self.$percent = $('<div>')
            .addClass('OxText')
            .css({width: '36px'})
            .appendTo(that);
    }

    if (self.options.showTime) {
        self.$time = $('<div>')
            .addClass('OxText')
            .css({width: '60px'})
            .appendTo(that);
    }

    if (self.options.showPauseButton) {
        self.$pauseButton = Ox.Button({
                style: 'symbol',
                title: [
                    {id: 'pause', title: 'pause', selected: !self.options.paused},
                    {id: 'resume', title: 'redo', selected: self.options.paused}
                ],
                tooltip: ['Pause', 'Resume'],
                type: 'image'
            })
            .bindEvent({
                click: togglePaused
            })
            .appendTo(that);
    }

    if (self.options.showCancelButton) {
        self.$cancelButton = Ox.Button({
                style: 'symbol',
                title: 'close',
                tooltip: 'Cancel',
                type: 'image'
            })
            .bindEvent({
                click: cancel
            })
            .appendTo(that);
    }

    !self.options.paused && resume();

    function cancel() {
        self.cancelled = true;
        stop();
        that.triggerEvent('cancel');
    }

    function pause() {
        self.pauseTime = +new Date();
        self.$progress.removeClass('OxAnimate');
        ($.browser.mozilla || $.browser.opera) && clearInterval(self.interval);
        self.$time && self.$time
            .addClass('OxSmall')
            .html(self.cancelled ? 'Cancelled' : 'Paused');
    }

    function resume() {
        self.startTime = !self.startTime
            ? +new Date()
            : self.startTime + +new Date() - self.pauseTime;
        self.$progress.addClass('OxAnimate');
        if ($.browser.mozilla || $.browser.opera) {
            self.offset = 0;
            self.interval = setInterval(function() {
                self.$progress.css({backgroundPosition: --self.offset + 'px 0, 0 0'})
            }, 1000 / 32);
        }
        self.$time && self.$time
            .removeClass('OxSmall')
            .html(self.options.progress ? Ox.formatDuration(that.status().remaining) : 'unknown');
    }

    function setProgress() {
        self.$percent && self.$percent.html(
            Math.floor(self.options.progress * 100) + '%'
        );
        self.$time && self.$time.html(
            self.options.progress ? Ox.formatDuration(that.status().remaining) : 'unknown'
        );
        self.$progress.stop().animate({
            width: Math.round(14 + self.options.progress * (self.trackWidth - 16)) + 'px'
        }, 250, function() {
            self.options.progress == 1 && stop();
        });
    }

    function stop() {
        pause();
        self.$time && self.$time
            .addClass('OxSmall')
            .html(self.cancelled ? 'Cancelled' : 'Complete');
        self.$pauseButton.options({disabled: true});
        self.$cancelButton.options({disabled: true});
    }

    function togglePaused() {
        self.options.paused = !self.options.paused;
        if (self.options.paused) {
            pause()
        } else {
            resume();
        }
        that.triggerEvent(self.options.paused ? 'pause' : 'resume');
    }

    self.setOption = function(key, value) {
        if (key == 'paused') {
            togglePaused();
        } if (key == 'progress') {
            self.options.progress = Ox.limit(self.options.progress, 0, 1);
            !self.options.paused && !self.options.cancelled && setProgress();
        }
    };

    that.status = function() {
        var elapsed = +new Date() - self.startTime,
            remaining = elapsed / self.options.progress * (1 - self.options.progress);
        return {
            elapsed: Math.floor(elapsed / 1000),
            remaining: self.options.progress
                ? Math.ceil(remaining / 1000)
                : Infinity
        };
    };

    return that;

};