/**
 * wavesurfer.js
 *
 * https://github.com/katspaugh/wavesurfer.js
 *
 * This work is licensed under a Creative Commons Attribution 3.0 Unported License.
 */

'use strict';
(function($){
    window.WaveSurfer = {
        defaultParams: {
            height              : 128,
            waveColor           : '#999',
            waveColor2          : '#999',
            progressColor       : '#555',
            progressColor2      : '#555',
            cursorColor         : '#333',
            cursorColor2        : '#333',
            hoverOpacity        : 50,
            cursorWidth         : 2,
            waveMode            : 0,
            gapWidth            : 1,
            minPxPerSec         : 20,
            pixelRatio          : window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI,
            normalize           : false,
            reflection          : false,
            compression         : 1,
            asymmetry           : 2,
            audioContext        : null,
            container           : null,
            backend             : 'WebAudio',
            mediaType           : 'audio',
        },

        init: function (params) {
            this.params = $.extend({}, this.defaultParams, params)

            this.container = 'string' == typeof params.container ?
                document.querySelector(this.params.container) :
                this.params.container

            if (!this.container) {
                throw new Error('Container element not found')
            }

            this.tmpEvents = []

            this.createDrawer()
            this.createBackend()
        },

        createDrawer: function () {
            var my = this

            this.drawer = $.extend({}, WaveSurfer.Drawer)
            this.drawer.init(this.container, this.params)

            this.drawer.on('redraw', function () {
                my.drawBuffer()
                my.drawer.progress(my.backend.getPlayedPercents())
            })

        },

        createBackend: function () {
            var my = this

            if (this.backend) {
                this.backend.destroy()
            }

            this.backend = $.extend({}, WaveSurfer['WebAudio']);
            this.backend.init(this.params);

            this.backend.on('finish', function () { my.fireEvent('finish') })
            this.backend.on('play', function () { my.fireEvent('play') })
            this.backend.on('pause', function () { my.fireEvent('pause') })

            this.backend.on('audioprocess', function (time) {
                my.drawer.progress(my.backend.getPlayedPercents())
                my.fireEvent('audioprocess', time)
            })
        },

        updateDrawer: function() {
            this.drawer.updateSize()
        },

        getDuration: function () {
            return this.backend.getDuration()
        },

        drawBuffer: function () {
            var nominalWidth = Math.round(
                this.getDuration() * this.params.minPxPerSec * this.params.pixelRatio
            )
            var width = this.drawer.getWidth()

            var peaks = this.backend.getPeaks(width)
            this.drawer.drawPeaks(peaks, width)
            this.fireEvent('redraw', peaks, width)
        },

        loadArrayBuffer: function (arraybuffer) {
            this.decodeArrayBuffer(arraybuffer, function (data) {
                this.loadDecodedBuffer(data)
            }.bind(this))
        },

        loadDecodedBuffer: function (buffer) {
            this.backend.load(buffer)
            this.drawBuffer()
            this.fireEvent('ready')
        },

        load: function (url) {
            return this.loadBuffer(url);
        },

        loadBuffer: function (url) {
            this.empty()
            return this.getArrayBuffer(url, this.loadArrayBuffer.bind(this))
        },

        decodeArrayBuffer: function (arraybuffer, callback) {
            this.backend.decodeArrayBuffer(
                arraybuffer,
                this.fireEvent.bind(this, 'decoded'),
                this.fireEvent.bind(this, 'error', 'Error decoding audiobuffer')
            )
            this.tmpEvents.push(
                this.once('decoded', callback)
            )
        },

        getArrayBuffer: function (url, callback) {
            var my = this
            var ajax = this.ajax({
                url: url,
                responseType: 'arraybuffer'
            })
            this.tmpEvents.push(
                ajax.on('progress', function (event) {
                    my.onProgress(event)
                }),
                ajax.on('success', callback),
                ajax.on('error', function (event) {
                    my.fireEvent('error', 'XHR error: ' + event.target.statusText)
                })
            )
            return ajax
        },

        onProgress: function (e) {
            if (e.lengthComputable) {
                var percentComplete = e.loaded / e.total
            } else {
                percentComplete = e.loaded / (e.loaded + 1000000)
            }
            this.fireEvent('loading', Math.round(percentComplete * 100), e.target)
        },

        clearTmpEvents: function () {
            this.tmpEvents.forEach(function (e) { e.un() })
        },

        ajax: function (options) {
            var ajax = $.extend({}, WaveSurfer.Observer)
            var xhr = new XMLHttpRequest()
            var fired100 = false

            xhr.open(options.method || 'GET', options.url, true)
            xhr.responseType = options.responseType || 'json'

            xhr.addEventListener('progress', function (e) {
                ajax.fireEvent('progress', e)
                if (e.lengthComputable && e.loaded == e.total) {
                    fired100 = true
                }
            })

            xhr.addEventListener('load', function (e) {
                if (!fired100) {
                    ajax.fireEvent('progress', e)
                }
                ajax.fireEvent('load', e)

                if (200 == xhr.status || 206 == xhr.status) {
                    ajax.fireEvent('success', xhr.response, e)
                } else {
                    ajax.fireEvent('error', e)
                }
            })

            xhr.addEventListener('error', function (e) {
                ajax.fireEvent('error', e)
            })

            xhr.send()
            ajax.xhr = xhr
            return ajax
        },

        empty: function () {
            this.backend.disconnectSource()
            this.clearTmpEvents()
            this.drawer.clearWave()
        },

        destroy: function () {
            this.fireEvent('destroy')
            this.clearTmpEvents()
            this.unAll()
            this.backend.destroy()
            this.drawer.destroy()
        }
    }

    WaveSurfer.create = function (params) {
        var wavesurfer = $.extend({}, WaveSurfer)
        wavesurfer.init(params)
        return wavesurfer
    }

    WaveSurfer.Observer = {
        on: function (event, fn) {
            if (!this.handlers) { this.handlers = {} }

            var handlers = this.handlers[event]
            if (!handlers) {
                handlers = this.handlers[event] = []
            }
            handlers.push(fn)

            return {
                name: event,
                callback: fn,
                un: this.un.bind(this, event, fn)
            }
        },

        un: function (event, fn) {
            if (!this.handlers) { return }

            var handlers = this.handlers[event]
            if (handlers) {
                if (fn) {
                    for (var i = handlers.length - 1; i >= 0; i--) {
                        if (handlers[i] == fn) {
                            handlers.splice(i, 1)
                        }
                    }
                } else {
                    handlers.length = 0
                }
            }
        },

        unAll: function () {
            this.handlers = null
        },

        once: function (event, handler) {
            var my = this
            var fn = function () {
                handler.apply(this, arguments)
                setTimeout(function () {
                    my.un(event, fn)
                }, 0)
            }
            return this.on(event, fn)
        },

        fireEvent: function (event) {
            if (!this.handlers) { return }
            var handlers = this.handlers[event]
            var args = Array.prototype.slice.call(arguments, 1)
            handlers && handlers.forEach(function (fn) {
                fn.apply(null, args)
            })
        }
    }

    $.extend(WaveSurfer, WaveSurfer.Observer)


    WaveSurfer.Drawer = {
        init: function (container, params) {
            this.container = container
            this.params = params
            this.progress = 0
            this.position = -1
            this.playing = false

            this.lastPos = 0
            this.createWrapper()
            this.createElements()
            this.centerLine = this.container.clientHeight / ( 1 + 1 / this.params.asymmetry )
        },

        createWrapper: function () {
            this.wrapper = this.container
            this.setupWrapperEvents()
        },

        handleEvent: function (e) {
            e.preventDefault()
            var bbox = this.wrapper.getBoundingClientRect()
            return ((e.clientX - bbox.left + this.wrapper.scrollLeft) / this.wrapper.scrollWidth) || 0
        },

        setupWrapperEvents: function () {
            var my = this

            this.wrapper.addEventListener('click', function a(e) {
                var scrollbarHeight = my.wrapper.height - my.wrapper.clientHeight
                if (scrollbarHeight != 0) {
                    // scrollbar is visible.  Check if click was on it
                    var bbox = my.wrapper.getBoundingClientRect()
                    if (e.clientY >= bbox.bottom - scrollbarHeight) {
                        // ignore mousedown as it was on the scrollbar
                        return
                    }
                }

                // my.updateProgress(e.offsetX / my.width)
                e.isCommentArea = e.offsetY > my.centerLine

                my.fireEvent( 'click', e, my.handleEvent(e) )
            })

            this.wrapper.addEventListener('mousemove', function a(e) {
                e.isCommentArea = e.offsetY > my.centerLine
                my.fireEvent('mousemove', e)
            })

            this.wrapper.addEventListener('mouseover', function a(e) {
                e.isCommentArea = e.offsetY > my.centerLine
                my.fireEvent( 'mouseover', e )
            })

            this.wrapper.addEventListener('mouseout', function a(e) {
                e.isCommentArea = e.offsetY > my.centerLine
                my.fireEvent( 'mouseout', e )
            })

            this.wrapper.addEventListener('scroll', function a(e) {
                my.fireEvent('scroll', e)
            })
        },

        drawPeaks: function (peaks, width) {
            this.resetScroll()
            this.setWidth(width)

            this.params.waveMode ?
                this.drawBars(peaks) :
                this.drawWave(peaks)
        },

        style: function (el, styles) {
            Object.keys(styles).forEach(function (prop) {
                if (el.style[prop] !== styles[prop]) {
                    el.style[prop] = styles[prop]
                }
            })
            return el
        },

        resetScroll: function () {
            if (this.wrapper !== null) {
                this.wrapper.scrollLeft = 0
            }
        },

        getWidth: function () {
            return Math.round(this.width * this.params.pixelRatio)
        },

        setWidth: function (width) {
            if (width == this.width) { return }

            this.width = width

            this.updateSize()
        },

        setHeight: function (height) {
            if (height == this.height) { return }
            this.height = height
            this.style(this.wrapper, {
                height: ~~(this.height / this.params.pixelRatio) + 'px'
            })
            this.updateSize()
        },

        destroy: function () {
            this.unAll()
            if (this.wrapper) {
                this.container.removeChild(this.canvas)
                this.wrapper = null
            }
        },

        createElements: function () {

            this.canvas = this.wrapper.appendChild(
                this.style(document.createElement('canvas'), {
                    height: '100%',
                    width: '100%',
                })
            )
            this.centerLine = this.canvas.height / ( 1 + 1 / this.params.asymmetry)
            this.canvasCc = this.canvas.getContext('2d')

            var wave = document.createElement('canvas')
            this.waveCc = wave.getContext('2d')

            var progress = document.createElement('canvas')
            this.progressCc = progress.getContext('2d')

            var position = document.createElement('canvas')
            this.positionCc = position.getContext('2d')

        },

        updateSize: function () {
            this.height = this.params.height * this.params.pixelRatio
            this.canvasCc.canvas.width = this.waveCc.canvas.width = this.progressCc.canvas.width = this.positionCc.canvas.width = this.width
            this.canvasCc.canvas.height = this.waveCc.canvas.height = this.progressCc.canvas.height = this.positionCc.canvas.height = this.height
            this.centerLine = this.container.clientHeight / ( 1 + 1 / this.params.asymmetry )
        },

        clearWave: function () {
            this.progress = 0
            this.position = -1
            this.canvasCc.clearRect(0, 0, this.width, this.height)
            this.waveCc.clearRect(0, 0, this.width, this.height)
            this.progressCc.clearRect(0, 0, this.width, this.height)
            this.positionCc.clearRect(0, 0, this.width, this.height)
        },

        drawBars: function (peaks) {
            this.peaks = peaks

            if (typeof(peaks) == 'undefined') return false

            var width = this.width,
                height = this.params.height * this.params.pixelRatio,
                length = ~~(peaks.length / 2),
                bar = this.params.waveMode * this.params.pixelRatio,
                gap = this.params.gapWidth * this.params.pixelRatio, //Math.max(this.params.pixelRatio, ~~(bar / 2)),
                cl = this.params.pixelRatio,
                step = bar + gap,
                a = this.params.asymmetry,
                c = this.params.compression,
                scale = width / length,
                halfH = height / (1 + 1/a)

            var absmax = 1
            if (this.params.normalize) {
                var min, max
                max = Math.max.apply(Math, peaks)
                min = Math.min.apply(Math, peaks)
                absmax = max
                if (-min > absmax) {
                    absmax = -min
                }
            }

            var waves = []
            waves.push({cc: this.waveCc, color1: this.params.waveColor, color2: this.params.waveColor2, alpha: 1 })
            waves.push({cc: this.progressCc, color1: this.params.progressColor, color2: this.params.progressColor2, alpha: 1 })
            waves.push({cc: this.positionCc, color1: this.params.progressColor, color2: this.params.progressColor2, alpha: this.params.hoverOpacity/100})

            waves.forEach(function (el) {

                var cc = el.cc,
                    halfH = el.cc.canvas.height / ( 1 + 1 / this.params.asymmetry)
                if (!cc) { return }

                var grd = this.waveCc.createLinearGradient(0, 0, 0, halfH)
                grd.addColorStop(0, el.color1)
                grd.addColorStop(1, typeof(el.color2) != 'undefined' ? el.color2 : el.color1)
                cc.fillStyle = grd
                cc.globalAlpha = el.alpha

                for (var i = 0; i < width; i += step) {
                    var peak = peaks[Math.floor(2 * i / scale)]
                    peak = Math.pow(Math.abs(peak), 1/c)
                    var h = halfH * peak / absmax
                    h = Math.max(this.params.pixelRatio, Math.abs(h))
                    cc.fillRect(i, halfH - h - cl/2, bar, h)
                    cc.save()
                    cc.fillStyle = el.color1
                    cc.fillRect(i, halfH + cl/2, bar, h * 1/a)
                    cc.restore()
                }
            }, this)
            if ( this.waveCc.canvas.width ) this.canvasCc.drawImage(this.waveCc.canvas, 0, 0)
            this.fireEvent('waveComplete')
        },

        drawWave: function (peaks, channelIndex) {
            if (typeof(peaks) == 'undefined') return false

            this.peaks = peaks

            var width = this.width,
                height = this.params.height * this.params.pixelRatio,
                length = ~~(peaks.length / 2),
                cl = this.params.pixelRatio,
                a = this.params.asymmetry,
                c = this.params.compression,
                scale = width / length,
                halfH = height / (1 + 1/a)

            var absmax = 1
            if (this.params.normalize) {
                var min, max
                max = Math.max.apply(Math, peaks)
                min = Math.min.apply(Math, peaks)
                absmax = max
                if (-min > absmax) {
                    absmax = -min
                }
            }

            var waves = []
            waves.push({cc: this.waveCc, color1: this.params.waveColor, color2: this.params.waveColor2, alpha: 1 })
            waves.push({cc: this.progressCc, color1: this.params.progressColor, color2: this.params.progressColor2, alpha: 1 })
            waves.push({cc: this.positionCc, color1: this.params.progressColor, color2: this.params.progressColor2, alpha: this.params.hoverOpacity/100})

            waves.forEach(function (el) {
                if ( ! el.cc.canvas.height ) return
                var cc = el.cc,
                    halfH = el.cc.canvas.height / ( 1 + 1 / this.params.asymmetry)
                if (!cc) { return }

                var grd = el.cc.createLinearGradient(0, 0, 0, halfH)
                grd.addColorStop(0, el.color1)
                grd.addColorStop(1, typeof(el.color2) != 'undefined' ? el.color2 : el.color1)
                cc.fillStyle = grd
                cc.globalAlpha = el.alpha

                if (this.params.reflection) {
                    cc.beginPath()
                    cc.moveTo(0, halfH)
                    for (var i = 0; i < length; i++) {
                        var peak = peaks[2 * i]
                        peak = Math.pow(Math.abs(peak), 1/c)
                        var h = peak / absmax * halfH
                        cc.lineTo(i * scale, halfH - h)
                    }
                    cc.lineTo(el.cc.canvas.width, halfH)
                    cc.closePath()
                    cc.fill()
                    cc.fillStyle = el.color1
                    cc.beginPath()
                    cc.moveTo(el.cc.canvas.width, halfH)
                    for (var i = length - 1; i >= 0; i--) {
                        var peak = peaks[2 * i]
                        peak = Math.pow(Math.abs(peak), 1/c)
                        var h = peak / absmax * halfH
                        cc.lineTo(i * scale, halfH + h * 1/a)
                    }
                    cc.lineTo(0, halfH)
                    cc.closePath()
                    cc.fill()
                } else {
                    cc.beginPath()
                    cc.moveTo(0, halfH)
                    for (var i = 0; i < length; i++) {
                        var peak = peaks[2 * i]
                        peak = Math.pow(Math.abs(peak), 1/c)
                        var h = peak / absmax * halfH
                        cc.lineTo(i * scale, halfH - h)
                    }
                    cc.closePath()
                    cc.fill()
                    cc.fillStyle = el.color1
                    cc.beginPath()
                    cc.moveTo(el.cc.canvas.width, halfH)
                    for (var i = length - 1; i >= 0; i--) {
                        var peak = peaks[2 * i + 1]
                        peak = Math.pow(Math.abs(peak), 1/c)
                        var h = peak / absmax * halfH
                        cc.lineTo(i * scale, halfH + h * 1/a)
                    }
                    cc.closePath()
                    cc.fill()
                }

            }, this)
            if ( this.waveCc.canvas.width ) this.canvasCc.drawImage(this.waveCc.canvas, 0, 0)
            this.fireEvent('waveComplete')
        },

        updateProgress: function (progress) {
            this.progress = progress
            var prog = Math.round(this.width * this.progress)
            var pos = Math.round(this.width * this.position)
            this.redrawContexts(prog, pos)
        },

        updatePosition: function (position) {
            var prog = Math.round(this.width * this.progress)
            this.position = position
            var pos = Math.round(this.width * position)
            this.redrawContexts(prog, pos)
        },

        redrawContexts: function(prog, pos) {
            var self = this

            var redraw = function(s1, e1, s2, e2) {
                cc.save()
                cc.beginPath()
                cc.rect(s1, 0, e1, self.height)
                cc.clip()
                if ( self.progressCc.canvas.width ) cc.drawImage(self.progressCc.canvas, 0, 0)
                cc.restore()
                cc.save()
                cc.beginPath()
                cc.rect(s2, 0, e2, self.height)
                cc.clip()
                if ( self.positionCc.canvas.width ) cc.drawImage(self.positionCc.canvas, 0, 0)
                cc.restore()

            }

            var cc = this.canvasCc

            cc.clearRect(0, 0, this.width, this.height)
            if ( this.waveCc.canvas.width ) cc.drawImage(this.waveCc.canvas, 0, 0)
            if ( pos < 0 ) {
                redraw(0, prog, 0, 0)
            } else {
                if (prog > pos) {
                    redraw(0, pos, pos, prog - pos)
                } else {
                    redraw(0, prog, prog, pos - prog)
                }
            }
            this.drawCursor(prog)
        },

        drawCursor: function(prog) {
            if (this.playing) {
                var halfH = ( this.params.height * this.params.pixelRatio ) / (1 + 1 / this.params.asymmetry)
                var t = this.params.cursorWidth * this.params.pixelRatio
                var cc = this.canvasCc
                var grd = this.waveCc.createLinearGradient(0, 0, 0, this.height)
                grd.addColorStop(0, this.params.cursorColor)
                grd.addColorStop(1, typeof(this.params.cursorColor2) != 'undefined' ? this.params.cursorColor2 : this.params.cursorColor)
                cc.fillStyle = grd
                cc.fillRect(prog - t/2, 0, t, this.height)
            }
        },

        colorLuminance: function(hex, lum) {

            hex = String(hex).replace(/[^0-9a-f]/gi, '')
            if (hex.length < 6) {
                hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]
            }
            lum = lum || 0

            var rgb = "#", c, i
            for (i = 0; i < 3; i++) {
                c = parseInt(hex.substr(i*2,2), 16)
                c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16)
                rgb += ("00"+c).substr(c.length)
            }

            return rgb
        }
    }

    $.extend(WaveSurfer.Drawer, WaveSurfer.Observer)



    WaveSurfer.WebAudio = {
        scriptBufferSize: 256,

        supportsWebAudio: function () {
            return !!(window.AudioContext || window.webkitAudioContext)
        },

        getAudioContext: function () {
            // if (!WaveSurfer.WebAudio.audioContext) {
            //     WaveSurfer.WebAudio.audioContext = new (
            //         window.AudioContext || window.webkitAudioContext
            //     );
            // }
            // return WaveSurfer.WebAudio.audioContext;
            if (!document.wvplAudioContext) {
                var AudioContext = window.AudioContext || window.webkitAudioContext
                try {
                    document.wvplAudioContext = new AudioContext;
                }
                catch(error) {
                    console.log(error);
                }
            }
            return document.wvplAudioContext
        },

        getOfflineAudioContext: function (sampleRate) {
            if (!this.offlineAudioContext) {
                this.offlineAudioContext = new (
                    window.OfflineAudioContext || window.webkitOfflineAudioContext
                )(1, 2, sampleRate)
            }
            return this.offlineAudioContext
        },

        init: function (params) {
            this.params = params
            // if (!document.wvplAudioContext && this.supportsWebAudio()) {
            //     var AudioContext = window.AudioContext || window.webkitAudioContext
            //     document.wvplAudioContext = new AudioContext
            // }
            // this.ac = document.wvplAudioContext
            this.ac = params.audioContext || this.getAudioContext();

            if ( ! this.ac ) return;

            this.lastPlay = this.ac.currentTime
            this.startPosition = 0
            this.scheduledPause = null

            this.createScriptNode()
            this.createAnalyserNode()

        },

        createScriptNode: function () {
            if (this.ac.createScriptProcessor) {
                this.scriptNode = this.ac.createScriptProcessor(this.scriptBufferSize)
            } else {
                this.scriptNode = this.ac.createJavaScriptNode(this.scriptBufferSize)
            }

            this.scriptNode.connect(this.ac.destination)
        },

        createAnalyserNode: function () {
            this.analyser = this.ac.createAnalyser()
        },

        decodeArrayBuffer: function (arraybuffer, callback, errback) {
            if (!this.offlineAc) {
                this.offlineAc = this.getOfflineAudioContext(this.ac ? this.ac.sampleRate : 44100)
            }
            this.offlineAc.decodeAudioData(arraybuffer, (function (data) {
                callback(data)
            }).bind(this), errback)
        },

        getPeaks: function (length) {
            var sampleSize = this.buffer.length / length
            var sampleStep = ~~(sampleSize / 10) || 1
            var channels = this.buffer.numberOfChannels
            var splitPeaks = []
            var mergedPeaks = []

            for (var c = 0; c < channels; c++) {
                var peaks = splitPeaks[c] = []
                var chan = this.buffer.getChannelData(c)

                for (var i = 0; i < length; i++) {
                    var start = ~~(i * sampleSize)
                    var end = ~~(start + sampleSize)
                    var min = chan[0]
                    var max = chan[0]

                    for (var j = start; j < end; j += sampleStep) {
                        var value = chan[j]

                        if (value > max) {
                            max = value
                        }

                        if (value < min) {
                            min = value
                        }
                    }

                    peaks[2 * i] = max
                    peaks[2 * i + 1] = min

                    if (c == 0 || max > mergedPeaks[2 * i]) {
                        mergedPeaks[2 * i] = max
                    }

                    if (c == 0 || min < mergedPeaks[2 * i + 1]) {
                        mergedPeaks[2 * i + 1] = min
                    }
                }
            }

            return this.params.splitChannels ? splitPeaks : mergedPeaks
        },

        disconnectSource: function () {
            if (this.source) {
                this.source.disconnect()
            }
        },

        destroy: function () {
            if (!this.isPaused()) {
                this.pause()
            }
            this.unAll()
            this.buffer = null
            this.disconnectFilters()
            this.disconnectSource()
            this.gainNode.disconnect()
            this.scriptNode.disconnect()
            this.analyser.disconnect()
        },

        load: function (buffer) {
            this.startPosition = 0
            this.lastPlay = this.ac.currentTime
            this.buffer = buffer
            this.createSource()
        },

        createSource: function () {
            this.disconnectSource()
            this.source = this.ac.createBufferSource()

            this.source.buffer = this.buffer
            this.source.connect(this.analyser)
        },

        getDuration: function () {
            if (!this.buffer) {
                return 0
            }
            return this.buffer.duration
        },

    }

    $.extend(WaveSurfer.WebAudio, WaveSurfer.Observer)

})(jQuery);

jQuery(window).load(function(){

    var $ = jQuery;

  	window.WavePlayer = {};
    window.WavePlayer.audio = new Audio();
    window.WavePlayer.audio.preload = 'none';
	window.WavePlayer.playlists = [];

    const STATUS_STOP = 0,
          STATUS_PLAY = 1,
          STATUS_PAUSE = 2;

    const ANIMATION_TIME = 300;

    var WavePlayerClass = function(element) {

        var elem = $(element),
            obj = this;

        this.audio = window.WavePlayer.audio;

        var getData = function(name) {
            return elem.attr('data-'+name);
        }

        var wavesurfer = $.extend({}, WaveSurfer),
            peaks,
            runtime = 0,
            info = getData('info'),
            lastStart = 0;

        var infoBar             = elem.find('.wvpl-infobar'),
            posterImg           = elem.find('.wvpl-poster'),
            overlay             = elem.find('.wvpl-overlay'),
            infoText            = elem.find('.wvpl-playing-info'),
            playlist            = elem.find('.wvpl-playlist'),
            playlistWrapper     = elem.find('.wvpl-playlist-wrapper'),
            waveform            = elem.find('.wvpl-waveform'),
            position            = elem.find('.wvpl-position'),
            duration            = elem.find('.wvpl-duration'),
            nextBtn             = elem.find('.wvpl-next'),
            prevBtn             = elem.find('.wvpl-prev'),
            playBtn             = elem.find('.wvpl-play'),
            timeLine            = elem.find('.wvpl-timeline'),
            centerLine          = elem.find('.wvpl-centerline'),
            pointer             = elem.find('.wvpl-pointer'),
            infoBtn             = elem.find('.wvpl-info'),
            volumeBtn           = elem.find('.wvpl-volume');

        /**
        *
        * Initializes the wavesurfer object to the waveform element of the current WavePlayer instance
        *
        */
        var wavesurferInit = function() {

            wavesurfer.init({
                container:          waveform[0],
                waveColor:          getData('wave_color'),
                waveColor2:         getData('wave_color_2'),
                progressColor:      getData('progress_color'),
                progressColor2:     getData('progress_color_2'),
                cursorColor:        getData('cursor_color'),
                cursorColor2:       getData('cursor_color_2'),
                cursorWidth:        getData('cursor_width'),
                hoverOpacity:       parseInt(getData('hover_opacity')),
                waveMode:           parseInt(getData('wave_mode')),
                gapWidth:           parseInt(getData('gap_width')),
                compression:        parseInt(getData('wave_compression')),
                asymmetry:          parseInt(getData('wave_asymmetry')),
                height:             waveform.outerHeight(false),
                normalize:          true,
                reflection:         true,
                hideScrollbar:      true,
            });

            wavesurfer.on('loading', function(p,t){

                overlay.find('.percentage').html( p+'%' );
                overlay.find('.wvpl-loading>div').css({width:p+'%'});
                overlay.find('.message').html( wvplVars.messages.audioAnalysis );

            });

            wavesurfer.on('error', function(e){
                console.log(e);
            })

            /**
            *
            * Called when the wavesurfer object finishes reading the audio
            * tries to save a peak file in the peak subfolder of the plugin
            *
            */
            wavesurfer.on('ready', function() {

                peaks = wavesurfer.backend.getPeaks(960).map(function(e){
                    return Number(e.toFixed(3));
                });
                peaks = peaks.join();

                overlay.find('.message').html( wvplVars.messages.savingPeaks );

                var thisTrack = obj.tracks[obj.currentTrack];

                if (thisTrack.id) {
                    var postData = {
                        action: 'waveplayer_write_peaks',
                        id: thisTrack.id,
                        temp_file: thisTrack.temp_file,
                        peaks: peaks,
                    };
                    var callback = function(result) {
                        if ( result.success ) {
                            if ( thisTrack.temp_file ) thisTrack.temp_file = null
                            obj.status = STATUS_STOP
                            displayInfo( thisTrack );
                            duration.text(thisTrack.length_formatted)
                            position.text(lengthFormatted())
                            peaks = result.data.peaks.split(',').map(parseFloat);
                            thisTrack.peaks = peaks
                            overlay.find('.message').html( wvplVars.messages.waveformRendering );
                            waveRedraw();
                            obj.analyzed();
							preloadTrack();
		                    displayPlaylist();
							obj.activated()
                            if ( obj.autoplay ) obj.play()
                        }
                    }
                    ajaxCall( postData, callback );
                }
            });

            /**
            *
            * Relocate the progress position when the user click on the waveform
            *
            */
            wavesurfer.drawer.on('click', function(event) {
                event.preventDefault();
                var switched = WavePlayer.currentPlayer != elem.attr('id');
                WavePlayer.currentPlayer = elem.attr('id');
                if ( switched ) {
                    $('.waveplayer').each(function(index, el){
                        if ( $(el).attr('id') != WavePlayer.currentPlayer ) {
                            $(el).waveplayer('reset');
                        }
                    });
                    obj.play();
                } else if (obj.status == STATUS_PLAY) {
                    var d = new Date();
                    runtime += d.getTime() - lastStart;
                    lastStart = (new Date()).getTime();
                    progress(event.offsetX / wavesurfer.container.clientWidth);
                    obj.audio.currentTime = obj.tracks[obj.currentTrack].length * event.offsetX / wavesurfer.container.clientWidth;
                } else {
                    obj.play();
                }
            });
            wavesurfer.drawer.on('mousemove', function(event) {
                if (wavesurfer.drawer.playing) wavesurfer.drawer.updatePosition(event.offsetX / ( wavesurfer.drawer.width / wavesurfer.drawer.params.pixelRatio ));
            });

            wavesurfer.drawer.on('mouseout', function(event) {
                if (wavesurfer.drawer.playing) wavesurfer.drawer.updatePosition(-1);
            });

        }

        /**
        *
        * Reset the waveform element to display a blank canvas
        *
        */
        var progress = function(pos){
            wavesurfer.drawer.updateProgress(pos);
        }

        /**
        *
        * Reset the waveform element to display a blank canvas
        *
        */
        var waveReset = function(){
            wavesurfer.drawer.clearWave();
            progress(0);
        }

        /**
        *
        * Redraw the current waveform
        *
        */
        var waveRedraw = function(){
            if (obj.currentTrack >= 0) {
                wavesurfer.params.height = waveform.height() - parseInt(waveform.css("marginTop")) + parseInt(waveform.css("marginBottom"));
                wavesurfer.updateDrawer();
                wavesurfer.drawer.clearWave();
                wavesurfer.drawer.drawPeaks(peaks, getRealWaveformWidth() * wavesurfer.params.pixelRatio);
            }
        }

        var getRealWaveformWidth = function() {
            var width = waveform.width();
            var hiddenAncestor = waveform.parentsUntil(':visible').last()[0];
            if ( hiddenAncestor ) {
                var root = $(hiddenAncestor).parent();
                var clone = $(hiddenAncestor).clone().css({opacity:0, display:'block'}).appendTo(root);
                width = clone.find('.wvpl-waveform').width();
                clone.remove();
            }
            return width;
        }

        /**
        *
        * Initializes the current WavePlayer instance
        *
        * @param params, not in use (for future development)
        */
        this.init = function(tracks) {
            obj.status = STATUS_STOP;
            waveform.empty();
            playlistWrapper.empty();
            var $bar = $('<div>', {class:'bar'})
            overlay.append(
                $('<div>', {id:'wave-animation'}),
                $('<div>', {class:'percentage'}),
                $('<div>', {class:'wvpl-loading'}).append($('<div>')),
                $('<div>', {class:'message'})
            );
            var overlayMainColor = getData('wave_color_2');
            overlay.css({color:overlayMainColor});
            overlay.find('.wvpl-loading>div').css({backgroundColor: overlayMainColor} );
            for ( var i = 0; i < 8; i++ ) {
                overlay.find( '#wave-animation' ).append( $('<div>', {class:'bar'}).css({backgroundColor: overlayMainColor} ) );
            }

            var asymmetry = parseInt(getData('wave_asymmetry'))
            asymmetry = asymmetry * 100 / ( 1 + asymmetry )
            position.css({top:asymmetry+'%'})
            duration.css({top:asymmetry+'%'})

            var ids = elem.data( 'ids' ),
                url = elem.data( 'url' )

            var playerID = elem.attr('id');

            obj.tracks = tracks;

            if (wvplVars.options.full_width_playlist) playlist.css({'margin-left': 0});

            // Initializes the wavesurfer plugin for this instance
            wavesurferInit()

            // Gets the options for this player from the waveplayer dataset
            obj.autoplay = elem.data( 'autoplay' )
            obj.repeatAll = elem.data( 'repeat_all' )

            if (obj.tracks && obj.tracks.length > 0) {
                obj.currentTrack = 0;
                if (obj.tracks[obj.currentTrack].temp_file) {
                    obj.analyzing();
                    wavesurfer.load( obj.tracks[obj.currentTrack].temp_file );
                } else {
                    infoBar.toggle(info != 'none');
                    playlist.toggle(info == 'playlist' && obj.tracks.length > 1);
                    displayPlaylist();
                    obj.status = STATUS_STOP;
                    wavesurfer.drawer.playing = false;
                    preloadTrack();
                    obj.activated()
                }
				obj.loaded();
            } else {
                playBtn.addClass('wvpl-disabled');
                elem.hide()
            }
        }

        /**
        *
        * Updates the current position of the player
        *
        */
        this.timeUpdate = function() {
            var pc = obj.audio.currentTime / obj.tracks[obj.currentTrack].length;
            progress(pc);
            position.text(lengthFormatted( obj.audio.currentTime ))
        }

        /**
        *
        * Returns the current position of the player
        *
        */
        this.getCurrentTime = function() {
            return obj.audio.currentTime;
        }

        /**
        *
        * Sets the current position of the player
        *
        */
        this.setCurrentTime = function(position) {
            if ( position < 0 || position > obj.tracks[obj.currentTrack].length ) return false;
            obj.audio.currentTime = position;
            obj.timeUpdate()
        }

        /**
        *
        * Starts the playback
        *
        */
        this.play = function() {
            lastStart = new Date();
            var switched = WavePlayer.currentPlayer != elem.attr('id');
            WavePlayer.currentPlayer = elem.attr('id');
			if ( switched ) {
				$('.waveplayer').each(function(index, el){
					$(el).waveplayer('reset');
				});
			}
            if ( obj.status == STATUS_STOP || obj.audio.src != location.protocol + obj.tracks[obj.currentTrack].file ) {
                obj.audio.pause();
                obj.audio.src = obj.tracks[obj.currentTrack].file;
	            if ( obj.status == STATUS_STOP ) obj.audio.load();
            }
            obj.audio.play();
            obj.playing();
            obj.status = STATUS_PLAY;
            wavesurfer.drawer.playing = true;
        }

        /**
        *
        * Pauses the playback
        *
        */
        this.pause = function() {
            obj.paused();
            var d = new Date();
            runtime += d.getTime() - lastStart;
            obj.audio.pause();
            wavesurfer.drawer.playing = false;
            obj.status = STATUS_PAUSE;
        }

        /**
         *
         * Stops the playback
         *
         * @param   (bool) reset    if true resets the playlist to the first track
         *
         */
        this.stop = function() {
            if (obj.status == STATUS_STOP) return false;
            obj.reset();
            obj.audio.pause();
            obj.audio.currentTime = 0;
            position.text(lengthFormatted(0))
            obj.currentTrack = 0;
            preloadTrack();
        }

        /**
        *
        * Skips to the next or previous track
        *
        * @param    (bool) next     if true or undefined, skips to next track, if false, skip to previous track
        */
        this.skip = function(next) {
            obj.pause();
            setTimeout(function(){
                if (obj.currentTrack >= 0) obj.updateStatistics();
                if (next == null) next = true;
                next ? obj.currentTrack++ : obj.currentTrack--;
                if ( !obj.repeatAll && ( (obj.currentTrack >= obj.tracks.length) || (obj.currentTrack < 0) ) ) {
		            obj.loading();
                    obj.stop();
                    obj.autoplay = false;
                    if (wvplVars.options.jump) {
                        var $nextPlayer = $(".waveplayer").eq( $(".waveplayer").index( $('#'+WavePlayer.currentPlayer) ) + 1 );
                        if ( $nextPlayer.length ) {
                            $('html').animate({
                                scrollTop: $nextPlayer.offset().top
                            }, 400, function(event) {
                                $nextPlayer.waveplayer('play');
                            });
                        }
                    }
                    preloadTrack();
                } else {
                    if (obj.currentTrack == obj.tracks.length) obj.currentTrack = 0;
                    if (obj.currentTrack < 0) obj.currentTrack = obj.tracks.length - 1;
		            obj.loading();
                    scrollTo( obj.currentTrack );
                    obj.status = STATUS_STOP;
                    wavesurfer.drawer.playing = false;
                    preloadTrack(true);
                }
            }, ANIMATION_TIME)
        }

        /**
        *
        * Skips to the next or previous track
        *
        * @param    (bool) next     if true or undefined, skips to next track, if false, skip to previous track
        */
        this.skipTo = function(index) {
            if (index == null || index < 0 || index >= obj.tracks.length) return false;
            if ( index == obj.currentTrack && obj.audio.paused ) {
                obj.play();
                return true;
            }
            obj.pause();
            setTimeout(function(){
                if (obj.currentTrack >= 0 && obj.currentTrack < obj.tracks.length) obj.updateStatistics();
                obj.currentTrack = index;
	            obj.loading();
                preloadTrack(true);
            }, ANIMATION_TIME)
        }


        var scrollTo = function(index) {
            var $el = playlist.find('li').eq(index);
            if ( !$el ) return false;

            if ( $el.position().top + $el.height() > playlist.scrollTop() + playlist.height() ) {
                playlist.animate({scrollTop: $el.position().top +$el.outerHeight() - playlist.outerHeight()});
            }
            if ( $el.position().top < playlist.scrollTop() ) {
                playlist.animate({scrollTop: $el.position().top});
            }
        }


        this.analyzing = function() {
            elem.addClass( 'analyzing' );
            elem.trigger( 'analyzing', [ obj.tracks[obj.currentTrack] ] );
        }

        this.analyzed = function() {
            elem.removeClass( 'analyzing' );
            overlay.find('.percentage').html('&nbsp;');
            overlay.find('.wvpl-loading>div').css({width:0});
            overlay.find('.message').html('&nbsp;');
            elem.trigger( 'analyzed', [ obj.tracks[obj.currentTrack], peaks ] );
        }

        this.loading = function() {
            elem.addClass('loading');
            elem.trigger( 'loading', [ obj.tracks[obj.currentTrack] ] );
        }

        this.loaded = function() {
            elem.removeClass('loading');
            elem.trigger( 'loaded', [ obj.tracks[obj.currentTrack] ] );
        }

        this.playing = function() {
            elem.addClass('playing');
            playlist.find('li').removeClass('playing')
            playlist.find('li:nth-child('+(1*obj.currentTrack+1)+')').addClass('playing')
            elem.trigger( 'playing', [ obj.tracks[obj.currentTrack], WavePlayer.audio.currentTime ] );
        }

        this.paused = function() {
            elem.removeClass('playing');
            playlist.find('li').removeClass('playing')
            elem.trigger( 'paused', [ obj.tracks[obj.currentTrack], WavePlayer.audio.currentTime ] );
        }

        this.ready = function() {
            obj.loaded();
            elem.trigger( 'ready', [ obj.tracks[obj.currentTrack] ] );
        }

        this.activated = function() {
            elem.addClass( 'active' );
            elem.trigger( 'activated', [ elem.attr('id'), $('.waveplayer').index(elem) ] );
        }

        this.refresh = function() {
            waveRedraw();
        }

        /**
        *
        * Preloads the current track
        *
        * @param playnow, if true, after preloading the track, automatically starts the playback
        */
        var preloadTrack = function(playnow) {
            if (obj.currentTrack < 0) obj.currentTrack = 0;
            checkSkipState();
            if ( typeof( playnow ) == 'undefined' ) playnow = false;
            if (obj.tracks.length == 1 && obj.repeatAll) obj.audio.loop = true;

            var thisTrack = obj.tracks[obj.currentTrack]

            if ( thisTrack.peaks ) {
                displayInfo( thisTrack );
                duration.text(thisTrack.length_formatted)
                position.text(lengthFormatted())
                peaks = thisTrack.peaks;
                waveRedraw();
                obj.loaded();
                if ( playnow || obj.autoplay ) obj.play();
            } else {
				var audio_file = thisTrack.file;
                if (obj.tracks[obj.currentTrack].temp_file) {
					audio_file = obj.tracks[obj.currentTrack].temp_file;
                }
                obj.loaded();
                obj.analyzing();
                wavesurfer.load(audio_file);
            }
        }

        this.reload = function() {
            preloadTrack();
        }

        this.reset = function() {
            obj.status = STATUS_STOP;
            wavesurfer.drawer.playing = false;
            progress(0);
            obj.paused();
            position.text(lengthFormatted(0))
            obj.status = STATUS_STOP;
        }

        /**
        *
        * Toggle the playback status
        *
        */
        this.toggle = function() {
            switch (obj.status) {
                case STATUS_PLAY:
                    obj.pause();
                    break;
                case STATUS_STOP:
                case STATUS_PAUSE:
                    obj.play();
                    break;
            }
        }

        this.toggleInfo = function() {
            if (info == 'playlist' && obj.tracks.length == 1 ) info = 'bar';
            switch (info) {
                // No info
                case 'playlist':
                    playlist.slideUp();
                    infoBar.fadeOut().addClass('wvpl-inactive');
                    infoBtn.removeClass('wvpl-times wvpl-list');
                    info = 'none';
                    break;
                // Info Bar only
                case 'none':
                    infoBar.show().removeClass('wvpl-inactive');
                    if ( obj.tracks.length > 1 ) {
                        infoBtn.addClass('wvpl-list').removeClass('wvpl-times');
                    } else {
                        infoBtn.addClass('wvpl-times').removeClass('wvpl-list');
                    }
                    info = 'bar';
                    break;
                // Info Bar and Playlist
                case 'bar':
                    if ( obj.tracks.length > 1 ) {
                        playlist.slideDown();
                        infoBtn.addClass('wvpl-times').removeClass('wvpl-list');
                        info = 'playlist';
                    } else {
                        infoBar.hide().addClass('wvpl-inactive');
                        infoBtn.removeClass('wvpl-times wvpl-list');
                        info = 'none';
                    }
                    break;
            }
        }


        /**
        *
        * Sets the visibility state of each skip button based on the current track in the playlist and the 'repeatAll' option
        *
        * @param playnow, if true, after preloading the track, automatically starts the playback
        *
        */
        var checkSkipState = function() {
            switch (obj.currentTrack) {
                case 0:
                    if (!obj.repeatAll) prevBtn.addClass('wvpl-disabled');
                    if (obj.tracks.length > 1) nextBtn.removeClass('wvpl-disabled');
                    break;
                case 1:
                    if (obj.tracks.length > 1) {
                        prevBtn.removeClass('wvpl-disabled');
                        nextBtn.removeClass('wvpl-disabled');
                    }
                    if (obj.tracks.length == 2 && !obj.repeatAll) nextBtn.addClass('wvpl-disabled');
                    break;
                case obj.tracks.length - 2:
                    if (obj.tracks.length > 1) {
                        prevBtn.removeClass('wvpl-disabled');
                        nextBtn.removeClass('wvpl-disabled');
                    }
                    break;
                case obj.tracks.length - 1:
                    if (obj.tracks.length > 1) {
                        prevBtn.removeClass('wvpl-disabled');
                        nextBtn.removeClass('wvpl-disabled');
                    }
                    if (!obj.repeatAll) nextBtn.addClass('wvpl-disabled');
                    break;
                default:
                    prevBtn.removeClass('wvpl-disabled');
                    nextBtn.removeClass('wvpl-disabled');
            }
        }

        /**
        *
        * Displays the information of the track provided
        *
        * @param track, an object containing the track whose info are going to be displayed
        *
        */
        var displayInfo = function(track) {

            var message = wvplVars.options.template;

            posterImg.empty().append(
                $('<img>', {srcset: track.thumbnail, sizes:posterImg.height()+'px'})
            );

            if ( message == null ) {
                message = track.title;
            } else {
                message = infoTemplate(message, track)
            }

            message ? infoText.show().find('.wvpl-infoblock').html( message ) : infoText.hide();

        }

        var infoTemplate = function(template, track) {
            template = template.replace(/(\{[^\}]*\})/g, function(match, $1){
                var att = JSON.parse($1);
                $.each(att, function(i,e){
					if ( typeof(att[i]) == 'string' ) {
						att[i] = e.replace(/%([^%]+)%/g, function(match, $11){
							return (track[ $11 ] ? track[ $11 ] : '');
						});
					}
                });
                return JSON.stringify(att);
            });
            template = template.replace(/%([^ \{]*)(\{[^\}]*\})*%/g, function(match, $1, $2){
                var key = $1,
                    s = track[ key ] ? track[ key ] : '',
                    attributes = $.extend({
                        class:'',
                        showValue: 1,
                        text: '',
                        raw: 0,
                        url: '',
                        target: '_blank',
                        icon: '',
                        event: ''
                    }, $2 ? JSON.parse($2) : ''),
                    $span = $('<span>');

                switch (key) {
                    case 'play_count':
                        if ( ! track.stats ) break;
                        s = track.stats.play_count;
                        $span = $('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-'+key+' '+attributes.class,
                            title: wvplVars.messages.playedTimes.replace("%s", s)
                        }).append(
                            $('<span>', {class: 'wvpl-value'}).html(s)
                        )
                        break;
                    case 'cart':
                        s = track.product_id;
                        var cart = track.in_cart > 0 ? 'wvpl-in_cart ' : 'wvpl-add_to_cart ';
                        var callback = track.in_cart > 0 ? 'goToCart' : 'addToCart';
                        var title = track.in_cart > 0 ? wvplVars.messages.alreadyInCart : wvplVars.messages.addToCart;
                        if (s) {
                            $span = $('<span>', {
                                class: 'wvpl-stats wvpl-icon wvpl-button '+cart+attributes.class,
                                title: title,
                                'data-product_id': s,
                                'data-event': callback,
                                'data-callback': callback
                            });
                        }
                        break;
                    case 'runtime':
                        if ( ! track.stats ) break;
                        s = lengthFormatted(track.stats.runtime);
                        $span = $('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-'+key+' '+attributes.class,
                            title: wvplVars.messages.totalRuntime.replace("%s", s)
                        }).append(
                            $('<span>', {class: 'wvpl-value'}).html(s)
                        )
                        break;
                    case 'length':
                    case 'length_formatted':
                        s = track.length_formatted;
                        $span = $('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-'+key+' '+attributes.class,
                            title: wvplVars.messages.trackLength.replace("%s", s)
                        }).append(
                            $('<span>', {class: 'wvpl-value'}).html(s)
                        )
                        break;
                    case 'likes':
                        if ( ! track.stats ) break;
                        s = track.stats.likes.length;
                        var id = track.id,
                            index = track.index,
                            msg = !WavePlayer.loggedUser ? ' ' + wvplVars.messages.loginToLike : '';
                        $span = $('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-button wvpl-'+key+' '+attributes.class,
                            'data-id': id,
                            'data-index': index,
                            'data-event': track.liked ? 'unlike' : 'like',
                            'data-callback': 'updateLikes',
                            title: wvplVars.messages.likedBy.replace("%s", s) + msg
                        }).toggleClass('liked', track.liked)
                            .toggleClass('disabled', !WavePlayer.loggedUser)
                            .append(
                                attributes.showValue ? $('<span>', {class: 'wvpl-value'}).html(s) : ''
                            )
                        break;
                    case 'downloads':
                        s = track.file ? track.file : ''
                        if ( ! track.stats ) break;
                        var count = track.stats.downloads != null ? track.stats.downloads : 0
                        $span = $('<a>', {href: s, download:''}).append($('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-button wvpl-'+key+' '+attributes.class,
                            'data-event': 'download',
                            'data-callback': 'updateDownloads',
                            title: wvplVars.messages.downloadedBy.replace("%s", count),
                            'data-id': track.id,
                            'data-index': track.index
                        }).append(
                            attributes.showValue ? $('<span>', {class: 'wvpl-value'}).html(count) : ''
                        ))
                        break;
                    case 'product_url':
                        s = track.product_url ? track.product_url : '';
                        $span = $('<a>', {
                            href: s
                        }).append($('<span>', {
                            class: 'wvpl-stats wvpl-icon wvpl-button wvpl-'+key+' '+attributes.class,
                            title: wvplVars.messages.goToProductPage,
                            'data-id': track.id,
                            'data-event': 'goToProduct',
                            'data-index': track.index,
                            'data-product_id': track.product_id
                        }));
                        break;
                    case 'share_fb':
                    case 'share_gp':
                    case 'share_tw':
                    case 'share_ln':
                        if ( track.post_url ) {
                            s = location.protocol + track.post_url;
                            var title = track.title,
                                social = key.replace('share_', '');
                            $span = $('<span>', {
                                class: 'wvpl-stats wvpl-icon wvplbutton wvpl-share wvpl-'+key,
                                'data-event': 'share',
                                'data-callback': 'socialShare',
                                'data-title': title,
                                'data-social': social,
                                'data-url': s,
                                title: wvplVars.messages.share
                            });
                        }
                        break;
                    default:
						s = attributes.showValue ? s : '';
                        var iconClass = '';
                        var buttonClass = '';
                        if ( attributes.icon ) iconClass = 'wvpl-stats wvpl-icon ' + attributes.icon;
                        if ( attributes.event ) buttonClass = 'wvpl-button ';
                        $span = $('<span>', {class: ('wvpl-'+key+' '+iconClass+' '+buttonClass+attributes.class).trim()}).append(
							$('<span>', {class: 'wvpl-value'}).html(s)
                        );
                        if (attributes.event) {
                            $span.attr('data-event', attributes.event);
                        }
                        if (attributes.url) {
                            $span = $('<a>')
                                .attr('href', attributes.url)
                                .attr('target', attributes.target)
                                .html($span);
                        }
                        break;
                }
                if ( !attributes.raw ) s = $span.prop('outerHTML');
                return s;
            });
            return template;
        }


        var displayPlaylist = function() {
            var list = $('<ul>')
            $.each(obj.tracks, function(i,e){
                var item = $('<li>')
                item.append(
                    $('<img>', {class:'wvpl-playlist-thumbnail', srcset:e.thumbnail, sizes:wvplVars.thumbHeight+'px'}),
                    $('<span>', {class:'wvpl-playlist-title'}).html(e.title),
                    $('<span>', {class:'wvpl-playlist-stats'}).html(infoTemplate(wvplVars.options.playlist_template, e))
                )
                list.append(item)
            })
            playlistWrapper.empty().append(list)
        }

        var lengthFormatted = function(position) {

            if (position == null) return '0:00'

            position = Math.round(position)

            var seconds = position % 60,
                minutes = Math.floor(position / 60) % 60,
                hours = Math.floor(position / 3600)

            return (hours > 0 ? hours + ":" : "") + (hours > 0 && minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds

        }

        this.updateStatistics = function() {
            var nLocal = obj.currentTrack,
                postData = {
                    action: 'waveplayer_update_statistics',
                    id: obj.tracks[nLocal].id,
                    length: obj.tracks[nLocal].length,
                    runtime: runtime
                };
            runtime = 0;
            if (lastStart == 0) return false;
            var callback = function(result) {
                if ( result.success ) {
                    obj.tracks[nLocal].stats = result.data.stats
                } else {
                    console.log('error', result.data.message)
                }
            }
            ajaxCall( postData, callback );
        }

        this.updateLikes = function(el) {
            if (!WavePlayer.loggedUser) return false;
            $(el).toggleClass('wvpl-spin');
            var postData = {
                    action: 'waveplayer_update_likes',
                    id: $(el).data('id'),
                };
            var callback = function(result) {
                $(el).toggleClass('wvpl-spin');
                if ( result.success ) {
                    obj.tracks[$(el).data('index')].liked = result.data.liked
                    obj.tracks[$(el).data('index')].stats = result.data.stats
                    $(el).data('event', result.data.liked?'unlike':'like').toggleClass('liked', result.data.liked).closest('.wvpl-stats').find('.wvpl-value').text(obj.tracks[$(el).data('index')].stats.likes.length)
                }
            }
            ajaxCall( postData, callback );
        }

        this.addToCart = function(cart) {
            var nLocal = obj.currentTrack,
                productID = $(cart).data('product_id'),
                postData = {
                    action: 'waveplayer_add_to_cart',
                    product_id: productID
                };
            $('[data-product_id='+productID+']').toggleClass('wvpl-spin');
            var callback = function(result) {
                if ( result.success ) {
                    $('[data-product_id='+productID+']').attr('title', wvplVars.messages.alreadyInCart)
                        .toggleClass('wvpl-spin wvpl-add_to_cart wvpl-in_cart')
                        .data('event', 'goToCart')
                        .data('callback', 'goToCart');
                    postData = {
                        action: 'woocommerce_get_refreshed_fragments'
                    };
                    var cb = function(response) {
                        $.each(response.fragments, function(i,e){
                            $(i).prop('outerHTML', e);
                        });
                    }

                    ajaxCall( postData, cb, woocommerce_params.ajax_url);
                } else {
                    window.location = result.data.product_url;
                }
            }
            ajaxCall( postData, callback );
        }

        this.goToCart = function(cart) {
            window.location = wvplVars.options.cartURL;
        }

        this.updateDownloads = function(el) {
            var postData = {
                    action: 'waveplayer_update_downloads',
                    id: $(el).data('id')
                };
            var callback = function(result) {
                if ( result.success ) {
                    obj.tracks[$(el).data('index')].stats = result.data.stats
                    $(el).closest('.wvpl-stats').find('.wvpl-value').text(obj.tracks[$(el).data('index')].stats.downloads)
                }
            }
            ajaxCall( postData, callback );
        }

        var ajaxCall = function( postData, callback, url ) {
            url = url || wvplVars.ajax_url;
            $.ajax({
                url: url,
                type: 'post',
                data: postData,
                dataType: 'json',
                success: callback,
                error: function(result) {
                    console.log(result.responseText);
                }
            });
        }

        this.socialShare = function(el) {
            var sharer,
                url = encodeURIComponent($(el).data('url')),
                title = encodeURIComponent($(el).data('title')),
                site = encodeURIComponent(wvplVars.options.site);

            switch($(el).data('social')){
                case 'fb':
                    sharer = 'https://www.facebook.com/sharer/sharer.php?display=popup&u=' + url;
                    break;
                case 'gp':
                    sharer = 'https://plus.google.com/share?url=' + url;
                    break;
                case 'tw':
                    sharer = 'https://twitter.com/intent/tweet?url=' + url;
                    break;
                case 'ln':
                    sharer = 'https://www.linkedin.com/shareArticle?mini=true&url=' + url + '&title=' + title + '&source=' + site;
                    break;
            }
            window.open( sharer, '' );
        }




        /**
        *
        * Bind the 'ended' event of the audio element to skip to the next track
        * and fires an 'ended' event
        *
        */

        var scrolling = false;
        infoText.on('mouseenter', function(event) {
            if (1) return false;

            if ( obj.status != STATUS_PLAY ) return false;
            var el = $(this).find('.wvpl-infoblock'),
                scrollable = el.prop('scrollWidth') >= infoBar.width();
            if (scrollable && !scrolling) {
                scrolling = true;
                var elClone = el.clone().css({marginLeft:'1em'});
                $(this).append(elClone);
                var dV = elClone.outerWidth(true),
                    dT = 50 * dV;
                el.animate({left: -dV}, dT, 'linear', function() {
                        $(this).css({left: 0});
                        elClone.remove();
                        elem.trigger('titleScrollEnd',  [ obj.tracks[obj.currentTrack] ] );
                        scrolling = false;
                });
                elem.trigger('titleScrollStart', [ obj.tracks[obj.currentTrack] ] );
                elClone.animate({left: -dV}, dT, 'linear');
            }
        });

        infoBtn.click(function(event) {
            event.preventDefault();
            obj.toggleInfo();
        });

        playBtn.click(function(event) {
            event.preventDefault();
            obj.toggle();
        });

        prevBtn.click(function(event) {
            event.preventDefault();
            if (!$(this).hasClass('wvpl-disabled')) obj.skip(false);
        });

        nextBtn.click(function(event) {
            event.preventDefault();
            if (!$(this).hasClass('wvpl-disabled')) obj.skip();
        });

        $(window).resize(function() {
            setTimeout(waveRedraw, 500);
        })


        /*
        *
        * Makes the volume button draggable (yet not moving it)
        * so that the user can alter the volume clicking on the volume button
        * and dragging horizontally
        *
        */

        var timerOverlay,
            startOffset,
            startVol;
        volumeBtn.on('mousedown', function(event) {
            startOffset = event.clientX;
            startVol = obj.audio.volume;

            // The overlay displaying the level appears after the user holds the mouse for more than 150ms
            timerOverlay = setTimeout(function() {

                // Disables the default behavior of the click and drag gesture
                event.preventDefault();
                elem.find('.wvpl-volume-overlay').addClass('dragging');
                elem.find('.wvpl-controls').addClass('wvpl-inactive');
                $(window)
                    .on('mousemove', function(event) {
                        event.preventDefault();
                        volumeBtn.addClass('dragging');
                        var newVol = Math.round( 100 * ( startVol + ( event.clientX - startOffset ) / ( elem.find('.wvpl-interface').width() * wavesurfer.params.pixelRatio / 4 ) ) ) / 100;
                        if ( newVol >= 0 && newVol <= 1 ) {
                            obj.audio.volume = newVol;
                            elem.find('.wvpl-volume-overlay').html(Math.round(newVol*100));
                            obj.audio.muted = (newVol == 0);
                            newVol == 0 ?
                                volumeBtn.removeClass('wvpl-volume_up').addClass('wvpl-volume_up') :
                                volumeBtn.removeClass('wvpl-volume_off').addClass('wvpl-volume_up');
                        }
                    }).on('mouseup', function(event) {
                        $(window).off('mousemove');
                        $('.dragging').removeClass('dragging');
                        elem.find('.wvpl-controls').removeClass('wvpl-inactive');
                    });
            }, 150);
        }).on('mouseup', function(event) {
            clearTimeout(timerOverlay);
            if ( !volumeBtn.hasClass('dragging') ) {
                volumeBtn.toggleClass('wvpl-volume_off').toggleClass('wvpl-volume_up');
                obj.audio.muted = volumeBtn.hasClass('wvpl-volume_off');
                elem.find('.wvpl-volume-overlay').html(Math.round(obj.audio.volume * 100))
            }
            $('.dragging').removeClass('dragging');
        });

    }

    $.fn.waveplayer = function(op, params) {
        return this.each(function(i,e) {
            var element = $(e),
                instance = null;

            if ( instance = element.data( 'waveplayer' ) ) {
                if ( 'object' !== typeof(params) ) params = [params];
                instance[op].apply(null, params);
                return;
            }

            var waveplayer = new WavePlayerClass(e);

            element.data('waveplayer', waveplayer);

            waveplayer['init'](params);
        });
    };

    WavePlayer.reloadInstances = function( container ) {
        WavePlayer.playlists = [];
        $('.waveplayer').removeClass('active');
        WavePlayer.loadInstances( container );
    }

    WavePlayer.loadInstances = function( container ) {
        if ( this.audio ) this.audio.pause();
        var selector;
        container = container || '';
        if ( $(container).hasClass('waveplayer') ) {
            selector = container;
        } else {
            selector = (container + ' .waveplayer').trim();
        }

        if ( $(selector).length ) {

            var listData = [];
            $(selector).each(function(i,e){
                if (!$(e).hasClass('active')) {
                    $(e).addClass('loading');
                    $(e).data('waveplayer', null);
                    var data = {player_id: $(e).attr('id'), ids: $(e).data('ids'), url: $(e).data('url')};
                    listData.push(data);
                }
            })

            $.ajax({
                url: wvplVars.ajax_url,
                type: 'post',
                data: {
                    action:     'waveplayer_load_page_playlist',
                    type:       'playlist',
                    list_data:  JSON.stringify(listData),
                    post_id:    wvplVars.post_id,
                },
                dataType: 'json',
                success: function( result ) {
                    var players = result.data.players;
                    $.each( result.data.players, function(i,p){
                        $.each( p.player_data, function(i,t){
                            if ( t.peaks ) {
                                t.peaks = t.peaks.split(',').map(parseFloat);
                            }
                        })
                    })
                    if ( $(selector).length === $('.waveplayer').length && $('.waveplayer.active').length === 0 ) {
                        WavePlayer.playlists = [];
                    }
					WavePlayer.playlists = WavePlayer.playlists.concat( result.data.players );
                    WavePlayer.loggedUser = wvplVars.currentUser.ID > 0;

                    WavePlayer.currentPlayer = null;

                    $.each(result.data.players, function(i,e){
                        $('#'+e.player_id).waveplayer('init', e.player_data)
                    });


                },
                error: function(response) {
                    console.log( response.responseText )
                }
            });
        }
    }

    if (wvplVars.options.loading_mode == 'page') {
        WavePlayer.loadInstances();
    } else if (wvplVars.options.loading_mode == 'instance') {
        $('.waveplayer').each(function(i,e){
            WavePlayer.loadInstances('#'+$(e).attr('id'));
        });
    }

    $(WavePlayer.audio).on('seeking', function(event) {
        var $instance = $('#'+WavePlayer.currentPlayer),
			obj = $instance.data('waveplayer');
        $instance.addClass('seeking');
        $instance.trigger('seeking', [ obj.tracks[obj.currentTrack], WavePlayer.audio.currentTime ] );
    });

    $(WavePlayer.audio).on('seeked', function(event) {
        var $instance = $('#'+WavePlayer.currentPlayer),
			obj = $instance.data('waveplayer');
        $instance.removeClass('seeking');
        $instance.trigger('seeked', [ obj.tracks[obj.currentTrack], WavePlayer.audio.currentTime ] );
        WavePlayer.audio.paused ?
            $instance.waveplayer('paused') :
            $instance.waveplayer('playing');
    });

    $(WavePlayer.audio).on('timeupdate', function(event) {
        var $instance = $('#'+WavePlayer.currentPlayer),
			obj = $instance.data('waveplayer');
        $instance.trigger('timeupdate', [ obj.tracks[obj.currentTrack], WavePlayer.audio.currentTime ] );
        $instance.waveplayer('timeUpdate' );
    });

    $(WavePlayer.audio).on('ended', function(event) {
        var $instance = $('#'+WavePlayer.currentPlayer),
			obj = $instance.data('waveplayer');
        $instance.trigger('ended', [ obj.tracks[obj.currentTrack] ] );
        $instance.waveplayer('skip');
    });

    $(document).on('click', '.woocommerce-music-type span.woocommerce-option:not(.active)', function(event){
        event.preventDefault();
        $('.woocommerce-music-type span.woocommerce-option').toggleClass('active');
        var type = $(this).data('type');
        $('input[name=music_type]').val(type);
        $('input[name=page]').val(1);
        $(this).closest('form').submit();
    });

	$(document).on('click', '.wvpl-playlist-title, .wvpl-playlist-thumbnail', function(event) {
        event.preventDefault();
		var index = $(this).closest('li').index();
		var $instance = $(this).closest('.waveplayer');
        $instance.waveplayer('skipTo', index);
	})

    $(document).on('click', '.wvpl-icon', function(event) {
        event.preventDefault();
        var $instance = $(this).closest('.waveplayer'),
            obj = $instance.data('waveplayer'),
            triggeredEvent = $(this).data('event'),
            callback = $(this).data('callback');
        if ( $(this).data('event') == 'download' ) {
            $(this).closest('a')[0].click();
        }
        if (triggeredEvent) $instance.trigger( triggeredEvent, [ obj.tracks[obj.currentTrack ] ] );
        if (callback) $instance.waveplayer(callback, $(this));
    })

    $( document.body ).on( 'removed_from_cart', function(event, fragments, cartHash, $button){
        if (!$button) return;
        $('[data-product_id='+$button.data('product_id') + ']')
            .attr('title', wvplVars.messages.addToCart)
            .data('event', 'addToCart')
            .data('callback', 'addToCart')
            .addClass('wvpl-add_to_cart')
            .removeClass('wvpl-in_cart');
    } );

});
