元素($li)中查找类名为'progress'的 元素内部的 元素,用于后续操作该进度条相关的 元素(比如更新进度条的显示等),并将找到的元素赋值给变量$prgress
+ $prgress = $li.find('p.progress span'),
+ // 通过jQuery选择器在创建的 元素($li)中查找类名为'imgWrap'的 元素,用于后续可能在该元素内添加文件的缩略图等操作,将找到的元素赋值给变量$wrap
+ $wrap = $li.find('p.imgWrap'),
+ // 创建一个用于展示文件错误信息的
元素,设置其类名为'error',初始设置为隐藏状态(调用hide方法),然后将其添加到之前创建的
元素($li)中,当文件出现错误时可以在该元素内显示相应的错误提示信息
+ $info = $('
').hide().appendTo($li),
+
+ // 定义一个名为showError的内部函数,用于根据传入的错误代码来显示相应的错误提示文本,通过判断不同的错误代码来设置要显示的具体文本内容(从预先定义好的国际化文本变量中获取相应的提示信息),并将文本显示在之前创建的错误信息元素($info)中(调用show方法使其显示出来)
+ showError = function (code) {
+ switch (code) {
+ case 'exceed_size':
+ text = lang.errorExceedSize;
+ break;
+ case 'interrupt':
+ text = lang.errorInterrupt;
+ break;
+ case 'http':
+ text = lang.errorHttp;
+ break;
+ case 'not_allow_type':
+ text = lang.errorFileType;
+ break;
+ default:
+ text = lang.errorUploadRetry;
+ break;
+ }
+ $info.text(text).show();
+ };
+
+ // 判断文件的状态是否为'invalid'(无效状态,可能表示文件不符合某些上传要求等情况)
+if (file.getStatus() === 'invalid') {
+ // 如果文件状态为'invalid',调用showError函数(之前定义的用于显示错误信息的函数),传入文件的statusText(应该是包含具体错误原因等的文本信息),以在页面上展示相应的错误提示给用户
+ showError(file.statusText);
+} else {
+ // 如果文件状态不是'invalid',则在用于展示文件缩略图等的元素($wrap)内设置文本内容为lang.uploadPreview(预先定义好的国际化文本变量,用于提示用户正在预览文件之类的信息)
+ $wrap.text(lang.uploadPreview);
+ // 检查文件的扩展名(file.ext),将其转换为小写形式后,判断是否在指定的图片文件扩展名列表('|png|jpg|jpeg|bmp|gif|')中,如果不在(indexOf方法返回 -1表示未找到),说明不是常见的图片文件类型
+ if ('|png|jpg|jpeg|bmp|gif|'.indexOf('|'+file.ext.toLowerCase()+'|') == -1) {
+ // 如果不是常见图片文件类型,先清空$wrap元素内部的内容(empty方法),然后添加类名'notimage'(可能用于后续设置样式区分非图片文件),接着在该元素内添加一个 元素(用于展示对应文件类型的图标,通过设置类名来匹配相应的图标样式)以及一个 元素(用于展示文件名称,并设置title属性同样为文件名称,方便鼠标悬停提示完整文件名)
+ $wrap.empty().addClass('notimage').append(' ' +
+ '' + file.name + ' ');
+ } else {
+ // 如果是常见的图片文件类型,判断当前浏览器是否为IE浏览器(browser.ie为真表示是IE浏览器)并且IE浏览器的版本是否小于等于7(browser.version <= 7)
+ if (browser.ie && browser.version <= 7) {
+ // 如果是低版本IE浏览器(IE7及以下),则在用于展示文件缩略图等的元素($wrap)内设置文本内容为lang.uploadNoPreview(预先定义好的国际化文本变量,可能表示该浏览器无法预览此文件之类的信息)
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ // 如果不是低版本IE浏览器,调用uploader对象(WebUploader实例)的makeThumb方法,用于生成文件的缩略图
+ // 传入文件对象(file)、一个回调函数(用于处理生成缩略图的结果,成功时获取到缩略图的源地址等信息,失败时进行相应错误处理)以及缩略图的宽度(thumbnailWidth)和高度(thumbnailHeight)参数
+ uploader.makeThumb(file, function (error, src) {
+ // 在回调函数内部,判断是否有错误(error为真)或者生成的缩略图源地址(src)为空,说明生成缩略图失败了
+ if (error ||!src) {
+ // 如果生成缩略图失败,就在用于展示文件缩略图等的元素($wrap)内设置文本内容为lang.uploadNoPreview(提示无法预览文件)
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ // 如果生成缩略图成功,创建一个 元素,设置其src属性为生成的缩略图源地址(src)
+ var $img = $(' ');
+ // 先清空$wrap元素内部的内容(empty方法),然后将创建好的 元素添加到$wrap元素内,用于在页面上展示文件的缩略图
+ $wrap.empty().append($img);
+ // 为添加的 元素绑定error事件处理函数,当图片加载出现错误(比如图片地址无效、网络问题等导致无法显示图片)时触发该函数
+ $img.on('error', function () {
+ // 在图片加载出错时,同样在用于展示文件缩略图等的元素($wrap)内设置文本内容为lang.uploadNoPreview(提示无法预览文件)
+ $wrap.text(lang.uploadNoPreview);
+ });
+ }
+ }, thumbnailWidth, thumbnailHeight);
+ }
+ }
+ // 创建一个数组,将文件的大小(file.size)和初始进度值0作为元素放入数组中,然后以文件的id为键,将这个数组存储到percentages对象中,用于记录每个文件的大小以及后续更新其上传进度情况
+ percentages[ file.id ] = [ file.size, 0 ];
+ // 初始化文件的旋转角度为0,可能用于后续支持文件旋转相关的操作(比如图片旋转等功能)
+ file.rotation = 0;
+
+ /* 检查文件格式 */
+ // 判断文件的扩展名(file.ext)是否为空或者文件的扩展名(转换为小写形式后)在允许上传的文件扩展名列表(acceptExtensions)中是否不存在(indexOf方法返回 -1表示不存在),也就是检查文件格式是否符合允许上传的要求
+ if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) {
+ // 如果文件格式不符合要求,调用showError函数,传入'not_allow_type'(表示文件类型不允许上传的错误代码),以显示相应的错误提示信息,告知用户文件类型不被允许上传
+ showError('not_allow_type');
+ // 调用uploader对象(WebUploader实例)的removeFile方法,将不符合文件格式要求的文件从上传队列中移除,避免上传不符合规定的文件
+ uploader.removeFile(file);
+ }
+}
+
+// 为文件对象绑定'statuschange'事件处理函数,当文件的状态发生改变时会触发该函数,传入当前状态(cur)和之前的状态(prev)作为参数,用于根据不同的状态变化执行相应的操作
+file.on('statuschange', function (cur, prev) {
+ // 如果文件之前的状态(prev)是'progress'(表示正在上传进度中)
+ if (prev === 'progress') {
+ // 隐藏用于展示文件上传进度的元素($prgress),并将其宽度设置为0,可能用于重置进度条的显示,比如上传中断等情况需要重新展示进度时先进行这样的重置操作
+ $prgress.hide().width(0);
+ } else if (prev === 'queued') {
+ // 如果文件之前的状态是'queued'(表示已添加到上传队列中等待上传),则移除为 元素($li,代表文件对应的整个展示元素)绑定的鼠标进入(mouseenter)和鼠标离开(mouseleave)事件处理函数(off方法用于移除事件绑定),同时移除文件操作按钮所在的 元素($btns),可能是在文件进入下一个阶段(比如开始上传等)时不需要这些交互元素或者需要重新绑定不同的交互逻辑了
+ $li.off('mouseenter mouseleave');
+ $btns.remove();
+ }
+ // 如果文件当前状态(cur)是'error'(表示出现错误)或者'invalid'(无效状态)
+ if (cur === 'error' || cur === 'invalid') {
+ // 调用showError函数,传入文件的statusText(包含具体错误原因等的文本信息),以在页面上展示相应的错误提示给用户,告知文件出现错误的情况
+ showError(file.statusText);
+ // 将文件在percentages对象中记录的进度值(数组中的第二个元素)设置为1,表示出现错误后认为文件上传已完成(但实际上是失败了),可能用于更新界面上进度相关的显示等情况
+ percentages[ file.id ][ 1 ] = 1;
+ } else if (cur === 'interrupt') {
+ // 如果文件当前状态是'interrupt'(表示上传被中断),调用showError函数,传入'interrupt'(表示上传中断的错误代码),以显示相应的错误提示信息,告知用户文件上传被中断了
+ showError('interrupt');
+ } else if (cur === 'queued') {
+ // 如果文件当前状态是'queued'(表示重新回到已添加到上传队列等待上传的状态,可能是之前出现问题后重新排队等情况),将文件在percentages对象中记录的进度值(数组中的第二个元素)设置为0,重置文件的上传进度为初始的等待上传状态,用于更新界面上进度相关的显示等情况
+ percentages[ file.id ][ 1 ] = 0;
+ } else if (cur === 'progress') {
+ // 如果文件当前状态是'progress'(表示正在上传进度中),隐藏之前创建的用于展示文件错误信息的元素($info,可能在上传过程中之前出现的错误提示等不需要显示了),并将用于展示文件上传进度的元素($prgress)的display样式属性设置为'block',使其显示出来,以展示当前文件正在上传的进度情况
+ $info.hide();
+ $prgress.css('display', 'block');
+ } else if (cur === 'complete') {
+ // 如果文件当前状态是'complete'(表示文件上传完成),这里暂时没有具体的操作,可能后续需要添加如更新界面显示完成状态、提示用户上传成功等相关操作逻辑,具体根据业务需求而定
+ }
+
+ // 根据文件状态变化,移除文件对应的
元素($li)上之前表示状态的类名(格式为'state-' + prev,prev是文件之前的状态),然后添加表示当前状态的类名(格式为'state-' + cur,cur是文件当前的状态),这样可以通过不同的类名来应用相应的样式,以体现文件在不同状态下的外观变化(例如不同状态下的颜色、图标显示等不同样式效果)
+$li.removeClass('state-' + prev).addClass('state-' + cur);
+// 为文件对应的 元素($li)绑定鼠标进入(mouseenter)事件处理函数,当鼠标移入该元素时触发此函数
+$li.on('mouseenter', function () {
+ // 当鼠标移入时,找到文件操作按钮所在的 元素($btns),先调用stop方法停止正在进行的动画(如果有的话,避免动画队列堆积等问题),然后调用animate方法触发一个动画效果,将该元素的高度从当前值渐变到30像素,实现鼠标移入时按钮区域展开显示的交互效果,方便用户操作按钮
+ $btns.stop().animate({height: 30});
+});
+// 为文件对应的
元素($li)绑定鼠标离开(mouseleave)事件处理函数,当鼠标移出该元素时触发此函数
+$li.on('mouseleave', function () {
+ // 当鼠标移出时,找到文件操作按钮所在的 元素($btns),同样先调用stop方法停止正在进行的动画,然后调用animate方法触发一个动画效果,将该元素的高度从当前值渐变到0像素,实现鼠标移出时按钮区域收缩隐藏的交互效果,保持页面的简洁性和美观性
+ $btns.stop().animate({height: 0});
+});
+
+// 为文件操作按钮所在的
元素($btns)绑定点击(click)事件处理函数,并且通过第二个参数'span'指定只监听
元素内部的
元素(也就是各个具体的操作按钮)的点击事件
+$btns.on('click', 'span', function () {
+ // 获取被点击的 元素(也就是具体的操作按钮)在其兄弟元素中的索引位置,用于区分不同的按钮,赋值给变量index,例如索引为0可能是删除按钮,索引为1可能是向右旋转按钮等
+ var index = $(this).index(),
+ deg;
+
+ // 根据按钮的索引(index)值进行不同的操作,通过switch语句来区分不同的按钮点击情况
+ switch (index) {
+ // 如果索引为0,说明点击的是第一个按钮(通常是删除按钮之类的功能)
+ case 0:
+ // 调用uploader对象(WebUploader实例)的removeFile方法,传入当前文件对象(file),将该文件从上传队列中移除,实现删除文件的功能
+ uploader.removeFile(file);
+ // 执行完删除操作后,直接返回,不再执行后续的代码,因为文件已经被删除,不需要再进行其他与该文件相关的操作了
+ return;
+ // 如果索引为1,说明点击的是第二个按钮(可能是向右旋转按钮之类的功能)
+ case 1:
+ // 将文件的旋转角度(file.rotation)增加90度,用于记录文件旋转的状态变化,后续可能根据这个角度来实际旋转文件的展示效果(比如图片旋转等)
+ file.rotation += 90;
+ break;
+ // 如果索引为2,说明点击的是第三个按钮(可能是向左旋转按钮之类的功能)
+ case 2:
+ // 将文件的旋转角度(file.rotation)减少90度,同样是用于记录文件旋转的状态变化,以便后续实现相应的展示效果调整
+ file.rotation -= 90;
+ break;
+ }
+
+ // 判断浏览器是否支持CSS过渡效果(supportTransition为真表示支持),如果支持
+ if (supportTransition) {
+ // 根据文件当前的旋转角度(file.rotation)构建一个CSS变换(transform)的旋转样式值,格式为'rotate(' + file.rotation + 'deg)',用于后续设置元素的旋转效果,赋值给变量deg
+ deg = 'rotate(' + file.rotation + 'deg)';
+ // 找到用于展示文件缩略图等的元素($wrap),通过css方法设置其多个浏览器前缀版本的CSS变换属性(-webkit-transform、-mos-transform、-o-transform以及标准的transform),都设置为刚刚构建的旋转样式值(deg),从而实现在支持CSS过渡效果的浏览器中通过CSS变换来旋转文件展示元素(比如图片),达到视觉上的旋转效果
+ $wrap.css({
+ '-webkit-transform': deg,
+ '-mos-transform': deg,
+ '-o-transform': deg,
+ 'transform': deg
+ });
+ } else {
+ // 如果浏览器不支持CSS过渡效果,通过设置IE浏览器的滤镜(filter)属性来实现文件展示元素($wrap)的旋转效果,使用特定的IE滤镜语法'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')',其中通过一些数学运算来将文件的旋转角度转换为IE滤镜能识别的旋转参数值,以实现在不支持CSS过渡效果的IE浏览器中也能进行文件展示元素的旋转操作(虽然这种方式相对较旧且兼容性有限,但用于应对旧版本IE浏览器的特殊情况)
+ $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
+ }
+
+});
+
+// 将文件对应的 元素($li)插入到id为$filePickerBlock的元素之前,这样可以调整文件展示元素在页面中的排列顺序,例如将新添加的文件展示元素显示在文件选择相关区域之前等,方便用户查看和操作文件相关信息
+$li.insertBefore($filePickerBlock);
+}
+
+// 定义一个名为removeFile的函数,用于销毁与文件相关的视图元素以及清理相关的数据等操作,也就是负责文件相关视图的销毁工作
+function removeFile(file) {
+ // 通过文件的id找到对应的 元素,赋值给变量$li,后续将基于这个元素进行相关的销毁和清理操作
+ var $li = $('#' + file.id);
+ // 从percentages对象中删除以该文件的id为键的数据,也就是移除记录该文件大小和进度等信息的数据项,因为文件要被销毁了,相关进度等信息不再需要保留
+ delete percentages[ file.id ];
+ // 调用updateTotalProgress函数(用于更新总体上传进度相关的显示等情况),在移除文件后及时更新整体的进度信息,确保界面上显示的进度情况是准确的
+ updateTotalProgress();
+ // 对找到的 元素($li)进行一系列操作,先调用off方法移除该元素上绑定的所有事件处理函数(避免内存泄漏以及不必要的事件触发等问题),然后找到该元素内部的.file-panel类元素(也就是文件操作按钮所在的 元素),同样调用off方法移除其绑定的所有事件处理函数,最后调用end方法回到最初的
元素,并调用remove方法将该 元素从DOM树中移除,彻底销毁与该文件相关的视图元素
+ $li.off().find('.file-panel').off().end().remove();
+}
+
+// 定义一个名为updateTotalProgress的函数,用于更新文件上传的总体进度信息,并根据进度情况更新界面上相关元素的显示内容(如进度条的百分比显示等)
+function updateTotalProgress() {
+ // 初始化一个变量loaded,用于记录已经上传的文件大小总和,初始值设为0
+ var loaded = 0,
+ // 初始化一个变量total,用于记录所有文件的总大小,初始值设为0
+ total = 0,
+ // 通过jQuery选择器找到总体进度条($progress)元素内部的所有子元素(可能是用于展示进度文本和进度条宽度的元素等),赋值给变量spans,方便后续操作这些子元素来更新进度显示
+ spans = $progress.children(),
+ percent;
+
+ // 使用jQuery的each方法遍历percentages对象(记录每个文件的大小和进度信息的对象),对于每个键值对(文件id作为键k,文件大小和进度数组作为值v)进行操作
+ $.each(percentages, function (k, v) {
+ // 将当前文件的大小(v[0])累加到total变量中,用于计算所有文件的总大小
+ total += v[ 0 ];
+ // 根据当前文件的大小(v[0])和当前文件的上传进度(v[1])计算已经上传的该文件的大小部分(即文件大小乘以进度百分比),并累加到loaded变量中,用于计算所有文件已经上传的总大小
+ loaded += v[ 0 ] * v[ 1 ];
+ });
+
+ // 计算总体上传进度百分比,通过判断总大小(total)是否为0来避免除数为0的情况,如果总大小不为0,则用已经上传的总大小(loaded)除以总大小(total)得到进度百分比,否则进度百分比设为0
+ percent = total? loaded / total : 0;
+
+ // 找到总体进度条元素($progress)的第一个子元素(可能是用于展示进度百分比文本的元素),调用text方法将计算得到的进度百分比转换为整数并拼接上 '%' 符号后设置为其文本内容,用于在界面上显示总体上传进度的百分比数值
+ spans.eq(0).text(Math.round(percent * 100) + '%');
+ // 找到总体进度条元素($progress)的第二个子元素(可能是用于展示进度条实际宽度的元素),调用css方法设置其宽度样式属性,将计算得到的进度百分比转换为整数并拼接上 '%' 符号后设置为宽度值,以通过改变宽度来直观展示总体上传进度的情况(例如进度条的填充长度等视觉效果)
+ spans.eq(1).css('width', Math.round(percent * 100) + '%');
+ // 调用updateStatus函数(应该是用于更新文件上传相关的其他状态显示等情况的函数,虽然此处未展示其具体代码,但从函数名推测其功能),在更新完进度信息后,进一步更新其他相关的状态显示内容,确保界面上展示的文件上传状态是完整且准确的
+ updateStatus();
+}
+
+ // 定义一个名为setState的函数,用于设置文件上传过程中的状态,并根据不同状态进行相应的界面元素显示、隐藏以及样式调整等操作,接收两个参数,val表示要设置的目标状态值,files参数(此处从代码来看暂未明确具体使用方式,但可能与文件相关,比如特定的文件列表等)
+function setState(val, files) {
+
+ // 判断传入的要设置的目标状态值(val)与当前的状态(state,应该是在函数外部定义的用于记录当前文件上传状态的变量)是否不相等,如果不相等,说明状态发生了变化,需要执行相应的状态更新操作
+ if (val!= state) {
+
+ // 调用uploader对象(WebUploader实例)的getStats方法,获取文件上传的统计信息(比如已上传成功的文件数量、失败的文件数量等各种状态相关的数据),并将结果赋值给变量stats,用于后续根据不同状态判断并进行相应的界面操作
+ var stats = uploader.getStats();
+
+ // 移除上传按钮($upload)上表示当前状态的类名(格式为'state-' + state,state是之前的状态),用于去除之前状态对应的样式,以便后续添加新状态对应的样式
+ $upload.removeClass('state-' + state);
+ // 为上传按钮($upload)添加表示目标状态(val)的类名(格式为'state-' + val),通过添加不同的类名可以应用不同的样式(例如按钮的文本、颜色、可操作性等样式变化)来体现当前文件上传所处的不同状态
+ $upload.addClass('state-' + val);
+
+ // 根据传入的目标状态值(val)进行不同的操作,通过switch语句来区分不同的状态情况
+ switch (val) {
+
+ /* 未选择文件 */
+ case 'pedding':
+ // 为文件列表所在的元素($queue)添加类名'element-invisible',这个类名可能在CSS中定义了相应的样式用于隐藏元素(比如设置display:none或者visibility:hidden等),使文件列表在未选择文件时隐藏起来,符合此时的状态展示需求
+ $queue.addClass('element-invisible');
+ // 为状态栏元素($statusBar)添加类名'element-invisible',同样使其隐藏起来,因为在未选择文件时,状态栏相关的进度、信息等展示可能不需要显示
+ $statusBar.addClass('element-invisible');
+ // 移除占位元素($placeHolder)上的类名'element-invisible',也就是让占位元素显示出来,可能用于在未选择文件时展示一些提示用户选择文件之类的信息(例如“请选择要上传的文件”等提示语所在的元素)
+ $placeHolder.removeClass('element-invisible');
+ // 隐藏总体进度条元素($progress),因为在未选择文件状态下不需要展示进度相关信息,调用hide方法实现隐藏效果
+ $progress.hide();
+ // 隐藏文件总体选择信息元素($info),同样此时不需要展示相关的文件选择等信息,进行隐藏操作
+ $info.hide();
+ // 调用uploader对象(WebUploader实例)的refresh方法,可能用于刷新WebUploader内部的一些状态或者界面相关的显示情况,确保在状态切换时其内部状态与界面展示保持一致,具体功能取决于WebUploader的实现逻辑
+ uploader.refresh();
+ break;
+
+ /* 可以开始上传 */
+ case 'ready':
+ // 为占位元素($placeHolder)添加类名'element-invisible',使其隐藏起来,因为此时已经可以开始上传文件了,不需要再显示占位提示信息等内容
+ $placeHolder.addClass('element-invisible');
+ // 移除文件列表所在元素($queue)的类名'element-invisible',让文件列表显示出来,方便用户查看已经添加到上传队列中的文件信息
+ $queue.removeClass('element-invisible');
+ // 移除状态栏元素($statusBar)的类名'element-invisible',使状态栏显示出来,用于展示文件上传相关的进度、信息等内容
+ $statusBar.removeClass('element-invisible');
+ // 隐藏总体进度条元素($progress),因为此时虽然可以上传但还未真正开始上传,不需要展示进度情况,先隐藏起来
+ $progress.hide();
+ // 显示文件总体选择信息元素($info),用于展示如已选择文件的数量、大小等相关信息,让用户了解当前的文件选择情况
+ $info.show();
+ // 设置上传按钮($upload)的文本内容为lang.uploadStart(预先定义好的国际化文本变量,可能表示“开始上传”之类的提示语),提示用户可以点击按钮进行文件上传操作了
+ $upload.text(lang.uploadStart);
+ // 调用uploader对象(WebUploader实例)的refresh方法,刷新WebUploader内部状态及界面显示情况,确保在进入可上传状态时相关展示是准确的
+ uploader.refresh();
+ break;
+
+ /* 上传中 */
+ case 'uploading':
+ // 显示总体进度条元素($progress),因为文件正在上传,需要展示上传的进度情况给用户,调用show方法使其显示出来
+ $progress.show();
+ // 隐藏文件总体选择信息元素($info),在上传过程中可能不需要展示文件选择相关信息了,将其隐藏
+ $info.hide();
+ // 设置上传按钮($upload)的文本内容为lang.uploadPause(预先定义好的国际化文本变量,可能表示“暂停上传”之类的提示语),提示用户此时点击按钮可以暂停正在进行的文件上传操作
+ $upload.text(lang.uploadPause);
+ break;
+
+ /* 暂停上传 */
+ case 'paused':
+ // 显示总体进度条元素($progress),虽然上传暂停了,但仍可以展示之前已经上传的进度情况等信息,所以使其显示出来
+ $progress.show();
+ // 隐藏文件总体选择信息元素($info),同样在暂停状态下可能不需要展示该信息,进行隐藏操作
+ $info.hide();
+ // 设置上传按钮($upload)的文本内容为lang.uploadContinue(预先定义好的国际化文本变量,可能表示“继续上传”之类的提示语),提示用户点击按钮可以继续之前暂停的文件上传操作
+ $upload.text(lang.uploadContinue);
+ break;
+
+ case 'confirm':
+ // 显示总体进度条元素($progress),用于展示上传的进度情况,调用show方法使其显示出来
+ $progress.show();
+ // 隐藏文件总体选择信息元素($info),可能在这个确认状态下不需要展示该信息,进行隐藏操作
+ $progress.show(); $info.hide();
+ // 设置上传按钮($upload)的文本内容为lang.uploadStart(预先定义好的国际化文本变量,可能表示“开始上传”之类的提示语),此处设置这个文本可能是根据业务逻辑,在确认相关情况后可以再次开始上传之类的操作
+ $upload.text(lang.uploadStart);
+
+ // 再次获取文件上传的统计信息(stats),可能前面的操作过程中相关统计数据有变化,重新获取最新数据用于后续判断
+ stats = uploader.getStats();
+ // 判断如果已经上传成功的文件数量(stats.successNum)大于0且上传失败的文件数量(stats.uploadFailNum)为0,也就是所有文件都上传成功了的情况
+ if (stats.successNum &&!stats.uploadFailNum) {
+ // 调用setState函数,传入'finish'作为状态值,将文件上传状态设置为完成状态,进入完成状态相关的界面展示和操作逻辑,然后直接返回,不再执行后续switch语句中的其他代码,因为已经完成了状态的最终切换
+ setState('finish');
+ return;
+ }
+ break;
+
+ case 'finish':
+ // 隐藏总体进度条元素($progress),因为文件上传已经完成,不需要再展示进度信息了,调用hide方法进行隐藏操作
+ $progress.hide();
+ // 显示文件总体选择信息元素($info),可能用于展示一些如上传完成的提示信息或者最终的文件上传结果相关内容等,调用show方法使其显示出来
+ $info.show();
+ // 判断如果上传失败的文件数量(stats.uploadFailNum)大于0,也就是存在文件上传失败的情况
+ if (stats.uploadFailNum) {
+ // 设置上传按钮($upload)的文本内容为lang.uploadRetry(预先定义好的国际化文本变量,可能表示“重试上传”之类的提示语),提示用户可以点击按钮重新尝试上传失败的文件
+ $upload.text(lang.uploadRetry);
+ } else {
+ // 如果没有文件上传失败,设置上传按钮($upload)的文本内容为lang.uploadStart(预先定义好的国际化文本变量,可能表示“开始上传”之类的提示语),可能用于提示用户可以继续上传新的文件等操作,具体根据业务逻辑而定
+ $upload.text(lang.uploadStart);
+ }
+ break;
+ }
+
+ // 将当前的状态(state)更新为传入的目标状态值(val),确保记录的状态与实际设置的状态保持一致,方便后续其他函数根据这个状态变量进行相应的操作判断
+ state = val;
+ // 调用updateStatus函数(应该是用于更新文件上传相关的其他一些状态显示、界面调整等操作的函数,虽然此处未展示其具体代码,但从函数名推测其功能),在状态更新后进一步确保整个文件上传界面展示的状态是完整且准确的
+ updateStatus();
+
+ }
+// 判断当前实例对象(_this)通过调用getQueueCount方法获取的上传队列中的文件数量是否为0,如果是0,表示上传队列中没有文件
+if (!_this.getQueueCount()) {
+ // 如果上传队列中没有文件,为上传按钮($upload)添加类名'disabled',这个类名可能在CSS中定义了相应的样式(比如设置按钮为不可点击状态、改变按钮颜色等外观样式来表示不可用),用于提示用户当前无法进行上传操作,因为没有文件在队列中等待上传
+ $upload.addClass('disabled')
+} else {
+ // 如果上传队列中文件数量不为0,也就是有文件在队列中等待上传,移除上传按钮($upload)上的类名'disabled',使按钮恢复可操作的正常样式(例如可以点击进行上传等操作)
+ $upload.removeClass('disabled')
+}
+
+// 定义一个名为updateStatus的函数,用于更新文件上传相关的状态信息展示内容,比如在页面上的特定元素中显示不同状态下的提示文本,告知用户当前文件上传的进展、结果等情况
+function updateStatus() {
+ // 初始化一个变量text,用于存储要展示的状态信息文本内容,初始为空字符串,后续会根据不同的文件上传状态进行相应的文本拼接和替换操作来生成最终要展示的文本
+ var text = '', stats;
+
+ // 判断当前文件上传状态(state)是否为'ready'(表示可以开始上传的状态)
+ if (state === 'ready') {
+ // 如果是'ready'状态,使用lang.updateStatusReady(预先定义好的国际化文本变量,应该是包含占位符的文本模板,用于展示准备上传状态相关信息),通过replace方法将其中的'_'占位符替换为实际的已添加到队列中的文件数量(fileCount),再将'_KB'占位符替换为通过WebUploader.formatSize方法(应该是WebUploader提供的用于格式化文件大小显示格式的函数)格式化后的文件总大小(fileSize),最终得到符合当前状态且展示具体文件数量和总大小信息的文本内容,赋值给text变量
+ text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize));
+ } else if (state === 'confirm') {
+ // 如果当前文件上传状态是'confirm'(可能表示某种确认状态,例如确认上传结果等情况),先获取文件上传的统计信息(通过调用uploader.getStats方法),并将结果赋值给stats变量,用于后续判断上传成功和失败的文件数量等情况
+ stats = uploader.getStats();
+ // 判断如果上传失败的文件数量(stats.uploadFailNum)大于0,也就是存在文件上传失败的情况
+ if (stats.uploadFailNum) {
+ // 使用lang.updateStatusConfirm(同样是预先定义好的国际化文本变量,包含占位符的文本模板,用于展示确认状态相关信息),通过replace方法将其中的'_'占位符替换为实际的上传成功的文件数量(stats.successNum),这里替换了两次,可能根据具体文本模板的格式需求来准确展示相关信息,最终生成符合当前状态且体现上传成功文件数量的文本内容,赋值给text变量
+ text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum);
+ }
+ } else {
+ // 如果当前文件上传状态不是'ready'也不是'confirm',也就是其他状态情况(例如完成状态等),先获取文件上传的统计信息(调用uploader.getStats方法),并将结果赋值给stats变量
+ stats = uploader.getStats();
+ // 使用lang.updateStatusFinish(预先定义好的国际化文本变量,包含占位符的文本模板,用于展示完成状态相关信息),通过replace方法依次进行占位符替换操作:
+ // 将第一个'_'占位符替换为实际的已添加到队列中的文件数量(fileCount),
+ // 将'_KB'占位符替换为通过WebUploader.formatSize方法格式化后的文件总大小(fileSize),
+ // 将第二个'_'占位符替换为实际的上传成功的文件数量(stats.successNum),
+ // 经过这些替换操作后,生成符合当前状态且展示文件数量、总大小以及上传成功文件数量等信息的文本内容,赋值给text变量
+ text = lang.updateStatusFinish.replace('_', fileCount).
+ replace('_KB', WebUploader.formatSize(fileSize)).
+ replace('_', stats.successNum);
+
+ // 判断如果上传失败的文件数量(stats.uploadFailNum)大于0,也就是存在文件上传失败的情况
+ if (stats.uploadFailNum) {
+ // 将预先定义好的用于展示错误信息的国际化文本变量lang.updateStatusError(同样包含占位符的文本模板,用于在有文件上传失败时补充相关错误信息),通过replace方法将其中的'_'占位符替换为实际的上传失败的文件数量(stats.uploadFailNum),然后将生成的包含失败文件数量的错误提示文本追加到之前生成的text变量内容后面,用于完整展示包含上传成功和失败文件数量等详细情况的最终状态信息文本
+ text += lang.updateStatusError.replace('_', stats.uploadFailNum);
+ }
+ }
+
+ // 找到用于展示文件总体选择信息的元素($info),通过html方法将刚刚生成的text变量中的文本内容设置为该元素的HTML内容,从而在页面上相应位置展示出更新后的文件上传状态信息,让用户了解当前的上传情况
+ $info.html(text);
+}
+
+// 为uploader对象(WebUploader实例)绑定'fileQueued'事件处理函数,当有单个文件添加到上传队列时会触发该函数,传入添加的文件对象(file)作为参数,用于处理文件添加到队列后的相关操作,比如更新文件数量、文件总大小等信息以及创建文件对应的视图展示元素等
+uploader.on('fileQueued', function (file) {
+ // 判断文件的扩展名(file.ext)是否存在,并且文件的扩展名(转换为小写形式后)在允许上传的文件扩展名列表(acceptExtensions)中是否能找到(indexOf方法返回值不为 -1表示存在),同时判断文件大小(file.size)是否小于等于允许上传的单个文件最大大小限制(fileMaxSize),也就是检查文件是否符合上传要求
+ if (file.ext && acceptExtensions.indexOf(file.ext.toLowerCase())!= -1 && file.size <= fileMaxSize) {
+ // 如果文件符合上传要求,将记录已添加到队列中的文件数量的变量(fileCount)加1,用于统计上传队列中的文件数量变化情况
+ fileCount++;
+ // 将当前文件的大小(file.size)累加到记录文件总大小的变量(fileSize)中,用于实时更新所有已添加到队列中的文件的总大小情况
+ fileSize += file.size;
+ }
+
+ // 判断如果上传队列中的文件数量(fileCount)等于1,也就是刚刚添加了第一个符合要求的文件到队列中时
+ if (fileCount === 1) {
+ // 为占位元素($placeHolder,可能是在还未添加文件时显示提示用户选择文件等信息的元素)添加类名'element-invisible',使其隐藏起来,因为已经有文件添加到队列了,不需要再显示占位提示信息了
+ $placeHolder.addClass('element-invisible');
+ // 显示状态栏元素($statusBar,用于展示文件上传相关的进度、信息等内容),使其显示出来,方便用户查看文件上传相关的情况,因为有文件在队列中了,需要展示相应的状态信息了
+ $statusBar.show();
+ }
+
+ // 调用addFile函数(之前定义的用于创建与文件对应的视图展示元素等操作的函数),传入当前添加的文件对象(file),为该文件创建相应的展示元素(如文件名称、进度条、操作按钮等元素)并添加到页面中,方便用户查看和操作该文件
+ addFile(file);
+});
+
+// 为uploader对象(WebUploader实例)绑定'fileDequeued'事件处理函数,当有单个文件从上传队列中移除时会触发该函数,传入被移除的文件对象(file)作为参数,用于处理文件移除后的相关操作,比如更新文件数量、文件总大小等信息以及销毁文件对应的视图展示元素等
+uploader.on('fileDequeued', function (file) {
+ // 判断文件的扩展名(file.ext)是否存在,并且文件的扩展名(转换为小写形式后)在允许上传的文件扩展名列表(acceptExtensions)中是否能找到(indexOf方法返回值不为 -1表示存在),同时判断文件大小(file.size)是否小于等于允许上传的单个文件最大大小限制(fileMaxSize),也就是检查被移除的这个文件是否原本是符合上传要求的文件(进行相应的数量和大小统计调整时需要确认是符合要求的文件)
+ if (file.ext && acceptExtensions.indexOf(file.ext.toLowerCase())!= -1 && file.size <= fileMaxSize) {
+ // 如果被移除的文件符合上传要求,将记录已添加到队列中的文件数量的变量(fileCount)减1,用于统计上传队列中的文件数量变化情况,因为有文件被移除了
+ fileCount--;
+ // 将当前文件的大小(file.size)从记录文件总大小的变量(fileSize)中减去,用于实时更新所有已添加到队列中的文件的总大小情况,因为有文件的大小需要从总大小中扣除了
+ fileSize -= file.size;
+ }
+
+ // 调用removeFile函数(之前定义的用于销毁文件相关视图元素以及清理相关数据等操作的函数),传入当前被移除的文件对象(file),执行销毁文件对应展示元素、清理相关数据(如移除记录该文件进度等信息的数据项)等操作,确保页面展示和相关数据与实际队列情况一致
+ removeFile(file);
+ // 调用updateTotalProgress函数(之前定义的用于更新文件上传的总体进度信息以及相关界面显示的函数),在文件移除后及时更新整体的进度信息以及相应的界面展示情况,保证展示给用户的上传进度等信息是准确的
+ updateTotalProgress();
+});
+
+// 为uploader对象(WebUploader实例)绑定'filesQueued'事件处理函数,当有多个文件添加到上传队列时会触发该函数,传入添加的文件对象(file)作为参数(虽然这里参数名和单个文件添加时一样,但实际传入的可能是多个文件组成的集合之类的情况,具体取决于WebUploader的实现方式),用于处理多个文件添加到队列后的相关状态判断和操作,比如根据当前状态判断是否可以切换到可上传状态等情况
+uploader.on('filesQueued', function (file) {
+ // 判断uploader对象(WebUploader实例)当前是否正在进行上传操作(通过调用isInProgress方法判断,返回false表示不在进行中),并且当前文件上传状态(state)是'pedding'(未选择文件状态)、'finish'(完成状态)、'confirm'(确认状态)或者'ready'(可开始上传状态)中的任意一种情况,也就是判断在合适的状态下且当前没有正在上传时,有新文件添加进来的情况
+ if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) {
+ // 如果满足上述条件,调用setState函数,传入'ready'作为状态值,将文件上传状态设置为可以开始上传的状态,方便用户能及时进行上传操作,更新相关的界面元素显示和状态信息展示等情况
+ setState('ready');
+ }
+ // 调用updateTotalProgress函数,在多个文件添加到队列后及时更新文件上传的总体进度信息以及相应的界面展示情况,确保展示给用户的进度等信息是准确的,反映最新的文件队列情况
+ updateTotalProgress();
+});
+ // 为uploader对象(WebUploader实例)绑定'all'事件处理函数,'all'事件会在多种不同的上传相关事件触发时都会被调用,传入事件类型(type)和相关文件对象(files,可能是单个文件对象或者多个文件对象组成的集合,取决于具体触发的事件情况)作为参数,用于根据不同的具体事件类型执行相应的操作
+uploader.on('all', function (type, files) {
+ // 根据传入的事件类型(type)进行不同的操作,通过switch语句来区分不同的事件情况
+ switch (type) {
+ // 如果事件类型是'uploadFinished',表示文件上传已经全部完成(所有文件都完成了上传操作)
+ case 'uploadFinished':
+ // 调用setState函数,传入'confirm'作为状态值,同时传入相关文件对象(files),将文件上传状态设置为确认状态(可能用于后续确认上传结果、进行相关统计等操作),并根据这个状态切换来更新界面元素显示、状态信息展示等情况
+ setState('confirm', files);
+ break;
+ // 如果事件类型是'startUpload',表示开始上传文件操作即将启动
+ case 'startUpload':
+ /* 添加额外的GET参数 */
+ // 通过调用utils对象的serializeParam函数(应该是用于序列化参数的自定义函数),传入editor对象的queryCommandValue方法获取'serverparam'命令对应的值(可能是一些自定义的服务器端相关参数),将返回结果赋值给params变量,如果返回结果为空(也就是没有获取到参数),则params为空字符串,用于后续构建包含额外参数的上传URL
+ var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '',
+ // 通过调用utils对象的formatUrl函数(应该是用于格式化URL的自定义函数),构建一个新的上传URL地址,在原有的actionUrl(文件上传的基础目标URL地址)基础上,根据其是否已经包含查询字符串(通过判断是否存在'?'来确定),添加合适的连接符号(如果没有查询字符串则添加'?',已有则添加'&'),然后拼接上固定的'encode=utf-8&'以及前面获取到的参数(params),最终生成包含额外GET参数的完整上传URL地址,赋值给url变量
+ url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1? '?':'&') + 'encode=utf-8&' + params);
+ // 调用uploader对象(WebUploader实例)的option方法,传入'server'作为配置项名称,将前面构建好的包含额外参数的上传URL地址(url)设置为新的文件上传服务器地址,用于实际的文件上传请求将按照这个更新后的URL进行发送
+ uploader.option('server', url);
+ // 调用setState函数,传入'uploading'作为状态值,同时传入相关文件对象(files),将文件上传状态设置为正在上传状态,并且根据这个状态切换来更新界面元素显示(比如显示进度条等)、状态信息展示等情况,告知用户文件正在上传过程中
+ setState('uploading', files);
+ break;
+ // 如果事件类型是'stopUpload',表示文件上传操作被停止(例如用户点击了暂停按钮等情况导致上传停止)
+ case 'stopUpload':
+ // 调用setState函数,传入'paused'作为状态值,同时传入相关文件对象(files),将文件上传状态设置为暂停状态,然后根据这个状态切换来更新界面元素显示(比如保持进度条显示等)、状态信息展示等情况,提示用户上传已暂停,可进行相应操作(如继续上传等)
+ setState('paused', files);
+ break;
+ }
+});
+
+// 为uploader对象(WebUploader实例)绑定'uploadBeforeSend'事件处理函数,当在文件上传即将发送请求之前会触发该函数,传入即将上传的文件对象(file)、用于存放POST请求参数的数据对象(data,可在这个对象中添加额外的POST参数)以及请求头对象(header,可用于设置请求头相关的信息)作为参数,用于在上传前进行一些请求相关的配置操作
+uploader.on('uploadBeforeSend', function (file, data, header) {
+ // 这里可以通过data对象添加POST参数,也就是在文件上传前,如果有需要向服务器端传递额外的POST数据,可以在这个函数内部操作data对象来添加相应的参数信息(具体添加哪些参数根据业务需求而定)
+
+ // 判断文件上传的目标URL地址(actionUrl)转换为小写形式后,是否包含'jsp'字符串,如果包含(indexOf方法返回值不为 -1),说明可能是向JSP相关的服务器端接口上传文件
+ if (actionUrl.toLowerCase().indexOf('jsp')!= -1) {
+ // 如果是向JSP相关的服务器端接口上传文件,在请求头对象(header)中添加一个名为'X_Requested_With'的属性,设置其值为'XMLHttpRequest',这是一种常见的设置,用于标识该请求是通过XMLHttpRequest方式发起的,可能用于服务器端识别请求来源或者进行相应的处理逻辑判断等情况
+ header['X_Requested_With'] = 'XMLHttpRequest';
+ }
+});
+
+// 为uploader对象(WebUploader实例)绑定'uploadProgress'事件处理函数,在文件上传过程中,当某个文件的上传进度有更新时会触发该函数,传入正在上传的文件对象(file)以及该文件当前的上传进度百分比(percentage)作为参数,用于更新界面上该文件的进度条显示以及整体的上传进度统计等相关信息
+uploader.on('uploadProgress', function (file, percentage) {
+ // 通过文件的id找到对应的 元素(也就是该文件在页面上对应的整个展示元素),赋值给变量$li,方便后续操作该元素内部的进度条相关元素来更新显示
+ var $li = $('#' + file.id),
+ // 通过jQuery选择器在找到的 元素($li)中查找类名为'progress'的 元素内部的 元素(也就是用于展示该文件上传进度条的具体元素),赋值给变量$percent,用于后续操作该进度条元素来更新其宽度等样式,以体现文件上传进度的变化
+ $percent = $li.find('.progress span');
+
+ // 通过css方法设置找到的进度条元素($percent)的宽度样式属性,将传入的上传进度百分比(percentage)乘以100并拼接上 '%' 符号后设置为宽度值,这样进度条的宽度就会根据文件上传进度按比例进行变化,直观地展示给用户文件当前的上传进度情况
+ $percent.css('width', percentage * 100 + '%');
+ // 将当前文件在percentages对象(用于记录每个文件的大小和进度信息的对象)中对应的进度值(数组中的第二个元素)更新为传入的当前上传进度百分比(percentage),确保记录的文件进度信息是最新的,方便后续计算总体上传进度等操作使用
+ percentages[ file.id ][ 1 ] = percentage;
+ // 调用updateTotalProgress函数(之前定义的用于更新文件上传的总体进度信息以及相关界面显示的函数),在单个文件上传进度更新后,及时更新整体的上传进度情况以及相应的界面展示(比如总体进度条的显示等),保证展示给用户的上传进度信息是准确且实时更新的
+ updateTotalProgress();
+});
+
+// 为uploader对象(WebUploader实例)绑定'uploadSuccess'事件处理函数,当某个文件上传成功后会触发该函数,传入上传成功的文件对象(file)以及服务器端返回的响应数据(ret,包含了服务器端返回的关于该文件上传结果等相关信息)作为参数,用于根据服务器端返回的结果进行相应的界面提示、数据记录等操作
+uploader.on('uploadSuccess', function (file, ret) {
+ // 通过文件的id找到对应的 元素(也就是该文件在页面上对应的整个展示元素),赋值给变量$file,方便后续操作该元素进行相应的界面更新操作等
+ var $file = $('#' + file.id);
+ try {
+ // 尝试获取服务器端返回的响应数据中的原始数据(ret._raw,如果存在的话),如果不存在则直接使用整个响应数据(ret),将获取到的数据赋值给responseText变量,用于后续解析操作
+ var responseText = (ret._raw || ret),
+ // 通过调用utils对象的str2json函数(应该是用于将字符串转换为JSON对象的自定义函数),将前面获取到的响应数据文本(responseText)转换为JSON对象,赋值给json变量,方便后续根据JSON对象中的具体属性来判断上传结果等情况
+ json = utils.str2json(responseText);
+ // 判断转换后的JSON对象(json)中的'state'属性值是否为'SUCCESS',也就是判断服务器端返回的结果表示文件上传是否成功
+ if (json.state == 'SUCCESS') {
+ // 如果文件上传成功,将服务器端返回的JSON对象(json,包含了文件相关的详细信息等)添加到当前实例对象(_this)的fileList属性(用于记录上传成功的文件相关信息的数组)中,方便后续对上传成功的文件数据进行统一管理和使用
+ _this.fileList.push(json);
+ // 在找到的文件对应的 元素($file)内部添加一个类名为'success'的 元素,可能用于在页面上展示一个表示文件上传成功的标识(比如一个成功图标之类的元素),给用户一个直观的提示,告知该文件已成功上传
+ $file.append(' ');
+ } else {
+ // 如果服务器端返回的结果表示文件上传不成功(json.state不是'SUCCESS'),通过jQuery选择器在找到的文件对应的 元素($file)中查找类名为'error'的元素(可能是用于展示文件错误信息的元素),调用text方法将JSON对象(json)中的'state'属性值(也就是服务器端返回的错误提示等相关信息)设置为该元素的文本内容,然后调用show方法使其显示出来,在页面上展示出文件上传失败的具体错误信息,告知用户该文件上传出现问题了
+ $file.find('.error').text(json.state).show();
+ }
+ } catch (e) {
+ // 如果在尝试解析服务器端返回的响应数据或者进行其他相关操作时出现了异常(比如数据格式不符合预期等情况导致转换JSON对象失败等),通过jQuery选择器在找到的文件对应的 元素($file)中查找类名为'error'的元素,调用text方法将预先定义好的表示服务器端上传错误的国际化文本变量lang.errorServerUpload设置为该元素的文本内容,然后调用show方法使其显示出来,在页面上展示出一个通用的服务器端上传错误提示信息,告知用户文件上传出现问题了,但具体原因由于解析异常无法准确展示
+ $file.find('.error').text(lang.errorServerUpload).show();
+ }
+});
+
+// 为uploader对象(WebUploader实例)绑定'uploadError'事件处理函数,当某个文件上传出现错误时会触发该函数,传入出现错误的文件对象(file)以及错误代码(code,可能是WebUploader内部定义的用于标识不同类型错误的代码)作为参数,不过此处函数体为空,可能后续需要根据具体的错误情况添加相应的处理逻辑,比如提示用户具体错误原因、进行重试等操作
+uploader.on('uploadError', function (file, code) {
+});
+
+// 为uploader对象(WebUploader实例)绑定'error'事件处理函数,当出现全局的上传相关错误时会触发该函数,传入错误代码(code,标识错误类型)以及可能涉及的文件对象(file)作为参数,用于处理一些全局性质的上传错误情况
+uploader.on('error', function (code, file) {
+ // 判断错误代码(code)是否是'Q_TYPE_DENIED'(可能表示文件类型被拒绝,也就是上传的文件类型不符合要求)或者'F_EXCEED_SIZE'(可能表示文件大小超出限制)这两种情况之一
+ if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') {
+ // 如果是文件类型不符合要求或者文件大小超出限制这两种错误情况之一,调用addFile函数(之前定义的用于创建与文件对应的视图展示元素等操作的函数),传入出现错误的文件对象(file),重新创建该文件对应的展示元素,可能用于在页面上再次展示该文件以及对应的错误提示信息等情况,让用户能清楚看到是哪个文件出现了什么类型的错误
+ addFile(file);
+ }
+});
+
+// 为uploader对象(WebUploader实例)绑定'uploadComplete'事件处理函数,当某个文件完成了整个上传流程(无论成功还是失败)后会触发该函数,传入完成上传流程的文件对象(file)以及服务器端返回的响应数据(ret)作为参数,不过此处函数体为空,可能后续需要根据具体业务需求添加相应的处理逻辑,比如进行一些清理操作、记录最终结果等情况
+uploader.on('uploadComplete', function (file, ret) {
+});
+
+ // 为上传按钮($upload)绑定点击(click)事件处理函数,当用户点击上传按钮时会触发该函数,用于根据当前文件上传的状态来执行相应的操作,比如开始上传、暂停上传等操作
+$upload.on('click', function () {
+ // 判断上传按钮($(this),在事件处理函数中this指向被点击的按钮元素,通过jQuery包装后进行判断)是否包含类名'disabled',如果包含该类名,表示按钮当前处于不可用状态(例如上传队列中没有文件时设置的不可点击状态等情况)
+ if ($(this).hasClass('disabled')) {
+ // 如果按钮处于不可用状态,直接返回false,阻止后续默认的点击事件行为(比如阻止按钮的表单提交等默认行为,在这里就是不让用户进行无效的点击操作)
+ return false;
+ }
+
+ // 判断当前文件上传状态(state)是否为'ready',即是否处于可以开始上传的状态
+ if (state === 'ready') {
+ // 如果是'ready'状态,调用uploader对象(WebUploader实例)的upload方法,触发文件上传操作,开始将上传队列中的文件发送到服务器端进行上传
+ uploader.upload();
+ } else if (state === 'paused') {
+ // 判断当前文件上传状态是否为'paused',即是否处于暂停上传的状态
+ // 如果是'paused'状态,同样调用uploader对象的upload方法,用于在暂停后继续进行文件上传操作,恢复文件的上传流程
+ uploader.upload();
+ } else if (state === 'uploading') {
+ // 判断当前文件上传状态是否为'uploading',即是否处于正在上传文件的状态
+ // 如果是'uploading'状态,调用uploader对象的uploader.stop方法,用于暂停正在进行的文件上传操作,停止向服务器发送文件数据等上传行为
+ uploader.stop();
+ }
+});
+
+// 为上传按钮($upload)添加表示当前文件上传状态(state)的类名(格式为'state-' + state),通过添加这个类名可以应用相应的样式(例如按钮的外观、可操作性等样式变化)来体现当前文件上传所处的状态情况
+$upload.addClass('state-' + state);
+// 调用updateTotalProgress函数(之前定义的用于更新文件上传的总体进度信息以及相关界面显示的函数),在按钮相关操作完成后(比如点击按钮开始、暂停上传等操作后),及时更新整体的上传进度情况以及相应的界面展示,保证展示给用户的上传进度信息是准确的
+updateTotalProgress();
+},
+// 定义一个名为getQueueCount的函数,用于获取当前处于可上传状态(比如已添加到队列中等待上传、正在上传、上传进度中这些状态的文件都算可上传状态的文件)的文件数量,是当前实例对象的一个方法
+getQueueCount: function () {
+ // 初始化几个变量,file用于在循环中临时存储每个文件对象,i作为循环计数器,status用于存储文件的状态,readyFile用于统计处于可上传状态的文件数量,初始值设为0,files通过调用this.uploader(当前实例对应的WebUploader实例)的getFiles方法获取所有已添加到WebUploader实例中的文件对象列表,用于后续遍历判断文件状态
+ var file, i, status, readyFile = 0, files = this.uploader.getFiles();
+ // 使用一个特殊的for循环语法(在循环条件中进行赋值操作),从files列表中依次取出每个文件对象赋值给file变量,并同时递增循环计数器i,只要file变量能获取到有效的文件对象(也就是files列表还没遍历完)就会继续循环
+ for (i = 0; file = files[i++]; ) {
+ // 获取当前文件(file)的状态,通过调用file对象的getStatus方法获取,将获取到的状态赋值给status变量,用于后续判断该文件是否处于可上传状态
+ status = file.getStatus();
+ // 判断文件的状态(status)是否是'queued'(已添加到队列中等待上传)、'uploading'(正在上传)或者'progress'(上传进度中)这几种可上传相关的状态之一,如果是,则将统计可上传状态文件数量的变量(readyFile)加1
+ if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++;
+ }
+ // 返回统计得到的处于可上传状态的文件数量(readyFile),以便其他地方可以获取并根据这个数量进行相应的操作(比如判断是否有文件可上传来决定上传按钮是否可用等情况)
+ return readyFile;
+},
+// 定义一个名为getInsertList的函数,用于获取要插入的文件列表信息(可能是用于后续在某个编辑器或者其他地方插入文件相关的链接、标题等信息),是当前实例对象的一个方法
+getInsertList: function () {
+ // 初始化几个变量,i作为循环计数器,link用于存储文件的链接地址,data用于临时存储每个文件相关的数据对象,list用于存储最终要返回的文件列表信息,初始化为一个空数组,prefix通过调用editor对象的getOpt方法获取'fileUrlPrefix'配置项的值(可能是文件链接的前缀地址,用于拼接完整的文件访问地址)
+ var i, link, data, list = [],
+ prefix = editor.getOpt('fileUrlPrefix');
+ // 使用for循环遍历当前实例对象(this)的fileList属性(用于记录上传成功的文件相关信息的数组),循环从0开始,每次递增1,直到遍历完所有元素
+ for (i = 0; i < this.fileList.length; i++) {
+ // 获取当前索引位置对应的文件相关数据对象,赋值给data变量,方便后续提取文件的相关信息(如链接、标题等)
+ data = this.fileList[i];
+ // 获取文件相关数据对象(data)中的文件链接地址(url属性),赋值给link变量,用于后续构建要插入的文件信息中的链接部分
+ link = data.url;
+ // 将一个包含文件标题和链接的对象添加到list数组中,文件标题优先取data.original属性(可能是文件原始名称之类的更合适的标题信息),如果不存在则取文件链接地址(link)中以'/'分割后的最后一部分(也就是文件名部分)作为标题,文件链接则通过将获取到的前缀(prefix)和文件链接地址(link)进行拼接得到完整的可访问地址,这样构建好的对象就包含了要插入的文件的标题和完整链接信息
+ list.push({
+ title: data.original || link.substr(link.lastIndexOf('/') + 1),
+ url: prefix + link
+ });
+ }
+ // 返回构建好的包含要插入的文件标题和链接信息的列表(list),以便其他地方(比如编辑器相关的插入文件操作处)可以获取并使用这些信息进行相应的文件插入操作
+ return list;
+}
+};
+
+
+/* 在线附件 */
+// 定义一个名为OnlineFile的构造函数,用于创建与在线文件相关的对象实例,接收一个参数target,用于指定相关的DOM元素或者元素的标识等信息,该函数是创建在线文件相关功能的入口
+function OnlineFile(target) {
+ // 判断传入的target参数是否为字符串类型,如果是字符串类型,则通过document.getElementById方法根据该字符串(当作元素的id)获取对应的DOM元素,否则直接将传入的参数当作已经获取到的DOM元素进行赋值,最终将获取到的元素赋值给this.container属性,用于后续操作中代表与在线文件相关的容器元素
+ this.container = utils.isString(target)? document.getElementById(target) : target;
+ // 调用init方法,用于进行一些初始化操作,比如初始化容器、事件绑定、数据初始化等相关设置,启动在线文件相关功能的初始化流程
+ this.init();
+}
+// 为OnlineFile构造函数的原型对象添加属性和方法,这样通过该构造函数创建的所有实例对象都可以共享这些属性和方法,实现在线文件相关功能的具体逻辑定义
+OnlineFile.prototype = {
+ // 定义init方法,作为在线文件相关功能初始化的总入口,会依次调用其他几个初始化子函数,完成诸如容器初始化、事件绑定、初始数据准备等各项初始化操作
+ init: function () {
+ // 调用initContainer方法,用于初始化与在线文件相关的容器元素,比如创建文件列表展示的DOM结构等操作
+ this.initContainer();
+ // 调用initEvents方法,用于初始化与在线文件相关的事件处理逻辑,比如滚动事件等,方便用户在操作在线文件时有相应的交互效果
+ this.initEvents();
+ // 调用initData方法,用于初始化在线文件相关的数据,可能是加载初始的文件列表数据等操作,具体根据业务需求而定
+ this.initData();
+ },
+ /* 初始化容器 */
+ initContainer: function () {
+ // 将this.container(代表与在线文件相关的容器元素)的innerHTML属性设置为空字符串,也就是清空该容器元素内部原有的所有内容,用于重新构建与在线文件展示相关的DOM结构
+ this.container.innerHTML = '';
+ // 创建一个 元素,用于作为在线文件列表的外层容器元素,将其赋值给this.list属性,方便后续向其中添加具体的文件列表项等操作
+ this.list = document.createElement('ul');
+ // 创建一个 元素,用于可能的清除浮动等样式相关操作(比如在一些布局中通过添加这个元素来确保父元素能正确撑开高度等情况),将其赋值给this.clearFloat属性,后续会将其添加到文件列表中用于布局相关目的
+ this.clearFloat = document.createElement('li');
+
+ // 使用domUtils对象的addClass方法(可能是自定义的添加类名函数)为创建的文件列表元素(this.list)添加类名'list',用于通过CSS样式来设置文件列表的外观、布局等样式效果
+ domUtils.addClass(this.list, 'list');
+ // 使用domUtils对象的addClass方法为创建的用于清除浮动的元素(this.clearFloat)添加类名'clearFloat',同样是为了通过CSS样式来实现相应的布局效果(比如清除浮动相关的样式规则应用到这个元素上)
+ domUtils.addClass(this.clearFloat, 'clearFloat');
+
+ // 将用于清除浮动的元素(this.clearFloat)添加到文件列表元素(this.list)中,使其成为文件列表的子元素,符合相应的布局结构要求
+ this.list.appendChild(this.clearFloat);
+ // 将包含了清除浮动元素的文件列表元素(this.list)添加到与在线文件相关的容器元素(this.container)中,完成在线文件列表展示的基本DOM结构搭建,后续可以向文件列表中添加具体的文件项等内容
+ this.container.appendChild(this.list);
+ },
+ /* 初始化滚动事件,滚动到地步自动拉取数据 */
+ initEvents: function () {
+ // 将当前实例对象(this)赋值给变量_this,用于在后续一些闭包环境中能正确访问到当前实例对象的属性和方法,避免this指向问题导致的错误
+ var _this = this;
+
+ /* 滚动拉取图片 */
+ // 使用domUtils对象的on方法(可能是自定义的事件绑定函数)为id为'fileList'的元素(可能就是前面初始化的文件列表所在元素或者相关的滚动容器元素)绑定滚动(scroll)事件处理函数,当该元素发生滚动时会触发此函数,传入事件对象(e)作为参数
+ domUtils.on($G('fileList'), 'scroll', function(e){
+ // 将触发滚动事件的元素(this,在事件处理函数中this指向触发事件的DOM元素,也就是绑定了滚动事件的那个元素)赋值给变量panel,方便后续操作该元素获取相关的滚动属性等信息
+ var panel = this;
+ // 判断文件列表容器元素(panel)的滚动高度(scrollHeight,表示整个可滚动内容的高度)减去(元素的可视高度(offsetHeight)加上已经滚动的距离(scrollTop))是否小于10像素,也就是判断是否滚动到了容器元素的底部附近(这里设定10像素的差值作为接近底部的判断条件)
+ if (panel.scrollHeight - (panel.offsetHeight + panel.scrollTop) < 10) {
+ // 如果滚动到了容器底部附近,调用当前实例对象(_this)的getFileData方法(应该是用于获取更多在线文件数据的函数,虽然此处未展示其具体代码,但从函数名推测其功能),实现自动拉取更多在线文件数据的功能,以满足用户滚动查看更多文件的需求
+ _this.getFileData();
+ }
+ });
+ /* 选中图片 */
+// 使用domUtils对象的on方法(可能是自定义的事件绑定函数)为当前实例对象(this)的list属性所代表的元素(应该是在线文件列表的DOM元素)绑定点击(click)事件处理函数,当用户点击文件列表中的元素时会触发该函数,传入事件对象(e)作为参数,用于处理图片选中相关的交互逻辑
+domUtils.on(this.list, 'click', function (e) {
+ // 获取触发点击事件的实际目标元素,兼容不同浏览器获取方式(e.target适用于标准浏览器,e.srcElement适用于IE浏览器),将其赋值给变量target,方便后续操作判断点击的具体元素是什么
+ var target = e.target || e.srcElement,
+ // 获取目标元素(target)的父节点元素(也就是所在的 元素,假设文件列表中的每个文件项是用 元素包裹的),赋值给变量li,用于后续判断是否点击的是文件列表项元素以及进行相应的选中样式处理等操作
+ li = target.parentNode;
+
+ // 判断获取到的父节点元素(li)的标签名(tagName)转换为小写形式后是否等于'li',也就是确认点击的目标元素的父元素确实是文件列表中的 元素(用于排除点击到其他不符合要求的元素的情况)
+ if (li.tagName.toLowerCase() == 'li') {
+ // 判断该 元素(li)是否已经包含类名'selected'(可能表示该文件项已被选中的样式类名),如果包含
+ if (domUtils.hasClass(li, 'selected')) {
+ // 使用domUtils对象的removeClasses方法(可能是自定义的移除类名函数)从该 元素(li)中移除'selected'类名,也就是取消该文件项的选中状态,对应的样式效果(比如选中时的背景色等样式)会消失
+ domUtils.removeClasses(li, 'selected');
+ } else {
+ // 如果该 元素(li)不包含'selected'类名,也就是当前未被选中,使用domUtils对象的addClass方法(可能是自定义的添加类名函数)为该 元素(li)添加'selected'类名,使其变为选中状态,显示出相应的选中样式效果
+ domUtils.addClass(li, 'selected');
+ }
+ }
+});
+},
+/* 初始化第一次的数据 */
+initData: function () {
+ // 以下是拉取数据需要使用到的几个变量的初始化,用于记录在线文件数据拉取相关的状态、数量、索引等信息
+
+ /* 拉取数据需要使用的值 */
+ // 初始化一个变量state,用于记录当前数据拉取的状态,初始值设为0,具体含义可能根据业务逻辑确定,比如0表示初始状态,后续可能根据不同阶段有不同的值来表示不同的数据拉取情况等
+ this.state = 0;
+ // 通过调用editor对象的getOpt方法获取'fileManagerListSize'配置项的值(可能表示每次拉取在线文件列表数据的数量大小限制),并赋值给this.listSize变量,用于确定每次向后台请求获取的文件数量
+ this.listSize = editor.getOpt('fileManagerListSize');
+ // 初始化一个变量this.listIndex,用于记录当前已经拉取的数据在整个数据集中的索引位置,初始值设为0,表示从最开始的位置开始拉取数据,后续会根据实际拉取情况进行更新
+ this.listIndex = 0;
+ // 初始化一个变量this.listEnd,用于标记是否已经拉取到了所有的在线文件数据,初始值设为false,表示还未拉取完所有数据,当拉取完所有数据后会将其设置为true
+ this.listEnd = false;
+
+ /* 第一次拉取数据 */
+ // 调用this.getFileData方法(用于向后台拉取图片列表数据的函数),开始进行第一次在线文件数据的拉取操作,获取初始的文件列表数据并展示在页面上,后续根据滚动等操作可能会继续拉取更多数据
+ this.getFileData();
+},
+/* 向后台拉取图片列表数据 */
+getFileData: function () {
+ // 将当前实例对象(this)赋值给变量_this,用于在后续一些闭包环境中能正确访问到当前实例对象的属性和方法,避免this指向问题导致的错误
+ var _this = this;
+
+ // 判断当前是否还未拉取完所有数据(_this.listEnd为false)并且当前是否不在加载数据的过程中(!this.isLoadingData,isLoadingData变量可能用于标记是否正在进行数据请求加载操作),只有这两个条件都满足时才执行后续的数据拉取操作,避免重复请求或者在数据还未加载完时又发起新请求等情况
+ if (!_this.listEnd &&!this.isLoadingData) {
+ // 如果满足条件,将表示正在加载数据的变量(this.isLoadingData)设置为true,标记当前开始进入数据加载状态,防止其他地方同时触发数据拉取操作
+ this.isLoadingData = true;
+ // 使用ajax对象(可能是自定义的用于发送异步请求的对象或者基于某个库的请求对象)的request方法发起一个GET请求,向后台服务器获取在线文件(图片)列表数据,传入请求的URL地址以及一些配置参数对象作为参数
+ ajax.request(editor.getActionUrl(editor.getOpt('fileManagerActionName')), {
+ // 设置请求的超时时间为100000毫秒(也就是100秒),如果请求在这个时间内没有响应则会触发超时处理逻辑,防止请求长时间无响应导致页面卡顿等问题
+ timeout: 100000,
+ // 设置请求携带的数据,通过调用utils对象的extend方法(可能是用于合并对象的自定义函数),将一个包含起始索引(start,使用this.listIndex变量的值,表示从哪个位置开始拉取数据)和拉取数量(size,使用this.listSize变量的值,表示每次拉取的数据数量)的对象与通过editor对象的queryCommandValue方法获取'serverparam'命令对应的值(可能是一些自定义的服务器端相关参数)进行合并,最终生成包含完整请求数据参数的对象,作为请求携带的数据发送给服务器端
+ data: utils.extend({
+ start: this.listIndex,
+ size: this.listSize
+ }, editor.queryCommandValue('serverparam')),
+ // 设置请求方法为'get',表示发起一个GET请求,向服务器端获取数据(而不是POST等其他请求方式)
+ method: 'get',
+ // 设置请求成功的回调函数,当服务器端成功返回数据时会触发该函数,传入服务器端返回的响应对象(r,包含了响应的各种信息,如响应文本、状态码等)作为参数,用于处理成功获取到的数据情况
+ onsuccess: function (r) {
+ try {
+ // 尝试将服务器端返回的响应文本(r.responseText)通过eval函数(虽然使用eval函数存在一定安全风险,但在这里可能是用于将服务器端返回的符合JSON格式的字符串转换为JavaScript对象,不过更好的方式是使用JSON.parse等安全的解析方法)转换为JavaScript对象,并赋值给变量json,方便后续根据对象中的属性判断数据情况并进行相应处理
+ var json = eval('(' + r.responseText + ')');
+ // 判断转换后的JavaScript对象(json)中的'state'属性值是否为'SUCCESS',也就是判断服务器端返回的结果表示此次数据获取是否成功(可能根据服务器端的业务逻辑,返回'SUCCESS'表示正常获取到了文件列表数据等情况)
+ if (json.state == 'SUCCESS') {
+ // 如果数据获取成功,调用当前实例对象(_this)的pushData方法(应该是用于将获取到的数据添加到页面或者相关数据存储中的函数,虽然此处未展示其具体代码,但从函数名推测其功能),传入服务器端返回的数据列表(json.list,应该是包含了多个文件相关信息的数组),将获取到的文件数据进行相应的展示等处理操作
+ _this.pushData(json.list);
+ // 更新当前已经拉取的数据在整个数据集中的索引位置(_this.listIndex),通过将服务器端返回的起始索引(json.start,可能是此次返回数据在整个数据集中的起始位置信息)转换为整数后加上返回的数据列表长度(json.list.length)来计算得到新的索引位置,以便下次拉取数据时能从正确的位置继续获取数据
+ _this.listIndex = parseInt(json.start) + parseInt(json.list.length);
+ // 判断如果更新后的索引位置(_this.listIndex)大于等于服务器端返回的总数据量(json.total,表示整个在线文件数据集的总数),说明已经拉取完了所有数据,将标记是否拉取完所有数据的变量(_this.listEnd)设置为true,表示数据拉取结束了
+ if (_this.listIndex >= json.total) {
+ _this.listEnd = true;
+ }
+ // 将表示正在加载数据的变量(_this.isLoadingData)设置为false,标记当前数据加载操作已完成,允许后续再次发起数据拉取请求(如果满足条件的话)
+ _this.isLoadingData = false;
+ }
+ } catch (e) {
+ // 如果在尝试解析服务器端返回的数据或者进行其他相关操作时出现了异常(比如数据格式不符合预期等情况导致转换对象失败等),进行以下异常处理逻辑
+ // 判断服务器端返回的响应文本(r.responseText)中是否包含'ue_separate_ue'字符串,如果包含(可能表示一种特殊的数据格式或者错误情况等,具体根据业务逻辑确定)
+ if (r.responseText.indexOf('ue_separate_ue')!= -1) {
+ // 通过split方法将响应文本(r.responseText)按照自身进行分割(这里的分割逻辑看起来有点奇怪,可能是根据特定的数据格式要求进行处理,也许是想获取其中的某个部分作为数据列表,具体需要结合实际业务来看),将分割后的结果赋值给变量list,作为获取到的数据列表
+ var list = r.responseText.split(r.responseText);
+ // 调用当前实例对象(_this)的pushData方法,传入获取到的数据列表(list),将数据进行相应的展示等处理操作,虽然数据格式可能不符合常规的预期,但按照这种特殊情况进行处理
+ _this.pushData(list);
+ // 将当前已经拉取的数据在整个数据集中的索引位置(_this.listIndex)更新为获取到的数据列表长度(list.length)转换为整数后的数值,因为可能这种特殊格式下无法按照常规方式计算索引,只能简单以数据列表长度来表示位置
+ _this.listIndex = parseInt(list.length);
+ // 将标记是否拉取完所有数据的变量(_this.listEnd)设置为true,表示数据拉取结束了,可能在这种特殊情况下认为已经获取完所有能处理的数据了
+ _this.listEnd = true;
+ // 将表示正在加载数据的变量(_this.isLoadingData)设置为false,标记当前数据加载操作已完成,结束这次特殊情况下的数据处理流程
+ _this.isLoadingData = false;
+ }
+ }
+ },
+ // 设置请求失败的回调函数,当请求出现错误(比如网络问题、服务器端返回错误状态码等情况)时会触发该函数,用于处理请求失败的情况,在这里只是简单地将表示正在加载数据的变量(_this.isLoadingData)设置为false,标记当前数据加载操作结束(虽然失败了),允许后续再次发起数据拉取请求(如果满足条件的话)
+ onerror: function () {
+ _this.isLoadingData = false;
+ }
+ });
+ }
+},
+ /* 添加图片到列表界面上 */
+// 定义一个名为pushData的函数,用于将获取到的在线文件(图片)数据添加到列表界面上进行展示,接收一个参数list,代表从服务器端获取到的文件数据列表(通常是包含多个文件相关信息的数组)
+pushData: function (list) {
+ // 初始化多个变量,i作为循环计数器,item用于创建每个文件对应的DOM元素( 元素,代表列表中的一个文件项),img用于创建图片元素(如果文件是图片类型的话),filetype用于存储文件的类型(通过文件链接的扩展名来判断),preview用于创建文件的预览元素(可能是图片元素或者包含文件相关信息的其他元素,根据文件类型不同而不同),icon用于创建一个图标元素(可能用于展示文件的一些标识等),_this用于保存当前实例对象(this)的引用,方便在闭包等场景下正确访问实例的属性和方法,urlPrefix通过调用editor对象的getOpt方法获取'fileManagerUrlPrefix'配置项的值(可能是文件链接的前缀地址,用于拼接完整的可访问文件链接)
+ var i, item, img, filetype, preview, icon, _this = this,
+ urlPrefix = editor.getOpt('fileManagerUrlPrefix');
+ // 使用for循环遍历传入的文件数据列表(list),从索引0开始,每次递增1,直到遍历完所有元素,用于逐个处理每个文件的数据并添加到界面上
+ for (i = 0; i < list.length; i++) {
+ // 判断当前索引位置的文件数据(list[i])是否存在并且其是否包含url属性(也就是判断这个文件数据是否有效且有对应的文件链接地址,用于后续操作)
+ if (list[i] && list[i].url) {
+ // 创建一个 元素,作为文件列表中的一个文件项元素,用于包裹文件的相关展示元素(如预览图、图标等),并将其赋值给变量item,后续会向这个元素中添加其他子元素来完善文件项的展示内容
+ item = document.createElement('li');
+ // 创建一个 元素,可能作为文件相关的图标元素(具体图标样式等可能后续通过类名等方式设置),赋值给变量icon,用于后续添加到文件项元素(item)中
+ icon = document.createElement('span');
+ // 获取当前文件的类型,通过截取文件链接(list[i].url)中最后一个 '.' 之后的字符串(也就是文件扩展名)来确定文件类型,并赋值给变量filetype,用于后续根据文件类型进行不同的展示处理(比如图片文件和非图片文件展示方式不同)
+ filetype = list[i].url.substr(list[i].url.lastIndexOf('.') + 1);
+
+ // 判断文件类型(filetype)是否在常见的图片文件类型列表("png|jpg|jpeg|gif|bmp")中,如果在这个列表中,说明是图片文件,进行以下处理逻辑来展示图片预览
+ if ("png|jpg|jpeg|gif|bmp".indexOf(filetype)!= -1) {
+ // 创建一个 元素,用于展示图片文件的预览图,赋值给变量preview,后续会设置其相关属性(如src、width等)来正确显示图片
+ preview = document.createElement('img');
+ // 使用domUtils对象的on方法(可能是自定义的事件绑定函数)为创建的图片元素(preview)绑定加载(load)事件处理函数,当图片加载完成时会触发该函数,传入一个立即执行函数返回的另一个函数作为事件处理函数,这样做是为了在闭包中正确传递当前的图片元素(preview)引用,避免出现变量作用域问题
+ domUtils.on(preview, 'load', (function (image) {
+ return function () {
+ // 在图片加载完成的回调函数内部,调用当前实例对象(_this)的scale方法(应该是用于调整图片大小、缩放等操作的函数,虽然此处未展示其具体代码,但从函数名推测其功能),传入当前图片元素(image,也就是传入的参数image,它在闭包中指向最初创建的preview元素)以及图片父元素(image.parentNode,也就是包含这个图片的元素,这里应该是文件项中的用于展示图片的区域元素)的宽度(offsetWidth)和高度(offsetHeight)作为参数,根据父元素的尺寸来对图片进行缩放等适配操作,确保图片在界面上能合适地展示
+ _this.scale(image, image.parentNode.offsetWidth, image.parentNode.offsetHeight);
+ };
+ })(preview));
+ // 设置图片元素(preview)的宽度为113像素,这里固定了一个初始宽度,后续可能根据缩放等操作再进行调整,具体根据界面布局和展示需求而定
+ preview.width = 113;
+ // 设置图片元素(preview)的src属性,通过拼接文件链接前缀(urlPrefix)、文件的实际链接地址(list[i].url)以及一个用于避免缓存的参数(根据文件链接中是否已经包含'?'来决定添加'?noCache='或者'&noCache=',并加上当前时间的时间戳转换为36进制后的字符串,这样每次请求图片时由于时间戳不同可以避免浏览器缓存旧的图片,确保获取到最新的图片数据),使得图片能正确加载并显示出来
+ preview.setAttribute('src', urlPrefix + list[i].url + (list[i].url.indexOf('?') == -1? '?noCache=' : '&noCache=') + (+new Date()).toString(36));
+ } else {
+ // 如果文件类型不是常见的图片文件类型,进行以下处理逻辑来展示非图片文件的相关信息
+ var ic = document.createElement('i'),
+ textSpan = document.createElement('span');
+ // 设置 元素(textSpan)的innerHTML属性为文件链接(list[i].url)中最后一个 '/' 之后的字符串(也就是文件名部分),用于展示文件的名称信息
+ textSpan.innerHTML = list[i].url.substr(list[i].url.lastIndexOf('/') + 1);
+ // 创建一个 元素,作为非图片文件的整体预览元素,将其赋值给变量preview,后续会向这个元素中添加其他子元素(如文件类型图标、文件名等)来展示文件相关信息
+ preview = document.createElement('div');
+ // 将创建的
元素(ic,可能用于展示文件类型对应的图标样式等)添加到 元素(preview)中,作为其子元素
+ preview.appendChild(ic);
+ // 将创建的
元素(textSpan,展示文件名)添加到 元素(preview)中,同样作为其子元素,完成非图片文件预览元素内部的结构搭建
+ preview.appendChild(textSpan);
+ // 使用domUtils对象的addClass方法(可能是自定义的添加类名函数)为
元素(preview)添加类名'file-wrapper',通过CSS样式类来设置这个元素的外观、布局等样式效果,使其符合非图片文件展示的整体样式要求
+ domUtils.addClass(preview, 'file-wrapper');
+ // 使用domUtils对象的addClass方法为
元素(textSpan)添加类名'file-title',用于设置文件名展示的样式效果(比如字体、颜色等样式),使其更清晰地展示文件名
+ domUtils.addClass(textSpan, 'file-title');
+ // 使用domUtils对象的addClass方法为 元素(ic)添加类名'file-type-' + filetype,通过动态添加包含文件类型的类名,可以根据不同的文件类型应用不同的图标样式等效果,方便用户直观识别文件类型
+ domUtils.addClass(ic, 'file-type-' + filetype);
+ // 使用domUtils对象的addClass方法再次为 元素(ic)添加类名'file-preview',可能用于统一设置文件预览相关元素的一些通用样式效果等情况
+ domUtils.addClass(ic, 'file-preview');
+ }
+ // 使用domUtils对象的addClass方法为之前创建的 元素(icon)添加类名'icon',用于设置这个图标元素的样式(比如图标大小、颜色、位置等样式效果),使其符合在文件项中的展示要求
+ domUtils.addClass(icon, 'icon');
+ // 为文件项元素(item)设置自定义的'data-url'属性,其值通过拼接文件链接前缀(urlPrefix)和文件的实际链接地址(list[i].url)得到,用于在后续操作中方便获取文件的完整链接信息(例如点击文件项进行相关操作时可能会用到这个链接)
+ item.setAttribute('data-url', urlPrefix + list[i].url);
+ // 判断当前文件数据(list[i])中是否包含'original'属性(可能是文件的原始名称等更合适的标题信息),如果包含
+ if (list[i].original) {
+ // 为文件项元素(item)设置自定义的'data-title'属性,其值为文件的'original'属性值,用于设置文件项更准确的标题信息,方便在界面上展示(比如鼠标悬停提示等场景可以显示这个标题)
+ item.setAttribute('data-title', list[i].original);
+ }
+
+ // 将创建好的文件预览元素(preview,可能是图片元素或者包含文件相关信息的 元素,取决于文件类型)添加到文件项元素(item)中,作为其子元素,完成文件项中主要内容的添加
+ item.appendChild(preview);
+ // 将设置好类名的图标元素(icon)添加到文件项元素(item)中,同样作为其子元素,进一步完善文件项的展示结构
+ item.appendChild(icon);
+ // 将构建好的文件项元素(item)插入到当前实例对象(this)的list属性所代表的文件列表元素(应该是之前初始化的
元素)中,并且插入位置在this.clearFloat元素(可能是用于清除浮动等布局相关的元素)之前,这样新添加的文件项就会按照顺序展示在文件列表中合适的位置上
+ this.list.insertBefore(item, this.clearFloat);
+ }
+ }
+},
+ /* 改变图片大小 */
+// 定义一个名为'scale'的函数,用于根据指定的参数来改变图片的大小以及对图片进行相应的位置调整(例如使其在某个容器内居中显示等情况),接收四个参数,分别是要操作的图片元素(img)、期望的目标宽度(w)、期望的目标高度(h)以及用于指定调整类型的参数(type)
+scale: function (img, w, h, type) {
+ // 获取传入的图片元素(img)当前的实际宽度值(单位应该是像素),并将其赋值给变量ow,用于后续根据原始宽高比例来计算调整后的宽高尺寸等操作
+ var ow = img.width,
+ // 获取传入的图片元素(img)当前的实际高度值(单位应该是像素),赋值给变量oh,同样方便后续按照原始宽高比例进行相应的尺寸计算和调整逻辑
+ oh = img.height;
+
+ // 判断传入的调整类型参数(type)是否等于'justify','justify'可能表示一种特定的图片缩放及对齐方式(比如让图片在某个区域内按比例缩放并居中显示等情况),如果等于该值,则进入以下相应的处理逻辑
+ if (type == 'justify') {
+ // 判断图片当前的原始宽度(ow)是否大于等于原始高度(oh),如果满足这个条件,说明图片比较“宽”,按照以下方式调整图片的宽高及位置
+ if (ow >= oh) {
+ // 将图片元素(img)的宽度属性(width)设置为传入的目标宽度值(w),也就是让图片宽度达到期望的宽度尺寸
+ img.width = w;
+ // 根据图片原始的宽高比例(oh / ow)来计算调整后的高度值,通过目标高度(h)乘以原始宽高比例得到调整后的高度(h * oh / ow),这样能保证图片按比例缩放,然后将计算得到的高度值赋给图片元素(img)的高度属性(height)
+ img.height = h * oh / ow;
+ // 计算图片在水平方向上需要设置的外边距(marginLeft),目的是让图片在水平方向上居中显示。先计算图片调整后的宽度(img.width)与目标宽度(w)的差值的一半((img.width - w) / 2),然后取其绝对值并转换为整数(通过parseInt函数),最后在前面添加负号('-'),将得到的值设置为图片元素(img)的marginLeft样式属性值,这样图片就能在水平方向上相对于父容器居中了
+ img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px';
+ } else {
+ // 如果图片当前的原始宽度(ow)小于原始高度(oh),说明图片比较“高”,则按照以下方式调整图片的宽高及位置
+ // 根据图片原始的宽高比例(ow / oh)来计算调整后的宽度值,通过目标宽度(w)乘以原始宽高比例得到调整后的宽度(w * ow / oh),保证图片按比例缩放,然后将该宽度值赋给图片元素(img)的宽度属性(width)
+ img.width = w * ow / oh;
+ // 将图片元素(img)的高度属性(height)设置为传入的目标高度值(h),使图片高度达到期望的高度尺寸
+ img.height = h;
+ // 计算图片在垂直方向上需要设置的外边距(marginTop),用于让图片在垂直方向上居中显示。先计算图片调整后的高度(img.height)与目标高度(h)的差值的一半((img.height - h) / 2),然后取其绝对值并转换为整数(通过parseInt函数),最后在前面添加负号('-'),将得到的值设置为图片元素(img)的marginTop样式属性值,使图片在垂直方向上相对于父容器居中
+ img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px';
+ }
+ } else {
+ // 如果传入的调整类型参数(type)不等于'justify',则进入以下默认的处理逻辑(可能是另一种图片缩放及位置调整方式)
+ // 判断图片当前的原始宽度(ow)是否大于等于原始高度(oh),如果满足这个条件,按照以下方式调整图片的宽高及位置
+ if (ow >= oh) {
+ // 根据图片原始的宽高比例(ow / oh)来计算调整后的宽度值,通过目标宽度(w)乘以原始宽高比例得到调整后的宽度(w * ow / oh),保证图片按比例缩放,然后将该宽度值赋给图片元素(img)的宽度属性(width)
+ img.width = w * ow / oh;
+ // 将图片元素(img)的高度属性(height)设置为传入的目标高度值(h),使图片高度达到期望的高度尺寸
+ img.height = h;
+ // 计算图片在水平方向上需要设置的外边距(marginLeft),用于让图片在水平方向上有一定的偏移以达到某种布局效果(具体根据实际需求而定)。先计算图片调整后的宽度(img.width)与目标宽度(w)的差值的一半((img.width - w) / 2),然后取其绝对值并转换为整数(通过parseInt函数),最后在前面添加负号('-'),将得到的值设置为图片元素(img)的marginLeft样式属性值
+ img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px';
+ } else {
+ // 如果图片当前的原始宽度(ow)小于原始高度(oh),则按照以下方式调整图片的宽高及位置
+ // 将图片元素(img)的宽度属性(width)设置为传入的目标宽度值(w),让图片宽度达到期望的宽度尺寸
+ img.width = w;
+ // 根据图片原始的宽高比例(oh / ow)来计算调整后的高度值,通过目标高度(h)乘以原始宽高比例得到调整后的高度(h * oh / ow),保证图片按比例缩放,然后将该高度值赋给图片元素(img)的高度属性(height)
+ img.height = h * oh / ow;
+ // 计算图片在垂直方向上需要设置的外边距(marginTop),用于让图片在垂直方向上有一定的偏移以达到某种布局效果(具体根据实际需求而定)。先计算图片调整后的高度(img.height)与目标高度(h)的差值的一半((img.height - h) / 2),然后取其绝对值并转换为整数(通过parseInt函数),最后在前面添加负号('-'),将得到的值设置为图片元素(img)的marginTop样式属性值
+ img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px';
+ }
+ }
+},
+// 定义一个名为'getInsertList'的函数,用于获取当前被选中的文件(图片)对应的列表信息,这些信息可能后续用于插入到其他地方(比如编辑器等场景),函数会遍历文件列表元素中的子元素,筛选出被选中的元素,并提取其相关属性信息组成列表返回
+getInsertList: function () {
+ // 初始化循环计数器变量i,用于后续遍历文件列表元素的子元素。同时通过this.list.children获取当前实例对象(this)的list属性所代表的文件列表元素(应该是之前创建的 元素)的所有子元素(也就是每个文件对应的 元素等),并赋值给变量lis,方便后续遍历这些子元素来判断哪些是被选中的元素。初始化一个空数组list,用于存储最终提取出来的被选中文件的相关信息(如标题、链接等)
+ var i, lis = this.list.children, list = [];
+ // 使用for循环遍历获取到的文件列表子元素(lis),从索引0开始,每次递增1,直到遍历完所有子元素,用于逐个检查每个子元素是否被选中,并提取相应的信息
+ for (i = 0; i < lis.length; i++) {
+ // 使用domUtils对象的hasClass方法(可能是自定义的判断元素是否包含某个类名的函数)来判断当前遍历到的子元素(lis[i],即每个 元素等)是否包含'selected'类名,'selected'类名可能表示该文件项在界面上处于被选中的状态,只有被选中的元素才进行以下信息提取操作
+ if (domUtils.hasClass(lis[i], 'selected')) {
+ // 通过getAttribute方法获取当前被选中的元素(lis[i])的自定义'data-url'属性值,该属性值应该是对应文件的链接地址,将其赋值给变量url,用于后续构建要返回的文件信息对象中的链接部分
+ var url = lis[i].getAttribute('data-url');
+ // 通过getAttribute方法获取当前被选中的元素(lis[i])的自定义'data-title'属性值,该属性值可能是对应文件更合适的标题信息(比如原始文件名等),如果该属性不存在(也就是返回值为假值),则通过截取文件链接(url)中最后一个'/'之后的字符串(也就是文件名部分)作为标题,将最终确定的标题赋值给变量title,用于后续构建要返回的文件信息对象中的标题部分
+ var title = lis[i].getAttribute('data-title') || url.substr(url.lastIndexOf('/') + 1);
+ // 将一个包含标题(title)和链接(url)信息的对象添加到list数组中,这样就构建好了一个代表被选中文件相关信息的对象,后续可以将整个list数组返回,供其他地方使用这些文件信息进行相应的插入等操作
+ list.push({
+ title: title,
+ url: url
+ });
+ }
+ }
+ // 返回包含所有被选中文件相关信息(标题和链接)的列表(list),以便在其他代码中可以获取并根据这些信息进行相应的处理(比如插入到编辑器中的相应位置等操作)
+ return list;
+}
+};
+
+
+})();
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/background/background.css b/public2/ueditor/dialogs/background/background.css
new file mode 100644
index 0000000..ae32a8b
--- /dev/null
+++ b/public2/ueditor/dialogs/background/background.css
@@ -0,0 +1,222 @@
+/* 以下是类名为'wrapper'的元素的样式规则 */
+.wrapper{
+ width: 424px; /* 设置元素的宽度为424px */
+ margin: 10px auto; /* 上下外边距为10px,左右外边距自动,使元素在父容器中水平居中 */
+ zoom:1; /* 触发IE浏览器的hasLayout属性,用于解决一些布局相关的兼容性问题,通常和浮动、定位等布局方式结合使用 */
+ position: relative; /* 将元素的定位方式设置为相对定位,相对其原本在文档流中的位置进行定位调整,方便后续子元素基于此进行绝对定位等操作 */
+}
+
+/* 以下是类名为'tabbody'的元素的样式规则 */
+.tabbody{
+ height:225px; /* 设置元素的高度为225px */
+}
+
+/* 以下是类名为'tabbody'下的类名为'panel'的子元素的样式规则 */
+.tabbody.panel {
+ position: absolute; /* 将元素设置为绝对定位,其位置将基于最近的已定位(非 static 定位)的祖先元素来确定,如果没有则相对于 body 元素定位 */
+ width:100%; /* 宽度占满父元素的宽度 */
+ height:100%; /* 高度占满父元素的高度 */
+ background: #fff; /* 设置背景颜色为白色 */
+ display: none; /* 初始状态下不显示该元素,可通过后续添加类名等方式改变显示状态 */
+}
+
+/* 以下是类名为'tabbody'下具有'focus'类名的元素的样式规则,可能用于切换显示不同面板等交互场景 */
+.tabbody.focus {
+ display: block; /* 当元素具有'focus'类名时,显示该元素,覆盖上面'.panel'中设置的'display: none'样式 */
+}
+
+/* 以下是 body 元素的样式规则,会应用到整个页面文档 */
+body{
+ font-size: 12px; /* 设置页面默认的字体大小为12px */
+ color: #888; /* 设置页面文本颜色为灰色(#888) */
+ overflow: hidden; /* 隐藏页面的滚动条,防止内容超出可视区域时出现滚动条 */
+}
+
+/* 以下是 input 和 label 元素的样式规则,使它们在垂直方向上居中对齐 */
+input,label{
+ vertical-align:middle /* 设置元素在垂直方向上的对齐方式为居中对齐,常用于表单元素等在同一行显示时的布局调整 */
+}
+
+/* 以下是类名为'clear'的元素的样式规则,用于清除浮动带来的影响,确保父元素能正确包裹浮动的子元素,保持布局的完整性 */
+.clear{
+ clear: both; /* 清除左右两侧的浮动元素影响,使该元素不受之前浮动元素的干扰,另起一行显示 */
+}
+
+/* 以下是类名为'pl'的元素的样式规则,通过属性选择器 hack(\9)为IE浏览器单独设置左边距,用于适配不同浏览器下的布局差异 */
+.pl{
+ padding-left: 18px;
+ padding-left: 23px\9; /* 在IE浏览器下,设置左边距为23px,正常浏览器下为18px */
+}
+
+/* 以下是 id 为'imageList'的元素的样式规则 */
+#imageList {
+ width: 420px; /* 设置元素的宽度为420px */
+ height: 215px; /* 设置元素的高度为215px */
+ margin-top: 10px; /* 设置元素的上外边距为10px,使其与上方元素间隔一定距离 */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏 */
+ overflow-y: auto; /* 允许垂直方向出现滚动条以查看超出部分内容,常用于展示较多内容且希望在垂直方向可滚动查看的区域 */
+}
+
+/* 以下是 id 为'imageList'下的 div 元素的样式规则 */
+#imageList div {
+ float: left; /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+ width: 100px; /* 设置元素的宽度为100px */
+ height: 95px; /* 设置元素的高度为95px */
+ margin: 5px 10px; /* 设置元素的外边距,上下外边距为5px,左右外边距为10px,用于控制元素之间的间隔距离 */
+}
+
+/* 以下是 id 为'imageList'下的 img 元素的样式规则 */
+#imageList img {
+ cursor: pointer; /* 设置鼠标指针样式为手型,提示用户该图片元素可点击交互 */
+ border: 2px solid white; /* 为图片添加白色的2px边框 */
+}
+
+/* 以下是类名为'bgarea'的元素的样式规则 */
+.bgarea{
+ margin: 10px; /* 设置元素的上下左右外边距均为10px,使其与周围元素间隔一定距离 */
+ padding: 5px; /* 设置元素的内边距为5px,在元素内部四周添加间隔空间 */
+ height: 84%; /* 设置元素的高度占父元素高度的84%,用于根据父元素高度自适应自身高度 */
+ border: 1px solid #A8A297; /* 为元素添加1px的边框,边框颜色为#A8A297 */
+}
+
+/* 以下是类名为'content'下的 div 元素的样式规则 */
+.content div{
+ margin: 10px 0 10px 5px; /* 设置元素的外边距,上外边距和下外边距为10px,左外边距为5px,右外边距为0,用于控制元素在水平和垂直方向的间隔位置 */
+}
+
+/* 以下是类名为'content'下的类名为'iptradio'的元素的样式规则 */
+.content.iptradio{
+ margin: 0px 5px 5px 0px; /* 设置元素的外边距,上外边距为0px,右外边距为5px,下外边距为5px,左外边距为0px,用于控制元素在水平和垂直方向的间隔位置 */
+}
+
+/* 以下是类名为'txt'的元素的样式规则 */
+.txt{
+ width:280px; /* 设置元素的宽度为280px */
+}
+
+/* 以下是类名为'wrapcolor'的元素的样式规则 */
+.wrapcolor{
+ height: 19px; /* 设置元素的高度为19px */
+}
+
+/* 以下是类名为'color'的 div 元素的样式规则 */
+div.color{
+ float: left; /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+ margin: 0; /* 设置元素的外边距为0,使其紧密排列 */
+}
+
+/* 以下是 id 为'colorPicker'的元素的样式规则 */
+#colorPicker{
+ width: 17px; /* 设置元素的宽度为17px */
+ height: 17px; /* 设置元素的高度为17px */
+ border: 1px solid #CCC; /* 为元素添加1px的边框,边框颜色为#CCC */
+ display: inline-block; /* 将元素设置为行内块级元素,使其可以在一行内与其他行内元素或行内块元素一起排列,同时又能设置宽度、高度等块级元素的属性 */
+ border-radius: 3px; /* 设置元素的边框圆角半径为3px,使其边角呈现圆形效果 */
+ box-shadow: 2px 2px 5px #D3D6DA; /* 为元素添加阴影效果,水平和垂直方向偏移2px,模糊半径为5px,阴影颜色为#D3D6DA */
+ margin: 0; /* 设置元素的外边距为0,使其紧密排列 */
+ float: left; /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+}
+
+/* 以下是类名为'alignment'的 div 元素以及 id 为'custom'的元素的样式规则,通过属性选择器 hack(\9)为IE浏览器单独设置左边距,用于适配不同浏览器下的布局差异 */
+div.alignment,#custom{
+ margin-left: 23px;
+ margin-left: 28px\9; /* 在IE浏览器下,设置左边距为28px,正常浏览器下为23px */
+}
+
+/* 以下是 id 为'custom'下的 input 元素的样式规则 */
+#custom input{
+ height: 15px; /* 设置元素的高度为15px */
+ min-height: 15px; /* 设置元素的最小高度为15px,确保在某些情况下元素高度不会小于此值 */
+ width:20px; /* 设置元素的宽度为20px */
+}
+
+/* 以下是 id 为'repeatType'的元素的样式规则 */
+#repeatType{
+ width:100px; /* 设置元素的宽度为100px */
+}
+
+/* 以下是 id 为'imgManager'的元素的样式规则,用于图片管理相关的区域样式设置 */
+#imgManager {
+ width: 100%; /* 宽度占满父元素的宽度 */
+ height: 225px; /* 设置元素的高度为225px */
+}
+
+/* 以下是 id 为'imgManager'下的 id 为'imageList'的子元素的样式规则 */
+#imgManager #imageList{
+ width: 100%; /* 宽度占满父元素的宽度 */
+ overflow-x: hidden; /* 隐藏水平方向的溢出内容 */
+ overflow-y: auto; /* 允许垂直方向出现滚动条以查看超出部分内容,常用于展示较多图片且希望在垂直方向可滚动查看的区域 */
+}
+
+/* 以下是 id 为'imgManager'下的 ul 元素的样式规则 */
+#imgManager ul {
+ display: block; /* 将元素显示为块级元素,独占一行,常用于列表等元素的布局设置 */
+ list-style: none; /* 清除默认的列表样式标记(如圆点、数字等) */
+ margin: 0; /* 设置元素的外边距为0,使其紧密排列 */
+ padding: 0; /* 设置元素的内边距为0,去除默认的内边距 */
+}
+
+/* 以下是 id 为'imgManager'下的 li 元素的样式规则 */
+#imgManager li {
+ float: left; /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+ display: block; /* 将元素显示为块级元素,独占一行,常用于列表等元素的布局设置 */
+ list-style: none; /* 清除默认的列表样式标记(如圆点、数字等) */
+ padding: 0; /* 设置元素的内边距为0,去除默认的内边距 */
+ width: 113px; /* 设置元素的宽度为113px */
+ height: 113px; /* 设置元素的高度为113px */
+ margin: 9px 0 0 19px; /* 设置元素的外边距,上外边距为9px,右外边距为0,下外边距为0,左外边距为19px,用于控制元素之间的间隔距离 */
+ background-color: #eee; /* 设置元素的背景颜色为浅灰色(#eee) */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏 */
+ cursor: pointer; /* 设置鼠标指针样式为手型,提示用户该列表项元素可点击交互 */
+ position: relative; /* 将元素的定位方式设置为相对定位,相对其原本在文档流中的位置进行定位调整,方便后续内部绝对定位元素基于此进行定位 */
+}
+
+/* 以下是 id 为'imgManager'下具有'clearFloat'类名的 li 元素的样式规则,用于清除浮动带来的影响,确保父元素能正确包裹浮动的子元素,保持布局的完整性 */
+#imgManager li.clearFloat {
+ float: none; /* 取消元素的浮动属性 */
+ clear: both; /* 清除左右两侧的浮动元素影响,使该元素不受之前浮动元素的干扰,另起一行显示 */
+ display: block; /* 将元素显示为块级元素,独占一行 */
+ width:0; /* 设置元素的宽度为0 */
+ height:0; /* 设置元素的高度为0 */
+ margin: 0; /* 设置元素的外边距为0 */
+ padding: 0; /* 设置元素的内边距为0 */
+}
+
+/* 以下是 id 为'imgManager'下的 li 元素内的 img 元素的样式规则 */
+#imgManager li img {
+ cursor: pointer; /* 设置鼠标指针样式为手型,提示用户该图片元素可点击交互 */
+}
+
+/* 以下是 id 为'imgManager'下的 li 元素内的类名为'icon'的元素的样式规则 */
+#imgManager li.icon {
+ cursor: pointer; /* 设置鼠标指针样式为手型,提示用户该元素可点击交互 */
+ width: 113px; /* 设置元素的宽度为113px */
+ height: 113px; /* 设置元素的高度为113px */
+ position: absolute; /* 将元素设置为绝对定位,其位置将基于最近的已定位(非 static 定位)的祖先元素来确定,如果没有则相对于 li 元素定位 */
+ top: 0; /* 基于父元素(li 元素)顶部定位,垂直方向距离顶部0px */
+ left: 0; /* 基于父元素(li 元素)左侧定位,水平方向距离左侧0px */
+ z-index: 2; /* 设置元素的层叠顺序为2,使其在一定程度上可以覆盖其他层叠顺序较低的元素显示 */
+ border: 0; /* 设置元素的边框宽度为0,即无边框 */
+ background-repeat: no-repeat; /* 设置背景图片不重复平铺 */
+}
+
+/* 以下是 id 为'imgManager'下的 li 元素内的类名为'icon'的元素在鼠标悬停时的样式规则 */
+#imgManager li.icon:hover {
+ width: 107px; /* 鼠标悬停时,设置元素的宽度为107px */
+ height: 107px; /* 鼠标悬停时,设置元素的高度为107px */
+ border: 3px solid #1094fa; /* 鼠标悬停时,为元素添加3px的边框,边框颜色为#1094fa */
+}
+
+/* 以下是 id 为'imgManager'下具有'selected'类名的 li 元素内的类名为'icon'的元素的样式规则 */
+#imgManager li.selected.icon {
+ background-image: url(images/success.png); /* 设置元素的背景图片,用于显示特定的选中标识等视觉效果 */
+ background-position: 75px 75px; /* 设置背景图片在元素内的定位位置,水平和垂直方向均距离元素左上角75px */
+}
+
+/* 以下是 id 为'imgManager'下具有'selected'类名且鼠标悬停的 li 元素内的类名为'icon'的元素的样式规则 */
+#imgManager li.selected.icon:hover {
+ width: 107px; /* 鼠标悬停时,设置元素的宽度为107px */
+ height: 107px; /* 鼠标悬停时,设置元素的高度为107px */
+ border: 3px solid #1094fa; /* 鼠标悬停时,为元素添加3px的边框,边框颜色为#1094fa */
+ background-position: 72px 72px; /* 设置背景图片在元素内的定位位置,水平和垂直方向均距离元素左上角72px,与未悬停时的背景位置有所变化,用于提供悬停交互的视觉效果变化 */
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/background/background.html b/public2/ueditor/dialogs/background/background.html
new file mode 100644
index 0000000..6e4f5a1
--- /dev/null
+++ b/public2/ueditor/dialogs/background/background.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/charts/chart.config.js b/public2/ueditor/dialogs/charts/chart.config.js
new file mode 100644
index 0000000..ecfec6b
--- /dev/null
+++ b/public2/ueditor/dialogs/charts/chart.config.js
@@ -0,0 +1,91 @@
+/*
+ * 图表配置文件
+ * 此文件定义了多种不同类型图表的配置信息,可能用于图表绘制库(如 Highcharts、ECharts 等,具体依赖使用场景)来创建具有特定样式和交互功能的图表。
+ */
+
+// 定义一个名为 typeConfig 的数组,用于存储不同类型图表的配置对象。每个对象对应一种图表类型的相关配置设置。
+var typeConfig = [
+ {
+ chart: {
+ type: 'line'
+ },
+ plotOptions: {
+ line: {
+ dataLabels: {
+ enabled: false
+ },
+ enableMouseTracking: true
+ // 以下是对内部配置项的解释:
+ // dataLabels.enabled: 设置是否显示数据标签(例如在折线上每个数据点对应的数值标签),这里设置为 false,表示不显示。
+ // enableMouseTracking: 设置是否启用鼠标跟踪功能,当设置为 true 时,鼠标悬停在图表元素(如折线的线段、数据点等)上时可能会触发相应的交互效果(如显示提示信息等)。
+ }
+ }
+ },
+ {
+ chart: {
+ type: 'line'
+ },
+ plotOptions: {
+ line: {
+ dataLabels: {
+ enabled: true
+ },
+ enableMouseTracking: false
+ // 对于这个配置对象:
+ // dataLabels.enabled: 设置为 true,表示显示数据标签,会在折线上相应的数据点位置显示对应的数值等信息。
+ // enableMouseTracking: 设置为 false,即禁用鼠标跟踪功能,鼠标悬停在图表元素上不会触发额外的交互效果。
+ }
+ }
+ },
+ {
+ chart: {
+ type: 'area'
+ }
+ // 这个配置对象仅设置了图表类型为 'area'(面积图),可能使用默认的其他配置项(具体取决于使用的图表库的默认配置规则)来绘制面积图,后续如果需要可以继续添加更多如颜色、样式、交互等相关配置在此对象内。
+ },
+ {
+ chart: {
+ type: 'bar'
+ }
+ // 配置图表类型为 'bar'(柱状图),同样可能依赖图表库默认配置来展示柱状图,如需个性化设置(如柱子颜色、宽度、间距等),可在此对象内添加对应配置项。
+ },
+ {
+ chart: {
+ type: 'column'
+ }
+ // 设置图表类型为 'column'(也是柱状图的一种常见表示形式,与 'bar' 在某些图表库中有细微区别,比如方向等,具体看库的实现),可根据需求进一步完善其详细配置内容。
+ },
+ {
+ chart: {
+ plotBackgroundColor: null,
+ plotBorderWidth: null,
+ plotShadow: false
+ },
+ plotOptions: {
+ pie: {
+ allowPointSelect: true,
+ cursor: 'pointer',
+ dataLabels: {
+ enabled: true,
+ color: '#000000',
+ connectorColor: '#000000',
+ formatter: function() {
+ return ''+ this.point.name +' : '+ ( Math.round( this.point.percentage*100 ) / 100 ) +' %';
+ }
+ }
+ }
+ // 对于此配置对象的详细解释:
+ // chart 部分的配置:
+ // plotBackgroundColor: 设置图表绘图区域的背景颜色,这里设置为 null,可能表示使用默认背景颜色(具体由图表库决定)。
+ // plotBorderWidth: 设置绘图区域边框宽度,null 值通常也意味着使用默认边框宽度设定(取决于图表库)。
+ // plotShadow: 设置绘图区域是否显示阴影效果,false 表示不显示阴影。
+ // plotOptions.pie 部分的配置(针对饼图的相关设置):
+ // allowPointSelect: 设置是否允许选中饼图中的单个数据点(扇区),true 表示允许,用户可以通过交互(如点击)来选中某个扇区。
+ // cursor: 设置鼠标指针在饼图区域上的样式,'pointer' 通常表示鼠标指针变为手型,提示用户此处可进行交互操作。
+ // dataLabels.enabled: 设置为 true,表示显示数据标签,即在饼图的每个扇区上显示相应的文字说明。
+ // dataLabels.color: 设置数据标签的文字颜色为黑色('#000000')。
+ // dataLabels.connectorColor: 设置数据标签与扇区之间连接线的颜色为黑色('#000000')。
+ // dataLabels.formatter: 这是一个函数,用于自定义数据标签的显示内容格式。在这里,它返回的格式是将扇区对应的名称加粗显示,后面跟着该扇区占比的百分比数值(保留两位小数),例如 "类别A : 25.00 %"。
+ }
+ }
+];
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/charts/charts.css b/public2/ueditor/dialogs/charts/charts.css
new file mode 100644
index 0000000..dbe207a
--- /dev/null
+++ b/public2/ueditor/dialogs/charts/charts.css
@@ -0,0 +1,192 @@
+/* 对 html 和 body 元素设置通用样式 */
+html, body {
+ width: 100%; /* 设置元素宽度占满整个视口宽度 */
+ height: 100%; /* 设置元素高度占满整个视口高度 */
+ margin: 0; /* 清除元素默认的外边距,使页面从边缘开始布局 */
+ padding: 0; /* 清除元素默认的内边距 */
+ overflow-x: hidden; /* 隐藏水平方向上的滚动条,防止内容超出可视区域时出现水平滚动条 */
+}
+
+/* 定义类名为'main'的元素的样式 */
+.main {
+ width: 100%; /* 宽度占满父元素宽度 */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+}
+
+/* 定义类名为'table-view'的元素的样式 */
+.table-view {
+ height: 100%; /* 高度占满父元素高度(结合父元素的高度设置情况而定) */
+ float: left; /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+ margin: 20px; /* 设置元素的外边距为上下左右各 20px,使其与周围元素间隔一定距离 */
+ width: 40%; /* 设置元素宽度占父元素宽度的 40%,用于划分页面布局中表格部分的宽度占比 */
+}
+
+/* 定义类名为'table-view'下的类名为'table-container'的子元素的样式 */
+.table-view.table-container {
+ width: 100%; /* 宽度占满父元素(.table-view)的宽度 */
+ margin-bottom: 50px; /* 设置元素的下外边距为 50px,使其与下方元素间隔一定距离 */
+ overflow: scroll; /* 当内容超出元素尺寸范围时,显示滚动条以便查看全部内容 */
+}
+
+/* 定义类名为'table-view'下的 th 元素(通常用于表格表头)的样式 */
+.table-view th {
+ padding: 5px 10px; /* 设置元素的内边距,上下内边距为 5px,左右内边距为 10px,用于在表格表头单元格内提供一定的空白空间 */
+ background-color: #F7F7F7; /* 设置元素的背景颜色为浅灰色(#F7F7F7),用于区分表头与表体部分 */
+}
+
+/* 定义类名为'table-view'下的 td 元素(通常用于表格表体单元格)的样式 */
+.table-view td {
+ width: 50px; /* 设置元素的宽度为 50px,统一表格单元格的宽度 */
+ text-align: center; /* 设置文本在单元格内居中对齐 */
+ padding: 0; /* 清除元素默认的内边距,使内容紧密贴合单元格边缘 */
+}
+
+/* 定义类名为'table-container'下的 input 元素的样式 */
+.table-container input {
+ width: 40px; /* 设置输入框元素的宽度为 40px */
+ padding: 5px; /* 设置输入框元素的内边距为 5px,使输入内容与边框有一定间隔 */
+ border: none; /* 清除输入框的边框,使其外观更简洁(可能通过其他方式体现选中或聚焦状态等) */
+ outline: none; /* 清除输入框获取焦点时的默认外边框样式,同样是为了外观简洁或自定义聚焦效果 */
+}
+
+/* 定义类名为'table-view'下的 caption 元素(通常用于表格标题)的样式 */
+.table-view caption {
+ font-size: 18px; /* 设置元素的字体大小为 18px,突出显示表格标题 */
+ text-align: left; /* 设置文本在标题元素内左对齐 */
+}
+
+/* 定义类名为'charts-view'的元素的样式 */
+.charts-view {
+ /* margin-left: 49%!important; */ /* 此处被注释掉了,原作用可能是通过强制设置左外边距为父元素宽度的 49%来进行布局定位,但当前未生效,实际以下面的'margin-left'属性为准 */
+ width: 50%; /* 设置元素宽度占父元素宽度的 50%,用于划分页面布局中图表部分的宽度占比 */
+ margin-left: 49%; /* 设置元素的左外边距为父元素宽度的 49%,将图表部分定位在页面右侧,与左侧的表格部分区分开来 */
+ height: 400px; /* 设置元素的高度为 400px,确定图表区域的高度大小 */
+}
+
+/* 定义类名为'charts-container'的元素的样式 */
+.charts-container {
+ border-left: 1px solid #c3c3c3; /* 为元素添加左边框,边框宽度为 1px,颜色为灰色(#c3c3c3),可能用于区分图表区域与其他部分 */
+}
+
+/* 定义类名为'charts-format'下的 fieldset 元素的样式 */
+.charts-format fieldset {
+ padding-left: 20px; /* 设置元素的左内边距为 20px,用于在内部提供一定的空白空间 */
+ margin-bottom: 50px; /* 设置元素的下外边距为 50px,使其与下方元素间隔一定距离 */
+}
+
+/* 定义类名为'charts-format'下的 legend 元素的样式 */
+.charts-format legend {
+ padding-left: 10px; /* 设置元素的左内边距为 10px,在元素内部左侧提供一定空白空间 */
+ padding-right: 10px; /* 设置元素的右内边距为 10px,在元素内部右侧提供一定空白空间 */
+}
+
+/* 定义类名为'format-item-container'的元素的样式 */
+.format-item-container {
+ padding: 20px; /* 设置元素的内边距为 20px,在元素内部四周提供一定的空白空间 */
+}
+
+/* 定义类名为'format-item-container'下的 label 元素的样式 */
+.format-item-container label {
+ display: block; /* 将元素显示为块级元素,独占一行,常用于表单标签等元素的布局,方便与对应的输入框等元素进行垂直排列 */
+ margin: 10px 0; /* 设置元素的外边距,上下外边距为 10px,左右外边距为 0,用于控制元素在垂直方向的间隔位置 */
+}
+
+/* 定义类名为'charts-format'下的类名为'data-item'的元素的样式 */
+.charts-format.data-item {
+ border: 1px solid black; /* 为元素添加边框,边框宽度为 1px,颜色为黑色,用于突出显示该元素 */
+ outline: none; /* 清除元素获取焦点时的默认外边框样式,可能是为了外观简洁或自定义聚焦效果 */
+ padding: 2px 3px; /* 设置元素的内边距,上下内边距为 2px,左右内边距为 3px,用于在元素内部提供一定的空白空间 */
+}
+
+/* 以下是图表类型相关的样式定义 */
+
+/* 定义类名为'charts-type'的元素的样式 */
+.charts-type {
+ margin-top: 50px; /* 设置元素的上外边距为 50px,使其与上方元素间隔一定距离 */
+ height: 300px; /* 设置元素的高度为 300px,确定图表类型相关区域的高度大小 */
+}
+
+/* 定义类名为'scroll-view'的元素的样式 */
+.scroll-view {
+ border: 1px solid #c3c3c3; /* 为元素添加边框,边框宽度为 1px,颜色为灰色(#c3c3c3) */
+ border-left: none; /* 清除元素的左边框,使其左边框不显示 */
+ border-right: none; /* 清除元素的右边框,使其右边框不显示 */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+}
+
+/* 定义类名为'scroll-container'的元素的样式 */
+.scroll-container {
+ margin: 20px; /* 设置元素的外边距为上下左右各 20px,使其与周围元素间隔一定距离 */
+ width: 100%; /* 宽度占满父元素宽度 */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+}
+
+/* 定义类名为'scroll-bed'的元素的样式 */
+.scroll-bed {
+ width: 10000px; /* 设置元素的宽度为一个较大值,可能用于实现滚动效果时容纳较多的内容(比如多个图表类型的展示元素等) */
+ _margin-top: 20px; /* 此处是一个私有属性(前面加下划线,可能是针对特定浏览器的 hack,如 IE 浏览器),设置元素的上外边距为 20px */
+ -webkit-transition: margin-left.5s ease; /* 针对webkit 内核浏览器(如 Chrome、Safari 等)设置当'margin-left'属性改变时的过渡效果,过渡时间为 0.5 秒,过渡动画为缓动效果(ease) */
+ -moz-transition: margin-left.5s ease; /* 针对 Mozilla Firefox 浏览器设置当'margin-left'属性改变时的过渡效果,过渡时间为 0.5 秒,过渡动画为缓动效果(ease) */
+ transition: margin-left.5s ease; /* 针对其他现代浏览器设置当'margin-left'属性改变时的过渡效果,过渡时间为 0.5 秒,过渡动画为缓动效果(ease) */
+}
+
+/* 定义类名为'view-box'的元素的样式 */
+.view-box {
+ display: inline-block; /* 将元素设置为行内块级元素,使其可以在一行内与其他行内元素或行内块元素一起排列,同时又能设置宽度、高度等块级元素的属性 */
+ *display: inline; /* 此处是针对低版本 IE 浏览器(IE7 及以下)的 hack,使其以行内元素显示,确保兼容性 */
+ *zoom: 1; /* 同样是针对低版本 IE 浏览器的 hack,触发 hasLayout 属性,解决一些布局相关的兼容性问题 */
+ margin-right: 20px; /* 设置元素的右外边距为 20px,使其与右侧相邻元素间隔一定距离 */
+ border: 2px solid white; /* 为元素添加边框,边框宽度为 2px,颜色为白色,用于视觉上区分不同的视图框元素 */
+ line-height: 0; /* 设置元素的行高为 0,可能用于去除元素内部默认的垂直间距等情况,具体看元素内部内容结构 */
+ overflow: hidden; /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+ cursor: pointer; /* 设置鼠标指针样式为手型,提示用户该元素可点击交互 */
+}
+
+/* 定义类名为'view-box'下的 img 元素的样式 */
+.view-box img {
+ border: 1px solid #cecece; /* 为图片元素添加边框,边框宽度为 1px,颜色为浅灰色(#cecece),用于视觉上对图片进行修饰 */
+}
+
+/* 定义类名为'view-box'下具有'selected'类名的元素的样式 */
+.view-box.selected {
+ border-color: #7274A7; /* 当元素具有'selected'类名时(可能表示被选中状态),改变其边框颜色为特定颜色(#7274A7),用于突出显示被选中的视图框元素 */
+}
+
+/* 定义类名为'button-container'的元素的样式 */
+.button-container {
+ margin-bottom: 20px; /* 设置元素的下外边距为 20px,使其与下方元素间隔一定距离 */
+ text-align: center; /* 设置元素内部的子元素在水平方向上居中对齐,常用于按钮组等元素的布局,使其在容器内居中显示 */
+}
+
+/* 定义类名为'button-container'下的 a 元素(通常用于链接或按钮样式)的样式 */
+.button-container a {
+ display: inline-block; /* 将元素设置为行内块级元素,使其可以在一行内与其他行内元素或行内块元素一起排列,同时又能设置宽度、高度等块级元素的属性 */
+ width: 100px; /* 设置元素的宽度为 100px,确定按钮或链接的宽度大小 */
+ height: 25px; /* 设置元素的高度为 25px,确定按钮或链接的高度大小 */
+ line-height: 25px; /* 设置元素的行高与元素高度相等,使文本在垂直方向上居中对齐 */
+ border: 1px solid #c2ccd1; /* 为元素添加边框,边框宽度为 1px,颜色为浅灰色(#c2ccd1),用于视觉上修饰按钮或链接元素 */
+ margin-right: 30px; /* 设置元素的右外边距为 30px,用于控制按钮或链接之间的间隔距离 */
+ text-decoration: none; /* 清除元素默认的文本下划线装饰(对于链接元素而言),使其外观更简洁 */
+ color: black; /* 设置元素的文本颜色为黑色 */
+ -webkit-border-radius: 2px; /* 针对 webkit 内核浏览器设置元素的边框圆角半径为 2px,使其边角呈现圆形效果 */
+ -moz-border-radius: 2px; /* 针对 Mozilla Firefox 浏览器设置元素的边框圆角半径为 2px,使其边角呈现圆形效果 */
+ border-radius: 2px; /* 针对其他现代浏览器设置元素的边框圆角半径为 2px,使其边角呈现圆形效果 */
+}
+
+/* 定义类名为'button-container'下的 a 元素在鼠标悬停时的样式 */
+.button-container a:HOVER {
+ background: #fcfcfc; /* 当鼠标悬停在按钮或链接元素上时,改变其背景颜色为浅白色(#fcfcfc),用于提供悬停交互的视觉反馈 */
+}
+
+/* 定义类名为'button-container'下的 a 元素在激活(如鼠标按下)时的样式 */
+.button-container a:ACTIVE {
+ border-top-color: #c2ccd1; /* 当按钮或链接元素处于激活状态时,改变其顶部边框的颜色为浅灰色(#c2ccd1),用于提供点击交互的视觉反馈 */
+ box-shadow: inset 0 5px 4px -4px rgba(49, 49, 64, 0.1); /* 为元素添加内阴影效果,水平和垂直方向偏移量、模糊半径等参数设置了阴影的样式,用于增强激活状态的视觉效果 */
+}
+
+/* 定义类名为'edui-charts-not-data'的元素的样式 */
+.edui-charts-not-data {
+ height: 100px; /* 设置元素的高度为 100px */
+ line-height: 100px; /* 设置元素的行高与元素高度相等,使文本在垂直方向上居中对齐 */
+ text-align: center; /* 设置元素内部的文本在水平方向上居中对齐 */
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/charts/charts.html b/public2/ueditor/dialogs/charts/charts.html
new file mode 100644
index 0000000..9ae625a
--- /dev/null
+++ b/public2/ueditor/dialogs/charts/charts.html
@@ -0,0 +1,137 @@
+
+
+
+
+ chart
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/charts/charts.js b/public2/ueditor/dialogs/charts/charts.js
new file mode 100644
index 0000000..9458bd6
--- /dev/null
+++ b/public2/ueditor/dialogs/charts/charts.js
@@ -0,0 +1,520 @@
+/*
+ * 图片转换对话框脚本
+ **/
+// 定义一个空数组,用于存储表格数据,后续会在脚本执行过程中填充具体的数据值
+var tableData = [],
+ //编辑器页面table
+ editorTable = null,
+ chartsConfig = window.typeConfig,
+ resizeTimer = null,
+ //初始默认图表类型
+ currentChartType = 0;
+
+// 当页面加载完成后执行的函数,用于初始化页面中的各种元素和事件绑定等操作
+window.onload = function () {
+
+ editorTable = domUtils.findParentByTagName( editor.selection.getRange().startContainer, 'table', true);
+
+ //未找到表格, 显示错误页面
+ if ( !editorTable ) {
+ document.body.innerHTML = "未找到数据
";
+ return;
+ }
+
+ //初始化图表类型选择
+ initChartsTypeView();
+ renderTable( editorTable );
+ initEvent();
+ initUserConfig( editorTable.getAttribute( "data-chart" ) );
+ $( "#scrollBed .view-box:eq("+ currentChartType +")" ).trigger( "click" );
+ updateViewType( currentChartType );
+
+ dialog.addListener( "resize", function () {
+
+ if ( resizeTimer != null ) {
+ window.clearTimeout( resizeTimer );
+ }
+
+ resizeTimer = window.setTimeout( function () {
+
+ resizeTimer = null;
+
+ renderCharts();
+
+ }, 500 );
+
+ } );
+
+};
+
+function initChartsTypeView () {
+
+ var contents = [];
+
+ for ( var i = 0, len = chartsConfig.length; i ' );
+
+ }
+
+ $( "#scrollBed" ).html( contents.join( "" ) );
+
+}
+
+//渲染table, 以便用户修改数据
+function renderTable ( table ) {
+
+ var tableHtml = [];
+
+ //构造数据
+ for ( var i = 0, row; row = table.rows[ i ]; i++ ) {
+
+ tableData[ i ] = [];
+ tableHtml[ i ] = [];
+
+ for ( var j = 0, cell; cell = row.cells[ j ]; j++ ) {
+
+ var value = getCellValue( cell );
+
+ if ( i > 0 && j > 0 ) {
+ value = +value;
+ }
+
+ if ( i === 0 || j === 0 ) {
+ tableHtml[ i ].push( ' '+ value +' ' );
+ } else {
+ tableHtml[ i ].push( '
' );
+ }
+
+ tableData[ i ][ j ] = value;
+
+ }
+
+ tableHtml[ i ] = tableHtml[ i ].join( "" );
+
+ }
+
+ //draw 表格
+ $( "#tableContainer" ).html( '
'+ tableHtml.join( " " ) +'
' );
+
+}
+
+/*
+ * 根据表格已有的图表属性初始化当前图表属性
+ */
+function initUserConfig ( config ) {
+
+ var parsedConfig = {};
+
+ if ( !config ) {
+ return;
+ }
+
+ config = config.split( ";" );
+
+ $.each( config, function ( index, item ) {
+
+ item = item.split( ":" );
+ parsedConfig[ item[ 0 ] ] = item[ 1 ];
+
+ } );
+
+ setUserConfig( parsedConfig );
+
+}
+
+function initEvent () {
+
+ var cacheValue = null,
+ //图表类型数
+ typeViewCount = chartsConfig.length- 1,
+ $chartsTypeViewBox = $( '#scrollBed .view-box' );
+
+ $( ".charts-format" ).delegate( ".format-ctrl", "change", function () {
+
+ renderCharts();
+
+ } )
+
+ $( ".table-view" ).delegate( ".data-item", "focus", function () {
+
+ cacheValue = this.value;
+
+ } ).delegate( ".data-item", "blur", function () {
+
+ if ( this.value !== cacheValue ) {
+ renderCharts();
+ }
+
+ cacheValue = null;
+
+ } );
+
+ $( "#buttonContainer" ).delegate( "a", "click", function (e) {
+
+ e.preventDefault();
+
+ if ( this.getAttribute( "data-title" ) === 'prev' ) {
+
+ if ( currentChartType > 0 ) {
+ currentChartType--;
+ updateViewType( currentChartType );
+ }
+
+ } else {
+
+ if ( currentChartType < typeViewCount ) {
+ currentChartType++;
+ updateViewType( currentChartType );
+ }
+
+ }
+
+ } );
+
+ //图表类型变化
+ $( '#scrollBed' ).delegate( ".view-box", "click", function (e) {
+
+ var index = $( this ).attr( "data-chart-type" );
+ $chartsTypeViewBox.removeClass( "selected" );
+ $( $chartsTypeViewBox[ index ] ).addClass( "selected" );
+
+ currentChartType = index | 0;
+
+ //饼图, 禁用部分配置
+ if ( currentChartType === chartsConfig.length - 1 ) {
+
+ disableNotPieConfig();
+
+ //启用完整配置
+ } else {
+
+ enableNotPieConfig();
+
+ }
+
+ renderCharts();
+
+ } );
+
+}
+
+function renderCharts () {
+
+ var data = collectData();
+
+ $('#chartsContainer').highcharts( $.extend( {}, chartsConfig[ currentChartType ], {
+
+ credits: {
+ enabled: false
+ },
+ exporting: {
+ enabled: false
+ },
+ title: {
+ text: data.title,
+ x: -20 //center
+ },
+ subtitle: {
+ text: data.subTitle,
+ x: -20
+ },
+ xAxis: {
+ title: {
+ text: data.xTitle
+ },
+ categories: data.categories
+ },
+ yAxis: {
+ title: {
+ text: data.yTitle
+ },
+ plotLines: [{
+ value: 0,
+ width: 1,
+ color: '#808080'
+ }]
+ },
+ tooltip: {
+ enabled: true,
+ valueSuffix: data.suffix
+ },
+ legend: {
+ layout: 'vertical',
+ align: 'right',
+ verticalAlign: 'middle',
+ borderWidth: 1
+ },
+ series: data.series
+
+ } ));
+
+}
+
+function updateViewType ( index ) {
+
+ $( "#scrollBed" ).css( 'marginLeft', -index*324+'px' );
+
+}
+
+function collectData () {
+
+ var form = document.forms[ 'data-form' ],
+ data = null;
+
+ if ( currentChartType !== chartsConfig.length - 1 ) {
+
+ data = getSeriesAndCategories();
+ $.extend( data, getUserConfig() );
+
+ //饼图数据格式
+ } else {
+ data = getSeriesForPieChart();
+ data.title = form[ 'title' ].value;
+ data.suffix = form[ 'unit' ].value;
+ }
+
+ return data;
+
+}
+
+/**
+ * 获取用户配置信息
+ */
+function getUserConfig () {
+
+ var form = document.forms[ 'data-form' ],
+ info = {
+ title: form[ 'title' ].value,
+ subTitle: form[ 'sub-title' ].value,
+ xTitle: form[ 'x-title' ].value,
+ yTitle: form[ 'y-title' ].value,
+ suffix: form[ 'unit' ].value,
+ //数据对齐方式
+ tableDataFormat: getTableDataFormat (),
+ //饼图提示文字
+ tip: $( "#tipInput" ).val()
+ };
+
+ return info;
+
+}
+
+function setUserConfig ( config ) {
+
+ var form = document.forms[ 'data-form' ];
+
+ config.title && ( form[ 'title' ].value = config.title );
+ config.subTitle && ( form[ 'sub-title' ].value = config.subTitle );
+ config.xTitle && ( form[ 'x-title' ].value = config.xTitle );
+ config.yTitle && ( form[ 'y-title' ].value = config.yTitle );
+ config.suffix && ( form[ 'unit' ].value = config.suffix );
+ config.dataFormat == "-1" && ( form[ 'charts-format' ][ 1 ].checked = true );
+ config.tip && ( form[ 'tip' ].value = config.tip );
+ currentChartType = config.chartType || 0;
+
+}
+
+function getSeriesAndCategories () {
+
+ var form = document.forms[ 'data-form' ],
+ series = [],
+ categories = [],
+ tmp = [],
+ tableData = getTableData();
+
+ //反转数据
+ if ( getTableDataFormat() === "-1" ) {
+
+ for ( var i = 0, len = tableData.length; i < len; i++ ) {
+
+ for ( var j = 0, jlen = tableData[ i ].length; j < jlen; j++ ) {
+
+ if ( !tmp[ j ] ) {
+ tmp[ j ] = [];
+ }
+
+ tmp[ j ][ i ] = tableData[ i ][ j ];
+
+ }
+
+ }
+
+ tableData = tmp;
+
+ }
+
+ categories = tableData[0].slice( 1 );
+
+ for ( var i = 1, data; data = tableData[ i ]; i++ ) {
+
+ series.push( {
+ name: data[ 0 ],
+ data: data.slice( 1 )
+ } );
+
+ }
+
+ return {
+ series: series,
+ categories: categories
+ };
+
+}
+
+/*
+ * 获取数据源数据对齐方式
+ */
+function getTableDataFormat () {
+
+ var form = document.forms[ 'data-form' ],
+ items = form['charts-format'];
+
+ return items[ 0 ].checked ? items[ 0 ].value : items[ 1 ].value;
+
+}
+
+/*
+ * 禁用非饼图类型的配置项
+ */
+function disableNotPieConfig() {
+
+ updateConfigItem( 'disable' );
+
+}
+
+/*
+ * 启用非饼图类型的配置项
+ */
+function enableNotPieConfig() {
+
+ updateConfigItem( 'enable' );
+
+}
+
+function updateConfigItem ( value ) {
+
+ var table = $( "#showTable" )[ 0 ],
+ isDisable = value === 'disable' ? true : false;
+
+ //table中的input处理
+ for ( var i = 2 , row; row = table.rows[ i ]; i++ ) {
+
+ for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) {
+
+ $( "input", cell ).attr( "disabled", isDisable );
+
+ }
+
+ }
+
+ //其他项处理
+ $( "input.not-pie-item" ).attr( "disabled", isDisable );
+ $( "#tipInput" ).attr( "disabled", !isDisable )
+
+}
+
+/*
+ * 获取饼图数据
+ * 饼图的数据只取第一行的
+ **/
+function getSeriesForPieChart () {
+
+ var series = {
+ type: 'pie',
+ name: $("#tipInput").val(),
+ data: []
+ },
+ tableData = getTableData();
+
+
+ for ( var j = 1, jlen = tableData[ 0 ].length; j < jlen; j++ ) {
+
+ var title = tableData[ 0 ][ j ],
+ val = tableData[ 1 ][ j ];
+
+ series.data.push( [ title, val ] );
+
+ }
+
+ return {
+ series: [ series ]
+ };
+
+}
+
+function getTableData () {
+
+ var table = document.getElementById( "showTable" ),
+ xCount = table.rows[0].cells.length - 1,
+ values = getTableInputValue();
+
+ for ( var i = 0, value; value = values[ i ]; i++ ) {
+
+ tableData[ Math.floor( i / xCount ) + 1 ][ i % xCount + 1 ] = values[ i ];
+
+ }
+
+ return tableData;
+
+}
+
+function getTableInputValue () {
+
+ var table = document.getElementById( "showTable" ),
+ inputs = table.getElementsByTagName( "input" ),
+ values = [];
+
+ for ( var i = 0, input; input = inputs[ i ]; i++ ) {
+ values.push( input.value | 0 );
+ }
+
+ return values;
+
+}
+
+function getCellValue ( cell ) {
+
+ var value = utils.trim( ( cell.innerText || cell.textContent || '' ) );
+
+ return value.replace( new RegExp( UE.dom.domUtils.fillChar, 'g' ), '' ).replace( /^\s+|\s+$/g, '' );
+
+}
+
+
+//dialog确认事件
+dialog.onok = function () {
+
+ //收集信息
+ var form = document.forms[ 'data-form' ],
+ info = getUserConfig();
+
+ //添加图表类型
+ info.chartType = currentChartType;
+
+ //同步表格数据到编辑器
+ syncTableData();
+
+ //执行图表命令
+ editor.execCommand( 'charts', info );
+
+};
+
+/*
+ * 同步图表编辑视图的表格数据到编辑器里的原始表格
+ */
+function syncTableData () {
+
+ var tableData = getTableData();
+
+ for ( var i = 1, row; row = editorTable.rows[ i ]; i++ ) {
+
+ for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) {
+
+ cell.innerHTML = tableData[ i ] [ j ];
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/emotion/emotion.css b/public2/ueditor/dialogs/emotion/emotion.css
new file mode 100644
index 0000000..6aec179
--- /dev/null
+++ b/public2/ueditor/dialogs/emotion/emotion.css
@@ -0,0 +1,185 @@
+/* 选择类名为 'jd' 下的 img 元素,并设置其样式 */
+.jd img{
+ background:transparent url(images/jxface2.gif?v=1.1) no-repeat scroll left top;
+ /* 设置背景为透明,背景图片为 'images/jxface2.gif'(版本号为 v=1.1,可能用于缓存控制等情况),背景图片不重复显示,并且滚动时背景固定在左上角位置 */
+ cursor:pointer;
+ /* 设置鼠标指针样式为手型,提示用户该元素可点击交互 */
+ width:35px;
+ /* 设置元素宽度为 35 像素 */
+ height:35px;
+ /* 设置元素高度为 35 像素 */
+ display:block;
+ /* 将元素显示为块级元素,独占一行,方便进行布局排版等操作 */
+}
+
+/* 选择类名为 'pp' 下的 img 元素,并设置其样式 */
+.pp img{
+ background:transparent url(images/fface.gif?v=1.1) no-repeat scroll left top;
+ /* 类似上面的背景设置逻辑,只是背景图片路径为 'images/fface.gif',同样不重复、固定在左上角 */
+ cursor:pointer;
+ width:25px;
+ /* 设置元素宽度为 25 像素,与上面 'jd' 下的 img 元素宽度不同,体现不同类名下元素样式差异 */
+ height:25px;
+ /* 设置元素高度为 25 像素 */
+ display:block;
+}
+
+/* 选择类名为 'ldw' 下的 img 元素,并设置其样式 */
+.ldw img{
+ background:transparent url(images/wface.gif?v=1.1) no-repeat scroll left top;
+ cursor:pointer;
+ width:35px;
+ height:35px;
+ display:block;
+}
+
+/* 选择类名为 'tsj' 下的 img 元素,并设置其样式 */
+.tsj img{
+ background:transparent url(images/tface.gif?v=1.1) no-repeat scroll left top;
+ cursor:pointer;
+ width:35px;
+ height:35px;
+ display:block;
+}
+
+/* 选择类名为 'cat' 下的 img 元素,并设置其样式 */
+.cat img{
+ background:transparent url(images/cface.gif?v=1.1) no-repeat scroll left top;
+ cursor:pointer;
+ width:35px;
+ height:35px;
+ display:block;
+}
+
+/* 选择类名为 'bb' 下的 img 元素,并设置其样式 */
+.bb img{
+ background:transparent url(images/bface.gif?v=1.1) no-repeat scroll left top;
+ cursor:pointer;
+ width:35px;
+ height:35px;
+ display:block;
+}
+
+/* 选择类名为 'youa' 下的 img 元素,并设置其样式 */
+.youa img{
+ background:transparent url(images/yface.gif?v=1.1) no-repeat scroll left top;
+ cursor:pointer;
+ width:35px;
+ height:35px;
+ display:block;
+}
+
+/* 选择类名为'smileytable' 下的 td 元素,并设置其样式 */
+.smileytable td {
+ height: 37px;
+ /* 设置表格单元格(td)的高度为 37 像素,统一该类表格单元格的高度尺寸 */
+}
+
+/* 选择 id 为 'tabPanel' 的元素,并设置其样式 */
+#tabPanel{
+ margin-left:5px;
+ /* 设置元素的左外边距为 5 像素,使其与左侧相邻元素间隔一定距离 */
+ overflow: hidden;
+ /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+}
+
+/* 选择 id 为 'tabContent' 的元素,并设置其样式 */
+#tabContent {
+ float:left;
+ /* 使元素向左浮动,常用于实现多栏布局,让后续元素围绕其进行排列 */
+ background:#FFFFFF;
+ /* 设置元素的背景颜色为白色(#FFFFFF) */
+}
+
+/* 选择 id 为 'tabContent' 下的 div 元素,并设置其样式 */
+#tabContent div{
+ display: none;
+ /* 初始状态下将这些 div 元素隐藏起来,可能通过后续 JavaScript 等操作来控制显示 */
+ width:480px;
+ /* 设置元素宽度为 480 像素 */
+ overflow:hidden;
+ /* 超出元素尺寸范围的内容将被隐藏,防止出现滚动条等影响布局 */
+}
+
+/* 选择类名为 '#tabIconReview' 且具有'show' 类名的元素,并设置其样式 */
+#tabIconReview.show{
+ left:17px;
+ /* 设置元素的左定位位置为 17 像素,改变其在页面中的水平位置(基于其定位上下文,此处应该是相对定位或绝对定位的情况) */
+ display:block;
+ /* 将元素显示为块级元素并显示出来,覆盖之前可能的隐藏状态(可能是通过添加或移除'show' 类名来控制显示隐藏切换) */
+}
+
+/* 选择类名为'menuFocus' 的元素,并设置其样式 */
+.menuFocus{
+ background:#ACCD3C;
+ /* 设置元素的背景颜色为特定的绿色(#ACCD3C),可能用于突出显示处于焦点状态的菜单元素等 */
+}
+
+/* 选择类名为'menuDefault' 的元素,并设置其样式 */
+.menuDefault{
+ background:#FFFFFF;
+ /* 设置元素的背景颜色为白色(#FFFFFF),可能作为默认的菜单背景颜色,与'menuFocus' 的背景色形成对比,用于区分不同状态 */
+}
+
+/* 选择 id 为 'tabIconReview' 的元素,并设置其样式 */
+#tabIconReview{
+ position:absolute;
+ /* 将元素设置为绝对定位,使其可以根据 'left'、'top' 等定位属性精确地在页面中定位,脱离文档流 */
+ left:406px;
+ left:398px \9;
+ /* 设置元素的左定位位置,对于大多数浏览器使用 406px,针对 IE9 及以下版本浏览器(通过 \9 这个 hack)使用 398px,用于解决不同浏览器下的兼容性问题 */
+ top:41px;
+ /* 设置元素的垂直定位位置为 41 像素,确定其在页面中的垂直方向位置 */
+ z-index:65533;
+ /* 设置元素的堆叠顺序(z-index)为一个较大的值 65533,使其在页面中显示时能够覆盖在很多其他元素之上,常用于弹出层、提示框等需要显示在最上层的元素 */
+ width:90px;
+ /* 设置元素宽度为 90 像素 */
+ height:76px;
+ /* 设置元素高度为 76 像素 */
+}
+
+/* 选择类名为'review' 的 img 元素,并设置其样式 */
+img.review{
+ width:90px;
+ height:76px;
+ border:2px solid #9cb945;
+ /* 为元素添加边框,边框宽度为 2 像素,颜色为特定的绿色(#9cb945),用于视觉上突出显示该元素 */
+ background:#FFFFFF;
+ background-position:center;
+ background-repeat:no-repeat;
+ /* 设置背景颜色为白色,背景图片在元素内部居中显示且不重复,这里的背景相关设置可能用于显示特定的图片内容,具体看是否有对应的背景图片设置(前面代码中未体现完整背景图片路径部分,可能在其他地方有补充或者通过 JavaScript 动态设置等情况) */
+}
+
+/* 选择类名为 'wrapper' 下的类名为 'tabbody' 的元素,并设置其样式 */
+.wrapper.tabbody{
+ position:relative;
+ /* 将元素设置为相对定位,为其子元素的绝对定位等操作提供相对的定位上下文,同时相对定位元素本身在文档流中的位置会保留,不会像绝对定位那样完全脱离文档流 */
+ float:left;
+ clear:both;
+ /* 使元素向左浮动,并且清除两侧的浮动元素影响,确保该元素按照期望的布局排列,常用于解决浮动元素造成的布局混乱问题 */
+ padding:10px;
+ /* 设置元素的内边距为 10 像素,在元素内部四周提供一定的空白空间 */
+ width: 95%;
+ /* 设置元素宽度占父元素宽度的 95%,用于控制该元素在布局中的宽度占比 */
+}
+
+/* 选择类名为 'tabbody' 下的 table 元素,并设置其样式 */
+.tabbody table{
+ width: 100%;
+ /* 设置表格宽度占满父元素宽度,使其自适应父元素的宽度大小 */
+}
+
+/* 选择类名为 'tabbody' 下的 td 元素,并设置其样式 */
+.tabbody td{
+ border:1px solid #BAC498;
+ /* 为表格单元格(td)添加边框,边框宽度为 1 像素,颜色为特定的浅灰色(#BAC498),用于区分单元格之间的界限等 */
+}
+
+/* 选择类名为 'tabbody' 下的 td 元素内的 span 元素,并设置其样式 */
+.tabbody td span{
+ display: block;
+ zoom:1;
+ /* 将 span 元素显示为块级元素(display: block),并通过 zoom:1 触发 hasLayout 属性(针对低版本 IE 浏览器的兼容性处理,用于解决一些布局相关问题),使其能像块级元素一样正常布局和设置样式 */
+ padding:0 4px;
+ /* 设置元素的内边距,左右内边距为 4 像素,上下内边距为 0,在元素内部水平方向提供一定的空白空间 */
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/emotion/emotion.html b/public2/ueditor/dialogs/emotion/emotion.html
new file mode 100644
index 0000000..1e59385
--- /dev/null
+++ b/public2/ueditor/dialogs/emotion/emotion.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/emotion/emotion.js b/public2/ueditor/dialogs/emotion/emotion.js
new file mode 100644
index 0000000..ff3f81c
--- /dev/null
+++ b/public2/ueditor/dialogs/emotion/emotion.js
@@ -0,0 +1,212 @@
+// 当页面加载完成后执行的函数,用于初始化页面中与表情相关的各种设置、元素创建以及事件绑定等操作
+window.onload = function () {
+ // 设置编辑器(editor,可能是自定义的富文本编辑器对象)的配置选项,将 'emotionLocalization' 设置为 false,表示表情相关资源的本地化属性为否,具体影响后续表情图片等资源的获取路径
+ editor.setOpt({
+ emotionLocalization: false
+ });
+
+ // 根据编辑器的 'emotionLocalization' 选项值来确定表情图片的路径(SmileyPath),如果为 true,则从本地相对路径 'images/' 获取,否则从指定的网络路径 "http://img.baidu.com/hi/" 获取
+ emotion.SmileyPath = editor.options.emotionLocalization === true? 'images/' : "http://img.baidu.com/hi/";
+ // 创建一个包含指定数量(emotion.tabNum)的空数组对象,用于存储不同面板(tab)下的相关信息(比如每个面板对应的表情图片文件名等),每个属性名以 'tab' 加数字的形式表示(如 'tab0'、'tab1' 等)
+ emotion.SmileyBox = createTabList(emotion.tabNum);
+ // 创建一个包含指定数量(emotion.tabNum)的数组,数组元素初始值都为 0,用于标记每个面板(tab)是否已经创建或存在相关内容,后续根据这个标记来决定是否需要重新创建对应面板的内容
+ emotion.tabExist = createArr(emotion.tabNum);
+
+ // 初始化表情图片名称相关的数据,比如根据已有的图片前缀名等信息生成完整的图片文件名列表,填充到对应的面板数组(emotion.SmileyBox)中
+ initImgName();
+ // 初始化事件处理函数,为特定 id(此处传入 'tabHeads')对应的元素及其子元素绑定点击等事件监听器,实现切换面板等交互功能
+ initEvtHandler("tabHeads");
+};
+
+// 初始化表情图片名称相关数据的函数,用于根据已有的表情图片前缀名(emotion.SmilmgName 中存储的信息)和数量信息,生成完整的表情图片文件名,并填充到对应的面板数组(emotion.SmileyBox)中
+function initImgName() {
+ // 遍历 emotion.SmilmgName 对象的每个属性(pro),每个属性对应一个面板(如 'tab0'、'tab1' 等)
+ for (var pro in emotion.SmilmgName) {
+ var tempName = emotion.SmilmgName[pro],
+ tempBox = emotion.SmileyBox[pro],
+ tempStr = "";
+
+ // 如果当前面板对应的数组(tempBox)已经有元素了(即长度大于 0),则直接返回,不进行重复处理,可能是避免多次初始化同一面板的数据
+ if (tempBox.length) return;
+ // 根据当前面板对应的图片前缀名(tempName[0])和数量(tempName[1])信息,循环生成完整的图片文件名,并添加到当前面板对应的数组(tempBox)中
+ for (var i = 1; i <= tempName[1]; i++) {
+ tempStr = tempName[0];
+ if (i < 10) tempStr = tempStr + '0';
+ tempStr = tempStr + i + '.gif';
+ tempBox.push(tempStr);
+ }
+ }
+}
+
+// 初始化事件处理函数,用于为指定 id(conId)对应的元素及其子元素绑定点击等事件监听器,实现切换面板等交互功能
+function initEvtHandler(conId) {
+ // 通过 $G 函数(可能是自定义的获取 DOM 元素的函数)获取指定 id(conId)对应的元素,此处应该是获取页面中用于切换面板的头部元素集合(如包含各个面板标题的 span 元素的父元素)
+ var tabHeads = $G(conId);
+ // 遍历头部元素集合的每个子节点(childNodes),为符合条件的元素绑定点击事件监听器
+ for (var i = 0, j = 0; i < tabHeads.childNodes.length; i++) {
+ var tabObj = tabHeads.childNodes[i];
+ // 只对节点类型为元素节点(nodeType == 1)的子节点进行操作,过滤掉文本节点、注释节点等非元素类型的节点
+ if (tabObj.nodeType == 1) {
+ // 使用 domUtils.on 函数(可能是自定义的事件绑定工具函数)为当前元素(tabObj)绑定点击事件监听器,点击时执行一个立即执行函数返回的函数,这个函数会调用 switchTab 函数,并传入当前面板的索引(j)作为参数,实现点击切换对应面板的功能,同时 j 自增,用于记录下一个面板的索引
+ domUtils.on(tabObj, "click", (function (index) {
+ return function () {
+ switchTab(index);
+ };
+ })(j));
+ j++;
+ }
+ }
+ // 初始时默认切换到第一个面板(索引为 0),调用 switchTab 函数显示第一个面板的内容
+ switchTab(0);
+ // 将用于显示表情预览的元素(id 为 'tabIconReview')隐藏起来,初始状态下不显示预览内容
+ $G("tabIconReview").style.display = 'none';
+}
+
+// 插入表情图片到编辑器中的函数,根据传入的图片 URL(url)以及触发的事件对象(evt),创建一个包含图片源地址(src)等信息的对象(obj),然后通过编辑器的命令(execCommand)插入图片,并且如果不是按下 Ctrl 键触发的操作,则隐藏相关的弹出对话框(popup.hide)
+function InsertSmiley(url, evt) {
+ var obj = {
+ src: editor.options.emotionLocalization? editor.options.UEDITOR_HOME_URL + "dialogs/emotion/" + url : url
+ };
+ obj._src = obj.src;
+ editor.execCommand('insertimage', obj);
+ if (!evt.ctrlKey) {
+ dialog.popup.hide();
+ }
+}
+
+// 切换面板显示的函数,根据传入的面板索引(index),执行调整面板高度(autoHeight)、创建面板内容(如果不存在)以及切换显示对应面板内容和隐藏其他面板内容等操作
+function switchTab(index) {
+ // 根据传入的面板索引调整对应面板的高度相关样式,比如设置 iframe 和其父元素的高度值,确保合适的显示布局
+ autoHeight(index);
+ // 如果当前面板还不存在(对应 emotion.tabExist 数组中该索引位置的值为 0),则标记该面板已存在(设置为 1),并调用 createTab 函数创建该面板的具体内容(如添加表情图片表格等元素)
+ if (emotion.tabExist[index] == 0) {
+ emotion.tabExist[index] = 1;
+ createTab('tab' + index);
+ }
+ // 获取用于切换面板的头部元素集合(包含各个面板标题的 span 元素)和主体元素集合(包含各个面板具体内容的 div 元素)
+ var tabHeads = $G("tabHeads").getElementsByTagName("span"),
+ tabBodys = $G("tabBodys").getElementsByTagName("div"),
+ i = 0, L = tabHeads.length;
+ // 循环遍历头部和主体元素集合,将所有元素的样式设置为初始状态,即隐藏主体元素(display="none"),清除头部元素的类名(className=""),用于清除之前可能设置的选中类名等样式
+ for (; i < L; i++) {
+ tabHeads[i].className = "";
+ tabBodys[i].style.display = "none";
+ }
+ // 将当前要显示的面板对应的头部元素添加特定的类名(可能用于设置选中样式,如背景色等变化),并显示其对应的主体元素内容
+ tabHeads[index].className = "focus";
+ tabBodys[index].style.display = "block";
+}
+
+// 根据传入的面板索引(index)调整对应面板的高度相关样式的函数,通过设置 iframe 及其父元素的高度值,为不同面板设置不同的合适高度,确保良好的显示布局效果
+function autoHeight(index) {
+ var iframe = dialog.getDom("iframe"),
+ parent = iframe.parentNode.parentNode;
+ switch (index) {
+ case 0:
+ iframe.style.height = "380px";
+ parent.style.height = "392px";
+ break;
+ case 1:
+ iframe.style.height = "220px";
+ parent.style.height = "232px";
+ break;
+ case 2:
+ iframe.style.height = "260px";
+ parent.style.height = "272px";
+ break;
+ case 3:
+ iframe.style.height = "300px";
+ parent.style.height = "312px";
+ break;
+ case 4:
+ iframe.style.height = "140px";
+ parent.style.height = "152px";
+ break;
+ case 5:
+ iframe.style.height = "260px";
+ parent.style.height = "272px";
+ break;
+ case 6:
+ iframe.style.height = "230px";
+ parent.style.height = "242px";
+ break;
+ default:
+
+ }
+}
+
+// 创建特定面板(tabName)内容的函数,主要用于生成包含表情图片、相关提示信息以及鼠标交互事件绑定的表格元素,并添加到对应的面板 div 元素中,实现表情图片的展示和交互功能
+function createTab(tabName) {
+ var faceVersion = "?v=1.1", //版本号
+ tab = $G(tabName), //获取将要生成的 Div 句柄,即对应面板的 div 元素对象,用于后续往里面添加生成的表格内容
+ imagePath = emotion.SmileyPath + emotion.imageFolders[tabName], //获取显示表情和预览表情的路径,通过拼接表情图片的基础路径(SmileyPath)和当前面板对应的文件夹路径(imageFolders[tabName])得到完整路径
+ positionLine = 11 / 2, //中间数,可能用于判断图片在表格中的位置(比如用于控制背景图片的定位等情况,结合后续代码看与鼠标悬停显示预览相关逻辑有关)
+ iWidth = iHeight = 35, //图片长宽,设置表情图片在页面上显示的宽度和高度均为 35 像素
+ iColWidth = 3, //表格剩余空间的显示比例,可能用于设置表格单元格的宽度占比等布局相关参数,影响表情图片在表格中的排列方式
+ tableCss = emotion.imageCss[tabName],
+ cssOffset = emotion.imageCssOffset[tabName],
+ textHTML = ['
'],
+ i = 0, imgNum = emotion.SmileyBox[tabName].length, imgColNum = 11, faceImage,
+ sUrl, realUrl, posflag, offset, infor;
+
+ // 循环遍历当前面板对应的表情图片数量(imgNum),生成表格的行(tr)和列(td)元素,将表情图片、相关提示信息以及鼠标交互事件绑定添加到表格中
+ for (; i < imgNum;) {
+ textHTML.push('');
+ for (var j = 0; j < imgColNum; j++, i++) {
+ faceImage = emotion.SmileyBox[tabName][i];
+ if (faceImage) {
+ sUrl = imagePath + faceImage + faceVersion;
+ realUrl = imagePath + faceImage;
+ posflag = j < positionLine? 0 : 1;
+ offset = cssOffset * i * (-1) - 1;
+ infor = emotion.SmileyInfor[tabName][i];
+
+ textHTML.push('');
+ textHTML.push('');
+ textHTML.push(' ');
+ textHTML.push(' ');
+ } else {
+ textHTML.push(' ');
+ }
+ textHTML.push(' ');
+ }
+ textHTML.push(' ');
+ }
+ textHTML.push('
');
+ textHTML = textHTML.join("");
+ tab.innerHTML = textHTML;
+}
+
+// 鼠标悬停在表情图片所在单元格(td)上时执行的函数,用于改变单元格背景色、显示表情预览图片以及显示表情预览元素(tabIconReview)等操作,实现鼠标悬停时的交互效果
+function over(td, srcPath, posFlag) {
+ td.style.backgroundColor = "#ACCD3C";
+ $G('faceReview').style.backgroundImage = "url(" + srcPath + ")";
+ if (posFlag == 1) $G("tabIconReview").className = "show";
+ $G("tabIconReview").style.display = 'block';
+}
+
+// 鼠标移出表情图片所在单元格(td)时执行的函数,用于恢复单元格背景色为透明、隐藏表情预览元素(tabIconReview)以及清除其相关类名等操作,还原到初始状态
+function out(td) {
+ td.style.backgroundColor = "transparent";
+ var tabIconRevew = $G("tabIconReview");
+ tabIconRevew.className = "";
+ tabIconRevew.style.display = 'none';
+}
+
+// 创建一个包含指定数量(tabNum)的空数组对象的函数,每个属性名以 'tab' 加数字的形式表示(如 'tab0'、'tab1' 等),用于后续存储不同面板相关的信息,初始时每个数组都是空的
+function createTabList(tabNum) {
+ var obj = {};
+ for (var i = 0; i < tabNum; i++) {
+ obj["tab" + i] = [];
+ }
+ return obj;
+}
+
+// 创建一个包含指定数量(tabNum)的数组,数组元素初始值都为 0 的函数,用于标记每个面板(tab)是否已经创建或存在相关内容,后续根据这个标记来决定是否需要重新创建对应面板的内容
+function createArr(tabNum) {
+ var arr = [];
+ for (var i = 0; i < tabNum; i++) {
+ arr[i] = 0;
+ }
+ return arr;
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/gmap/gmap.html b/public2/ueditor/dialogs/gmap/gmap.html
new file mode 100644
index 0000000..c120e86
--- /dev/null
+++ b/public2/ueditor/dialogs/gmap/gmap.html
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/help/help.css b/public2/ueditor/dialogs/help/help.css
new file mode 100644
index 0000000..61ba8a2
--- /dev/null
+++ b/public2/ueditor/dialogs/help/help.css
@@ -0,0 +1,61 @@
+/* 选择类名为 'wrapper' 的元素,并设置其样式 */
+.wrapper{
+ width: 370px;
+ /* 设置元素的宽度为 370 像素,用于控制该元素在页面中的水平尺寸大小 */
+ margin: 10px auto;
+ /* 设置元素的上下外边距为 10 像素,左右外边距自动(auto),使元素在水平方向上自动居中,常用于将某个模块在页面中水平居中显示 */
+ zoom: 1;
+ /* 通过设置 zoom 属性为 1,触发元素的 hasLayout 属性(主要针对低版本 IE 浏览器的兼容性处理),以解决一些布局相关的问题,确保元素能按照预期的样式和布局规则进行显示 */
+}
+
+/* 选择类名为 'tabbody' 的元素,并设置其样式 */
+.tabbody{
+ height: 360px;
+ /* 设置元素的高度为 360 像素,用于限定该元素在页面中的垂直尺寸大小 */
+}
+
+/* 选择类名为 'tabbody' 下的类名为 'panel' 的元素,并设置其样式 */
+.tabbody.panel{
+ width:100%;
+ /* 设置元素的宽度占满其父元素的宽度,使其自适应父元素的宽度大小,常用于实现布局上的全屏或全宽效果 */
+ height: 360px;
+ /* 设置元素的高度为 360 像素,与父元素(.tabbody)的高度保持一致,可能用于创建具有固定高度的面板区域 */
+ position: absolute;
+ /* 将元素设置为绝对定位,使其可以根据 'left'、'top' 等定位属性精确地在页面中定位,脱离文档流,方便进行层叠布局以及与其他元素的位置重叠等效果实现 */
+ background: #fff;
+ /* 设置元素的背景颜色为白色(#fff),用于提供一个清晰的背景视觉效果 */
+}
+
+/* 选择类名为 'tabbody' 下的类名为 'panel' 的元素内部的 h1 标题元素,并设置其样式 */
+.tabbody.panel h1{
+ font-size:26px;
+ /* 设置 h1 标题元素的字体大小为 26 像素,用于控制标题的文字大小,使其更加醒目突出 */
+ margin: 5px 0 0 5px;
+ /* 设置元素的上、左外边距为 5 像素,下、右外边距为 0,使标题在面板内有一定的内边距间隔,呈现出合适的排版位置 */
+}
+
+/* 选择类名为 'tabbody' 下的类名为 'panel' 的元素内部的 p 段落元素,并设置其样式 */
+.tabbody.panel p{
+ font-size:12px;
+ /* 设置 p 段落元素的字体大小为 12 像素,通常用于正文内容的文字大小设置,使其与标题等元素区分开来,保持合适的层次结构 */
+ margin: 5px 0 0 5px;
+ /* 同样设置元素的上、左外边距为 5 像素,下、右外边距为 0,让段落文字在面板内有合适的排版间隔 */
+}
+
+/* 选择类名为 'tabbody' 下的 table 表格元素,并设置其样式 */
+.tabbody table{
+ width:90%;
+ /* 设置表格的宽度占其父元素宽度的 90%,使其在父元素内按一定比例自适应宽度,而不是占满整个父元素宽度,提供一定的布局灵活性 */
+ line-height: 20px;
+ /* 设置表格内行高为 20 像素,用于控制表格内文字行与行之间的垂直间距,影响文字的排版效果和可读性 */
+ margin: 5px 0 0 5px;
+ /* 设置表格的上、左外边距为 5 像素,下、右外边距为 0,让表格在父元素内有一定的间隔位置,呈现出合适的布局效果 */
+}
+
+/* 选择类名为 'tabbody' 下的 table 表格元素内部的 thead 表头元素,并设置其样式 */
+.tabbody table thead{
+ font-weight: bold;
+ /* 设置表头元素内文字的字体粗细为粗体(bold),使其在视觉上与表格主体内容区分开来,更突出表头的重要性和标识作用 */
+ line-height: 25px;
+ /* 设置表头行的行高为 25 像素,通常表头可能需要更大一点的行高来保证文字显示效果和美观性,与表格主体行高(前面设置的 20 像素)有所区别 */
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/help/help.html b/public2/ueditor/dialogs/help/help.html
new file mode 100644
index 0000000..818bfc6
--- /dev/null
+++ b/public2/ueditor/dialogs/help/help.html
@@ -0,0 +1,110 @@
+
+
+
+
+
帮助
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ctrl+b
+
+
+
+
+ ctrl+c
+
+
+
+
+ ctrl+x
+
+
+
+
+ ctrl+v
+
+
+
+
+ ctrl+y
+
+
+
+
+ ctrl+z
+
+
+
+
+ ctrl+i
+
+
+
+
+ ctrl+u
+
+
+
+
+ ctrl+a
+
+
+
+
+ shift+enter
+
+
+
+
+ alt+z
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/help/help.js b/public2/ueditor/dialogs/help/help.js
new file mode 100644
index 0000000..31ead54
--- /dev/null
+++ b/public2/ueditor/dialogs/help/help.js
@@ -0,0 +1,69 @@
+/**
+ * Created with JetBrains PhpStorm.
+ * User: xuheng
+ * Date: 12-9-26
+ * Time: 下午1:06
+ * To change this template use File | Settings | File Templates.
+ */
+// 以上部分是代码的创建相关信息注释,说明了代码是使用 JetBrains PhpStorm 创建,以及作者、创建日期和时间等信息,对代码本身功能无实质影响。
+
+// 定义一个名为 'clickHandler' 的函数,用于处理 tab 点击相关的操作,比如改变头部元素的样式以及控制对应主体内容的显示隐藏和层级关系等。
+// @param tabHeads :传入的参数,表示 tab 头部元素的集合(通常是一组用于切换不同 tab 的按钮或链接等元素组成的数组)。
+// @param tabBodys :传入的参数,表示 tab 主体内容的集合(通常是一组对应不同 tab 的详细内容展示区域的元素组成的数组)。
+// @param obj :传入的参数,代表当前被点击的 tab 头部元素对象,用于针对该特定元素进行样式等相关操作。
+function clickHandler( tabHeads, tabBodys, obj ) {
+ //head样式更改
+ // 循环遍历 tab 头部元素集合(tabHeads),将每个元素的类名(className)设置为空字符串,目的是清除之前可能存在的任何样式类,用于后续重新设置样式。
+ for ( var k = 0, len = tabHeads.length; k < len; k++ ) {
+ tabHeads[k].className = "";
+ }
+ // 将当前被点击的 tab 头部元素(obj)的类名设置为 "focus",可能在 CSS 中有对应的样式定义,用于突出显示当前选中的 tab 头部,比如改变背景色、字体颜色等样式来体现选中状态。
+ obj.className = "focus";
+
+ //body显隐
+ // 获取当前被点击的 tab 头部元素(obj)上自定义的 'tabSrc' 属性值,该值可能对应着某个 tab 主体内容的唯一标识(比如 id),用于后续查找并显示对应的主体内容。
+ var tabSrc = obj.getAttribute( "tabSrc" );
+ // 循环遍历 tab 主体内容集合(tabBodys),对每个主体内容元素进行相关操作。
+ for ( var j = 0, length = tabBodys.length; j < length; j++ ) {
+ var body = tabBodys[j],
+ id = body.getAttribute( "id" );
+ // 为每个主体内容元素(body)绑定点击事件监听器,当点击主体内容元素时,执行一个匿名函数,函数内将该元素的 'zoom' 属性设置为 1,可能是用于触发某些浏览器特定的布局相关行为(比如在低版本 IE 中用于解决一些布局问题等),具体功能依赖于页面的 CSS 和整体布局设置。
+ body.onclick = function () {
+ this.style.zoom = 1;
+ };
+ // 判断当前主体内容元素的 id 是否与通过 tab 头部元素获取的 'tabSrc' 值不相等,如果不相等,表示不是当前选中 tab 对应的主体内容。
+ if ( id!= tabSrc ) {
+ // 将该主体内容元素的 z-index 属性设置为 1,用于控制元素在页面中的堆叠层级,值为 1 表示较低的层级,可能使其在页面中显示在其他元素下方或者隐藏起来(结合其他 CSS 样式),实现非当前选中 tab 主体内容的隐藏或后置效果。
+ body.style.zIndex = 1;
+ } else {
+ // 如果当前主体内容元素的 id 与 'tabSrc' 值相等,即表示是当前选中 tab 对应的主体内容,则将其 z-index 属性设置为 200,设置较高的层级值,使其能够显示在其他元素之上,保证当前选中 tab 的主体内容处于可见且突出显示的状态。
+ body.style.zIndex = 200;
+ }
+ }
+}
+
+// 定义一个名为'switchTab' 的函数,用于实现整体的 TAB 切换功能,主要是遍历 tab 元素,为每个 tab 头部元素绑定点击事件监听器,点击时调用 'clickHandler' 函数来处理相应的样式改变和主体内容切换显示等操作。
+// @param tabParentId :传入的参数,代表 tab 的父节点 ID 或者传入 tab 所在的父元素对象本身,用于基于此查找 tab 头部和主体元素等相关子元素进行操作。
+function switchTab( tabParentId ) {
+ // 通过 $G 函数(可能是自定义的获取 DOM 元素的函数),根据传入的 tabParentId 获取对应的元素对象,然后获取其所有子元素(children),这些子元素包含了 tab 的头部和主体相关元素集合等信息。
+ var tabElements = $G( tabParentId ).children,
+ tabHeads = tabElements[0].children,
+ tabBodys = tabElements[1].children;
+
+ // 循环遍历 tab 头部元素集合(tabHeads),对每个头部元素进行相关操作。
+ for ( var i = 0, length = tabHeads.length; i < length; i++ ) {
+ var head = tabHeads[i];
+ // 判断当前头部元素的类名是否为 "focus",如果是,表示当前元素已经处于选中状态,直接调用 'clickHandler' 函数传入相应参数进行相关样式和显示处理(可能是页面加载时默认选中某个 tab 的情况需要进行初始化处理)。
+ if ( head.className === "focus" ) clickHandler( tabHeads, tabBodys, head );
+ // 为每个 tab 头部元素绑定点击事件监听器,当点击头部元素时,执行一个匿名函数,函数内调用 'clickHandler' 函数并传入 tab 头部元素集合(tabHeads)、tab 主体内容集合(tabBodys)以及当前被点击的头部元素对象(this)作为参数,实现点击切换 tab 时的样式改变和主体内容切换显示等操作。
+ head.onclick = function () {
+ clickHandler( tabHeads, tabBodys, this );
+ }
+ }
+}
+
+// 调用'switchTab' 函数,并传入 "helptab" 作为参数,启动整个 TAB 切换功能,"helptab" 可能是页面中包含 tab 结构的某个父元素的 id,通过这个调用使得页面加载后就能对相应的 tab 进行切换操作了。
+switchTab( "helptab" );
+
+// 获取页面中 id 为 'version' 的元素(可能是一个用于显示版本信息的 HTML 元素,比如段落元素等),然后将其内部 HTML 内容(innerHTML)设置为 'parent.UE.version' 的值,这里的 'parent' 可能是指父窗口或者某个包含版本信息的上级对象,通过访问其 'UE' 属性下的'version' 值来获取并显示具体的版本号信息,用于在页面上展示相关软件或工具的版本情况。
+document.getElementById( 'version' ).innerHTML = parent.UE.version;
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/image/image.css b/public2/ueditor/dialogs/image/image.css
new file mode 100644
index 0000000..b584c9d
--- /dev/null
+++ b/public2/ueditor/dialogs/image/image.css
@@ -0,0 +1,915 @@
+@charset "utf-8";
+/* 定义该 CSS 文件的字符编码为 UTF-8,确保能够正确解析和显示各种字符,如中文、特殊符号等 */
+/* dialog样式 */
+.wrapper {
+ zoom: 1;
+ /* 通过设置 zoom 属性为 1,触发元素的 hasLayout 属性(主要针对低版本 IE 浏览器的兼容性处理),避免一些布局相关的问题,保证元素按预期布局显示 */
+ width: 630px;
+ /* 设置元素的宽度为 630 像素,用于控制该元素在页面中的水平尺寸大小 */
+ *width: 626px;
+ /* 针对特定浏览器(可能是低版本 IE 浏览器)的 hack 写法,为其设置另一个宽度值 626px,用于解决该浏览器下可能出现的样式差异问题 */
+ height: 380px;
+ /* 设置元素的高度为 380 像素,用于限定该元素在页面中的垂直尺寸大小 */
+ margin: 0 auto;
+ /* 设置元素的上下外边距为 0,左右外边距自动(auto),使元素在水平方向上自动居中,常用于将某个模块在页面中水平居中显示 */
+ padding: 10px;
+ /* 为元素内部添加 10 像素的内边距,在元素内容与边框之间留出一定空间,使内容显示更美观、布局更合理 */
+ position: relative;
+ /* 将元素设置为相对定位,方便基于其自身原始位置进行子元素的定位等操作,且不会脱离文档流,对其他元素的布局影响相对较小 */
+ font-family: sans-serif;
+ /* 设置元素内文本的字体为无衬线字体(sans-serif),提供一种简洁清晰的字体外观风格 */
+}
+
+
+/* tab样式框大小 */
+.tabhead {
+ float: left;
+ /* 将元素向左浮动,使其脱离文档流并按照从左到右的顺序排列,常用于实现多栏布局等效果,与其他浮动元素或非浮动元素相互配合来构建页面布局 */
+}
+.tabbody {
+ width: 100%;
+ /* 设置元素的宽度占满其父元素的宽度,使其自适应父元素的宽度大小,确保能占满可用空间 */
+ height: 346px;
+ /* 设置元素的高度为 346 像素,用于限定该元素在页面中的垂直尺寸大小 */
+ position: relative;
+ /* 将元素设置为相对定位,同样方便基于其自身原始位置进行内部元素的定位等操作,不脱离文档流 */
+ clear: both;
+ /* 清除左右两侧的浮动元素影响,确保该元素在垂直方向上不会与之前的浮动元素产生布局上的重叠等问题,重新开启新的一行布局 */
+}
+
+.tabbody.panel {
+ position: absolute;
+ /* 将元素设置为绝对定位,使其脱离文档流,可以根据 'left'、'top' 等定位属性精确地在页面中定位,常用于实现层叠、覆盖等布局效果 */
+ width: 0;
+ height: 0;
+ /* 初始时将宽度和高度设置为 0,可能是先隐藏该元素或者通过后续的交互(如选中对应的 tab 时)来动态改变其尺寸以显示内容 */
+ background: #fff;
+ /* 设置元素的背景颜色为白色(#fff),提供一个清晰的背景视觉效果 */
+ overflow: hidden;
+ /* 当元素内容超出其设定的宽度和高度范围时,隐藏超出部分的内容,防止内容溢出显示造成布局混乱 */
+ display: none;
+ /* 初始状态下将元素隐藏,不显示在页面中,等待相应的触发条件(比如对应的 tab 被选中)来显示该元素 */
+}
+
+.tabbody.panel.focus {
+ width: 100%;
+ height: 346px;
+ display: block;
+ /* 当元素具有 'focus' 类名时(可能通过 JavaScript 动态添加该类名来表示当前选中的 tab 对应的面板),设置其宽度占满父元素宽度,高度恢复为 346 像素,并将 display 属性设置为 'block',使其显示出来,展示对应的 tab 内容 */
+}
+
+/* 图片对齐方式 */
+.alignBar{
+ float:right;
+ margin-top: 5px;
+ position: relative;
+}
+
+.alignBar .algnLabel{
+ float:left;
+ height: 20px;
+ line-height: 20px;
+}
+
+.alignBar #alignIcon{
+ zoom:1;
+ _display: inline;
+ display: inline-block;
+ position: relative;
+}
+.alignBar #alignIcon span{
+ float: left;
+ cursor: pointer;
+ display: block;
+ width: 19px;
+ height: 17px;
+ margin-right: 3px;
+ margin-left: 3px;
+ background-image: url(./images/alignicon.jpg);
+}
+.alignBar #alignIcon .none-align{
+ background-position: 0 -18px;
+}
+.alignBar #alignIcon .left-align{
+ background-position: -20px -18px;
+}
+.alignBar #alignIcon .right-align{
+ background-position: -40px -18px;
+}
+.alignBar #alignIcon .center-align{
+ background-position: -60px -18px;
+}
+.alignBar #alignIcon .none-align.focus{
+ background-position: 0 0;
+}
+.alignBar #alignIcon .left-align.focus{
+ background-position: -20px 0;
+}
+.alignBar #alignIcon .right-align.focus{
+ background-position: -40px 0;
+}
+.alignBar #alignIcon .center-align.focus{
+ background-position: -60px 0;
+}
+
+
+
+
+/* 远程图片样式 */
+#remote {
+ z-index: 200;
+}
+
+#remote .top{
+ width: 100%;
+ margin-top: 25px;
+}
+#remote .left{
+ display: block;
+ float: left;
+ width: 300px;
+ height:10px;
+}
+#remote .right{
+ display: block;
+ float: right;
+ width: 300px;
+ height:10px;
+}
+#remote .row{
+ margin-left: 20px;
+ clear: both;
+ height: 40px;
+}
+
+#remote .row label{
+ text-align: center;
+ width: 50px;
+ zoom:1;
+ _display: inline;
+ display:inline-block;
+ vertical-align: middle;
+}
+#remote .row label.algnLabel{
+ float: left;
+
+}
+
+#remote input.text{
+ width: 150px;
+ padding: 3px 6px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+#remote input.text:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+}
+#remote #url{
+ width: 500px;
+ margin-bottom: 2px;
+}
+#remote #width,
+#remote #height{
+ width: 20px;
+ margin-left: 2px;
+ margin-right: 2px;
+}
+#remote #border,
+#remote #vhSpace,
+#remote #title{
+ width: 180px;
+ margin-right: 5px;
+}
+#remote #lock{
+}
+#remote #lockicon{
+ zoom: 1;
+ _display:inline;
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: url("../../themes/default/images/lock.gif") -13px -13px no-repeat;
+ vertical-align: middle;
+}
+#remote #preview{
+ clear: both;
+ width: 260px;
+ height: 240px;
+ z-index: 9999;
+ margin-top: 10px;
+ background-color: #eee;
+ overflow: hidden;
+}
+
+/* 上传图片 */
+.tabbody #upload.panel {
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ position: absolute !important;
+ clip: rect(1px, 1px, 1px, 1px);
+ background: #fff;
+ display: block;
+}
+
+.tabbody #upload.panel.focus {
+ width: 100%;
+ height: 346px;
+ display: block;
+ clip: auto;
+}
+
+#upload .queueList {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ overflow: hidden;
+}
+
+#upload p {
+ margin: 0;
+}
+
+.element-invisible {
+ width: 0 !important;
+ height: 0 !important;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+ position: absolute !important;
+ clip: rect(1px, 1px, 1px, 1px);
+}
+
+#upload .placeholder {
+ margin: 10px;
+ border: 2px dashed #e6e6e6;
+ *border: 0px dashed #e6e6e6;
+ height: 172px;
+ padding-top: 150px;
+ text-align: center;
+ background: url(./images/image.png) center 70px no-repeat;
+ color: #cccccc;
+ font-size: 18px;
+ position: relative;
+ top:0;
+ *top: 10px;
+}
+
+#upload .placeholder .webuploader-pick {
+ font-size: 18px;
+ background: #00b7ee;
+ border-radius: 3px;
+ line-height: 44px;
+ padding: 0 30px;
+ *width: 120px;
+ color: #fff;
+ display: inline-block;
+ margin: 0 auto 20px auto;
+ cursor: pointer;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+}
+
+#upload .placeholder .webuploader-pick-hover {
+ background: #00a2d4;
+}
+
+
+#filePickerContainer {
+ text-align: center;
+}
+
+#upload .placeholder .flashTip {
+ color: #666666;
+ font-size: 12px;
+ position: absolute;
+ width: 100%;
+ text-align: center;
+ bottom: 20px;
+}
+
+#upload .placeholder .flashTip a {
+ color: #0785d1;
+ text-decoration: none;
+}
+
+#upload .placeholder .flashTip a:hover {
+ text-decoration: underline;
+}
+
+#upload .placeholder.webuploader-dnd-over {
+ border-color: #999999;
+}
+
+#upload .filelist {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: relative;
+ height: 300px;
+}
+
+#upload .filelist:after {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ clear: both;
+ position: relative;
+}
+
+#upload .filelist li {
+ width: 113px;
+ height: 113px;
+ background: url(./images/bg.png);
+ text-align: center;
+ margin: 9px 0 0 9px;
+ *margin: 6px 0 0 6px;
+ position: relative;
+ display: block;
+ float: left;
+ overflow: hidden;
+ font-size: 12px;
+}
+
+#upload .filelist li p.log {
+ position: relative;
+ top: -45px;
+}
+
+#upload .filelist li p.title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ top: 5px;
+ text-indent: 5px;
+ text-align: left;
+}
+
+#upload .filelist li p.progress {
+ position: absolute;
+ width: 100%;
+ bottom: 0;
+ left: 0;
+ height: 8px;
+ overflow: hidden;
+ z-index: 50;
+ margin: 0;
+ border-radius: 0;
+ background: none;
+ -webkit-box-shadow: 0 0 0;
+}
+
+#upload .filelist li p.progress span {
+ display: none;
+ overflow: hidden;
+ width: 0;
+ height: 100%;
+ background: #1483d8 url(./images/progress.png) repeat-x;
+
+ -webit-transition: width 200ms linear;
+ -moz-transition: width 200ms linear;
+ -o-transition: width 200ms linear;
+ -ms-transition: width 200ms linear;
+ transition: width 200ms linear;
+
+ -webkit-animation: progressmove 2s linear infinite;
+ -moz-animation: progressmove 2s linear infinite;
+ -o-animation: progressmove 2s linear infinite;
+ -ms-animation: progressmove 2s linear infinite;
+ animation: progressmove 2s linear infinite;
+
+ -webkit-transform: translateZ(0);
+}
+
+@-webkit-keyframes progressmove {
+ 0% {
+ background-position: 0 0;
+ }
+ 100% {
+ background-position: 17px 0;
+ }
+}
+
+@-moz-keyframes progressmove {
+ 0% {
+ background-position: 0 0;
+ }
+ 100% {
+ background-position: 17px 0;
+ }
+}
+
+@keyframes progressmove {
+ 0% {
+ background-position: 0 0;
+ }
+ 100% {
+ background-position: 17px 0;
+ }
+}
+
+#upload .filelist li p.imgWrap {
+ position: relative;
+ z-index: 2;
+ line-height: 113px;
+ vertical-align: middle;
+ overflow: hidden;
+ width: 113px;
+ height: 113px;
+
+ -webkit-transform-origin: 50% 50%;
+ -moz-transform-origin: 50% 50%;
+ -o-transform-origin: 50% 50%;
+ -ms-transform-origin: 50% 50%;
+ transform-origin: 50% 50%;
+
+ -webit-transition: 200ms ease-out;
+ -moz-transition: 200ms ease-out;
+ -o-transition: 200ms ease-out;
+ -ms-transition: 200ms ease-out;
+ transition: 200ms ease-out;
+}
+
+#upload .filelist li img {
+ width: 100%;
+}
+
+#upload .filelist li p.error {
+ background: #f43838;
+ color: #fff;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ height: 28px;
+ line-height: 28px;
+ width: 100%;
+ z-index: 100;
+ display:none;
+}
+
+#upload .filelist li .success {
+ display: block;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ height: 40px;
+ width: 100%;
+ z-index: 200;
+ background: url(./images/success.png) no-repeat right bottom;
+ background: url(./images/success.gif) no-repeat right bottom \9;
+}
+
+#upload .filelist li.filePickerBlock {
+ width: 113px;
+ height: 113px;
+ background: url(./images/image.png) no-repeat center 12px;
+ border: 1px solid #eeeeee;
+ border-radius: 0;
+}
+#upload .filelist li.filePickerBlock div.webuploader-pick {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ opacity: 0;
+ background: none;
+ font-size: 0;
+}
+
+#upload .filelist div.file-panel {
+ position: absolute;
+ height: 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#80000000', endColorstr='#80000000') \0;
+ background: rgba(0, 0, 0, 0.5);
+ width: 100%;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+ z-index: 300;
+}
+
+#upload .filelist div.file-panel span {
+ width: 24px;
+ height: 24px;
+ display: inline;
+ float: right;
+ text-indent: -9999px;
+ overflow: hidden;
+ background: url(./images/icons.png) no-repeat;
+ background: url(./images/icons.gif) no-repeat \9;
+ margin: 5px 1px 1px;
+ cursor: pointer;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+#upload .filelist div.file-panel span.rotateLeft {
+ display:none;
+ background-position: 0 -24px;
+}
+
+#upload .filelist div.file-panel span.rotateLeft:hover {
+ background-position: 0 0;
+}
+
+#upload .filelist div.file-panel span.rotateRight {
+ display:none;
+ background-position: -24px -24px;
+}
+
+#upload .filelist div.file-panel span.rotateRight:hover {
+ background-position: -24px 0;
+}
+
+#upload .filelist div.file-panel span.cancel {
+ background-position: -48px -24px;
+}
+
+#upload .filelist div.file-panel span.cancel:hover {
+ background-position: -48px 0;
+}
+
+#upload .statusBar {
+ height: 45px;
+ border-bottom: 1px solid #dadada;
+ margin: 0 10px;
+ padding: 0;
+ line-height: 45px;
+ vertical-align: middle;
+ position: relative;
+}
+
+#upload .statusBar .progress {
+ border: 1px solid #1483d8;
+ width: 198px;
+ background: #fff;
+ height: 18px;
+ position: absolute;
+ top: 12px;
+ display: none;
+ text-align: center;
+ line-height: 18px;
+ color: #6dbfff;
+ margin: 0 10px 0 0;
+}
+#upload .statusBar .progress span.percentage {
+ width: 0;
+ height: 100%;
+ left: 0;
+ top: 0;
+ background: #1483d8;
+ position: absolute;
+}
+#upload .statusBar .progress span.text {
+ position: relative;
+ z-index: 10;
+}
+
+#upload .statusBar .info {
+ display: inline-block;
+ font-size: 14px;
+ color: #666666;
+}
+
+#upload .statusBar .btns {
+ position: absolute;
+ top: 7px;
+ right: 0;
+ line-height: 30px;
+}
+
+#filePickerBtn {
+ display: inline-block;
+ float: left;
+}
+#upload .statusBar .btns .webuploader-pick,
+#upload .statusBar .btns .uploadBtn,
+#upload .statusBar .btns .uploadBtn.state-uploading,
+#upload .statusBar .btns .uploadBtn.state-paused {
+ background: #ffffff;
+ border: 1px solid #cfcfcf;
+ color: #565656;
+ padding: 0 18px;
+ display: inline-block;
+ border-radius: 3px;
+ margin-left: 10px;
+ cursor: pointer;
+ font-size: 14px;
+ float: left;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+#upload .statusBar .btns .webuploader-pick-hover,
+#upload .statusBar .btns .uploadBtn:hover,
+#upload .statusBar .btns .uploadBtn.state-uploading:hover,
+#upload .statusBar .btns .uploadBtn.state-paused:hover {
+ background: #f0f0f0;
+}
+
+#upload .statusBar .btns .uploadBtn,
+#upload .statusBar .btns .uploadBtn.state-paused{
+ background: #00b7ee;
+ color: #fff;
+ border-color: transparent;
+}
+#upload .statusBar .btns .uploadBtn:hover,
+#upload .statusBar .btns .uploadBtn.state-paused:hover{
+ background: #00a2d4;
+}
+
+#upload .statusBar .btns .uploadBtn.disabled {
+ pointer-events: none;
+ filter:alpha(opacity=60);
+ -moz-opacity:0.6;
+ -khtml-opacity: 0.6;
+ opacity: 0.6;
+}
+
+
+
+/* 图片管理样式 */
+#online {
+ width: 100%;
+ height: 336px;
+ padding: 10px 0 0 0;
+}
+#online #imageList{
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: relative;
+}
+#online ul {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+#online li {
+ float: left;
+ display: block;
+ list-style: none;
+ padding: 0;
+ width: 113px;
+ height: 113px;
+ margin: 0 0 9px 9px;
+ *margin: 0 0 6px 6px;
+ background-color: #eee;
+ overflow: hidden;
+ cursor: pointer;
+ position: relative;
+}
+#online li.clearFloat {
+ float: none;
+ clear: both;
+ display: block;
+ width:0;
+ height:0;
+ margin: 0;
+ padding: 0;
+}
+#online li img {
+ cursor: pointer;
+}
+#online li .icon {
+ cursor: pointer;
+ width: 113px;
+ height: 113px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ border: 0;
+ background-repeat: no-repeat;
+}
+#online li .icon:hover {
+ width: 107px;
+ height: 107px;
+ border: 3px solid #1094fa;
+}
+#online li.selected .icon {
+ background-image: url(images/success.png);
+ background-image: url(images/success.gif)\9;
+ background-position: 75px 75px;
+}
+#online li.selected .icon:hover {
+ width: 107px;
+ height: 107px;
+ border: 3px solid #1094fa;
+ background-position: 72px 72px;
+}
+
+
+/* 图片搜索样式 */
+#search .searchBar {
+ width: 100%;
+ height: 30px;
+ margin: 10px 0 5px 0;
+ padding: 0;
+}
+
+#search input.text{
+ width: 150px;
+ padding: 3px 6px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+#search input.text:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+}
+#search input.searchTxt {
+ margin-left:5px;
+ padding-left: 5px;
+ background: #FFF;
+ width: 300px;
+ *width: 260px;
+ height: 21px;
+ line-height: 21px;
+ float: left;
+ dislay: block;
+}
+
+#search .searchType {
+ width: 65px;
+ height: 28px;
+ padding:0;
+ line-height: 28px;
+ border: 1px solid #d7d7d7;
+ border-radius: 0;
+ vertical-align: top;
+ margin-left: 5px;
+ float: left;
+ dislay: block;
+}
+
+#search #searchBtn,
+#search #searchReset {
+ display: inline-block;
+ margin-bottom: 0;
+ margin-right: 5px;
+ padding: 4px 10px;
+ font-weight: 400;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ background-image: none;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ font-size: 14px;
+ border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ vertical-align: top;
+ float: right;
+}
+
+#search #searchBtn {
+ color: white;
+ border-color: #285e8e;
+ background-color: #3b97d7;
+}
+#search #searchReset {
+ color: #333;
+ border-color: #ccc;
+ background-color: #fff;
+}
+#search #searchBtn:hover {
+ background-color: #3276b1;
+}
+#search #searchReset:hover {
+ background-color: #eee;
+}
+
+#search .msg {
+ margin-left: 5px;
+}
+
+#search .searchList{
+ width: 100%;
+ height: 300px;
+ overflow: hidden;
+ clear: both;
+}
+#search .searchList ul{
+ margin:0;
+ padding:0;
+ list-style:none;
+ clear: both;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ zoom: 1;
+ position: relative;
+}
+
+#search .searchList li {
+ list-style:none;
+ float: left;
+ display: block;
+ width: 115px;
+ margin: 5px 10px 5px 20px;
+ *margin: 5px 10px 5px 15px;
+ padding:0;
+ font-size: 12px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
+ position: relative;
+ vertical-align: top;
+ text-align: center;
+ overflow: hidden;
+ cursor: pointer;
+ filter: alpha(Opacity=100);
+ -moz-opacity: 1;
+ opacity: 1;
+ border: 2px solid #eee;
+}
+
+#search .searchList li.selected {
+ filter: alpha(Opacity=40);
+ -moz-opacity: 0.4;
+ opacity: 0.4;
+ border: 2px solid #00a0e9;
+}
+
+#search .searchList li p {
+ background-color: #eee;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ width:100%;
+ height:115px;
+ overflow: hidden;
+}
+
+#search .searchList li p img {
+ cursor: pointer;
+ border: 0;
+}
+
+#search .searchList li a {
+ color: #999;
+ border-top: 1px solid #F2F2F2;
+ background: #FAFAFA;
+ text-align: center;
+ display: block;
+ padding: 0 5px;
+ width: 105px;
+ height:32px;
+ line-height:32px;
+ white-space:nowrap;
+ text-overflow:ellipsis;
+ text-decoration: none;
+ overflow: hidden;
+ word-break: break-all;
+}
+
+#search .searchList a:hover {
+ text-decoration: underline;
+ color: #333;
+}
+#search .searchList .clearFloat{
+ clear: both;
+}
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/image/image.html b/public2/ueditor/dialogs/image/image.html
new file mode 100644
index 0000000..949604c
--- /dev/null
+++ b/public2/ueditor/dialogs/image/image.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+
ueditor图片对话框
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public2/ueditor/dialogs/image/image.js b/public2/ueditor/dialogs/image/image.js
new file mode 100644
index 0000000..f9607f4
--- /dev/null
+++ b/public2/ueditor/dialogs/image/image.js
@@ -0,0 +1,1134 @@
+/**
+ * User: Jinqn
+ * Date: 14-04-08
+ * Time: 下午16:34
+ * 上传图片对话框逻辑代码,包括tab: 远程图片/上传图片/在线图片/搜索图片
+ * 以上部分是代码头部的注释信息,说明了代码的作者、创建日期和时间,以及简要介绍了代码的功能是用于处理上传图片对话框相关的逻辑,涵盖了不同功能的选项卡(tab)操作等内容。
+ */
+
+(function () {
+ // 创建一个立即执行的函数表达式(IIFE),用于创建一个独立的作用域,避免变量污染全局作用域,使得内部定义的变量和函数只能在这个闭包内部访问和使用,保证代码的模块化和独立性。
+
+ var remoteImage,
+ uploadImage,
+ onlineImage,
+ searchImage;
+ // 声明四个变量,分别用于存储不同类型图片相关的实例对象(后续可能会根据不同的 tab 操作创建对应的实例,比如远程图片实例、上传图片实例等),初始值都为 undefined,它们的具体实例化会在相应的逻辑中根据用户操作和条件进行。
+
+ window.onload = function () {
+ initTabs();
+ initAlign();
+ initButtons();
+ };
+ // 当页面加载完成(window.onload 事件触发)时,执行一个匿名函数,在这个匿名函数内依次调用三个函数:initTabs()、initAlign() 和 initButtons(),分别用于初始化选项卡(tab)相关操作、图片对齐相关操作以及按钮相关的操作,这几个函数的具体定义在后续代码中,整体实现了在页面加载后对上传图片对话框各功能模块的初始化设置。
+
+ /* 初始化tab标签 */
+ function initTabs() {
+ var tabs = $G('tabhead').children;
+ // 通过调用 $G 函数(可能是自定义的获取 DOM 元素的函数)获取页面中 id 为 'tabhead' 的元素,并获取其所有子元素(children),这些子元素就是各个 tab 标签对应的 DOM 节点,将它们存储在 tabs 变量中,用于后续遍历操作。
+ for (var i = 0; i < tabs.length; i++) {
+ domUtils.on(tabs[i], "click", function (e) {
+ var target = e.target || e.srcElement;
+ setTabFocus(target.getAttribute('data-content-id'));
+ });
+ }
+ // 循环遍历 tabs 数组中的每个 tab 标签元素,使用 domUtils.on 函数(可能是自定义的事件绑定工具函数)为每个 tab 标签元素绑定 'click' 点击事件监听器,当点击某个 tab 标签时,会执行一个匿名函数。在这个匿名函数内,首先获取当前点击的目标元素(e.target 或 e.srcElement,兼容不同浏览器获取事件触发元素的方式),然后调用 setTabFocus 函数,并传入当前点击的 tab 标签元素上自定义的 'data-content-id' 属性值作为参数,这个属性值可能对应着不同的功能板块(如 '远程图片'、'上传图片' 等),通过调用 setTabFocus 函数来实现切换显示对应功能板块的操作。
+
+ var img = editor.selection.getRange().getClosedNode();
+ if (img && img.tagName && img.tagName.toLowerCase() == 'img') {
+ setTabFocus('remote');
+ } else {
+ setTabFocus('upload');
+ }
+ // 获取编辑器(editor,可能是富文本编辑器对象,具体依赖代码上下文环境)当前选区范围(editor.selection.getRange())内的闭合节点(getClosedNode(),可能是获取选区对应的 HTML 元素节点),判断如果该节点存在(img)并且节点的标签名(tagName)存在且转换为小写后是 'img'(即当前选区是一个图片元素),则调用 setTabFocus 函数并传入'remote' 参数,可能是默认显示远程图片相关功能板块;如果不符合上述条件(即选区不是图片元素或者不存在选区等情况),则调用 setTabFocus 函数并传入 'upload' 参数,默认显示上传图片相关功能板块,这部分代码实现了根据初始页面状态来决定默认显示哪个功能板块的逻辑。
+ }
+
+ /* 初始化tabbody */
+ function setTabFocus(id) {
+ if (!id) return;
+ // 如果传入的参数 id 为空(即没有传递有效的标识来确定要操作的功能板块),则直接从函数中返回,不执行后续操作,起到一个简单的参数校验作用。
+ var i, bodyId, tabs = $G('tabhead').children;
+ for (i = 0; i < tabs.length; i++) {
+ bodyId = tabs[i].getAttribute('data-content-id');
+ if (bodyId == id) {
+ domUtils.addClass(tabs[i], 'focus');
+ domUtils.addClass($G(bodyId), 'focus');
+ } else {
+ domUtils.removeClasses(tabs[i], 'focus');
+ domUtils.removeClasses($G(bodyId), 'focus');
+ }
+ }
+ // 首先通过 $G 函数获取页面中 id 为 'tabhead' 的元素的所有子元素(也就是各个 tab 标签元素),然后循环遍历这些 tab 标签元素,获取每个 tab 标签元素上自定义的 'data-content-id' 属性值(存储在 bodyId 变量中),用于和传入的参数 id 进行比较。如果某个 tab 标签元素的 'data-content-id' 值与传入的 id 相等,表示当前这个 tab 对应的功能板块需要被设置为选中状态,通过 domUtils.addClass 函数(可能是自定义的添加 CSS 类名的工具函数)为该 tab 标签元素和对应的功能板块(通过 $G(bodyId) 获取对应的 DOM 元素)添加 'focus' 类名(可能在 CSS 中有对应的样式定义,用于突出显示选中状态,比如改变背景色、字体颜色等样式);如果不相等,表示不是当前要选中的功能板块,则通过 domUtils.removeClasses 函数(可能是自定义的移除 CSS 类名的工具函数)移除它们的 'focus' 类名,实现切换不同功能板块的选中状态以及对应的显示隐藏等样式改变操作。
+
+ switch (id) {
+ case'remote':
+ remoteImage = remoteImage || new RemoteImage();
+ break;
+ case 'upload':
+ setAlign(editor.getOpt('imageInsertAlign'));
+ uploadImage = uploadImage || new UploadImage('queueList');
+ break;
+ case 'online':
+ setAlign(editor.getOpt('imageManagerInsertAlign'));
+ onlineImage = onlineImage || new OnlineImage('imageList');
+ onlineImage.reset();
+ break;
+ case'search':
+ setAlign(editor.getOpt('imageManagerInsertAlign'));
+ searchImage = searchImage || new SearchImage();
+ break;
+ }
+ // 根据传入的参数 id 的不同值(对应不同的功能板块标识),执行不同的操作来初始化相应的图片相关实例对象或者进行其他设置。例如,当 id 为'remote' 时,如果 remoteImage 变量还未实例化(通过 || 短路运算符判断),则创建一个 RemoteImage 类(可能是自定义的用于处理远程图片相关逻辑的类)的实例赋值给 remoteImage 变量;同理,对于 'upload'、'online' 和'search' 等不同情况,分别进行相应的实例化操作、调用 setAlign 函数(用于设置图片对齐方式,具体功能看其函数定义)以及调用对应类的特定方法(如 OnlineImage 类的 reset 方法)等操作,整体实现了根据当前选中的功能板块来初始化相应的数据和实例对象,为后续各功能板块的具体操作做准备。
+ }
+})();
+ /* 初始化onok事件 */
+ function initButtons() {
+
+ dialog.onok = function () {
+ var remote = false, list = [], id, tabs = $G('tabhead').children;
+ for (var i = 0; i < tabs.length; i++) {
+ if (domUtils.hasClass(tabs[i], 'focus')) {
+ id = tabs[i].getAttribute('data-content-id');
+ break;
+ }
+ }
+
+ switch (id) {
+ case 'remote':
+ list = remoteImage.getInsertList();
+ break;
+ case 'upload':
+ list = uploadImage.getInsertList();
+ var count = uploadImage.getQueueCount();
+ if (count) {
+ $('.info', '#queueList').html('
' + '还有2个未上传文件'.replace(/[\d]/, count) + ' ');
+ return false;
+ }
+ break;
+ case 'online':
+ list = onlineImage.getInsertList();
+ break;
+ case 'search':
+ list = searchImage.getInsertList();
+ remote = true;
+ break;
+ }
+
+ if(list) {
+ editor.execCommand('insertimage', list);
+ remote && editor.fireEvent("catchRemoteImage");
+ }
+ };
+ }
+
+
+ /* 初始化对其方式的点击事件 */
+ function initAlign(){
+ /* 点击align图标 */
+ domUtils.on($G("alignIcon"), 'click', function(e){
+ var target = e.target || e.srcElement;
+ if(target.className && target.className.indexOf('-align') != -1) {
+ setAlign(target.getAttribute('data-align'));
+ }
+ });
+ }
+
+ /* 设置对齐方式 */
+ function setAlign(align){
+ align = align || 'none';
+ var aligns = $G("alignIcon").children;
+ for(i = 0; i < aligns.length; i++){
+ if(aligns[i].getAttribute('data-align') == align) {
+ domUtils.addClass(aligns[i], 'focus');
+ $G("align").value = aligns[i].getAttribute('data-align');
+ } else {
+ domUtils.removeClasses(aligns[i], 'focus');
+ }
+ }
+ }
+ /* 获取对齐方式 */
+ function getAlign(){
+ var align = $G("align").value || 'none';
+ return align == 'none' ? '':align;
+ }
+
+
+ /* 在线图片 */
+ function RemoteImage(target) {
+ this.container = utils.isString(target) ? document.getElementById(target) : target;
+ this.init();
+ }
+ RemoteImage.prototype = {
+ init: function () {
+ this.initContainer();
+ this.initEvents();
+ },
+ initContainer: function () {
+ this.dom = {
+ 'url': $G('url'),
+ 'width': $G('width'),
+ 'height': $G('height'),
+ 'border': $G('border'),
+ 'vhSpace': $G('vhSpace'),
+ 'title': $G('title'),
+ 'align': $G('align')
+ };
+ var img = editor.selection.getRange().getClosedNode();
+ if (img) {
+ this.setImage(img);
+ }
+ },
+ initEvents: function () {
+ var _this = this,
+ locker = $G('lock');
+
+ /* 改变url */
+ domUtils.on($G("url"), 'keyup', updatePreview);
+ domUtils.on($G("border"), 'keyup', updatePreview);
+ domUtils.on($G("title"), 'keyup', updatePreview);
+
+ domUtils.on($G("width"), 'keyup', function(){
+ if(locker.checked) {
+ var proportion =locker.getAttribute('data-proportion');
+ $G('height').value = Math.round(this.value / proportion);
+ } else {
+ _this.updateLocker();
+ }
+ updatePreview();
+ });
+ domUtils.on($G("height"), 'keyup', function(){
+ if(locker.checked) {
+ var proportion =locker.getAttribute('data-proportion');
+ $G('width').value = Math.round(this.value * proportion);
+ } else {
+ _this.updateLocker();
+ }
+ updatePreview();
+ });
+ domUtils.on($G("lock"), 'change', function(){
+ var proportion = parseInt($G("width").value) /parseInt($G("height").value);
+ locker.setAttribute('data-proportion', proportion);
+ });
+
+ function updatePreview(){
+ _this.setPreview();
+ }
+ },
+ updateLocker: function(){
+ var width = $G('width').value,
+ height = $G('height').value,
+ locker = $G('lock');
+ if(width && height && width == parseInt(width) && height == parseInt(height)) {
+ locker.disabled = false;
+ locker.title = '';
+ } else {
+ locker.checked = false;
+ locker.disabled = 'disabled';
+ locker.title = lang.remoteLockError;
+ }
+ },
+ setImage: function(img){
+ /* 不是正常的图片 */
+ if (!img.tagName || img.tagName.toLowerCase() != 'img' && !img.getAttribute("src") || !img.src) return;
+
+ var wordImgFlag = img.getAttribute("word_img"),
+ src = wordImgFlag ? wordImgFlag.replace("&", "&") : (img.getAttribute('_src') || img.getAttribute("src", 2).replace("&", "&")),
+ align = editor.queryCommandValue("imageFloat");
+
+ /* 防止onchange事件循环调用 */
+ if (src !== $G("url").value) $G("url").value = src;
+ if(src) {
+ /* 设置表单内容 */
+ $G("width").value = img.width || '';
+ $G("height").value = img.height || '';
+ $G("border").value = img.getAttribute("border") || '0';
+ $G("vhSpace").value = img.getAttribute("vspace") || '0';
+ $G("title").value = img.title || img.alt || '';
+ setAlign(align);
+ this.setPreview();
+ this.updateLocker();
+ }
+ },
+ getData: function(){
+ var data = {};
+ for(var k in this.dom){
+ data[k] = this.dom[k].value;
+ }
+ return data;
+ },
+ setPreview: function(){
+ var url = $G('url').value,
+ ow = $G('width').value,
+ oh = $G('height').value,
+ border = $G('border').value,
+ title = $G('title').value,
+ preview = $G('preview'),
+ width,
+ height;
+
+ width = ((!ow || !oh) ? preview.offsetWidth:Math.min(ow, preview.offsetWidth));
+ width = width+(border*2) > preview.offsetWidth ? width:(preview.offsetWidth - (border*2));
+ height = (!ow || !oh) ? '':width*oh/ow;
+
+ if(url) {
+ preview.innerHTML = '
';
+ }
+ },
+ getInsertList: function () {
+ var data = this.getData();
+ if(data['url']) {
+ return [{
+ src: data['url'],
+ _src: data['url'],
+ width: data['width'] || '',
+ height: data['height'] || '',
+ border: data['border'] || '',
+ floatStyle: data['align'] || '',
+ vspace: data['vhSpace'] || '',
+ alt: data['title'] || '',
+ style: "width:" + data['width'] + "px;height:" + data['height'] + "px;"
+ }];
+ } else {
+ return [];
+ }
+ }
+ };
+
+
+
+ /* 上传图片 */
+ function UploadImage(target) {
+ this.$wrap = target.constructor == String ? $('#' + target) : $(target);
+ this.init();
+ }
+ UploadImage.prototype = {
+ init: function () {
+ this.imageList = [];
+ this.initContainer();
+ this.initUploader();
+ },
+ initContainer: function () {
+ this.$queue = this.$wrap.find('.filelist');
+ },
+ /* 初始化容器 */
+ initUploader: function () {
+ var _this = this,
+ $ = jQuery, // just in case. Make sure it's not an other libaray.
+ $wrap = _this.$wrap,
+ // 图片容器
+ $queue = $wrap.find('.filelist'),
+ // 状态栏,包括进度和控制按钮
+ $statusBar = $wrap.find('.statusBar'),
+ // 文件总体选择信息。
+ $info = $statusBar.find('.info'),
+ // 上传按钮
+ $upload = $wrap.find('.uploadBtn'),
+ // 上传按钮
+ $filePickerBtn = $wrap.find('.filePickerBtn'),
+ // 上传按钮
+ $filePickerBlock = $wrap.find('.filePickerBlock'),
+ // 没选择文件之前的内容。
+ $placeHolder = $wrap.find('.placeholder'),
+ // 总体进度条
+ $progress = $statusBar.find('.progress').hide(),
+ // 添加的文件数量
+ fileCount = 0,
+ // 添加的文件总大小
+ fileSize = 0,
+ // 优化retina, 在retina下这个值是2
+ ratio = window.devicePixelRatio || 1,
+ // 缩略图大小
+ thumbnailWidth = 113 * ratio,
+ thumbnailHeight = 113 * ratio,
+ // 可能有pedding, ready, uploading, confirm, done.
+ state = '',
+ // 所有文件的进度信息,key为file id
+ percentages = {},
+ supportTransition = (function () {
+ var s = document.createElement('p').style,
+ r = 'transition' in s ||
+ 'WebkitTransition' in s ||
+ 'MozTransition' in s ||
+ 'msTransition' in s ||
+ 'OTransition' in s;
+ s = null;
+ return r;
+ })(),
+ // WebUploader实例
+ uploader,
+ actionUrl = editor.getActionUrl(editor.getOpt('imageActionName')),
+ acceptExtensions = (editor.getOpt('imageAllowFiles') || []).join('').replace(/\./g, ',').replace(/^[,]/, ''),
+ imageMaxSize = editor.getOpt('imageMaxSize'),
+ imageCompressBorder = editor.getOpt('imageCompressBorder');
+
+ if (!WebUploader.Uploader.support()) {
+ $('#filePickerReady').after($('
').html(lang.errorNotSupport)).hide();
+ return;
+ } else if (!editor.getOpt('imageActionName')) {
+ $('#filePickerReady').after($('
').html(lang.errorLoadConfig)).hide();
+ return;
+ }
+
+ uploader = _this.uploader = WebUploader.create({
+ pick: {
+ id: '#filePickerReady',
+ label: lang.uploadSelectFile
+ },
+ accept: {
+ title: 'Images',
+ extensions: acceptExtensions,
+ mimeTypes: 'image/*'
+ },
+ swf: '../../third-party/webuploader/Uploader.swf',
+ server: actionUrl,
+ fileVal: editor.getOpt('imageFieldName'),
+ duplicate: true,
+ fileSingleSizeLimit: imageMaxSize, // 默认 2 M
+ compress: editor.getOpt('imageCompressEnable') ? {
+ width: imageCompressBorder,
+ height: imageCompressBorder,
+ // 图片质量,只有type为`image/jpeg`的时候才有效。
+ quality: 90,
+ // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
+ allowMagnify: false,
+ // 是否允许裁剪。
+ crop: false,
+ // 是否保留头部meta信息。
+ preserveHeaders: true
+ }:false
+ });
+ uploader.addButton({
+ id: '#filePickerBlock'
+ });
+ uploader.addButton({
+ id: '#filePickerBtn',
+ label: lang.uploadAddFile
+ });
+
+ setState('pedding');
+
+ // 当有文件添加进来时执行,负责view的创建
+ function addFile(file) {
+ var $li = $('
' +
+ '' + file.name + '
' +
+ '
' +
+ '
' +
+ ' '),
+
+ $btns = $('
' +
+ '' + lang.uploadDelete + ' ' +
+ '' + lang.uploadTurnRight + ' ' +
+ '' + lang.uploadTurnLeft + '
').appendTo($li),
+ $prgress = $li.find('p.progress span'),
+ $wrap = $li.find('p.imgWrap'),
+ $info = $('
').hide().appendTo($li),
+
+ showError = function (code) {
+ switch (code) {
+ case 'exceed_size':
+ text = lang.errorExceedSize;
+ break;
+ case 'interrupt':
+ text = lang.errorInterrupt;
+ break;
+ case 'http':
+ text = lang.errorHttp;
+ break;
+ case 'not_allow_type':
+ text = lang.errorFileType;
+ break;
+ default:
+ text = lang.errorUploadRetry;
+ break;
+ }
+ $info.text(text).show();
+ };
+
+ if (file.getStatus() === 'invalid') {
+ showError(file.statusText);
+ } else {
+ $wrap.text(lang.uploadPreview);
+ if (browser.ie && browser.version <= 7) {
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ uploader.makeThumb(file, function (error, src) {
+ if (error || !src) {
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ var $img = $('
');
+ $wrap.empty().append($img);
+ $img.on('error', function () {
+ $wrap.text(lang.uploadNoPreview);
+ });
+ }
+ }, thumbnailWidth, thumbnailHeight);
+ }
+ percentages[ file.id ] = [ file.size, 0 ];
+ file.rotation = 0;
+
+ /* 检查文件格式 */
+ if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) {
+ showError('not_allow_type');
+ uploader.removeFile(file);
+ }
+ }
+
+ file.on('statuschange', function (cur, prev) {
+ if (prev === 'progress') {
+ $prgress.hide().width(0);
+ } else if (prev === 'queued') {
+ $li.off('mouseenter mouseleave');
+ $btns.remove();
+ }
+ // 成功
+ if (cur === 'error' || cur === 'invalid') {
+ showError(file.statusText);
+ percentages[ file.id ][ 1 ] = 1;
+ } else if (cur === 'interrupt') {
+ showError('interrupt');
+ } else if (cur === 'queued') {
+ percentages[ file.id ][ 1 ] = 0;
+ } else if (cur === 'progress') {
+ $info.hide();
+ $prgress.css('display', 'block');
+ } else if (cur === 'complete') {
+ }
+
+ $li.removeClass('state-' + prev).addClass('state-' + cur);
+ });
+
+ $li.on('mouseenter', function () {
+ $btns.stop().animate({height: 30});
+ });
+ $li.on('mouseleave', function () {
+ $btns.stop().animate({height: 0});
+ });
+
+ $btns.on('click', 'span', function () {
+ var index = $(this).index(),
+ deg;
+
+ switch (index) {
+ case 0:
+ uploader.removeFile(file);
+ return;
+ case 1:
+ file.rotation += 90;
+ break;
+ case 2:
+ file.rotation -= 90;
+ break;
+ }
+
+ if (supportTransition) {
+ deg = 'rotate(' + file.rotation + 'deg)';
+ $wrap.css({
+ '-webkit-transform': deg,
+ '-mos-transform': deg,
+ '-o-transform': deg,
+ 'transform': deg
+ });
+ } else {
+ $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
+ }
+
+ });
+
+ $li.insertBefore($filePickerBlock);
+ }
+
+ // 负责view的销毁
+ function removeFile(file) {
+ var $li = $('#' + file.id);
+ delete percentages[ file.id ];
+ updateTotalProgress();
+ $li.off().find('.file-panel').off().end().remove();
+ }
+
+ function updateTotalProgress() {
+ var loaded = 0,
+ total = 0,
+ spans = $progress.children(),
+ percent;
+
+ $.each(percentages, function (k, v) {
+ total += v[ 0 ];
+ loaded += v[ 0 ] * v[ 1 ];
+ });
+
+ percent = total ? loaded / total : 0;
+
+ spans.eq(0).text(Math.round(percent * 100) + '%');
+ spans.eq(1).css('width', Math.round(percent * 100) + '%');
+ updateStatus();
+ }
+
+ function setState(val, files) {
+
+ if (val != state) {
+
+ var stats = uploader.getStats();
+
+ $upload.removeClass('state-' + state);
+ $upload.addClass('state-' + val);
+
+ switch (val) {
+
+ /* 未选择文件 */
+ case 'pedding':
+ $queue.addClass('element-invisible');
+ $statusBar.addClass('element-invisible');
+ $placeHolder.removeClass('element-invisible');
+ $progress.hide(); $info.hide();
+ uploader.refresh();
+ break;
+
+ /* 可以开始上传 */
+ case 'ready':
+ $placeHolder.addClass('element-invisible');
+ $queue.removeClass('element-invisible');
+ $statusBar.removeClass('element-invisible');
+ $progress.hide(); $info.show();
+ $upload.text(lang.uploadStart);
+ uploader.refresh();
+ break;
+
+ /* 上传中 */
+ case 'uploading':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadPause);
+ break;
+
+ /* 暂停上传 */
+ case 'paused':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadContinue);
+ break;
+
+ case 'confirm':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadStart);
+
+ stats = uploader.getStats();
+ if (stats.successNum && !stats.uploadFailNum) {
+ setState('finish');
+ return;
+ }
+ break;
+
+ case 'finish':
+ $progress.hide(); $info.show();
+ if (stats.uploadFailNum) {
+ $upload.text(lang.uploadRetry);
+ } else {
+ $upload.text(lang.uploadStart);
+ }
+ break;
+ }
+
+ state = val;
+ updateStatus();
+
+ }
+
+ if (!_this.getQueueCount()) {
+ $upload.addClass('disabled')
+ } else {
+ $upload.removeClass('disabled')
+ }
+
+ }
+
+ function updateStatus() {
+ var text = '', stats;
+
+ if (state === 'ready') {
+ text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize));
+ } else if (state === 'confirm') {
+ stats = uploader.getStats();
+ if (stats.uploadFailNum) {
+ text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum);
+ }
+ } else {
+ stats = uploader.getStats();
+ text = lang.updateStatusFinish.replace('_', fileCount).
+ replace('_KB', WebUploader.formatSize(fileSize)).
+ replace('_', stats.successNum);
+
+ if (stats.uploadFailNum) {
+ text += lang.updateStatusError.replace('_', stats.uploadFailNum);
+ }
+ }
+
+ $info.html(text);
+ }
+
+ uploader.on('fileQueued', function (file) {
+ fileCount++;
+ fileSize += file.size;
+
+ if (fileCount === 1) {
+ $placeHolder.addClass('element-invisible');
+ $statusBar.show();
+ }
+
+ addFile(file);
+ });
+
+ uploader.on('fileDequeued', function (file) {
+ if (file.ext && acceptExtensions.indexOf(file.ext.toLowerCase()) != -1 && file.size <= imageMaxSize) {
+ fileCount--;
+ fileSize -= file.size;
+ }
+
+ removeFile(file);
+ updateTotalProgress();
+ });
+
+ uploader.on('filesQueued', function (file) {
+ if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) {
+ setState('ready');
+ }
+ updateTotalProgress();
+ });
+
+ uploader.on('all', function (type, files) {
+ switch (type) {
+ case 'uploadFinished':
+ setState('confirm', files);
+ break;
+ case 'startUpload':
+ /* 添加额外的GET参数 */
+ var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '',
+ url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + 'encode=utf-8&' + params);
+ uploader.option('server', url);
+ setState('uploading', files);
+ break;
+ case 'stopUpload':
+ setState('paused', files);
+ break;
+ }
+ });
+
+ uploader.on('uploadBeforeSend', function (file, data, header) {
+ //这里可以通过data对象添加POST参数
+ if (actionUrl.toLowerCase().indexOf('jsp') != -1) {
+ header['X_Requested_With'] = 'XMLHttpRequest';
+ }
+ });
+
+ uploader.on('uploadProgress', function (file, percentage) {
+ var $li = $('#' + file.id),
+ $percent = $li.find('.progress span');
+
+ $percent.css('width', percentage * 100 + '%');
+ percentages[ file.id ][ 1 ] = percentage;
+ updateTotalProgress();
+ });
+
+ uploader.on('uploadSuccess', function (file, ret) {
+ var $file = $('#' + file.id);
+ try {
+ var responseText = (ret._raw || ret),
+ json = utils.str2json(responseText);
+ if (json.state == 'SUCCESS') {
+ _this.imageList.push(json);
+ $file.append('
');
+ } else {
+ $file.find('.error').text(json.state).show();
+ }
+ } catch (e) {
+ $file.find('.error').text(lang.errorServerUpload).show();
+ }
+ });
+
+ uploader.on('uploadError', function (file, code) {
+ });
+ uploader.on('error', function (code, file) {
+ if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') {
+ addFile(file);
+ }
+ });
+ uploader.on('uploadComplete', function (file, ret) {
+ });
+
+ $upload.on('click', function () {
+ if ($(this).hasClass('disabled')) {
+ return false;
+ }
+
+ if (state === 'ready') {
+ uploader.upload();
+ } else if (state === 'paused') {
+ uploader.upload();
+ } else if (state === 'uploading') {
+ uploader.stop();
+ }
+ });
+
+ $upload.addClass('state-' + state);
+ updateTotalProgress();
+ },
+ getQueueCount: function () {
+ var file, i, status, readyFile = 0, files = this.uploader.getFiles();
+ for (i = 0; file = files[i++]; ) {
+ status = file.getStatus();
+ if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++;
+ }
+ return readyFile;
+ },
+ destroy: function () {
+ this.$wrap.remove();
+ },
+ getInsertList: function () {
+ var i, data, list = [],
+ align = getAlign(),
+ prefix = editor.getOpt('imageUrlPrefix');
+ for (i = 0; i < this.imageList.length; i++) {
+ data = this.imageList[i];
+ list.push({
+ src: prefix + data.url,
+ _src: prefix + data.url,
+ alt: data.original,
+ floatStyle: align
+ });
+ }
+ return list;
+ }
+ };
+
+
+ /* 在线图片 */
+ function OnlineImage(target) {
+ this.container = utils.isString(target) ? document.getElementById(target) : target;
+ this.init();
+ }
+ OnlineImage.prototype = {
+ init: function () {
+ this.reset();
+ this.initEvents();
+ },
+ /* 初始化容器 */
+ initContainer: function () {
+ this.container.innerHTML = '';
+ this.list = document.createElement('ul');
+ this.clearFloat = document.createElement('li');
+
+ domUtils.addClass(this.list, 'list');
+ domUtils.addClass(this.clearFloat, 'clearFloat');
+
+ this.list.appendChild(this.clearFloat);
+ this.container.appendChild(this.list);
+ },
+ /* 初始化滚动事件,滚动到地步自动拉取数据 */
+ initEvents: function () {
+ var _this = this;
+
+ /* 滚动拉取图片 */
+ domUtils.on($G('imageList'), 'scroll', function(e){
+ var panel = this;
+ if (panel.scrollHeight - (panel.offsetHeight + panel.scrollTop) < 10) {
+ _this.getImageData();
+ }
+ });
+ /* 选中图片 */
+ domUtils.on(this.container, 'click', function (e) {
+ var target = e.target || e.srcElement,
+ li = target.parentNode;
+
+ if (li.tagName.toLowerCase() == 'li') {
+ if (domUtils.hasClass(li, 'selected')) {
+ domUtils.removeClasses(li, 'selected');
+ } else {
+ domUtils.addClass(li, 'selected');
+ }
+ }
+ });
+ },
+ /* 初始化第一次的数据 */
+ initData: function () {
+
+ /* 拉取数据需要使用的值 */
+ this.state = 0;
+ this.listSize = editor.getOpt('imageManagerListSize');
+ this.listIndex = 0;
+ this.listEnd = false;
+
+ /* 第一次拉取数据 */
+ this.getImageData();
+ },
+ /* 重置界面 */
+ reset: function() {
+ this.initContainer();
+ this.initData();
+ },
+ /* 向后台拉取图片列表数据 */
+ getImageData: function () {
+ var _this = this;
+
+ if(!_this.listEnd && !this.isLoadingData) {
+ this.isLoadingData = true;
+ var url = editor.getActionUrl(editor.getOpt('imageManagerActionName')),
+ isJsonp = utils.isCrossDomainUrl(url);
+ ajax.request(url, {
+ 'timeout': 100000,
+ 'dataType': isJsonp ? 'jsonp':'',
+ 'data': utils.extend({
+ start: this.listIndex,
+ size: this.listSize
+ }, editor.queryCommandValue('serverparam')),
+ 'method': 'get',
+ 'onsuccess': function (r) {
+ try {
+ var json = isJsonp ? r:eval('(' + r.responseText + ')');
+ if (json.state == 'SUCCESS') {
+ _this.pushData(json.list);
+ _this.listIndex = parseInt(json.start) + parseInt(json.list.length);
+ if(_this.listIndex >= json.total) {
+ _this.listEnd = true;
+ }
+ _this.isLoadingData = false;
+ }
+ } catch (e) {
+ if(r.responseText.indexOf('ue_separate_ue') != -1) {
+ var list = r.responseText.split(r.responseText);
+ _this.pushData(list);
+ _this.listIndex = parseInt(list.length);
+ _this.listEnd = true;
+ _this.isLoadingData = false;
+ }
+ }
+ },
+ 'onerror': function () {
+ _this.isLoadingData = false;
+ }
+ });
+ }
+ },
+ /* 添加图片到列表界面上 */
+ pushData: function (list) {
+ var i, item, img, icon, _this = this,
+ urlPrefix = editor.getOpt('imageManagerUrlPrefix');
+ for (i = 0; i < list.length; i++) {
+ if(list[i] && list[i].url) {
+ item = document.createElement('li');
+ img = document.createElement('img');
+ icon = document.createElement('span');
+
+ domUtils.on(img, 'load', (function(image){
+ return function(){
+ _this.scale(image, image.parentNode.offsetWidth, image.parentNode.offsetHeight);
+ }
+ })(img));
+ img.width = 113;
+ img.setAttribute('src', urlPrefix + list[i].url + (list[i].url.indexOf('?') == -1 ? '?noCache=':'&noCache=') + (+new Date()).toString(36) );
+ img.setAttribute('_src', urlPrefix + list[i].url);
+ domUtils.addClass(icon, 'icon');
+
+ item.appendChild(img);
+ item.appendChild(icon);
+ this.list.insertBefore(item, this.clearFloat);
+ }
+ }
+ },
+ /* 改变图片大小 */
+ scale: function (img, w, h, type) {
+ var ow = img.width,
+ oh = img.height;
+
+ if (type == 'justify') {
+ if (ow >= oh) {
+ img.width = w;
+ img.height = h * oh / ow;
+ img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px';
+ } else {
+ img.width = w * ow / oh;
+ img.height = h;
+ img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px';
+ }
+ } else {
+ if (ow >= oh) {
+ img.width = w * ow / oh;
+ img.height = h;
+ img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px';
+ } else {
+ img.width = w;
+ img.height = h * oh / ow;
+ img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px';
+ }
+ }
+ },
+ getInsertList: function () {
+ var i, lis = this.list.children, list = [], align = getAlign();
+ for (i = 0; i < lis.length; i++) {
+ if (domUtils.hasClass(lis[i], 'selected')) {
+ var img = lis[i].firstChild,
+ src = img.getAttribute('_src');
+ list.push({
+ src: src,
+ _src: src,
+ alt: src.substr(src.lastIndexOf('/') + 1),
+ floatStyle: align
+ });
+ }
+
+ }
+ return list;
+ }
+ };
+
+ /*搜索图片 */
+ function SearchImage() {
+ this.init();
+ }
+ SearchImage.prototype = {
+ init: function () {
+ this.initEvents();
+ },
+ initEvents: function(){
+ var _this = this;
+
+ /* 点击搜索按钮 */
+ domUtils.on($G('searchBtn'), 'click', function(){
+ var key = $G('searchTxt').value;
+ if(key && key != lang.searchRemind) {
+ _this.getImageData();
+ }
+ });
+ /* 点击清除妞 */
+ domUtils.on($G('searchReset'), 'click', function(){
+ $G('searchTxt').value = lang.searchRemind;
+ $G('searchListUl').innerHTML = '';
+ $G('searchType').selectedIndex = 0;
+ });
+ /* 搜索框聚焦 */
+ domUtils.on($G('searchTxt'), 'focus', function(){
+ var key = $G('searchTxt').value;
+ if(key && key == lang.searchRemind) {
+ $G('searchTxt').value = '';
+ }
+ });
+ /* 搜索框回车键搜索 */
+ domUtils.on($G('searchTxt'), 'keydown', function(e){
+ var keyCode = e.keyCode || e.which;
+ if (keyCode == 13) {
+ $G('searchBtn').click();
+ }
+ });
+
+ /* 选中图片 */
+ domUtils.on($G('searchList'), 'click', function(e){
+ var target = e.target || e.srcElement,
+ li = target.parentNode.parentNode;
+
+ if (li.tagName.toLowerCase() == 'li') {
+ if (domUtils.hasClass(li, 'selected')) {
+ domUtils.removeClasses(li, 'selected');
+ } else {
+ domUtils.addClass(li, 'selected');
+ }
+ }
+ });
+ },
+ /* 改变图片大小 */
+ scale: function (img, w, h) {
+ var ow = img.width,
+ oh = img.height;
+
+ if (ow >= oh) {
+ img.width = w * ow / oh;
+ img.height = h;
+ img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px';
+ } else {
+ img.width = w;
+ img.height = h * oh / ow;
+ img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px';
+ }
+ },
+ getImageData: function(){
+ var _this = this,
+ key = $G('searchTxt').value,
+ type = $G('searchType').value,
+ keepOriginName = editor.options.keepOriginName ? "1" : "0",
+ url = "http://image.baidu.com/i?ct=201326592&cl=2&lm=-1&st=-1&tn=baiduimagejson&istype=2&rn=32&fm=index&pv=&word=" + key + type + "&ie=utf-8&oe=utf-8&keeporiginname=" + keepOriginName + "&" + +new Date;
+
+ $G('searchListUl').innerHTML = lang.searchLoading;
+ ajax.request(url, {
+ 'dataType': 'jsonp',
+ 'charset': 'GB18030',
+ 'onsuccess':function(json){
+ var list = [];
+ if(json && json.data) {
+ for(var i = 0; i < json.data.length; i++) {
+ if(json.data[i].objURL) {
+ list.push({
+ title: json.data[i].fromPageTitleEnc,
+ src: json.data[i].objURL,
+ url: json.data[i].fromURL
+ });
+ }
+ }
+ }
+ _this.setList(list);
+ },
+ 'onerror':function(){
+ $G('searchListUl').innerHTML = lang.searchRetry;
+ }
+ });
+ },
+ /* 添加图片到列表界面上 */
+ setList: function (list) {
+ var i, item, p, img, link, _this = this,
+ listUl = $G('searchListUl');
+
+ listUl.innerHTML = '';
+ if(list.length) {
+ for (i = 0; i < list.length; i++) {
+ item = document.createElement('li');
+ p = document.createElement('p');
+ img = document.createElement('img');
+ link = document.createElement('a');
+
+ img.onload = function () {
+ _this.scale(this, 113, 113);
+ };
+ img.width = 113;
+ img.setAttribute('src', list[i].src);
+
+ link.href = list[i].url;
+ link.target = '_blank';
+ link.title = list[i].title;
+ link.innerHTML = list[i].title;
+
+ p.appendChild(img);
+ item.appendChild(p);
+ item.appendChild(link);
+ listUl.appendChild(item);
+ }
+ } else {
+ listUl.innerHTML = lang.searchRetry;
+ }
+ },
+ getInsertList: function () {
+ var child,
+ src,
+ align = getAlign(),
+ list = [],
+ items = $G('searchListUl').children;
+ for(var i = 0; i < items.length; i++) {
+ child = items[i].firstChild && items[i].firstChild.firstChild;
+ if(child.tagName && child.tagName.toLowerCase() == 'img' && domUtils.hasClass(items[i], 'selected')) {
+ src = child.src;
+ list.push({
+ src: src,
+ _src: src,
+ alt: src.substr(src.lastIndexOf('/') + 1),
+ floatStyle: align
+ });
+ }
+ }
+ return list;
+ }
+ };
+
+})();
diff --git a/public2/ueditor/dialogs/insertframe/insertframe.html b/public2/ueditor/dialogs/insertframe/insertframe.html
new file mode 100644
index 0000000..a78220d
--- /dev/null
+++ b/public2/ueditor/dialogs/insertframe/insertframe.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/internal.js b/public2/ueditor/dialogs/internal.js
new file mode 100644
index 0000000..2ab09f3
--- /dev/null
+++ b/public2/ueditor/dialogs/internal.js
@@ -0,0 +1,109 @@
+(function () {
+ // 获取 `window.parent` 对象,通常在页面通过 `iframe` 嵌套的情况下,`window.parent` 指向包含当前 `iframe` 的父页面的 `window` 对象,后续可能会基于这个父页面的环境获取一些全局可用的对象、变量等资源,用于当前 `iframe` 页面内的功能实现。
+ var parent = window.parent;
+
+ // 获取 `dialog` 对象,通过父页面(`parent`)的 `$EDITORUI` 对象(从命名推测可能是编辑器相关的 UI 管理对象,存储了与编辑器界面相关的各种组件、元素等信息,具体结构和功能依赖整体应用的设计),根据当前 `iframe` 的 `id`(通过 `window.frameElement.id` 获取 `iframe` 的 `id`,并使用正则表达式替换掉末尾的 `_iframe` 字符串)来获取对应的 `dialog` 对象,这个 `dialog` 对象可能用于弹出对话框、交互提示等相关功能,在编辑器相关操作中起到与用户交互的作用。
+ dialog = parent.$EDITORUI[window.frameElement.id.replace(/_iframe$/, '')];
+
+ // 获取当前打开 `dialog` 的编辑器实例对象,将 `dialog` 对象的 `editor` 属性值赋给 `editor` 变量,这个 `editor` 对象可能包含了各种文本编辑相关的方法、属性,用于操作编辑器中的内容,比如插入文本、图片,执行各种编辑命令等功能,是整个编辑器功能实现的核心对象之一。
+ editor = dialog.editor;
+
+ UE = parent.UE;
+ // 获取父页面中的 `UE` 对象,从命名推测 `UE` 可能是整个应用(可能是一个富文本编辑器应用)的全局核心对象,包含了众多功能模块相关的属性、方法,比如 `dom`(用于操作 DOM 元素相关功能)、`utils`(工具函数集合)、`browser`(用于检测浏览器相关信息)等,后续通过它来获取其他细分功能的对象引用。
+
+ domUtils = UE.dom.domUtils;
+ // 从 `UE` 对象的 `dom` 属性(可能是与 DOM 元素操作相关的子对象)下获取 `domUtils` 对象,这个对象通常包含了一系列用于操作 DOM 元素的工具函数,比如创建元素、添加删除类名、获取计算样式等功能相关的函数,方便在代码中对页面元素进行各种操作。
+
+ utils = UE.utils;
+ // 获取 `UE` 对象的 `utils` 对象,它可能是一个工具函数集合,包含了诸如加载文件、对象扩展、字符串处理等各种通用的工具函数,用于辅助其他功能模块实现一些常见的操作逻辑。
+
+ browser = UE.browser;
+ // 获取 `UE` 对象的 `browser` 对象,这个对象可能用于检测浏览器的相关信息,比如浏览器类型(是 `IE`、`Chrome`、`Firefox` 等)、浏览器版本号等,以便在代码中针对不同浏览器进行兼容性处理或者执行特定于某些浏览器的功能逻辑。
+
+ ajax = UE.ajax;
+ // 获取 `UE` 对象的 `ajax` 对象,它可能是用于实现异步 HTTP 请求(如 `XMLHttpRequest` 相关功能)的对象,通过它可以向服务器发送请求获取数据、提交表单等操作,在与服务器端交互获取或更新编辑器相关内容等场景中会用到。
+
+ $G = function (id) {
+ return document.getElementById(id)
+ };
+ // 定义一个名为 `$G` 的函数,它是对 `document.getElementById` 方法的简单封装,用于根据传入的元素 `id` 获取对应的页面 DOM 元素,方便在代码中更简洁地获取元素引用,后续代码中会多次使用这个函数来获取特定 `id` 的元素进行操作。
+
+ // `$focus` 函数用于设置页面元素的焦点,通过传入一个节点元素(`node`),根据浏览器类型(判断是否为 `IE` 浏览器)来采用不同的方式设置焦点。如果是 `IE` 浏览器,使用 `createTextRange` 方法创建一个文本范围对象(`r`),调用 `collapse` 方法将光标移动到文本末尾(`false` 参数表示移动到末尾,`true` 表示移动到开头),然后调用 `select` 方法选中该文本范围,实现设置焦点的效果;如果不是 `IE` 浏览器,则直接调用节点元素的 `focus` 方法来设置焦点,通过 `setTimeout` 函数在下次事件循环开始时(延迟时间为 0)执行这个设置焦点的操作,确保操作在合适的时机执行。
+ $focus = function (node) {
+ setTimeout(function () {
+ if (browser.ie) {
+ var r = node.createTextRange();
+ r.collapse(false);
+ r.select();
+ } else {
+ node.focus()
+ }
+ }, 0)
+ };
+
+ // 使用 `utils` 对象的 `loadFile` 函数(具体功能依赖其内部实现,但从命名推测是用于加载文件资源到页面中)向页面中加载一个样式表文件。传入 `document` 对象(表示当前页面的文档对象)以及一个配置对象,配置对象中指定了要加载的文件的 `href`(文件路径,通过获取编辑器的 `options` 对象中的 `themePath`(主题路径相关配置)、`theme`(当前主题名称)以及添加一个随机数作为缓存破坏参数,拼接成完整的样式表文件路径)、`tag`(设置为 `"link"`,表示通过 `
` 标签的方式加载样式表文件)、`type`(设置为 `"text/css"`,表明文件类型为 CSS 样式表)、`rel`(设置为 `"stylesheet"`,表示是样式表的关联关系)等属性,实现动态加载特定的样式表文件到页面,用于应用相应的样式规则,可能是根据当前编辑器的主题来加载对应的样式表。
+ utils.loadFile(document, {
+ href: editor.options.themePath + editor.options.theme + "/dialogbase.css?cache=" + Math.random(),
+ tag: "link",
+ type: "text/css",
+ rel: "stylesheet"
+ });
+
+ lang = editor.getLang(dialog.className.split("-")[2]);
+ // 通过 `editor` 对象的 `getLang` 方法(可能是根据传入的参数获取对应的语言相关配置对象,用于实现多语言功能,根据不同语言环境展示不同的文本内容等),传入 `dialog.className`(`dialog` 对象的类名)通过字符串分割(以 `-` 为分隔符)获取第三个元素(从索引为 2 的位置获取,推测可能是与语言标识相关的部分,具体格式依赖整体应用的命名约定)作为参数,获取对应的语言配置对象,并赋值给 `lang` 变量,后续会根据这个 `lang` 对象来更新页面中的静态资源相关元素的文本、样式等内容,实现多语言展示效果。
+
+ if (lang) {
+ domUtils.on(window, 'load', function () {
+ // 使用 `domUtils` 对象的 `on` 函数(可能是用于添加事件监听器的函数,类似于原生的 `addEventListener` 方法,但可能有更多自定义的逻辑处理)为 `window` 对象的 `load` 事件(该事件在页面所有资源加载完成后触发)添加事件处理函数,在页面加载完成后执行以下操作,用于根据语言配置更新页面中的静态资源相关元素内容。
+
+ var langImgPath = editor.options.langPath + editor.options.lang + "/images/";
+ // 构建一个 `langImgPath` 变量,通过获取编辑器的 `options` 对象中的 `langPath`(语言相关的路径前缀)和 `lang`(当前语言标识)以及固定的 `"/images/"` 拼接而成,这个路径用于后续查找和替换与语言相关的图片资源路径等操作,确保在不同语言环境下能正确加载对应的图片资源。
+
+ // 遍历 `lang` 对象的 `static` 属性(从命名推测可能是存储了各种静态资源相关配置信息的对象,比如页面中固定的文本内容、图片引用、元素样式等与语言相关的配置,每个属性对应一个页面元素的相关配置)中的所有属性(通过 `for...in` 循环遍历对象的可枚举属性),获取每个属性对应的配置信息,并根据配置内容和元素类型进行相应的更新操作。
+ for (var i in lang["static"]) {
+ var dom = $G(i);
+ if (!dom) continue;
+ // 通过 `$G` 函数获取 `id` 为当前循环属性名(`i`)的页面元素,赋值给 `dom` 变量,如果获取到的元素不存在(返回 `null`),则直接跳过本次循环,继续下一个属性的处理,确保只对存在的页面元素进行操作。
+ var tagName = dom.tagName,
+ content = lang["static"][i];
+ // 获取元素的标签名(通过 `tagName` 属性),以及对应的 `lang["static"][i]` 配置对象(包含了该元素根据语言配置的文本内容、样式、属性等信息),用于后续根据元素类型和配置内容进行不同的更新操作。
+
+ if (content.src) {
+ // 如果配置对象中存在 `src` 属性(通常表示图片元素的资源路径相关属性,意味着该元素是图片相关元素或者引用了外部资源等情况),则执行以下操作。
+ //clone
+ content = utils.extend({}, content, false);
+ // 使用 `utils.extend` 函数(可能是自定义的用于合并对象属性的函数,将第一个对象的属性复制到第二个对象上,如果有同名属性则覆盖,这里将 `content` 对象复制一份,避免直接修改原始配置对象,可能影响其他地方的使用)创建一个新的 `content` 对象副本,传入 `false` 参数可能是控制合并的方式(具体作用依赖 `utils.extend` 函数内部实现),然后对副本进行操作。
+ content.src = langImgPath + content.src;
+ // 将新的 `content` 对象副本的 `src` 属性值更新为 `langImgPath`(前面构建的语言相关图片资源路径前缀)与原始 `src` 属性值(图片文件名等)拼接后的字符串,实现根据语言环境替换图片资源路径的效果,确保在不同语言下能正确加载对应的图片。
+ }
+ if (content.style) {
+ content = utils.extend({}, content, false);
+ content.style = content.style.replace(/url\s*\(/g, "url(" + langImgPath)
+ // 如果配置对象中存在 `style` 属性(表示元素的样式相关配置信息),同样先复制一份 `content` 对象,然后使用字符串替换方法 `replace`,将样式字符串中所有出现的 `url(`(匹配 `url` 后面跟着空格和左括号的情况,用于处理样式中引用图片资源的路径部分,确保能准确替换)替换为 `url(` 加上 `langImgPath`(语言相关图片资源路径前缀),实现将样式中引用的图片资源路径替换为对应语言环境下的正确路径,使样式中的图片能正确显示。
+ }
+ switch (tagName.toLowerCase()) {
+ case "var":
+ dom.parentNode.replaceChild(document.createTextNode(content), dom);
+ break;
+ // 根据元素的标签名(转换为小写后进行判断)来执行不同的更新操作。如果元素标签名是 `"var"`(可能是用于显示特定文本内容的占位元素,比如用于显示多语言相关的变量文本等情况),则通过 `dom.parentNode.replaceChild` 方法(在元素的父节点上,用一个新创建的文本节点(使用 `document.createTextNode` 方法创建,内容为 `content`,即语言配置中的对应文本内容)替换当前元素,实现更新元素显示文本内容的效果。
+ case "select":
+ var ops = dom.options;
+ for (var j = 0, oj; oj = ops[j]; ) {
+ oj.innerHTML = content.options[j++];
+ }
+ for (var p in content) {
+ p!= "options" && dom.setAttribute(p, content[p]);
+ }
+ break;
+ // 如果元素标签名是 `"select"`(下拉选择框元素),先获取其所有的 `
` 选项元素(通过 `dom.options` 获取),然后遍历这些选项元素,将每个选项的 `innerHTML`(显示的文本内容)更新为 `content.options` 数组(语言配置中对应下拉框选项的文本内容数组)中相应位置的元素内容;接着遍历 `content` 对象的所有属性,对于除了 `"options"` 属性之外的其他属性(通过判断属性名是否不等于 `"options"`),使用 `dom.setAttribute` 方法(为元素设置属性)将属性设置到下拉框元素上,实现更新下拉框的选项文本以及其他相关属性的效果,比如设置 `selected` 属性来默认选中某个选项等情况。
+ default:
+ domUtils.setAttributes(dom, content);
+
+ // 如果元素标签名不属于上述两种情况,则使用 `domUtils` 对象的 `setAttributes` 函数(可能是用于批量设置元素多个属性的函数,根据传入的配置对象中的属性名和属性值来设置元素相应的属性)将 `content` 对象中的属性设置到元素上,实现更新元素各种属性(比如 `class`、`id`、`style` 等属性根据语言配置进行更新)的效果。
+ }
+ }
+ });
+ }
+
+
+})();
\ No newline at end of file
diff --git a/public2/ueditor/dialogs/link/link.html b/public2/ueditor/dialogs/link/link.html
new file mode 100644
index 0000000..668ca70
--- /dev/null
+++ b/public2/ueditor/dialogs/link/link.html
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+