function Music() {
    this.init();
}
// 定义一个名为 'Music' 的构造函数，当创建 'Music' 类的实例时，会自动调用 'init' 方法，用于初始化音乐相关的一些设置和绑定事件等操作，这是面向对象编程中构造函数的常见用法，用于创建具有特定功能和属性的对象实例。

(function () {
    var pages = [],
        panels = [],
        selectedItem = null;
    // 创建三个私有变量，'pages' 数组用于存储分页相关的元素标识（可能是页面上分页按钮等元素的 ID 之类），'panels' 数组用于存储页面面板相关的元素标识（可能是用于展示不同页面内容的面板元素 ID），'selectedItem' 初始化为 null，用于记录用户选择的音乐相关信息（比如歌曲对象等），这几个变量在函数内部使用，外部无法直接访问，起到了数据封装的作用。

    Music.prototype = {
        total: 70,
        pageSize: 10,
        dataUrl: "http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.common",
        playerUrl: "http://box.baidu.com/widget/flash/bdspacesong.swf",
        // 在 'Music' 类的原型对象上定义了四个属性。'total' 表示总的数据量（可能是搜索结果的总数之类），初始值为 70；'pageSize' 表示每页显示的数据条数，初始值为 10，用于分页功能相关计算；'dataUrl' 定义了获取音乐数据的 API 地址，指向百度音乐的一个搜索接口，通过向这个接口发送请求来获取音乐相关信息；'playerUrl' 定义了音乐播放器对应的 Flash 文件的 URL 地址，用于后续创建音乐播放相关的 HTML 元素嵌入到页面中实现音乐播放功能。

        init: function () {
            var me = this;
            // 在 'init' 方法内部，首先将当前对象（this）赋值给变量'me'，方便在内部函数中引用外部对象的属性和方法，这是一种常见的解决 JavaScript 中 this 指针指向问题的技巧，尤其是在事件回调函数中使用时。

            domUtils.on($G("J_searchName"), "keyup", function (event) {
                var e = window.event || event;
                if (e.keyCode == 13) {
                    me.dosearch();
                }
            });
            // 使用 'domUtils'（可能是自定义的 DOM 操作工具对象，包含处理 DOM 事件绑定等功能的函数）的 'on' 方法，为页面中 id 为 'J_searchName' 的元素（可能是一个输入框，用于输入搜索关键词）绑定 'keyup' 键盘按键抬起事件监听器。在事件回调函数中，先处理浏览器兼容性问题（获取正确的事件对象，因为不同浏览器获取事件对象的方式略有不同，IE 使用 'window.event'，其他标准浏览器使用传入的参数 'event'），然后判断按下的键码（keyCode）是否为 13（回车键的键码），如果是回车键按下，则调用当前对象（通过'me' 引用）的 'dosearch' 方法，实现用户在输入框中输入关键词后按回车键触发搜索的功能。

            domUtils.on($G("J_searchName"), "click", function () {
                me.dosearch();
            });
            // 同样使用 'domUtils' 的 'on' 方法，为 id 为 'J_searchName' 的元素绑定 'click' 点击事件监听器，当用户点击该元素时，直接调用 'dosearch' 方法，不过通常输入框点击事件的使用场景较少，这里可能是一种额外的触发搜索方式或者是代码冗余（具体看实际需求），正常更常见的是通过回车键触发搜索。

            domUtils.on($G("J_searchBtn"), "click", function () {
                me.dosearch();
            });
            // 使用 'domUtils' 为页面中 id 为 'J_searchBtn' 的元素（可能是一个按钮，用于触发音乐搜索操作）绑定 'click' 点击事件监听器，当用户点击这个按钮时，调用 'dosearch' 方法，触发音乐搜索逻辑，这是比较常见的通过按钮点击来执行搜索功能的实现方式。
        },

        callback: function (data) {
            var me = this;
            me.data = data.song_list;
            setTimeout(function () {
                $G('J_resultBar').innerHTML = me._renderTemplate(data.song_list);
            }, 300);
            // 定义 'callback' 方法，用于接收从服务器获取音乐数据后的回调处理。首先将传入的 'data' 参数中的'song_list' 属性值（可能是包含歌曲信息的数组）赋值给当前对象（通过'me' 引用）的 'data' 属性，以便后续在其他方法中可以访问这些歌曲数据。然后使用'setTimeout' 函数，延迟 300 毫秒后执行一个匿名函数，在匿名函数内通过 '$G' 函数（可能是自定义的获取 DOM 元素的函数）获取页面中 id 为 'J_resultBar' 的元素，并将其 'innerHTML' 属性设置为调用 '_renderTemplate' 方法（后续定义）处理后的歌曲列表数据（data.song_list），实现将获取到的音乐数据渲染并展示到页面相应区域的功能，延迟执行可能是为了等待页面其他相关元素加载或准备好，避免出现数据渲染的错误或不一致情况。
        },

        dosearch: function () {
            var me = this;
            selectedItem = null;
            var key = $G('J_searchName').value;
            if (utils.trim(key) == "") return false;
            key = encodeURIComponent(key);
            me._sent(key);
            // 定义 'dosearch' 方法，用于执行音乐搜索操作。首先将'selectedItem' 重置为 null，表示当前没有选中的歌曲。然后通过 '$G' 函数获取页面中 id 为 'J_searchName' 的输入框元素的值（用户输入的搜索关键词），并使用 'utils.trim' 函数（可能是自定义的去除字符串两端空白字符的函数）去除关键词两端的空白字符，如果处理后的关键词为空字符串，则直接返回 false，阻止搜索操作执行（因为没有有效的搜索内容）。如果关键词不为空，则使用 'encodeURIComponent' 函数对关键词进行 URL 编码，使其符合 URL 传参的格式要求，最后调用当前对象（通过'me' 引用）的 '_sent' 方法（后续定义），传入编码后的关键词，发起向服务器获取音乐数据的请求。
        },

        doselect: function (i) {
            var me = this;
            if (typeof i == 'object') {
                selectedItem = i;
            } else if (typeof i == 'number') {
                selectedItem = me.data[i];
            }
            // 定义 'doselect' 方法，用于处理用户选择音乐的操作。根据传入参数 'i' 的数据类型进行不同的处理，如果 'i' 是一个对象（可能是代表歌曲信息的对象），则直接将其赋值给'selectedItem'，表示选中了这个歌曲对象；如果 'i' 是一个数字，则将当前对象（通过'me' 引用）的 'data' 属性（之前存储的歌曲列表数据）中对应索引的歌曲对象赋值给'selectedItem'，这样就记录了用户选择的歌曲信息，方便后续进行播放、插入等相关操作。
        },

        onpageclick: function (id) {
            var me = this;
            for (var i = 0; i < pages.length; i++) {
                $G(pages[i]).className = 'pageoff';
                $G(panels[i]).className = 'paneloff';
            }
            $G('page' + id).className = 'pageon';
            $G('panel' + id).className = 'panelon';
            // 定义 'onpageclick' 方法，用于处理分页点击事件。首先遍历 'pages' 和 'panels' 数组（之前存储了分页和面板相关元素的标识），通过 '$G' 函数获取对应的元素，并将它们的 'className' 属性分别设置为 'pageoff' 和 'paneloff'，也就是将所有分页按钮和对应面板设置为未选中状态（关闭状态）。然后通过 '$G' 函数获取当前点击的分页按钮（根据传入的 'id' 参数拼接出对应的元素 ID，如 'page' + id），将其 'className' 属性设置为 'pageon'（选中状态），同时将对应的面板（'panel' + id）的 'className' 设置为 'panelon'（显示状态），实现分页切换时相应页面内容显示和分页按钮状态切换的功能。
        },

        listenTest: function (elem) {
            var me = this,
                view = $G('J_preview'),
                is_play_action = (elem.className == 'm-try'),
                old_trying = me._getTryingElem();

            if (old_trying) {
                old_trying.className = 'm-try';
                view.innerHTML = '';
            }
            if (is_play_action) {
                elem.className = 'm-trying';
                view.innerHTML = me._buildMusicHtml(me._getUrl(true));
            }
            // 定义 'listenTest' 方法，用于处理音乐试听相关操作。首先获取当前对象（通过'me' 引用）、页面中 id 为 'J_preview' 的元素（可能是用于音乐预览展示的区域），判断传入的元素（elem）的 'className' 是否为'm-try'，如果是则将 'is_play_action' 设置为 true，表示是播放操作（从类名推测'm-try' 可能代表播放相关的样式类）。接着调用 '_getTryingElem' 方法（后续定义）获取之前正在试听的元素（如果存在的话），如果存在，则将其 'className' 恢复为'm-try'（表示停止播放状态），并清空 'J_preview' 元素的 'innerHTML'（清除之前的试听内容）。如果是播放操作（'is_play_action' 为 true），则将传入元素（elem）的 'className' 修改为'm-trying'（可能代表正在播放的样式类），并通过调用 '_buildMusicHtml' 方法（后续定义）传入获取到的音乐播放 URL（通过 '_getUrl' 方法获取，且传入参数 true 表示是试听情况），将生成的包含音乐播放器的 HTML 代码设置为 'J_preview' 元素的 'innerHTML'，实现在预览区域播放音乐的功能。
        },

        _sent: function (param) {
            var me = this;
            $G('J_resultBar').innerHTML = '<div class="loading"></div>';

            utils.loadFile(document, {
                src: me.dataUrl + '&query=' + param + '&page_size=' + me.total + '&callback=music.callback&.r=' + Math.random(),
                tag: "script",
                type: "text/javascript",
                defer: "defer"
            });
            // 定义 '_sent' 方法，用于向服务器发送获取音乐数据的请求。首先通过 '$G' 函数获取页面中 id 为 'J_resultBar' 的元素，并将其 'innerHTML' 属性设置为一个包含 'loading' 类名的 div 元素（从类名推测用于展示加载提示信息，样式可能在外部 CSS 文件中定义），用于提示用户正在加载数据。然后使用 'utils.loadFile' 函数（可能是自定义的加载文件的函数，这里用于加载 JavaScript 脚本文件），向 'document' 对象（页面的文档对象）添加一个 script 标签，设置其'src' 属性为拼接好的请求 URL，包含了之前定义的 'dataUrl'（基础 API 地址）、搜索关键词参数（param）、每页显示数量（page_size）、回调函数名称（callback=music.callback，表示数据获取成功后调用 'Music' 类的 'callback' 方法）以及一个随机数（用于避免缓存，确保每次请求都是新的，.r= + Math.random()），同时设置标签的 'tag' 为 "script"（表示是脚本标签），'type' 为 "text/javascript"（脚本类型），'defer' 属性为 "defer"（表示延迟加载，即页面解析完后再执行脚本，避免阻塞页面渲染），通过这种方式向服务器发起获取音乐数据的请求，并在获取成功后通过指定的回调函数处理数据。
        },

        _removeHtml: function (str) {
            var reg = /<\s*\/?\s*[^>]*\s*>/gi;
            return str.replace(reg, "");
            // 定义 '_removeHtml' 方法，用于去除字符串中的 HTML 标签。通过创建一个正则表达式对象（reg），匹配以 '<' 开头，中间包含任意字符（除了 '>'），以 '>' 结尾的 HTML 标签（包括自闭和标签以及成对标签），不区分大小写（gi 修饰符），然后使用字符串的'replace' 方法将匹配到的 HTML 标签替换为空字符串，返回处理后的字符串，可用于清理歌曲信息等文本中的 HTML 标签，获取纯文本内容，比如歌曲标题、歌手名等文本信息的提取和清理。
        },

        _getUrl: function (isTryListen) {
            var me = this;
            var param = 'from=tiebasongwidget&url=&name=' + encodeURIComponent(me._removeHtml(selectedItem.title)) + '&artist='
                + encodeURIComponent(me._removeHtml(selectedItem.author)) + '&extra='
                + encodeURIComponent(me._removeHtml(selectedItem.album_title))
                + '&autoPlay=' + isTryListen + '' + '&loop=true';
            return me.playerUrl + "?" + param;
            // 定义 '_getUrl' 方法，用于获取音乐播放的 URL 参数。根据传入的 'isTryListen' 参数（布尔值，用于区分是试听还是正式插入播放等情况）构建请求参数 'param'，参数中包含了来源信息（from=tiebasongwidget）、歌曲名称（通过获取'selectedItem' 的 'title' 属性，去除其中的 HTML 标签后进行 URL 编码）、歌手信息（同理处理 'author' 属性）、额外信息（可能是专辑相关，处理 'album_title' 属性）以及自动播放（autoPlay 根据 'isTryListen' 设置为 true 或 false）和循环播放（loop=true）等设置，最后将构建好的参数拼接在 'playerUrl'（音乐播放器的 Flash 文件 URL）后面，返回完整的音乐播放 URL，用于后续创建音乐播放相关的 HTML 元素或者发起播放请求等操作。
        },

        _getTryingElem: function () {
            var s = $G('J_listPanel').getElementsByTagName('span');

            for (var i = 0; i < s.length; i++) {
                if (s[i].className == 'm-trying')
                    return s[i];
            }
            return null;
            // 定义 '_getTryingElem' 方法，用于查找页面中正在试听的元素（通过样式类'm-trying' 判断）。首先通过 '$G' 函数获取页面中 id 为 'J_listPanel' 的元素，并获取其内部所有的'span' 标签元素，存储在's' 数组中。然后循环遍历这个数组，判断每个'span' 元素的 'className' 是否为'm-trying'，如果找到符合条件的元素，则返回该元素，表示找到了正在试听的元素；如果循环结束都没有找到，则返回 null，说明当前没有正在试听的元素。
        },

        _buildMusicHtml: function (playerUrl) {
            var html = '<embed class="BDE_try_Music" allowfullscreen="false" pluginspage="http://www.macromedia.com/go/getflashplayer"';
            html += ' src="' + playerUrl + '"';
            html += ' width="1" height="1" style="position:absolute;left:-2000px;"';
            html += ' type="application/x-shockwave-flash" wmode="transparent" play="true" loop="false"';
            html += ' menu="false" allowscriptaccess="never" scale="noborder">';
            return html;
            // 定义 '_buildMusicHtml' 方法，用于构建包含音乐播放器的 HTML 代码。根据传入的音乐播放 URL（playerUrl 参数），创建一个 '<embed>' 嵌入元素，设置其类名为 'BDE_try_Music'，禁止全屏（allowfullscreen="false"），指定 Flash 插件获取地址（pluginspage="http://www.macromedia.com/go/getflashplayer"），设置'src' 属性为传入的播放 URL，同时设置宽度和高度都为 1 像素，并通过样式将其定位到页面不可见的位置（left:-2000px，可能是为了在不需要显示播放器时隐藏它，又不影响页面布局），指定类型为 'application/x-shockwave-flash'，设置透明模式（wmode="transparent"）、自动播放（play="true"）、不循环播放（loop="false"）、隐藏菜单（menu="false"）、禁止脚本访问（allowscriptaccess="never"）以及无边框缩放（scale="noborder"）等属性，最后返回构建好的 HTML 代码，可用于插入到页面中实现音乐播放功能（虽然设置了隐藏位置，但在合适的情况下可以调整位置使其可见并播放音乐）。
        },

        _byteLength: function (str) {
            return str.replace(/[^\u0000-\u007f]/g, "\u0061\u0061").length;
            // 定义 '_byteLength' 方法，用于计算字符串的字节长度。通过正则表达式将字符串中非
        },
        _getMaxText:function (s) {
            var me = this;
            s = me._removeHtml(s);
            if (me._byteLength(s) > 12)
                return s.substring(0, 5) + '...';
            if (!s) s = "&nbsp;";
            return s;
        },
        _rebuildData:function (data) {
            var me = this,
                newData = [],
                d = me.pageSize,
                itembox;
            for (var i = 0; i < data.length; i++) {
                if ((i + d) % d == 0) {
                    itembox = [];
                    newData.push(itembox)
                }
                itembox.push(data[i]);
            }
            return newData;
        },
        _renderTemplate:function (data) {
            var me = this;
            if (data.length == 0)return '<div class="empty">' + lang.emptyTxt + '</div>';
            data = me._rebuildData(data);
            var s = [], p = [], t = [];
            s.push('<div id="J_listPanel" class="listPanel">');
            p.push('<div class="page">');
            for (var i = 0, tmpList; tmpList = data[i++];) {
                panels.push('panel' + i);
                pages.push('page' + i);
                if (i == 1) {
                    s.push('<div id="panel' + i + '" class="panelon">');
                    if (data.length != 1) {
                        t.push('<div id="page' + i + '" onclick="music.onpageclick(' + i + ')" class="pageon">' + (i ) + '</div>');
                    }
                } else {
                    s.push('<div id="panel' + i + '" class="paneloff">');
                    t.push('<div id="page' + i + '" onclick="music.onpageclick(' + i + ')" class="pageoff">' + (i ) + '</div>');
                }
                s.push('<div class="m-box">');
                s.push('<div class="m-h"><span class="m-t">' + lang.chapter + '</span><span class="m-s">' + lang.singer
                    + '</span><span class="m-z">' + lang.special + '</span><span class="m-try-t">' + lang.listenTest + '</span></div>');
                for (var j = 0, tmpObj; tmpObj = tmpList[j++];) {
                    s.push('<label for="radio-' + i + '-' + j + '" class="m-m">');
                    s.push('<input type="radio" id="radio-' + i + '-' + j + '" name="musicId" class="m-l" onclick="music.doselect(' + (me.pageSize * (i-1) + (j-1)) + ')"/>');
                    s.push('<span class="m-t">' + me._getMaxText(tmpObj.title) + '</span>');
                    s.push('<span class="m-s">' + me._getMaxText(tmpObj.author) + '</span>');
                    s.push('<span class="m-z">' + me._getMaxText(tmpObj.album_title) + '</span>');
                    s.push('<span class="m-try" onclick="music.doselect(' + (me.pageSize * (i-1) + (j-1)) + ');music.listenTest(this)"></span>');
                    s.push('</label>');
                }
                s.push('</div>');
                s.push('</div>');
            }
            t.reverse();
            p.push(t.join(''));
            s.push('</div>');
            p.push('</div>');
            return s.join('') + p.join('');
        },
        exec:function () {
            var me = this;
            if (selectedItem == null)   return;
            $G('J_preview').innerHTML = "";
            editor.execCommand('music', {
                url:me._getUrl(false),
                width:400,
                height:95
            });
        }
    };
})();



