

function DVM() {

    var oUtils = new Utils() ;    // in Utils.sc

    function command_hide( oItem, bHide ) {
        oItem.includeInMenu = ! bHide ;  
    }

    function command_insert( oVideos, oItem, sPos ) {
        // internal only
        if ( typeof sPos == "undefined" ) sPos = "1" ;
        var nPos = parseInt( sPos ) ;
        // if passed nPos > 0, position from top, else position from bottom
        nPos-- ; // adjust for zero-base array
        var nLen = oVideos.items.length ;
        if ( nPos < 0 ) nPos = nLen + 1 + nPos ;
        if ( nPos > nLen ) nPos = nLen ;
        else if ( nPos < 0 ) nPos = 0 ;
        oVideos.items.splice( nPos, 0, oItem ) ;
    }

    function command_move( oVideos, oItem, sPos ) {
        oVideos.item( oItem.name, true ) ;  // removes from oVideos.items
        command_insert( oVideos, oItem, sPos ) ; 
    }

    function command_add( oVideos, oDVM_Videos, sName, sVimeoID, sPos ) {
        if ( typeof sVimeoID == "undefined" ) return ;
        var oItem = {
            name: sName
            , id: sVimeoID
            , host: 'vimeo'
        } ;
        if ( oDVM_Videos[ sVimeoID ] == null )
            oItem.error = 'Video ID not found' ;
        else {
            with ( oDVM_Videos[ sVimeoID ] ) {
                // Vimeo doesn't provide for line feeds in the title
                // Alt-255 on the Numeric Keypad is used as a workaround 
                sTitle = title ;
                var c = '\u00a0' ;  // Alt-255 on numeric keypad
                // replace pairs and all that's inside
                var oRegExp = new RegExp( c + '[^' + c + ']*' + c, 'g' ) ;
                sTitle = sTitle.replace( oRegExp, '<br>' ) ;
                // replace a single occurrence
                sTitle = sTitle.replace( c, '<br>' ) ;
                oItem.title = sTitle ;
                oItem.description = description ;
                oItem.width = parseInt( width ) ;
                oItem.height = parseInt( height ) ;
                oItem.includeInMenu = true ;
                oItem.time = oUtils.formatSeconds( duration ) ;
            }
        }
        command_insert( oVideos, oItem, sPos ) ;      
    }

    function command_text( oItem, sKeyword, sText ) {
        if ( typeof sText == "undefined" ) sText = "" ;
        if ( sKeyword == "desc" ) sKeyword = "description" ;
        if ( sText.substr( 0, 1 ) == '|' ) {
            sText = sText.substr( 1, sText.length - 1 ) ;
            if ( sText.substr( sText.length - 1 ) == '|' )
                sText = sText.substr( 0, sText.length - 1 ) ;
            //sText = sText.replace( /\\"/g, '"' ) ;
            //sText = sText.replace( /\\\\/g, '\\' ) ;
            sText = sText.replace( /[|][|]/g, '|' ) ;            
        }
        oItem[ sKeyword ] = sText ;  
    }

    function command_dim( oItem, sWidth, sHeight ) {
        if ( typeof sHeight == "undefined" ) return ;
        oItem.width = parseInt( sWidth ) ;
        oItem.height = parseInt( sHeight ) ;  
    }

    function exec1( oVideos, oDVM_Videos, sKeyword, aParms ) { 
        var sName = aParms[1].toLowerCase() ;
        var oItem = oVideos.item( sName ) ;
        if ( oItem == null && sKeyword != 'add' ) return ;
        else if ( oItem != null && sKeyword == 'add' ) return ;
        switch( sKeyword ) {
        case 'add' :
            command_add( oVideos, oDVM_Videos, sName, aParms[2], aParms[3] ) ;
            break ;
        case 'move' :
            command_move( oVideos, oItem, aParms[2] ) ;
            break ;
        case 'hide' :
        case 'show' :
            command_hide( oItem, sKeyword == 'hide' ) ;
            break ;
        case 'desc' :
        case 'title' :
            command_text( oItem, sKeyword, aParms[2] ) ;
            break ;
        case 'dim' :
            command_dim( oItem, aParms[2], aParms[3] ) ;
        }
    }

    this.exec = function( oVideos, sCommands, oDVM_Videos, bGetAddIDs ) {
        if ( typeof bGetAddIDs == "undefined" ) bGetAddIDs = false ;
        var sAddIDs = "" ;
        var sParm1 = "" ;
        sCommands = oUtils.trim( sCommands ) ;
        //oRegExp = /([|]([|][|]|[^|])*[|]|[^\n])*(\n|$)/g ;
        // next avoids extra empty array element
        //oRegExp = /^("(\\["\\]|[^"])*"|[^\n])*(\n|$)/gm ;
        oRegExp = /^([|](\\[|\\]|[^|])*[|]|[^\n])*(\n|$)/gm ; 
        aCommands = sCommands.match( oRegExp ) ;
        for ( var i = 0; i < aCommands.length; i++ ) {
            aCommands[i] = oUtils.trim( aCommands[i] ) ;
            //oRegExp = /"(""|[^"])*("|$)|[^\s"]+/g ;
            oRegExp = /[|]([|][|]|[^|])*([|]|$)|[^\s|]+/g ;
            var aParms = aCommands[i].match( oRegExp ) ; 
            if ( aParms == null || aParms.length < 2 ) continue ;
            var sKeyword = aParms[0].toLowerCase() ;
            if ( bGetAddIDs ) {
                if ( sKeyword == "add" ) {
                    if ( sAddIDs != "" ) sAddIDs += "," ;
                    sAddIDs += aParms[ 2 ] ;
                }
            } else {
                // asterisk means use preceding videoname
                if ( aParms[ 1 ] == '.' && sParm1 != '' ) aParms[ 1 ] = sParm1 ;
                else sParm1 = aParms[ 1 ] ;
                exec1( oVideos, oDVM_Videos, sKeyword, aParms ) ;
            }
        }
        if ( bGetAddIDs ) return sAddIDs ;
    }

    this.getAddIDs = function( sCommands ) {
        return this.exec( null, sCommands, null, true ) ;
    }

}  // end function DVM()


