var pagePlayer = null;


function PagePlayer() {
    var self = this,
        pl = this,
        sm = soundManager,
        _event, vuDataCanvas = null,
        controlTemplate = null,
        _head = document.getElementsByTagName('head')[0],
        spectrumContainer = null,
        ua = navigator.userAgent,
        supportsFavicon = (ua.match(/(opera|firefox)/i)),
        isTouchDevice = (ua.match(/ipad|ipod|iphone/i)),
        cleanup;
    this.config = {
        usePeakData: false,
        useWaveformData: false,
        useEQData: false,
        fillGraph: false,
        useMovieStar: true,
        allowRightClick: true,
        useThrottling: true,
        autoStart: false,
        playNext: true,
        updatePageTitle: false,
        emptyTime: '-:--',
        useFavIcon: false
    };
    this.css = {
        sDefault: 'sm2_link',
        sLoading: 'sm2_loading',
        sPlaying: 'sm2_playing active',
        sPaused: 'sm2_paused active'
    };
    this.sounds = [];
    this.soundsByObject = [];
    this.lastSound = null;
    this.soundCount = 0;
    this.strings = [];
    this.dragActive = false;
    this.dragExec = new Date();
    this.dragTimer = null;
    this.pageTitle = document.title;
    this.lastWPExec = new Date();
    this.lastWLExec = new Date();
    this.vuMeterData = [];
    this.oControls = null;
    this._mergeObjects = function (oMain, oAdd) {
        var o1 = {},
            o2, i, o;
        for (i in oMain) {
            if (oMain.hasOwnProperty(i)) {
                o1[i] = oMain[i]
            }
        }
        o2 = (typeof oAdd === 'undefined' ? {} : oAdd);
        for (o in o2) {
            if (typeof o1[o] === 'undefined') {
                o1[o] = o2[o]
            }
        }
        return o1
    };
    _event = (function () {
        var old = (window.attachEvent && !window.addEventListener),
            _slice = Array.prototype.slice,
            evt = {
                add: (old ? 'attachEvent' : 'addEventListener'),
                remove: (old ? 'detachEvent' : 'removeEventListener')
            };

        function getArgs(oArgs) {
            var args = _slice.call(oArgs),
                len = args.length;
            if (old) {
                args[1] = 'on' + args[1];
                if (len > 3) {
                    args.pop()
                }
            } else if (len === 3) {
                args.push(false)
            }
            return args
        }
        function apply(args, sType) {
            var element = args.shift(),
                method = [evt[sType]];
            if (old) {
                element[method](args[0], args[1])
            } else {
                element[method].apply(element, args)
            }
        }
        function add() {
            apply(getArgs(arguments), 'add')
        }
        function remove() {
            apply(getArgs(arguments), 'remove')
        }
        return {
            'add': add,
            'remove': remove
        }
    }());
    this.hasClass = function (o, cStr) {
        return (typeof (o.className) !== 'undefined' ? new RegExp('(^|\\s)' + cStr + '(\\s|$)').test(o.className) : false)
    };
    this.addClass = function (o, cStr) {
        if (!o || !cStr || self.hasClass(o, cStr)) {
            return false
        }
        o.className = (o.className ? o.className + ' ' : '') + cStr
    };
    this.removeClass = function (o, cStr) {
        if (!o || !cStr || !self.hasClass(o, cStr)) {
            return false
        }
        o.className = o.className.replace(new RegExp('( ' + cStr + ')|(' + cStr + ')', 'g'), '')
    };
    this.select = function (className, oParent) {
        var result = self.getByClassName(className, 'div', oParent || null);
        return (result ? result[0] : null)
    };
    this.getByClassName = (document.querySelectorAll ?
    function (className, tagNames, oParent) {
        var pattern = ('.' + className),
            qs;
        if (tagNames) {
            tagNames = tagNames.split(' ')
        }
        qs = (tagNames.length > 1 ? tagNames.join(pattern + ', ') : tagNames[0] + pattern);
        return (oParent ? oParent : document).querySelectorAll(qs)
    } : function (className, tagNames, oParent) {
        var node = (oParent ? oParent : document),
            matches = [],
            i, j, nodes = [];
        if (tagNames) {
            tagNames = tagNames.split(' ')
        }
        if (tagNames instanceof Array) {
            for (i = tagNames.length; i--;) {
                if (!nodes || !nodes[tagNames[i]]) {
                    nodes[tagNames[i]] = node.getElementsByTagName(tagNames[i])
                }
            }
            for (i = tagNames.length; i--;) {
                for (j = nodes[tagNames[i]].length; j--;) {
                    if (self.hasClass(nodes[tagNames[i]][j], className)) {
                        matches.push(nodes[tagNames[i]][j])
                    }
                }
            }
        } else {
            nodes = node.all || node.getElementsByTagName('*');
            for (i = 0, j = nodes.length; i < j; i++) {
                if (self.hasClass(nodes[i], className)) {
                    matches.push(nodes[i])
                }
            }
        }
        return matches
    });
    this.isChildOfClass = function (oChild, oClass) {
        if (!oChild || !oClass) {
            return false
        }
        while (oChild.parentNode && !self.hasClass(oChild, oClass)) {
            oChild = oChild.parentNode
        }
        return (self.hasClass(oChild, oClass))
    };
    this.getParentByNodeName = function (oChild, sParentNodeName) {
        if (!oChild || !sParentNodeName) {
            return false
        }
        sParentNodeName = sParentNodeName.toLowerCase();
        while (oChild.parentNode && sParentNodeName !== oChild.parentNode.nodeName.toLowerCase()) {
            oChild = oChild.parentNode
        }
        return (oChild.parentNode && sParentNodeName === oChild.parentNode.nodeName.toLowerCase() ? oChild.parentNode : null)
    };
    this.getOffX = function (o) {
        var curleft = 0;
        if (o.offsetParent) {
            while (o.offsetParent) {
                curleft += o.offsetLeft;
                o = o.offsetParent
            }
        } else if (o.x) {
            curleft += o.x
        }
        return curleft
    };
    this.getTime = function (nMSec, bAsString) {
        var nSec = Math.floor(nMSec / 1000),
            min = Math.floor(nSec / 60),
            sec = nSec - (min * 60);
        return (bAsString ? (min + ':' + (sec < 10 ? '0' + sec : sec)) : {
            'min': min,
            'sec': sec
        })
    };
    this.getSoundByObject = function (o) {
        return (typeof self.soundsByObject[o.id] !== 'undefined' ? self.soundsByObject[o.id] : null)
    };
    this.getNextItem = function (o) {
        if (o.nextElementSibling) {
            o = o.nextElementSibling
        } else {
            o = o.nextSibling;
            while (o && o.nextSibling && o.nextSibling.nodeType !== 1) {
                o = o.nextSibling
            }
        }
        if (o.nodeName.toLowerCase() !== 'li') {
            return null
        } else {
            return o.getElementsByTagName('a')[0]
        }
    };
    this.getPreviousItem = function (o) {
        if (o.previousElementSibling) {
            o = o.previousElementSibling
        } else {
            o = o.previousSibling;
            while (o && o.previousSibling && o.previousSibling.nodeType !== 1) {
                o = o.previousSibling
            }
        }
        if (o.nodeName.toLowerCase() !== 'li') {
            return null
        } else {
            return o.getElementsByTagName('a')[0]
        }
    };
    this.playNext = function (oSound) {
        if (!oSound) {
            oSound = self.lastSound
        }
        if (!oSound) {
            return false
        }
        var nextItem = self.getNextItem(oSound._data.oLI);
        if (nextItem) {
            pl.handleClick({
                target: nextItem
            })
        }
        return nextItem
    };
    this.playPrevious = function (oSound) {
        if (!oSound) {
            oSound = self.lastSound
        }
        if (!oSound) {
            return false
        }
        var prevItem = self.getPreviousItem(oSound._data.oLI);
        if (prevItem) {
            pl.handleClick({
                target: prevItem
            })
        }
        return nextItem
    };
    this.setPageTitle = function (sTitle) {
        if (!self.config.updatePageTitle) {
            return false
        }
        try {
            document.title = (sTitle ? sTitle + ' - ' : '') + self.pageTitle
        } catch (e) {
            self.setPageTitle = function () {
                return false
            }
        }
    };
    this.events = {
        play: function () {
            pl.removeClass(this._data.oLI, this._data.className);
            this._data.className = pl.css.sPlaying;
            pl.addClass(this._data.oLI, this._data.className);
            self.setPageTitle(this._data.originalTitle);
            $('.now-playing-wrapper').fadeIn('fast');
            $('.transport').fadeIn('fast');
            var whatsplaying = $('.sm2_playing .song-details').html();
            $('.now-playing').html(whatsplaying);
            $('.transport .playpause').addClass('pause').removeClass('play');
            $('.now-playing-wrapper h2').show();
        },
        stop: function () {
            pl.removeClass(this._data.oLI, this._data.className);
            this._data.className = '';
            this._data.oPosition.style.width = '0px';
            self.setPageTitle();
            self.resetPageIcon()
        },
        pause: function () {
            if (pl.dragActive) {
                return false
            }
            pl.removeClass(this._data.oLI, this._data.className);
            this._data.className = pl.css.sPaused;
            pl.addClass(this._data.oLI, this._data.className);
            self.setPageTitle();
            self.resetPageIcon();
            $('.transport .playpause').addClass('play').removeClass('pause')
        },
        resume: function () {
            if (pl.dragActive) {
                return false
            }
            pl.removeClass(this._data.oLI, this._data.className);
            this._data.className = pl.css.sPlaying;
            pl.addClass(this._data.oLI, this._data.className);
            $('.transport .playpause').addClass('pause').removeClass('play')
        },
        finish: function () {
            pl.removeClass(this._data.oLI, this._data.className);
            this._data.className = '';
            this._data.oPosition.style.width = '0px';
            if (self.config.playNext) {
                pl.playNext(this)
            } else {
                self.setPageTitle();
                self.resetPageIcon()
            }
        },
        whileloading: function () {
            function doWork() {
                this._data.oLoading.style.width = (((this.bytesLoaded / this.bytesTotal) * 100) + '%');
                if (!this._data.didRefresh && this._data.metadata) {
                    this._data.didRefresh = true;
                    this._data.metadata.refresh()
                }
            }
            if (!pl.config.useThrottling) {
                doWork.apply(this)
            } else {
                var d = new Date();
                if (d && d - self.lastWLExec > 30 || this.bytesLoaded === this.bytesTotal) {
                    doWork.apply(this);
                    self.lastWLExec = d
                }
            }
        },
        onload: function () {
            if (!this.loaded) {
                var oTemp = this._data.oLI.getElementsByTagName('a')[0],
                    oString = oTemp.innerHTML,
                    oThis = this;
                oTemp.innerHTML = oString + ' <span style="font-size:0.5em"> | Load failed, d\'oh! ' + (sm.sandbox.noRemote ? ' Possible cause: Flash sandbox is denying remote URL access.' : (sm.sandbox.noLocal ? 'Flash denying local filesystem access' : '404?')) + '</span>';
                setTimeout(function () {
                    oTemp.innerHTML = oString
                }, 5000)
            } else {
                if (this._data.metadata) {
                    this._data.metadata.refresh()
                }
            }
        },
        whileplaying: function () {
            var d = null;
            if (pl.dragActive || !pl.config.useThrottling) {
                self.updateTime.apply(this);
                if (sm.flashVersion >= 9) {
                    if (pl.config.usePeakData && this.instanceOptions.usePeakData) {
                        self.updatePeaks.apply(this)
                    }
                    if (pl.config.useWaveformData && this.instanceOptions.useWaveformData || pl.config.useEQData && this.instanceOptions.useEQData) {
                        self.updateGraph.apply(this)
                    }
                }
                if (this._data.metadata) {
                    d = new Date();
                    if (d && d - self.lastWPExec > 500) {
                        this._data.metadata.refreshMetadata(this);
                        self.lastWPExec = d
                    }
                }
                this._data.oPosition.style.width = (((this.position / self.getDurationEstimate(this)) * 100) + '%')
            } else {
                d = new Date();
                if (d - self.lastWPExec > 30) {
                    self.updateTime.apply(this);
                    if (sm.flashVersion >= 9) {
                        if (pl.config.usePeakData && this.instanceOptions.usePeakData) {
                            self.updatePeaks.apply(this)
                        }
                        if (pl.config.useWaveformData && this.instanceOptions.useWaveformData || pl.config.useEQData && this.instanceOptions.useEQData) {
                            self.updateGraph.apply(this)
                        }
                    }
                    if (this._data.metadata) {
                        this._data.metadata.refreshMetadata(this)
                    }
                    this._data.oPosition.style.width = (((this.position / self.getDurationEstimate(this)) * 100) + '%');
                    self.lastWPExec = d
                }
            }
        }
    };
    this.setPageIcon = function (sDataURL) {
        if (!self.config.useFavIcon || !self.config.usePeakData || !sDataURL) {
            return false
        }
        var link = document.getElementById('sm2-favicon');
        if (link) {
            _head.removeChild(link);
            link = null
        }
        if (!link) {
            link = document.createElement('link');
            link.id = 'sm2-favicon';
            link.rel = 'shortcut icon';
            link.type = 'image/png';
            link.href = sDataURL;
            document.getElementsByTagName('head')[0].appendChild(link)
        }
    };
    this.resetPageIcon = function () {
        if (!self.config.useFavIcon) {
            return false
        }
        var link = document.getElementById('favicon');
        if (link) {
            link.href = '/favicon.ico'
        }
    };
    this.updatePeaks = function () {
        var o = this._data.oPeak,
            oSpan = o.getElementsByTagName('span');
        oSpan[0].style.marginTop = (13 - (Math.floor(15 * this.peakData.left)) + 'px');
        oSpan[1].style.marginTop = (13 - (Math.floor(15 * this.peakData.right)) + 'px');
        if (sm.flashVersion > 8 && self.config.useFavIcon && self.config.usePeakData) {
            self.setPageIcon(self.vuMeterData[parseInt(16 * this.peakData.left, 10)][parseInt(16 * this.peakData.right, 10)])
        }
    };
    this.updateGraph = function () {
        if (pl.config.flashVersion < 9 || (!pl.config.useWaveformData && !pl.config.useEQData)) {
            return false
        }
        var sbC = this._data.oGraph.getElementsByTagName('div'),
            scale, i, offset;
        if (pl.config.useWaveformData) {
            scale = 8;
            for (i = 255; i--;) {
                sbC[255 - i].style.marginTop = (1 + scale + Math.ceil(this.waveformData.left[i] * -scale)) + 'px'
            }
        } else {
            offset = 9;
            for (i = 255; i--;) {
                sbC[255 - i].style.marginTop = ((offset * 2) - 1 + Math.ceil(this.eqData[i] * -offset)) + 'px'
            }
        }
    };
    this.resetGraph = function () {
        if (!pl.config.useEQData || pl.config.flashVersion < 9) {
            return false
        }
        var sbC = this._data.oGraph.getElementsByTagName('div'),
            scale = (!pl.config.useEQData ? '9px' : '17px'),
            nHeight = (!pl.config.fillGraph ? '1px' : '32px'),
            i;
        for (i = 255; i--;) {
            sbC[255 - i].style.marginTop = scale;
            sbC[255 - i].style.height = nHeight
        }
    };
    this.updateTime = function () {
        var str = self.strings.timing.replace('%s1', self.getTime(this.position, true));
        str = str.replace('%s2', self.getTime(self.getDurationEstimate(this), true));
        this._data.oTiming.innerHTML = str
    };
    this.getTheDamnTarget = function (e) {
        return (e.target || (window.event ? window.event.srcElement : null))
    };
    this.withinStatusBar = function (o) {
        return (self.isChildOfClass(o, 'controls'))
    };
    this.handleClick = function (e) {
/*         console.log(e); */
        if (e.button === 2) {
            if (!pl.config.allowRightClick) {
                pl.stopEvent(e)
            }
            return pl.config.allowRightClick
        }
        var o = self.getTheDamnTarget(e),
            sURL, soundURL, thisSound, oControls, oLI, str;
        if (!o) {
            return true
        }
        if (self.dragActive) {
            self.stopDrag()
        }
        if (self.withinStatusBar(o)) {
            return false
        }
        if (o.className == 'next') {
            console.log('next clicked')
        };
        if (o.nodeName.toLowerCase() !== 'a' && o.className != 'next') {
            o = self.getParentByNodeName(o, 'a')
        }
        if (!o) {
            return true
        }
        sURL = o.getAttribute('href');
        if ((!o.href && o.className != 'next') || (!sm.canPlayLink(o) && !self.hasClass(o, 'playable')) || self.hasClass(o, 'exclude')) {
            return true
        } else {
            self.initUL(self.getParentByNodeName(o, 'ul'));
            self.initItem(o);
            soundURL = o.href;
            thisSound = self.getSoundByObject(o);
            if (thisSound) {
                self.setPageTitle(thisSound._data.originalTitle);
                if (thisSound === self.lastSound) {
                    if (thisSound.readyState !== 2) {
                        if (thisSound.playState !== 1) {
                            thisSound.play()
                        } else {
                            thisSound.togglePause()
                        }
                    } else {
                        sm._writeDebug('Warning: sound failed to load (security restrictions, 404 or bad format)', 2)
                    }
                } else {
                    if (self.lastSound) {
                        self.stopSound(self.lastSound)
                    }
                    if (spectrumContainer) {
                        thisSound._data.oTimingBox.appendChild(spectrumContainer)
                    }
                    thisSound.togglePause()
                }
            } else {
                thisSound = sm.createSound({
                    id: o.id,
                    url: decodeURI(soundURL),
                    onplay: self.events.play,
                    onstop: self.events.stop,
                    onpause: self.events.pause,
                    onresume: self.events.resume,
                    onfinish: self.events.finish,
                    whileloading: self.events.whileloading,
                    whileplaying: self.events.whileplaying,
                    onmetadata: self.events.metadata,
                    onload: self.events.onload
                });
                oControls = self.oControls.cloneNode(true);
                oLI = o.parentNode;
                oLI.appendChild(oControls);
                if (spectrumContainer) {
                    oLI.appendChild(spectrumContainer)
                }
                self.soundsByObject[o.id] = thisSound;
                thisSound._data = {
                    oLink: o,
                    oLI: oLI,
                    oControls: self.select('controls', oLI),
                    oStatus: self.select('statusbar', oLI),
                    oLoading: self.select('loading', oLI),
                    oPosition: self.select('position', oLI),
                    oTimingBox: self.select('timing', oLI),
                    oTiming: self.select('timing', oLI).getElementsByTagName('div')[0],
                    oPeak: self.select('peak', oLI),
                    oGraph: self.select('spectrum-box', oLI),
                    className: self.css.sPlaying,
                    originalTitle: o.innerHTML,
                    metadata: null
                };
                if (spectrumContainer) {
                    thisSound._data.oTimingBox.appendChild(spectrumContainer)
                }
                if (thisSound._data.oLI.getElementsByTagName('ul').length) {
                    thisSound._data.metadata = new Metadata(thisSound)
                }
                str = self.strings.timing.replace('%s1', self.config.emptyTime);
                str = str.replace('%s2', self.config.emptyTime);
                thisSound._data.oTiming.innerHTML = str;
                self.sounds.push(thisSound);
                if (self.lastSound) {
                    self.stopSound(self.lastSound)
                }
                self.resetGraph.apply(thisSound);
                thisSound.play()
            }
            self.lastSound = thisSound;
            return self.stopEvent(e)
        }
    };
    this.handleMouseDown = function (e) {
        if (isTouchDevice && e.touches) {
            e = e.touches[0]
        }
        if (e.button === 2) {
            if (!pl.config.allowRightClick) {
                pl.stopEvent(e)
            }
            return pl.config.allowRightClick
        }
        var o = self.getTheDamnTarget(e);
        if (!o) {
            return true
        }
        if (!self.withinStatusBar(o)) {
            return true
        }
        self.dragActive = true;
        self.lastSound.pause();
        self.setPosition(e);
        if (!isTouchDevice) {
            _event.add(document, 'mousemove', self.handleMouseMove)
        } else {
            _event.add(document, 'touchmove', self.handleMouseMove)
        }
        self.addClass(self.lastSound._data.oControls, 'dragging');
        return self.stopEvent(e)
    };
    this.handleMouseMove = function (e) {
        if (isTouchDevice && e.touches) {
            e = e.touches[0]
        }
        if (self.dragActive) {
            if (self.config.useThrottling) {
                var d = new Date();
                if (d - self.dragExec > 20) {
                    self.setPosition(e)
                } else {
                    window.clearTimeout(self.dragTimer);
                    self.dragTimer = window.setTimeout(function () {
                        self.setPosition(e)
                    }, 20)
                }
                self.dragExec = d
            } else {
                self.setPosition(e)
            }
        } else {
            self.stopDrag()
        }
        e.stopPropagation = true;
        return false
    };
    this.stopDrag = function (e) {
        if (self.dragActive) {
            self.removeClass(self.lastSound._data.oControls, 'dragging');
            if (!isTouchDevice) {
                _event.remove(document, 'mousemove', self.handleMouseMove)
            } else {
                _event.remove(document, 'touchmove', self.handleMouseMove)
            }
            if (!pl.hasClass(self.lastSound._data.oLI, self.css.sPaused)) {
                self.lastSound.resume()
            }
            self.dragActive = false;
            return self.stopEvent(e)
        }
    };
    this.handleStatusClick = function (e) {
        self.setPosition(e);
        if (!pl.hasClass(self.lastSound._data.oLI, self.css.sPaused)) {
            self.resume()
        }
        return self.stopEvent(e)
    };
    this.stopEvent = function (e) {
        if (typeof e !== 'undefined') {
            if (typeof e.preventDefault !== 'undefined') {
                e.preventDefault()
            } else {
                e.stopPropagation = true;
                e.returnValue = false
            }
        }
        return false
    };
    this.setPosition = function (e) {
        var oThis = self.getTheDamnTarget(e),
            x, oControl, oSound, nMsecOffset;
        if (!oThis) {
            return true
        }
        oControl = oThis;
        while (!self.hasClass(oControl, 'controls') && oControl.parentNode) {
            oControl = oControl.parentNode
        }
        oSound = self.lastSound;
        x = parseInt(e.clientX, 10);
        nMsecOffset = Math.floor((x - self.getOffX(oControl) - 4) / (oControl.offsetWidth) * self.getDurationEstimate(oSound));
        if (!isNaN(nMsecOffset)) {
            nMsecOffset = Math.min(nMsecOffset, oSound.duration)
        }
        if (!isNaN(nMsecOffset)) {
            oSound.setPosition(nMsecOffset)
        }
    };
    this.stopSound = function (oSound) {
        sm._writeDebug('stopping sound: ' + oSound.sID);
        sm.stop(oSound.sID);
        if (!isTouchDevice) {
            sm.unload(oSound.sID)
        }
    };
    this.getDurationEstimate = function (oSound) {
        if (oSound.instanceOptions.isMovieStar) {
            return (oSound.duration)
        } else {
            return (!oSound._data.metadata || !oSound._data.metadata.data.givenDuration ? (oSound.durationEstimate || 0) : oSound._data.metadata.data.givenDuration)
        }
    };
    this.createVUData = function () {
        var i = 0,
            j = 0,
            canvas = vuDataCanvas.getContext('2d'),
            vuGrad = canvas.createLinearGradient(0, 16, 0, 0),
            bgGrad, outline;
        vuGrad.addColorStop(0, 'rgb(0,192,0)');
        vuGrad.addColorStop(0.30, 'rgb(0,255,0)');
        vuGrad.addColorStop(0.625, 'rgb(255,255,0)');
        vuGrad.addColorStop(0.85, 'rgb(255,0,0)');
        bgGrad = canvas.createLinearGradient(0, 16, 0, 0);
        outline = 'rgba(0,0,0,0.2)';
        bgGrad.addColorStop(0, outline);
        bgGrad.addColorStop(1, 'rgba(0,0,0,0.5)');
        for (i = 0; i < 16; i++) {
            self.vuMeterData[i] = []
        }
        for (i = 0; i < 16; i++) {
            for (j = 0; j < 16; j++) {
                vuDataCanvas.setAttribute('width', 16);
                vuDataCanvas.setAttribute('height', 16);
                canvas.fillStyle = bgGrad;
                canvas.fillRect(0, 0, 7, 15);
                canvas.fillRect(8, 0, 7, 15);
                canvas.fillStyle = vuGrad;
                canvas.fillRect(0, 15 - i, 7, 16 - (16 - i));
                canvas.fillRect(8, 15 - j, 7, 16 - (16 - j));
                canvas.clearRect(0, 3, 16, 1);
                canvas.clearRect(0, 7, 16, 1);
                canvas.clearRect(0, 11, 16, 1);
                self.vuMeterData[i][j] = vuDataCanvas.toDataURL('image/png')
            }
        }
    };
    this.testCanvas = function () {
        var c = document.createElement('canvas'),
            ctx = null,
            ok;
        if (!c || typeof c.getContext === 'undefined') {
            return null
        }
        ctx = c.getContext('2d');
        if (!ctx || typeof c.toDataURL !== 'function') {
            return null
        }
        try {
            ok = c.toDataURL('image/png')
        } catch (e) {
            return null
        }
        return c
    };
    this.initItem = function (oNode) {
        if (!oNode.id) {
            oNode.id = 'pagePlayerMP3Sound' + (self.soundCount++)
        }
        self.addClass(oNode, self.css.sDefault)
    };
    this.initUL = function (oULNode) {
        if (sm.flashVersion >= 9) {
            self.addClass(oULNode, self.cssBase)
        }
    };
    this.init = function (oConfig) {
        if (oConfig) {
            sm._writeDebug('pagePlayer.init(): Using custom configuration');
            this.config = this._mergeObjects(oConfig, this.config)
        } else {
            sm._writeDebug('pagePlayer.init(): Using default configuration')
        }
        var i, spectrumBox, sbC, oF, oClone, oTiming;
        this.cssBase = [];
        sm.useFlashBlock = true;
        if (sm.flashVersion >= 9) {
            sm.useMovieStar = this.config.useMovieStar;
            sm.defaultOptions.usePeakData = this.config.usePeakData;
            sm.defaultOptions.useWaveformData = this.config.useWaveformData;
            sm.defaultOptions.useEQData = this.config.useEQData;
            if (this.config.usePeakData) {
                this.cssBase.push('use-peak')
            }
            if (this.config.useWaveformData || this.config.useEQData) {
                this.cssBase.push('use-spectrum')
            }
            this.cssBase = this.cssBase.join(' ');
            if (this.config.useFavIcon) {
                vuDataCanvas = self.testCanvas();
                if (vuDataCanvas && supportsFavicon) {
                    self.createVUData()
                } else {
                    this.config.useFavIcon = false
                }
            }
        } else if (this.config.usePeakData || this.config.useWaveformData || this.config.useEQData) {
            sm._writeDebug('Page player: Note: soundManager.flashVersion = 9 is required for peak/waveform/EQ features.')
        }
        controlTemplate = document.createElement('div');
        controlTemplate.innerHTML = ['  <div class="controls">', '   <div class="statusbar">', '    <div class="loading"></div>', '    <div class="position"></div>', '   </div>', '  </div>', '  <div class="timing">', '   <div id="sm2_timing" class="timing-data">', '    <span class="sm2_position">%s1</span>/<span class="sm2_total">%s2</span>', '   </div>', '  </div>', '  <div class="peak">', '   <div class="peak-box"><span class="l"></span><span class="r"></span></div>', '  </div>', ' <div class="spectrum-container">', '  <div class="spectrum-box">', '   <div class="spectrum"></div>', '  </div>', ' </div>'].join('\n');
        if (sm.flashVersion >= 9) {
            spectrumContainer = self.select('spectrum-container', controlTemplate);
            spectrumContainer = controlTemplate.removeChild(spectrumContainer);
            spectrumBox = self.select('spectrum-box', spectrumContainer);
            sbC = spectrumBox.getElementsByTagName('div')[0];
            oF = document.createDocumentFragment();
            oClone = null;
            for (i = 256; i--;) {
                oClone = sbC.cloneNode(false);
                oClone.style.left = (i) + 'px';
                oF.appendChild(oClone)
            }
            spectrumBox.removeChild(sbC);
            spectrumBox.appendChild(oF)
        } else {
            controlTemplate.removeChild(self.select('spectrum-container', controlTemplate));
            controlTemplate.removeChild(self.select('peak', controlTemplate))
        }
        self.oControls = controlTemplate.cloneNode(true);
        oTiming = self.select('timing-data', controlTemplate);
        self.strings.timing = oTiming.innerHTML;
        oTiming.innerHTML = '';
        oTiming.id = '';

        function doEvents(action) {
            _event[action](document, 'click', self.handleClick);
            if (!isTouchDevice) {
                _event[action](document, 'mousedown', self.handleMouseDown);
                _event[action](document, 'mouseup', self.stopDrag)
            } else {
                _event[action](document, 'touchstart', self.handleMouseDown);
                _event[action](document, 'touchend', self.stopDrag)
            }
            _event[action](window, 'unload', cleanup)
        }
        cleanup = function () {
            doEvents('remove')
        };
        doEvents('add');
        sm._writeDebug('pagePlayer.init(): Ready', 1);
        if (self.config.autoStart) {
            pl.handleClick({
                target: pl.getByClassName('playlist', 'ul')[0].getElementsByTagName('a')[0]
            })
        }
    }
}
soundManager.useFlashBlock = true;
soundManager.onready(function () {
    pagePlayer = new PagePlayer();
    pagePlayer.init(typeof PP_CONFIG !== 'undefined' ? PP_CONFIG : null)
});
