fs.video = (function() {

    var player = {},
        config = {
            defaultPlayer   : 'html5',
            canvasMode      : false
        };

    var playerType  = ['html5', 'flash'];

    var defPlayer   = config.defaultPlayer;
    var forceType;

    var tmpl = { notSupportMsg : fs.tmpl('Your Browser Doesn\'t Support {%= o.type %} Player.') };

    return {

        init: function(playerId, base, media, lang) {

            if ( !media ) return 500;
            if ( media.src.length == 0 ) return 501;
            this.playerType = this.getType( media );
            if ( !this.playerType ) return 400;

            if (base.playlist) {
                this.playerType = 'html5';
            }
// this.playerType = 'flash';
// this.playerType = 'html5';

            if ( lang ) this.setLang( lang );

            this.playerId   = playerId;
            this.base       = base;
            this.media      = media;

            if (fs.video[ this.playerType ].isSupport()) {
                player = fs.video[ this.playerType ].init(this.playerId, this.base, this.media);
                player.bindEvent();
            } else if(this.playerType === 'html5') {
                $("#" + playerId).html(_T('player-no-support-html5'));
            } else {
                $("#" + playerId).html(tmpl.notSupportMsg({type:  this.playerType }));
            }

            return 200;
        },

        setDefPlayer : function(type){
            if (typeof fs.video[type] == 'undefined') return false;
            defPlayer = type;
        },

        getType : function(media){

            if ( media.playtype == 'embed' ) return 'embed';

            var sup     = new Array(),
                self    = this;

            $.each(playerType, function(k, i){
                if ( self.isSupport(i) ) sup.push( i );
            });

            if ( !fs.storage.getItem('defaultPlayer') ) fs.storage.setItem('defaultPlayer', defPlayer);

            var type = fs.storage.getItem('defaultPlayer');

            if ( $.inArray(type, sup) === -1 )  {

                type = sup.pop();

                if ( !type ) {
                    fs.storage.setItem('defaultPlayer', '');
                    return false;
                }

                fs.storage.setItem('defaultPlayer', type);
            }

            return type;
        },

        getPlayerId: function() { return ( player.hasOwnProperty('getPlayerId') ) ? player.getPlayerId() : false; },
        getPlayerType: function() { return this.playerType; },

        defaultPlayer: function(type) {
            if (typeof fs.video[type] == 'undefined') return false;
            fs.storage.setItem('defaultPlayer', type);
        },

        focus       : function() { $('#' + this.playerId).focus(); },
        type        : function() { return player.type; },
        width       : function() { return $('#' + this.playerId).width(); },
        height      : function() { return $('#' + this.playerId).height();},
        isSeeking   : function() { return ( player.hasOwnProperty('isSeeking') ) ? player.isSeeking() : false; },

        play    : function() { return ( player.hasOwnProperty('play')  )  ? player.play()   : false; },
        pause   : function() { return ( player.hasOwnProperty('pause') )  ? player.pause()  : false; },
        isPaused: function() { return ( player.hasOwnProperty('isPaused') )  ? player.isPaused()  : false; },
        getPos  : function() {
            if ( player.hasOwnProperty('getPos') ) {
                var currentPos = player.getPos();
                return (currentPos > this.media.duration) ? this.media.duration : currentPos;
            }
            return false;
        },
        setPos  : function(pos) {
            if ( pos > this.media.duration ) return;
            return ( player.hasOwnProperty('setPos') ) ? player.setPos(pos) : false;
        },
        getDuration : function() {
            return player.getDuration();
        },

        getPlaybackrate : function()     { return ( player.hasOwnProperty('getPlaybackrate') ) ? player.getPlaybackrate()     : false; },

        getSlide    : function()        { return ( player.hasOwnProperty('getSlide') ) ? player.getSlide()      : false; },
        setSlide    : function(slide)   { return ( player.hasOwnProperty('setSlide') ) ? player.setSlide(slide) : false; },
        getCurrIndex    : function()        { return ( player.hasOwnProperty('getCurrIndex') ) ? player.getCurrIndex() : false; },

        appendElement: function(obj) {
            if ( player.hasOwnProperty('appendElement') ) player.appendElement(obj);
        },

        isSeeking       : function(){ return ( player.hasOwnProperty('isSeeking') )     ? player.isSeeking()    : false; },
        isFullscreen    : function(){ return ( player.hasOwnProperty('isFullscreen') )  ? player.isFullscreen() : false; },
        toFullscreen    : function(){ return ( player.hasOwnProperty('toFullscreen') )  ? player.toFullscreen() : false; },
        isReady         : function(){
            try {
                return player.isReady();
            } catch (e) {
                return false;
            };
        },

        changePlayer: function(type) {

            if (type == player.type) return;

            fs.storage.setItem('defaultPlayer', type);

            var pos = player.getPos();

            player.destory();

            delete player;

            var base = this.base;
            base.pos = pos;

            var self = this;

            fs.event.trigger('player.playerChanged', type);

            player = fs.video[type].init(this.playerId, base, this.media);
            player.bindEvent();
        },

        changeSourceFromUrl: function(url) {

            var self = this;

            $.post(url, {}, function(obj) {

                player.changeSource(obj, function() {
                    self.init(self.playerId, obj.base, obj.media);
                });

            }, 'json')
        },

        canvasMode: function( stat ) {

            if ( typeof stat == 'undefined' ) {
                return player.canvasMode();
            }

            (stat) ? player.enableCanvasMode() : player.disableCanvasMode();

            fs.storage.setItem('canvasMode', stat);

            return stat;
        },

        startSeekable : function(){
            return ( player.hasOwnProperty('startSeekable') ) ? player.startSeekable() : false;
        },

        resize : function(){
            if ( player.hasOwnProperty('resize') ) player.resize();
        },

        setCuePoint : function( cue ){
            if ( player.hasOwnProperty('setCuePoint') ) player.setCuePoint( cue );
        },

        setBeforeSeek : function( func ){
            if ( player.hasOwnProperty('setBeforeSeek') ) player.setBeforeSeek( func );
        },

        resizeProgressBar : function(){
            if ( player.hasOwnProperty('resizeProgressBar') ) player.resizeProgressBar();
        },

        isSupport : function( type ) {

            if ( forceType ) return ( forceType.type == type ) ? true : false;

            return (fs.video[ type ]) ? fs.video[ type ].isSupport() : false;
        },

        getNotSupportMsg : function( type ) {

            if ( forceType && forceType.type != type ) return forceType.hint;

            if ( fs.video && fs.video[ type ].isSupport() ) return '';

            return tmpl.notSupportMsg({ 'type' : type });
        },

        forcePlayerType : function( type, hint ){

            if ( forceType ) return false;
            if ( fs.video[ type ] && !fs.video[ type ].isSupport() ) return false;

            forceType = { type : type, hint : hint };
        },

        getSize: function() {if(player.hasOwnProperty('getSize')) {return player.getSize()}},

        cogAppend : function( html ){ if ( player.hasOwnProperty('cogAppend') ) player.cogAppend(html); },

        setLang : function( lang ){ this.lang = $.extend( this.lang, lang ); },

        setResolution: function(key) { if(player.hasOwnProperty('setResolution')) {return player.setResolution(key)}},

        getResolution: function() { if(player.hasOwnProperty('getResolution')) {return player.getResolution()}},

        getResolutionList: function() { if(player.hasOwnProperty('getResolutionList')) {return player.getResolutionList()}},

        getResolutionKey: function() { if(player.hasOwnProperty('getResolutionKey')) {return player.getResolutionKey()}},

        getSize: function() { if(player.hasOwnProperty('getSize')) {return player.getSize()}},

        autoSelectVideo: function() { if(player.hasOwnProperty('autoSelectVideo')) {return player.autoSelectVideo()}},

        isUserSelectedResolution: function() { if(player.hasOwnProperty('isUserSelectedResolution')) {return player.isUserSelectedResolution()}},

        config : config
    };
})();

fs.video.lang = {
    'playback'      : 'Playback',
    'normal'        : 'Normal',

    'resolution'    : 'Resolution',
    'fullscreen'    : 'Fullscreen',
    'volume'        : 'Volume',

    'skipADAfter'   : 'You can skip this ad after %sec seconds',
    'skipAD'        : 'Skip ad',

    'embed' :  {
        'seekFailed' : 'seeking failed'
    }
};

fs.video.html5 = (function() {
    var player,
        playerId,
        tryTime = 0,
        tryLimit = 5,
        html5PlayerReady = false;

    var media;
    /**
     * id: player container
     * base: {
     *     pos (int): start time
     *     autoplay (boolean): autoplay video
     *     waterMark {
     *         type (string): disable water mark / small / large
     *         param {
     *             account (string): param for water mark
     *             fullname (string): param for water mark
     *             sitename (string): param for water mark
     *             division (string): param for water mark
     *         }
     *     }
     *     ad (object): {
     *         src (string):
     *         skipAllow (boolean):
     *         duration (string): time format of ad's duration
     *         skipAfter (int): time of skip ad's time
     *         size (object): {
     *             width (int): width of ad
     *             height (int): height of ad
     *         }
     *     }
     * }
     **/
    return {
        type: 'html5',
        init: function(id, base, data) {
            playerId = id;
            media = data;

            var wrapper = $('#' + playerId).empty(),
                src = data.src.slice(),
                volume = fs.storage.getItem('player.volume') << 0 || 1,
                canvasMode = fs.storage.getItem('canvasMode') === 'true',
                startFrom = base.pos || 0;

            player = wrapper.jQPlayer({
                controls: [
                    'returnWaterMark', 'separator', 'play', 'separator', 'leftRadius', 'index',
                    'progress', 'storyboard', 'volume', 'cog', 'fullscreen', 'toggleList',
                    'stretch', 'time', 'indexTime', 'rightRadius', 'separator'
                ],
                startFrom: startFrom,
                storyboard: data.storyboard || '',
                duration: data.duration,
                video: src,
                index: data.index,
                poster: data.poster,
                volume: volume,
                canvasMode: canvasMode,
                autoplay: base.autoplay,

                embed       : base.embed,
                link        : base.link,
                sameDomain      : base.sameDomain,

                returnWaterMark      : data.returnWaterMark,
                returnWaterMarkAlpha : data.returnWaterMarkAlpha,

                waterMark: base.waterMark,
                playlist: base.playlist,
                ad: base.ad,
                onADStart    : function() {fs.event.trigger('player.ad.start');    },
                onADEnd        : function() {fs.event.trigger('player.ad.end');    },
                onPlay: function() {
                    fs.event.trigger('player.play');
                },
                onPause: function() {
                    fs.event.trigger('player.pause');
                },
                lang: fs.video.lang,
                onTimeUpdate: function(currentTime) {
                    fs.event.trigger('player.timeUpdate', currentTime);
                },
                onIndexChange: function(index) {
                    fs.event.trigger('player.indexChange', index);
                },
                onVolumeChange: function(volume) {
                    fs.storage.setItem('player.volume', volume);
                    fs.event.trigger('player.volumeChange', volume);
                },
                onVideoReady: function(api) {
                    html5PlayerReady = true;
                    fs.event.trigger('player.videoReady');
                },
                onFullscreen: function(status) {
                    fs.event.trigger('player.fullscreen', status);
                },
                onModeChange: function(isCanvas) {
                    fs.storage.setItem('canvasMode', isCanvas);
                    fs.event.trigger('player.onModeChange', isCanvas);
                },
                onEnd: function() {
                    fs.event.trigger('player.end');
                },
                onSeekFinished: function(pos) {
                    fs.event.trigger('player.seekFinished', pos);
                },
                onCanplay: function() {
                    fs.event.trigger('player.onCanplay');
                },
                onResolutionChanged: function(video) {
                    fs.event.trigger('player.onResolutionChanged', video);
                },
                onCuePoint: function(evt, args) {
                    fs.event.trigger(evt, args);
                }
            });

            return this;
        },
        bindEvent: function() {
            // event will be fired when width > height or height > width
            $(window).bind('orientationchange', function() {
                fs.event.trigger('player.reposition');
            });
        },
        getPlayerId: function() {
            return playerId;
        },
        isSupport: function() {
            var video = document.createElement('video');
            if (!video.canPlayType) {
                return false;
            }
            return video.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
        },
        isSeeking: function() {
            return player.isSeeking();
        },
        play: function() {
            player.play();
        },
        pause: function() {
            player.pause();
        },
        isPaused: function() {
            return player.paused();
        },
        getPos: function() {
            return player.getCurrTime();
        },
        setPos: function(pos) {

            // return if  pos > duration
            if (pos > player.getDuration()) {
                return;
            }
            player.setCurrTime(pos);
        },
        getDuration : function()    { 
            return player.getDuration();
        },
        getPlaybackrate: function() {
            return player.getPlaybackrate();
        },
        getSlide: function() {
            return player.currentIndex;
        },
        getSize: function() {
             return {"width": media.size.width, "height": media.size.height};
        },
        getCurrIndex: function() {
            return player.getCurrIndex();
        },
        setSlide: function(slide) {
            player.setIndex(slide);
        },

        // for canvas view (maybe draw graphic)
        appendElement: function(o) {
            player.appendElement(o);
        },
        changeSource: function(data, cb) {
            this.destory();
            if (typeof cb === 'function') {
                cb();
            }
        },

        // config html
        cogAppend: function(html) {
            player.cogAppend(html);
        },
        isSeeking: function() {
            return player.isSeeking();
        },
        isFullscreen: function() {
            return player.fullscreen;
        },
        toFullscreen: function(){ player.toFullscreen();  },

        isReady: function() {
            return html5PlayerReady;
        },
        destory: function() {
            $('#' + playerId)
                .find('video')
                .attr('src', '')
                .end()
                .empty()
                .unbind('contextmenu mousedown');

            delete playerId;
            delete player;
        },
        resize: function() {
            player.resize();
        },
        startSeekable: function() {
            return player.startSeekable();
        },

        // wtf is this???
        setCuePoint: function(cue) {
            player.setCuePoint(cue);
        },

        // used in video exam
        setBeforeSeek: function(func) {
            player.setBeforeSeek(func);
        },
        setResolution: function(key) {
            return player.setResolution(key);
        },
        getResolution: function() {
             return player.getResolution();
        },

        getResolutionList: function() {
            return player.getResolutionList();
        },

        getResolutionKey: function() {
            return player.getResolutionKey();
        },

        getSize: function() {
             return {"width": media.size.width, "height": media.size.height};
        },
        autoSelectVideo: function() {
             return player.autoSelectVideo();
        },
        isUserSelectedResolution: function() {
             return player.isUserSelectedResolution();
        },
        resizeProgressBar: function() {
            return player.resizeProgressBar();
        },
        canvasMode: function() {
            return player.canvasMode;
        },
        enableCanvasMode: function() {
            player.enableCanvasMode();
        },
        disableCanvasMode: function() {
            player.disableCanvasMode();
        }
    };
})();



fs.video.embed = (function() {

    var op = {};
    var playerId, player = null;
    var currentTime = 0, duration = 0;
    var index, currentKey, currIndex;
    var played  = false, paused = true;

    var media;
    var tick    = 250;

    var cue = new Array();


    var embedPlayerReady = false;

    return {
        type: 'embed',

        getPlayerId: function() {
            return playerId;
        },

        isSupport :function(){ return true; },

        init: function(id, base, data) {
            playerId = id;

            media = data;
            var self = this;

            self.playerInit(id, base, {html: data.src[0].embed});

            var _index = new Array();
            var s = 1,
                k = 0;

            $.each(data.index, function(key, itm) {
                itm.sn  = s;
                itm.key = k;

                _index[k] = itm;

                s++;
                k++;
            });

            index = _index.slice(0);

            return this;
        },

        playerInit: function(id, base, obj){

            var wrap = $('#' + id);
            wrap.append(obj.html);
            wrap.css('height', '100%');

            player      = new playerjs.Player(wrap.find('iframe').get(0));
            var self    = this;

            player.on('ready', function(){
                embedPlayerReady = true;

                player.getDuration(function(v){ duration = v; });

                if ( FlashDetect.installed ) {
                    if ( base.autoplay == 'yes' ) {
                        paused = false;
                        self.play();
                    }

                    var startFrom = 0;

                    if ( !isNaN( base.pos ) ) startFrom = base.pos;

                    if ( startFrom ) self.setPos( startFrom );
                }

                fs.event.trigger('player.videoReady');

                self.bindEvent();
            });

            player.on('ended',      function(e){ fs.event.trigger('player.ended'); });
            player.on('play',       function(e){ fs.event.trigger('player.play');  paused = false; played = true;});
            player.on('pause',      function(e){ fs.event.trigger('player.pause'); paused = true;  });

            player.on('progress',   function(e){ if ( media.subtype == 'vimeo' || media.subtype == 'youtube' ) currentTime = e.seconds; });
            player.on('seeked',     function(e){ if ( media.subtype == 'vimeo' || media.subtype == 'youtube' ) currentTime = e.seconds; });
            player.on('timeupdate', function(e){ if ( media.subtype == 'vimeo' || media.subtype == 'youtube' ) currentTime = e.seconds; });
        },

        bindEvent: function(){

            var self = this;
            var eventTmr = setInterval(function() {

                self.checkCurrentTime();

                var time = self.getPos();
                self.cuePointListening( time );
            }, tick);
        },

        checkCurrentTime : function(){

            var self = this;

            player.getCurrentTime(function(v){

                if ( v == currentTime ) return;
                if ( Math.abs(v - currentTime) > 1 ) {

                    var ret = {status: true};

                    $.each( self.beforeSeek, function(k, v){
                        ret = v.call(this, v);
                        if ( ret.status != true ) return false;
                    } );

                    if ( ret.status == false ) {
                        currentTime = v;

                        if ( ret.hasOwnProperty('pause') && ret.pause ) self.pause();
                        if ( ret.hasOwnProperty('pos') ) self.setPos(ret.pos);

                        return;
                    }

                    fs.event.trigger('player.seekFinished');
                }

                fs.event.trigger('player.timeUpdate', v);

                currentTime = v;

                var ci = self.getSlide();

                if ( index[ci] && currIndex != ci ) {
                    currIndex = ci;
                    fs.event.trigger( 'player.indexChange', index[ci] );
                }
            });
        },

        play    : function() { player.play();  },
        pause   : function() { player.pause(); },

        getDuration : function()    { return duration;      },
        getPos      : function()    { return currentTime;   },
        setPos      : function(pos) {

            if ( op.type ) return;

            if ( !played && fs.util.ua().isMobile ) {
                alert(fs.video.lang.embed.seekFailed);
                return;
            }

            if ( pos > duration ) return;

            op = {
                type: 'seek',
                args: {
                    pos:  pos,
                    play: ( this.isPaused() ) ? false : true
                }
            };

            this.doMute();

            if ( !played ) {
                var self = this;

                fs.event.registerOne('player.play', function() {
                    self.doSeek(op.args.pos);
                    self.checkSeeked();
                } );

                this.play();
            }

            if ( this.isPaused() ) this.play();

            this.doSeek(op.args.pos);
            this.checkSeeked();
        },

        isReady     : function()    { return embedPlayerReady; },
        isPaused    : function()    { return paused; },

        doSeek      : function(pos) { player.setCurrentTime( pos ); },
        doMute      : function(){ player.mute();   },
        doUnmute    : function(){ player.unmute(); },

        checkSeeked : function() {
            if ( !op.type ) return;

            var currentTime = this.getPos();

            if ( currentTime >= op.args.pos ) {
                this.seekFinished();
            }
            else {
                var self = this;
                setTimeout( function(){ self.checkSeeked(); }, 500 );
            }
        },

        seekFinished: function() {
            if ( !op.args.play ) this.pause();
            op = {};

            this.doUnmute();
            fs.event.trigger('player.seekFinished');
        },

        getSlide: function() {

            var list = index.slice(0),
                currentKey = currIndex;

            var currentTime = this.getPos();

            list.reverse();

            $.each(list, function(key, itm) {

                if ((parseInt(itm.time, 10) / 1000) > currentTime) return;
                currentKey = key;

                return false;
            });

            return (list[currentKey]) ? list[currentKey].key : -1;
        },

        setSlide: function(slide) {
            this.setPos( parseInt(slide.time, 10) / 1000 );
        },

        cogAppend   : function(){},
        resize      : function(){},

        lastTime    : 0,
        beforeSeek  : new Array(),

        cuePointListening : function(time){

            if ( cue.length == 0 ) return;

            if ( this.lastTime && time - this.lastTime < .015)
                return this.lastTime = time;

            this.lastTime = time;

            $.each(cue, function(k, v){

                if ( time > v.time ) return;
                if ( Math.abs(v.time-time) > .25 ) return;

                fs.event.trigger(v.evt, v.args);
            });
        },

        setCuePoint     : function( c )   { $.merge(cue, c); },
        setBeforeSeek   : function( func ){ this.beforeSeek.push( func ) },
        getSize: function() {
             return {"width": media.size.width, "height": media.size.height};
        }
    }
})();


fs.video.flash = (function() {
    var playerId,
        eventTmr, canPlayTmr,
        playerTmpl = fs.tmpl('\
        <object class="player" id="{%= o.id %}" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,12,0">\
            <param name="allowScriptAccess" value="sameDomain">\
            <param name="allowFullScreen"   value="true">\
            <param name="movie"             value="{%# o.path %}">\
            <param name="bgcolor"           value="#fff">\
            <param name="wmode"             value="transparent">\
            <param name="fixctrlpanel"      value="0">\
            <embed name="{%= o.id %}" menu="false" wmode="transparent" src="{%# o.path %}" allowfullscreen="true" allowscriptaccess="sameDomain" type="application/x-shockwave-flash">\
        </object>');

    var fullscreenStat = false;

    var tick = 500;
    var currIndex = -1;

    var media, index;

    var currentTime;

    var isFinish    = false,
        isSeeking   = false,
        isReady     = false;

    var pauseTriggered = false, isPaused = true;

    var op = {};

    return {
        type: 'flash',
        getPlayerId: function() {
            return playerId;
        },

        init: function(id, base, data) {
            playerId = id + 'player';

            var wrap = $('#' + id);
            wrap.css('height', '100%');

            var src  = data.src.slice();
            var fileindex = 0, key = 1;

            var width = wrap.width();

            $.each(src, function(k, i){
                if ( i.size.width > width ) {
                    key++;
                    return;
                }

                fileindex = key;
                return false;
            });

            if ( fileindex ) base.path += '&fileindex=' + fileindex;

            wrap.html('');

            wrap.append(playerTmpl({
                'id'   : playerId,
                'path' : base.path
            }));

            media = data;

            var _index = new Array();
            var s = 1,
                k = 0;

            $.each(data.index, function(key, itm) {
                itm.sn  = s;
                itm.key = k;

                _index[k] = itm;

                s++;
                k++;
            });

            index = _index.slice(0);


            var startFrom = 0;

            if ( !isNaN( base.pos ) ) startFrom = base.pos;

            if ( startFrom ) {

                var self = this;

                fs.event.registerOne('player.videoReady', function(){
                    self.setPos( startFrom );
                });

            }

            return this;
        },

        isSupport :function(){
            return FlashDetect.installed;
        },

        bindEvent: function() {
            isReady = false;

            this.checkVideoReady();

        },

        checkVideoReady: function() {
            var self = this,
                swf = window.document[playerId];

            try {

                if ( !swf.jsCanSetSlide() ) {
                    isReady = false;
                    throw 'flash not ready!';
                }

            } catch(e){

                clearTimeout(canPlayTmr);

                canPlayTmr = setTimeout(function(e) {
                    self.checkVideoReady();
                }, tick);
                return;
            }

            isReady = true;

            this.checkEvent();

            fs.event.trigger('player.videoReady');
        },

        checkEvent: function() {

            if ( !isReady ) return;
            var self = this;

            eventTmr = setInterval(function() {

                self.checkStat();
                self.checkCurrentTime();
                self.checkFinish();

                self.checkFullscreen();
                self.checkIndex();

            }, tick);
        },

        checkStat : function(){

            if ( !isReady ) return;

            var time = this.getPos();

            if ( currentTime == time && !pauseTriggered ) {

                pauseTriggered = true;
                isPaused       = true;

                fs.event.trigger('player.pause');
            }

            if ( time != currentTime ) {

                if ( Math.abs(currentTime-time) < 0.5 ) {

                    pauseTriggered = false;
                    isPaused       = false;

                    fs.event.trigger('player.play');
                }

            }
        },

        checkCurrentTime : function(){

            if ( !isReady ) return;

            var time = this.getPos();

            if ( time != currentTime ) {

                currentTime = time;
                fs.event.trigger('player.timeUpdate', time);
            }

        },

        checkFinish: function() {

            if ( !isReady ) return;

            var swf = window.document[playerId];
            var finish = swf.jsIsFinish();

            if ( finish && finish != isFinish ) {
                fs.event.trigger('player.end');
                isFinish = true;
            }
        },

        checkIndex: function() {

            if ( !isReady ) return;
            if ( op.type )  return;

            var ci   = this.getSlide(),
                self = this;

            if (index[ci] && currIndex != ci) {
                fs.event.trigger('player.indexChange', index[ci]);
                currIndex = ci;
            }
        },

        checkFullscreen: function() {

            if ( !isReady ) return;

            var swf = window.document[playerId];

            isFullScreen = swf.jsIsFullScreen();

            if (fullscreenStat != isFullScreen) {
                fs.event.trigger('player.fullscreen', isFullScreen);
                fullscreenStat = isFullScreen;
            }
        },

        play: function() {

            if ( !isReady ) return false;

            var swf = window.document[playerId];

            fs.event.trigger('player.play');
            return swf.jsPlay2();
        },

        pause: function(){

            if ( !isReady ) return false;

            var swf = window.document[playerId];

            fs.event.trigger('player.pause');
            return swf.jsStop2();
        },
        isPaused: function() {return isPaused; },
        paused: function(){ return isPaused; },

        getPos: function() {

            if ( !isReady ) return false;

            var swf = window.document[playerId];
            return swf.jsGetPos();
        },

        setPos: function( pos ) {

            if ( !isReady ) return false;
            if ( op.type )  return;

            var swf     = window.document[playerId];

            op = {type:"seek", args:{
                play : (!this.paused()) ? true : false,
                time : pos
            }};

            isSeeking = true;
            swf.jsSetPos(pos);

            var _this = this;
            setTimeout(function(){

                var ct = _this.getPos();

                if ( ct >= op.args.time ) {
                    _this.seekFinished();
                    return;
                }

                _this.waitUntilSeekFinish(op.args.time);

            }, 300);
        },

        seekFinished: function() {
            op  = {};
            isSeeking = false;
            fs.event.trigger('player.seekFinished');
        },

        waitUntilSeekFinish: function(pos) {

            var _this = this;

            setTimeout(function(){
                var ct = _this.getPos();

                if ( ct >= pos || Math.abs(ct-pos) < .125 ) {

                    _this.seekFinished();
                    return;
                }

                _this.waitUntilSeekFinish( pos );
            }, tick);
        },

        getSlide: function() {

            var list = index.slice(0),
                currentKey = currIndex;

            var currentTime = this.getPos();

            list.reverse();

            $.each(list, function(key, itm) {

                if ((parseInt(itm.time, 10) / 1000) > currentTime) return;
                currentKey = key;

                return false;
            });

            return (list[currentKey]) ? list[currentKey].key : -1;
        },

        setSlide: function(slide) {

            if ( !isReady ) return false;

            var time = (typeof slide === 'number') ? index[slide].time : slide.time;

            time = parseInt(time, 10) / 1000;
            this.setPos(time);
        },

        appendElement: function() {},

        isFullscreen: function() {

            if ( !isReady ) return false;

            var swf = window.document[playerId];

            swf.jsIsFullscreen();
        },

        isSeeking   : function(){ return player.isSeeking(); },
        isFullscreen: function(){ return player.fullscreen;  },
        isReady     : function(){ return isReady;   },

        changeSource: function(data) {

            if ( !isReady ) return false;

            var swf = window.document[playerId];
            swf.jsChangeSrc(data.media.path);
        },

        destory: function() {

            $('#' + playerId).empty();

            delete playerId;

            clearTimeout(canPlayTmr);
            clearInterval(eventTmr);
        },

        setCuePoint : function( cue ){},

        resize : function(){},
        startSeekable : function(){ return true; },

        canvasMode : function(){ return false; },
        enableCanvasMode    : function(){},
        disableCanvasMode   : function(){},

        cogAppend : function(){}
    };

})();

fs.video.canvas = (function() {

    var fsVideo = fs.video,
        canvas, config;

    var canvasEle;

    return {

        init: function() {
            var self = this;

            if ( fsVideo.type() != 'html5' ) return;

            config = fs.video.canvas.config;

            canvasEle = $('<canvas>', {
                'id'    : 'fs-canvasArea',
                'class' : 'fs-canvasArea',
                'css'   : {'z-index': 20},
                'attr': {
                    'width' : fsVideo.width(),
                    'height': fsVideo.height()
                }
            });

            fsVideo.appendElement(canvasEle);

            canvas = new fabric.Canvas('fs-canvasArea', {
                selection: false
            });
            canvas.hoverCursor = 'default';

            this.hide();

            canvas.on('object:moving', function(options) {

                var top = options.target.top,
                    left = options.target.left;

                if (top <= 0) top = 0;
                if ((top + options.target.height) >= parseInt(fsVideo.height(), 10))
                    top = fsVideo.height() - options.target.height;

                if (left <= 0) left = 0;
                if ((left + options.target.width) >= parseInt(fsVideo.width(), 10))
                    left = fsVideo.width() - options.target.width;

                options.target.set({
                    left: left,
                    top: top
                });
            });

            fs.event.register('player.fullscreen', function() {

                var ratio = fsVideo.width() / canvasEle.width();

                canvas.setWidth(fsVideo.width());
                canvas.setHeight(fsVideo.height());

                var obj = canvas.getObjects();

                for (var i in obj) {

                    obj[i].set('width',  obj[i].getWidth() * ratio);
                    obj[i].set('height', obj[i].getHeight() * ratio);

                    obj[i].set('left',   obj[i].get('left') * ratio);
                    obj[i].set('top',    obj[i].get('top') * ratio);

                    obj[i].setCoords();
                }

                canvas.renderAll();

                canvas.calcOffset();
            });

            fs.event.register('player.reposition', function() {
                self.reposition();

                // dont know why
                // chrome : anchor couldn't move without window resize
                $(window).trigger('resize');
            });
        },

        reposition: function() {
            var self = this;

            var isShow = self.isShow();
            if (!isShow) self.show();

            canvasEle.parent().position({
                my: 'left top',
                at: 'left top',
                of: $('video')
            });

            if (!isShow) self.hide();

        },
        remove: function(instance) {
            canvas.remove(instance);

            var self = this;
            if (canvas.getObjects().length == 0) self.hide();
        },

        addImg: function(element, instance) {
            var self = this;

            canvas.add(instance);
            element.onload = function() {
                self.show();
                canvas.renderAll();
            };
        },

        renderAll: function() {
            canvas.renderAll();
        },

        isShow: function() {
            return canvasEle.parent().is(':visible');
        },
        show: function() {
            canvasEle.parent().show();
        },
        hide: function() {
            canvasEle.parent().hide();
        }
    };
})();

fs.video.canvas.anchor = (function() {

    var fsVideo     = fs.video,
        fsCanvas    = fs.video.canvas;

    var config = fs.video.canvas.config,
        anchor = new Array();

    return {

        isSupport: function(type) {

            if ( typeof type === 'undefined' ) {
                type = fsVideo.type();
            }

            if ( type == 'embed' ) {
                return false;
            }

            return true;
        },

        getAll: function() { return anchor; },

        removeAll: function() {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;

            if ( type == 'html5' ) {
                for (var key in anchor) this.remove(key);

            } else {
                var swf = window.document[fsVideo.getPlayerId()];
                swf.jsAnchorClearAll();
                anchor = new Array();
            }
        },

        remove: function(id) {

            if (!anchor[id]) return;

            fsCanvas.remove(anchor[id]);

            delete anchor[id];
        },

        enableEdit: function(id) {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;
            if ( !anchor[id] ) return;

            if ( type == 'html5' ) {
                anchor[id].set({
                    hasBorders: true,
                    lockMovementX: false,
                    lockMovementY: false,
                    hoverCursor: 'move'
                });

                fsCanvas.reposition();

            } else {
                var _flashMovie = window.document[fsVideo.getPlayerId()];
                _flashMovie.jsAnchorSetEditable(id, true);
            }
        },

        disableEdit: function(id) {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;
            if ( !anchor[id] ) return;

            if ( type == 'html5' ) {
                anchor[id].set({
                    hasBorders: false,
                    lockMovementX: true,
                    lockMovementY: true,
                    hoverCursor: 'pointer'
                });
            } else {
                var _flashMovie = window.document[fsVideo.getPlayerId()];
                _flashMovie.jsAnchorSetEditable(id, false);
            }
        },

        add: function(id, y, x) {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;

            if ( type == 'html5' ) {

                var config = fs.video.canvas.config;

                fsCanvas.show();

                var imgElement = document.createElement('img');
                imgElement.setAttribute('src', config.image.anchor.src);

                var imgInstance = new fabric.Image(imgElement, {
                    width         : config.image.anchor.width,
                    height        : config.image.anchor.height,
                    hasControls   : false,
                    hasBorders    : false,
                    lockMovementX : true,
                    lockMovementY : true,
                    lockScalingX  : true,
                    lockScalingY  : true,
                    lockRotation  : true
                });

                imgInstance.set({
                    parcentage : {
                        left   : x,
                        top    : y
                    },
                    left: x * fsVideo.width()  - fs.video.canvas.config.image.anchor.width / 2,
                    top : y * fsVideo.height() - fs.video.canvas.config.image.anchor.height
                });

                fsCanvas.addImg(imgElement, imgInstance);
                anchor[id] = imgInstance;

            } else {

                var _flashMovie = window.document[fsVideo.getPlayerId()];

                (id == 0) ? _flashMovie.jsAnchorAdd(id, .5, .5) : _flashMovie.jsAnchorAdd(id, x, y);

                anchor[id] = 1;
            }
        },

        currKey: function() {

            var firstKey;
            for (var key in anchor) {
                firstKey = key;
                break;
            }

            return firstKey;
        },

        length: function() {

            var type = fsVideo.type();

            if (type == "embed") return 0;
            if (type == 'html5') return Object.keys(anchor).length;

            // other cases
            var _flashMovie = window.document[fsVideo.getPlayerId()];
            return _flashMovie.jsAnchorGetAll().length;
        },

        getPos: function(id) {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;
            if ( !anchor[id] ) return;

            if ( type == 'html5' ) {
                var pos = {
                    x: (anchor[id].left + (fs.video.canvas.config.image.anchor.width / 2)) / fsVideo.width(),
                    y: (anchor[id].top + (fs.video.canvas.config.image.anchor.height)) / fsVideo.height()
                };

                return pos;
            } else {
                var _flashMovie = window.document[fsVideo.getPlayerId()];
                return _flashMovie.jsAnchorGetPos(id);
            }
        },

        fadeOut: function(id) {

            var type = fsVideo.type();

            if ( !this.isSupport(type) ) return;
            if ( !anchor[id] ) return;

            if ( type == 'html5' ) {

                var _this = this;

                anchor[id].animate('opacity', 0, {
                    duration: 1500,
                    onChange: function() {
                        fsCanvas.renderAll();
                    },
                    onComplete: function() {
                        _this.remove(id);
                    }
                });

            } else {
                var _flashMovie = window.document[fsVideo.getPlayerId()];
                _flashMovie.jsAnchorFadeOut(id);
            }
        }

    };
})();

fs.video.canvas.config = {
    image: {
        anchor: {
            src: '/sys/res/icon/pin.png',
            width: 25,
            height: 40
        }
    }
};
