diff --git a/modules/Logistics.js b/modules/Logistics.js
index 5fb82e3..534bb6e 100644
--- a/modules/Logistics.js
+++ b/modules/Logistics.js
@@ -1,54 +1,68 @@
-// 导入 request 模块
-const request = require('request')
+// 导入 request 模块,该模块常用于在 Node.js 中发起 HTTP 请求,方便与其他网络服务进行交互
+const request = require('request');
// 自动匹配运单号所属的物流公司
function autoComNumber(orderno) {
- const url = `https://www.kuaidi100.com/autonumber/autoComNum?resultv2=1&text=${orderno}`
- return new Promise(function(resolve, reject) {
- request(url, (err, response, body) => {
- if (err) return reject({ status: 500, msg: err.message })
- // resolve(body)
- // console.log(body.num)
- body = JSON.parse(body)
- if (body.auto.length <= 0) return reject({ status: 501, msg: '无对应的物流公司' })
- resolve({ status: 200, msg: body.auto[0], comCode: body.auto[0].comCode })
- })
- })
+ // 构造请求的 URL,向快递100的接口发送请求,尝试自动匹配运单号对应的物流公司
+ // 将运单号拼接到 URL 参数中,resultv2=1 可能是用于指定返回结果的某种格式或版本相关参数
+ const url = `https://www.kuaidi100.com/autonumber/autoComNum?resultv2=1&text=${orderno}`;
+ // 返回一个 Promise 对象,用于处理异步操作,使得外部可以使用 async/await 语法来等待该函数执行完成
+ return new Promise(function (resolve, reject) {
+ // 使用 request 模块发起 GET 请求,传入 URL 和回调函数,回调函数接收请求过程中的错误、响应对象以及响应体内容作为参数
+ request(url, (err, response, body) => {
+ // 如果请求过程中出现错误,调用 reject 函数拒绝这个 Promise,并返回包含状态码 500 和错误信息的对象
+ if (err) return reject({ status: 500, msg: err.message });
+ // 将响应体内容(通常是 JSON 字符串格式)解析为 JavaScript 对象,方便后续提取数据
+ body = JSON.parse(body);
+ // 如果解析后的 body.auto 数组长度小于等于 0,意味着没有匹配到对应的物流公司,同样调用 reject 函数拒绝 Promise,并返回相应的错误信息对象
+ if (body.auto.length <= 0) return reject({ status: 501, msg: '无对应的物流公司' });
+ // 如果匹配到了物流公司,调用 resolve 函数成功解决这个 Promise,返回包含状态码 200、物流公司信息以及对应的公司编码的对象
+ resolve({ status: 200, msg: body.auto[0], comCode: body.auto[0].comCode });
+ });
+ });
}
+// 定义一个异步函数,用于获取物流信息,接收 Express 框架的请求(req)和响应(res)对象作为参数(从函数名和使用方式推测可能在 Express 应用中使用)
async function getLogisticsInfo(req, res) {
- const result = await autoComNumber(req.params.orderno)
+ // 调用 autoComNumber 函数,传入请求参数中的运单号(req.params.orderno),并使用 await 等待该异步操作完成,获取匹配物流公司后的结果
+ const result = await autoComNumber(req.params.orderno);
- if (result.status !== 200) {
- return {
- meta: {
- status: 500,
- message: '获取物流信息失败!'
- }
+ // 如果返回结果的状态码不是 200,说明匹配物流公司出现问题,直接返回一个包含错误状态和相应错误消息的对象给客户端
+ if (result.status!== 200) {
+ return {
+ meta: {
+ status: 500,
+ message: '获取物流信息失败!'
+ }
+ };
}
- }
- const dataUrl = `https://www.kuaidi100.com/query?type=${result.comCode}&postid=${req.params.orderno}&temp=0.2595247267684455`
- request(dataUrl, (err, response, body) => {
- if (err) {
- return res.send({
- meta: {
- status: 501,
- message: '获取物流信息失败!'
+ // 根据匹配到的物流公司编码和运单号构造查询物流信息的 URL,向快递100的另一个接口发送请求以获取详细物流信息
+ const dataUrl = `https://www.kuaidi100.com/query?type=${result.comCode}&postid=${req.params.orderno}&temp=0.2595247267684455`;
+ // 使用 request 模块发起请求,传入构造好的 URL 和回调函数,处理获取物流信息过程中的各种情况
+ request(dataUrl, (err, response, body) => {
+ // 如果请求过程中出现错误,使用 Express 的 res.send 方法向客户端发送包含错误状态和相应错误消息的对象,表示获取物流信息失败
+ if (err) {
+ return res.send({
+ meta: {
+ status: 501,
+ message: '获取物流信息失败!'
+ }
+ });
}
- })
- }
- // 获取物流信息成功
- return res.send({
- meta: {
- status: 200,
- message: '获取物流信息成功!'
- },
- data: (JSON.parse(body)).data
- })
- })
+ // 如果请求成功,解析响应体内容(假设为 JSON 格式),提取其中的物流信息数据(.data 属性,具体格式取决于快递100接口返回的数据结构),
+ // 然后使用 Express 的 res.send 方法向客户端发送包含成功状态、相应消息以及物流信息数据的对象,表示获取物流信息成功
+ return res.send({
+ meta: {
+ status: 200,
+ message: '获取物流信息成功!'
+ },
+ data: (JSON.parse(body)).data
+ });
+ });
}
+// 将 getLogisticsInfo 函数作为模块的属性导出,方便其他模块引入并使用这个函数来获取物流信息
module.exports = {
- getLogisticsInfo
-}
+ getLogisticsInfo
+};
\ No newline at end of file
diff --git a/modules/authorization.js b/modules/authorization.js
index 7142ec4..82ada1f 100644
--- a/modules/authorization.js
+++ b/modules/authorization.js
@@ -1,74 +1,91 @@
+// 引入Node.js的path模块,用于处理文件路径相关操作
var path = require('path');
+// 原本要引入的roles控制器模块,此处被注释掉了,可能后续会按需启用
// var roles_controller = require("../controllers/roles");
+// 再次引入path模块,前面的引入语句重复了,但在Node.js中不会有实质影响,可考虑优化掉重复引入的情况
var path = require("path");
+// 创建一个全局对象service_caches,用于缓存服务相关的模块或数据,初始为空对象
global.service_caches = {};
-// 存储全局验证函数
+// 创建一个全局变量service_auth_fn,用于存储全局验证函数,初始值为null
global.service_auth_fn = null;
/**
- * 构造回调对象格式
+ * Invocation函数用于构造一个回调对象格式,主要用于在调用服务的具体方法时进行权限验证等相关操作。
*
- * @param {[type]} serviceName 服务名称
- * @param {[type]} actionName 动作名称(方法名)
- * @param {[type]} serviceModule 服务模块
- * @param {[type]} origFunc 原始方法
+ * @param {[type]} serviceName 服务名称,用于标识具体是哪个服务。
+ * @param {[type]} actionName 动作名称(方法名),对应服务模块里具体的函数名称。
+ * @param {[type]} serviceModule 服务模块,即通过require加载进来的包含具体业务逻辑函数的模块对象。
+ * @param {[type]} origFunc 原始方法,也就是服务模块里未经包装的原始业务逻辑函数。
*/
-function Invocation(serviceName,actionName,serviceModule,origFunc) {
- return function() {
- var origArguments = arguments;
- return function(req,res,next) {
- if(global.service_auth_fn) {
- global.service_auth_fn(req,res,next,serviceName,actionName,function(pass) {
- if(pass) {
- origFunc.apply(serviceModule,origArguments);
- } else {
- res.sendResult(null,401,"权限验证失败");
- }
- });
- } else {
- res.sendResult(null,401,"权限验证失败");
- }
- }
- }
+function Invocation(serviceName, actionName, serviceModule, origFunc) {
+ // 返回一个新的函数,这个函数会接收一些参数(此处未在形参中体现,在调用时会传入),并返回另一个函数作为真正的回调函数
+ return function () {
+ var origArguments = arguments;
+ return function (req, res, next) {
+ // 判断全局验证函数是否存在,如果存在则进行权限验证流程
+ if (global.service_auth_fn) {
+ global.service_auth_fn(req, res, next, serviceName, actionName, function (pass) {
+ // 如果权限验证通过(pass为true),则执行原始的业务逻辑函数
+ if (pass) {
+ origFunc.apply(serviceModule, origArguments);
+ } else {
+ // 如果权限验证失败,向客户端返回相应的错误信息,状态码为401表示未授权
+ res.sendResult(null, 401, "权限验证失败");
+ }
+ });
+ } else {
+ // 如果全局验证函数不存在,直接返回权限验证失败的错误信息给客户端
+ res.sendResult(null, 401, "权限验证失败");
+ }
+ }
+ }
}
-// 获取服务对象
-module.exports.getService = function(serviceName) {
+// 定义一个函数用于获取服务对象,它是模块对外暴露的接口之一
+module.exports.getService = function (serviceName) {
+ // 先检查全局缓存中是否已经存在指定名称的服务对象,如果存在则直接返回缓存中的服务对象
+ if (global.service_caches[serviceName]) {
+ return global.service_caches[serviceName];
+ }
- if(global.service_caches[serviceName]) {
- return global.service_caches[serviceName];
- }
+ // 构建服务模块的文件路径,通过拼接当前工作目录、"services"文件夹以及服务名称来确定具体路径
+ var servicePath = path.join(process.cwd(), "services", serviceName);
- var servicePath = path.join(process.cwd(),"services",serviceName);
-
- var serviceModule = require(servicePath);
- if(!serviceModule) {
- console.log("模块没有被发现");
- return null;
- }
- global.service_caches[serviceName] = {};
+ // 通过require加载对应的服务模块,如果模块不存在则返回null并在控制台打印提示信息
+ var serviceModule = require(servicePath);
+ if (!serviceModule) {
+ console.log("模块没有被发现");
+ return null;
+ }
- console.log("*****************************************");
- console.log("拦截服务 => %s",serviceName);
- console.log("*****************************************");
- for(actionName in serviceModule) {
+ // 在全局缓存对象中为当前服务名称创建一个空对象,用于后续存储该服务的具体方法相关的回调对象等信息
+ global.service_caches[serviceName] = {};
- if(serviceModule && serviceModule[actionName] && typeof(serviceModule[actionName]) == "function") {
- var origFunc = serviceModule[actionName];
- global.service_caches[serviceName][actionName] = Invocation(serviceName,actionName,serviceModule,origFunc);
- console.log("action => %s",actionName);
- }
- }
- // console.log(global.service_caches);
- console.log("*****************************************\n");
- return global.service_caches[serviceName];
-}
+ // 打印一些提示信息,表明正在拦截指定名称的服务,方便调试和查看流程
+ console.log("*****************************************");
+ console.log("拦截服务 => %s", serviceName);
+ console.log("*****************************************");
-// 设置全局验证函数
-module.exports.setAuthFn = function(authFn) {
- global.service_auth_fn = authFn;
+ // 遍历服务模块里的所有属性(通常是方法)
+ for (actionName in serviceModule) {
+ // 检查属性对应的是否是一个函数(即服务模块里有效的业务逻辑函数)
+ if (serviceModule && serviceModule[actionName] && typeof (serviceModule[actionName]) == "function") {
+ var origFunc = serviceModule[actionName];
+ // 使用Invocation函数构造该方法对应的回调对象,并将其存储到全局缓存对象中对应的服务和方法名下
+ global.service_caches[serviceName][actionName] = Invocation(serviceName, actionName, serviceModule, origFunc);
+ console.log("action => %s", actionName);
+ }
+ }
+ // 打印一些分割线和空行,用于在控制台输出中更清晰地区分不同服务的处理情况,此处注释掉了打印全局缓存对象的语句
+ // console.log(global.service_caches);
+ console.log("*****************************************\n");
+ return global.service_caches[serviceName];
}
+// 定义一个函数用于设置全局验证函数,它也是模块对外暴露的接口,供外部传入具体的验证函数
+module.exports.setAuthFn = function (authFn) {
+ global.service_auth_fn = authFn;
+}
\ No newline at end of file
diff --git a/modules/database.js b/modules/database.js
index 7fd8a20..32988e9 100644
--- a/modules/database.js
+++ b/modules/database.js
@@ -1,77 +1,95 @@
+// 引入Node.js的mysql模块,用于与MySQL数据库进行交互(虽然此处只是引入,后续具体使用情况暂未明确体现)
require('mysql');
+// 引入Node.js的文件系统模块fs,用于对文件进行读写等操作
var fs = require("fs");
+// 引入orm模块,可能用于对象关系映射(Object Relational Mapping)相关操作,方便在Node.js中操作数据库
var orm = require("orm");
+// 引入bluebird模块,用于提供更强大的Promise功能,便于处理异步操作
var Promise = require("bluebird");
+// 引入path模块,用于处理文件路径相关的操作
var path = require("path");
/*
- app: 应用程序环境
- config: 数据库配置
- callback: 回调
-*/
-function initialize(app,callback) {
+ * initialize函数用于初始化数据库相关的配置以及加载ORM(对象关系映射)模型,
+ * 它接收应用程序环境对象、回调函数作为参数,通过一系列操作完成数据库连接和模型加载,
+ * 并在操作完成后通过回调函数返回相应结果。
+ *
+ * @param {Object} app - 应用程序环境对象,通常包含了应用的一些全局配置和上下文信息,可用于挂载数据库实例、模型等相关对象。
+ * @param {Function} callback - 回调函数,在数据库初始化及模型加载完成(成功或失败)后被调用,用于传递操作结果等信息。
+ */
+function initialize(app, callback) {
+ // 加载配置文件,通过config模块(这里应该是自定义的配置管理模块)获取名为"db_config"的配置信息
+ var config = require('config').get("db_config");
- // 加载配置文件
- var config = require('config').get("db_config");
-
- // 从配置中获取数据库配置
- var opts = {
- protocol : config.get("protocol"),
- host : config.get("host"),
- database : config.get("database"),
- port : config.get("port"),
- user : config.get("user"),
- password : config.get("password"),
- query : {pool: true,debug: true}
- };
+ // 从配置信息中提取数据库相关的配置参数,构建一个包含协议、主机、数据库名称、端口、用户名、密码以及查询相关配置(启用连接池、开启调试模式)的对象opts
+ var opts = {
+ protocol: config.get("protocol"),
+ host: config.get("host"),
+ database: config.get("database"),
+ port: config.get("port"),
+ user: config.get("user"),
+ password: config.get("password"),
+ query: { pool: true, debug: true }
+ };
-
- console.log("数据库连接参数 %s",JSON.stringify(opts));
+ // 在控制台打印数据库连接参数,将opts对象转换为JSON字符串格式输出,方便查看配置情况
+ console.log("数据库连接参数 %s", JSON.stringify(opts));
- // 初始化ORM模型
- app.use(orm.express(opts, {
- define: function (db, models, next) {
+ // 使用orm模块的express方法初始化ORM模型,将数据库配置参数opts以及一个包含define函数的对象作为参数传入
+ app.use(orm.express(opts, {
+ define: function (db, models, next) {
+ // 将数据库实例db挂载到应用程序环境对象app上,方便在其他地方访问数据库实例
+ app.db = db;
+ // 同时也将数据库实例挂载到全局对象global上,命名为database,不过使用全局变量需要谨慎,避免命名冲突等问题
+ global.database = db;
- app.db = db;
- global.database = db;
+ // 获取映射文件的路径,通过拼接当前工作目录和"/models"来确定模型文件所在的文件夹路径
+ var modelsPath = path.join(process.cwd(), "/models");
- // 获取映射文件路径
- var modelsPath = path.join(process.cwd(),"/models");
-
- // 读取所有模型文件
- fs.readdir(modelsPath,function(err, files) {
- // 存放所有的加载模型函数
- var loadModelAsynFns = new Array();
- // console.log("开始加载 ORM 模型层文件 ");
- for (var i = 0; i < files.length; i++) {
- var modelPath = modelsPath + "/" +files[i];
- // console.log("加载模型 %s",modelPath);
- loadModelAsynFns[i] = db.loadAsync(modelPath);
- }
-
- Promise.all(loadModelAsynFns)
- .then(function(){
- // console.log("ORM 模型加载完成");
- // 挂载模型集合
+ // 使用文件系统模块的readdir方法读取指定路径下的所有文件(即模型文件),读取完成后会调用回调函数处理结果
+ fs.readdir(modelsPath, function (err, files) {
+ // 创建一个数组,用于存放所有加载模型的异步函数,每个元素对应一个模型文件的加载操作
+ var loadModelAsynFns = new Array();
+ // 以下循环遍历读取到的所有文件,准备加载每个模型文件对应的模型定义(通过db.loadAsync方法异步加载)
+ // console.log("开始加载 ORM 模型层文件 ");
+ for (var i = 0; i < files.length; i++) {
+ var modelPath = modelsPath + "/" + files[i];
+ // console.log("加载模型 %s", modelPath);
+ // 将每个模型文件的异步加载函数存入数组中,后续会统一处理这些异步加载操作
+ loadModelAsynFns[i] = db.loadAsync(modelPath);
+ }
- for(var modelName in db.models){
- models[modelName] = db.models[modelName];
- }
- app.models = models;
- callback(null);
- next();
- })
- .catch(function(error){
- console.error('加载模块出错 error: ' + err);
- callback(error);
- next();
- });
- });
- }
- }));
+ // 使用Promise.all方法来并行处理所有模型文件的加载操作,当所有加载操作都成功完成后,执行then回调函数
+ Promise.all(loadModelAsynFns)
+ .then(function () {
+ // console.log("ORM 模型加载完成");
+ // 遍历数据库实例中已加载的所有模型,将每个模型挂载到models对象上,方便统一管理和访问
+ for (var modelName in db.models) {
+ models[modelName] = db.models[modelName];
+ }
+ // 将包含所有模型的models对象挂载到应用程序环境对象app上,方便在应用中使用模型进行数据库操作
+ app.models = models;
+ // 调用传入的回调函数,传递null表示操作成功完成,无错误信息
+ callback(null);
+ // 执行next函数,用于告知orm模块相关操作已完成,可继续后续流程(具体取决于orm模块的设计和使用方式)
+ next();
+ })
+ .catch(function (error) {
+ // 如果在加载模型过程中出现错误,在控制台打印错误信息,同时调用回调函数并传递错误对象,告知调用者操作失败
+ console.error('加载模块出错 error: ' + err);
+ callback(error);
+ // 同样执行next函数,告知orm模块相关流程结束(即便出现错误)
+ next();
+ });
+ });
+ }
+ }));
}
+// 将initialize函数作为模块的一个属性暴露出去,方便其他模块引入并调用该函数进行数据库初始化操作
module.exports.initialize = initialize;
-module.exports.getDatabase = function() {
- return global.database;
+
+// 定义一个函数用于获取全局的数据库实例对象,通过返回global对象上挂载的database属性(在initialize函数中进行挂载的)来实现
+module.exports.getDatabase = function () {
+ return global.database;
}
\ No newline at end of file
diff --git a/modules/logger.js b/modules/logger.js
index a811e85..fb0e1b4 100644
--- a/modules/logger.js
+++ b/modules/logger.js
@@ -1,20 +1,44 @@
+// 引入log4js模块,它是一个用于在Node.js应用中进行日志记录的常用库,能够方便地实现不同级别的日志输出以及日志的格式化、存储等功能
var log4js = require('log4js');
+// 使用log4js的configure方法进行日志配置,配置项以对象形式传入
log4js.configure({
- appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
- categories: { default: { appenders: ['cheese'], level: 'error' } }
+ // 配置日志的输出目标(appenders),这里定义了一个名为'cheese'的输出配置
+ appenders: {
+ // 'type'指定了输出类型为'file',即输出到文件
+ // 'filename'指定了输出的文件名是'cheese.log',意味着日志信息将会被记录到该文件中
+ cheese: { type: 'file', filename: 'cheese.log' }
+ },
+ // 配置日志的分类(categories),用于将不同的日志记录器划分到不同的类别,并指定每个类别的相关属性
+ categories: {
+ // 'default'表示默认的日志类别
+ default: {
+ // 'appenders'数组指定了该类别使用的日志输出目标,这里只使用了前面定义的'cheese'输出目标
+ appenders: ['cheese'],
+ // 'level'指定了该类别日志记录的级别,这里设置为'error',表示只有错误级别及以上的日志才会被记录到对应的输出目标中
+ level: 'error'
+ }
+ }
});
+// 将一个名为'logger'的函数作为模块的属性导出,用于获取日志记录器实例
exports.logger = function (level) {
+ // 通过log4js的getLogger方法获取名为'cheese'的日志记录器实例,后续可以使用这个实例来记录相应的日志信息
var logger = log4js.getLogger("cheese");
+ // 设置该日志记录器的日志级别为'debug',这意味着从调试级别(最低级别)开始的所有日志都会被记录,覆盖了之前配置中'default'类别里设置的'error'级别(此处修改可能根据实际需求而定)
logger.level = 'debug';
+ // 返回配置好的日志记录器实例,外部模块可以调用该函数获取实例后进行日志记录操作
return logger;
};
-// 配合 express 使用的方法
+// 以下是一段被注释掉的代码,从函数名和使用方式来看,它原本是用于配合Express框架使用的方法,用于在Express应用中集成log4js日志记录功能
// exports.use = function (app, level) {
+// // 在Express应用中使用log4js的connectLogger中间件来记录请求相关的日志信息
+// // 首先通过log4js的getLogger方法获取名为'logInfo'的日志记录器实例(这里假设'logInfo'是之前定义好的某个日志记录器名称,实际可能需要准确配置)
// app.use(log4js.connectLogger(log4js.getLogger('logInfo'), {
+// // 设置日志记录的级别,它会尝试从传入的'level'参数获取对应级别,如果没有则使用默认的'debug'级别(这里的'levels'变量未在提供的代码中定义,可能是一个定义了不同日志级别映射关系的对象,实际应用中需要准确处理)
// level: levels[level] || levels['debug'],
+// // 定义日志记录的格式,这里使用了简单的格式字符串,指定记录请求的方法、URL以及响应状态等信息,方便后续查看请求相关的日志详情
// format: ':method :url :status'
// }));
// };
\ No newline at end of file
diff --git a/modules/passport.js b/modules/passport.js
index dfd4712..1f38ec8 100644
--- a/modules/passport.js
+++ b/modules/passport.js
@@ -1,86 +1,123 @@
+// 引入Passport模块,它是Node.js中用于处理用户认证的常用中间件,可方便地集成多种认证策略
const passport = require('passport');
+// 引入Passport的本地策略(LocalStrategy),通常用于基于用户名和密码的认证方式,比如常见的表单登录验证
const LocalStrategy = require('passport-local').Strategy;
+// 引入Passport的HTTP Bearer策略(Strategy),常用于基于令牌(Token)的认证,比如JSON Web Tokens(JWT)认证场景
const Strategy = require('passport-http-bearer').Strategy;
+// 引入jsonwebtoken模块,用于处理JSON Web Tokens(JWT)的生成、验证等操作
var jwt = require("jsonwebtoken");
-
+// 引入Lodash库,它提供了很多实用的工具函数,用于处理数据、对象等,此处虽未明确体现具体使用的功能,但可能用于辅助数据操作等
var _ = require('lodash');
-
+// 引入配置模块(config),并获取名为"jwt_config"的配置项,通常用于获取与JWT相关的配置信息,如密钥、过期时间等
var jwt_config = require("config").get("jwt_config");
// 通过登录函数初始化
/**
- * 初始化 passport 框架
+ * 初始化passport框架
+ * 此函数的主要作用是配置Passport的不同认证策略,并将Passport初始化到给定的应用程序(app)中,
+ * 同时提供回调机制,方便在初始化完成后执行额外的操作。
*
- * @param {[type]} app 全局应用程序
- * @param {[type]} loginFunc 登录函数
- * @param {Function} callback 回调函数
+ * @param {[type]} app 全局应用程序,通常是Express等Web框架创建的应用实例,用于挂载中间件等操作。
+ * @param {[type]} loginFunc 登录函数,用于执行具体的用户名和密码验证逻辑,比如查询数据库验证用户输入的用户名和密码是否匹配等操作。
+ * @param {Function} callback 回调函数,在Passport初始化及相关策略配置完成后被调用,可用于执行后续自定义的初始化步骤等操作(可选参数)。
*/
-module.exports.setup = function(app,loginFunc,callback) {
- // 用户名密码 登录策略
- passport.use(new LocalStrategy(
- function(username, password, done) {
- if(!loginFunc) return done("登录验证函数未设置");
+module.exports.setup = function (app, loginFunc, callback) {
+ // 用户名密码 登录策略
+ // 使用Passport的LocalStrategy配置基于用户名和密码的认证策略
+ passport.use(new LocalStrategy(
+ // 定义验证逻辑函数,接收用户名、密码以及回调函数(done)作为参数
+ function (username, password, done) {
+ // 如果没有传入登录验证函数(loginFunc),则直接通过回调函数(done)返回错误信息,表示登录验证函数未设置
+ if (!loginFunc) return done("登录验证函数未设置");
- loginFunc(username,password,function(err,user) {
- if(err) return done(err);
- return done(null, user);
- });
- })
- );
+ // 调用传入的登录验证函数(loginFunc),传入用户名和密码进行验证,验证完成后会在回调函数中返回结果
+ loginFunc(username, password, function (err, user) {
+ // 如果验证过程中出现错误,通过回调函数(done)返回错误信息给Passport,告知认证失败原因
+ if (err) return done(err);
+ // 如果验证成功,通过回调函数(done)返回用户信息(通常是包含用户相关数据的对象)给Passport,表示认证成功
+ return done(null, user);
+ });
+ }
+ ));
- // token 验证策略
- passport.use(new Strategy(
- function(token, done) {
- jwt.verify(token, jwt_config.get("secretKey"), function (err, decode) {
- if (err) { return done("验证错误"); }
- return done(null, decode);
- });
- }
- ));
+ // token验证策略
+ // 使用Passport的HTTP Bearer策略配置基于令牌(Token)的认证策略
+ passport.use(new Strategy(
+ // 定义验证逻辑函数,接收令牌(token)以及回调函数(done)作为参数
+ function (token, done) {
+ // 使用jsonwebtoken模块的verify方法验证传入的令牌(token)是否有效,
+ // 传入令牌、配置中的密钥(jwt_config.get("secretKey"))以及验证回调函数,验证回调函数接收验证结果(err)和解析后的令牌数据(decode)作为参数
+ jwt.verify(token, jwt_config.get("secretKey"), function (err, decode) {
+ // 如果验证过程中出现错误,通过回调函数(done)返回错误信息给Passport,表示令牌验证失败
+ if (err) { return done("验证错误"); }
+ // 如果验证成功,通过回调函数(done)返回解析后的令牌数据(decode)给Passport,表示令牌验证通过
+ return done(null, decode);
+ });
+ }
+ ));
- // 初始化passport模块
- app.use(passport.initialize());
+ // 初始化passport模块
+ // 将Passport中间件初始化到应用程序(app)中,使其能够在后续的请求处理流程中生效,用于处理各种认证相关的操作
+ app.use(passport.initialize());
- if(callback) callback();
+ // 如果传入了回调函数(callback),则执行该回调函数,用于在初始化完成后执行额外的自定义操作
+ if (callback) callback();
};
/**
* 登录验证逻辑
+ * 此函数主要用于处理基于用户名和密码的登录请求验证逻辑,通过调用Passport的认证中间件进行验证,
+ * 根据验证结果生成相应的响应信息返回给客户端,包括在验证成功时生成并返回JWT令牌等操作。
*
- * @param {[type]} req 请求
- * @param {[type]} res 响应
- * @param {Function} next [description]
+ * @param {[type]} req 请求对象,包含了客户端发送的请求相关信息,如请求头、请求体等内容,常用于获取用户名、密码等登录信息。
+ * @param {[type]} res 响应对象,用于向客户端发送响应信息,如返回登录成功或失败的消息、生成的令牌等内容。
+ * @param {Function} next 传递事件函数,用于将请求传递给下一个中间件继续处理(在Express等框架的中间件链机制中使用),此处未明确体现后续传递逻辑,但遵循中间件规范保留该参数。
*/
-module.exports.login = function(req,res,next) {
+module.exports.login = function (req, res, next) {
+ // 使用Passport的authenticate方法,传入'local'表示采用之前配置的基于用户名和密码的本地认证策略进行认证,
+ // 同时传入一个回调函数用于处理认证结果,该回调函数接收认证过程中的错误(err)、认证通过后的用户信息(user)以及额外的提示信息(info)作为参数
+ passport.authenticate('local', function (err, user, info) {
+ // 如果认证过程中出现错误,使用响应对象(res)的sendResult方法(这里假设是自定义的用于统一返回响应格式的方法)向客户端发送包含错误状态码(400表示请求错误)和错误信息的响应
+ if (err) return res.sendResult(null, 400, err);
+ // 如果没有获取到用户信息(即认证失败,未找到匹配的用户),同样使用响应对象的sendResult方法向客户端发送包含错误状态码和相应错误提示信息的响应
+ if (!user) return res.sendResult(null, 400, "参数错误");
- passport.authenticate('local', function(err, user, info) {
-
- if(err) return res.sendResult(null,400,err);
- if(!user) return res.sendResult(null,400,"参数错误");
-
- // 获取角色信息
- var token = jwt.sign({"uid":user.id,"rid":user.rid}, jwt_config.get("secretKey"), {"expiresIn": jwt_config.get("expiresIn")});
- user.token = "Bearer " + token;
- return res.sendResult(user,200,'登录成功');
- })(req, res, next);
-
-}
+ // 获取角色信息
+ // 使用jsonwebtoken模块的sign方法生成JWT令牌,传入包含用户ID(uid)和角色ID(rid)的对象、配置中的密钥以及令牌过期时间等配置信息,生成一个有效的JWT令牌
+ var token = jwt.sign({"uid": user.id, "rid": user.rid}, jwt_config.get("secretKey"), {"expiresIn": jwt_config.get("expiresIn")});
+ // 将生成的令牌添加到用户对象中,并添加"Bearer "前缀,符合Bearer Token的规范格式,方便后续在请求头中传递和验证
+ user.token = "Bearer " + token;
+ // 使用响应对象的sendResult方法向客户端发送包含用户信息、成功状态码(200)以及登录成功消息的响应,表示登录操作成功完成,并返回相关用户数据和令牌
+ return res.sendResult(user, 200, '登录成功');
+ })(req, res, next);
+};
/**
* token验证函数
+ * 此函数用于处理基于令牌(Token)的验证逻辑,通过调用Passport的相应认证中间件验证传入的令牌是否有效,
+ * 根据验证结果设置请求对象(req)中的用户信息,并决定是否将请求传递给下一个中间件继续处理。
*
- * @param {[type]} req 请求对象
- * @param {[type]} res 响应对象
- * @param {Function} next 传递事件函数
+ * @param {[type]} req 请求对象,包含了客户端发送的请求相关信息以及在验证通过后可用于存储用户相关数据等内容,方便后续中间件使用验证后的用户信息进行业务逻辑处理。
+ * @param {[type]} res 响应对象,用于向客户端发送响应信息,如在令牌验证失败时返回相应的错误消息等内容。
+ * @param {Function} next 传递事件函数,用于将请求传递给下一个中间件继续处理,在令牌验证通过后会调用该函数将请求传递下去,让后续中间件继续执行相应的业务逻辑。
*/
-module.exports.tokenAuth = function(req,res,next) {
- passport.authenticate('bearer', { session: false },function(err,tokenData) {
- if(err) return res.sendResult(null,400,'无效token');
- if(!tokenData) return res.sendResult(null,400,'无效token');
- req.userInfo = {};
- req.userInfo.uid = tokenData["uid"];
- req.userInfo.rid = tokenData["rid"];
- next();
- })(req, res, next);
-}
+module.exports.tokenAuth = function (req, res, next) {
+ // 使用Passport的authenticate方法,传入'bearer'表示采用之前配置的基于HTTP Bearer的认证策略进行令牌验证,
+ // 同时传入{ session: false }表示不依赖会话(Session)进行验证(常用于无状态的API认证场景,如基于JWT的认证),
+ // 并传入一个回调函数用于处理验证结果,该回调函数接收验证过程中的错误(err)以及验证通过后的令牌数据(tokenData)作为参数
+ passport.authenticate('bearer', { session: false }, function (err, tokenData) {
+ // 如果验证过程中出现错误,使用响应对象(res)的sendResult方法向客户端发送包含错误状态码(400表示请求错误)和相应错误提示信息(无效token)的响应
+ if (err) return res.sendResult(null, 400, '无效token');
+ // 如果没有获取到有效的令牌数据(即令牌验证失败),同样使用响应对象的sendResult方法向客户端发送包含错误状态码和相应错误提示信息的响应
+ if (!tokenData) return res.sendResult(null, 400, '无效token');
+ // 如果令牌验证通过,在请求对象(req)中创建一个空的用户信息对象(userInfo),用于存储从令牌中解析出的相关用户信息,方便后续中间件使用
+ req.userInfo = {};
+ // 将从令牌数据(tokenData)中解析出的用户ID(uid)存储到请求对象的用户信息对象中
+ req.userInfo.uid = tokenData["uid"];
+ // 将从令牌数据(tokenData)中解析出的角色ID(rid)存储到请求对象的用户信息对象中
+ req.userInfo.rid = tokenData["rid"];
+ // 调用传递事件函数(next),将请求传递给下一个中间件继续处理,表示令牌验证通过,后续中间件可以基于请求对象中的用户信息进行相应的业务逻辑操作
+ next();
+ })(req, res, next);
+}
\ No newline at end of file
diff --git a/modules/resextra.js b/modules/resextra.js
index 313da22..8c5d67e 100644
--- a/modules/resextra.js
+++ b/modules/resextra.js
@@ -1,17 +1,29 @@
// 添加统一的返回结果方法
-module.exports = function(req, res, next){
- res.sendResult = function(data,code,message) {
- var fmt = req.query.fmt ? req.query.fmt : "rest";
- if(fmt == "rest") {
- res.json(
- {
- "data" : data,
- "meta" : {
- "msg" : message,
- "status" : code
- }
- });
- }
- };
- next();
+// 此代码将一个函数作为模块的导出内容,该函数很可能作为中间件在 Express 等 Node.js 的 Web 框架中使用,
+// 目的是为响应对象(res)添加一个自定义的 `sendResult` 方法,用于统一格式化返回给客户端的结果数据。
+module.exports = function (req, res, next) {
+ // 为响应对象(res)添加 `sendResult` 方法,用于按照特定格式向客户端返回结果数据,
+ // 该方法接收要返回的数据(data)、状态码(code)以及提示消息(message)作为参数。
+ res.sendResult = function (data, code, message) {
+ // 获取请求对象(req)中查询参数(query)里名为 `fmt` 的参数值,若不存在则默认为 "rest",
+ // 这个参数用于指定返回结果的格式,这里根据不同的值可以实现不同的返回格式逻辑(目前仅实现了 "rest" 格式)。
+ var fmt = req.query.fmt? req.query.fmt : "rest";
+ // 判断返回结果的格式是否为 "rest",如果是,则按照相应的 JSON 格式进行数据组装并返回给客户端。
+ if (fmt == "rest") {
+ res.json(
+ {
+ // 将传入的要返回的数据(data)放置在 "data" 字段下。
+ "data": data,
+ "meta": {
+ // 将传入的提示消息(message)放置在 "meta" 字段下的 "msg" 子字段中,用于告知客户端相关操作的提示信息。
+ "msg": message,
+ // 将传入的状态码(code)放置在 "meta" 字段下的 "status" 子字段中,用于告知客户端操作的状态,比如 200 表示成功,400 表示客户端错误等。
+ "status": code
+ }
+ });
+ }
+ };
+ // 调用 `next` 函数,将请求传递给下一个中间件继续处理,遵循 Express 等框架中间件的执行顺序和规范,
+ // 保证整个请求处理流程能够继续进行下去。
+ next();
}
\ No newline at end of file
diff --git a/modules/ueditor.js b/modules/ueditor.js
index 3d8ccb0..fcba046 100644
--- a/modules/ueditor.js
+++ b/modules/ueditor.js
@@ -1,67 +1,101 @@
+// 引入Lodash库,它提供了很多实用的工具函数,用于处理数据、对象、数组等操作,虽然此处未明确体现具体使用的功能,但后续可能会借助它进行一些便捷的数据处理
var _ = require('lodash');
+// 引入Node.js的path模块,用于处理文件路径相关的操作,比如拼接、解析路径等
var path = require("path");
+// 引入Busboy模块,它常用于处理文件上传的中间件,能够方便地解析包含文件的HTTP请求中的文件数据
var Busboy = require('busboy');
+// 引入Node.js的文件系统模块fs,用于对文件进行读写、查询目录等操作
var fs = require("fs");
+// 引入uniqid模块,用于生成唯一的标识符,在这里可能用于为上传的文件生成唯一的文件名
var uniqid = require('uniqid');
-var ueditor_config = require(path.join(process.cwd(),"/config/ueditor.config.js"));
+// 引入UEditor的配置文件,通过拼接当前工作目录和配置文件的相对路径来获取其准确路径,该配置文件可能包含了UEditor相关的各种配置参数,如上传路径、允许的文件类型等
+var ueditor_config = require(path.join(process.cwd(), "/config/ueditor.config.js"));
+// 引入项目的上传配置信息,通过配置模块(config)获取名为"upload_config"的配置项,里面应该包含了如上传目录、基础URL等与上传相关的配置内容
var upload_config = require('config').get("upload_config");
+// 定义允许上传的文件类型列表,以逗号分隔的字符串形式表示,目前包含了常见的图片文件类型以及ico、bmp文件类型
var filetype = 'jpg,png,gif,ico,bmp';
-module.exports = function(req,res,next) {
- if(req.query.action == "config") {
- // 吐给客户端配置信息
- res.jsonp(ueditor_config);
- } else if (req.query.action === 'uploadimage' || req.query.action === 'uploadfile' || req.query.action === 'uploadvideo') {
- var busboy = new Busboy({ headers: req.headers });
- busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
- var fileExtArray = filename.split(".");
- var ext = fileExtArray[fileExtArray.length - 1];
- var save_filename = uniqid() + "." + ext;
- var savePath = path.join(process.cwd(),upload_config.get("upload_ueditor"),save_filename);
- file.on('end', function () {
- var result = {
- 'url': upload_config.get("baseURL")+"/" + upload_config.get("upload_ueditor") + "/" + save_filename,
- 'title': req.body && req.body.pictitle || filename,
- 'original': filename,
- 'state': 'SUCCESS'
- };
- if(req.query.encode) {
- res.jsonp(result);
- } else {
+// 将一个函数作为模块的导出内容,这个函数很可能作为中间件在Express等Node.js的Web框架中使用,用于处理不同UEditor相关的请求操作,比如获取配置、上传文件、获取文件列表等
+module.exports = function (req, res, next) {
+ // 判断请求中的查询参数action的值是否为"config",如果是,则表示客户端请求获取UEditor的配置信息
+ if (req.query.action == "config") {
+ // 使用res.jsonp方法将UEditor的配置信息(ueditor_config)返回给客户端,jsonp是一种跨域数据传输的方式,常用于解决浏览器的同源策略限制问题,使得客户端能够获取到配置数据
+ res.jsonp(ueditor_config);
+ } else if (req.query.action === 'uploadimage' || req.query.action === 'uploadfile' || req.query.action === 'uploadvideo') {
+ // 创建一个Busboy实例,用于解析包含文件上传的请求,传入请求头(headers)信息,使得Busboy能够根据请求头中的相关信息(如Content-Type等)来正确解析文件数据
+ var busboy = new Busboy({ headers: req.headers });
- res.redirect(upload_config.get("simple_upload_redirect") + "?result=" + JSON.stringify(result));
- // res.redirect(result.url);
- }
-
- });
+ // 监听Busboy实例的'file'事件,当解析到文件数据时,该事件会被触发,事件回调函数接收多个参数,用于获取文件相关的各种信息
+ busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
+ // 将文件名(filename)按"."进行分割,得到一个包含文件名各部分的数组,用于提取文件的扩展名
+ var fileExtArray = filename.split(".");
+ // 获取文件扩展名,即分割后的数组中的最后一个元素
+ var ext = fileExtArray[fileExtArray.length - 1];
+ // 使用uniqid模块生成一个唯一的标识符,并与文件扩展名拼接起来,作为保存文件时使用的文件名,这样可以确保文件名的唯一性,避免文件覆盖等问题
+ var save_filename = uniqid() + "." + ext;
+ // 拼接文件保存的完整路径,通过结合当前工作目录、配置中的上传目录(upload_config.get("upload_ueditor"))以及生成的文件名(save_filename)来确定最终保存位置
+ var savePath = path.join(process.cwd(), upload_config.get("upload_ueditor"), save_filename);
- file.pipe(fs.createWriteStream(savePath));
- });
- req.pipe(busboy);
- } else if(req.query.action === 'listimage') {
- fs.readdir(path.join(process.cwd(),upload_config.get("upload_ueditor")),function(err, files){
- if(err) return res.end();
- var total = files.length;
+ // 监听文件流(file)的'end'事件,当文件数据全部读取完毕后,该事件会被触发,在这里可以进行一些后续操作,比如组装返回给客户端的文件上传结果信息
+ file.on('end', function () {
+ // 组装文件上传成功后的结果对象,包含文件的访问URL、标题(可根据请求体中的pictitle字段获取,如果不存在则使用原始文件名)、原始文件名以及表示上传状态为成功的标志
+ var result = {
+ 'url': upload_config.get("baseURL") + "/" + upload_config.get("upload_ueditor") + "/" + save_filename,
+ 'title': req.body && req.body.pictitle || filename,
+ 'original': filename,
+ 'state': 'SUCCESS'
+ };
+ // 判断请求中是否包含encode参数,如果有,则使用res.jsonp方法将结果对象以JSONP的形式返回给客户端,同样是考虑到跨域等情况的一种数据返回方式
+ if (req.query.encode) {
+ res.jsonp(result);
+ } else {
+ // 如果没有encode参数,使用res.redirect方法进行重定向,重定向的URL由配置中的简单上传重定向地址(upload_config.get("simple_upload_redirect"))和文件上传结果信息(经过JSON.stringify转换为字符串形式)拼接而成,这样客户端可以根据重定向后的地址获取到上传结果信息
+ // 另一个被注释掉的res.redirect(result.url),原本可能是直接重定向到文件的访问URL,但这里采用了传递结果信息的重定向方式
+ res.redirect(upload_config.get("simple_upload_redirect") + "?result=" + JSON.stringify(result));
+ }
+ });
- var filelist = [];
- var total = 0;
- _(files).forEach(function(file){
- var fileExtArray = file.split(".");
- var ext = fileExtArray[fileExtArray.length - 1];
- if (filetype.indexOf(ext.toLowerCase()) >= 0) {
- var result_file = {};
- result_file.url = upload_config.get("baseURL")+"/" + upload_config.get("upload_ueditor") + "/" + file;
- filelist.push(result_file);
- total ++;
- }
- });
- res.jsonp({
- "state": "SUCCESS",
- "list": filelist,
- "start": 1,
- "total": total
- });
- })
- }
+ // 将文件流(file)通过管道(pipe)写入到创建的可写文件流(fs.createWriteStream(savePath))中,实现将上传的文件保存到指定的文件路径下
+ file.pipe(fs.createWriteStream(savePath));
+ });
+
+ // 将请求对象(req)通过管道(pipe)传入到Busboy实例中,触发Busboy开始解析请求中的文件数据,从而进入上述的文件上传处理流程
+ req.pipe(busboy);
+ } else if (req.query.action === 'listimage') {
+ // 使用文件系统模块(fs)的readdir方法读取指定目录下的所有文件,目录路径通过结合当前工作目录和配置中的上传目录(upload_config.get("upload_ueditor"))来确定,读取完成后会调用回调函数处理结果(err表示读取过程中是否出现错误,files表示读取到的文件列表)
+ fs.readdir(path.join(process.cwd(), upload_config.get("upload_ueditor")), function (err, files) {
+ // 如果读取过程中出现错误,直接结束响应,不返回任何数据给客户端(这里可以考虑返回更合适的错误信息给客户端)
+ if (err) return res.end();
+ // 初始化一个变量用于统计符合条件的文件总数,初始值为0
+ var total = 0;
+ // 创建一个空数组,用于存放符合条件的文件信息,后续会将满足文件类型要求的文件相关信息添加到这个数组中
+ var filelist = [];
+
+ // 使用Lodash的forEach方法遍历读取到的所有文件,对每个文件进行相关处理
+ _(files).forEach(function (file) {
+ // 将文件名按"."进行分割,获取文件扩展名,用于后续判断文件类型是否符合要求
+ var fileExtArray = file.split(".");
+ var ext = fileExtArray[fileExtArray.length - 1];
+ // 判断文件扩展名是否在允许的文件类型列表(filetype)中(将扩展名转换为小写后进行判断),如果是,则表示该文件符合要求,进行相应的信息组装和添加到文件列表操作
+ if (filetype.indexOf(ext.toLowerCase()) >= 0) {
+ var result_file = {};
+ // 组装文件的访问URL,格式与前面文件上传成功后的URL格式类似,通过配置中的基础URL、上传目录以及文件名来确定
+ result_file.url = upload_config.get("baseURL") + "/" + upload_config.get("upload_ueditor") + "/" + file;
+ // 将组装好的文件信息对象添加到文件列表数组中
+ filelist.push(result_file);
+ // 符合条件的文件总数加1
+ total++;
+ }
+ });
+
+ // 使用res.jsonp方法将包含文件列表信息、状态(SUCCESS表示成功获取列表)、起始索引(这里固定为1)以及文件总数的结果对象返回给客户端,同样采用JSONP的方式考虑跨域等情况进行数据传输
+ res.jsonp({
+ "state": "SUCCESS",
+ "list": filelist,
+ "start": 1,
+ "total": total
+ });
+ })
+ }
}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/map/map.html b/public2/ueditor/dialogs/map/map.html
index e763b8e..ab3e489 100644
--- a/public2/ueditor/dialogs/map/map.html
+++ b/public2/ueditor/dialogs/map/map.html
@@ -2,133 +2,193 @@
+
+
+
+
-
-
-
+
+
+ editor.execCommand('inserthtml', '

');
+ }
+ };
+ document.getElementById("address").focus();
+
diff --git a/public2/ueditor/dialogs/map/show.html b/public2/ueditor/dialogs/map/show.html
index 329cfeb..9eff473 100644
--- a/public2/ueditor/dialogs/map/show.html
+++ b/public2/ueditor/dialogs/map/show.html
@@ -1,11 +1,15 @@
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-
-
+
+
+
+
+
+
百度地图API自定义地图
+
+
-
-
+
+
+
+
diff --git a/public2/ueditor/dialogs/music/music.css b/public2/ueditor/dialogs/music/music.css
index 8fb7a94..5b0f28a 100644
--- a/public2/ueditor/dialogs/music/music.css
+++ b/public2/ueditor/dialogs/music/music.css
@@ -1,30 +1,190 @@
-.wrapper{margin: 5px 10px;}
+/* 定义类名为.wrapper 的元素样式 */
+.wrapper{
+ margin: 5px 10px;
+ /* 设置元素的外边距,上下外边距为 5 像素,左右外边距为 10 像素,用于控制该元素与周围元素在页面中的间隔距离 */
+}
-.searchBar{height:30px;padding:7px 0 3px;text-align:center;}
-.searchBtn{font-size:13px;height:24px;}
+/* 定义类名为.searchBar 的元素样式 */
+.searchBar{
+ height: 30px;
+ padding: 7px 0 3px;
+ text-align: center;
+ /* 设置元素的高度为 30 像素,内边距方面,上方内边距为 7 像素,下方内边距为 3 像素,左右内边距为 0 像素,使元素内部内容在垂直方向上有一定间隔;同时将文本水平居中对齐,常用于包含搜索相关输入框、按钮等元素的容器,使其内部元素布局更规整 */
+}
-.resultBar{width:460px;margin:5px auto;border: 1px solid #CCC;border-radius: 5px;box-shadow: 2px 2px 5px #D3D6DA;overflow: hidden;}
+/* 定义类名为.searchBtn 的元素样式 */
+.searchBtn{
+ font-size: 13px;
+ height: 24px;
+ /* 设置元素的字体大小为 13 像素,高度为 24 像素,可用于按钮等交互元素,使其呈现出合适的尺寸和文字显示大小,方便用户操作与查看 */
+}
-.listPanel{overflow: hidden;}
-.panelon{display:block;}
-.paneloff{display:none}
+/* 定义类名为.resultBar 的元素样式 */
+.resultBar{
+ width: 460px;
+ margin: 5px auto;
+ border: 1px solid #CCC;
+ border-radius: 5px;
+ box-shadow: 2px 2px 5px #D3D6DA;
+ overflow: hidden;
+ /* 设置元素的宽度为 460 像素,使其具有固定宽度;外边距方面,上下外边距为 5 像素,左右自动(auto)居中,让该元素在页面中水平居中显示;边框为 1 像素宽的浅灰色(#CCC)实线;添加 5 像素的圆角效果(border-radius),使边框角变得圆润,提升外观美感;添加阴影效果(box-shadow),营造出立体层次感;同时设置溢出内容隐藏(overflow: hidden),防止内部元素超出该容器范围显示,常用于展示搜索结果等内容的容器样式设置 */
+}
-.page{width:220px;margin:20px auto;overflow: hidden;}
-.pageon{float:right;width:24px;line-height:24px;height:24px;margin-right: 5px;background: none;border: none;color: #000;font-weight: bold;text-align:center}
-.pageoff{float:right;width:24px;line-height:24px;height:24px;cursor:pointer;background-color: #fff;
- border: 1px solid #E7ECF0;color: #2D64B3;margin-right: 5px;text-decoration: none;text-align:center;}
+/* 定义类名为.listPanel 的元素样式 */
+.listPanel{
+ overflow: hidden;
+ /* 设置元素的溢出内容隐藏,通常用于包含多个子元素且希望控制子元素显示范围,避免出现滚动条或者超出部分不显示等布局情况,常作为列表展示相关的父容器样式 */
+}
-.m-box{width:460px;}
-.m-m{float: left;line-height: 20px;height: 20px;}
-.m-h{height:24px;line-height:24px;padding-left: 46px;background-color:#FAFAFA;border-bottom: 1px solid #DAD8D8;font-weight: bold;font-size: 12px;color: #333;}
-.m-l{float:left;width:40px; }
-.m-t{float:left;width:140px;}
-.m-s{float:left;width:110px;}
-.m-z{float:left;width:100px;}
-.m-try-t{float: left;width: 60px;;}
+/* 定义类名为.panelon 的元素样式 */
+.panelon{
+ display: block;
+ /* 将元素设置为块级元素显示,会独占一行,常用于控制元素的显示与隐藏逻辑,当需要显示某个面板或模块时应用此样式 */
+}
-.m-try{float:left;width:20px;height:20px;background:url('http://static.tieba.baidu.com/tb/editor/images/try_music.gif') no-repeat ;}
-.m-trying{float:left;width:20px;height:20px;background:url('http://static.tieba.baidu.com/tb/editor/images/stop_music.gif') no-repeat ;}
+/* 定义类名为.paneloff 的元素样式 */
+.paneloff{
+ display: none;
+ /* 将元素设置为隐藏状态,不显示在页面上,同样用于元素的显示与隐藏逻辑控制,比如隐藏不需要展示的面板或模块时应用此样式 */
+}
-.loading{width:95px;height:7px;font-size:7px;margin:60px auto;background:url(http://static.tieba.baidu.com/tb/editor/images/loading.gif) no-repeat}
-.empty{width:300px;height:40px;padding:2px;margin:50px auto;line-height:40px; color:#006699;text-align:center;}
\ No newline at end of file
+/* 定义类名为.page 的元素样式 */
+.page{
+ width: 220px;
+ margin: 20px auto;
+ overflow: hidden;
+ /* 设置元素的宽度为 220 像素,外边距上下为 20 像素且左右自动(auto)居中,使其在页面中水平居中显示;同样设置溢出内容隐藏,可用于分页相关的导航元素容器样式,控制内部分页链接等元素的布局范围 */
+}
+
+/* 定义类名为.pageon 的元素样式 */
+.pageon{
+ float: right;
+ width: 24px;
+ line-height: 24px;
+ height: 24px;
+ margin-right: 5px;
+ background: none;
+ border: none;
+ color: #000;
+ font-weight: bold;
+ text-align: center;
+ /* 将元素设置为右浮动(float: right),使其在父容器内靠右排列;宽度为 24 像素,高度为 24 像素,行高也为 24 像素,使文本在元素内垂直居中;右边距为 5 像素,与其他相邻元素隔开一定距离;背景设置为无(none),边框设置为无(none),文本颜色为黑色(#000),字体加粗(font-weight: bold),文本水平居中对齐(text-align: center),这种样式可能用于表示当前选中的分页链接等元素,使其在页面上突出显示 */
+}
+
+/* 定义类名为.pageoff 的元素样式 */
+.pageoff{
+ float: right;
+ width: 24px;
+ line-height: 24px;
+ height: 24px;
+ cursor: pointer;
+ background-color: #fff;
+ border: 1px solid #E7ECF0;
+ color: #2D64B3;
+ margin-right: 5px;
+ text-decoration: none;
+ text-align: center;
+ /* 同样设置为右浮动,尺寸方面与.pageon 类似,设置宽度、高度和行高都为 24 像素;鼠标指针悬停时变为手型(cursor: pointer),表示可点击交互;背景颜色为白色(#fff),边框为 1 像素宽的淡蓝色(#E7ECF0)实线;文本颜色为浅蓝色(#2D64B3),右边距为 5 像素,去除文本的下划线装饰(text-decoration: none),文本水平居中对齐,这种样式可能用于表示未选中的分页链接等可点击元素,通过不同的样式与.pageon 区分开来,便于用户识别和操作 */
+}
+
+/* 定义类名为.m-box 的元素样式 */
+.m-box{
+ width: 460px;
+ /* 设置元素的宽度为 460 像素,可作为一个整体的容器,控制内部相关元素的布局宽度范围 */
+}
+
+/* 定义类名为.m-m 的元素样式 */
+.m-m{
+ float: left;
+ line-height: 20px;
+ height: 20px;
+ /* 将元素设置为左浮动(float: left),使其在父容器内靠左排列;行高为 20 像素,高度也为 20 像素,可用于设置文本等内容在垂直方向上的对齐和显示高度,常用于包含多个并列信息的布局场景,比如列表项中的不同字段内容展示 */
+}
+
+/* 定义类名为.m-h 的元素样式 */
+.m-h{
+ height: 24px;
+ line-height: 24px;
+ padding-left: 46px;
+ background-color: #FAFAFA;
+ border-bottom: 1px solid #DAD8D8;
+ font-weight: bold;
+ font-size: 12px;
+ color: #333;
+ /* 设置元素的高度为 24 像素,行高与高度相同,使文本垂直居中;左边内边距为 46 像素,增加左侧空白间隔;背景颜色为浅灰色(#FAFAFA),底部添加 1 像素宽的稍深一点的灰色(#DAD8D8)边框,用于区分不同部分;字体加粗,字体大小为 12 像素,文本颜色为深灰色(#333),这种样式可能用于标题栏等元素,使其在页面上突出显示且与其他内容区分开来 */
+}
+
+/* 定义类名为.m-l 的元素样式 */
+.m-l{
+ float: left;
+ width: 40px;
+ /* 将元素设置为左浮动,宽度设置为 40 像素,常用于在一行内划分出固定宽度的区域,放置相应的内容,比如列表项中的某个字段内容展示 */
+}
+
+/* 定义类名为.m-t 的元素样式 */
+.m-t{
+ float: left;
+ width: 140px;
+ /* 将元素设置为左浮动,宽度设置为 140 像素,同样用于在一行内划分出特定宽度区域来展示相关内容,与其他浮动元素一起实现多列信息的布局展示 */
+}
+
+/* 定义类名为.m-s 的元素样式 */
+.m-s{
+ float: left;
+ width: 110px;
+ /* 将元素设置为左浮动,宽度设置为 110 像素,也是用于布局多列信息,按照设定的宽度分配空间展示不同内容 */
+}
+
+/* 定义类名为.m-z 的元素样式 */
+.m-z{
+ float: left;
+ width: 100px;
+ /* 将元素设置为左浮动,宽度设置为 100 像素,与其他浮动元素配合实现一行内多列信息展示的布局效果 */
+}
+
+/* 定义类名为.m-try-t 的元素样式 */
+.m-try-t{
+ float: left;
+ width: 60px;
+ /* 将元素设置为左浮动,宽度设置为 60 像素,可用于在布局中划分出特定宽度区域来放置相应的元素或展示内容 */
+}
+
+/* 定义类名为.m-try 的元素样式 */
+.m-try{
+ float: left;
+ width: 20px;
+ height: 20px;
+ background: url('http://static.tieba.baidu.com/tb/editor/images/try_music.gif') no-repeat ;
+ /* 将元素设置为左浮动,宽度为 20 像素,高度为 20 像素,并设置背景图片,图片不重复平铺(no-repeat),该样式可能用于展示一个特定的音乐播放相关的图标等元素,通过背景图片呈现出可视化的效果 */
+}
+
+/* 定义类名为.m-trying 的元素样式 */
+.m-trying{
+ float: left;
+ width: 20px;
+ height: 20px;
+ background: url('http://static.tieba.baidu.com/tb/editor/images/stop_music.gif') no-repeat ;
+ /* 同样设置为左浮动,尺寸与.m-try 相同,也是通过设置不同的背景图片(这里是停止音乐相关的图标图片)且不重复平铺,用于呈现另一种音乐相关操作的可视化图标元素,与.m-try 样式配合实现音乐播放相关的交互界面展示 */
+}
+
+/* 定义类名为.loading 的元素样式 */
+.loading{
+ width: 95px;
+ height: 7px;
+ font-size: 7px;
+ margin: 60px auto;
+ background: url(http://static.tieba.baidu.com/tb/editor/images/loading.gif) no-repeat;
+ /* 设置元素的宽度为 95 像素,高度为 7 像素,字体大小为 7 像素,外边距上下为 60 像素且左右自动(auto)居中,使其在页面中水平居中显示;通过设置背景图片(loading 相关的动图,通常用于表示加载状态)且不重复平铺,用于展示加载中的可视化效果,提示用户当前正在进行数据加载等操作 */
+}
+
+/* 定义类名为.empty 的元素样式 */
+.empty{
+ width: 300px;
+ height: 40px;
+ padding: 2px;
+ margin: 50px auto;
+ line-height: 40px;
+ color: #006699;
+ text-align: center;
+ /* 设置元素的宽度为 300 像素,高度为 40 像素,内边距为 2 像素,外边距上下为 50 像素且左右自动(auto)居中,使其在页面中水平居中显示;行高与高度相同,使文本垂直居中;文本颜色为浅蓝色(#006699),文本水平居中对齐,这种样式常用于当没有相关数据时,展示一个提示信息的区域,告知用户当前无数据等情况 */
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/music/music.html b/public2/ueditor/dialogs/music/music.html
index e7ef04f..ae18602 100644
--- a/public2/ueditor/dialogs/music/music.html
+++ b/public2/ueditor/dialogs/music/music.html
@@ -2,31 +2,50 @@
+
插入音乐
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/music/music.js b/public2/ueditor/dialogs/music/music.js
index 1c538bf..44231be 100644
--- a/public2/ueditor/dialogs/music/music.js
+++ b/public2/ueditor/dialogs/music/music.js
@@ -1,52 +1,74 @@
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",
+ 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 () {
+ 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) {
+
+ 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 () {
+
+ dosearch: function () {
var me = this;
selectedItem = null;
var key = $G('J_searchName').value;
- if (utils.trim(key) == "")return false;
+ 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) {
+
+ 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) {
+
+ onpageclick: function (id) {
var me = this;
for (var i = 0; i < pages.length; i++) {
$G(pages[i]).className = 'pageoff';
@@ -54,8 +76,10 @@ function Music() {
}
$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) {
+
+ listenTest: function (elem) {
var me = this,
view = $G('J_preview'),
is_play_action = (elem.className == 'm-try'),
@@ -69,31 +93,39 @@ function Music() {
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) {
+
+ _sent: function (param) {
var me = this;
$G('J_resultBar').innerHTML = '
';
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"
+ 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) {
+
+ _removeHtml: function (str) {
var reg = /<\s*\/?\s*[^>]*\s*>/gi;
return str.replace(reg, "");
+ // 定义 '_removeHtml' 方法,用于去除字符串中的 HTML 标签。通过创建一个正则表达式对象(reg),匹配以 '<' 开头,中间包含任意字符(除了 '>'),以 '>' 结尾的 HTML 标签(包括自闭和标签以及成对标签),不区分大小写(gi 修饰符),然后使用字符串的'replace' 方法将匹配到的 HTML 标签替换为空字符串,返回处理后的字符串,可用于清理歌曲信息等文本中的 HTML 标签,获取纯文本内容,比如歌曲标题、歌手名等文本信息的提取和清理。
},
- _getUrl:function (isTryListen) {
+
+ _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;
+ + '&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 () {
+
+ _getTryingElem: function () {
var s = $G('J_listPanel').getElementsByTagName('span');
for (var i = 0; i < s.length; i++) {
@@ -101,17 +133,22 @@ function Music() {
return s[i];
}
return null;
+ // 定义 '_getTryingElem' 方法,用于查找页面中正在试听的元素(通过样式类'm-trying' 判断)。首先通过 '$G' 函数获取页面中 id 为 'J_listPanel' 的元素,并获取其内部所有的'span' 标签元素,存储在's' 数组中。然后循环遍历这个数组,判断每个'span' 元素的 'className' 是否为'm-trying',如果找到符合条件的元素,则返回该元素,表示找到了正在试听的元素;如果循环结束都没有找到,则返回 null,说明当前没有正在试听的元素。
},
- _buildMusicHtml:function (playerUrl) {
+
+ _buildMusicHtml: function (playerUrl) {
var html = '