').html(lang.errorNotSupport)).hide();
+ return;
+ } else if (!editor.getOpt('videoActionName')) {
+ $('#filePickerReady').after($('
').html(lang.errorLoadConfig)).hide();
+ return;
+ }
+
+ uploader = _this.uploader = WebUploader.create({
+ pick: {
+ id: '#filePickerReady',
+ label: lang.uploadSelectFile
+ },
+ swf: '../../third-party/webuploader/Uploader.swf',
+ server: actionUrl,
+ fileVal: editor.getOpt('videoFieldName'),
+ duplicate: true,
+ fileSingleSizeLimit: fileMaxSize,
+ compress: 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 ('|png|jpg|jpeg|bmp|gif|'.indexOf('|'+file.ext.toLowerCase()+'|') == -1) {
+ $wrap.empty().addClass('notimage').append('
' +
+ '
' + file.name + '');
+ } else {
+ if (browser.ie && browser.version <= 7) {
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ uploader.makeThumb(file, function (error, src) {
+ if (error || !src || (/^data:/.test(src) && browser.ie && browser.version <= 7)) {
+ $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) {
+ 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参数
+ 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') {
+ uploadVideoList.push({
+ 'url': json.url,
+ 'type': json.type,
+ 'original':json.original
+ });
+ $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;
+ },
+ refresh: function(){
+ this.uploader.refresh();
+ }
+ };
+
+})();
diff --git a/assets/dialogs/webapp/webapp.html b/assets/dialogs/webapp/webapp.html
index 1614377..73f8dc2 100644
--- a/assets/dialogs/webapp/webapp.html
+++ b/assets/dialogs/webapp/webapp.html
@@ -1,53 +1,53 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/dialogs/wordimage/wordimage.html b/assets/dialogs/wordimage/wordimage.html
index 6cf6067..f8c0905 100644
--- a/assets/dialogs/wordimage/wordimage.html
+++ b/assets/dialogs/wordimage/wordimage.html
@@ -1,111 +1,111 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/dialogs/wordimage/wordimage.js b/assets/dialogs/wordimage/wordimage.js
index 98f3a22..e360263 100644
--- a/assets/dialogs/wordimage/wordimage.js
+++ b/assets/dialogs/wordimage/wordimage.js
@@ -1,157 +1,157 @@
-/**
- * Created by JetBrains PhpStorm.
- * User: taoqili
- * Date: 12-1-30
- * Time: 下午12:50
- * To change this template use File | Settings | File Templates.
- */
-
-
-
-var wordImage = {};
-//(function(){
-var g = baidu.g,
- flashObj,flashContainer;
-
-wordImage.init = function(opt, callbacks) {
- showLocalPath("localPath");
- //createCopyButton("clipboard","localPath");
- createFlashUploader(opt, callbacks);
- addUploadListener();
- addOkListener();
-};
-
-function hideFlash(){
- flashObj = null;
- flashContainer.innerHTML = "";
-}
-function addOkListener() {
- dialog.onok = function() {
- if (!imageUrls.length) return;
- var urlPrefix = editor.getOpt('imageUrlPrefix'),
- images = domUtils.getElementsByTagName(editor.document,"img");
- editor.fireEvent('saveScene');
- for (var i = 0,img; img = images[i++];) {
- var src = img.getAttribute("word_img");
- if (!src) continue;
- for (var j = 0,url; url = imageUrls[j++];) {
- if (src.indexOf(url.original.replace(" ","")) != -1) {
- img.src = urlPrefix + url.url;
- img.setAttribute("_src", urlPrefix + url.url); //同时修改"_src"属性
- img.setAttribute("title",url.title);
- domUtils.removeAttributes(img, ["word_img","style","width","height"]);
- editor.fireEvent("selectionchange");
- break;
- }
- }
- }
- editor.fireEvent('saveScene');
- hideFlash();
- };
- dialog.oncancel = function(){
- hideFlash();
- }
-}
-
-/**
- * 绑定开始上传事件
- */
-function addUploadListener() {
- g("upload").onclick = function () {
- flashObj.upload();
- this.style.display = "none";
- };
-}
-
-function showLocalPath(id) {
- //单张编辑
- var img = editor.selection.getRange().getClosedNode();
- var images = editor.execCommand('wordimage');
- if(images.length==1 || img && img.tagName == 'IMG'){
- g(id).value = images[0];
- return;
- }
- var path = images[0];
- var leftSlashIndex = path.lastIndexOf("/")||0, //不同版本的doc和浏览器都可能影响到这个符号,故直接判断两种
- rightSlashIndex = path.lastIndexOf("\\")||0,
- separater = leftSlashIndex > rightSlashIndex ? "/":"\\" ;
-
- path = path.substring(0, path.lastIndexOf(separater)+1);
- g(id).value = path;
-}
-
-function createFlashUploader(opt, callbacks) {
- //由于lang.flashI18n是静态属性,不可以直接进行修改,否则会影响到后续内容
- var i18n = utils.extend({},lang.flashI18n);
- //处理图片资源地址的编码,补全等问题
- for(var i in i18n){
- if(!(i in {"lang":1,"uploadingTF":1,"imageTF":1,"textEncoding":1}) && i18n[i]){
- i18n[i] = encodeURIComponent(editor.options.langPath + editor.options.lang + "/images/" + i18n[i]);
- }
- }
- opt = utils.extend(opt,i18n,false);
- var option = {
- createOptions:{
- id:'flash',
- url:opt.flashUrl,
- width:opt.width,
- height:opt.height,
- errorMessage:lang.flashError,
- wmode:browser.safari ? 'transparent' : 'window',
- ver:'10.0.0',
- vars:opt,
- container:opt.container
- }
- };
-
- option = extendProperty(callbacks, option);
- flashObj = new baidu.flash.imageUploader(option);
- flashContainer = $G(opt.container);
-}
-
-function extendProperty(fromObj, toObj) {
- for (var i in fromObj) {
- if (!toObj[i]) {
- toObj[i] = fromObj[i];
- }
- }
- return toObj;
-}
-
-//})();
-
-function getPasteData(id) {
- baidu.g("msg").innerHTML = lang.copySuccess + "";
- setTimeout(function() {
- baidu.g("msg").innerHTML = "";
- }, 5000);
- return baidu.g(id).value;
-}
-
-function createCopyButton(id, dataFrom) {
- baidu.swf.create({
- id:"copyFlash",
- url:"fClipboard_ueditor.swf",
- width:"58",
- height:"25",
- errorMessage:"",
- bgColor:"#CBCBCB",
- wmode:"transparent",
- ver:"10.0.0",
- vars:{
- tid:dataFrom
- }
- }, id
- );
-
- var clipboard = baidu.swf.getMovie("copyFlash");
- var clipinterval = setInterval(function() {
- if (clipboard && clipboard.flashInit) {
- clearInterval(clipinterval);
- clipboard.setHandCursor(true);
- clipboard.setContentFuncName("getPasteData");
- //clipboard.setMEFuncName("mouseEventHandler");
- }
- }, 500);
-}
+/**
+ * Created by JetBrains PhpStorm.
+ * User: taoqili
+ * Date: 12-1-30
+ * Time: 下午12:50
+ * To change this template use File | Settings | File Templates.
+ */
+
+
+
+var wordImage = {};
+//(function(){
+var g = baidu.g,
+ flashObj,flashContainer;
+
+wordImage.init = function(opt, callbacks) {
+ showLocalPath("localPath");
+ //createCopyButton("clipboard","localPath");
+ createFlashUploader(opt, callbacks);
+ addUploadListener();
+ addOkListener();
+};
+
+function hideFlash(){
+ flashObj = null;
+ flashContainer.innerHTML = "";
+}
+function addOkListener() {
+ dialog.onok = function() {
+ if (!imageUrls.length) return;
+ var urlPrefix = editor.getOpt('imageUrlPrefix'),
+ images = domUtils.getElementsByTagName(editor.document,"img");
+ editor.fireEvent('saveScene');
+ for (var i = 0,img; img = images[i++];) {
+ var src = img.getAttribute("word_img");
+ if (!src) continue;
+ for (var j = 0,url; url = imageUrls[j++];) {
+ if (src.indexOf(url.original.replace(" ","")) != -1) {
+ img.src = urlPrefix + url.url;
+ img.setAttribute("_src", urlPrefix + url.url); //同时修改"_src"属性
+ img.setAttribute("title",url.title);
+ domUtils.removeAttributes(img, ["word_img","style","width","height"]);
+ editor.fireEvent("selectionchange");
+ break;
+ }
+ }
+ }
+ editor.fireEvent('saveScene');
+ hideFlash();
+ };
+ dialog.oncancel = function(){
+ hideFlash();
+ }
+}
+
+/**
+ * 绑定开始上传事件
+ */
+function addUploadListener() {
+ g("upload").onclick = function () {
+ flashObj.upload();
+ this.style.display = "none";
+ };
+}
+
+function showLocalPath(id) {
+ //单张编辑
+ var img = editor.selection.getRange().getClosedNode();
+ var images = editor.execCommand('wordimage');
+ if(images.length==1 || img && img.tagName == 'IMG'){
+ g(id).value = images[0];
+ return;
+ }
+ var path = images[0];
+ var leftSlashIndex = path.lastIndexOf("/")||0, //不同版本的doc和浏览器都可能影响到这个符号,故直接判断两种
+ rightSlashIndex = path.lastIndexOf("\\")||0,
+ separater = leftSlashIndex > rightSlashIndex ? "/":"\\" ;
+
+ path = path.substring(0, path.lastIndexOf(separater)+1);
+ g(id).value = path;
+}
+
+function createFlashUploader(opt, callbacks) {
+ //由于lang.flashI18n是静态属性,不可以直接进行修改,否则会影响到后续内容
+ var i18n = utils.extend({},lang.flashI18n);
+ //处理图片资源地址的编码,补全等问题
+ for(var i in i18n){
+ if(!(i in {"lang":1,"uploadingTF":1,"imageTF":1,"textEncoding":1}) && i18n[i]){
+ i18n[i] = encodeURIComponent(editor.options.langPath + editor.options.lang + "/images/" + i18n[i]);
+ }
+ }
+ opt = utils.extend(opt,i18n,false);
+ var option = {
+ createOptions:{
+ id:'flash',
+ url:opt.flashUrl,
+ width:opt.width,
+ height:opt.height,
+ errorMessage:lang.flashError,
+ wmode:browser.safari ? 'transparent' : 'window',
+ ver:'10.0.0',
+ vars:opt,
+ container:opt.container
+ }
+ };
+
+ option = extendProperty(callbacks, option);
+ flashObj = new baidu.flash.imageUploader(option);
+ flashContainer = $G(opt.container);
+}
+
+function extendProperty(fromObj, toObj) {
+ for (var i in fromObj) {
+ if (!toObj[i]) {
+ toObj[i] = fromObj[i];
+ }
+ }
+ return toObj;
+}
+
+//})();
+
+function getPasteData(id) {
+ baidu.g("msg").innerHTML = lang.copySuccess + "";
+ setTimeout(function() {
+ baidu.g("msg").innerHTML = "";
+ }, 5000);
+ return baidu.g(id).value;
+}
+
+function createCopyButton(id, dataFrom) {
+ baidu.swf.create({
+ id:"copyFlash",
+ url:"fClipboard_ueditor.swf",
+ width:"58",
+ height:"25",
+ errorMessage:"",
+ bgColor:"#CBCBCB",
+ wmode:"transparent",
+ ver:"10.0.0",
+ vars:{
+ tid:dataFrom
+ }
+ }, id
+ );
+
+ var clipboard = baidu.swf.getMovie("copyFlash");
+ var clipinterval = setInterval(function() {
+ if (clipboard && clipboard.flashInit) {
+ clearInterval(clipinterval);
+ clipboard.setHandCursor(true);
+ clipboard.setContentFuncName("getPasteData");
+ //clipboard.setMEFuncName("mouseEventHandler");
+ }
+ }, 500);
+}
createCopyButton("clipboard", "localPath");
\ No newline at end of file
diff --git a/assets/index.html b/assets/index.html
new file mode 100644
index 0000000..f02b43c
--- /dev/null
+++ b/assets/index.html
@@ -0,0 +1,175 @@
+
+
+
+
完整demo
+
+
+
+
+
+
+
+
+
+
+
+
完整demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/php/Uploader.class.php b/assets/php/Uploader.class.php
new file mode 100644
index 0000000..ae9818a
--- /dev/null
+++ b/assets/php/Uploader.class.php
@@ -0,0 +1,372 @@
+ "临时文件错误",
+ "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
+ "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
+ "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
+ "ERROR_CREATE_DIR" => "目录创建失败",
+ "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
+ "ERROR_FILE_MOVE" => "文件保存时出错",
+ "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
+ "ERROR_WRITE_CONTENT" => "写入文件内容错误",
+ "ERROR_UNKNOWN" => "未知错误",
+ "ERROR_DEAD_LINK" => "链接不可用",
+ "ERROR_HTTP_LINK" => "链接不是http链接",
+ "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确",
+ "INVALID_URL" => "非法 URL",
+ "INVALID_IP" => "非法 IP"
+ );
+
+ /**
+ * 构造函数
+ * @param string $fileField 表单名称
+ * @param array $config 配置项
+ * @param bool $base64 是否解析base64编码,可省略。若开启,则$fileField代表的是base64编码的字符串表单名
+ */
+ public function __construct($fileField, $config, $type = "upload")
+ {
+ $this->fileField = $fileField;
+ $this->config = $config;
+ $this->type = $type;
+ if ($type == "remote") {
+ $this->saveRemote();
+ } else if($type == "base64") {
+ $this->upBase64();
+ } else {
+ $this->upFile();
+ }
+
+ $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']);
+ }
+
+ /**
+ * 上传文件的主处理方法
+ * @return mixed
+ */
+ private function upFile()
+ {
+ $file = $this->file = $_FILES[$this->fileField];
+ if (!$file) {
+ $this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND");
+ return;
+ }
+ if ($this->file['error']) {
+ $this->stateInfo = $this->getStateInfo($file['error']);
+ return;
+ } else if (!file_exists($file['tmp_name'])) {
+ $this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND");
+ return;
+ } else if (!is_uploaded_file($file['tmp_name'])) {
+ $this->stateInfo = $this->getStateInfo("ERROR_TMPFILE");
+ return;
+ }
+
+ $this->oriName = $file['name'];
+ $this->fileSize = $file['size'];
+ $this->fileType = $this->getFileExt();
+ $this->fullName = $this->getFullName();
+ $this->filePath = $this->getFilePath();
+ $this->fileName = $this->getFileName();
+ $dirname = dirname($this->filePath);
+
+ //检查文件大小是否超出限制
+ if (!$this->checkSize()) {
+ $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
+ return;
+ }
+
+ //检查是否不允许的文件格式
+ if (!$this->checkType()) {
+ $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
+ return;
+ }
+
+ //创建目录失败
+ if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
+ return;
+ } else if (!is_writeable($dirname)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
+ return;
+ }
+
+ //移动文件
+ if (!(move_uploaded_file($file["tmp_name"], $this->filePath) && file_exists($this->filePath))) { //移动失败
+ $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
+ } else { //移动成功
+ $this->stateInfo = $this->stateMap[0];
+ }
+ }
+
+ /**
+ * 处理base64编码的图片上传
+ * @return mixed
+ */
+ private function upBase64()
+ {
+ $base64Data = $_POST[$this->fileField];
+ $img = base64_decode($base64Data);
+
+ $this->oriName = $this->config['oriName'];
+ $this->fileSize = strlen($img);
+ $this->fileType = $this->getFileExt();
+ $this->fullName = $this->getFullName();
+ $this->filePath = $this->getFilePath();
+ $this->fileName = $this->getFileName();
+ $dirname = dirname($this->filePath);
+
+ //检查文件大小是否超出限制
+ if (!$this->checkSize()) {
+ $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
+ return;
+ }
+
+ //创建目录失败
+ if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
+ return;
+ } else if (!is_writeable($dirname)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
+ return;
+ }
+
+ //移动文件
+ if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
+ $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
+ } else { //移动成功
+ $this->stateInfo = $this->stateMap[0];
+ }
+
+ }
+
+ /**
+ * 拉取远程图片
+ * @return mixed
+ */
+ private function saveRemote()
+ {
+ $imgUrl = htmlspecialchars($this->fileField);
+ $imgUrl = str_replace("&", "&", $imgUrl);
+
+ //http开头验证
+ if (strpos($imgUrl, "http") !== 0) {
+ $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
+ return;
+ }
+
+ preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);
+ $host_with_protocol = count($matches) > 1 ? $matches[1] : '';
+
+ // 判断是否是合法 url
+ if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) {
+ $this->stateInfo = $this->getStateInfo("INVALID_URL");
+ return;
+ }
+
+ preg_match('/^https*:\/\/(.+)/', $host_with_protocol, $matches);
+ $host_without_protocol = count($matches) > 1 ? $matches[1] : '';
+
+ // 此时提取出来的可能是 ip 也有可能是域名,先获取 ip
+ $ip = gethostbyname($host_without_protocol);
+ // 判断是否是私有 ip
+ if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
+ $this->stateInfo = $this->getStateInfo("INVALID_IP");
+ return;
+ }
+
+ //获取请求头并检测死链
+ $heads = get_headers($imgUrl, 1);
+ if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
+ $this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK");
+ return;
+ }
+ //格式验证(扩展名验证和Content-Type验证)
+ $fileType = strtolower(strrchr($imgUrl, '.'));
+ if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
+ $this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE");
+ return;
+ }
+
+ //打开输出缓冲区并获取远程图片
+ ob_start();
+ $context = stream_context_create(
+ array('http' => array(
+ 'follow_location' => false // don't follow redirects
+ ))
+ );
+ readfile($imgUrl, false, $context);
+ $img = ob_get_contents();
+ ob_end_clean();
+ preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $m);
+
+ $this->oriName = $m ? $m[1]:"";
+ $this->fileSize = strlen($img);
+ $this->fileType = $this->getFileExt();
+ $this->fullName = $this->getFullName();
+ $this->filePath = $this->getFilePath();
+ $this->fileName = $this->getFileName();
+ $dirname = dirname($this->filePath);
+
+ //检查文件大小是否超出限制
+ if (!$this->checkSize()) {
+ $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
+ return;
+ }
+
+ //创建目录失败
+ if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
+ return;
+ } else if (!is_writeable($dirname)) {
+ $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
+ return;
+ }
+
+ //移动文件
+ if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
+ $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
+ } else { //移动成功
+ $this->stateInfo = $this->stateMap[0];
+ }
+
+ }
+
+ /**
+ * 上传错误检查
+ * @param $errCode
+ * @return string
+ */
+ private function getStateInfo($errCode)
+ {
+ return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
+ }
+
+ /**
+ * 获取文件扩展名
+ * @return string
+ */
+ private function getFileExt()
+ {
+ return strtolower(strrchr($this->oriName, '.'));
+ }
+
+ /**
+ * 重命名文件
+ * @return string
+ */
+ private function getFullName()
+ {
+ //替换日期事件
+ $t = time();
+ $d = explode('-', date("Y-y-m-d-H-i-s"));
+ $format = $this->config["pathFormat"];
+ $format = str_replace("{yyyy}", $d[0], $format);
+ $format = str_replace("{yy}", $d[1], $format);
+ $format = str_replace("{mm}", $d[2], $format);
+ $format = str_replace("{dd}", $d[3], $format);
+ $format = str_replace("{hh}", $d[4], $format);
+ $format = str_replace("{ii}", $d[5], $format);
+ $format = str_replace("{ss}", $d[6], $format);
+ $format = str_replace("{time}", $t, $format);
+
+ //过滤文件名的非法自负,并替换文件名
+ $oriName = substr($this->oriName, 0, strrpos($this->oriName, '.'));
+ $oriName = preg_replace("/[\|\?\"\<\>\/\*\\\\]+/", '', $oriName);
+ $format = str_replace("{filename}", $oriName, $format);
+
+ //替换随机字符串
+ $randNum = rand(1, 10000000000) . rand(1, 10000000000);
+ if (preg_match("/\{rand\:([\d]*)\}/i", $format, $matches)) {
+ $format = preg_replace("/\{rand\:[\d]*\}/i", substr($randNum, 0, $matches[1]), $format);
+ }
+
+ $ext = $this->getFileExt();
+ return $format . $ext;
+ }
+
+ /**
+ * 获取文件名
+ * @return string
+ */
+ private function getFileName () {
+ return substr($this->filePath, strrpos($this->filePath, '/') + 1);
+ }
+
+ /**
+ * 获取文件完整路径
+ * @return string
+ */
+ private function getFilePath()
+ {
+ $fullname = $this->fullName;
+ $rootPath = $_SERVER['DOCUMENT_ROOT'];
+
+ if (substr($fullname, 0, 1) != '/') {
+ $fullname = '/' . $fullname;
+ }
+
+ return $rootPath . $fullname;
+ }
+
+ /**
+ * 文件类型检测
+ * @return bool
+ */
+ private function checkType()
+ {
+ return in_array($this->getFileExt(), $this->config["allowFiles"]);
+ }
+
+ /**
+ * 文件大小检测
+ * @return bool
+ */
+ private function checkSize()
+ {
+ return $this->fileSize <= ($this->config["maxSize"]);
+ }
+
+ /**
+ * 获取当前上传成功文件的各项信息
+ * @return array
+ */
+ public function getFileInfo()
+ {
+ return array(
+ "state" => $this->stateInfo,
+ "url" => $this->fullName,
+ "title" => $this->fileName,
+ "original" => $this->oriName,
+ "type" => $this->fileType,
+ "size" => $this->fileSize
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/assets/php/action_crawler.php b/assets/php/action_crawler.php
new file mode 100644
index 0000000..b9e18df
--- /dev/null
+++ b/assets/php/action_crawler.php
@@ -0,0 +1,44 @@
+ $CONFIG['catcherPathFormat'],
+ "maxSize" => $CONFIG['catcherMaxSize'],
+ "allowFiles" => $CONFIG['catcherAllowFiles'],
+ "oriName" => "remote.png"
+);
+$fieldName = $CONFIG['catcherFieldName'];
+
+/* 抓取远程图片 */
+$list = array();
+if (isset($_POST[$fieldName])) {
+ $source = $_POST[$fieldName];
+} else {
+ $source = $_GET[$fieldName];
+}
+foreach ($source as $imgUrl) {
+ $item = new Uploader($imgUrl, $config, "remote");
+ $info = $item->getFileInfo();
+ array_push($list, array(
+ "state" => $info["state"],
+ "url" => $info["url"],
+ "size" => $info["size"],
+ "title" => htmlspecialchars($info["title"]),
+ "original" => htmlspecialchars($info["original"]),
+ "source" => htmlspecialchars($imgUrl)
+ ));
+}
+
+/* 返回抓取数据 */
+return json_encode(array(
+ 'state'=> count($list) ? 'SUCCESS':'ERROR',
+ 'list'=> $list
+));
\ No newline at end of file
diff --git a/assets/php/action_list.php b/assets/php/action_list.php
new file mode 100644
index 0000000..bf9cd62
--- /dev/null
+++ b/assets/php/action_list.php
@@ -0,0 +1,92 @@
+ "no match file",
+ "list" => array(),
+ "start" => $start,
+ "total" => count($files)
+ ));
+}
+
+/* 获取指定范围的列表 */
+$len = count($files);
+for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){
+ $list[] = $files[$i];
+}
+//倒序
+//for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){
+// $list[] = $files[$i];
+//}
+
+/* 返回数据 */
+$result = json_encode(array(
+ "state" => "SUCCESS",
+ "list" => $list,
+ "start" => $start,
+ "total" => count($files)
+));
+
+return $result;
+
+
+/**
+ * 遍历获取目录下的指定类型的文件
+ * @param $path
+ * @param array $files
+ * @return array
+ */
+function getfiles($path, $allowFiles, &$files = array())
+{
+ if (!is_dir($path)) return null;
+ if(substr($path, strlen($path) - 1) != '/') $path .= '/';
+ $handle = opendir($path);
+ while (false !== ($file = readdir($handle))) {
+ if ($file != '.' && $file != '..') {
+ $path2 = $path . $file;
+ if (is_dir($path2)) {
+ getfiles($path2, $allowFiles, $files);
+ } else {
+ if (preg_match("/\.(".$allowFiles.")$/i", $file)) {
+ $files[] = array(
+ 'url'=> substr($path2, strlen($_SERVER['DOCUMENT_ROOT'])),
+ 'mtime'=> filemtime($path2)
+ );
+ }
+ }
+ }
+ }
+ return $files;
+}
\ No newline at end of file
diff --git a/assets/php/action_upload.php b/assets/php/action_upload.php
new file mode 100644
index 0000000..d55b659
--- /dev/null
+++ b/assets/php/action_upload.php
@@ -0,0 +1,66 @@
+ $CONFIG['imagePathFormat'],
+ "maxSize" => $CONFIG['imageMaxSize'],
+ "allowFiles" => $CONFIG['imageAllowFiles']
+ );
+ $fieldName = $CONFIG['imageFieldName'];
+ break;
+ case 'uploadscrawl':
+ $config = array(
+ "pathFormat" => $CONFIG['scrawlPathFormat'],
+ "maxSize" => $CONFIG['scrawlMaxSize'],
+ "allowFiles" => $CONFIG['scrawlAllowFiles'],
+ "oriName" => "scrawl.png"
+ );
+ $fieldName = $CONFIG['scrawlFieldName'];
+ $base64 = "base64";
+ break;
+ case 'uploadvideo':
+ $config = array(
+ "pathFormat" => $CONFIG['videoPathFormat'],
+ "maxSize" => $CONFIG['videoMaxSize'],
+ "allowFiles" => $CONFIG['videoAllowFiles']
+ );
+ $fieldName = $CONFIG['videoFieldName'];
+ break;
+ case 'uploadfile':
+ default:
+ $config = array(
+ "pathFormat" => $CONFIG['filePathFormat'],
+ "maxSize" => $CONFIG['fileMaxSize'],
+ "allowFiles" => $CONFIG['fileAllowFiles']
+ );
+ $fieldName = $CONFIG['fileFieldName'];
+ break;
+}
+
+/* 生成上传实例对象并完成上传 */
+$up = new Uploader($fieldName, $config, $base64);
+
+/**
+ * 得到上传文件所对应的各个参数,数组结构
+ * array(
+ * "state" => "", //上传状态,上传成功时必须返回"SUCCESS"
+ * "url" => "", //返回的地址
+ * "title" => "", //新文件名
+ * "original" => "", //原始文件名
+ * "type" => "" //文件类型
+ * "size" => "", //文件大小
+ * )
+ */
+
+/* 返回数据 */
+return json_encode($up->getFileInfo());
diff --git a/assets/php/config.json b/assets/php/config.json
new file mode 100644
index 0000000..1792bba
--- /dev/null
+++ b/assets/php/config.json
@@ -0,0 +1,247 @@
+/* 前后端通信相关的配置,注释只允许使用多行方式 */
+{
+ /* 上传图片配置项 */
+ "imageActionName": "uploadimage",
+ /* 执行上传图片的action名称 */
+ "imageFieldName": "upfile",
+ /* 提交的图片表单名称 */
+ "imageMaxSize": 2048000,
+ /* 上传大小限制,单位B */
+ "imageAllowFiles": [
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp"
+ ],
+ /* 上传图片格式显示 */
+ "imageCompressEnable": true,
+ /* 是否压缩图片,默认是true */
+ "imageCompressBorder": 1600,
+ /* 图片压缩最长边限制 */
+ "imageInsertAlign": "none",
+ /* 插入的图片浮动方式 */
+ "imageUrlPrefix": "",
+ /* 图片访问路径前缀 */
+ "imagePathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
+ /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
+ /* {time} 会替换成时间戳 */
+ /* {yyyy} 会替换成四位年份 */
+ /* {yy} 会替换成两位年份 */
+ /* {mm} 会替换成两位月份 */
+ /* {dd} 会替换成两位日期 */
+ /* {hh} 会替换成两位小时 */
+ /* {ii} 会替换成两位分钟 */
+ /* {ss} 会替换成两位秒 */
+ /* 非法字符 \ : * ? " < > | */
+ /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
+
+ /* 涂鸦图片上传配置项 */
+ "scrawlActionName": "uploadscrawl",
+ /* 执行上传涂鸦的action名称 */
+ "scrawlFieldName": "upfile",
+ /* 提交的图片表单名称 */
+ "scrawlPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ "scrawlMaxSize": 2048000,
+ /* 上传大小限制,单位B */
+ "scrawlUrlPrefix": "",
+ /* 图片访问路径前缀 */
+ "scrawlInsertAlign": "none",
+ /* 截图工具上传 */
+ "snapscreenActionName": "uploadimage",
+ /* 执行上传截图的action名称 */
+ "snapscreenPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ "snapscreenUrlPrefix": "",
+ /* 图片访问路径前缀 */
+ "snapscreenInsertAlign": "none",
+ /* 插入的图片浮动方式 */
+
+ /* 抓取远程图片配置 */
+ "catcherLocalDomain": [
+ "127.0.0.1",
+ "localhost",
+ "img.baidu.com"
+ ],
+ "catcherActionName": "catchimage",
+ /* 执行抓取远程图片的action名称 */
+ "catcherFieldName": "source",
+ /* 提交的图片列表表单名称 */
+ "catcherPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ "catcherUrlPrefix": "",
+ /* 图片访问路径前缀 */
+ "catcherMaxSize": 2048000,
+ /* 上传大小限制,单位B */
+ "catcherAllowFiles": [
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp"
+ ],
+ /* 抓取图片格式显示 */
+
+ /* 上传视频配置 */
+ "videoActionName": "uploadvideo",
+ /* 执行上传视频的action名称 */
+ "videoFieldName": "upfile",
+ /* 提交的视频表单名称 */
+ "videoPathFormat": "/ueditor/php/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ "videoUrlPrefix": "",
+ /* 视频访问路径前缀 */
+ "videoMaxSize": 102400000,
+ /* 上传大小限制,单位B,默认100MB */
+ "videoAllowFiles": [
+ ".flv",
+ ".swf",
+ ".mkv",
+ ".avi",
+ ".rm",
+ ".rmvb",
+ ".mpeg",
+ ".mpg",
+ ".ogg",
+ ".ogv",
+ ".mov",
+ ".wmv",
+ ".mp4",
+ ".webm",
+ ".mp3",
+ ".wav",
+ ".mid"
+ ],
+ /* 上传视频格式显示 */
+
+ /* 上传文件配置 */
+ "fileActionName": "uploadfile",
+ /* controller里,执行上传视频的action名称 */
+ "fileFieldName": "upfile",
+ /* 提交的文件表单名称 */
+ "filePathFormat": "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}",
+ /* 上传保存路径,可以自定义保存路径和文件名格式 */
+ "fileUrlPrefix": "",
+ /* 文件访问路径前缀 */
+ "fileMaxSize": 51200000,
+ /* 上传大小限制,单位B,默认50MB */
+ "fileAllowFiles": [
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp",
+ ".flv",
+ ".swf",
+ ".mkv",
+ ".avi",
+ ".rm",
+ ".rmvb",
+ ".mpeg",
+ ".mpg",
+ ".ogg",
+ ".ogv",
+ ".mov",
+ ".wmv",
+ ".mp4",
+ ".webm",
+ ".mp3",
+ ".wav",
+ ".mid",
+ ".rar",
+ ".zip",
+ ".tar",
+ ".gz",
+ ".7z",
+ ".bz2",
+ ".cab",
+ ".iso",
+ ".doc",
+ ".docx",
+ ".xls",
+ ".xlsx",
+ ".ppt",
+ ".pptx",
+ ".pdf",
+ ".txt",
+ ".md",
+ ".xml"
+ ],
+ /* 上传文件格式显示 */
+
+ /* 列出指定目录下的图片 */
+ "imageManagerActionName": "listimage",
+ /* 执行图片管理的action名称 */
+ "imageManagerListPath": "/ueditor/php/upload/image/",
+ /* 指定要列出图片的目录 */
+ "imageManagerListSize": 20,
+ /* 每次列出文件数量 */
+ "imageManagerUrlPrefix": "",
+ /* 图片访问路径前缀 */
+ "imageManagerInsertAlign": "none",
+ /* 插入的图片浮动方式 */
+ "imageManagerAllowFiles": [
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp"
+ ],
+ /* 列出的文件类型 */
+
+ /* 列出指定目录下的文件 */
+ "fileManagerActionName": "listfile",
+ /* 执行文件管理的action名称 */
+ "fileManagerListPath": "/ueditor/php/upload/file/",
+ /* 指定要列出文件的目录 */
+ "fileManagerUrlPrefix": "",
+ /* 文件访问路径前缀 */
+ "fileManagerListSize": 20,
+ /* 每次列出文件数量 */
+ "fileManagerAllowFiles": [
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".bmp",
+ ".flv",
+ ".swf",
+ ".mkv",
+ ".avi",
+ ".rm",
+ ".rmvb",
+ ".mpeg",
+ ".mpg",
+ ".ogg",
+ ".ogv",
+ ".mov",
+ ".wmv",
+ ".mp4",
+ ".webm",
+ ".mp3",
+ ".wav",
+ ".mid",
+ ".rar",
+ ".zip",
+ ".tar",
+ ".gz",
+ ".7z",
+ ".bz2",
+ ".cab",
+ ".iso",
+ ".doc",
+ ".docx",
+ ".xls",
+ ".xlsx",
+ ".ppt",
+ ".pptx",
+ ".pdf",
+ ".txt",
+ ".md",
+ ".xml"
+ ]
+ /* 列出的文件类型 */
+}
\ No newline at end of file
diff --git a/assets/php/controller.php b/assets/php/controller.php
new file mode 100644
index 0000000..feac890
--- /dev/null
+++ b/assets/php/controller.php
@@ -0,0 +1,59 @@
+ '请求地址出错'
+ ));
+ break;
+}
+
+/* 输出结果 */
+if (isset($_GET["callback"])) {
+ if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
+ echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
+ } else {
+ echo json_encode(array(
+ 'state'=> 'callback参数不合法'
+ ));
+ }
+} else {
+ echo $result;
+}
\ No newline at end of file
diff --git a/assets/themes/default/css/ueditor.css b/assets/themes/default/css/ueditor.css
index 124e886..37b7edf 100644
--- a/assets/themes/default/css/ueditor.css
+++ b/assets/themes/default/css/ueditor.css
@@ -1,1691 +1,1691 @@
-/*基础UI构建
-*/
-/* common layer */
-.edui-default .edui-box {
- border: none;
- padding: 0;
- margin: 0;
- overflow: hidden;
-}
-
-.edui-default a.edui-box {
- display: block;
- text-decoration: none;
- color: black;
-}
-
-.edui-default a.edui-box:hover {
- text-decoration: none;
-}
-
-.edui-default a.edui-box:active {
- text-decoration: none;
-}
-
-.edui-default table.edui-box {
- border-collapse: collapse;
-}
-
-.edui-default ul.edui-box {
- list-style-type: none;
-}
-
-div.edui-box {
- position: relative;
- display: -moz-inline-box !important;
- display: inline-block !important;
- vertical-align: top;
-}
-
-.edui-default .edui-clearfix {
- zoom: 1
-}
-
-.edui-default .edui-clearfix:after {
- content: '\20';
- display: block;
- clear: both;
-}
-
- * html div.edui-box {
- display: inline !important;
-}
-
-*:first-child+html div.edui-box {
- display: inline !important;
-}
-
-/* control layout */
-.edui-default .edui-button-body, .edui-splitbutton-body, .edui-menubutton-body, .edui-combox-body {
- position: relative;
-}
-
-.edui-default .edui-popup {
- position: absolute;
- -webkit-user-select: none;
- -moz-user-select: none;
-}
-
-.edui-default .edui-popup .edui-shadow {
- position: absolute;
- z-index: -1;
-}
-
-.edui-default .edui-popup .edui-bordereraser {
- position: absolute;
- overflow: hidden;
-}
-
-.edui-default .edui-tablepicker .edui-canvas {
- position: relative;
-}
-
-.edui-default .edui-tablepicker .edui-canvas .edui-overlay {
- position: absolute;
-}
-
-.edui-default .edui-dialog-modalmask, .edui-dialog-dragmask {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
-}
-
-.edui-default .edui-toolbar {
- position: relative;
-}
-
-/*
- * default theme
- */
-.edui-default .edui-label {
- cursor: default;
-}
-
-.edui-default span.edui-clickable {
- color: blue;
- cursor: pointer;
- text-decoration: underline;
-}
-
-.edui-default span.edui-unclickable {
- color: gray;
- cursor: default;
-}
-/* 工具栏 */
-.edui-default .edui-toolbar {
- cursor: default;
- -webkit-user-select: none;
- -moz-user-select: none;
- padding: 1px;
- overflow: hidden; /*全屏下单独一行不占位*/
- zoom: 1;
- width:auto;
- height:auto;
-}
-
-.edui-default .edui-toolbar .edui-button,
-.edui-default .edui-toolbar .edui-splitbutton,
-.edui-default .edui-toolbar .edui-menubutton,
-.edui-default .edui-toolbar .edui-combox {
- margin: 1px;
-}
-/*UI工具栏、编辑区域、底部*/
-.edui-default .edui-editor {
- border: 1px solid #d4d4d4;
- background-color: white;
- position: relative;
- overflow: visible;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-.edui-editor div{
- width:auto;
- height:auto;
-}
-.edui-default .edui-editor-toolbarbox {
- position: relative;
- zoom: 1;
- -webkit-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
- -moz-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
- box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
- border-top-left-radius:2px;
- border-top-right-radius:2px;
-}
-
-.edui-default .edui-editor-toolbarboxouter {
- border-bottom: 1px solid #d4d4d4;
- background-color: #fafafa;
- background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
- background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
- background-repeat: repeat-x;
- /*border: 1px solid #d4d4d4;*/
- -webkit-border-radius: 4px 4px 0 0;
- -moz-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
- *zoom: 1;
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
-}
-
-.edui-default .edui-editor-toolbarboxinner {
- padding: 2px;
-}
-
-.edui-default .edui-editor-iframeholder {
- position: relative;
- /*for fix ie6 toolbarmsg under iframe bug. relative -> static */
- /*_position: static !important;*
-}
-
-.edui-default .edui-editor-iframeholder textarea {
- font-family: consolas, "Courier New", "lucida console", monospace;
- font-size: 12px;
- line-height: 18px;
-}
-
-.edui-default .edui-editor-bottombar {
- /*border-top: 1px solid #ccc;*/
- /*height: 20px;*/
- /*width: 40%;*/
- /*float: left;*/
- /*overflow: hidden;*/
-}
-
-.edui-default .edui-editor-bottomContainer {
- overflow: hidden;
-}
-
-.edui-default .edui-editor-bottomContainer table {
- width: 100%;
- height: 0;
- overflow: hidden;
- border-spacing: 0;
-}
-
-.edui-default .edui-editor-bottomContainer td {
- white-space: nowrap;
- border-top: 1px solid #ccc;
- line-height: 20px;
- font-size: 12px;
- font-family: Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
-}
-
-.edui-default .edui-editor-wordcount {
- text-align: right;
- margin-right: 5px;
- color: #aaa;
-}
-.edui-default .edui-editor-scale {
- width: 12px;
-}
-.edui-default .edui-editor-scale .edui-editor-icon {
- float: right;
- width: 100%;
- height: 12px;
- margin-top: 10px;
- background: url(../images/scale.png) no-repeat;
- cursor: se-resize;
-}
-.edui-default .edui-editor-breadcrumb {
- margin: 2px 0 0 3px;
-}
-
-.edui-default .edui-editor-breadcrumb span {
- cursor: pointer;
- text-decoration: underline;
- color: blue;
-}
-
-.edui-default .edui-toolbar .edui-for-fullscreen {
- float: right;
-}
-
-.edui-default .edui-bubble .edui-popup-content {
- border: 1px solid #DCAC6C;
- background-color: #fff6d9;
- padding: 5px;
- font-size: 10pt;
- font-family: "宋体";
-}
-
-.edui-default .edui-bubble .edui-shadow {
- /*box-shadow: 1px 1px 3px #818181;*/
- /*-webkit-box-shadow: 2px 2px 3px #818181;*/
- /*-moz-box-shadow: 2px 2px 3px #818181;*/
- /*filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius = '2', MakeShadow = 'true', ShadowOpacity = '0.5');*/
-}
-
-.edui-default .edui-editor-toolbarmsg {
- background-color: #FFF6D9;
- border-bottom: 1px solid #ccc;
- position: absolute;
- bottom: -25px;
- left: 0;
- z-index: 1009;
- width: 99.9%;
-}
-
-.edui-default .edui-editor-toolbarmsg-upload {
- font-size: 14px;
- color: blue;
- width: 100px;
- height: 16px;
- line-height: 16px;
- cursor: pointer;
- position: absolute;
- top: 5px;
- left: 350px;
-}
-
-.edui-default .edui-editor-toolbarmsg-label {
- font-size: 12px;
- line-height: 16px;
- padding: 4px;
-}
-
-.edui-default .edui-editor-toolbarmsg-close {
- float: right;
- width: 20px;
- height: 16px;
- line-height: 16px;
- cursor: pointer;
- color: red;
-}
-/*可选中菜单按钮*/
-.edui-default .edui-list .edui-bordereraser {
- display: none;
-}
-
-.edui-default .edui-listitem {
- padding: 1px;
- white-space: nowrap;
-}
-
-.edui-default .edui-list .edui-state-hover {
- position: relative;
- background-color: #fff5d4;
- border: 1px solid #dcac6c;
- padding: 0;
-}
-
-.edui-default .edui-for-fontfamily .edui-listitem-label {
- min-width: 130px;
- _width: 120px;
- font-size: 12px;
- height: 22px;
- line-height: 22px;
- padding-left: 5px;
-}
-.edui-default .edui-for-insertcode .edui-listitem-label {
- min-width: 120px;
- _width: 120px;
- font-size: 12px;
- height: 22px;
- line-height: 22px;
- padding-left: 5px;
-}
-.edui-default .edui-for-underline .edui-listitem-label {
- min-width: 120px;
- _width: 120px;
- padding: 3px 5px;
- font-size: 12px;
-}
-
-.edui-default .edui-for-fontsize .edui-listitem-label {
- min-width: 120px;
- _width: 120px;
- padding: 3px 5px;
-
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label {
- min-width: 200px;
- _width: 200px;
- padding: 2px 5px;
-}
-
-.edui-default .edui-for-rowspacingtop .edui-listitem-label,
-.edui-default .edui-for-rowspacingbottom .edui-listitem-label {
- min-width: 53px;
- _width: 53px;
- padding: 2px 5px;
-}
-
-.edui-default .edui-for-lineheight .edui-listitem-label {
- min-width: 53px;
- _width: 53px;
- padding: 2px 5px;
-}
-
-.edui-default .edui-for-customstyle .edui-listitem-label {
- min-width: 200px;
- _width: 200px;
- width: 200px !important;
- padding: 2px 5px;
-}
-/* 可选中按钮弹出菜单*/
-.edui-default .edui-menu {
- z-index: 3000;
-}
-
-.edui-default .edui-menu .edui-popup-content {
- padding: 3px;
-}
-
-.edui-default .edui-menu-body {
- _width: 150px;
- min-width: 170px;
- background: url("../images/sparator_v.png") repeat-y 25px;
-}
-
-.edui-default .edui-menuitem-body {
-}
-
-.edui-default .edui-menuitem {
- height: 20px;
- cursor: default;
- vertical-align: top;
-}
-
-.edui-default .edui-menuitem .edui-icon {
- width: 20px !important;
- height: 20px !important;
- background: url(../images/icons.png) 0 -4000px;
- background: url(../images/icons.gif) 0 -4000px\9;
-}
-
-.edui-default .edui-menuitem .edui-label {
- font-size: 12px;
- line-height: 20px;
- height: 20px;
- padding-left: 10px;
-}
-
-.edui-default .edui-state-checked .edui-menuitem-body {
- background: url("../images/icons-all.gif") no-repeat 6px -205px;
-}
-
-.edui-default .edui-state-disabled .edui-menuitem-label {
- color: gray;
-}
-
-
-/*不可选中菜单按钮 */
-.edui-default .edui-toolbar .edui-combox-body .edui-button-body {
- width: 60px;
- font-size: 12px;
- height: 20px;
- line-height: 20px;
- padding-left: 5px;
- white-space: nowrap;
- margin: 0 3px 0 0;
-}
-
-.edui-default .edui-toolbar .edui-combox-body .edui-arrow {
- background: url(../images/icons.png) -741px 0;
- _background: url(../images/icons.gif) -741px 0;
- height: 20px;
- width: 9px;
-}
-
-.edui-default .edui-toolbar .edui-combox .edui-combox-body {
- border: 1px solid #CCC;
- background-color: white;
- border-radius: 2px;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
-}
-
-.edui-default .edui-toolbar .edui-combox-body .edui-splitborder {
- display: none;
-}
-
-.edui-default .edui-toolbar .edui-combox-body .edui-arrow {
- border-left: 1px solid #CCC;
-}
-
-.edui-default .edui-toolbar .edui-state-hover .edui-combox-body {
- background-color: #fff5d4;
- border: 1px solid #dcac6c;
-}
-
-.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow {
- border-left: 1px solid #dcac6c;
-}
-
-.edui-default .edui-toolbar .edui-state-checked .edui-combox-body {
- background-color: #FFE69F;
- border: 1px solid #DCAC6C;
-}
-
-.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow {
- border-left: 1px solid #DCAC6C;
-}
-
-.edui-toolbar .edui-state-disabled .edui-combox-body {
- background-color: #F0F0EE;
- opacity: 0.3;
- filter: alpha(opacity = 30);
-}
-
-.edui-toolbar .edui-state-opened .edui-combox-body {
- background-color: white;
- border: 1px solid gray;
-}
-/*普通按钮样式及状态*/
-.edui-default .edui-toolbar .edui-button .edui-icon,
-.edui-default .edui-toolbar .edui-menubutton .edui-icon,
-.edui-default .edui-toolbar .edui-splitbutton .edui-icon {
- height: 20px !important;
- width: 20px !important;
- background-image: url(../images/icons.png);
- background-image: url(../images/icons.gif) \9;
-}
-
-.edui-default .edui-toolbar .edui-button .edui-button-wrap {
- padding: 1px;
- position: relative;
-}
-
-.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap {
- background-color: #fff5d4;
- padding: 0;
- border: 1px solid #dcac6c;
-}
-
-.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap {
- background-color: #ffe69f;
- padding: 0;
- border: 1px solid #dcac6c;
- border-radius: 2px;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
-}
-
-.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap {
- background-color: #ffffff;
- padding: 0;
- border: 1px solid gray;
-}
-.edui-default .edui-toolbar .edui-state-disabled .edui-label {
- color: #ccc;
-}
-.edui-default .edui-toolbar .edui-state-disabled .edui-icon {
- opacity: 0.3;
- filter: alpha(opacity = 30);
-}
-
-/* toolbar icons */
-.edui-default .edui-for-undo .edui-icon {
- background-position: -160px 0;
-}
-
-.edui-default .edui-for-redo .edui-icon {
- background-position: -100px 0;
-}
-
-.edui-default .edui-for-bold .edui-icon {
- background-position: 0 0;
-}
-
-.edui-default .edui-for-italic .edui-icon {
- background-position: -60px 0;
-}
-
-.edui-default .edui-for-fontborder .edui-icon {
- background-position:-160px -40px;
-}
-.edui-default .edui-for-underline .edui-icon {
- background-position: -140px 0;
-}
-
-.edui-default .edui-for-strikethrough .edui-icon {
- background-position: -120px 0;
-}
-
-.edui-default .edui-for-subscript .edui-icon {
- background-position: -600px 0;
-}
-
-.edui-default .edui-for-superscript .edui-icon {
- background-position: -620px 0;
-}
-
-.edui-default .edui-for-blockquote .edui-icon {
- background-position: -220px 0;
-}
-
-.edui-default .edui-for-forecolor .edui-icon {
- background-position: -720px 0;
-}
-
-.edui-default .edui-for-backcolor .edui-icon {
- background-position: -760px 0;
-}
-
-.edui-default .edui-for-inserttable .edui-icon {
- background-position: -580px -20px;
-}
-
-.edui-default .edui-for-autotypeset .edui-icon {
- background-position: -640px -40px;
-}
-
-.edui-default .edui-for-justifyleft .edui-icon {
- background-position: -460px 0;
-}
-
-.edui-default .edui-for-justifycenter .edui-icon {
- background-position: -420px 0;
-}
-
-.edui-default .edui-for-justifyright .edui-icon {
- background-position: -480px 0;
-}
-
-.edui-default .edui-for-justifyjustify .edui-icon {
- background-position: -440px 0;
-}
-
-.edui-default .edui-for-insertorderedlist .edui-icon {
- background-position: -80px 0;
-}
-
-.edui-default .edui-for-insertunorderedlist .edui-icon {
- background-position: -20px 0;
-}
-
-.edui-default .edui-for-lineheight .edui-icon {
- background-position: -725px -40px;
-}
-
-.edui-default .edui-for-rowspacingbottom .edui-icon {
- background-position: -745px -40px;
-}
-
-.edui-default .edui-for-rowspacingtop .edui-icon {
- background-position: -765px -40px;
-}
-
-.edui-default .edui-for-horizontal .edui-icon {
- background-position: -360px 0;
-}
-
-.edui-default .edui-for-link .edui-icon {
- background-position: -500px 0;
-}
-
-.edui-default .edui-for-code .edui-icon {
- background-position: -440px -40px;
-}
-
-.edui-default .edui-for-insertimage .edui-icon {
- background-position: -726px -77px;
-}
-
-.edui-default .edui-for-insertframe .edui-icon {
- background-position: -240px -40px;
-}
-
-.edui-default .edui-for-emoticon .edui-icon {
- background-position: -60px -20px;
-}
-
-.edui-default .edui-for-spechars .edui-icon {
- background-position: -240px 0;
-}
-
-.edui-default .edui-for-help .edui-icon {
- background-position: -340px 0;
-}
-
-.edui-default .edui-for-print .edui-icon {
- background-position: -440px -20px;
-}
-
-.edui-default .edui-for-preview .edui-icon {
- background-position: -420px -20px;
-}
-
-.edui-default .edui-for-selectall .edui-icon {
- background-position: -400px -20px;
-}
-
-.edui-default .edui-for-searchreplace .edui-icon {
- background-position: -520px -20px;
-}
-
-.edui-default .edui-for-map .edui-icon {
- background-position: -40px -40px;
-}
-
-.edui-default .edui-for-gmap .edui-icon {
- background-position: -260px -40px;
-}
-
-.edui-default .edui-for-insertvideo .edui-icon {
- background-position: -320px -20px;
-}
-
-.edui-default .edui-for-time .edui-icon {
- background-position: -160px -20px;
-}
-
-.edui-default .edui-for-date .edui-icon {
- background-position: -140px -20px;
-}
-
-.edui-default .edui-for-cut .edui-icon {
- background-position: -680px 0;
-}
-
-.edui-default .edui-for-copy .edui-icon {
- background-position: -700px 0;
-}
-
-.edui-default .edui-for-paste .edui-icon {
- background-position: -560px 0;
-}
-
-.edui-default .edui-for-formatmatch .edui-icon {
- background-position: -40px 0;
-}
-
-.edui-default .edui-for-pasteplain .edui-icon {
- background-position: -360px -20px;
-}
-
-.edui-default .edui-for-directionalityltr .edui-icon {
- background-position: -20px -20px;
-}
-
-.edui-default .edui-for-directionalityrtl .edui-icon {
- background-position: -40px -20px;
-}
-
-.edui-default .edui-for-source .edui-icon {
- background-position: -261px -0px;
-}
-
-.edui-default .edui-for-removeformat .edui-icon {
- background-position: -580px 0;
-}
-
-.edui-default .edui-for-unlink .edui-icon {
- background-position: -640px 0;
-}
-
-.edui-default .edui-for-touppercase .edui-icon {
- background-position: -786px 0;
-}
-
-.edui-default .edui-for-tolowercase .edui-icon {
- background-position: -806px 0;
-}
-
-.edui-default .edui-for-insertrow .edui-icon {
- background-position: -478px -76px;
-}
-
-.edui-default .edui-for-insertrownext .edui-icon {
- background-position: -498px -76px;
-}
-
-.edui-default .edui-for-insertcol .edui-icon {
- background-position: -455px -76px;
-}
-
-.edui-default .edui-for-insertcolnext .edui-icon {
- background-position: -429px -76px;
-}
-
-.edui-default .edui-for-mergeright .edui-icon {
- background-position: -60px -40px;
-}
-
-.edui-default .edui-for-mergedown .edui-icon {
- background-position: -80px -40px;
-}
-
-.edui-default .edui-for-splittorows .edui-icon {
- background-position: -100px -40px;
-}
-
-.edui-default .edui-for-splittocols .edui-icon {
- background-position: -120px -40px;
-}
-
-.edui-default .edui-for-insertparagraphbeforetable .edui-icon {
- background-position: -140px -40px;
-}
-
-.edui-default .edui-for-deleterow .edui-icon {
- background-position: -660px -20px;
-}
-
-.edui-default .edui-for-deletecol .edui-icon {
- background-position: -640px -20px;
-}
-
-.edui-default .edui-for-splittocells .edui-icon {
- background-position: -800px -20px;
-}
-
-.edui-default .edui-for-mergecells .edui-icon {
- background-position: -760px -20px;
-}
-
-.edui-default .edui-for-deletetable .edui-icon {
- background-position: -620px -20px;
-}
-
-.edui-default .edui-for-cleardoc .edui-icon {
- background-position: -520px 0;
-}
-
-.edui-default .edui-for-fullscreen .edui-icon {
- background-position: -100px -20px;
-}
-
-.edui-default .edui-for-anchor .edui-icon {
- background-position: -200px 0;
-}
-
-.edui-default .edui-for-pagebreak .edui-icon {
- background-position: -460px -40px;
-}
-
-.edui-default .edui-for-imagenone .edui-icon {
- background-position: -480px -40px;
-}
-
-.edui-default .edui-for-imageleft .edui-icon {
- background-position: -500px -40px;
-}
-
-.edui-default .edui-for-wordimage .edui-icon {
- background-position: -660px -40px;
-}
-
-.edui-default .edui-for-imageright .edui-icon {
- background-position: -520px -40px;
-}
-
-.edui-default .edui-for-imagecenter .edui-icon {
- background-position: -540px -40px;
-}
-
-.edui-default .edui-for-indent .edui-icon {
- background-position: -400px 0;
-}
-
-.edui-default .edui-for-outdent .edui-icon {
- background-position: -540px 0;
-}
-
-.edui-default .edui-for-webapp .edui-icon {
- background-position: -601px -40px
-}
-
-.edui-default .edui-for-table .edui-icon {
- background-position: -580px -20px;
-}
-
-.edui-default .edui-for-edittable .edui-icon {
- background-position: -420px -40px;
-}
-
-.edui-default .edui-for-template .edui-icon {
- background-position: -339px -40px;
-}
-
-.edui-default .edui-for-delete .edui-icon {
- background-position: -360px -40px;
-}
-
-.edui-default .edui-for-attachment .edui-icon {
- background-position: -620px -40px;
-}
-
-.edui-default .edui-for-edittd .edui-icon {
- background-position: -700px -40px;
-}
-
-.edui-default .edui-for-snapscreen .edui-icon {
- background-position: -581px -40px
-}
-
-.edui-default .edui-for-scrawl .edui-icon {
- background-position: -801px -41px
-}
-
-.edui-default .edui-for-background .edui-icon {
- background-position: -680px -40px;
-}
-
-.edui-default .edui-for-music .edui-icon {
- background-position: -18px -40px
-}
-
-.edui-default .edui-for-formula .edui-icon {
- background-position: -200px -40px
-}
-
-.edui-default .edui-for-aligntd .edui-icon {
- background-position: -236px -76px;
-}
-
-.edui-default .edui-for-insertparagraphtrue .edui-icon {
- background-position: -625px -76px;
-}
-
-.edui-default .edui-for-insertparagraph .edui-icon {
- background-position: -602px -76px;
-}
-
-.edui-default .edui-for-insertcaption .edui-icon {
- background-position: -336px -76px;
-}
-
-.edui-default .edui-for-deletecaption .edui-icon {
- background-position: -362px -76px;
-}
-
-.edui-default .edui-for-inserttitle .edui-icon {
- background-position: -286px -76px;
-}
-
-.edui-default .edui-for-deletetitle .edui-icon {
- background-position: -311px -76px;
-}
-
-.edui-default .edui-for-aligntable .edui-icon {
- background-position: -440px 0;
-}
-
-.edui-default .edui-for-tablealignment-left .edui-icon {
- background-position: -460px 0;
-}
-
-.edui-default .edui-for-tablealignment-center .edui-icon {
- background-position: -420px 0;
-}
-
-.edui-default .edui-for-tablealignment-right .edui-icon {
- background-position: -480px 0;
-}
-
-.edui-default .edui-for-drafts .edui-icon {
- background-position: -560px 0;
-}
-
-.edui-default .edui-for-charts .edui-icon {
- background: url(../images/charts.png) no-repeat 2px 3px!important;
-}
-
-.edui-default .edui-for-inserttitlecol .edui-icon {
- background-position: -673px -76px;
-}
-
-.edui-default .edui-for-deletetitlecol .edui-icon {
- background-position: -698px -76px;
-}
-
-.edui-default .edui-for-simpleupload .edui-icon {
- background-position: -380px 0px;
-}
-/*splitbutton*/
-.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow,
-.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow {
- background: url(../images/icons.png) -741px 0;
- _background: url(../images/icons.gif) -741px 0;
- height: 20px;
- width: 9px;
-}
-
-.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body,
-.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body {
- padding: 1px;
-}
-
-.edui-default .edui-toolbar .edui-splitborder {
- width: 1px;
- height: 20px;
-}
-
-.edui-default .edui-toolbar .edui-state-hover .edui-splitborder {
- width: 1px;
- border-left: 0px solid #dcac6c;
-}
-
-.edui-default .edui-toolbar .edui-state-active .edui-splitborder {
- width: 0;
- border-left: 1px solid gray;
-}
-
-.edui-default .edui-toolbar .edui-state-opened .edui-splitborder {
- width: 1px;
- border: 0;
-}
-
-.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body,
-.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body {
- background-color: #fff5d4;
- border: 1px solid #dcac6c;
- padding: 0;
-}
-
-.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body,
-.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body {
- background-color: #FFE69F;
- border: 1px solid #DCAC6C;
- padding: 0;
-}
-
-.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body,
-.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body {
- background-color: #ffffff;
- border: 1px solid gray;
- padding: 0;
-}
-
-.edui-default .edui-state-disabled .edui-arrow {
- opacity: 0.3;
- _filter: alpha(opacity = 30);
-}
-
-.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body,
-.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body {
- background-color: white;
- border: 1px solid gray;
- padding: 0;
-}
-
-.edui-default .edui-for-insertorderedlist .edui-bordereraser,
-.edui-default .edui-for-lineheight .edui-bordereraser,
-.edui-default .edui-for-rowspacingtop .edui-bordereraser,
-.edui-default .edui-for-rowspacingbottom .edui-bordereraser,
-.edui-default .edui-for-insertunorderedlist .edui-bordereraser {
- background-color: white;
-}
-
-/* 解决嵌套导致的图标问题 */
-.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon,
-.edui-default .edui-for-lineheight .edui-popup-body .edui-icon,
-.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon,
-.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon,
-.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon {
- /*background-position: 0 -40px;*/
- background-image: none ;
-}
-
-/* 弹出菜单 */
-.edui-default .edui-popup {
- z-index: 3000;
- background-color: #ffffff;
- width:auto;
- height:auto;
-
-}
-
-.edui-default .edui-popup .edui-shadow {
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
-}
-
-.edui-default .edui-popup-content {
- border:1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- *border-right-width: 2px;
- *border-bottom-width: 2px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- -webkit-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
- box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
- padding: 5px;
- background:#ffffff;
-}
-
-.edui-default .edui-popup .edui-bordereraser {
- background-color: white;
- height: 3px;
-}
-
-.edui-default .edui-menu .edui-bordereraser {
- height: 3px;
-}
-
-.edui-default .edui-anchor-topleft .edui-bordereraser {
- left: 1px;
- top: -2px;
-}
-
-.edui-default .edui-anchor-topright .edui-bordereraser {
- right: 1px;
- top: -2px;
-}
-
-.edui-default .edui-anchor-bottomleft .edui-bordereraser {
- left: 0;
- bottom: -6px;
- height: 7px;
- border-left: 1px solid gray;
- border-right: 1px solid gray;
-}
-
-.edui-default .edui-anchor-bottomright .edui-bordereraser {
- right: 0;
- bottom: -6px;
- height: 7px;
- border-left: 1px solid gray;
- border-right: 1px solid gray;
-}
-
-.edui-popup div{
- width:auto;
- height:auto;
-}
-.edui-default .edui-editor-messageholder {
- display: block;
- width: 150px;
- height: auto;
- border: 0;
- margin: 0;
- padding: 0;
- position: absolute;
- top: 28px;
- right: 3px;
-}
-
-.edui-default .edui-message{
- min-height: 10px;
- text-shadow: 0 1px 0 rgba(255,255,255,0.5);
- padding: 0;
- margin-bottom: 3px;
- position: relative;
-}
-.edui-default .edui-message-body{
- border-radius: 3px;
- padding: 8px 15px 8px 8px;
- color: #c09853;
- background-color: #fcf8e3;
- border: 1px solid #fbeed5;
-}
-.edui-default .edui-message-type-info{
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #bce8f1
-}
-.edui-default .edui-message-type-success{
- color: #468847;
- background-color: #dff0d8;
- border-color: #d6e9c6
-}
-.edui-default .edui-message-type-danger,
-.edui-default .edui-message-type-error{
- color: #b94a48;
- background-color: #f2dede;
- border-color: #eed3d7
-}
-.edui-default .edui-message .edui-message-closer {
- display: block;
- width: 16px;
- height: 16px;
- line-height: 16px;
- position: absolute;
- top: 0;
- right: 0;
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- float: right;
- font-size: 20px;
- font-weight: bold;
- color: #999;
- text-shadow: 0 1px 0 #fff;
- font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
-}
-.edui-default .edui-message .edui-message-content {
- font-size: 10pt;
- word-wrap: break-word;
- word-break: normal;
-}
-/* 弹出对话框按钮和对话框大小 */
-.edui-default .edui-dialog {
- z-index: 2000;
- position: absolute;
-
-}
-
-.edui-dialog div{
- width:auto;
-}
-
-.edui-default .edui-dialog-wrap {
- margin-right: 6px;
- margin-bottom: 6px;
-}
-
-.edui-default .edui-dialog-fullscreen-flag {
- margin-right: 0;
- margin-bottom: 0;
-}
-
-.edui-default .edui-dialog-body {
- position: relative;
- padding:2px 0 0 2px;
- _zoom: 1;
-}
-
-.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body {
- padding: 0;
-}
-
-.edui-default .edui-dialog-shadow {
- position: absolute;
- z-index: -1;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- background-color: #ffffff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- *border-right-width: 2px;
- *border-bottom-width: 2px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
-}
-
-.edui-default .edui-dialog-foot {
- background-color: white;
-}
-
-.edui-default .edui-dialog-titlebar {
- height: 26px;
- border-bottom: 1px solid #c6c6c6;
- background: url(../images/dialog-title-bg.png) repeat-x bottom;
- position: relative;
- cursor: move;
-}
-.edui-default .edui-dialog-caption {
- font-weight: bold;
- font-size: 12px;
- line-height: 26px;
- padding-left: 5px;
-}
-
-.edui-default .edui-dialog-draghandle {
- height: 26px;
+/*基础UI构建
+*/
+/* common layer */
+.edui-default .edui-box {
+ border: none;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+}
+
+.edui-default a.edui-box {
+ display: block;
+ text-decoration: none;
+ color: black;
+}
+
+.edui-default a.edui-box:hover {
+ text-decoration: none;
+}
+
+.edui-default a.edui-box:active {
+ text-decoration: none;
+}
+
+.edui-default table.edui-box {
+ border-collapse: collapse;
+}
+
+.edui-default ul.edui-box {
+ list-style-type: none;
+}
+
+div.edui-box {
+ position: relative;
+ display: -moz-inline-box !important;
+ display: inline-block !important;
+ vertical-align: top;
+}
+
+.edui-default .edui-clearfix {
+ zoom: 1
+}
+
+.edui-default .edui-clearfix:after {
+ content: '\20';
+ display: block;
+ clear: both;
+}
+
+ * html div.edui-box {
+ display: inline !important;
+}
+
+*:first-child+html div.edui-box {
+ display: inline !important;
+}
+
+/* control layout */
+.edui-default .edui-button-body, .edui-splitbutton-body, .edui-menubutton-body, .edui-combox-body {
+ position: relative;
+}
+
+.edui-default .edui-popup {
+ position: absolute;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+}
+
+.edui-default .edui-popup .edui-shadow {
+ position: absolute;
+ z-index: -1;
+}
+
+.edui-default .edui-popup .edui-bordereraser {
+ position: absolute;
+ overflow: hidden;
+}
+
+.edui-default .edui-tablepicker .edui-canvas {
+ position: relative;
+}
+
+.edui-default .edui-tablepicker .edui-canvas .edui-overlay {
+ position: absolute;
+}
+
+.edui-default .edui-dialog-modalmask, .edui-dialog-dragmask {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.edui-default .edui-toolbar {
+ position: relative;
+}
+
+/*
+ * default theme
+ */
+.edui-default .edui-label {
+ cursor: default;
+}
+
+.edui-default span.edui-clickable {
+ color: blue;
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.edui-default span.edui-unclickable {
+ color: gray;
+ cursor: default;
+}
+/* 工具栏 */
+.edui-default .edui-toolbar {
+ cursor: default;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ padding: 1px;
+ overflow: hidden; /*全屏下单独一行不占位*/
+ zoom: 1;
+ width:auto;
+ height:auto;
+}
+
+.edui-default .edui-toolbar .edui-button,
+.edui-default .edui-toolbar .edui-splitbutton,
+.edui-default .edui-toolbar .edui-menubutton,
+.edui-default .edui-toolbar .edui-combox {
+ margin: 1px;
+}
+/*UI工具栏、编辑区域、底部*/
+.edui-default .edui-editor {
+ border: 1px solid #d4d4d4;
+ background-color: white;
+ position: relative;
+ overflow: visible;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.edui-editor div{
+ width:auto;
+ height:auto;
+}
+.edui-default .edui-editor-toolbarbox {
+ position: relative;
+ zoom: 1;
+ -webkit-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
+ -moz-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
+ box-shadow:0 1px 4px rgba(204, 204, 204, 0.6);
+ border-top-left-radius:2px;
+ border-top-right-radius:2px;
+}
+
+.edui-default .edui-editor-toolbarboxouter {
+ border-bottom: 1px solid #d4d4d4;
+ background-color: #fafafa;
+ background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
+ background-repeat: repeat-x;
+ /*border: 1px solid #d4d4d4;*/
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+}
+
+.edui-default .edui-editor-toolbarboxinner {
+ padding: 2px;
+}
+
+.edui-default .edui-editor-iframeholder {
+ position: relative;
+ /*for fix ie6 toolbarmsg under iframe bug. relative -> static */
+ /*_position: static !important;*
+}
+
+.edui-default .edui-editor-iframeholder textarea {
+ font-family: consolas, "Courier New", "lucida console", monospace;
+ font-size: 12px;
+ line-height: 18px;
+}
+
+.edui-default .edui-editor-bottombar {
+ /*border-top: 1px solid #ccc;*/
+ /*height: 20px;*/
+ /*width: 40%;*/
+ /*float: left;*/
+ /*overflow: hidden;*/
+}
+
+.edui-default .edui-editor-bottomContainer {
+ overflow: hidden;
+}
+
+.edui-default .edui-editor-bottomContainer table {
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ border-spacing: 0;
+}
+
+.edui-default .edui-editor-bottomContainer td {
+ white-space: nowrap;
+ border-top: 1px solid #ccc;
+ line-height: 20px;
+ font-size: 12px;
+ font-family: Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
+}
+
+.edui-default .edui-editor-wordcount {
+ text-align: right;
+ margin-right: 5px;
+ color: #aaa;
+}
+.edui-default .edui-editor-scale {
+ width: 12px;
+}
+.edui-default .edui-editor-scale .edui-editor-icon {
+ float: right;
+ width: 100%;
+ height: 12px;
+ margin-top: 10px;
+ background: url(../images/scale.png) no-repeat;
+ cursor: se-resize;
+}
+.edui-default .edui-editor-breadcrumb {
+ margin: 2px 0 0 3px;
+}
+
+.edui-default .edui-editor-breadcrumb span {
+ cursor: pointer;
+ text-decoration: underline;
+ color: blue;
+}
+
+.edui-default .edui-toolbar .edui-for-fullscreen {
+ float: right;
+}
+
+.edui-default .edui-bubble .edui-popup-content {
+ border: 1px solid #DCAC6C;
+ background-color: #fff6d9;
+ padding: 5px;
+ font-size: 10pt;
+ font-family: "宋体";
+}
+
+.edui-default .edui-bubble .edui-shadow {
+ /*box-shadow: 1px 1px 3px #818181;*/
+ /*-webkit-box-shadow: 2px 2px 3px #818181;*/
+ /*-moz-box-shadow: 2px 2px 3px #818181;*/
+ /*filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius = '2', MakeShadow = 'true', ShadowOpacity = '0.5');*/
+}
+
+.edui-default .edui-editor-toolbarmsg {
+ background-color: #FFF6D9;
+ border-bottom: 1px solid #ccc;
+ position: absolute;
+ bottom: -25px;
+ left: 0;
+ z-index: 1009;
+ width: 99.9%;
+}
+
+.edui-default .edui-editor-toolbarmsg-upload {
+ font-size: 14px;
+ color: blue;
+ width: 100px;
+ height: 16px;
+ line-height: 16px;
+ cursor: pointer;
+ position: absolute;
+ top: 5px;
+ left: 350px;
+}
+
+.edui-default .edui-editor-toolbarmsg-label {
+ font-size: 12px;
+ line-height: 16px;
+ padding: 4px;
+}
+
+.edui-default .edui-editor-toolbarmsg-close {
+ float: right;
+ width: 20px;
+ height: 16px;
+ line-height: 16px;
+ cursor: pointer;
+ color: red;
+}
+/*可选中菜单按钮*/
+.edui-default .edui-list .edui-bordereraser {
+ display: none;
+}
+
+.edui-default .edui-listitem {
+ padding: 1px;
+ white-space: nowrap;
+}
+
+.edui-default .edui-list .edui-state-hover {
+ position: relative;
+ background-color: #fff5d4;
+ border: 1px solid #dcac6c;
+ padding: 0;
+}
+
+.edui-default .edui-for-fontfamily .edui-listitem-label {
+ min-width: 130px;
+ _width: 120px;
+ font-size: 12px;
+ height: 22px;
+ line-height: 22px;
+ padding-left: 5px;
+}
+.edui-default .edui-for-insertcode .edui-listitem-label {
+ min-width: 120px;
+ _width: 120px;
+ font-size: 12px;
+ height: 22px;
+ line-height: 22px;
+ padding-left: 5px;
+}
+.edui-default .edui-for-underline .edui-listitem-label {
+ min-width: 120px;
+ _width: 120px;
+ padding: 3px 5px;
+ font-size: 12px;
+}
+
+.edui-default .edui-for-fontsize .edui-listitem-label {
+ min-width: 120px;
+ _width: 120px;
+ padding: 3px 5px;
+
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label {
+ min-width: 200px;
+ _width: 200px;
+ padding: 2px 5px;
+}
+
+.edui-default .edui-for-rowspacingtop .edui-listitem-label,
+.edui-default .edui-for-rowspacingbottom .edui-listitem-label {
+ min-width: 53px;
+ _width: 53px;
+ padding: 2px 5px;
+}
+
+.edui-default .edui-for-lineheight .edui-listitem-label {
+ min-width: 53px;
+ _width: 53px;
+ padding: 2px 5px;
+}
+
+.edui-default .edui-for-customstyle .edui-listitem-label {
+ min-width: 200px;
+ _width: 200px;
+ width: 200px !important;
+ padding: 2px 5px;
+}
+/* 可选中按钮弹出菜单*/
+.edui-default .edui-menu {
+ z-index: 3000;
+}
+
+.edui-default .edui-menu .edui-popup-content {
+ padding: 3px;
+}
+
+.edui-default .edui-menu-body {
+ _width: 150px;
+ min-width: 170px;
+ background: url("../images/sparator_v.png") repeat-y 25px;
+}
+
+.edui-default .edui-menuitem-body {
+}
+
+.edui-default .edui-menuitem {
+ height: 20px;
+ cursor: default;
+ vertical-align: top;
+}
+
+.edui-default .edui-menuitem .edui-icon {
+ width: 20px !important;
+ height: 20px !important;
+ background: url(../images/icons.png) 0 -4000px;
+ background: url(../images/icons.gif) 0 -4000px\9;
+}
+
+.edui-default .edui-menuitem .edui-label {
+ font-size: 12px;
+ line-height: 20px;
+ height: 20px;
+ padding-left: 10px;
+}
+
+.edui-default .edui-state-checked .edui-menuitem-body {
+ background: url("../images/icons-all.gif") no-repeat 6px -205px;
+}
+
+.edui-default .edui-state-disabled .edui-menuitem-label {
+ color: gray;
+}
+
+
+/*不可选中菜单按钮 */
+.edui-default .edui-toolbar .edui-combox-body .edui-button-body {
+ width: 60px;
+ font-size: 12px;
+ height: 20px;
+ line-height: 20px;
+ padding-left: 5px;
+ white-space: nowrap;
+ margin: 0 3px 0 0;
+}
+
+.edui-default .edui-toolbar .edui-combox-body .edui-arrow {
+ background: url(../images/icons.png) -741px 0;
+ _background: url(../images/icons.gif) -741px 0;
+ height: 20px;
+ width: 9px;
+}
+
+.edui-default .edui-toolbar .edui-combox .edui-combox-body {
+ border: 1px solid #CCC;
+ background-color: white;
+ border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+}
+
+.edui-default .edui-toolbar .edui-combox-body .edui-splitborder {
+ display: none;
+}
+
+.edui-default .edui-toolbar .edui-combox-body .edui-arrow {
+ border-left: 1px solid #CCC;
+}
+
+.edui-default .edui-toolbar .edui-state-hover .edui-combox-body {
+ background-color: #fff5d4;
+ border: 1px solid #dcac6c;
+}
+
+.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow {
+ border-left: 1px solid #dcac6c;
+}
+
+.edui-default .edui-toolbar .edui-state-checked .edui-combox-body {
+ background-color: #FFE69F;
+ border: 1px solid #DCAC6C;
+}
+
+.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow {
+ border-left: 1px solid #DCAC6C;
+}
+
+.edui-toolbar .edui-state-disabled .edui-combox-body {
+ background-color: #F0F0EE;
+ opacity: 0.3;
+ filter: alpha(opacity = 30);
+}
+
+.edui-toolbar .edui-state-opened .edui-combox-body {
+ background-color: white;
+ border: 1px solid gray;
+}
+/*普通按钮样式及状态*/
+.edui-default .edui-toolbar .edui-button .edui-icon,
+.edui-default .edui-toolbar .edui-menubutton .edui-icon,
+.edui-default .edui-toolbar .edui-splitbutton .edui-icon {
+ height: 20px !important;
+ width: 20px !important;
+ background-image: url(../images/icons.png);
+ background-image: url(../images/icons.gif) \9;
+}
+
+.edui-default .edui-toolbar .edui-button .edui-button-wrap {
+ padding: 1px;
+ position: relative;
+}
+
+.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap {
+ background-color: #fff5d4;
+ padding: 0;
+ border: 1px solid #dcac6c;
+}
+
+.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap {
+ background-color: #ffe69f;
+ padding: 0;
+ border: 1px solid #dcac6c;
+ border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+}
+
+.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap {
+ background-color: #ffffff;
+ padding: 0;
+ border: 1px solid gray;
+}
+.edui-default .edui-toolbar .edui-state-disabled .edui-label {
+ color: #ccc;
+}
+.edui-default .edui-toolbar .edui-state-disabled .edui-icon {
+ opacity: 0.3;
+ filter: alpha(opacity = 30);
+}
+
+/* toolbar icons */
+.edui-default .edui-for-undo .edui-icon {
+ background-position: -160px 0;
+}
+
+.edui-default .edui-for-redo .edui-icon {
+ background-position: -100px 0;
+}
+
+.edui-default .edui-for-bold .edui-icon {
+ background-position: 0 0;
+}
+
+.edui-default .edui-for-italic .edui-icon {
+ background-position: -60px 0;
+}
+
+.edui-default .edui-for-fontborder .edui-icon {
+ background-position:-160px -40px;
+}
+.edui-default .edui-for-underline .edui-icon {
+ background-position: -140px 0;
+}
+
+.edui-default .edui-for-strikethrough .edui-icon {
+ background-position: -120px 0;
+}
+
+.edui-default .edui-for-subscript .edui-icon {
+ background-position: -600px 0;
+}
+
+.edui-default .edui-for-superscript .edui-icon {
+ background-position: -620px 0;
+}
+
+.edui-default .edui-for-blockquote .edui-icon {
+ background-position: -220px 0;
+}
+
+.edui-default .edui-for-forecolor .edui-icon {
+ background-position: -720px 0;
+}
+
+.edui-default .edui-for-backcolor .edui-icon {
+ background-position: -760px 0;
+}
+
+.edui-default .edui-for-inserttable .edui-icon {
+ background-position: -580px -20px;
+}
+
+.edui-default .edui-for-autotypeset .edui-icon {
+ background-position: -640px -40px;
+}
+
+.edui-default .edui-for-justifyleft .edui-icon {
+ background-position: -460px 0;
+}
+
+.edui-default .edui-for-justifycenter .edui-icon {
+ background-position: -420px 0;
+}
+
+.edui-default .edui-for-justifyright .edui-icon {
+ background-position: -480px 0;
+}
+
+.edui-default .edui-for-justifyjustify .edui-icon {
+ background-position: -440px 0;
+}
+
+.edui-default .edui-for-insertorderedlist .edui-icon {
+ background-position: -80px 0;
+}
+
+.edui-default .edui-for-insertunorderedlist .edui-icon {
+ background-position: -20px 0;
+}
+
+.edui-default .edui-for-lineheight .edui-icon {
+ background-position: -725px -40px;
+}
+
+.edui-default .edui-for-rowspacingbottom .edui-icon {
+ background-position: -745px -40px;
+}
+
+.edui-default .edui-for-rowspacingtop .edui-icon {
+ background-position: -765px -40px;
+}
+
+.edui-default .edui-for-horizontal .edui-icon {
+ background-position: -360px 0;
+}
+
+.edui-default .edui-for-link .edui-icon {
+ background-position: -500px 0;
+}
+
+.edui-default .edui-for-code .edui-icon {
+ background-position: -440px -40px;
+}
+
+.edui-default .edui-for-insertimage .edui-icon {
+ background-position: -726px -77px;
+}
+
+.edui-default .edui-for-insertframe .edui-icon {
+ background-position: -240px -40px;
+}
+
+.edui-default .edui-for-emoticon .edui-icon {
+ background-position: -60px -20px;
+}
+
+.edui-default .edui-for-spechars .edui-icon {
+ background-position: -240px 0;
+}
+
+.edui-default .edui-for-help .edui-icon {
+ background-position: -340px 0;
+}
+
+.edui-default .edui-for-print .edui-icon {
+ background-position: -440px -20px;
+}
+
+.edui-default .edui-for-preview .edui-icon {
+ background-position: -420px -20px;
+}
+
+.edui-default .edui-for-selectall .edui-icon {
+ background-position: -400px -20px;
+}
+
+.edui-default .edui-for-searchreplace .edui-icon {
+ background-position: -520px -20px;
+}
+
+.edui-default .edui-for-map .edui-icon {
+ background-position: -40px -40px;
+}
+
+.edui-default .edui-for-gmap .edui-icon {
+ background-position: -260px -40px;
+}
+
+.edui-default .edui-for-insertvideo .edui-icon {
+ background-position: -320px -20px;
+}
+
+.edui-default .edui-for-time .edui-icon {
+ background-position: -160px -20px;
+}
+
+.edui-default .edui-for-date .edui-icon {
+ background-position: -140px -20px;
+}
+
+.edui-default .edui-for-cut .edui-icon {
+ background-position: -680px 0;
+}
+
+.edui-default .edui-for-copy .edui-icon {
+ background-position: -700px 0;
+}
+
+.edui-default .edui-for-paste .edui-icon {
+ background-position: -560px 0;
+}
+
+.edui-default .edui-for-formatmatch .edui-icon {
+ background-position: -40px 0;
+}
+
+.edui-default .edui-for-pasteplain .edui-icon {
+ background-position: -360px -20px;
+}
+
+.edui-default .edui-for-directionalityltr .edui-icon {
+ background-position: -20px -20px;
+}
+
+.edui-default .edui-for-directionalityrtl .edui-icon {
+ background-position: -40px -20px;
+}
+
+.edui-default .edui-for-source .edui-icon {
+ background-position: -261px -0px;
+}
+
+.edui-default .edui-for-removeformat .edui-icon {
+ background-position: -580px 0;
+}
+
+.edui-default .edui-for-unlink .edui-icon {
+ background-position: -640px 0;
+}
+
+.edui-default .edui-for-touppercase .edui-icon {
+ background-position: -786px 0;
+}
+
+.edui-default .edui-for-tolowercase .edui-icon {
+ background-position: -806px 0;
+}
+
+.edui-default .edui-for-insertrow .edui-icon {
+ background-position: -478px -76px;
+}
+
+.edui-default .edui-for-insertrownext .edui-icon {
+ background-position: -498px -76px;
+}
+
+.edui-default .edui-for-insertcol .edui-icon {
+ background-position: -455px -76px;
+}
+
+.edui-default .edui-for-insertcolnext .edui-icon {
+ background-position: -429px -76px;
+}
+
+.edui-default .edui-for-mergeright .edui-icon {
+ background-position: -60px -40px;
+}
+
+.edui-default .edui-for-mergedown .edui-icon {
+ background-position: -80px -40px;
+}
+
+.edui-default .edui-for-splittorows .edui-icon {
+ background-position: -100px -40px;
+}
+
+.edui-default .edui-for-splittocols .edui-icon {
+ background-position: -120px -40px;
+}
+
+.edui-default .edui-for-insertparagraphbeforetable .edui-icon {
+ background-position: -140px -40px;
+}
+
+.edui-default .edui-for-deleterow .edui-icon {
+ background-position: -660px -20px;
+}
+
+.edui-default .edui-for-deletecol .edui-icon {
+ background-position: -640px -20px;
+}
+
+.edui-default .edui-for-splittocells .edui-icon {
+ background-position: -800px -20px;
+}
+
+.edui-default .edui-for-mergecells .edui-icon {
+ background-position: -760px -20px;
+}
+
+.edui-default .edui-for-deletetable .edui-icon {
+ background-position: -620px -20px;
+}
+
+.edui-default .edui-for-cleardoc .edui-icon {
+ background-position: -520px 0;
+}
+
+.edui-default .edui-for-fullscreen .edui-icon {
+ background-position: -100px -20px;
+}
+
+.edui-default .edui-for-anchor .edui-icon {
+ background-position: -200px 0;
+}
+
+.edui-default .edui-for-pagebreak .edui-icon {
+ background-position: -460px -40px;
+}
+
+.edui-default .edui-for-imagenone .edui-icon {
+ background-position: -480px -40px;
+}
+
+.edui-default .edui-for-imageleft .edui-icon {
+ background-position: -500px -40px;
+}
+
+.edui-default .edui-for-wordimage .edui-icon {
+ background-position: -660px -40px;
+}
+
+.edui-default .edui-for-imageright .edui-icon {
+ background-position: -520px -40px;
+}
+
+.edui-default .edui-for-imagecenter .edui-icon {
+ background-position: -540px -40px;
+}
+
+.edui-default .edui-for-indent .edui-icon {
+ background-position: -400px 0;
+}
+
+.edui-default .edui-for-outdent .edui-icon {
+ background-position: -540px 0;
+}
+
+.edui-default .edui-for-webapp .edui-icon {
+ background-position: -601px -40px
+}
+
+.edui-default .edui-for-table .edui-icon {
+ background-position: -580px -20px;
+}
+
+.edui-default .edui-for-edittable .edui-icon {
+ background-position: -420px -40px;
+}
+
+.edui-default .edui-for-template .edui-icon {
+ background-position: -339px -40px;
+}
+
+.edui-default .edui-for-delete .edui-icon {
+ background-position: -360px -40px;
+}
+
+.edui-default .edui-for-attachment .edui-icon {
+ background-position: -620px -40px;
+}
+
+.edui-default .edui-for-edittd .edui-icon {
+ background-position: -700px -40px;
+}
+
+.edui-default .edui-for-snapscreen .edui-icon {
+ background-position: -581px -40px
+}
+
+.edui-default .edui-for-scrawl .edui-icon {
+ background-position: -801px -41px
+}
+
+.edui-default .edui-for-background .edui-icon {
+ background-position: -680px -40px;
+}
+
+.edui-default .edui-for-music .edui-icon {
+ background-position: -18px -40px
+}
+
+.edui-default .edui-for-formula .edui-icon {
+ background-position: -200px -40px
+}
+
+.edui-default .edui-for-aligntd .edui-icon {
+ background-position: -236px -76px;
+}
+
+.edui-default .edui-for-insertparagraphtrue .edui-icon {
+ background-position: -625px -76px;
+}
+
+.edui-default .edui-for-insertparagraph .edui-icon {
+ background-position: -602px -76px;
+}
+
+.edui-default .edui-for-insertcaption .edui-icon {
+ background-position: -336px -76px;
+}
+
+.edui-default .edui-for-deletecaption .edui-icon {
+ background-position: -362px -76px;
+}
+
+.edui-default .edui-for-inserttitle .edui-icon {
+ background-position: -286px -76px;
+}
+
+.edui-default .edui-for-deletetitle .edui-icon {
+ background-position: -311px -76px;
+}
+
+.edui-default .edui-for-aligntable .edui-icon {
+ background-position: -440px 0;
+}
+
+.edui-default .edui-for-tablealignment-left .edui-icon {
+ background-position: -460px 0;
+}
+
+.edui-default .edui-for-tablealignment-center .edui-icon {
+ background-position: -420px 0;
+}
+
+.edui-default .edui-for-tablealignment-right .edui-icon {
+ background-position: -480px 0;
+}
+
+.edui-default .edui-for-drafts .edui-icon {
+ background-position: -560px 0;
+}
+
+.edui-default .edui-for-charts .edui-icon {
+ background: url( ../images/charts.png ) no-repeat 2px 3px!important;
+}
+
+.edui-default .edui-for-inserttitlecol .edui-icon {
+ background-position: -673px -76px;
+}
+
+.edui-default .edui-for-deletetitlecol .edui-icon {
+ background-position: -698px -76px;
+}
+
+.edui-default .edui-for-simpleupload .edui-icon {
+ background-position: -380px 0px;
+}
+/*splitbutton*/
+.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow,
+.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow {
+ background: url(../images/icons.png) -741px 0;
+ _background: url(../images/icons.gif) -741px 0;
+ height: 20px;
+ width: 9px;
+}
+
+.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body,
+.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body {
+ padding: 1px;
+}
+
+.edui-default .edui-toolbar .edui-splitborder {
+ width: 1px;
+ height: 20px;
+}
+
+.edui-default .edui-toolbar .edui-state-hover .edui-splitborder {
+ width: 1px;
+ border-left: 0px solid #dcac6c;
+}
+
+.edui-default .edui-toolbar .edui-state-active .edui-splitborder {
+ width: 0;
+ border-left: 1px solid gray;
+}
+
+.edui-default .edui-toolbar .edui-state-opened .edui-splitborder {
+ width: 1px;
+ border: 0;
+}
+
+.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body,
+.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body {
+ background-color: #fff5d4;
+ border: 1px solid #dcac6c;
+ padding: 0;
+}
+
+.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body,
+.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body {
+ background-color: #FFE69F;
+ border: 1px solid #DCAC6C;
+ padding: 0;
+}
+
+.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body,
+.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body {
+ background-color: #ffffff;
+ border: 1px solid gray;
+ padding: 0;
+}
+
+.edui-default .edui-state-disabled .edui-arrow {
+ opacity: 0.3;
+ _filter: alpha(opacity = 30);
+}
+
+.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body,
+.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body {
+ background-color: white;
+ border: 1px solid gray;
+ padding: 0;
+}
+
+.edui-default .edui-for-insertorderedlist .edui-bordereraser,
+.edui-default .edui-for-lineheight .edui-bordereraser,
+.edui-default .edui-for-rowspacingtop .edui-bordereraser,
+.edui-default .edui-for-rowspacingbottom .edui-bordereraser,
+.edui-default .edui-for-insertunorderedlist .edui-bordereraser {
+ background-color: white;
+}
+
+/* 解决嵌套导致的图标问题 */
+.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon,
+.edui-default .edui-for-lineheight .edui-popup-body .edui-icon,
+.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon,
+.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon,
+.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon {
+ /*background-position: 0 -40px;*/
+ background-image: none ;
+}
+
+/* 弹出菜单 */
+.edui-default .edui-popup {
+ z-index: 3000;
+ background-color: #ffffff;
+ width:auto;
+ height:auto;
+
+}
+
+.edui-default .edui-popup .edui-shadow {
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.edui-default .edui-popup-content {
+ border:1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+ padding: 5px;
+ background:#ffffff;
+}
+
+.edui-default .edui-popup .edui-bordereraser {
+ background-color: white;
+ height: 3px;
+}
+
+.edui-default .edui-menu .edui-bordereraser {
+ height: 3px;
+}
+
+.edui-default .edui-anchor-topleft .edui-bordereraser {
+ left: 1px;
+ top: -2px;
+}
+
+.edui-default .edui-anchor-topright .edui-bordereraser {
+ right: 1px;
+ top: -2px;
+}
+
+.edui-default .edui-anchor-bottomleft .edui-bordereraser {
+ left: 0;
+ bottom: -6px;
+ height: 7px;
+ border-left: 1px solid gray;
+ border-right: 1px solid gray;
+}
+
+.edui-default .edui-anchor-bottomright .edui-bordereraser {
+ right: 0;
+ bottom: -6px;
+ height: 7px;
+ border-left: 1px solid gray;
+ border-right: 1px solid gray;
+}
+
+.edui-popup div{
+ width:auto;
+ height:auto;
}
-
-.edui-default .edui-dialog-closebutton {
- position: absolute !important;
- right: 5px;
- top: 3px;
-}
-
-.edui-default .edui-dialog-closebutton .edui-button-body {
- height: 20px;
- width: 20px;
- cursor: pointer;
- background: url("../images/icons-all.gif") no-repeat 0 -59px;
-}
-
-.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body {
- background: url("../images/icons-all.gif") no-repeat 0 -89px;
-}
-
-.edui-default .edui-dialog-foot {
- height: 40px;
-}
-
-.edui-default .edui-dialog-buttons {
- position: absolute;
- right: 0;
-}
-
-.edui-default .edui-dialog-buttons .edui-button {
- margin-right: 10px;
-}
-
-.edui-default .edui-dialog-buttons .edui-button .edui-button-body {
- background: url("../images/icons-all.gif") no-repeat;
- height: 24px;
- width: 96px;
- font-size: 12px;
- line-height: 24px;
- text-align: center;
- cursor: default;
-}
-
-.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body {
- background: url("../images/icons-all.gif") no-repeat 0 -30px;
-}
-
-.edui-default .edui-dialog iframe {
+.edui-default .edui-editor-messageholder {
+ display: block;
+ width: 150px;
+ height: auto;
border: 0;
- padding: 0;
margin: 0;
- vertical-align: top;
-}
-
-.edui-default .edui-dialog-modalmask {
- opacity: 0.3;
- filter: alpha(opacity = 30);
- background-color: #ccc;
- position: absolute;
- /*z-index: 1999;*/
-}
-
-.edui-default .edui-dialog-dragmask {
- position: absolute;
- /*z-index: 2001;*/
- background-color: transparent;
- cursor: move;
-}
-
-.edui-default .edui-dialog-content {
- position: relative;
-}
-
-.edui-default .dialogcontmask {
- cursor: move;
- visibility: hidden;
- display: block;
+ padding: 0;
position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0;
- filter: alpha(opacity = 0);
-}
-
-/*link-dialog*/
-.edui-default .edui-for-link .edui-dialog-content {
- width: 420px;
- height: 200px;
- overflow: hidden;
-}
-/*background-dialog*/
-.edui-default .edui-for-background .edui-dialog-content {
- width: 440px;
- height: 280px;
- overflow: hidden;
-}
-
-/*template-dialog*/
-.edui-default .edui-for-template .edui-dialog-content {
- width: 630px;
- height: 390px;
- overflow: hidden;
-}
-
-/*scrawl-dialog*/
-.edui-default .edui-for-scrawl .edui-dialog-content {
- width: 515px;
- *width: 506px;
- height: 360px;
-}
-
-/*spechars-dialog*/
-.edui-default .edui-for-spechars .edui-dialog-content {
- width: 620px;
- height: 500px;
- *width: 630px;
- *height: 570px;
-}
-
-/*image-dialog*/
-.edui-default .edui-for-insertimage .edui-dialog-content {
- width: 650px;
- height: 400px;
- overflow: hidden;
-}
-/*webapp-dialog*/
-.edui-default .edui-for-webapp .edui-dialog-content {
- width: 560px;
- _width: 565px;
- height: 450px;
- overflow: hidden;
-}
-
-/*image-insertframe*/
-.edui-default .edui-for-insertframe .edui-dialog-content {
- width: 350px;
- height: 200px;
- overflow: hidden;
-}
-
-/*wordImage-dialog*/
-.edui-default .edui-for-wordimage .edui-dialog-content {
- width: 620px;
- height: 380px;
- overflow: hidden;
-}
-
-/*attachment-dialog*/
-.edui-default .edui-for-attachment .edui-dialog-content {
- width: 650px;
- height: 400px;
- overflow: hidden;
-}
-
-
-/*map-dialog*/
-.edui-default .edui-for-map .edui-dialog-content {
- width: 550px;
- height: 400px;
-}
-
-/*gmap-dialog*/
-.edui-default .edui-for-gmap .edui-dialog-content {
- width: 550px;
- height: 400px;
-}
-
-/*video-dialog*/
-.edui-default .edui-for-insertvideo .edui-dialog-content {
- width: 590px;
- height: 390px;
-}
-
-/*anchor-dialog*/
-.edui-default .edui-for-anchor .edui-dialog-content {
- width: 320px;
- height: 60px;
- overflow: hidden;
-}
-
-/*searchreplace-dialog*/
-.edui-default .edui-for-searchreplace .edui-dialog-content {
- width: 400px;
- height: 220px;
-}
-
-/*help-dialog*/
-.edui-default .edui-for-help .edui-dialog-content {
- width: 400px;
- height: 420px;
-}
-
-/*edittable-dialog*/
-.edui-default .edui-for-edittable .edui-dialog-content {
- width: 540px;
- _width:590px;
- height: 335px;
-}
-
-/*edittip-dialog*/
-.edui-default .edui-for-edittip .edui-dialog-content {
- width: 225px;
- height: 60px;
-}
-
-/*edittd-dialog*/
-.edui-default .edui-for-edittd .edui-dialog-content {
- width: 240px;
- height: 50px;
-}
-/*snapscreen-dialog*/
-.edui-default .edui-for-snapscreen .edui-dialog-content {
- width: 400px;
- height: 220px;
-}
-
-/*music-dialog*/
-.edui-default .edui-for-music .edui-dialog-content {
- width: 515px;
- height: 360px;
-}
-
-/*段落弹出菜单*/
-.edui-default .edui-for-paragraph .edui-listitem-label {
- font-family: Tahoma, Verdana, Arial, Helvetica;
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p {
- font-size: 22px;
- line-height: 27px;
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1 {
- font-weight: bolder;
- font-size: 32px;
- line-height: 36px;
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2 {
- font-weight: bolder;
- font-size: 27px;
- line-height: 29px;
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3 {
- font-weight: bolder;
- font-size: 19px;
- line-height: 23px;
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4 {
- font-weight: bolder;
- font-size: 16px;
- line-height: 19px
-}
-
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5 {
- font-weight: bolder;
- font-size: 13px;
- line-height: 16px;
+ top: 28px;
+ right: 3px;
}
-.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6 {
- font-weight: bolder;
- font-size: 12px;
- line-height: 14px;
-}
-/* 表格弹出菜单 */
-.edui-default .edui-for-inserttable .edui-splitborder {
- display: none
-}
-.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow {
- width: 0
-}
-.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{
- border-left: 1px solid transparent;
-}
-.edui-default .edui-tablepicker .edui-infoarea {
- height: 14px;
- line-height: 14px;
- font-size: 12px;
- width: 220px;
+.edui-default .edui-message{
+ min-height: 10px;
+ text-shadow: 0 1px 0 rgba(255,255,255,0.5);
+ padding: 0;
margin-bottom: 3px;
- clear: both;
-}
-
-.edui-default .edui-tablepicker .edui-infoarea .edui-label {
- float: left;
-}
-
-.edui-default .edui-dialog-buttons .edui-label {
- line-height: 24px;
-}
-
-.edui-default .edui-tablepicker .edui-infoarea .edui-clickable {
- float: right;
-}
-
-.edui-default .edui-tablepicker .edui-pickarea {
- background: url("../images/unhighlighted.gif") repeat;
- height: 220px;
- width: 220px;
-}
-
-.edui-default .edui-tablepicker .edui-pickarea .edui-overlay {
- background: url("../images/highlighted.gif") repeat;
-}
-
-/* 颜色弹出菜单 */
-.edui-default .edui-colorpicker-topbar {
- height: 27px;
- width: 200px;
- /*border-bottom: 1px gray dashed;*/
-}
-
-.edui-default .edui-colorpicker-preview {
- height: 20px;
- border: 1px inset black;
- margin-left: 1px;
- width: 128px;
- float: left;
-}
-
-.edui-default .edui-colorpicker-nocolor {
- float: right;
- margin-right: 1px;
- font-size: 12px;
- line-height: 14px;
- height: 14px;
- border: 1px solid #333;
- padding: 3px 5px;
- cursor: pointer;
-}
-
-.edui-default .edui-colorpicker-tablefirstrow {
- height: 30px;
-}
-
-.edui-default .edui-colorpicker-colorcell {
- width: 14px;
- height: 14px;
- display: block;
- margin: 0;
- cursor: pointer;
-}
-
-.edui-default .edui-colorpicker-colorcell:hover {
- width: 14px;
- height: 14px;
- margin: 0;
-}
-.edui-default .edui-colorpicker-advbtn{
- display: block;
- text-align: center;
- cursor: pointer;
- height:20px;
-}
-.arrow_down{
- background: white url('../images/arrow_down.png') no-repeat center;
-}
-.arrow_up{
- background: white url('../images/arrow_up.png') no-repeat center;
-}
-/*高级的样式*/
-.edui-colorpicker-adv{
position: relative;
- overflow: hidden;
- height: 180px;
- display: none;
-}
-.edui-colorpicker-plant, .edui-colorpicker-hue {
- border: solid 1px #666;
-}
-.edui-colorpicker-pad {
- width: 150px;
- height: 150px;
- left: 14px;
- top: 13px;
- position: absolute;
- background: red;
- overflow: hidden;
- cursor: crosshair;
}
-.edui-colorpicker-cover{
- position: absolute;
- top: 0;
- left: 0;
- width: 150px;
- height: 150px;
- background: url("../images/tangram-colorpicker.png") -160px -200px;
+.edui-default .edui-message-body{
+ border-radius: 3px;
+ padding: 8px 15px 8px 8px;
+ color: #c09853;
+ background-color: #fcf8e3;
+ border: 1px solid #fbeed5;
}
-.edui-colorpicker-padDot{
- position: absolute;
- top: 0;
- left: 0;
- width: 11px;
- height: 11px;
- overflow: hidden;
- background: url(../images/tangram-colorpicker.png) 0px -200px repeat-x;
- z-index: 1000;
-
+.edui-default .edui-message-type-info{
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1
}
-.edui-colorpicker-sliderMain {
- position: absolute;
- left: 171px;
- top: 13px;
- width: 19px;
- height: 152px;
- background: url(../images/tangram-colorpicker.png) -179px -12px no-repeat;
-
+.edui-default .edui-message-type-success{
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6
}
-.edui-colorpicker-slider {
- width: 100%;
- height: 100%;
- cursor: pointer;
+.edui-default .edui-message-type-danger,
+.edui-default .edui-message-type-error{
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7
}
-.edui-colorpicker-thumb{
+.edui-default .edui-message .edui-message-closer {
+ display: block;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
position: absolute;
top: 0;
+ right: 0;
+ padding: 0;
cursor: pointer;
- height: 3px;
- left: -1px;
- right: -1px;
- border: 1px solid black;
- background: white;
- opacity: .8;
-}
-/*自动排版弹出菜单*/
-.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body {
- font-size: 12px;
- margin-bottom: 3px;
- clear: both;
-}
-
-.edui-default .edui-autotypesetpicker-body table {
- border-collapse: separate;
- border-spacing: 2px;
+ background: transparent;
+ border: 0;
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ color: #999;
+ text-shadow: 0 1px 0 #fff;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}
-
-.edui-default .edui-autotypesetpicker-body td {
- font-size: 12px;
- word-wrap:break-word;
+.edui-default .edui-message .edui-message-content {
+ font-size: 10pt;
+ word-wrap: break-word;
+ word-break: normal;
}
-
-.edui-default .edui-autotypesetpicker-body td input {
- margin: 3px 3px 3px 4px;
- *margin: 1px 0 0 0;
+/* 弹出对话框按钮和对话框大小 */
+.edui-default .edui-dialog {
+ z-index: 2000;
+ position: absolute;
+
+}
+
+.edui-dialog div{
+ width:auto;
+}
+
+.edui-default .edui-dialog-wrap {
+ margin-right: 6px;
+ margin-bottom: 6px;
+}
+
+.edui-default .edui-dialog-fullscreen-flag {
+ margin-right: 0;
+ margin-bottom: 0;
+}
+
+.edui-default .edui-dialog-body {
+ position: relative;
+ padding:2px 0 0 2px;
+ _zoom: 1;
+}
+
+.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body {
+ padding: 0;
+}
+
+.edui-default .edui-dialog-shadow {
+ position: absolute;
+ z-index: -1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.edui-default .edui-dialog-foot {
+ background-color: white;
+}
+
+.edui-default .edui-dialog-titlebar {
+ height: 26px;
+ border-bottom: 1px solid #c6c6c6;
+ background: url(../images/dialog-title-bg.png) repeat-x bottom;
+ position: relative;
+ cursor: move;
+}
+.edui-default .edui-dialog-caption {
+ font-weight: bold;
+ font-size: 12px;
+ line-height: 26px;
+ padding-left: 5px;
+}
+
+.edui-default .edui-dialog-draghandle {
+ height: 26px;
+}
+
+.edui-default .edui-dialog-closebutton {
+ position: absolute !important;
+ right: 5px;
+ top: 3px;
+}
+
+.edui-default .edui-dialog-closebutton .edui-button-body {
+ height: 20px;
+ width: 20px;
+ cursor: pointer;
+ background: url("../images/icons-all.gif") no-repeat 0 -59px;
+}
+
+.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body {
+ background: url("../images/icons-all.gif") no-repeat 0 -89px;
+}
+
+.edui-default .edui-dialog-foot {
+ height: 40px;
+}
+
+.edui-default .edui-dialog-buttons {
+ position: absolute;
+ right: 0;
+}
+
+.edui-default .edui-dialog-buttons .edui-button {
+ margin-right: 10px;
+}
+
+.edui-default .edui-dialog-buttons .edui-button .edui-button-body {
+ background: url("../images/icons-all.gif") no-repeat;
+ height: 24px;
+ width: 96px;
+ font-size: 12px;
+ line-height: 24px;
+ text-align: center;
+ cursor: default;
+}
+
+.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body {
+ background: url("../images/icons-all.gif") no-repeat 0 -30px;
+}
+
+.edui-default .edui-dialog iframe {
+ border: 0;
+ padding: 0;
+ margin: 0;
+ vertical-align: top;
+}
+
+.edui-default .edui-dialog-modalmask {
+ opacity: 0.3;
+ filter: alpha(opacity = 30);
+ background-color: #ccc;
+ position: absolute;
+ /*z-index: 1999;*/
+}
+
+.edui-default .edui-dialog-dragmask {
+ position: absolute;
+ /*z-index: 2001;*/
+ background-color: transparent;
+ cursor: move;
+}
+
+.edui-default .edui-dialog-content {
+ position: relative;
+}
+
+.edui-default .dialogcontmask {
+ cursor: move;
+ visibility: hidden;
+ display: block;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ filter: alpha(opacity = 0);
+}
+
+/*link-dialog*/
+.edui-default .edui-for-link .edui-dialog-content {
+ width: 420px;
+ height: 200px;
+ overflow: hidden;
+}
+/*background-dialog*/
+.edui-default .edui-for-background .edui-dialog-content {
+ width: 440px;
+ height: 280px;
+ overflow: hidden;
+}
+
+/*template-dialog*/
+.edui-default .edui-for-template .edui-dialog-content {
+ width: 630px;
+ height: 390px;
+ overflow: hidden;
+}
+
+/*scrawl-dialog*/
+.edui-default .edui-for-scrawl .edui-dialog-content {
+ width: 515px;
+ *width: 506px;
+ height: 360px;
+}
+
+/*spechars-dialog*/
+.edui-default .edui-for-spechars .edui-dialog-content {
+ width: 620px;
+ height: 500px;
+ *width: 630px;
+ *height: 570px;
+}
+
+/*image-dialog*/
+.edui-default .edui-for-insertimage .edui-dialog-content {
+ width: 650px;
+ height: 400px;
+ overflow: hidden;
+}
+/*webapp-dialog*/
+.edui-default .edui-for-webapp .edui-dialog-content {
+ width: 560px;
+ _width: 565px;
+ height: 450px;
+ overflow: hidden;
+}
+
+/*image-insertframe*/
+.edui-default .edui-for-insertframe .edui-dialog-content {
+ width: 350px;
+ height: 200px;
+ overflow: hidden;
+}
+
+/*wordImage-dialog*/
+.edui-default .edui-for-wordimage .edui-dialog-content {
+ width: 620px;
+ height: 380px;
+ overflow: hidden;
+}
+
+/*attachment-dialog*/
+.edui-default .edui-for-attachment .edui-dialog-content {
+ width: 650px;
+ height: 400px;
+ overflow: hidden;
+}
+
+
+/*map-dialog*/
+.edui-default .edui-for-map .edui-dialog-content {
+ width: 550px;
+ height: 400px;
+}
+
+/*gmap-dialog*/
+.edui-default .edui-for-gmap .edui-dialog-content {
+ width: 550px;
+ height: 400px;
+}
+
+/*video-dialog*/
+.edui-default .edui-for-insertvideo .edui-dialog-content {
+ width: 590px;
+ height: 390px;
+}
+
+/*anchor-dialog*/
+.edui-default .edui-for-anchor .edui-dialog-content {
+ width: 320px;
+ height: 60px;
+ overflow: hidden;
+}
+
+/*searchreplace-dialog*/
+.edui-default .edui-for-searchreplace .edui-dialog-content {
+ width: 400px;
+ height: 220px;
+}
+
+/*help-dialog*/
+.edui-default .edui-for-help .edui-dialog-content {
+ width: 400px;
+ height: 420px;
+}
+
+/*edittable-dialog*/
+.edui-default .edui-for-edittable .edui-dialog-content {
+ width: 540px;
+ _width:590px;
+ height: 335px;
+}
+
+/*edittip-dialog*/
+.edui-default .edui-for-edittip .edui-dialog-content {
+ width: 225px;
+ height: 60px;
+}
+
+/*edittd-dialog*/
+.edui-default .edui-for-edittd .edui-dialog-content {
+ width: 240px;
+ height: 50px;
+}
+/*snapscreen-dialog*/
+.edui-default .edui-for-snapscreen .edui-dialog-content {
+ width: 400px;
+ height: 220px;
+}
+
+/*music-dialog*/
+.edui-default .edui-for-music .edui-dialog-content {
+ width: 515px;
+ height: 360px;
+}
+
+/*段落弹出菜单*/
+.edui-default .edui-for-paragraph .edui-listitem-label {
+ font-family: Tahoma, Verdana, Arial, Helvetica;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p {
+ font-size: 22px;
+ line-height: 27px;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1 {
+ font-weight: bolder;
+ font-size: 32px;
+ line-height: 36px;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2 {
+ font-weight: bolder;
+ font-size: 27px;
+ line-height: 29px;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3 {
+ font-weight: bolder;
+ font-size: 19px;
+ line-height: 23px;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4 {
+ font-weight: bolder;
+ font-size: 16px;
+ line-height: 19px
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5 {
+ font-weight: bolder;
+ font-size: 13px;
+ line-height: 16px;
+}
+
+.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6 {
+ font-weight: bolder;
+ font-size: 12px;
+ line-height: 14px;
+}
+/* 表格弹出菜单 */
+.edui-default .edui-for-inserttable .edui-splitborder {
+ display: none
+}
+.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow {
+ width: 0
+}
+.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{
+ border-left: 1px solid transparent;
+}
+.edui-default .edui-tablepicker .edui-infoarea {
+ height: 14px;
+ line-height: 14px;
+ font-size: 12px;
+ width: 220px;
+ margin-bottom: 3px;
+ clear: both;
+}
+
+.edui-default .edui-tablepicker .edui-infoarea .edui-label {
+ float: left;
+}
+
+.edui-default .edui-dialog-buttons .edui-label {
+ line-height: 24px;
+}
+
+.edui-default .edui-tablepicker .edui-infoarea .edui-clickable {
+ float: right;
+}
+
+.edui-default .edui-tablepicker .edui-pickarea {
+ background: url("../images/unhighlighted.gif") repeat;
+ height: 220px;
+ width: 220px;
+}
+
+.edui-default .edui-tablepicker .edui-pickarea .edui-overlay {
+ background: url("../images/highlighted.gif") repeat;
+}
+
+/* 颜色弹出菜单 */
+.edui-default .edui-colorpicker-topbar {
+ height: 27px;
+ width: 200px;
+ /*border-bottom: 1px gray dashed;*/
+}
+
+.edui-default .edui-colorpicker-preview {
+ height: 20px;
+ border: 1px inset black;
+ margin-left: 1px;
+ width: 128px;
+ float: left;
+}
+
+.edui-default .edui-colorpicker-nocolor {
+ float: right;
+ margin-right: 1px;
+ font-size: 12px;
+ line-height: 14px;
+ height: 14px;
+ border: 1px solid #333;
+ padding: 3px 5px;
+ cursor: pointer;
+}
+
+.edui-default .edui-colorpicker-tablefirstrow {
+ height: 30px;
+}
+
+.edui-default .edui-colorpicker-colorcell {
+ width: 14px;
+ height: 14px;
+ display: block;
+ margin: 0;
+ cursor: pointer;
+}
+
+.edui-default .edui-colorpicker-colorcell:hover {
+ width: 14px;
+ height: 14px;
+ margin: 0;
+}
+.edui-default .edui-colorpicker-advbtn{
+ display: block;
+ text-align: center;
+ cursor: pointer;
+ height:20px;
+}
+.arrow_down{
+ background: white url('../images/arrow_down.png') no-repeat center;
+}
+.arrow_up{
+ background: white url('../images/arrow_up.png') no-repeat center;
+}
+/*高级的样式*/
+.edui-colorpicker-adv{
+ position: relative;
+ overflow: hidden;
+ height: 180px;
+ display: none;
+}
+.edui-colorpicker-plant, .edui-colorpicker-hue {
+ border: solid 1px #666;
+}
+.edui-colorpicker-pad {
+ width: 150px;
+ height: 150px;
+ left: 14px;
+ top: 13px;
+ position: absolute;
+ background: red;
+ overflow: hidden;
+ cursor: crosshair;
+}
+.edui-colorpicker-cover{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 150px;
+ height: 150px;
+ background: url("../images/tangram-colorpicker.png") -160px -200px;
+}
+.edui-colorpicker-padDot{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 11px;
+ height: 11px;
+ overflow: hidden;
+ background: url(../images/tangram-colorpicker.png) 0px -200px repeat-x;
+ z-index: 1000;
+
+}
+.edui-colorpicker-sliderMain {
+ position: absolute;
+ left: 171px;
+ top: 13px;
+ width: 19px;
+ height: 152px;
+ background: url(../images/tangram-colorpicker.png) -179px -12px no-repeat;
+
+}
+.edui-colorpicker-slider {
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+}
+.edui-colorpicker-thumb{
+ position: absolute;
+ top: 0;
+ cursor: pointer;
+ height: 3px;
+ left: -1px;
+ right: -1px;
+ border: 1px solid black;
+ background: white;
+ opacity: .8;
+}
+/*自动排版弹出菜单*/
+.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body {
+ font-size: 12px;
+ margin-bottom: 3px;
+ clear: both;
+}
+
+.edui-default .edui-autotypesetpicker-body table {
+ border-collapse: separate;
+ border-spacing: 2px;
+}
+
+.edui-default .edui-autotypesetpicker-body td {
+ font-size: 12px;
+ word-wrap:break-word;
+}
+
+.edui-default .edui-autotypesetpicker-body td input {
+ margin: 3px 3px 3px 4px;
+ *margin: 1px 0 0 0;
}
/*自动排版弹出菜单*/
.edui-default .edui-cellalignpicker .edui-cellalignpicker-body {
@@ -1743,81 +1743,81 @@ div.edui-box {
.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-right {
background-position: -271px 0;
}
-/*分隔线*/
-.edui-default .edui-toolbar .edui-separator {
- width: 2px;
- height: 20px;
- margin: 2px 4px 2px 3px;
- background: url(../images/icons.png) -181px 0;
- background: url(../images/icons.gif) -181px 0 \9;
-}
-
-/*颜色按钮 */
-.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump {
- position: absolute;
- overflow: hidden;
- bottom: 1px;
- left: 1px;
- width: 18px;
- height: 4px;
-}
-/*表情按钮及弹出菜单*/
-/*去除了表情的下拉箭头*/
-.edui-default .edui-for-emotion .edui-icon {
- background-position: -60px -20px;
-}
-.edui-default .edui-for-emotion .edui-popup-content iframe
-{
- width: 514px;
- height: 380px;
- overflow: hidden;
-}
-.edui-default .edui-for-emotion .edui-popup-content
-{
- position: relative;
- z-index: 555
-}
-
-.edui-default .edui-for-emotion .edui-splitborder {
- display: none
-}
-
-.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow
-{
- width: 0
-}
-.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder
-{
- border-left: 1px solid transparent;
-}
-/*contextmenu*/
-.edui-default .edui-hassubmenu .edui-arrow {
- height: 20px;
- width: 20px;
- float: right;
- background: url("../images/icons-all.gif") no-repeat 10px -233px;
-}
-
-.edui-default .edui-menu-body .edui-menuitem {
- padding: 1px;
-}
-
-.edui-default .edui-menuseparator {
- margin: 2px 0;
- height: 1px;
- overflow: hidden;
-}
-
-.edui-default .edui-menuseparator-inner {
- border-bottom: 1px solid #e2e3e3;
- margin-left: 29px;
- margin-right: 1px;
-}
-
-.edui-default .edui-menu-body .edui-state-hover {
- padding: 0 !important;
- background-color: #fff5d4;
- border: 1px solid #dcac6c;
+/*分隔线*/
+.edui-default .edui-toolbar .edui-separator {
+ width: 2px;
+ height: 20px;
+ margin: 2px 4px 2px 3px;
+ background: url(../images/icons.png) -181px 0;
+ background: url(../images/icons.gif) -181px 0 \9;
+}
+
+/*颜色按钮 */
+.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump {
+ position: absolute;
+ overflow: hidden;
+ bottom: 1px;
+ left: 1px;
+ width: 18px;
+ height: 4px;
+}
+/*表情按钮及弹出菜单*/
+/*去除了表情的下拉箭头*/
+.edui-default .edui-for-emotion .edui-icon {
+ background-position: -60px -20px;
+}
+.edui-default .edui-for-emotion .edui-popup-content iframe
+{
+ width: 514px;
+ height: 380px;
+ overflow: hidden;
+}
+.edui-default .edui-for-emotion .edui-popup-content
+{
+ position: relative;
+ z-index: 555
+}
+
+.edui-default .edui-for-emotion .edui-splitborder {
+ display: none
+}
+
+.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow
+{
+ width: 0
+}
+.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder
+{
+ border-left: 1px solid transparent;
+}
+/*contextmenu*/
+.edui-default .edui-hassubmenu .edui-arrow {
+ height: 20px;
+ width: 20px;
+ float: right;
+ background: url("../images/icons-all.gif") no-repeat 10px -233px;
+}
+
+.edui-default .edui-menu-body .edui-menuitem {
+ padding: 1px;
+}
+
+.edui-default .edui-menuseparator {
+ margin: 2px 0;
+ height: 1px;
+ overflow: hidden;
+}
+
+.edui-default .edui-menuseparator-inner {
+ border-bottom: 1px solid #e2e3e3;
+ margin-left: 29px;
+ margin-right: 1px;
+}
+
+.edui-default .edui-menu-body .edui-state-hover {
+ padding: 0 !important;
+ background-color: #fff5d4;
+ border: 1px solid #dcac6c;
}
/*弹出菜单*/
.edui-default .edui-shortcutmenu {
diff --git a/assets/themes/default/css/ueditor.min.css b/assets/themes/default/css/ueditor.min.css
index 6436531..4bc00c7 100644
--- a/assets/themes/default/css/ueditor.min.css
+++ b/assets/themes/default/css/ueditor.min.css
@@ -1,8 +1,8 @@
/*!
* UEditor
* version: ueditor
- * build: Thu May 29 2014 16:47:49 GMT+0800 (中国标准时间)
+ * build: Wed Aug 10 2016 11:06:03 GMT+0800 (CST)
*/
-.edui-default .edui-box{border:0;padding:0;margin:0;overflow:hidden}.edui-default a.edui-box{display:block;text-decoration:none;color:#000}.edui-default a.edui-box:hover{text-decoration:none}.edui-default a.edui-box:active{text-decoration:none}.edui-default table.edui-box{border-collapse:collapse}.edui-default ul.edui-box{list-style-type:none}div.edui-box{position:relative;display:-moz-inline-box!important;display:inline-block!important;vertical-align:top}.edui-default .edui-clearfix{zoom:1}.edui-default .edui-clearfix:after{content:'\20';display:block;clear:both}* html div.edui-box{display:inline!important}:first-child+html div.edui-box{display:inline!important}.edui-default .edui-button-body,.edui-splitbutton-body,.edui-menubutton-body,.edui-combox-body{position:relative}.edui-default .edui-popup{position:absolute;-webkit-user-select:none;-moz-user-select:none}.edui-default .edui-popup .edui-shadow{position:absolute;z-index:-1}.edui-default .edui-popup .edui-bordereraser{position:absolute;overflow:hidden}.edui-default .edui-tablepicker .edui-canvas{position:relative}.edui-default .edui-tablepicker .edui-canvas .edui-overlay{position:absolute}.edui-default .edui-dialog-modalmask,.edui-dialog-dragmask{position:absolute;left:0;top:0;width:100%;height:100%}.edui-default .edui-toolbar{position:relative}.edui-default .edui-label{cursor:default}.edui-default span.edui-clickable{color:#00f;cursor:pointer;text-decoration:underline}.edui-default span.edui-unclickable{color:gray;cursor:default}.edui-default .edui-toolbar{cursor:default;-webkit-user-select:none;-moz-user-select:none;padding:1px;overflow:hidden;zoom:1;width:auto;height:auto}.edui-default .edui-toolbar .edui-button,.edui-default .edui-toolbar .edui-splitbutton,.edui-default .edui-toolbar .edui-menubutton,.edui-default .edui-toolbar .edui-combox{margin:1px}.edui-default .edui-editor{border:1px solid #d4d4d4;background-color:#fff;position:relative;overflow:visible;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.edui-editor div{width:auto;height:auto}.edui-default .edui-editor-toolbarbox{position:relative;zoom:1;-webkit-box-shadow:0 1px 4px rgba(204,204,204,.6);-moz-box-shadow:0 1px 4px rgba(204,204,204,.6);box-shadow:0 1px 4px rgba(204,204,204,.6);border-top-left-radius:2px;border-top-right-radius:2px}.edui-default .edui-editor-toolbarboxouter{border-bottom:1px solid #d4d4d4;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,.065);box-shadow:0 1px 4px rgba(0,0,0,.065)}.edui-default .edui-editor-toolbarboxinner{padding:2px}.edui-default .edui-editor-iframeholder{position:relative}.edui-default .edui-editor-bottomContainer{overflow:hidden}.edui-default .edui-editor-bottomContainer table{width:100%;height:0;overflow:hidden;border-spacing:0}.edui-default .edui-editor-bottomContainer td{white-space:nowrap;border-top:1px solid #ccc;line-height:20px;font-size:12px;font-family:Arial,Helvetica,Tahoma,Verdana,Sans-Serif}.edui-default .edui-editor-wordcount{text-align:right;margin-right:5px;color:#aaa}.edui-default .edui-editor-scale{width:12px}.edui-default .edui-editor-scale .edui-editor-icon{float:right;width:100%;height:12px;margin-top:10px;background:url(../images/scale.png) no-repeat;cursor:se-resize}.edui-default .edui-editor-breadcrumb{margin:2px 0 0 3px}.edui-default .edui-editor-breadcrumb span{cursor:pointer;text-decoration:underline;color:#00f}.edui-default .edui-toolbar .edui-for-fullscreen{float:right}.edui-default .edui-bubble .edui-popup-content{border:1px solid #DCAC6C;background-color:#fff6d9;padding:5px;font-size:10pt;font-family:"宋体"}.edui-default .edui-bubble .edui-shadow{}.edui-default .edui-editor-toolbarmsg{background-color:#FFF6D9;border-bottom:1px solid #ccc;position:absolute;bottom:-25px;left:0;z-index:1009;width:99.9%}.edui-default .edui-editor-toolbarmsg-upload{font-size:14px;color:#00f;width:100px;height:16px;line-height:16px;cursor:pointer;position:absolute;top:5px;left:350px}.edui-default .edui-editor-toolbarmsg-label{font-size:12px;line-height:16px;padding:4px}.edui-default .edui-editor-toolbarmsg-close{float:right;width:20px;height:16px;line-height:16px;cursor:pointer;color:red}.edui-default .edui-list .edui-bordereraser{display:none}.edui-default .edui-listitem{padding:1px;white-space:nowrap}.edui-default .edui-list .edui-state-hover{position:relative;background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-for-fontfamily .edui-listitem-label{min-width:130px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-insertcode .edui-listitem-label{min-width:120px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-underline .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px;font-size:12px}.edui-default .edui-for-fontsize .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px}.edui-default .edui-for-paragraph .edui-listitem-label{min-width:200px;_width:200px;padding:2px 5px}.edui-default .edui-for-rowspacingtop .edui-listitem-label,.edui-default .edui-for-rowspacingbottom .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-lineheight .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-customstyle .edui-listitem-label{min-width:200px;_width:200px;width:200px!important;padding:2px 5px}.edui-default .edui-menu{z-index:3000}.edui-default .edui-menu .edui-popup-content{padding:3px}.edui-default .edui-menu-body{_width:150px;min-width:170px;background:url(../images/sparator_v.png) repeat-y 25px}.edui-default .edui-menuitem-body{}.edui-default .edui-menuitem{height:20px;cursor:default;vertical-align:top}.edui-default .edui-menuitem .edui-icon{width:20px!important;height:20px!important;background:url(../images/icons.png) 0 -4000px;background:url(../images/icons.gif) 0 -4000px\9}.edui-default .edui-menuitem .edui-label{font-size:12px;line-height:20px;height:20px;padding-left:10px}.edui-default .edui-state-checked .edui-menuitem-body{background:url(../images/icons-all.gif) no-repeat 6px -205px}.edui-default .edui-state-disabled .edui-menuitem-label{color:gray}.edui-default .edui-toolbar .edui-combox-body .edui-button-body{width:60px;font-size:12px;height:20px;line-height:20px;padding-left:5px;white-space:nowrap;margin:0 3px 0 0}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-combox .edui-combox-body{border:1px solid #CCC;background-color:#fff;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-combox-body .edui-splitborder{display:none}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{border-left:1px solid #CCC}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body{background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow{border-left:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-checked .edui-combox-body{background-color:#FFE69F;border:1px solid #DCAC6C}.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow{border-left:1px solid #DCAC6C}.edui-toolbar .edui-state-disabled .edui-combox-body{background-color:#F0F0EE;opacity:.3;filter:alpha(opacity=30)}.edui-toolbar .edui-state-opened .edui-combox-body{background-color:#fff;border:1px solid gray}.edui-default .edui-toolbar .edui-button .edui-icon,.edui-default .edui-toolbar .edui-menubutton .edui-icon,.edui-default .edui-toolbar .edui-splitbutton .edui-icon{height:20px!important;width:20px!important;background-image:url(../images/icons.png);background-image:url(../images/icons.gif) \9}.edui-default .edui-toolbar .edui-button .edui-button-wrap{padding:1px;position:relative}.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap{background-color:#fff5d4;padding:0;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap{background-color:#ffe69f;padding:0;border:1px solid #dcac6c;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap{background-color:#fff;padding:0;border:1px solid gray}.edui-default .edui-toolbar .edui-state-disabled .edui-label{color:#ccc}.edui-default .edui-toolbar .edui-state-disabled .edui-icon{opacity:.3;filter:alpha(opacity=30)}.edui-default .edui-for-undo .edui-icon{background-position:-160px 0}.edui-default .edui-for-redo .edui-icon{background-position:-100px 0}.edui-default .edui-for-bold .edui-icon{background-position:0 0}.edui-default .edui-for-italic .edui-icon{background-position:-60px 0}.edui-default .edui-for-fontborder .edui-icon{background-position:-160px -40px}.edui-default .edui-for-underline .edui-icon{background-position:-140px 0}.edui-default .edui-for-strikethrough .edui-icon{background-position:-120px 0}.edui-default .edui-for-subscript .edui-icon{background-position:-600px 0}.edui-default .edui-for-superscript .edui-icon{background-position:-620px 0}.edui-default .edui-for-blockquote .edui-icon{background-position:-220px 0}.edui-default .edui-for-forecolor .edui-icon{background-position:-720px 0}.edui-default .edui-for-backcolor .edui-icon{background-position:-760px 0}.edui-default .edui-for-inserttable .edui-icon{background-position:-580px -20px}.edui-default .edui-for-autotypeset .edui-icon{background-position:-640px -40px}.edui-default .edui-for-justifyleft .edui-icon{background-position:-460px 0}.edui-default .edui-for-justifycenter .edui-icon{background-position:-420px 0}.edui-default .edui-for-justifyright .edui-icon{background-position:-480px 0}.edui-default .edui-for-justifyjustify .edui-icon{background-position:-440px 0}.edui-default .edui-for-insertorderedlist .edui-icon{background-position:-80px 0}.edui-default .edui-for-insertunorderedlist .edui-icon{background-position:-20px 0}.edui-default .edui-for-lineheight .edui-icon{background-position:-725px -40px}.edui-default .edui-for-rowspacingbottom .edui-icon{background-position:-745px -40px}.edui-default .edui-for-rowspacingtop .edui-icon{background-position:-765px -40px}.edui-default .edui-for-horizontal .edui-icon{background-position:-360px 0}.edui-default .edui-for-link .edui-icon{background-position:-500px 0}.edui-default .edui-for-code .edui-icon{background-position:-440px -40px}.edui-default .edui-for-insertimage .edui-icon{background-position:-726px -77px}.edui-default .edui-for-insertframe .edui-icon{background-position:-240px -40px}.edui-default .edui-for-emoticon .edui-icon{background-position:-60px -20px}.edui-default .edui-for-spechars .edui-icon{background-position:-240px 0}.edui-default .edui-for-help .edui-icon{background-position:-340px 0}.edui-default .edui-for-print .edui-icon{background-position:-440px -20px}.edui-default .edui-for-preview .edui-icon{background-position:-420px -20px}.edui-default .edui-for-selectall .edui-icon{background-position:-400px -20px}.edui-default .edui-for-searchreplace .edui-icon{background-position:-520px -20px}.edui-default .edui-for-map .edui-icon{background-position:-40px -40px}.edui-default .edui-for-gmap .edui-icon{background-position:-260px -40px}.edui-default .edui-for-insertvideo .edui-icon{background-position:-320px -20px}.edui-default .edui-for-time .edui-icon{background-position:-160px -20px}.edui-default .edui-for-date .edui-icon{background-position:-140px -20px}.edui-default .edui-for-cut .edui-icon{background-position:-680px 0}.edui-default .edui-for-copy .edui-icon{background-position:-700px 0}.edui-default .edui-for-paste .edui-icon{background-position:-560px 0}.edui-default .edui-for-formatmatch .edui-icon{background-position:-40px 0}.edui-default .edui-for-pasteplain .edui-icon{background-position:-360px -20px}.edui-default .edui-for-directionalityltr .edui-icon{background-position:-20px -20px}.edui-default .edui-for-directionalityrtl .edui-icon{background-position:-40px -20px}.edui-default .edui-for-source .edui-icon{background-position:-261px -0px}.edui-default .edui-for-removeformat .edui-icon{background-position:-580px 0}.edui-default .edui-for-unlink .edui-icon{background-position:-640px 0}.edui-default .edui-for-touppercase .edui-icon{background-position:-786px 0}.edui-default .edui-for-tolowercase .edui-icon{background-position:-806px 0}.edui-default .edui-for-insertrow .edui-icon{background-position:-478px -76px}.edui-default .edui-for-insertrownext .edui-icon{background-position:-498px -76px}.edui-default .edui-for-insertcol .edui-icon{background-position:-455px -76px}.edui-default .edui-for-insertcolnext .edui-icon{background-position:-429px -76px}.edui-default .edui-for-mergeright .edui-icon{background-position:-60px -40px}.edui-default .edui-for-mergedown .edui-icon{background-position:-80px -40px}.edui-default .edui-for-splittorows .edui-icon{background-position:-100px -40px}.edui-default .edui-for-splittocols .edui-icon{background-position:-120px -40px}.edui-default .edui-for-insertparagraphbeforetable .edui-icon{background-position:-140px -40px}.edui-default .edui-for-deleterow .edui-icon{background-position:-660px -20px}.edui-default .edui-for-deletecol .edui-icon{background-position:-640px -20px}.edui-default .edui-for-splittocells .edui-icon{background-position:-800px -20px}.edui-default .edui-for-mergecells .edui-icon{background-position:-760px -20px}.edui-default .edui-for-deletetable .edui-icon{background-position:-620px -20px}.edui-default .edui-for-cleardoc .edui-icon{background-position:-520px 0}.edui-default .edui-for-fullscreen .edui-icon{background-position:-100px -20px}.edui-default .edui-for-anchor .edui-icon{background-position:-200px 0}.edui-default .edui-for-pagebreak .edui-icon{background-position:-460px -40px}.edui-default .edui-for-imagenone .edui-icon{background-position:-480px -40px}.edui-default .edui-for-imageleft .edui-icon{background-position:-500px -40px}.edui-default .edui-for-wordimage .edui-icon{background-position:-660px -40px}.edui-default .edui-for-imageright .edui-icon{background-position:-520px -40px}.edui-default .edui-for-imagecenter .edui-icon{background-position:-540px -40px}.edui-default .edui-for-indent .edui-icon{background-position:-400px 0}.edui-default .edui-for-outdent .edui-icon{background-position:-540px 0}.edui-default .edui-for-webapp .edui-icon{background-position:-601px -40px}.edui-default .edui-for-table .edui-icon{background-position:-580px -20px}.edui-default .edui-for-edittable .edui-icon{background-position:-420px -40px}.edui-default .edui-for-template .edui-icon{background-position:-339px -40px}.edui-default .edui-for-delete .edui-icon{background-position:-360px -40px}.edui-default .edui-for-attachment .edui-icon{background-position:-620px -40px}.edui-default .edui-for-edittd .edui-icon{background-position:-700px -40px}.edui-default .edui-for-snapscreen .edui-icon{background-position:-581px -40px}.edui-default .edui-for-scrawl .edui-icon{background-position:-801px -41px}.edui-default .edui-for-background .edui-icon{background-position:-680px -40px}.edui-default .edui-for-music .edui-icon{background-position:-18px -40px}.edui-default .edui-for-formula .edui-icon{background-position:-200px -40px}.edui-default .edui-for-aligntd .edui-icon{background-position:-236px -76px}.edui-default .edui-for-insertparagraphtrue .edui-icon{background-position:-625px -76px}.edui-default .edui-for-insertparagraph .edui-icon{background-position:-602px -76px}.edui-default .edui-for-insertcaption .edui-icon{background-position:-336px -76px}.edui-default .edui-for-deletecaption .edui-icon{background-position:-362px -76px}.edui-default .edui-for-inserttitle .edui-icon{background-position:-286px -76px}.edui-default .edui-for-deletetitle .edui-icon{background-position:-311px -76px}.edui-default .edui-for-aligntable .edui-icon{background-position:-440px 0}.edui-default .edui-for-tablealignment-left .edui-icon{background-position:-460px 0}.edui-default .edui-for-tablealignment-center .edui-icon{background-position:-420px 0}.edui-default .edui-for-tablealignment-right .edui-icon{background-position:-480px 0}.edui-default .edui-for-drafts .edui-icon{background-position:-560px 0}.edui-default .edui-for-charts .edui-icon{background:url(../images/charts.png) no-repeat 2px 3px!important}.edui-default .edui-for-inserttitlecol .edui-icon{background-position:-673px -76px}.edui-default .edui-for-deletetitlecol .edui-icon{background-position:-698px -76px}.edui-default .edui-for-simpleupload .edui-icon{background-position:-380px 0}.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow,.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body{padding:1px}.edui-default .edui-toolbar .edui-splitborder{width:1px;height:20px}.edui-default .edui-toolbar .edui-state-hover .edui-splitborder{width:1px;border-left:0 solid #dcac6c}.edui-default .edui-toolbar .edui-state-active .edui-splitborder{width:0;border-left:1px solid gray}.edui-default .edui-toolbar .edui-state-opened .edui-splitborder{width:1px;border:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body{background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body{background-color:#FFE69F;border:1px solid #DCAC6C;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-state-disabled .edui-arrow{opacity:.3;_filter:alpha(opacity=30)}.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-for-insertorderedlist .edui-bordereraser,.edui-default .edui-for-lineheight .edui-bordereraser,.edui-default .edui-for-rowspacingtop .edui-bordereraser,.edui-default .edui-for-rowspacingbottom .edui-bordereraser,.edui-default .edui-for-insertunorderedlist .edui-bordereraser{background-color:#fff}.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon,.edui-default .edui-for-lineheight .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon,.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon{background-image:none}.edui-default .edui-popup{z-index:3000;background-color:#fff;width:auto;height:auto}.edui-default .edui-popup .edui-shadow{left:0;top:0;width:100%;height:100%}.edui-default .edui-popup-content{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 4px rgba(0,0,0,.2);-moz-box-shadow:0 3px 4px rgba(0,0,0,.2);box-shadow:0 3px 4px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;padding:5px;background:#fff}.edui-default .edui-popup .edui-bordereraser{background-color:#fff;height:3px}.edui-default .edui-menu .edui-bordereraser{height:3px}.edui-default .edui-anchor-topleft .edui-bordereraser{left:1px;top:-2px}.edui-default .edui-anchor-topright .edui-bordereraser{right:1px;top:-2px}.edui-default .edui-anchor-bottomleft .edui-bordereraser{left:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-default .edui-anchor-bottomright .edui-bordereraser{right:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-popup div{width:auto;height:auto}.edui-default .edui-editor-messageholder{display:block;width:150px;height:auto;border:0;margin:0;padding:0;position:absolute;top:28px;right:3px}.edui-default .edui-message{min-height:10px;text-shadow:0 1px 0 rgba(255,255,255,.5);padding:0;margin-bottom:3px;position:relative}.edui-default .edui-message-body{border-radius:3px;padding:8px 15px 8px 8px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5}.edui-default .edui-message-type-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.edui-default .edui-message-type-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.edui-default .edui-message-type-danger,.edui-default .edui-message-type-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.edui-default .edui-message .edui-message-closer{display:block;width:16px;height:16px;line-height:16px;position:absolute;top:0;right:0;padding:0;cursor:pointer;background:transparent;border:0;float:right;font-size:20px;font-weight:700;color:#999;text-shadow:0 1px 0 #fff;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.edui-default .edui-message .edui-message-content{font-size:10pt;word-wrap:break-word;word-break:normal}.edui-default .edui-dialog{z-index:2000;position:absolute}.edui-dialog div{width:auto}.edui-default .edui-dialog-wrap{margin-right:6px;margin-bottom:6px}.edui-default .edui-dialog-fullscreen-flag{margin-right:0;margin-bottom:0}.edui-default .edui-dialog-body{position:relative;padding:2px 0 0 2px;_zoom:1}.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body{padding:0}.edui-default .edui-dialog-shadow{position:absolute;z-index:-1;left:0;top:0;width:100%;height:100%;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.edui-default .edui-dialog-foot{background-color:#fff}.edui-default .edui-dialog-titlebar{height:26px;border-bottom:1px solid #c6c6c6;background:url(../images/dialog-title-bg.png) repeat-x bottom;position:relative;cursor:move}.edui-default .edui-dialog-caption{font-weight:700;font-size:12px;line-height:26px;padding-left:5px}.edui-default .edui-dialog-draghandle{height:26px}.edui-default .edui-dialog-closebutton{position:absolute!important;right:5px;top:3px}.edui-default .edui-dialog-closebutton .edui-button-body{height:20px;width:20px;cursor:pointer;background:url(../images/icons-all.gif) no-repeat 0 -59px}.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -89px}.edui-default .edui-dialog-foot{height:40px}.edui-default .edui-dialog-buttons{position:absolute;right:0}.edui-default .edui-dialog-buttons .edui-button{margin-right:10px}.edui-default .edui-dialog-buttons .edui-button .edui-button-body{background:url(../images/icons-all.gif) no-repeat;height:24px;width:96px;font-size:12px;line-height:24px;text-align:center;cursor:default}.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -30px}.edui-default .edui-dialog iframe{border:0;padding:0;margin:0;vertical-align:top}.edui-default .edui-dialog-modalmask{opacity:.3;filter:alpha(opacity=30);background-color:#ccc;position:absolute}.edui-default .edui-dialog-dragmask{position:absolute;background-color:transparent;cursor:move}.edui-default .edui-dialog-content{position:relative}.edui-default .dialogcontmask{cursor:move;visibility:hidden;display:block;position:absolute;width:100%;height:100%;opacity:0;filter:alpha(opacity=0)}.edui-default .edui-for-link .edui-dialog-content{width:420px;height:200px;overflow:hidden}.edui-default .edui-for-background .edui-dialog-content{width:440px;height:280px;overflow:hidden}.edui-default .edui-for-template .edui-dialog-content{width:630px;height:390px;overflow:hidden}.edui-default .edui-for-scrawl .edui-dialog-content{width:515px;*width:506px;height:360px}.edui-default .edui-for-spechars .edui-dialog-content{width:620px;height:500px;*width:630px;*height:570px}.edui-default .edui-for-insertimage .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-webapp .edui-dialog-content{width:560px;_width:565px;height:450px;overflow:hidden}.edui-default .edui-for-insertframe .edui-dialog-content{width:350px;height:200px;overflow:hidden}.edui-default .edui-for-wordimage .edui-dialog-content{width:620px;height:380px;overflow:hidden}.edui-default .edui-for-attachment .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-map .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-gmap .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-insertvideo .edui-dialog-content{width:590px;height:390px}.edui-default .edui-for-anchor .edui-dialog-content{width:320px;height:60px;overflow:hidden}.edui-default .edui-for-searchreplace .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-help .edui-dialog-content{width:400px;height:420px}.edui-default .edui-for-edittable .edui-dialog-content{width:540px;_width:590px;height:335px}.edui-default .edui-for-edittip .edui-dialog-content{width:225px;height:60px}.edui-default .edui-for-edittd .edui-dialog-content{width:240px;height:50px}.edui-default .edui-for-snapscreen .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-music .edui-dialog-content{width:515px;height:360px}.edui-default .edui-for-paragraph .edui-listitem-label{font-family:Tahoma,Verdana,Arial,Helvetica}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p{font-size:22px;line-height:27px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1{font-weight:bolder;font-size:32px;line-height:36px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2{font-weight:bolder;font-size:27px;line-height:29px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3{font-weight:bolder;font-size:19px;line-height:23px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4{font-weight:bolder;font-size:16px;line-height:19px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5{font-weight:bolder;font-size:13px;line-height:16px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6{font-weight:bolder;font-size:12px;line-height:14px}.edui-default .edui-for-inserttable .edui-splitborder{display:none}.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-tablepicker .edui-infoarea{height:14px;line-height:14px;font-size:12px;width:220px;margin-bottom:3px;clear:both}.edui-default .edui-tablepicker .edui-infoarea .edui-label{float:left}.edui-default .edui-dialog-buttons .edui-label{line-height:24px}.edui-default .edui-tablepicker .edui-infoarea .edui-clickable{float:right}.edui-default .edui-tablepicker .edui-pickarea{background:url(../images/unhighlighted.gif) repeat;height:220px;width:220px}.edui-default .edui-tablepicker .edui-pickarea .edui-overlay{background:url(../images/highlighted.gif) repeat}.edui-default .edui-colorpicker-topbar{height:27px;width:200px}.edui-default .edui-colorpicker-preview{height:20px;border:1px inset #000;margin-left:1px;width:128px;float:left}.edui-default .edui-colorpicker-nocolor{float:right;margin-right:1px;font-size:12px;line-height:14px;height:14px;border:1px solid #333;padding:3px 5px;cursor:pointer}.edui-default .edui-colorpicker-tablefirstrow{height:30px}.edui-default .edui-colorpicker-colorcell{width:14px;height:14px;display:block;margin:0;cursor:pointer}.edui-default .edui-colorpicker-colorcell:hover{width:14px;height:14px;margin:0}.edui-default .edui-colorpicker-advbtn{display:block;text-align:center;cursor:pointer;height:20px}.arrow_down{background:#fff url(../images/arrow_down.png) no-repeat center}.arrow_up{background:#fff url(../images/arrow_up.png) no-repeat center}.edui-colorpicker-adv{position:relative;overflow:hidden;height:180px;display:none}.edui-colorpicker-plant,.edui-colorpicker-hue{border:solid 1px #666}.edui-colorpicker-pad{width:150px;height:150px;left:14px;top:13px;position:absolute;background:red;overflow:hidden;cursor:crosshair}.edui-colorpicker-cover{position:absolute;top:0;left:0;width:150px;height:150px;background:url(../images/tangram-colorpicker.png) -160px -200px}.edui-colorpicker-padDot{position:absolute;top:0;left:0;width:11px;height:11px;overflow:hidden;background:url(../images/tangram-colorpicker.png) 0 -200px repeat-x;z-index:1000}.edui-colorpicker-sliderMain{position:absolute;left:171px;top:13px;width:19px;height:152px;background:url(../images/tangram-colorpicker.png) -179px -12px no-repeat}.edui-colorpicker-slider{width:100%;height:100%;cursor:pointer}.edui-colorpicker-thumb{position:absolute;top:0;cursor:pointer;height:3px;left:-1px;right:-1px;border:1px solid #000;background:#fff;opacity:.8}.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body{font-size:12px;margin-bottom:3px;clear:both}.edui-default .edui-autotypesetpicker-body table{border-collapse:separate;border-spacing:2px}.edui-default .edui-autotypesetpicker-body td{font-size:12px;word-wrap:break-word}.edui-default .edui-autotypesetpicker-body td input{margin:3px 3px 3px 4px;*margin:1px 0 0}.edui-default .edui-cellalignpicker .edui-cellalignpicker-body{width:70px;font-size:12px;cursor:default}.edui-default .edui-cellalignpicker-body table{border-collapse:separate;border-spacing:0}.edui-default .edui-cellalignpicker-body td{padding:1px}.edui-default .edui-cellalignpicker-body .edui-icon{height:20px;width:20px;padding:1px;background-image:url(../images/table-cell-align.png)}.edui-default .edui-cellalignpicker-body .edui-left{background-position:0 0}.edui-default .edui-cellalignpicker-body .edui-center{background-position:-25px 0}.edui-default .edui-cellalignpicker-body .edui-right{background-position:-51px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-left{background-position:-73px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-center{background-position:-98px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-right{background-position:-124px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-left{background-position:-146px 0;background-color:#f1f4f5}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-center{background-position:-245px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-right{background-position:-271px 0}.edui-default .edui-toolbar .edui-separator{width:2px;height:20px;margin:2px 4px 2px 3px;background:url(../images/icons.png) -181px 0;background:url(../images/icons.gif) -181px 0 \9}.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump{position:absolute;overflow:hidden;bottom:1px;left:1px;width:18px;height:4px}.edui-default .edui-for-emotion .edui-icon{background-position:-60px -20px}.edui-default .edui-for-emotion .edui-popup-content iframe{width:514px;height:380px;overflow:hidden}.edui-default .edui-for-emotion .edui-popup-content{position:relative;z-index:555}.edui-default .edui-for-emotion .edui-splitborder{display:none}.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-hassubmenu .edui-arrow{height:20px;width:20px;float:right;background:url(../images/icons-all.gif) no-repeat 10px -233px}.edui-default .edui-menu-body .edui-menuitem{padding:1px}.edui-default .edui-menuseparator{margin:2px 0;height:1px;overflow:hidden}.edui-default .edui-menuseparator-inner{border-bottom:1px solid #e2e3e3;margin-left:29px;margin-right:1px}.edui-default .edui-menu-body .edui-state-hover{padding:0!important;background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-shortcutmenu{padding:2px;width:190px;height:50px;background-color:#fff;border:1px solid #ccc;border-radius:5px}.edui-default .edui-wordpastepop .edui-popup-content{border:0;padding:0;width:54px;height:21px}.edui-default .edui-pasteicon{width:100%;height:100%;background-image:url(../images/wordpaste.png);background-position:0 0}.edui-default .edui-pasteicon.edui-state-opened{background-position:0 -34px}.edui-default .edui-pastecontainer{position:relative;visibility:hidden;width:97px;background:#fff;border:1px solid #ccc}.edui-default .edui-pastecontainer .edui-title{font-weight:700;background:#F8F8FF;height:25px;line-height:25px;font-size:12px;padding-left:5px}.edui-default .edui-pastecontainer .edui-button{overflow:hidden;margin:3px 0}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon,.edui-default .edui-pastecontainer .edui-button .edui-tagicon,.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{float:left;cursor:pointer;width:29px;height:29px;margin-left:5px;background-image:url(../images/wordpaste.png);background-repeat:no-repeat}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon{margin-left:0;background-position:-109px 0}.edui-default .edui-pastecontainer .edui-button .edui-tagicon{background-position:-148px 1px}.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{background-position:-72px 0}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-richtxticon{background-position:-109px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-tagicon{background-position:-148px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-plaintxticon{background-position:-72px -34px}
\ No newline at end of file
+.edui-default .edui-box{border:0;padding:0;margin:0;overflow:hidden}.edui-default a.edui-box{display:block;text-decoration:none;color:#000}.edui-default a.edui-box:hover{text-decoration:none}.edui-default a.edui-box:active{text-decoration:none}.edui-default table.edui-box{border-collapse:collapse}.edui-default ul.edui-box{list-style-type:none}div.edui-box{position:relative;display:-moz-inline-box!important;display:inline-block!important;vertical-align:top}.edui-default .edui-clearfix{zoom:1}.edui-default .edui-clearfix:after{content:'\20';display:block;clear:both}* html div.edui-box{display:inline!important}:first-child+html div.edui-box{display:inline!important}.edui-default .edui-button-body,.edui-splitbutton-body,.edui-menubutton-body,.edui-combox-body{position:relative}.edui-default .edui-popup{position:absolute;-webkit-user-select:none;-moz-user-select:none}.edui-default .edui-popup .edui-shadow{position:absolute;z-index:-1}.edui-default .edui-popup .edui-bordereraser{position:absolute;overflow:hidden}.edui-default .edui-tablepicker .edui-canvas{position:relative}.edui-default .edui-tablepicker .edui-canvas .edui-overlay{position:absolute}.edui-default .edui-dialog-modalmask,.edui-dialog-dragmask{position:absolute;left:0;top:0;width:100%;height:100%}.edui-default .edui-toolbar{position:relative}.edui-default .edui-label{cursor:default}.edui-default span.edui-clickable{color:#00f;cursor:pointer;text-decoration:underline}.edui-default span.edui-unclickable{color:gray;cursor:default}.edui-default .edui-toolbar{cursor:default;-webkit-user-select:none;-moz-user-select:none;padding:1px;overflow:hidden;zoom:1;width:auto;height:auto}.edui-default .edui-toolbar .edui-button,.edui-default .edui-toolbar .edui-splitbutton,.edui-default .edui-toolbar .edui-menubutton,.edui-default .edui-toolbar .edui-combox{margin:1px}.edui-default .edui-editor{border:1px solid #d4d4d4;background-color:#fff;position:relative;overflow:visible;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.edui-editor div{width:auto;height:auto}.edui-default .edui-editor-toolbarbox{position:relative;zoom:1;-webkit-box-shadow:0 1px 4px rgba(204,204,204,.6);-moz-box-shadow:0 1px 4px rgba(204,204,204,.6);box-shadow:0 1px 4px rgba(204,204,204,.6);border-top-left-radius:2px;border-top-right-radius:2px}.edui-default .edui-editor-toolbarboxouter{border-bottom:1px solid #d4d4d4;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,.065);box-shadow:0 1px 4px rgba(0,0,0,.065)}.edui-default .edui-editor-toolbarboxinner{padding:2px}.edui-default .edui-editor-iframeholder{position:relative}.edui-default .edui-editor-bottomContainer{overflow:hidden}.edui-default .edui-editor-bottomContainer table{width:100%;height:0;overflow:hidden;border-spacing:0}.edui-default .edui-editor-bottomContainer td{white-space:nowrap;border-top:1px solid #ccc;line-height:20px;font-size:12px;font-family:Arial,Helvetica,Tahoma,Verdana,Sans-Serif}.edui-default .edui-editor-wordcount{text-align:right;margin-right:5px;color:#aaa}.edui-default .edui-editor-scale{width:12px}.edui-default .edui-editor-scale .edui-editor-icon{float:right;width:100%;height:12px;margin-top:10px;background:url(../images/scale.png) no-repeat;cursor:se-resize}.edui-default .edui-editor-breadcrumb{margin:2px 0 0 3px}.edui-default .edui-editor-breadcrumb span{cursor:pointer;text-decoration:underline;color:#00f}.edui-default .edui-toolbar .edui-for-fullscreen{float:right}.edui-default .edui-bubble .edui-popup-content{border:1px solid #DCAC6C;background-color:#fff6d9;padding:5px;font-size:10pt;font-family:"宋体"}.edui-default .edui-bubble .edui-shadow{}.edui-default .edui-editor-toolbarmsg{background-color:#FFF6D9;border-bottom:1px solid #ccc;position:absolute;bottom:-25px;left:0;z-index:1009;width:99.9%}.edui-default .edui-editor-toolbarmsg-upload{font-size:14px;color:#00f;width:100px;height:16px;line-height:16px;cursor:pointer;position:absolute;top:5px;left:350px}.edui-default .edui-editor-toolbarmsg-label{font-size:12px;line-height:16px;padding:4px}.edui-default .edui-editor-toolbarmsg-close{float:right;width:20px;height:16px;line-height:16px;cursor:pointer;color:red}.edui-default .edui-list .edui-bordereraser{display:none}.edui-default .edui-listitem{padding:1px;white-space:nowrap}.edui-default .edui-list .edui-state-hover{position:relative;background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-for-fontfamily .edui-listitem-label{min-width:130px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-insertcode .edui-listitem-label{min-width:120px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-underline .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px;font-size:12px}.edui-default .edui-for-fontsize .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px}.edui-default .edui-for-paragraph .edui-listitem-label{min-width:200px;_width:200px;padding:2px 5px}.edui-default .edui-for-rowspacingtop .edui-listitem-label,.edui-default .edui-for-rowspacingbottom .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-lineheight .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-customstyle .edui-listitem-label{min-width:200px;_width:200px;width:200px!important;padding:2px 5px}.edui-default .edui-menu{z-index:3000}.edui-default .edui-menu .edui-popup-content{padding:3px}.edui-default .edui-menu-body{_width:150px;min-width:170px;background:url(../images/sparator_v.png) repeat-y 25px}.edui-default .edui-menuitem-body{}.edui-default .edui-menuitem{height:20px;cursor:default;vertical-align:top}.edui-default .edui-menuitem .edui-icon{width:20px!important;height:20px!important;background:url(../images/icons.png) 0 -4000px;background:url(../images/icons.gif) 0 -4000px\9}.edui-default .edui-menuitem .edui-label{font-size:12px;line-height:20px;height:20px;padding-left:10px}.edui-default .edui-state-checked .edui-menuitem-body{background:url(../images/icons-all.gif) no-repeat 6px -205px}.edui-default .edui-state-disabled .edui-menuitem-label{color:gray}.edui-default .edui-toolbar .edui-combox-body .edui-button-body{width:60px;font-size:12px;height:20px;line-height:20px;padding-left:5px;white-space:nowrap;margin:0 3px 0 0}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-combox .edui-combox-body{border:1px solid #CCC;background-color:#fff;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-combox-body .edui-splitborder{display:none}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{border-left:1px solid #CCC}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body{background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow{border-left:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-checked .edui-combox-body{background-color:#FFE69F;border:1px solid #DCAC6C}.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow{border-left:1px solid #DCAC6C}.edui-toolbar .edui-state-disabled .edui-combox-body{background-color:#F0F0EE;opacity:.3;filter:alpha(opacity=30)}.edui-toolbar .edui-state-opened .edui-combox-body{background-color:#fff;border:1px solid gray}.edui-default .edui-toolbar .edui-button .edui-icon,.edui-default .edui-toolbar .edui-menubutton .edui-icon,.edui-default .edui-toolbar .edui-splitbutton .edui-icon{height:20px!important;width:20px!important;background-image:url(../images/icons.png);background-image:url(../images/icons.gif) \9}.edui-default .edui-toolbar .edui-button .edui-button-wrap{padding:1px;position:relative}.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap{background-color:#fff5d4;padding:0;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap{background-color:#ffe69f;padding:0;border:1px solid #dcac6c;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap{background-color:#fff;padding:0;border:1px solid gray}.edui-default .edui-toolbar .edui-state-disabled .edui-label{color:#ccc}.edui-default .edui-toolbar .edui-state-disabled .edui-icon{opacity:.3;filter:alpha(opacity=30)}.edui-default .edui-for-undo .edui-icon{background-position:-160px 0}.edui-default .edui-for-redo .edui-icon{background-position:-100px 0}.edui-default .edui-for-bold .edui-icon{background-position:0 0}.edui-default .edui-for-italic .edui-icon{background-position:-60px 0}.edui-default .edui-for-fontborder .edui-icon{background-position:-160px -40px}.edui-default .edui-for-underline .edui-icon{background-position:-140px 0}.edui-default .edui-for-strikethrough .edui-icon{background-position:-120px 0}.edui-default .edui-for-subscript .edui-icon{background-position:-600px 0}.edui-default .edui-for-superscript .edui-icon{background-position:-620px 0}.edui-default .edui-for-blockquote .edui-icon{background-position:-220px 0}.edui-default .edui-for-forecolor .edui-icon{background-position:-720px 0}.edui-default .edui-for-backcolor .edui-icon{background-position:-760px 0}.edui-default .edui-for-inserttable .edui-icon{background-position:-580px -20px}.edui-default .edui-for-autotypeset .edui-icon{background-position:-640px -40px}.edui-default .edui-for-justifyleft .edui-icon{background-position:-460px 0}.edui-default .edui-for-justifycenter .edui-icon{background-position:-420px 0}.edui-default .edui-for-justifyright .edui-icon{background-position:-480px 0}.edui-default .edui-for-justifyjustify .edui-icon{background-position:-440px 0}.edui-default .edui-for-insertorderedlist .edui-icon{background-position:-80px 0}.edui-default .edui-for-insertunorderedlist .edui-icon{background-position:-20px 0}.edui-default .edui-for-lineheight .edui-icon{background-position:-725px -40px}.edui-default .edui-for-rowspacingbottom .edui-icon{background-position:-745px -40px}.edui-default .edui-for-rowspacingtop .edui-icon{background-position:-765px -40px}.edui-default .edui-for-horizontal .edui-icon{background-position:-360px 0}.edui-default .edui-for-link .edui-icon{background-position:-500px 0}.edui-default .edui-for-code .edui-icon{background-position:-440px -40px}.edui-default .edui-for-insertimage .edui-icon{background-position:-726px -77px}.edui-default .edui-for-insertframe .edui-icon{background-position:-240px -40px}.edui-default .edui-for-emoticon .edui-icon{background-position:-60px -20px}.edui-default .edui-for-spechars .edui-icon{background-position:-240px 0}.edui-default .edui-for-help .edui-icon{background-position:-340px 0}.edui-default .edui-for-print .edui-icon{background-position:-440px -20px}.edui-default .edui-for-preview .edui-icon{background-position:-420px -20px}.edui-default .edui-for-selectall .edui-icon{background-position:-400px -20px}.edui-default .edui-for-searchreplace .edui-icon{background-position:-520px -20px}.edui-default .edui-for-map .edui-icon{background-position:-40px -40px}.edui-default .edui-for-gmap .edui-icon{background-position:-260px -40px}.edui-default .edui-for-insertvideo .edui-icon{background-position:-320px -20px}.edui-default .edui-for-time .edui-icon{background-position:-160px -20px}.edui-default .edui-for-date .edui-icon{background-position:-140px -20px}.edui-default .edui-for-cut .edui-icon{background-position:-680px 0}.edui-default .edui-for-copy .edui-icon{background-position:-700px 0}.edui-default .edui-for-paste .edui-icon{background-position:-560px 0}.edui-default .edui-for-formatmatch .edui-icon{background-position:-40px 0}.edui-default .edui-for-pasteplain .edui-icon{background-position:-360px -20px}.edui-default .edui-for-directionalityltr .edui-icon{background-position:-20px -20px}.edui-default .edui-for-directionalityrtl .edui-icon{background-position:-40px -20px}.edui-default .edui-for-source .edui-icon{background-position:-261px -0px}.edui-default .edui-for-removeformat .edui-icon{background-position:-580px 0}.edui-default .edui-for-unlink .edui-icon{background-position:-640px 0}.edui-default .edui-for-touppercase .edui-icon{background-position:-786px 0}.edui-default .edui-for-tolowercase .edui-icon{background-position:-806px 0}.edui-default .edui-for-insertrow .edui-icon{background-position:-478px -76px}.edui-default .edui-for-insertrownext .edui-icon{background-position:-498px -76px}.edui-default .edui-for-insertcol .edui-icon{background-position:-455px -76px}.edui-default .edui-for-insertcolnext .edui-icon{background-position:-429px -76px}.edui-default .edui-for-mergeright .edui-icon{background-position:-60px -40px}.edui-default .edui-for-mergedown .edui-icon{background-position:-80px -40px}.edui-default .edui-for-splittorows .edui-icon{background-position:-100px -40px}.edui-default .edui-for-splittocols .edui-icon{background-position:-120px -40px}.edui-default .edui-for-insertparagraphbeforetable .edui-icon{background-position:-140px -40px}.edui-default .edui-for-deleterow .edui-icon{background-position:-660px -20px}.edui-default .edui-for-deletecol .edui-icon{background-position:-640px -20px}.edui-default .edui-for-splittocells .edui-icon{background-position:-800px -20px}.edui-default .edui-for-mergecells .edui-icon{background-position:-760px -20px}.edui-default .edui-for-deletetable .edui-icon{background-position:-620px -20px}.edui-default .edui-for-cleardoc .edui-icon{background-position:-520px 0}.edui-default .edui-for-fullscreen .edui-icon{background-position:-100px -20px}.edui-default .edui-for-anchor .edui-icon{background-position:-200px 0}.edui-default .edui-for-pagebreak .edui-icon{background-position:-460px -40px}.edui-default .edui-for-imagenone .edui-icon{background-position:-480px -40px}.edui-default .edui-for-imageleft .edui-icon{background-position:-500px -40px}.edui-default .edui-for-wordimage .edui-icon{background-position:-660px -40px}.edui-default .edui-for-imageright .edui-icon{background-position:-520px -40px}.edui-default .edui-for-imagecenter .edui-icon{background-position:-540px -40px}.edui-default .edui-for-indent .edui-icon{background-position:-400px 0}.edui-default .edui-for-outdent .edui-icon{background-position:-540px 0}.edui-default .edui-for-webapp .edui-icon{background-position:-601px -40px}.edui-default .edui-for-table .edui-icon{background-position:-580px -20px}.edui-default .edui-for-edittable .edui-icon{background-position:-420px -40px}.edui-default .edui-for-template .edui-icon{background-position:-339px -40px}.edui-default .edui-for-delete .edui-icon{background-position:-360px -40px}.edui-default .edui-for-attachment .edui-icon{background-position:-620px -40px}.edui-default .edui-for-edittd .edui-icon{background-position:-700px -40px}.edui-default .edui-for-snapscreen .edui-icon{background-position:-581px -40px}.edui-default .edui-for-scrawl .edui-icon{background-position:-801px -41px}.edui-default .edui-for-background .edui-icon{background-position:-680px -40px}.edui-default .edui-for-music .edui-icon{background-position:-18px -40px}.edui-default .edui-for-formula .edui-icon{background-position:-200px -40px}.edui-default .edui-for-aligntd .edui-icon{background-position:-236px -76px}.edui-default .edui-for-insertparagraphtrue .edui-icon{background-position:-625px -76px}.edui-default .edui-for-insertparagraph .edui-icon{background-position:-602px -76px}.edui-default .edui-for-insertcaption .edui-icon{background-position:-336px -76px}.edui-default .edui-for-deletecaption .edui-icon{background-position:-362px -76px}.edui-default .edui-for-inserttitle .edui-icon{background-position:-286px -76px}.edui-default .edui-for-deletetitle .edui-icon{background-position:-311px -76px}.edui-default .edui-for-aligntable .edui-icon{background-position:-440px 0}.edui-default .edui-for-tablealignment-left .edui-icon{background-position:-460px 0}.edui-default .edui-for-tablealignment-center .edui-icon{background-position:-420px 0}.edui-default .edui-for-tablealignment-right .edui-icon{background-position:-480px 0}.edui-default .edui-for-drafts .edui-icon{background-position:-560px 0}.edui-default .edui-for-charts .edui-icon{background:url( ../images/charts.png ) no-repeat 2px 3px!important}.edui-default .edui-for-inserttitlecol .edui-icon{background-position:-673px -76px}.edui-default .edui-for-deletetitlecol .edui-icon{background-position:-698px -76px}.edui-default .edui-for-simpleupload .edui-icon{background-position:-380px 0}.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow,.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body{padding:1px}.edui-default .edui-toolbar .edui-splitborder{width:1px;height:20px}.edui-default .edui-toolbar .edui-state-hover .edui-splitborder{width:1px;border-left:0 solid #dcac6c}.edui-default .edui-toolbar .edui-state-active .edui-splitborder{width:0;border-left:1px solid gray}.edui-default .edui-toolbar .edui-state-opened .edui-splitborder{width:1px;border:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body{background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body{background-color:#FFE69F;border:1px solid #DCAC6C;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-state-disabled .edui-arrow{opacity:.3;_filter:alpha(opacity=30)}.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-for-insertorderedlist .edui-bordereraser,.edui-default .edui-for-lineheight .edui-bordereraser,.edui-default .edui-for-rowspacingtop .edui-bordereraser,.edui-default .edui-for-rowspacingbottom .edui-bordereraser,.edui-default .edui-for-insertunorderedlist .edui-bordereraser{background-color:#fff}.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon,.edui-default .edui-for-lineheight .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon,.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon{background-image:none}.edui-default .edui-popup{z-index:3000;background-color:#fff;width:auto;height:auto}.edui-default .edui-popup .edui-shadow{left:0;top:0;width:100%;height:100%}.edui-default .edui-popup-content{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 4px rgba(0,0,0,.2);-moz-box-shadow:0 3px 4px rgba(0,0,0,.2);box-shadow:0 3px 4px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;padding:5px;background:#fff}.edui-default .edui-popup .edui-bordereraser{background-color:#fff;height:3px}.edui-default .edui-menu .edui-bordereraser{height:3px}.edui-default .edui-anchor-topleft .edui-bordereraser{left:1px;top:-2px}.edui-default .edui-anchor-topright .edui-bordereraser{right:1px;top:-2px}.edui-default .edui-anchor-bottomleft .edui-bordereraser{left:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-default .edui-anchor-bottomright .edui-bordereraser{right:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-popup div{width:auto;height:auto}.edui-default .edui-editor-messageholder{display:block;width:150px;height:auto;border:0;margin:0;padding:0;position:absolute;top:28px;right:3px}.edui-default .edui-message{min-height:10px;text-shadow:0 1px 0 rgba(255,255,255,.5);padding:0;margin-bottom:3px;position:relative}.edui-default .edui-message-body{border-radius:3px;padding:8px 15px 8px 8px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5}.edui-default .edui-message-type-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.edui-default .edui-message-type-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.edui-default .edui-message-type-danger,.edui-default .edui-message-type-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.edui-default .edui-message .edui-message-closer{display:block;width:16px;height:16px;line-height:16px;position:absolute;top:0;right:0;padding:0;cursor:pointer;background:transparent;border:0;float:right;font-size:20px;font-weight:700;color:#999;text-shadow:0 1px 0 #fff;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.edui-default .edui-message .edui-message-content{font-size:10pt;word-wrap:break-word;word-break:normal}.edui-default .edui-dialog{z-index:2000;position:absolute}.edui-dialog div{width:auto}.edui-default .edui-dialog-wrap{margin-right:6px;margin-bottom:6px}.edui-default .edui-dialog-fullscreen-flag{margin-right:0;margin-bottom:0}.edui-default .edui-dialog-body{position:relative;padding:2px 0 0 2px;_zoom:1}.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body{padding:0}.edui-default .edui-dialog-shadow{position:absolute;z-index:-1;left:0;top:0;width:100%;height:100%;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.edui-default .edui-dialog-foot{background-color:#fff}.edui-default .edui-dialog-titlebar{height:26px;border-bottom:1px solid #c6c6c6;background:url(../images/dialog-title-bg.png) repeat-x bottom;position:relative;cursor:move}.edui-default .edui-dialog-caption{font-weight:700;font-size:12px;line-height:26px;padding-left:5px}.edui-default .edui-dialog-draghandle{height:26px}.edui-default .edui-dialog-closebutton{position:absolute!important;right:5px;top:3px}.edui-default .edui-dialog-closebutton .edui-button-body{height:20px;width:20px;cursor:pointer;background:url(../images/icons-all.gif) no-repeat 0 -59px}.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -89px}.edui-default .edui-dialog-foot{height:40px}.edui-default .edui-dialog-buttons{position:absolute;right:0}.edui-default .edui-dialog-buttons .edui-button{margin-right:10px}.edui-default .edui-dialog-buttons .edui-button .edui-button-body{background:url(../images/icons-all.gif) no-repeat;height:24px;width:96px;font-size:12px;line-height:24px;text-align:center;cursor:default}.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -30px}.edui-default .edui-dialog iframe{border:0;padding:0;margin:0;vertical-align:top}.edui-default .edui-dialog-modalmask{opacity:.3;filter:alpha(opacity=30);background-color:#ccc;position:absolute}.edui-default .edui-dialog-dragmask{position:absolute;background-color:transparent;cursor:move}.edui-default .edui-dialog-content{position:relative}.edui-default .dialogcontmask{cursor:move;visibility:hidden;display:block;position:absolute;width:100%;height:100%;opacity:0;filter:alpha(opacity=0)}.edui-default .edui-for-link .edui-dialog-content{width:420px;height:200px;overflow:hidden}.edui-default .edui-for-background .edui-dialog-content{width:440px;height:280px;overflow:hidden}.edui-default .edui-for-template .edui-dialog-content{width:630px;height:390px;overflow:hidden}.edui-default .edui-for-scrawl .edui-dialog-content{width:515px;*width:506px;height:360px}.edui-default .edui-for-spechars .edui-dialog-content{width:620px;height:500px;*width:630px;*height:570px}.edui-default .edui-for-insertimage .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-webapp .edui-dialog-content{width:560px;_width:565px;height:450px;overflow:hidden}.edui-default .edui-for-insertframe .edui-dialog-content{width:350px;height:200px;overflow:hidden}.edui-default .edui-for-wordimage .edui-dialog-content{width:620px;height:380px;overflow:hidden}.edui-default .edui-for-attachment .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-map .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-gmap .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-insertvideo .edui-dialog-content{width:590px;height:390px}.edui-default .edui-for-anchor .edui-dialog-content{width:320px;height:60px;overflow:hidden}.edui-default .edui-for-searchreplace .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-help .edui-dialog-content{width:400px;height:420px}.edui-default .edui-for-edittable .edui-dialog-content{width:540px;_width:590px;height:335px}.edui-default .edui-for-edittip .edui-dialog-content{width:225px;height:60px}.edui-default .edui-for-edittd .edui-dialog-content{width:240px;height:50px}.edui-default .edui-for-snapscreen .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-music .edui-dialog-content{width:515px;height:360px}.edui-default .edui-for-paragraph .edui-listitem-label{font-family:Tahoma,Verdana,Arial,Helvetica}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p{font-size:22px;line-height:27px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1{font-weight:bolder;font-size:32px;line-height:36px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2{font-weight:bolder;font-size:27px;line-height:29px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3{font-weight:bolder;font-size:19px;line-height:23px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4{font-weight:bolder;font-size:16px;line-height:19px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5{font-weight:bolder;font-size:13px;line-height:16px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6{font-weight:bolder;font-size:12px;line-height:14px}.edui-default .edui-for-inserttable .edui-splitborder{display:none}.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-tablepicker .edui-infoarea{height:14px;line-height:14px;font-size:12px;width:220px;margin-bottom:3px;clear:both}.edui-default .edui-tablepicker .edui-infoarea .edui-label{float:left}.edui-default .edui-dialog-buttons .edui-label{line-height:24px}.edui-default .edui-tablepicker .edui-infoarea .edui-clickable{float:right}.edui-default .edui-tablepicker .edui-pickarea{background:url(../images/unhighlighted.gif) repeat;height:220px;width:220px}.edui-default .edui-tablepicker .edui-pickarea .edui-overlay{background:url(../images/highlighted.gif) repeat}.edui-default .edui-colorpicker-topbar{height:27px;width:200px}.edui-default .edui-colorpicker-preview{height:20px;border:1px inset #000;margin-left:1px;width:128px;float:left}.edui-default .edui-colorpicker-nocolor{float:right;margin-right:1px;font-size:12px;line-height:14px;height:14px;border:1px solid #333;padding:3px 5px;cursor:pointer}.edui-default .edui-colorpicker-tablefirstrow{height:30px}.edui-default .edui-colorpicker-colorcell{width:14px;height:14px;display:block;margin:0;cursor:pointer}.edui-default .edui-colorpicker-colorcell:hover{width:14px;height:14px;margin:0}.edui-default .edui-colorpicker-advbtn{display:block;text-align:center;cursor:pointer;height:20px}.arrow_down{background:#fff url(../images/arrow_down.png) no-repeat center}.arrow_up{background:#fff url(../images/arrow_up.png) no-repeat center}.edui-colorpicker-adv{position:relative;overflow:hidden;height:180px;display:none}.edui-colorpicker-plant,.edui-colorpicker-hue{border:solid 1px #666}.edui-colorpicker-pad{width:150px;height:150px;left:14px;top:13px;position:absolute;background:red;overflow:hidden;cursor:crosshair}.edui-colorpicker-cover{position:absolute;top:0;left:0;width:150px;height:150px;background:url(../images/tangram-colorpicker.png) -160px -200px}.edui-colorpicker-padDot{position:absolute;top:0;left:0;width:11px;height:11px;overflow:hidden;background:url(../images/tangram-colorpicker.png) 0 -200px repeat-x;z-index:1000}.edui-colorpicker-sliderMain{position:absolute;left:171px;top:13px;width:19px;height:152px;background:url(../images/tangram-colorpicker.png) -179px -12px no-repeat}.edui-colorpicker-slider{width:100%;height:100%;cursor:pointer}.edui-colorpicker-thumb{position:absolute;top:0;cursor:pointer;height:3px;left:-1px;right:-1px;border:1px solid #000;background:#fff;opacity:.8}.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body{font-size:12px;margin-bottom:3px;clear:both}.edui-default .edui-autotypesetpicker-body table{border-collapse:separate;border-spacing:2px}.edui-default .edui-autotypesetpicker-body td{font-size:12px;word-wrap:break-word}.edui-default .edui-autotypesetpicker-body td input{margin:3px 3px 3px 4px;*margin:1px 0 0}.edui-default .edui-cellalignpicker .edui-cellalignpicker-body{width:70px;font-size:12px;cursor:default}.edui-default .edui-cellalignpicker-body table{border-collapse:separate;border-spacing:0}.edui-default .edui-cellalignpicker-body td{padding:1px}.edui-default .edui-cellalignpicker-body .edui-icon{height:20px;width:20px;padding:1px;background-image:url(../images/table-cell-align.png)}.edui-default .edui-cellalignpicker-body .edui-left{background-position:0 0}.edui-default .edui-cellalignpicker-body .edui-center{background-position:-25px 0}.edui-default .edui-cellalignpicker-body .edui-right{background-position:-51px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-left{background-position:-73px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-center{background-position:-98px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-right{background-position:-124px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-left{background-position:-146px 0;background-color:#f1f4f5}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-center{background-position:-245px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-right{background-position:-271px 0}.edui-default .edui-toolbar .edui-separator{width:2px;height:20px;margin:2px 4px 2px 3px;background:url(../images/icons.png) -181px 0;background:url(../images/icons.gif) -181px 0 \9}.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump{position:absolute;overflow:hidden;bottom:1px;left:1px;width:18px;height:4px}.edui-default .edui-for-emotion .edui-icon{background-position:-60px -20px}.edui-default .edui-for-emotion .edui-popup-content iframe{width:514px;height:380px;overflow:hidden}.edui-default .edui-for-emotion .edui-popup-content{position:relative;z-index:555}.edui-default .edui-for-emotion .edui-splitborder{display:none}.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-hassubmenu .edui-arrow{height:20px;width:20px;float:right;background:url(../images/icons-all.gif) no-repeat 10px -233px}.edui-default .edui-menu-body .edui-menuitem{padding:1px}.edui-default .edui-menuseparator{margin:2px 0;height:1px;overflow:hidden}.edui-default .edui-menuseparator-inner{border-bottom:1px solid #e2e3e3;margin-left:29px;margin-right:1px}.edui-default .edui-menu-body .edui-state-hover{padding:0!important;background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-shortcutmenu{padding:2px;width:190px;height:50px;background-color:#fff;border:1px solid #ccc;border-radius:5px}.edui-default .edui-wordpastepop .edui-popup-content{border:0;padding:0;width:54px;height:21px}.edui-default .edui-pasteicon{width:100%;height:100%;background-image:url(../images/wordpaste.png);background-position:0 0}.edui-default .edui-pasteicon.edui-state-opened{background-position:0 -34px}.edui-default .edui-pastecontainer{position:relative;visibility:hidden;width:97px;background:#fff;border:1px solid #ccc}.edui-default .edui-pastecontainer .edui-title{font-weight:700;background:#F8F8FF;height:25px;line-height:25px;font-size:12px;padding-left:5px}.edui-default .edui-pastecontainer .edui-button{overflow:hidden;margin:3px 0}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon,.edui-default .edui-pastecontainer .edui-button .edui-tagicon,.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{float:left;cursor:pointer;width:29px;height:29px;margin-left:5px;background-image:url(../images/wordpaste.png);background-repeat:no-repeat}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon{margin-left:0;background-position:-109px 0}.edui-default .edui-pastecontainer .edui-button .edui-tagicon{background-position:-148px 1px}.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{background-position:-72px 0}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-richtxticon{background-position:-109px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-tagicon{background-position:-148px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-plaintxticon{background-position:-72px -34px}
\ No newline at end of file
diff --git a/assets/themes/default/dialogbase.css b/assets/themes/default/dialogbase.css
index bbec343..ea71266 100644
--- a/assets/themes/default/dialogbase.css
+++ b/assets/themes/default/dialogbase.css
@@ -1,100 +1,100 @@
-/*弹出对话框页面样式组件
-*/
-
-/*reset
-*/
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, font, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td {
- margin: 0;
- padding: 0;
- outline: 0;
- font-size: 100%;
-}
-
-body {
- line-height: 1;
-}
-
-ol, ul {
- list-style: none;
-}
-
-blockquote, q {
- quotes: none;
-}
-
-ins {
- text-decoration: none;
-}
-
-del {
- text-decoration: line-through;
-}
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-/*module
-*/
-body {
- background-color: #fff;
- font: 12px/1.5 sans-serif, "宋体", "Arial Narrow", HELVETICA;
- color: #646464;
-}
-
-/*tab*/
-.tabhead {
- position: relative;
- z-index: 10;
-}
-
-.tabhead span {
- display: inline-block;
- padding: 0 5px;
- height: 30px;
- border: 1px solid #ccc;
- background: url("images/dialog-title-bg.png") repeat-x;
- text-align: center;
- line-height: 30px;
- cursor: pointer;
- *margin-right: 5px;
-}
-
-.tabhead span.focus {
- height: 31px;
- border-bottom: none;
- background: #fff;
-}
-
-.tabbody {
- position: relative;
- top: -1px;
- margin: 0 auto;
- border: 1px solid #ccc;
-}
-
-/*button*/
-a.button {
- display: block;
- text-align: center;
- line-height: 24px;
- text-decoration: none;
- height: 24px;
- width: 95px;
- border: 0;
- color: #838383;
- background: url(images/icons-all.gif) no-repeat;
-}
-
-a.button:hover {
- background-position: 0 -30px;
+/*弹出对话框页面样式组件
+*/
+
+/*reset
+*/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ outline: 0;
+ font-size: 100%;
+}
+
+body {
+ line-height: 1;
+}
+
+ol, ul {
+ list-style: none;
+}
+
+blockquote, q {
+ quotes: none;
+}
+
+ins {
+ text-decoration: none;
+}
+
+del {
+ text-decoration: line-through;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+/*module
+*/
+body {
+ background-color: #fff;
+ font: 12px/1.5 sans-serif, "宋体", "Arial Narrow", HELVETICA;
+ color: #646464;
+}
+
+/*tab*/
+.tabhead {
+ position: relative;
+ z-index: 10;
+}
+
+.tabhead span {
+ display: inline-block;
+ padding: 0 5px;
+ height: 30px;
+ border: 1px solid #ccc;
+ background: url("images/dialog-title-bg.png") repeat-x;
+ text-align: center;
+ line-height: 30px;
+ cursor: pointer;
+ *margin-right: 5px;
+}
+
+.tabhead span.focus {
+ height: 31px;
+ border-bottom: none;
+ background: #fff;
+}
+
+.tabbody {
+ position: relative;
+ top: -1px;
+ margin: 0 auto;
+ border: 1px solid #ccc;
+}
+
+/*button*/
+a.button {
+ display: block;
+ text-align: center;
+ line-height: 24px;
+ text-decoration: none;
+ height: 24px;
+ width: 95px;
+ border: 0;
+ color: #838383;
+ background: url(../../themes/default/images/icons-all.gif) no-repeat;
+}
+
+a.button:hover {
+ background-position: 0 -30px;
}
\ No newline at end of file
diff --git a/assets/themes/iframe.css b/assets/themes/iframe.css
index 774013a..32e7222 100644
--- a/assets/themes/iframe.css
+++ b/assets/themes/iframe.css
@@ -1 +1 @@
-/*可以在这里添加你自己的css*/
+/*可以在这里添加你自己的css*/
diff --git a/assets/third-party/codemirror/codemirror.js b/assets/third-party/codemirror/codemirror.js
index 966c320..083f242 100644
--- a/assets/third-party/codemirror/codemirror.js
+++ b/assets/third-party/codemirror/codemirror.js
@@ -1,3581 +1,3581 @@
-// CodeMirror version 2.2
-//
-// All functions that need access to the editor's state live inside
-// the CodeMirror function. Below that, at the bottom of the file,
-// some utilities are defined.
-
-// CodeMirror is the only global var we claim
-var CodeMirror = (function() {
- // This is the function that produces an editor instance. It's
- // closure is used to store the editor state.
- function CodeMirror(place, givenOptions) {
- // Determine effective options based on given values and defaults.
- var options = {}, defaults = CodeMirror.defaults;
- for (var opt in defaults)
- if (defaults.hasOwnProperty(opt))
- options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
-
- var targetDocument = options["document"];
- // The element in which the editor lives.
- var wrapper = targetDocument.createElement("div");
- wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
- // This mess creates the base DOM structure for the editor.
- wrapper.innerHTML =
- '
' + // Wraps and hides input textarea
- '
' +
- '
';
- if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
- // I've never seen more elegant code in my life.
- var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
- scroller = wrapper.lastChild, code = scroller.firstChild,
- mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
- lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
- cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
- themeChanged();
- // Needed to hide big blue blinking cursor on Mobile Safari
- if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
- if (!webkit) lineSpace.draggable = true;
- if (options.tabindex != null) input.tabIndex = options.tabindex;
- if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
-
- // Check for problem with IE innerHTML not working when we have a
- // P (or similar) parent node.
- try { stringWidth("x"); }
- catch (e) {
- if (e.message.match(/runtime/i))
- e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
- throw e;
- }
-
- // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
- var poll = new Delayed(), highlight = new Delayed(), blinker;
-
- // mode holds a mode API object. doc is the tree of Line objects,
- // work an array of lines that should be parsed, and history the
- // undo history (instance of History constructor).
- var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
- loadMode();
- // The selection. These are always maintained to point at valid
- // positions. Inverted is used to remember that the user is
- // selecting bottom-to-top.
- var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
- // Selection-related flags. shiftSelecting obviously tracks
- // whether the user is holding shift.
- var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
- // Variables used by startOperation/endOperation to track what
- // happened during the operation.
- var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
- gutterDirty, callbacks;
- // Current visible range (may be bigger than the view window).
- var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
- // bracketHighlighted is used to remember that a backet has been
- // marked.
- var bracketHighlighted;
- // Tracks the maximum line length so that the horizontal scrollbar
- // can be kept static when scrolling.
- var maxLine = "", maxWidth, tabText = computeTabText();
-
- // Initialize the content.
- operation(function(){setValue(options.value || ""); updateInput = false;})();
- var history = new History();
-
- // Register our event handlers.
- connect(scroller, "mousedown", operation(onMouseDown));
- connect(scroller, "dblclick", operation(onDoubleClick));
- connect(lineSpace, "dragstart", onDragStart);
- connect(lineSpace, "selectstart", e_preventDefault);
- // Gecko browsers fire contextmenu *after* opening the menu, at
- // which point we can't mess with it anymore. Context menu is
- // handled in onMouseDown for Gecko.
- if (!gecko) connect(scroller, "contextmenu", onContextMenu);
- connect(scroller, "scroll", function() {
- updateDisplay([]);
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
- if (options.onScroll) options.onScroll(instance);
- });
- connect(window, "resize", function() {updateDisplay(true);});
- connect(input, "keyup", operation(onKeyUp));
- connect(input, "input", fastPoll);
- connect(input, "keydown", operation(onKeyDown));
- connect(input, "keypress", operation(onKeyPress));
- connect(input, "focus", onFocus);
- connect(input, "blur", onBlur);
-
- connect(scroller, "dragenter", e_stop);
- connect(scroller, "dragover", e_stop);
- connect(scroller, "drop", operation(onDrop));
- connect(scroller, "paste", function(){focusInput(); fastPoll();});
- connect(input, "paste", fastPoll);
- connect(input, "cut", operation(function(){replaceSelection("");}));
-
- // IE throws unspecified error in certain cases, when
- // trying to access activeElement before onload
- var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
- if (hasFocus) setTimeout(onFocus, 20);
- else onBlur();
-
- function isLine(l) {return l >= 0 && l < doc.size;}
- // The instance object that we'll return. Mostly calls out to
- // local functions in the CodeMirror function. Some do some extra
- // range checking and/or clipping. operation is used to wrap the
- // call so that changes it makes are tracked, and the display is
- // updated afterwards.
- var instance = wrapper.CodeMirror = {
- getValue: getValue,
- setValue: operation(setValue),
- getSelection: getSelection,
- replaceSelection: operation(replaceSelection),
- focus: function(){focusInput(); onFocus(); fastPoll();},
- setOption: function(option, value) {
- var oldVal = options[option];
- options[option] = value;
- if (option == "mode" || option == "indentUnit") loadMode();
- else if (option == "readOnly" && value) {onBlur(); input.blur();}
- else if (option == "theme") themeChanged();
- else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
- else if (option == "tabSize") operation(tabsChanged)();
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
- operation(gutterChanged)();
- },
- getOption: function(option) {return options[option];},
- undo: operation(undo),
- redo: operation(redo),
- indentLine: operation(function(n, dir) {
- if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
- }),
- indentSelection: operation(indentSelected),
- historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
- clearHistory: function() {history = new History();},
- matchBrackets: operation(function(){matchBrackets(true);}),
- getTokenAt: operation(function(pos) {
- pos = clipPos(pos);
- return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
- }),
- getStateAfter: function(line) {
- line = clipLine(line == null ? doc.size - 1: line);
- return getStateBefore(line + 1);
- },
- cursorCoords: function(start){
- if (start == null) start = sel.inverted;
- return pageCoords(start ? sel.from : sel.to);
- },
- charCoords: function(pos){return pageCoords(clipPos(pos));},
- coordsChar: function(coords) {
- var off = eltOffset(lineSpace);
- return coordsChar(coords.x - off.left, coords.y - off.top);
- },
- markText: operation(markText),
- setBookmark: setBookmark,
- setMarker: operation(addGutterMarker),
- clearMarker: operation(removeGutterMarker),
- setLineClass: operation(setLineClass),
- hideLine: operation(function(h) {return setLineHidden(h, true);}),
- showLine: operation(function(h) {return setLineHidden(h, false);}),
- onDeleteLine: function(line, f) {
- if (typeof line == "number") {
- if (!isLine(line)) return null;
- line = getLine(line);
- }
- (line.handlers || (line.handlers = [])).push(f);
- return line;
- },
- lineInfo: lineInfo,
- addWidget: function(pos, node, scroll, vert, horiz) {
- pos = localCoords(clipPos(pos));
- var top = pos.yBot, left = pos.x;
- node.style.position = "absolute";
- code.appendChild(node);
- if (vert == "over") top = pos.y;
- else if (vert == "near") {
- var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
- hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
- if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
- top = pos.y - node.offsetHeight;
- if (left + node.offsetWidth > hspace)
- left = hspace - node.offsetWidth;
- }
- node.style.top = (top + paddingTop()) + "px";
- node.style.left = node.style.right = "";
- if (horiz == "right") {
- left = code.clientWidth - node.offsetWidth;
- node.style.right = "0px";
- } else {
- if (horiz == "left") left = 0;
- else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
- node.style.left = (left + paddingLeft()) + "px";
- }
- if (scroll)
- scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
- },
-
- lineCount: function() {return doc.size;},
- clipPos: clipPos,
- getCursor: function(start) {
- if (start == null) start = sel.inverted;
- return copyPos(start ? sel.from : sel.to);
- },
- somethingSelected: function() {return !posEq(sel.from, sel.to);},
- setCursor: operation(function(line, ch, user) {
- if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
- else setCursor(line, ch, user);
- }),
- setSelection: operation(function(from, to, user) {
- (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
- }),
- getLine: function(line) {if (isLine(line)) return getLine(line).text;},
- getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
- setLine: operation(function(line, text) {
- if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
- }),
- removeLine: operation(function(line) {
- if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
- }),
- replaceRange: operation(replaceRange),
- getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
-
- execCommand: function(cmd) {return commands[cmd](instance);},
- // Stuff used by commands, probably not much use to outside code.
- moveH: operation(moveH),
- deleteH: operation(deleteH),
- moveV: operation(moveV),
- toggleOverwrite: function() {overwrite = !overwrite;},
-
- posFromIndex: function(off) {
- var lineNo = 0, ch;
- doc.iter(0, doc.size, function(line) {
- var sz = line.text.length + 1;
- if (sz > off) { ch = off; return true; }
- off -= sz;
- ++lineNo;
- });
- return clipPos({line: lineNo, ch: ch});
- },
- indexFromPos: function (coords) {
- if (coords.line < 0 || coords.ch < 0) return 0;
- var index = coords.ch;
- doc.iter(0, coords.line, function (line) {
- index += line.text.length + 1;
- });
- return index;
- },
-
- operation: function(f){return operation(f)();},
- refresh: function(){updateDisplay(true);},
- getInputField: function(){return input;},
- getWrapperElement: function(){return wrapper;},
- getScrollerElement: function(){return scroller;},
- getGutterElement: function(){return gutter;}
- };
-
- function getLine(n) { return getLineAt(doc, n); }
- function updateLineHeight(line, height) {
- gutterDirty = true;
- var diff = height - line.height;
- for (var n = line; n; n = n.parent) n.height += diff;
- }
-
- function setValue(code) {
- var top = {line: 0, ch: 0};
- updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
- splitLines(code), top, top);
- updateInput = true;
- }
- function getValue(code) {
- var text = [];
- doc.iter(0, doc.size, function(line) { text.push(line.text); });
- return text.join("\n");
- }
-
- function onMouseDown(e) {
- setShift(e.shiftKey);
- // Check whether this is a click in a widget
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
- if (n.parentNode == code && n != mover) return;
-
- // See if this is a click in the gutter
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
- if (n.parentNode == gutterText) {
- if (options.onGutterClick)
- options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
- return e_preventDefault(e);
- }
-
- var start = posFromMouse(e);
-
- switch (e_button(e)) {
- case 3:
- if (gecko && !mac) onContextMenu(e);
- return;
- case 2:
- if (start) setCursor(start.line, start.ch, true);
- return;
- }
- // For button 1, if it was clicked inside the editor
- // (posFromMouse returning non-null), we have to adjust the
- // selection.
- if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
-
- if (!focused) onFocus();
-
- var now = +new Date;
- if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
- e_preventDefault(e);
- setTimeout(focusInput, 20);
- return selectLine(start.line);
- } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
- lastDoubleClick = {time: now, pos: start};
- e_preventDefault(e);
- return selectWordAt(start);
- } else { lastClick = {time: now, pos: start}; }
-
- var last = start, going;
- if (dragAndDrop && !posEq(sel.from, sel.to) &&
- !posLess(start, sel.from) && !posLess(sel.to, start)) {
- // Let the drag handler handle this.
- if (webkit) lineSpace.draggable = true;
- var up = connect(targetDocument, "mouseup", operation(function(e2) {
- if (webkit) lineSpace.draggable = false;
- draggingText = false;
- up();
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
- e_preventDefault(e2);
- setCursor(start.line, start.ch, true);
- focusInput();
- }
- }), true);
- draggingText = true;
- return;
- }
- e_preventDefault(e);
- setCursor(start.line, start.ch, true);
-
- function extend(e) {
- var cur = posFromMouse(e, true);
- if (cur && !posEq(cur, last)) {
- if (!focused) onFocus();
- last = cur;
- setSelectionUser(start, cur);
- updateInput = false;
- var visible = visibleLines();
- if (cur.line >= visible.to || cur.line < visible.from)
- going = setTimeout(operation(function(){extend(e);}), 150);
- }
- }
-
- var move = connect(targetDocument, "mousemove", operation(function(e) {
- clearTimeout(going);
- e_preventDefault(e);
- extend(e);
- }), true);
- var up = connect(targetDocument, "mouseup", operation(function(e) {
- clearTimeout(going);
- var cur = posFromMouse(e);
- if (cur) setSelectionUser(start, cur);
- e_preventDefault(e);
- focusInput();
- updateInput = true;
- move(); up();
- }), true);
- }
- function onDoubleClick(e) {
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
- if (n.parentNode == gutterText) return e_preventDefault(e);
- var start = posFromMouse(e);
- if (!start) return;
- lastDoubleClick = {time: +new Date, pos: start};
- e_preventDefault(e);
- selectWordAt(start);
- }
- function onDrop(e) {
- e.preventDefault();
- var pos = posFromMouse(e, true), files = e.dataTransfer.files;
- if (!pos || options.readOnly) return;
- if (files && files.length && window.FileReader && window.File) {
- function loadFile(file, i) {
- var reader = new FileReader;
- reader.onload = function() {
- text[i] = reader.result;
- if (++read == n) {
- pos = clipPos(pos);
- operation(function() {
- var end = replaceRange(text.join(""), pos, pos);
- setSelectionUser(pos, end);
- })();
- }
- };
- reader.readAsText(file);
- }
- var n = files.length, text = Array(n), read = 0;
- for (var i = 0; i < n; ++i) loadFile(files[i], i);
- }
- else {
- try {
- var text = e.dataTransfer.getData("Text");
- if (text) {
- var end = replaceRange(text, pos, pos);
- var curFrom = sel.from, curTo = sel.to;
- setSelectionUser(pos, end);
- if (draggingText) replaceRange("", curFrom, curTo);
- focusInput();
- }
- }
- catch(e){}
- }
- }
- function onDragStart(e) {
- var txt = getSelection();
- // This will reset escapeElement
- htmlEscape(txt);
- e.dataTransfer.setDragImage(escapeElement, 0, 0);
- e.dataTransfer.setData("Text", txt);
- }
- function handleKeyBinding(e) {
- var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
- if (name == null || e.altGraphKey) {
- if (next) options.keyMap = next;
- return null;
- }
- if (e.altKey) name = "Alt-" + name;
- if (e.ctrlKey) name = "Ctrl-" + name;
- if (e.metaKey) name = "Cmd-" + name;
- if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
- dropShift = true;
- } else {
- bound = lookupKey(name, options.extraKeys, options.keyMap);
- }
- if (typeof bound == "string") {
- if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
- else bound = null;
- }
- if (next && (bound || !isModifierKey(e))) options.keyMap = next;
- if (!bound) return false;
- if (dropShift) {
- var prevShift = shiftSelecting;
- shiftSelecting = null;
- bound(instance);
- shiftSelecting = prevShift;
- } else bound(instance);
- e_preventDefault(e);
- return true;
- }
- var lastStoppedKey = null;
- function onKeyDown(e) {
- if (!focused) onFocus();
- var code = e.keyCode;
- // IE does strange things with escape.
- if (ie && code == 27) { e.returnValue = false; }
- setShift(code == 16 || e.shiftKey);
- // First give onKeyEvent option a chance to handle this.
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
- var handled = handleKeyBinding(e);
- if (window.opera) {
- lastStoppedKey = handled ? e.keyCode : null;
- // Opera has no cut event... we try to at least catch the key combo
- if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
- replaceSelection("");
- }
- }
- function onKeyPress(e) {
- if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
- if (window.opera && !e.which && handleKeyBinding(e)) return;
- if (options.electricChars && mode.electricChars) {
- var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
- if (mode.electricChars.indexOf(ch) > -1)
- setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
- }
- fastPoll();
- }
- function onKeyUp(e) {
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
- if (e.keyCode == 16) shiftSelecting = null;
- }
-
- function onFocus() {
- if (options.readOnly) return;
- if (!focused) {
- if (options.onFocus) options.onFocus(instance);
- focused = true;
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
- wrapper.className += " CodeMirror-focused";
- if (!leaveInputAlone) resetInput(true);
- }
- slowPoll();
- restartBlink();
- }
- function onBlur() {
- if (focused) {
- if (options.onBlur) options.onBlur(instance);
- focused = false;
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
- }
- clearInterval(blinker);
- setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
- }
-
- // Replace the range from from to to by the strings in newText.
- // Afterwards, set the selection to selFrom, selTo.
- function updateLines(from, to, newText, selFrom, selTo) {
- if (history) {
- var old = [];
- doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
- history.addChange(from.line, newText.length, old);
- while (history.done.length > options.undoDepth) history.done.shift();
- }
- updateLinesNoUndo(from, to, newText, selFrom, selTo);
- }
- function unredoHelper(from, to) {
- var change = from.pop();
- if (change) {
- var replaced = [], end = change.start + change.added;
- doc.iter(change.start, end, function(line) { replaced.push(line.text); });
- to.push({start: change.start, added: change.old.length, old: replaced});
- var pos = clipPos({line: change.start + change.old.length - 1,
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
- updateInput = true;
- }
- }
- function undo() {unredoHelper(history.done, history.undone);}
- function redo() {unredoHelper(history.undone, history.done);}
-
- function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
- var recomputeMaxLength = false, maxLineLength = maxLine.length;
- if (!options.lineWrapping)
- doc.iter(from.line, to.line, function(line) {
- if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
- });
- if (from.line != to.line || newText.length > 1) gutterDirty = true;
-
- var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
- // First adjust the line structure, taking some care to leave highlighting intact.
- if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
- // This is a whole-line replace. Treated specially to make
- // sure line objects move the way they are supposed to.
- var added = [], prevLine = null;
- if (from.line) {
- prevLine = getLine(from.line - 1);
- prevLine.fixMarkEnds(lastLine);
- } else lastLine.fixMarkStarts();
- for (var i = 0, e = newText.length - 1; i < e; ++i)
- added.push(Line.inheritMarks(newText[i], prevLine));
- if (nlines) doc.remove(from.line, nlines, callbacks);
- if (added.length) doc.insert(from.line, added);
- } else if (firstLine == lastLine) {
- if (newText.length == 1)
- firstLine.replace(from.ch, to.ch, newText[0]);
- else {
- lastLine = firstLine.split(to.ch, newText[newText.length-1]);
- firstLine.replace(from.ch, null, newText[0]);
- firstLine.fixMarkEnds(lastLine);
- var added = [];
- for (var i = 1, e = newText.length - 1; i < e; ++i)
- added.push(Line.inheritMarks(newText[i], firstLine));
- added.push(lastLine);
- doc.insert(from.line + 1, added);
- }
- } else if (newText.length == 1) {
- firstLine.replace(from.ch, null, newText[0]);
- lastLine.replace(null, to.ch, "");
- firstLine.append(lastLine);
- doc.remove(from.line + 1, nlines, callbacks);
- } else {
- var added = [];
- firstLine.replace(from.ch, null, newText[0]);
- lastLine.replace(null, to.ch, newText[newText.length-1]);
- firstLine.fixMarkEnds(lastLine);
- for (var i = 1, e = newText.length - 1; i < e; ++i)
- added.push(Line.inheritMarks(newText[i], firstLine));
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
- doc.insert(from.line + 1, added);
- }
- if (options.lineWrapping) {
- var perLine = scroller.clientWidth / charWidth() - 3;
- doc.iter(from.line, from.line + newText.length, function(line) {
- if (line.hidden) return;
- var guess = Math.ceil(line.text.length / perLine) || 1;
- if (guess != line.height) updateLineHeight(line, guess);
- });
- } else {
- doc.iter(from.line, i + newText.length, function(line) {
- var l = line.text;
- if (l.length > maxLineLength) {
- maxLine = l; maxLineLength = l.length; maxWidth = null;
- recomputeMaxLength = false;
- }
- });
- if (recomputeMaxLength) {
- maxLineLength = 0; maxLine = ""; maxWidth = null;
- doc.iter(0, doc.size, function(line) {
- var l = line.text;
- if (l.length > maxLineLength) {
- maxLineLength = l.length; maxLine = l;
- }
- });
- }
- }
-
- // Add these lines to the work array, so that they will be
- // highlighted. Adjust work lines if lines were added/removed.
- var newWork = [], lendiff = newText.length - nlines - 1;
- for (var i = 0, l = work.length; i < l; ++i) {
- var task = work[i];
- if (task < from.line) newWork.push(task);
- else if (task > to.line) newWork.push(task + lendiff);
- }
- var hlEnd = from.line + Math.min(newText.length, 500);
- highlightLines(from.line, hlEnd);
- newWork.push(hlEnd);
- work = newWork;
- startWorker(100);
- // Remember that these lines changed, for updating the display
- changes.push({from: from.line, to: to.line + 1, diff: lendiff});
- var changeObj = {from: from, to: to, text: newText};
- if (textChanged) {
- for (var cur = textChanged; cur.next; cur = cur.next) {}
- cur.next = changeObj;
- } else textChanged = changeObj;
-
- // Update the selection
- function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
-
- // Make sure the scroll-size div has the correct height.
- code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
- }
-
- function replaceRange(code, from, to) {
- from = clipPos(from);
- if (!to) to = from; else to = clipPos(to);
- code = splitLines(code);
- function adjustPos(pos) {
- if (posLess(pos, from)) return pos;
- if (!posLess(to, pos)) return end;
- var line = pos.line + code.length - (to.line - from.line) - 1;
- var ch = pos.ch;
- if (pos.line == to.line)
- ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
- return {line: line, ch: ch};
- }
- var end;
- replaceRange1(code, from, to, function(end1) {
- end = end1;
- return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
- });
- return end;
- }
- function replaceSelection(code, collapse) {
- replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
- if (collapse == "end") return {from: end, to: end};
- else if (collapse == "start") return {from: sel.from, to: sel.from};
- else return {from: sel.from, to: end};
- });
- }
- function replaceRange1(code, from, to, computeSel) {
- var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
- var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
- updateLines(from, to, code, newSel.from, newSel.to);
- }
-
- function getRange(from, to) {
- var l1 = from.line, l2 = to.line;
- if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
- var code = [getLine(l1).text.slice(from.ch)];
- doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
- code.push(getLine(l2).text.slice(0, to.ch));
- return code.join("\n");
- }
- function getSelection() {
- return getRange(sel.from, sel.to);
- }
-
- var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
- function slowPoll() {
- if (pollingFast) return;
- poll.set(options.pollInterval, function() {
- startOperation();
- readInput();
- if (focused) slowPoll();
- endOperation();
- });
- }
- function fastPoll() {
- var missed = false;
- pollingFast = true;
- function p() {
- startOperation();
- var changed = readInput();
- if (!changed && !missed) {missed = true; poll.set(60, p);}
- else {pollingFast = false; slowPoll();}
- endOperation();
- }
- poll.set(20, p);
- }
-
- // Previnput is a hack to work with IME. If we reset the textarea
- // on every change, that breaks IME. So we look for changes
- // compared to the previous content instead. (Modern browsers have
- // events that indicate IME taking place, but these are not widely
- // supported or compatible enough yet to rely on.)
- var prevInput = "";
- function readInput() {
- if (leaveInputAlone || !focused || hasSelection(input)) return false;
- var text = input.value;
- if (text == prevInput) return false;
- shiftSelecting = null;
- var same = 0, l = Math.min(prevInput.length, text.length);
- while (same < l && prevInput[same] == text[same]) ++same;
- if (same < prevInput.length)
- sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
- else if (overwrite && posEq(sel.from, sel.to))
- sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
- replaceSelection(text.slice(same), "end");
- prevInput = text;
- return true;
- }
- function resetInput(user) {
- if (!posEq(sel.from, sel.to)) {
- prevInput = "";
- input.value = getSelection();
- input.select();
- } else if (user) prevInput = input.value = "";
- }
-
- function focusInput() {
- if (!options.readOnly) input.focus();
- }
-
- function scrollEditorIntoView() {
- if (!cursor.getBoundingClientRect) return;
- var rect = cursor.getBoundingClientRect();
- // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
- if (ie && rect.top == rect.bottom) return;
- var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
- }
- function scrollCursorIntoView() {
- var cursor = localCoords(sel.inverted ? sel.from : sel.to);
- var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
- return scrollIntoView(x, cursor.y, x, cursor.yBot);
- }
- function scrollIntoView(x1, y1, x2, y2) {
- var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
- y1 += pt; y2 += pt; x1 += pl; x2 += pl;
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
-
- var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
- var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
- if (x1 < screenleft + gutterw) {
- if (x1 < 50) x1 = 0;
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
- scrolled = true;
- }
- else if (x2 > screenw + screenleft - 3) {
- scroller.scrollLeft = x2 + 10 - screenw;
- scrolled = true;
- if (x2 > code.clientWidth) result = false;
- }
- if (scrolled && options.onScroll) options.onScroll(instance);
- return result;
- }
-
- function visibleLines() {
- var lh = textHeight(), top = scroller.scrollTop - paddingTop();
- var from_height = Math.max(0, Math.floor(top / lh));
- var to_height = Math.ceil((top + scroller.clientHeight) / lh);
- return {from: lineAtHeight(doc, from_height),
- to: lineAtHeight(doc, to_height)};
- }
- // Uses a set of changes plus the current scroll position to
- // determine which DOM updates have to be made, and makes the
- // updates.
- function updateDisplay(changes, suppressCallback) {
- if (!scroller.clientWidth) {
- showingFrom = showingTo = displayOffset = 0;
- return;
- }
- // Compute the new visible window
- var visible = visibleLines();
- // Bail out if the visible area is already rendered and nothing changed.
- if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
- var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
- if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
- if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
-
- // Create a range of theoretically intact lines, and punch holes
- // in that using the change info.
- var intact = changes === true ? [] :
- computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
- // Clip off the parts that won't be visible
- var intactLines = 0;
- for (var i = 0; i < intact.length; ++i) {
- var range = intact[i];
- if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
- if (range.to > to) range.to = to;
- if (range.from >= range.to) intact.splice(i--, 1);
- else intactLines += range.to - range.from;
- }
- if (intactLines == to - from) return;
- intact.sort(function(a, b) {return a.domStart - b.domStart;});
-
- var th = textHeight(), gutterDisplay = gutter.style.display;
- lineDiv.style.display = gutter.style.display = "none";
- patchDisplay(from, to, intact);
- lineDiv.style.display = "";
-
- // Position the mover div to align with the lines it's supposed
- // to be showing (which will cover the visible display)
- var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
- // This is just a bogus formula that detects when the editor is
- // resized or the font size changes.
- if (different) lastSizeC = scroller.clientHeight + th;
- showingFrom = from; showingTo = to;
- displayOffset = heightAtLine(doc, from);
- mover.style.top = (displayOffset * th) + "px";
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
-
- // Since this is all rather error prone, it is honoured with the
- // only assertion in the whole file.
- if (lineDiv.childNodes.length != showingTo - showingFrom)
- throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
- " nodes=" + lineDiv.childNodes.length);
-
- if (options.lineWrapping) {
- maxWidth = scroller.clientWidth;
- var curNode = lineDiv.firstChild;
- doc.iter(showingFrom, showingTo, function(line) {
- if (!line.hidden) {
- var height = Math.round(curNode.offsetHeight / th) || 1;
- if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
- }
- curNode = curNode.nextSibling;
- });
- } else {
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
- if (maxWidth > scroller.clientWidth) {
- lineSpace.style.width = maxWidth + "px";
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
- code.style.width = "";
- code.style.width = scroller.scrollWidth + "px";
- } else {
- lineSpace.style.width = code.style.width = "";
- }
- }
- gutter.style.display = gutterDisplay;
- if (different || gutterDirty) updateGutter();
- updateCursor();
- if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
- return true;
- }
-
- function computeIntact(intact, changes) {
- for (var i = 0, l = changes.length || 0; i < l; ++i) {
- var change = changes[i], intact2 = [], diff = change.diff || 0;
- for (var j = 0, l2 = intact.length; j < l2; ++j) {
- var range = intact[j];
- if (change.to <= range.from && change.diff)
- intact2.push({from: range.from + diff, to: range.to + diff,
- domStart: range.domStart});
- else if (change.to <= range.from || change.from >= range.to)
- intact2.push(range);
- else {
- if (change.from > range.from)
- intact2.push({from: range.from, to: change.from, domStart: range.domStart});
- if (change.to < range.to)
- intact2.push({from: change.to + diff, to: range.to + diff,
- domStart: range.domStart + (change.to - range.from)});
- }
- }
- intact = intact2;
- }
- return intact;
- }
-
- function patchDisplay(from, to, intact) {
- // The first pass removes the DOM nodes that aren't intact.
- if (!intact.length) lineDiv.innerHTML = "";
- else {
- function killNode(node) {
- var tmp = node.nextSibling;
- node.parentNode.removeChild(node);
- return tmp;
- }
- var domPos = 0, curNode = lineDiv.firstChild, n;
- for (var i = 0; i < intact.length; ++i) {
- var cur = intact[i];
- while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
- for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
- }
- while (curNode) curNode = killNode(curNode);
- }
- // This pass fills in the lines that actually changed.
- var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
- var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
- var scratch = targetDocument.createElement("div"), newElt;
- doc.iter(from, to, function(line) {
- var ch1 = null, ch2 = null;
- if (inSel) {
- ch1 = 0;
- if (sto == j) {inSel = false; ch2 = sel.to.ch;}
- } else if (sfrom == j) {
- if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
- else {inSel = true; ch1 = sel.from.ch;}
- }
- if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
- if (!nextIntact || nextIntact.from > j) {
- if (line.hidden) scratch.innerHTML = "
";
- else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
- lineDiv.insertBefore(scratch.firstChild, curNode);
- } else {
- curNode = curNode.nextSibling;
- }
- ++j;
- });
- }
-
- function updateGutter() {
- if (!options.gutter && !options.lineNumbers) return;
- var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
- gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
- var html = [], i = showingFrom;
- doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
- if (line.hidden) {
- html.push("
");
- } else {
- var marker = line.gutterMarker;
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
- if (marker && marker.text)
- text = marker.text.replace("%N%", text != null ? text : "");
- else if (text == null)
- text = "\u00a0";
- html.push((marker && marker.style ? '
' : ""), text);
- for (var j = 1; j < line.height; ++j) html.push("
");
- html.push("
");
- }
- ++i;
- });
- gutter.style.display = "none";
- gutterText.innerHTML = html.join("");
- var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
- while (val.length + pad.length < minwidth) pad += "\u00a0";
- if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
- gutter.style.display = "";
- lineSpace.style.marginLeft = gutter.offsetWidth + "px";
- gutterDirty = false;
- }
- function updateCursor() {
- var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
- var pos = localCoords(head, true);
- var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
- inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
- inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
- if (posEq(sel.from, sel.to)) {
- cursor.style.top = pos.y + "px";
- cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
- cursor.style.display = "";
- }
- else cursor.style.display = "none";
- }
-
- function setShift(val) {
- if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
- else shiftSelecting = null;
- }
- function setSelectionUser(from, to) {
- var sh = shiftSelecting && clipPos(shiftSelecting);
- if (sh) {
- if (posLess(sh, from)) from = sh;
- else if (posLess(to, sh)) to = sh;
- }
- setSelection(from, to);
- userSelChange = true;
- }
- // Update the selection. Last two args are only used by
- // updateLines, since they have to be expressed in the line
- // numbers before the update.
- function setSelection(from, to, oldFrom, oldTo) {
- goalColumn = null;
- if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
- if (posEq(sel.from, from) && posEq(sel.to, to)) return;
- if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
-
- // Skip over hidden lines.
- if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
- if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
-
- if (posEq(from, to)) sel.inverted = false;
- else if (posEq(from, sel.to)) sel.inverted = false;
- else if (posEq(to, sel.from)) sel.inverted = true;
-
- // Some ugly logic used to only mark the lines that actually did
- // see a change in selection as changed, rather than the whole
- // selected range.
- if (posEq(from, to)) {
- if (!posEq(sel.from, sel.to))
- changes.push({from: oldFrom, to: oldTo + 1});
- }
- else if (posEq(sel.from, sel.to)) {
- changes.push({from: from.line, to: to.line + 1});
- }
- else {
- if (!posEq(from, sel.from)) {
- if (from.line < oldFrom)
- changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
- else
- changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
- }
- if (!posEq(to, sel.to)) {
- if (to.line < oldTo)
- changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
- else
- changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
- }
- }
- sel.from = from; sel.to = to;
- selectionChanged = true;
- }
- function skipHidden(pos, oldLine, oldCh) {
- function getNonHidden(dir) {
- var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
- while (lNo != end) {
- var line = getLine(lNo);
- if (!line.hidden) {
- var ch = pos.ch;
- if (ch > oldCh || ch > line.text.length) ch = line.text.length;
- return {line: lNo, ch: ch};
- }
- lNo += dir;
- }
- }
- var line = getLine(pos.line);
- if (!line.hidden) return pos;
- if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
- else return getNonHidden(-1) || getNonHidden(1);
- }
- function setCursor(line, ch, user) {
- var pos = clipPos({line: line, ch: ch || 0});
- (user ? setSelectionUser : setSelection)(pos, pos);
- }
-
- function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
- function clipPos(pos) {
- if (pos.line < 0) return {line: 0, ch: 0};
- if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
- var ch = pos.ch, linelen = getLine(pos.line).text.length;
- if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
- else if (ch < 0) return {line: pos.line, ch: 0};
- else return pos;
- }
-
- function findPosH(dir, unit) {
- var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
- var lineObj = getLine(line);
- function findNextLine() {
- for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
- var lo = getLine(l);
- if (!lo.hidden) { line = l; lineObj = lo; return true; }
- }
- }
- function moveOnce(boundToLine) {
- if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
- if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
- else return false;
- } else ch += dir;
- return true;
- }
- if (unit == "char") moveOnce();
- else if (unit == "column") moveOnce(true);
- else if (unit == "word") {
- var sawWord = false;
- for (;;) {
- if (dir < 0) if (!moveOnce()) break;
- if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
- else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
- if (dir > 0) if (!moveOnce()) break;
- }
- }
- return {line: line, ch: ch};
- }
- function moveH(dir, unit) {
- var pos = dir < 0 ? sel.from : sel.to;
- if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
- setCursor(pos.line, pos.ch, true);
- }
- function deleteH(dir, unit) {
- if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
- else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
- else replaceRange("", sel.from, findPosH(dir, unit));
- userSelChange = true;
- }
- var goalColumn = null;
- function moveV(dir, unit) {
- var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
- if (goalColumn != null) pos.x = goalColumn;
- if (unit == "page") dist = scroller.clientHeight;
- else if (unit == "line") dist = textHeight();
- var target = coordsChar(pos.x, pos.y + dist * dir + 2);
- setCursor(target.line, target.ch, true);
- goalColumn = pos.x;
- }
-
- function selectWordAt(pos) {
- var line = getLine(pos.line).text;
- var start = pos.ch, end = pos.ch;
- while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
- while (end < line.length && isWordChar(line.charAt(end))) ++end;
- setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
- }
- function selectLine(line) {
- setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
- }
- function indentSelected(mode) {
- if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
- var e = sel.to.line - (sel.to.ch ? 0 : 1);
- for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
- }
-
- function indentLine(n, how) {
- if (!how) how = "add";
- if (how == "smart") {
- if (!mode.indent) how = "prev";
- else var state = getStateBefore(n);
- }
-
- var line = getLine(n), curSpace = line.indentation(options.tabSize),
- curSpaceString = line.text.match(/^\s*/)[0], indentation;
- if (how == "prev") {
- if (n) indentation = getLine(n-1).indentation(options.tabSize);
- else indentation = 0;
- }
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
- else if (how == "add") indentation = curSpace + options.indentUnit;
- else if (how == "subtract") indentation = curSpace - options.indentUnit;
- indentation = Math.max(0, indentation);
- var diff = indentation - curSpace;
-
- if (!diff) {
- if (sel.from.line != n && sel.to.line != n) return;
- var indentString = curSpaceString;
- }
- else {
- var indentString = "", pos = 0;
- if (options.indentWithTabs)
- for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
- while (pos < indentation) {++pos; indentString += " ";}
- }
-
- replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
- }
-
- function loadMode() {
- mode = CodeMirror.getMode(options, options.mode);
- doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
- work = [0];
- startWorker();
- }
- function gutterChanged() {
- var visible = options.gutter || options.lineNumbers;
- gutter.style.display = visible ? "" : "none";
- if (visible) gutterDirty = true;
- else lineDiv.parentNode.style.marginLeft = 0;
- }
- function wrappingChanged(from, to) {
- if (options.lineWrapping) {
- wrapper.className += " CodeMirror-wrap";
- var perLine = scroller.clientWidth / charWidth() - 3;
- doc.iter(0, doc.size, function(line) {
- if (line.hidden) return;
- var guess = Math.ceil(line.text.length / perLine) || 1;
- if (guess != 1) updateLineHeight(line, guess);
- });
- lineSpace.style.width = code.style.width = "";
- } else {
- wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
- maxWidth = null; maxLine = "";
- doc.iter(0, doc.size, function(line) {
- if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
- if (line.text.length > maxLine.length) maxLine = line.text;
- });
- }
- changes.push({from: 0, to: doc.size});
- }
- function computeTabText() {
- for (var str = '', i = 0; i < options.tabSize; ++i) str += " ";
- return str + "";
- }
- function tabsChanged() {
- tabText = computeTabText();
- updateDisplay(true);
- }
- function themeChanged() {
- scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
- options.theme.replace(/(^|\s)\s*/g, " cm-s-");
- }
-
- function TextMarker() { this.set = []; }
- TextMarker.prototype.clear = operation(function() {
- var min = Infinity, max = -Infinity;
- for (var i = 0, e = this.set.length; i < e; ++i) {
- var line = this.set[i], mk = line.marked;
- if (!mk || !line.parent) continue;
- var lineN = lineNo(line);
- min = Math.min(min, lineN); max = Math.max(max, lineN);
- for (var j = 0; j < mk.length; ++j)
- if (mk[j].set == this.set) mk.splice(j--, 1);
- }
- if (min != Infinity)
- changes.push({from: min, to: max + 1});
- });
- TextMarker.prototype.find = function() {
- var from, to;
- for (var i = 0, e = this.set.length; i < e; ++i) {
- var line = this.set[i], mk = line.marked;
- for (var j = 0; j < mk.length; ++j) {
- var mark = mk[j];
- if (mark.set == this.set) {
- if (mark.from != null || mark.to != null) {
- var found = lineNo(line);
- if (found != null) {
- if (mark.from != null) from = {line: found, ch: mark.from};
- if (mark.to != null) to = {line: found, ch: mark.to};
- }
- }
- }
- }
- }
- return {from: from, to: to};
- };
-
- function markText(from, to, className) {
- from = clipPos(from); to = clipPos(to);
- var tm = new TextMarker();
- function add(line, from, to, className) {
- getLine(line).addMark(new MarkedText(from, to, className, tm.set));
- }
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
- else {
- add(from.line, from.ch, null, className);
- for (var i = from.line + 1, e = to.line; i < e; ++i)
- add(i, null, null, className);
- add(to.line, null, to.ch, className);
- }
- changes.push({from: from.line, to: to.line + 1});
- return tm;
- }
-
- function setBookmark(pos) {
- pos = clipPos(pos);
- var bm = new Bookmark(pos.ch);
- getLine(pos.line).addMark(bm);
- return bm;
- }
-
- function addGutterMarker(line, text, className) {
- if (typeof line == "number") line = getLine(clipLine(line));
- line.gutterMarker = {text: text, style: className};
- gutterDirty = true;
- return line;
- }
- function removeGutterMarker(line) {
- if (typeof line == "number") line = getLine(clipLine(line));
- line.gutterMarker = null;
- gutterDirty = true;
- }
-
- function changeLine(handle, op) {
- var no = handle, line = handle;
- if (typeof handle == "number") line = getLine(clipLine(handle));
- else no = lineNo(handle);
- if (no == null) return null;
- if (op(line, no)) changes.push({from: no, to: no + 1});
- else return null;
- return line;
- }
- function setLineClass(handle, className) {
- return changeLine(handle, function(line) {
- if (line.className != className) {
- line.className = className;
- return true;
- }
- });
- }
- function setLineHidden(handle, hidden) {
- return changeLine(handle, function(line, no) {
- if (line.hidden != hidden) {
- line.hidden = hidden;
- updateLineHeight(line, hidden ? 0 : 1);
- if (hidden && (sel.from.line == no || sel.to.line == no))
- setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
- skipHidden(sel.to, sel.to.line, sel.to.ch));
- return (gutterDirty = true);
- }
- });
- }
-
- function lineInfo(line) {
- if (typeof line == "number") {
- if (!isLine(line)) return null;
- var n = line;
- line = getLine(line);
- if (!line) return null;
- }
- else {
- var n = lineNo(line);
- if (n == null) return null;
- }
- var marker = line.gutterMarker;
- return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
- markerClass: marker && marker.style, lineClass: line.className};
- }
-
- function stringWidth(str) {
- measure.innerHTML = "x
";
- measure.firstChild.firstChild.firstChild.nodeValue = str;
- return measure.firstChild.firstChild.offsetWidth || 10;
- }
- // These are used to go from pixel positions to character
- // positions, taking varying character widths into account.
- function charFromX(line, x) {
- if (x <= 0) return 0;
- var lineObj = getLine(line), text = lineObj.text;
- function getX(len) {
- measure.innerHTML = "" + lineObj.getHTML(null, null, false, tabText, len) + "
";
- return measure.firstChild.firstChild.offsetWidth;
- }
- var from = 0, fromX = 0, to = text.length, toX;
- // Guess a suitable upper bound for our search.
- var estimated = Math.min(to, Math.ceil(x / charWidth()));
- for (;;) {
- var estX = getX(estimated);
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
- else {toX = estX; to = estimated; break;}
- }
- if (x > toX) return to;
- // Try to guess a suitable lower bound as well.
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
- if (estX < x) {from = estimated; fromX = estX;}
- // Do a binary search between these bounds.
- for (;;) {
- if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
- if (middleX > x) {to = middle; toX = middleX;}
- else {from = middle; fromX = middleX;}
- }
- }
-
- var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
- function measureLine(line, ch) {
- var extra = "";
- // Include extra text at the end to make sure the measured line is wrapped in the right way.
- if (options.lineWrapping) {
- var end = line.text.indexOf(" ", ch + 2);
- extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
- }
- measure.innerHTML = "" + line.getHTML(null, null, false, tabText, ch) +
- '' + htmlEscape(line.text.charAt(ch) || " ") + "" +
- extra + "
";
- var elt = document.getElementById("CodeMirror-temp-" + tempId);
- var top = elt.offsetTop, left = elt.offsetLeft;
- // Older IEs report zero offsets for spans directly after a wrap
- if (ie && ch && top == 0 && left == 0) {
- var backup = document.createElement("span");
- backup.innerHTML = "x";
- elt.parentNode.insertBefore(backup, elt.nextSibling);
- top = backup.offsetTop;
- }
- return {top: top, left: left};
- }
- function localCoords(pos, inLineWrap) {
- var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
- if (pos.ch == 0) x = 0;
- else {
- var sp = measureLine(getLine(pos.line), pos.ch);
- x = sp.left;
- if (options.lineWrapping) y += Math.max(0, sp.top);
- }
- return {x: x, y: y, yBot: y + lh};
- }
- // Coords must be lineSpace-local
- function coordsChar(x, y) {
- if (y < 0) y = 0;
- var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
- var lineNo = lineAtHeight(doc, heightPos);
- if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
- var lineObj = getLine(lineNo), text = lineObj.text;
- var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
- if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
- function getX(len) {
- var sp = measureLine(lineObj, len);
- if (tw) {
- var off = Math.round(sp.top / th);
- return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
- }
- return sp.left;
- }
- var from = 0, fromX = 0, to = text.length, toX;
- // Guess a suitable upper bound for our search.
- var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
- for (;;) {
- var estX = getX(estimated);
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
- else {toX = estX; to = estimated; break;}
- }
- if (x > toX) return {line: lineNo, ch: to};
- // Try to guess a suitable lower bound as well.
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
- if (estX < x) {from = estimated; fromX = estX;}
- // Do a binary search between these bounds.
- for (;;) {
- if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
- if (middleX > x) {to = middle; toX = middleX;}
- else {from = middle; fromX = middleX;}
- }
- }
- function pageCoords(pos) {
- var local = localCoords(pos, true), off = eltOffset(lineSpace);
- return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
- }
-
- var cachedHeight, cachedHeightFor, measureText;
- function textHeight() {
- if (measureText == null) {
- measureText = "";
- for (var i = 0; i < 49; ++i) measureText += "x
";
- measureText += "x
";
- }
- var offsetHeight = lineDiv.clientHeight;
- if (offsetHeight == cachedHeightFor) return cachedHeight;
- cachedHeightFor = offsetHeight;
- measure.innerHTML = measureText;
- cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
- measure.innerHTML = "";
- return cachedHeight;
- }
- var cachedWidth, cachedWidthFor = 0;
- function charWidth() {
- if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
- cachedWidthFor = scroller.clientWidth;
- return (cachedWidth = stringWidth("x"));
- }
- function paddingTop() {return lineSpace.offsetTop;}
- function paddingLeft() {return lineSpace.offsetLeft;}
-
- function posFromMouse(e, liberal) {
- var offW = eltOffset(scroller, true), x, y;
- // Fails unpredictably on IE[67] when mouse is dragged around quickly.
- try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
- // This is a mess of a heuristic to try and determine whether a
- // scroll-bar was clicked or not, and to return null if one was
- // (and !liberal).
- if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
- return null;
- var offL = eltOffset(lineSpace, true);
- return coordsChar(x - offL.left, y - offL.top);
- }
- function onContextMenu(e) {
- var pos = posFromMouse(e);
- if (!pos || window.opera) return; // Opera is difficult.
- if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
- operation(setCursor)(pos.line, pos.ch);
-
- var oldCSS = input.style.cssText;
- inputDiv.style.position = "absolute";
- input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
- "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
- leaveInputAlone = true;
- var val = input.value = getSelection();
- focusInput();
- input.select();
- function rehide() {
- var newVal = splitLines(input.value).join("\n");
- if (newVal != val) operation(replaceSelection)(newVal, "end");
- inputDiv.style.position = "relative";
- input.style.cssText = oldCSS;
- leaveInputAlone = false;
- resetInput(true);
- slowPoll();
- }
-
- if (gecko) {
- e_stop(e);
- var mouseup = connect(window, "mouseup", function() {
- mouseup();
- setTimeout(rehide, 20);
- }, true);
- }
- else {
- setTimeout(rehide, 50);
- }
- }
-
- // Cursor-blinking
- function restartBlink() {
- clearInterval(blinker);
- var on = true;
- cursor.style.visibility = "";
- blinker = setInterval(function() {
- cursor.style.visibility = (on = !on) ? "" : "hidden";
- }, 650);
- }
-
- var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
- function matchBrackets(autoclear) {
- var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
- var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
- if (!match) return;
- var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
- for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
- if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
-
- var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
- function scan(line, from, to) {
- if (!line.text) return;
- var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
- for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
- var text = st[i];
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
- for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
- if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
- var match = matching[cur];
- if (match.charAt(1) == ">" == forward) stack.push(cur);
- else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
- else if (!stack.length) return {pos: pos, match: true};
- }
- }
- }
- }
- for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
- var line = getLine(i), first = i == head.line;
- var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
- if (found) break;
- }
- if (!found) found = {pos: null, match: false};
- var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
- var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
- two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
- var clear = operation(function(){one.clear(); two && two.clear();});
- if (autoclear) setTimeout(clear, 800);
- else bracketHighlighted = clear;
- }
-
- // Finds the line to start with when starting a parse. Tries to
- // find a line with a stateAfter, so that it can start with a
- // valid state. If that fails, it returns the line with the
- // smallest indentation, which tends to need the least context to
- // parse correctly.
- function findStartLine(n) {
- var minindent, minline;
- for (var search = n, lim = n - 40; search > lim; --search) {
- if (search == 0) return 0;
- var line = getLine(search-1);
- if (line.stateAfter) return search;
- var indented = line.indentation(options.tabSize);
- if (minline == null || minindent > indented) {
- minline = search - 1;
- minindent = indented;
- }
- }
- return minline;
- }
- function getStateBefore(n) {
- var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
- if (!state) state = startState(mode);
- else state = copyState(mode, state);
- doc.iter(start, n, function(line) {
- line.highlight(mode, state, options.tabSize);
- line.stateAfter = copyState(mode, state);
- });
- if (start < n) changes.push({from: start, to: n});
- if (n < doc.size && !getLine(n).stateAfter) work.push(n);
- return state;
- }
- function highlightLines(start, end) {
- var state = getStateBefore(start);
- doc.iter(start, end, function(line) {
- line.highlight(mode, state, options.tabSize);
- line.stateAfter = copyState(mode, state);
- });
- }
- function highlightWorker() {
- var end = +new Date + options.workTime;
- var foundWork = work.length;
- while (work.length) {
- if (!getLine(showingFrom).stateAfter) var task = showingFrom;
- else var task = work.pop();
- if (task >= doc.size) continue;
- var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
- if (state) state = copyState(mode, state);
- else state = startState(mode);
-
- var unchanged = 0, compare = mode.compareStates, realChange = false,
- i = start, bail = false;
- doc.iter(i, doc.size, function(line) {
- var hadState = line.stateAfter;
- if (+new Date > end) {
- work.push(i);
- startWorker(options.workDelay);
- if (realChange) changes.push({from: task, to: i + 1});
- return (bail = true);
- }
- var changed = line.highlight(mode, state, options.tabSize);
- if (changed) realChange = true;
- line.stateAfter = copyState(mode, state);
- if (compare) {
- if (hadState && compare(hadState, state)) return true;
- } else {
- if (changed !== false || !hadState) unchanged = 0;
- else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
- return true;
- }
- ++i;
- });
- if (bail) return;
- if (realChange) changes.push({from: task, to: i + 1});
- }
- if (foundWork && options.onHighlightComplete)
- options.onHighlightComplete(instance);
- }
- function startWorker(time) {
- if (!work.length) return;
- highlight.set(time, operation(highlightWorker));
- }
-
- // Operations are used to wrap changes in such a way that each
- // change won't have to update the cursor and display (which would
- // be awkward, slow, and error-prone), but instead updates are
- // batched and then all combined and executed at once.
- function startOperation() {
- updateInput = userSelChange = textChanged = null;
- changes = []; selectionChanged = false; callbacks = [];
- }
- function endOperation() {
- var reScroll = false, updated;
- if (selectionChanged) reScroll = !scrollCursorIntoView();
- if (changes.length) updated = updateDisplay(changes, true);
- else {
- if (selectionChanged) updateCursor();
- if (gutterDirty) updateGutter();
- }
- if (reScroll) scrollCursorIntoView();
- if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
-
- if (focused && !leaveInputAlone &&
- (updateInput === true || (updateInput !== false && selectionChanged)))
- resetInput(userSelChange);
-
- if (selectionChanged && options.matchBrackets)
- setTimeout(operation(function() {
- if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
- if (posEq(sel.from, sel.to)) matchBrackets(false);
- }), 20);
- var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
- if (selectionChanged && options.onCursorActivity)
- options.onCursorActivity(instance);
- if (tc && options.onChange && instance)
- options.onChange(instance, tc);
- for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
- if (updated && options.onUpdate) options.onUpdate(instance);
- }
- var nestedOperation = 0;
- function operation(f) {
- return function() {
- if (!nestedOperation++) startOperation();
- try {var result = f.apply(this, arguments);}
- finally {if (!--nestedOperation) endOperation();}
- return result;
- };
- }
-
- for (var ext in extensions)
- if (extensions.propertyIsEnumerable(ext) &&
- !instance.propertyIsEnumerable(ext))
- instance[ext] = extensions[ext];
- return instance;
- } // (end of function CodeMirror)
-
- // The default configuration options.
- CodeMirror.defaults = {
- value: "",
- mode: null,
- theme: "default",
- indentUnit: 2,
- indentWithTabs: false,
- tabSize: 4,
- keyMap: "default",
- extraKeys: null,
- electricChars: true,
- onKeyEvent: null,
- lineWrapping: false,
- lineNumbers: false,
- gutter: false,
- fixedGutter: false,
- firstLineNumber: 1,
- readOnly: false,
- onChange: null,
- onCursorActivity: null,
- onGutterClick: null,
- onHighlightComplete: null,
- onUpdate: null,
- onFocus: null, onBlur: null, onScroll: null,
- matchBrackets: false,
- workTime: 100,
- workDelay: 200,
- pollInterval: 100,
- undoDepth: 40,
- tabindex: null,
- document: window.document
- };
-
- var mac = /Mac/.test(navigator.platform);
- var win = /Win/.test(navigator.platform);
-
- // Known modes, by name and by MIME
- var modes = {}, mimeModes = {};
- CodeMirror.defineMode = function(name, mode) {
- if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
- modes[name] = mode;
- };
- CodeMirror.defineMIME = function(mime, spec) {
- mimeModes[mime] = spec;
- };
- CodeMirror.getMode = function(options, spec) {
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
- spec = mimeModes[spec];
- if (typeof spec == "string")
- var mname = spec, config = {};
- else if (spec != null)
- var mname = spec.name, config = spec;
- var mfactory = modes[mname];
- if (!mfactory) {
- if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
- return CodeMirror.getMode(options, "text/plain");
- }
- return mfactory(options, config || {});
- };
- CodeMirror.listModes = function() {
- var list = [];
- for (var m in modes)
- if (modes.propertyIsEnumerable(m)) list.push(m);
- return list;
- };
- CodeMirror.listMIMEs = function() {
- var list = [];
- for (var m in mimeModes)
- if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
- return list;
- };
-
- var extensions = CodeMirror.extensions = {};
- CodeMirror.defineExtension = function(name, func) {
- extensions[name] = func;
- };
-
- var commands = CodeMirror.commands = {
- selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
- killLine: function(cm) {
- var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
- if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
- else cm.replaceRange("", from, sel ? to : {line: from.line});
- },
- deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
- undo: function(cm) {cm.undo();},
- redo: function(cm) {cm.redo();},
- goDocStart: function(cm) {cm.setCursor(0, 0, true);},
- goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
- goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
- goLineStartSmart: function(cm) {
- var cur = cm.getCursor();
- var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
- cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
- },
- goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
- goLineUp: function(cm) {cm.moveV(-1, "line");},
- goLineDown: function(cm) {cm.moveV(1, "line");},
- goPageUp: function(cm) {cm.moveV(-1, "page");},
- goPageDown: function(cm) {cm.moveV(1, "page");},
- goCharLeft: function(cm) {cm.moveH(-1, "char");},
- goCharRight: function(cm) {cm.moveH(1, "char");},
- goColumnLeft: function(cm) {cm.moveH(-1, "column");},
- goColumnRight: function(cm) {cm.moveH(1, "column");},
- goWordLeft: function(cm) {cm.moveH(-1, "word");},
- goWordRight: function(cm) {cm.moveH(1, "word");},
- delCharLeft: function(cm) {cm.deleteH(-1, "char");},
- delCharRight: function(cm) {cm.deleteH(1, "char");},
- delWordLeft: function(cm) {cm.deleteH(-1, "word");},
- delWordRight: function(cm) {cm.deleteH(1, "word");},
- indentAuto: function(cm) {cm.indentSelection("smart");},
- indentMore: function(cm) {cm.indentSelection("add");},
- indentLess: function(cm) {cm.indentSelection("subtract");},
- insertTab: function(cm) {cm.replaceSelection("\t", "end");},
- transposeChars: function(cm) {
- var cur = cm.getCursor(), line = cm.getLine(cur.line);
- if (cur.ch > 0 && cur.ch < line.length - 1)
- cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
- {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
- },
- newlineAndIndent: function(cm) {
- cm.replaceSelection("\n", "end");
- cm.indentLine(cm.getCursor().line);
- },
- toggleOverwrite: function(cm) {cm.toggleOverwrite();}
- };
-
- var keyMap = CodeMirror.keyMap = {};
- keyMap.basic = {
- "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
- "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
- "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
- "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
- };
- // Note that the save and find-related commands aren't defined by
- // default. Unknown commands are simply ignored.
- keyMap.pcDefault = {
- "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
- "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
- "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
- "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
- "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
- fallthrough: "basic"
- };
- keyMap.macDefault = {
- "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
- "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
- "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
- fallthrough: ["basic", "emacsy"]
- };
- keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
- keyMap.emacsy = {
- "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
- "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
- "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
- "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
- };
-
- function lookupKey(name, extraMap, map) {
- function lookup(name, map, ft) {
- var found = map[name];
- if (found != null) return found;
- if (ft == null) ft = map.fallthrough;
- if (ft == null) return map.catchall;
- if (typeof ft == "string") return lookup(name, keyMap[ft]);
- for (var i = 0, e = ft.length; i < e; ++i) {
- found = lookup(name, keyMap[ft[i]]);
- if (found != null) return found;
- }
- return null;
- }
- return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
- }
- function isModifierKey(event) {
- var name = keyNames[event.keyCode];
- return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
- }
-
- CodeMirror.fromTextArea = function(textarea, options) {
- if (!options) options = {};
- options.value = textarea.value;
- if (!options.tabindex && textarea.tabindex)
- options.tabindex = textarea.tabindex;
-
- function save() {textarea.value = instance.getValue();}
- if (textarea.form) {
- // Deplorable hack to make the submit method do the right thing.
- var rmSubmit = connect(textarea.form, "submit", save, true);
- if (typeof textarea.form.submit == "function") {
- var realSubmit = textarea.form.submit;
- function wrappedSubmit() {
- save();
- textarea.form.submit = realSubmit;
- textarea.form.submit();
- textarea.form.submit = wrappedSubmit;
- }
- textarea.form.submit = wrappedSubmit;
- }
- }
-
- textarea.style.display = "none";
- var instance = CodeMirror(function(node) {
- textarea.parentNode.insertBefore(node, textarea.nextSibling);
- }, options);
- instance.save = save;
- instance.getTextArea = function() { return textarea; };
- instance.toTextArea = function() {
- save();
- textarea.parentNode.removeChild(instance.getWrapperElement());
- textarea.style.display = "";
- if (textarea.form) {
- rmSubmit();
- if (typeof textarea.form.submit == "function")
- textarea.form.submit = realSubmit;
- }
- };
- return instance;
- };
-
- // Utility functions for working with state. Exported because modes
- // sometimes need to do this.
- function copyState(mode, state) {
- if (state === true) return state;
- if (mode.copyState) return mode.copyState(state);
- var nstate = {};
- for (var n in state) {
- var val = state[n];
- if (val instanceof Array) val = val.concat([]);
- nstate[n] = val;
- }
- return nstate;
- }
- CodeMirror.copyState = copyState;
- function startState(mode, a1, a2) {
- return mode.startState ? mode.startState(a1, a2) : true;
- }
- CodeMirror.startState = startState;
-
- // The character stream used by a mode's parser.
- function StringStream(string, tabSize) {
- this.pos = this.start = 0;
- this.string = string;
- this.tabSize = tabSize || 8;
- }
- StringStream.prototype = {
- eol: function() {return this.pos >= this.string.length;},
- sol: function() {return this.pos == 0;},
- peek: function() {return this.string.charAt(this.pos);},
- next: function() {
- if (this.pos < this.string.length)
- return this.string.charAt(this.pos++);
- },
- eat: function(match) {
- var ch = this.string.charAt(this.pos);
- if (typeof match == "string") var ok = ch == match;
- else var ok = ch && (match.test ? match.test(ch) : match(ch));
- if (ok) {++this.pos; return ch;}
- },
- eatWhile: function(match) {
- var start = this.pos;
- while (this.eat(match)){}
- return this.pos > start;
- },
- eatSpace: function() {
- var start = this.pos;
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
- return this.pos > start;
- },
- skipToEnd: function() {this.pos = this.string.length;},
- skipTo: function(ch) {
- var found = this.string.indexOf(ch, this.pos);
- if (found > -1) {this.pos = found; return true;}
- },
- backUp: function(n) {this.pos -= n;},
- column: function() {return countColumn(this.string, this.start, this.tabSize);},
- indentation: function() {return countColumn(this.string, null, this.tabSize);},
- match: function(pattern, consume, caseInsensitive) {
- if (typeof pattern == "string") {
- function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
- if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
- if (consume !== false) this.pos += pattern.length;
- return true;
- }
- }
- else {
- var match = this.string.slice(this.pos).match(pattern);
- if (match && consume !== false) this.pos += match[0].length;
- return match;
- }
- },
- current: function(){return this.string.slice(this.start, this.pos);}
- };
- CodeMirror.StringStream = StringStream;
-
- function MarkedText(from, to, className, set) {
- this.from = from; this.to = to; this.style = className; this.set = set;
- }
- MarkedText.prototype = {
- attach: function(line) { this.set.push(line); },
- detach: function(line) {
- var ix = indexOf(this.set, line);
- if (ix > -1) this.set.splice(ix, 1);
- },
- split: function(pos, lenBefore) {
- if (this.to <= pos && this.to != null) return null;
- var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
- var to = this.to == null ? null : this.to - pos + lenBefore;
- return new MarkedText(from, to, this.style, this.set);
- },
- dup: function() { return new MarkedText(null, null, this.style, this.set); },
- clipTo: function(fromOpen, from, toOpen, to, diff) {
- if (this.from != null && this.from >= from)
- this.from = Math.max(to, this.from) + diff;
- if (this.to != null && this.to > from)
- this.to = to < this.to ? this.to + diff : from;
- if (fromOpen && to > this.from && (to < this.to || this.to == null))
- this.from = null;
- if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
- this.to = null;
- },
- isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
- sameSet: function(x) { return this.set == x.set; }
- };
-
- function Bookmark(pos) {
- this.from = pos; this.to = pos; this.line = null;
- }
- Bookmark.prototype = {
- attach: function(line) { this.line = line; },
- detach: function(line) { if (this.line == line) this.line = null; },
- split: function(pos, lenBefore) {
- if (pos < this.from) {
- this.from = this.to = (this.from - pos) + lenBefore;
- return this;
- }
- },
- isDead: function() { return this.from > this.to; },
- clipTo: function(fromOpen, from, toOpen, to, diff) {
- if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
- this.from = 0; this.to = -1;
- } else if (this.from > from) {
- this.from = this.to = Math.max(to, this.from) + diff;
- }
- },
- sameSet: function(x) { return false; },
- find: function() {
- if (!this.line || !this.line.parent) return null;
- return {line: lineNo(this.line), ch: this.from};
- },
- clear: function() {
- if (this.line) {
- var found = indexOf(this.line.marked, this);
- if (found != -1) this.line.marked.splice(found, 1);
- this.line = null;
- }
- }
- };
-
- // Line objects. These hold state related to a line, including
- // highlighting info (the styles array).
- function Line(text, styles) {
- this.styles = styles || [text, null];
- this.text = text;
- this.height = 1;
- this.marked = this.gutterMarker = this.className = this.handlers = null;
- this.stateAfter = this.parent = this.hidden = null;
- }
- Line.inheritMarks = function(text, orig) {
- var ln = new Line(text), mk = orig && orig.marked;
- if (mk) {
- for (var i = 0; i < mk.length; ++i) {
- if (mk[i].to == null && mk[i].style) {
- var newmk = ln.marked || (ln.marked = []), mark = mk[i];
- var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
- }
- }
- }
- return ln;
- }
- Line.prototype = {
- // Replace a piece of a line, keeping the styles around it intact.
- replace: function(from, to_, text) {
- var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
- copyStyles(0, from, this.styles, st);
- if (text) st.push(text, null);
- copyStyles(to, this.text.length, this.styles, st);
- this.styles = st;
- this.text = this.text.slice(0, from) + text + this.text.slice(to);
- this.stateAfter = null;
- if (mk) {
- var diff = text.length - (to - from);
- for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
- mark.clipTo(from == null, from || 0, to_ == null, to, diff);
- if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
- }
- }
- },
- // Split a part off a line, keeping styles and markers intact.
- split: function(pos, textBefore) {
- var st = [textBefore, null], mk = this.marked;
- copyStyles(pos, this.text.length, this.styles, st);
- var taken = new Line(textBefore + this.text.slice(pos), st);
- if (mk) {
- for (var i = 0; i < mk.length; ++i) {
- var mark = mk[i];
- var newmark = mark.split(pos, textBefore.length);
- if (newmark) {
- if (!taken.marked) taken.marked = [];
- taken.marked.push(newmark); newmark.attach(taken);
- }
- }
- }
- return taken;
- },
- append: function(line) {
- var mylen = this.text.length, mk = line.marked, mymk = this.marked;
- this.text += line.text;
- copyStyles(0, line.text.length, line.styles, this.styles);
- if (mymk) {
- for (var i = 0; i < mymk.length; ++i)
- if (mymk[i].to == null) mymk[i].to = mylen;
- }
- if (mk && mk.length) {
- if (!mymk) this.marked = mymk = [];
- outer: for (var i = 0; i < mk.length; ++i) {
- var mark = mk[i];
- if (!mark.from) {
- for (var j = 0; j < mymk.length; ++j) {
- var mymark = mymk[j];
- if (mymark.to == mylen && mymark.sameSet(mark)) {
- mymark.to = mark.to == null ? null : mark.to + mylen;
- if (mymark.isDead()) {
- mymark.detach(this);
- mk.splice(i--, 1);
- }
- continue outer;
- }
- }
- }
- mymk.push(mark);
- mark.attach(this);
- mark.from += mylen;
- if (mark.to != null) mark.to += mylen;
- }
- }
- },
- fixMarkEnds: function(other) {
- var mk = this.marked, omk = other.marked;
- if (!mk) return;
- for (var i = 0; i < mk.length; ++i) {
- var mark = mk[i], close = mark.to == null;
- if (close && omk) {
- for (var j = 0; j < omk.length; ++j)
- if (omk[j].sameSet(mark)) {close = false; break;}
- }
- if (close) mark.to = this.text.length;
- }
- },
- fixMarkStarts: function() {
- var mk = this.marked;
- if (!mk) return;
- for (var i = 0; i < mk.length; ++i)
- if (mk[i].from == null) mk[i].from = 0;
- },
- addMark: function(mark) {
- mark.attach(this);
- if (this.marked == null) this.marked = [];
- this.marked.push(mark);
- this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
- },
- // Run the given mode's parser over a line, update the styles
- // array, which contains alternating fragments of text and CSS
- // classes.
- highlight: function(mode, state, tabSize) {
- var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
- var changed = false, curWord = st[0], prevWord;
- if (this.text == "" && mode.blankLine) mode.blankLine(state);
- while (!stream.eol()) {
- var style = mode.token(stream, state);
- var substr = this.text.slice(stream.start, stream.pos);
- stream.start = stream.pos;
- if (pos && st[pos-1] == style)
- st[pos-2] += substr;
- else if (substr) {
- if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
- st[pos++] = substr; st[pos++] = style;
- prevWord = curWord; curWord = st[pos];
- }
- // Give up when line is ridiculously long
- if (stream.pos > 5000) {
- st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
- break;
- }
- }
- if (st.length != pos) {st.length = pos; changed = true;}
- if (pos && st[pos-2] != prevWord) changed = true;
- // Short lines with simple highlights return null, and are
- // counted as changed by the driver because they are likely to
- // highlight the same way in various contexts.
- return changed || (st.length < 5 && this.text.length < 10 ? null : false);
- },
- // Fetch the parser token for a given character. Useful for hacks
- // that want to inspect the mode state (say, for completion).
- getTokenAt: function(mode, state, ch) {
- var txt = this.text, stream = new StringStream(txt);
- while (stream.pos < ch && !stream.eol()) {
- stream.start = stream.pos;
- var style = mode.token(stream, state);
- }
- return {start: stream.start,
- end: stream.pos,
- string: stream.current(),
- className: style || null,
- state: state};
- },
- indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
- // Produces an HTML fragment for the line, taking selection,
- // marking, and highlighting into account.
- getHTML: function(sfrom, sto, includePre, tabText, endAt) {
- var html = [], first = true;
- if (includePre)
- html.push(this.className ? '': "");
- function span(text, style) {
- if (!text) return;
- // Work around a bug where, in some compat modes, IE ignores leading spaces
- if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
- first = false;
- if (style) html.push('', htmlEscape(text).replace(/\t/g, tabText), "");
- else html.push(htmlEscape(text).replace(/\t/g, tabText));
- }
- var st = this.styles, allText = this.text, marked = this.marked;
- if (sfrom == sto) sfrom = null;
- var len = allText.length;
- if (endAt != null) len = Math.min(endAt, len);
-
- if (!allText && endAt == null)
- span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
- else if (!marked && sfrom == null)
- for (var i = 0, ch = 0; ch < len; i+=2) {
- var str = st[i], style = st[i+1], l = str.length;
- if (ch + l > len) str = str.slice(0, len - ch);
- ch += l;
- span(str, style && "cm-" + style);
- }
- else {
- var pos = 0, i = 0, text = "", style, sg = 0;
- var markpos = -1, mark = null;
- function nextMark() {
- if (marked) {
- markpos += 1;
- mark = (markpos < marked.length) ? marked[markpos] : null;
- }
- }
- nextMark();
- while (pos < len) {
- var upto = len;
- var extraStyle = "";
- if (sfrom != null) {
- if (sfrom > pos) upto = sfrom;
- else if (sto == null || sto > pos) {
- extraStyle = " CodeMirror-selected";
- if (sto != null) upto = Math.min(upto, sto);
- }
- }
- while (mark && mark.to != null && mark.to <= pos) nextMark();
- if (mark) {
- if (mark.from > pos) upto = Math.min(upto, mark.from);
- else {
- extraStyle += " " + mark.style;
- if (mark.to != null) upto = Math.min(upto, mark.to);
- }
- }
- for (;;) {
- var end = pos + text.length;
- var appliedStyle = style;
- if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
- pos = end;
- text = st[i++]; style = "cm-" + st[i++];
- }
- }
- if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
- }
- if (includePre) html.push("
");
- return html.join("");
- },
- cleanUp: function() {
- this.parent = null;
- if (this.marked)
- for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
- }
- };
- // Utility used by replace and split above
- function copyStyles(from, to, source, dest) {
- for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
- var part = source[i], end = pos + part.length;
- if (state == 0) {
- if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
- if (end >= from) state = 1;
- }
- else if (state == 1) {
- if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
- else dest.push(part, source[i+1]);
- }
- pos = end;
- }
- }
-
- // Data structure that holds the sequence of lines.
- function LeafChunk(lines) {
- this.lines = lines;
- this.parent = null;
- for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
- lines[i].parent = this;
- height += lines[i].height;
- }
- this.height = height;
- }
- LeafChunk.prototype = {
- chunkSize: function() { return this.lines.length; },
- remove: function(at, n, callbacks) {
- for (var i = at, e = at + n; i < e; ++i) {
- var line = this.lines[i];
- this.height -= line.height;
- line.cleanUp();
- if (line.handlers)
- for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
- }
- this.lines.splice(at, n);
- },
- collapse: function(lines) {
- lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
- },
- insertHeight: function(at, lines, height) {
- this.height += height;
- this.lines.splice.apply(this.lines, [at, 0].concat(lines));
- for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
- },
- iterN: function(at, n, op) {
- for (var e = at + n; at < e; ++at)
- if (op(this.lines[at])) return true;
- }
- };
- function BranchChunk(children) {
- this.children = children;
- var size = 0, height = 0;
- for (var i = 0, e = children.length; i < e; ++i) {
- var ch = children[i];
- size += ch.chunkSize(); height += ch.height;
- ch.parent = this;
- }
- this.size = size;
- this.height = height;
- this.parent = null;
- }
- BranchChunk.prototype = {
- chunkSize: function() { return this.size; },
- remove: function(at, n, callbacks) {
- this.size -= n;
- for (var i = 0; i < this.children.length; ++i) {
- var child = this.children[i], sz = child.chunkSize();
- if (at < sz) {
- var rm = Math.min(n, sz - at), oldHeight = child.height;
- child.remove(at, rm, callbacks);
- this.height -= oldHeight - child.height;
- if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
- if ((n -= rm) == 0) break;
- at = 0;
- } else at -= sz;
- }
- if (this.size - n < 25) {
- var lines = [];
- this.collapse(lines);
- this.children = [new LeafChunk(lines)];
- }
- },
- collapse: function(lines) {
- for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
- },
- insert: function(at, lines) {
- var height = 0;
- for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
- this.insertHeight(at, lines, height);
- },
- insertHeight: function(at, lines, height) {
- this.size += lines.length;
- this.height += height;
- for (var i = 0, e = this.children.length; i < e; ++i) {
- var child = this.children[i], sz = child.chunkSize();
- if (at <= sz) {
- child.insertHeight(at, lines, height);
- if (child.lines && child.lines.length > 50) {
- while (child.lines.length > 50) {
- var spilled = child.lines.splice(child.lines.length - 25, 25);
- var newleaf = new LeafChunk(spilled);
- child.height -= newleaf.height;
- this.children.splice(i + 1, 0, newleaf);
- newleaf.parent = this;
- }
- this.maybeSpill();
- }
- break;
- }
- at -= sz;
- }
- },
- maybeSpill: function() {
- if (this.children.length <= 10) return;
- var me = this;
- do {
- var spilled = me.children.splice(me.children.length - 5, 5);
- var sibling = new BranchChunk(spilled);
- if (!me.parent) { // Become the parent node
- var copy = new BranchChunk(me.children);
- copy.parent = me;
- me.children = [copy, sibling];
- me = copy;
- } else {
- me.size -= sibling.size;
- me.height -= sibling.height;
- var myIndex = indexOf(me.parent.children, me);
- me.parent.children.splice(myIndex + 1, 0, sibling);
- }
- sibling.parent = me.parent;
- } while (me.children.length > 10);
- me.parent.maybeSpill();
- },
- iter: function(from, to, op) { this.iterN(from, to - from, op); },
- iterN: function(at, n, op) {
- for (var i = 0, e = this.children.length; i < e; ++i) {
- var child = this.children[i], sz = child.chunkSize();
- if (at < sz) {
- var used = Math.min(n, sz - at);
- if (child.iterN(at, used, op)) return true;
- if ((n -= used) == 0) break;
- at = 0;
- } else at -= sz;
- }
- }
- };
-
- function getLineAt(chunk, n) {
- while (!chunk.lines) {
- for (var i = 0;; ++i) {
- var child = chunk.children[i], sz = child.chunkSize();
- if (n < sz) { chunk = child; break; }
- n -= sz;
- }
- }
- return chunk.lines[n];
- }
- function lineNo(line) {
- if (line.parent == null) return null;
- var cur = line.parent, no = indexOf(cur.lines, line);
- for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
- for (var i = 0, e = chunk.children.length; ; ++i) {
- if (chunk.children[i] == cur) break;
- no += chunk.children[i].chunkSize();
- }
- }
- return no;
- }
- function lineAtHeight(chunk, h) {
- var n = 0;
- outer: do {
- for (var i = 0, e = chunk.children.length; i < e; ++i) {
- var child = chunk.children[i], ch = child.height;
- if (h < ch) { chunk = child; continue outer; }
- h -= ch;
- n += child.chunkSize();
- }
- return n;
- } while (!chunk.lines);
- for (var i = 0, e = chunk.lines.length; i < e; ++i) {
- var line = chunk.lines[i], lh = line.height;
- if (h < lh) break;
- h -= lh;
- }
- return n + i;
- }
- function heightAtLine(chunk, n) {
- var h = 0;
- outer: do {
- for (var i = 0, e = chunk.children.length; i < e; ++i) {
- var child = chunk.children[i], sz = child.chunkSize();
- if (n < sz) { chunk = child; continue outer; }
- n -= sz;
- h += child.height;
- }
- return h;
- } while (!chunk.lines);
- for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
- return h;
- }
-
- // The history object 'chunks' changes that are made close together
- // and at almost the same time into bigger undoable units.
- function History() {
- this.time = 0;
- this.done = []; this.undone = [];
- }
- History.prototype = {
- addChange: function(start, added, old) {
- this.undone.length = 0;
- var time = +new Date, last = this.done[this.done.length - 1];
- if (time - this.time > 400 || !last ||
- last.start > start + added || last.start + last.added < start - last.added + last.old.length)
- this.done.push({start: start, added: added, old: old});
- else {
- var oldoff = 0;
- if (start < last.start) {
- for (var i = last.start - start - 1; i >= 0; --i)
- last.old.unshift(old[i]);
- last.added += last.start - start;
- last.start = start;
- }
- else if (last.start < start) {
- oldoff = start - last.start;
- added += oldoff;
- }
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
- last.old.push(old[i]);
- if (last.added < added) last.added = added;
- }
- this.time = time;
- }
- };
-
- function stopMethod() {e_stop(this);}
- // Ensure an event has a stop method.
- function addStop(event) {
- if (!event.stop) event.stop = stopMethod;
- return event;
- }
-
- function e_preventDefault(e) {
- if (e.preventDefault) e.preventDefault();
- else e.returnValue = false;
- }
- function e_stopPropagation(e) {
- if (e.stopPropagation) e.stopPropagation();
- else e.cancelBubble = true;
- }
- function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
- CodeMirror.e_stop = e_stop;
- CodeMirror.e_preventDefault = e_preventDefault;
- CodeMirror.e_stopPropagation = e_stopPropagation;
-
- function e_target(e) {return e.target || e.srcElement;}
- function e_button(e) {
- if (e.which) return e.which;
- else if (e.button & 1) return 1;
- else if (e.button & 2) return 3;
- else if (e.button & 4) return 2;
- }
-
- // Event handler registration. If disconnect is true, it'll return a
- // function that unregisters the handler.
- function connect(node, type, handler, disconnect) {
- if (typeof node.addEventListener == "function") {
- node.addEventListener(type, handler, false);
- if (disconnect) return function() {node.removeEventListener(type, handler, false);};
- }
- else {
- var wrapHandler = function(event) {handler(event || window.event);};
- node.attachEvent("on" + type, wrapHandler);
- if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
- }
- }
- CodeMirror.connect = connect;
-
- function Delayed() {this.id = null;}
- Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
-
- // Detect drag-and-drop
- var dragAndDrop = function() {
- // IE8 has ondragstart and ondrop properties, but doesn't seem to
- // actually support ondragstart the way it's supposed to work.
- if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
- var div = document.createElement('div');
- return "draggable" in div;
- }();
-
- var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
- var ie = /MSIE \d/.test(navigator.userAgent);
- var webkit = /WebKit\//.test(navigator.userAgent);
-
- var lineSep = "\n";
- // Feature-detect whether newlines in textareas are converted to \r\n
- (function () {
- var te = document.createElement("textarea");
- te.value = "foo\nbar";
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
- }());
-
- // Counts the column offset in a string, taking tabs into account.
- // Used mostly to find indentation.
- function countColumn(string, end, tabSize) {
- if (end == null) {
- end = string.search(/[^\s\u00a0]/);
- if (end == -1) end = string.length;
- }
- for (var i = 0, n = 0; i < end; ++i) {
- if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
- else ++n;
- }
- return n;
- }
-
- function computedStyle(elt) {
- if (elt.currentStyle) return elt.currentStyle;
- return window.getComputedStyle(elt, null);
- }
-
- // Find the position of an element by following the offsetParent chain.
- // If screen==true, it returns screen (rather than page) coordinates.
- function eltOffset(node, screen) {
- var bod = node.ownerDocument.body;
- var x = 0, y = 0, skipBody = false;
- for (var n = node; n; n = n.offsetParent) {
- var ol = n.offsetLeft, ot = n.offsetTop;
- // Firefox reports weird inverted offsets when the body has a border.
- if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
- else { x += ol, y += ot; }
- if (screen && computedStyle(n).position == "fixed")
- skipBody = true;
- }
- var e = screen && !skipBody ? null : bod;
- for (var n = node.parentNode; n != e; n = n.parentNode)
- if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
- return {left: x, top: y};
- }
- // Use the faster and saner getBoundingClientRect method when possible.
- if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
- // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
- // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
- try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
- catch(e) { box = {top: 0, left: 0}; }
- if (!screen) {
- // Get the toplevel scroll, working around browser differences.
- if (window.pageYOffset == null) {
- var t = document.documentElement || document.body.parentNode;
- if (t.scrollTop == null) t = document.body;
- box.top += t.scrollTop; box.left += t.scrollLeft;
- } else {
- box.top += window.pageYOffset; box.left += window.pageXOffset;
- }
- }
- return box;
- };
-
- // Get a node's text content.
- function eltText(node) {
- return node.textContent || node.innerText || node.nodeValue || "";
- }
-
- // Operations on {line, ch} objects.
- function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
- function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
- function copyPos(x) {return {line: x.line, ch: x.ch};}
-
- var escapeElement = document.createElement("pre");
- function htmlEscape(str) {
- escapeElement.textContent = str;
- return escapeElement.innerHTML;
- }
- // Recent (late 2011) Opera betas insert bogus newlines at the start
- // of the textContent, so we strip those.
- if (htmlEscape("a") == "\na")
- htmlEscape = function(str) {
- escapeElement.textContent = str;
- return escapeElement.innerHTML.slice(1);
- };
- // Some IEs don't preserve tabs through innerHTML
- else if (htmlEscape("\t") != "\t")
- htmlEscape = function(str) {
- escapeElement.innerHTML = "";
- escapeElement.appendChild(document.createTextNode(str));
- return escapeElement.innerHTML;
- };
- CodeMirror.htmlEscape = htmlEscape;
-
- // Used to position the cursor after an undo/redo by finding the
- // last edited character.
- function editEnd(from, to) {
- if (!to) return from ? from.length : 0;
- if (!from) return to.length;
- for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
- if (from.charAt(i) != to.charAt(j)) break;
- return j + 1;
- }
-
- function indexOf(collection, elt) {
- if (collection.indexOf) return collection.indexOf(elt);
- for (var i = 0, e = collection.length; i < e; ++i)
- if (collection[i] == elt) return i;
- return -1;
- }
- function isWordChar(ch) {
- return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
- }
-
- // See if "".split is the broken IE version, if so, provide an
- // alternative way to split lines.
- var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
- var pos = 0, nl, result = [];
- while ((nl = string.indexOf("\n", pos)) > -1) {
- result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
- pos = nl + 1;
- }
- result.push(string.slice(pos));
- return result;
- } : function(string){return string.split(/\r?\n/);};
- CodeMirror.splitLines = splitLines;
-
- var hasSelection = window.getSelection ? function(te) {
- try { return te.selectionStart != te.selectionEnd; }
- catch(e) { return false; }
- } : function(te) {
- try {var range = te.ownerDocument.selection.createRange();}
- catch(e) {}
- if (!range || range.parentElement() != te) return false;
- return range.compareEndPoints("StartToEnd", range) != 0;
- };
-
- CodeMirror.defineMode("null", function() {
- return {token: function(stream) {stream.skipToEnd();}};
- });
- CodeMirror.defineMIME("text/plain", "null");
-
- var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
- 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
- 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
- 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
- 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
- 63233: "Down", 63302: "Insert", 63272: "Delete"};
- CodeMirror.keyNames = keyNames;
- (function() {
- // Number keys
- for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
- // Alphabetic keys
- for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
- // Function keys
- for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
- })();
-
- return CodeMirror;
-})();
-CodeMirror.defineMode("xml", function(config, parserConfig) {
- var indentUnit = config.indentUnit;
- var Kludges = parserConfig.htmlMode ? {
- autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
- "meta": true, "col": true, "frame": true, "base": true, "area": true},
- doNotIndent: {"pre": true},
- allowUnquoted: true
- } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false};
- var alignCDATA = parserConfig.alignCDATA;
-
- // Return variables for tokenizers
- var tagName, type;
-
- function inText(stream, state) {
- function chain(parser) {
- state.tokenize = parser;
- return parser(stream, state);
- }
-
- var ch = stream.next();
- if (ch == "<") {
- if (stream.eat("!")) {
- if (stream.eat("[")) {
- if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
- else return null;
- }
- else if (stream.match("--")) return chain(inBlock("comment", "-->"));
- else if (stream.match("DOCTYPE", true, true)) {
- stream.eatWhile(/[\w\._\-]/);
- return chain(doctype(1));
- }
- else return null;
- }
- else if (stream.eat("?")) {
- stream.eatWhile(/[\w\._\-]/);
- state.tokenize = inBlock("meta", "?>");
- return "meta";
- }
- else {
- type = stream.eat("/") ? "closeTag" : "openTag";
- stream.eatSpace();
- tagName = "";
- var c;
- while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
- state.tokenize = inTag;
- return "tag";
- }
- }
- else if (ch == "&") {
- stream.eatWhile(/[^;]/);
- stream.eat(";");
- return "atom";
- }
- else {
- stream.eatWhile(/[^&<]/);
- return null;
- }
- }
-
- function inTag(stream, state) {
- var ch = stream.next();
- if (ch == ">" || (ch == "/" && stream.eat(">"))) {
- state.tokenize = inText;
- type = ch == ">" ? "endTag" : "selfcloseTag";
- return "tag";
- }
- else if (ch == "=") {
- type = "equals";
- return null;
- }
- else if (/[\'\"]/.test(ch)) {
- state.tokenize = inAttribute(ch);
- return state.tokenize(stream, state);
- }
- else {
- stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
- return "word";
- }
- }
-
- function inAttribute(quote) {
- return function(stream, state) {
- while (!stream.eol()) {
- if (stream.next() == quote) {
- state.tokenize = inTag;
- break;
- }
- }
- return "string";
- };
- }
-
- function inBlock(style, terminator) {
- return function(stream, state) {
- while (!stream.eol()) {
- if (stream.match(terminator)) {
- state.tokenize = inText;
- break;
- }
- stream.next();
- }
- return style;
- };
- }
- function doctype(depth) {
- return function(stream, state) {
- var ch;
- while ((ch = stream.next()) != null) {
- if (ch == "<") {
- state.tokenize = doctype(depth + 1);
- return state.tokenize(stream, state);
- } else if (ch == ">") {
- if (depth == 1) {
- state.tokenize = inText;
- break;
- } else {
- state.tokenize = doctype(depth - 1);
- return state.tokenize(stream, state);
- }
- }
- }
- return "meta";
- };
- }
-
- var curState, setStyle;
- function pass() {
- for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
- }
- function cont() {
- pass.apply(null, arguments);
- return true;
- }
-
- function pushContext(tagName, startOfLine) {
- var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
- curState.context = {
- prev: curState.context,
- tagName: tagName,
- indent: curState.indented,
- startOfLine: startOfLine,
- noIndent: noIndent
- };
- }
- function popContext() {
- if (curState.context) curState.context = curState.context.prev;
- }
-
- function element(type) {
- if (type == "openTag") {
- curState.tagName = tagName;
- return cont(attributes, endtag(curState.startOfLine));
- } else if (type == "closeTag") {
- var err = false;
- if (curState.context) {
- err = curState.context.tagName != tagName;
- } else {
- err = true;
- }
- if (err) setStyle = "error";
- return cont(endclosetag(err));
- }
- return cont();
- }
- function endtag(startOfLine) {
- return function(type) {
- if (type == "selfcloseTag" ||
- (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
- return cont();
- if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
- return cont();
- };
- }
- function endclosetag(err) {
- return function(type) {
- if (err) setStyle = "error";
- if (type == "endTag") { popContext(); return cont(); }
- setStyle = "error";
- return cont(arguments.callee);
- }
- }
-
- function attributes(type) {
- if (type == "word") {setStyle = "attribute"; return cont(attributes);}
- if (type == "equals") return cont(attvalue, attributes);
- if (type == "string") {setStyle = "error"; return cont(attributes);}
- return pass();
- }
- function attvalue(type) {
- if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
- if (type == "string") return cont(attvaluemaybe);
- return pass();
- }
- function attvaluemaybe(type) {
- if (type == "string") return cont(attvaluemaybe);
- else return pass();
- }
-
- return {
- startState: function() {
- return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
- },
-
- token: function(stream, state) {
- if (stream.sol()) {
- state.startOfLine = true;
- state.indented = stream.indentation();
- }
- if (stream.eatSpace()) return null;
-
- setStyle = type = tagName = null;
- var style = state.tokenize(stream, state);
- state.type = type;
- if ((style || type) && style != "comment") {
- curState = state;
- while (true) {
- var comb = state.cc.pop() || element;
- if (comb(type || style)) break;
- }
- }
- state.startOfLine = false;
- return setStyle || style;
- },
-
- indent: function(state, textAfter, fullLine) {
- var context = state.context;
- if ((state.tokenize != inTag && state.tokenize != inText) ||
- context && context.noIndent)
- return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
- if (alignCDATA && /!?|]/;
-
- function chain(stream, state, f) {
- state.tokenize = f;
- return f(stream, state);
- }
-
- function nextUntilUnescaped(stream, end) {
- var escaped = false, next;
- while ((next = stream.next()) != null) {
- if (next == end && !escaped)
- return false;
- escaped = !escaped && next == "\\";
- }
- return escaped;
- }
-
- // Used as scratch variables to communicate multiple values without
- // consing up tons of objects.
- var type, content;
- function ret(tp, style, cont) {
- type = tp; content = cont;
- return style;
- }
-
- function jsTokenBase(stream, state) {
- var ch = stream.next();
- if (ch == '"' || ch == "'")
- return chain(stream, state, jsTokenString(ch));
- else if (/[\[\]{}\(\),;\:\.]/.test(ch))
- return ret(ch);
- else if (ch == "0" && stream.eat(/x/i)) {
- stream.eatWhile(/[\da-f]/i);
- return ret("number", "number");
- }
- else if (/\d/.test(ch)) {
- stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
- return ret("number", "number");
- }
- else if (ch == "/") {
- if (stream.eat("*")) {
- return chain(stream, state, jsTokenComment);
- }
- else if (stream.eat("/")) {
- stream.skipToEnd();
- return ret("comment", "comment");
- }
- else if (state.reAllowed) {
- nextUntilUnescaped(stream, "/");
- stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
- return ret("regexp", "string");
- }
- else {
- stream.eatWhile(isOperatorChar);
- return ret("operator", null, stream.current());
- }
- }
- else if (ch == "#") {
- stream.skipToEnd();
- return ret("error", "error");
- }
- else if (isOperatorChar.test(ch)) {
- stream.eatWhile(isOperatorChar);
- return ret("operator", null, stream.current());
- }
- else {
- stream.eatWhile(/[\w\$_]/);
- var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
- return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
- ret("variable", "variable", word);
- }
- }
-
- function jsTokenString(quote) {
- return function(stream, state) {
- if (!nextUntilUnescaped(stream, quote))
- state.tokenize = jsTokenBase;
- return ret("string", "string");
- };
- }
-
- function jsTokenComment(stream, state) {
- var maybeEnd = false, ch;
- while (ch = stream.next()) {
- if (ch == "/" && maybeEnd) {
- state.tokenize = jsTokenBase;
- break;
- }
- maybeEnd = (ch == "*");
- }
- return ret("comment", "comment");
- }
-
- // Parser
-
- var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
-
- function JSLexical(indented, column, type, align, prev, info) {
- this.indented = indented;
- this.column = column;
- this.type = type;
- this.prev = prev;
- this.info = info;
- if (align != null) this.align = align;
- }
-
- function inScope(state, varname) {
- for (var v = state.localVars; v; v = v.next)
- if (v.name == varname) return true;
- }
-
- function parseJS(state, style, type, content, stream) {
- var cc = state.cc;
- // Communicate our context to the combinators.
- // (Less wasteful than consing up a hundred closures on every call.)
- cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
-
- if (!state.lexical.hasOwnProperty("align"))
- state.lexical.align = true;
-
- while(true) {
- var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
- if (combinator(type, content)) {
- while(cc.length && cc[cc.length - 1].lex)
- cc.pop()();
- if (cx.marked) return cx.marked;
- if (type == "variable" && inScope(state, content)) return "variable-2";
- return style;
- }
- }
- }
-
- // Combinator utils
-
- var cx = {state: null, column: null, marked: null, cc: null};
- function pass() {
- for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
- }
- function cont() {
- pass.apply(null, arguments);
- return true;
- }
- function register(varname) {
- var state = cx.state;
- if (state.context) {
- cx.marked = "def";
- for (var v = state.localVars; v; v = v.next)
- if (v.name == varname) return;
- state.localVars = {name: varname, next: state.localVars};
- }
- }
-
- // Combinators
-
- var defaultVars = {name: "this", next: {name: "arguments"}};
- function pushcontext() {
- if (!cx.state.context) cx.state.localVars = defaultVars;
- cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
- }
- function popcontext() {
- cx.state.localVars = cx.state.context.vars;
- cx.state.context = cx.state.context.prev;
- }
- function pushlex(type, info) {
- var result = function() {
- var state = cx.state;
- state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
- };
- result.lex = true;
- return result;
- }
- function poplex() {
- var state = cx.state;
- if (state.lexical.prev) {
- if (state.lexical.type == ")")
- state.indented = state.lexical.indented;
- state.lexical = state.lexical.prev;
- }
- }
- poplex.lex = true;
-
- function expect(wanted) {
- return function expecting(type) {
- if (type == wanted) return cont();
- else if (wanted == ";") return pass();
- else return cont(arguments.callee);
- };
- }
-
- function statement(type) {
- if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
- if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
- if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
- if (type == "{") return cont(pushlex("}"), block, poplex);
- if (type == ";") return cont();
- if (type == "function") return cont(functiondef);
- if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
- poplex, statement, poplex);
- if (type == "variable") return cont(pushlex("stat"), maybelabel);
- if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
- block, poplex, poplex);
- if (type == "case") return cont(expression, expect(":"));
- if (type == "default") return cont(expect(":"));
- if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
- statement, poplex, popcontext);
- return pass(pushlex("stat"), expression, expect(";"), poplex);
- }
- function expression(type) {
- if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
- if (type == "function") return cont(functiondef);
- if (type == "keyword c") return cont(maybeexpression);
- if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
- if (type == "operator") return cont(expression);
- if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
- if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
- return cont();
- }
- function maybeexpression(type) {
- if (type.match(/[;\}\)\],]/)) return pass();
- return pass(expression);
- }
-
- function maybeoperator(type, value) {
- if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
- if (type == "operator") return cont(expression);
- if (type == ";") return;
- if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
- if (type == ".") return cont(property, maybeoperator);
- if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
- }
- function maybelabel(type) {
- if (type == ":") return cont(poplex, statement);
- return pass(maybeoperator, expect(";"), poplex);
- }
- function property(type) {
- if (type == "variable") {cx.marked = "property"; return cont();}
- }
- function objprop(type) {
- if (type == "variable") cx.marked = "property";
- if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
- }
- function commasep(what, end) {
- function proceed(type) {
- if (type == ",") return cont(what, proceed);
- if (type == end) return cont();
- return cont(expect(end));
- }
- return function commaSeparated(type) {
- if (type == end) return cont();
- else return pass(what, proceed);
- };
- }
- function block(type) {
- if (type == "}") return cont();
- return pass(statement, block);
- }
- function vardef1(type, value) {
- if (type == "variable"){register(value); return cont(vardef2);}
- return cont();
- }
- function vardef2(type, value) {
- if (value == "=") return cont(expression, vardef2);
- if (type == ",") return cont(vardef1);
- }
- function forspec1(type) {
- if (type == "var") return cont(vardef1, forspec2);
- if (type == ";") return pass(forspec2);
- if (type == "variable") return cont(formaybein);
- return pass(forspec2);
- }
- function formaybein(type, value) {
- if (value == "in") return cont(expression);
- return cont(maybeoperator, forspec2);
- }
- function forspec2(type, value) {
- if (type == ";") return cont(forspec3);
- if (value == "in") return cont(expression);
- return cont(expression, expect(";"), forspec3);
- }
- function forspec3(type) {
- if (type != ")") cont(expression);
- }
- function functiondef(type, value) {
- if (type == "variable") {register(value); return cont(functiondef);}
- if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
- }
- function funarg(type, value) {
- if (type == "variable") {register(value); return cont();}
- }
-
- // Interface
-
- return {
- startState: function(basecolumn) {
- return {
- tokenize: jsTokenBase,
- reAllowed: true,
- kwAllowed: true,
- cc: [],
- lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
- localVars: null,
- context: null,
- indented: 0
- };
- },
-
- token: function(stream, state) {
- if (stream.sol()) {
- if (!state.lexical.hasOwnProperty("align"))
- state.lexical.align = false;
- state.indented = stream.indentation();
- }
- if (stream.eatSpace()) return null;
- var style = state.tokenize(stream, state);
- if (type == "comment") return style;
- state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
- state.kwAllowed = type != '.';
- return parseJS(state, style, type, content, stream);
- },
-
- indent: function(state, textAfter) {
- if (state.tokenize != jsTokenBase) return 0;
- var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
- type = lexical.type, closing = firstChar == type;
- if (type == "vardef") return lexical.indented + 4;
- else if (type == "form" && firstChar == "{") return lexical.indented;
- else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
- else if (lexical.info == "switch" && !closing)
- return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
- else if (lexical.align) return lexical.column + (closing ? 0 : 1);
- else return lexical.indented + (closing ? 0 : indentUnit);
- },
-
- electricChars: ":{}"
- };
-});
-
-CodeMirror.defineMIME("text/javascript", "javascript");
-CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
-
-CodeMirror.defineMode("css", function(config) {
- var indentUnit = config.indentUnit, type;
- function ret(style, tp) {type = tp; return style;}
-
- function tokenBase(stream, state) {
- var ch = stream.next();
- if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
- else if (ch == "/" && stream.eat("*")) {
- state.tokenize = tokenCComment;
- return tokenCComment(stream, state);
- }
- else if (ch == "<" && stream.eat("!")) {
- state.tokenize = tokenSGMLComment;
- return tokenSGMLComment(stream, state);
- }
- else if (ch == "=") ret(null, "compare");
- else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
- else if (ch == "\"" || ch == "'") {
- state.tokenize = tokenString(ch);
- return state.tokenize(stream, state);
- }
- else if (ch == "#") {
- stream.eatWhile(/[\w\\\-]/);
- return ret("atom", "hash");
- }
- else if (ch == "!") {
- stream.match(/^\s*\w*/);
- return ret("keyword", "important");
- }
- else if (/\d/.test(ch)) {
- stream.eatWhile(/[\w.%]/);
- return ret("number", "unit");
- }
- else if (/[,.+>*\/]/.test(ch)) {
- return ret(null, "select-op");
- }
- else if (/[;{}:\[\]]/.test(ch)) {
- return ret(null, ch);
- }
- else {
- stream.eatWhile(/[\w\\\-]/);
- return ret("variable", "variable");
- }
- }
-
- function tokenCComment(stream, state) {
- var maybeEnd = false, ch;
- while ((ch = stream.next()) != null) {
- if (maybeEnd && ch == "/") {
- state.tokenize = tokenBase;
- break;
- }
- maybeEnd = (ch == "*");
- }
- return ret("comment", "comment");
- }
-
- function tokenSGMLComment(stream, state) {
- var dashes = 0, ch;
- while ((ch = stream.next()) != null) {
- if (dashes >= 2 && ch == ">") {
- state.tokenize = tokenBase;
- break;
- }
- dashes = (ch == "-") ? dashes + 1 : 0;
- }
- return ret("comment", "comment");
- }
-
- function tokenString(quote) {
- return function(stream, state) {
- var escaped = false, ch;
- while ((ch = stream.next()) != null) {
- if (ch == quote && !escaped)
- break;
- escaped = !escaped && ch == "\\";
- }
- if (!escaped) state.tokenize = tokenBase;
- return ret("string", "string");
- };
- }
-
- return {
- startState: function(base) {
- return {tokenize: tokenBase,
- baseIndent: base || 0,
- stack: []};
- },
-
- token: function(stream, state) {
- if (stream.eatSpace()) return null;
- var style = state.tokenize(stream, state);
-
- var context = state.stack[state.stack.length-1];
- if (type == "hash" && context == "rule") style = "atom";
- else if (style == "variable") {
- if (context == "rule") style = "number";
- else if (!context || context == "@media{") style = "tag";
- }
-
- if (context == "rule" && /^[\{\};]$/.test(type))
- state.stack.pop();
- if (type == "{") {
- if (context == "@media") state.stack[state.stack.length-1] = "@media{";
- else state.stack.push("{");
- }
- else if (type == "}") state.stack.pop();
- else if (type == "@media") state.stack.push("@media");
- else if (context == "{" && type != "comment") state.stack.push("rule");
- return style;
- },
-
- indent: function(state, textAfter) {
- var n = state.stack.length;
- if (/^\}/.test(textAfter))
- n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
- return state.baseIndent + n * indentUnit;
- },
-
- electricChars: "}"
- };
-});
-
-CodeMirror.defineMIME("text/css", "css");
-CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
- var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
- var jsMode = CodeMirror.getMode(config, "javascript");
- var cssMode = CodeMirror.getMode(config, "css");
-
- function html(stream, state) {
- var style = htmlMode.token(stream, state.htmlState);
- if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
- if (/^script$/i.test(state.htmlState.context.tagName)) {
- state.token = javascript;
- state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
- state.mode = "javascript";
- }
- else if (/^style$/i.test(state.htmlState.context.tagName)) {
- state.token = css;
- state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
- state.mode = "css";
- }
- }
- return style;
- }
- function maybeBackup(stream, pat, style) {
- var cur = stream.current();
- var close = cur.search(pat);
- if (close > -1) stream.backUp(cur.length - close);
- return style;
- }
- function javascript(stream, state) {
- if (stream.match(/^<\/\s*script\s*>/i, false)) {
- state.token = html;
- state.curState = null;
- state.mode = "html";
- return html(stream, state);
- }
- return maybeBackup(stream, /<\/\s*script\s*>/,
- jsMode.token(stream, state.localState));
- }
- function css(stream, state) {
- if (stream.match(/^<\/\s*style\s*>/i, false)) {
- state.token = html;
- state.localState = null;
- state.mode = "html";
- return html(stream, state);
- }
- return maybeBackup(stream, /<\/\s*style\s*>/,
- cssMode.token(stream, state.localState));
- }
-
- return {
- startState: function() {
- var state = htmlMode.startState();
- return {token: html, localState: null, mode: "html", htmlState: state};
- },
-
- copyState: function(state) {
- if (state.localState)
- var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
- return {token: state.token, localState: local, mode: state.mode,
- htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
- },
-
- token: function(stream, state) {
- return state.token(stream, state);
- },
-
- indent: function(state, textAfter) {
- if (state.token == html || /^\s*<\//.test(textAfter))
- return htmlMode.indent(state.htmlState, textAfter);
- else if (state.token == javascript)
- return jsMode.indent(state.localState, textAfter);
- else
- return cssMode.indent(state.localState, textAfter);
- },
-
- compareStates: function(a, b) {
- return htmlMode.compareStates(a.htmlState, b.htmlState);
- },
-
- electricChars: "/{}:"
- }
-});
-
-CodeMirror.defineMIME("text/html", "htmlmixed");
+// CodeMirror version 2.2
+//
+// All functions that need access to the editor's state live inside
+// the CodeMirror function. Below that, at the bottom of the file,
+// some utilities are defined.
+
+// CodeMirror is the only global var we claim
+var CodeMirror = (function() {
+ // This is the function that produces an editor instance. It's
+ // closure is used to store the editor state.
+ function CodeMirror(place, givenOptions) {
+ // Determine effective options based on given values and defaults.
+ var options = {}, defaults = CodeMirror.defaults;
+ for (var opt in defaults)
+ if (defaults.hasOwnProperty(opt))
+ options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
+
+ var targetDocument = options["document"];
+ // The element in which the editor lives.
+ var wrapper = targetDocument.createElement("div");
+ wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
+ // This mess creates the base DOM structure for the editor.
+ wrapper.innerHTML =
+ '' + // Wraps and hides input textarea
+ '
' +
+ '';
+ if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
+ // I've never seen more elegant code in my life.
+ var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
+ scroller = wrapper.lastChild, code = scroller.firstChild,
+ mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
+ lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
+ cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
+ themeChanged();
+ // Needed to hide big blue blinking cursor on Mobile Safari
+ if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
+ if (!webkit) lineSpace.draggable = true;
+ if (options.tabindex != null) input.tabIndex = options.tabindex;
+ if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
+
+ // Check for problem with IE innerHTML not working when we have a
+ // P (or similar) parent node.
+ try { stringWidth("x"); }
+ catch (e) {
+ if (e.message.match(/runtime/i))
+ e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
+ throw e;
+ }
+
+ // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
+ var poll = new Delayed(), highlight = new Delayed(), blinker;
+
+ // mode holds a mode API object. doc is the tree of Line objects,
+ // work an array of lines that should be parsed, and history the
+ // undo history (instance of History constructor).
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
+ loadMode();
+ // The selection. These are always maintained to point at valid
+ // positions. Inverted is used to remember that the user is
+ // selecting bottom-to-top.
+ var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
+ // Selection-related flags. shiftSelecting obviously tracks
+ // whether the user is holding shift.
+ var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
+ // Variables used by startOperation/endOperation to track what
+ // happened during the operation.
+ var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
+ gutterDirty, callbacks;
+ // Current visible range (may be bigger than the view window).
+ var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
+ // bracketHighlighted is used to remember that a backet has been
+ // marked.
+ var bracketHighlighted;
+ // Tracks the maximum line length so that the horizontal scrollbar
+ // can be kept static when scrolling.
+ var maxLine = "", maxWidth, tabText = computeTabText();
+
+ // Initialize the content.
+ operation(function(){setValue(options.value || ""); updateInput = false;})();
+ var history = new History();
+
+ // Register our event handlers.
+ connect(scroller, "mousedown", operation(onMouseDown));
+ connect(scroller, "dblclick", operation(onDoubleClick));
+ connect(lineSpace, "dragstart", onDragStart);
+ connect(lineSpace, "selectstart", e_preventDefault);
+ // Gecko browsers fire contextmenu *after* opening the menu, at
+ // which point we can't mess with it anymore. Context menu is
+ // handled in onMouseDown for Gecko.
+ if (!gecko) connect(scroller, "contextmenu", onContextMenu);
+ connect(scroller, "scroll", function() {
+ updateDisplay([]);
+ if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
+ if (options.onScroll) options.onScroll(instance);
+ });
+ connect(window, "resize", function() {updateDisplay(true);});
+ connect(input, "keyup", operation(onKeyUp));
+ connect(input, "input", fastPoll);
+ connect(input, "keydown", operation(onKeyDown));
+ connect(input, "keypress", operation(onKeyPress));
+ connect(input, "focus", onFocus);
+ connect(input, "blur", onBlur);
+
+ connect(scroller, "dragenter", e_stop);
+ connect(scroller, "dragover", e_stop);
+ connect(scroller, "drop", operation(onDrop));
+ connect(scroller, "paste", function(){focusInput(); fastPoll();});
+ connect(input, "paste", fastPoll);
+ connect(input, "cut", operation(function(){replaceSelection("");}));
+
+ // IE throws unspecified error in certain cases, when
+ // trying to access activeElement before onload
+ var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
+ if (hasFocus) setTimeout(onFocus, 20);
+ else onBlur();
+
+ function isLine(l) {return l >= 0 && l < doc.size;}
+ // The instance object that we'll return. Mostly calls out to
+ // local functions in the CodeMirror function. Some do some extra
+ // range checking and/or clipping. operation is used to wrap the
+ // call so that changes it makes are tracked, and the display is
+ // updated afterwards.
+ var instance = wrapper.CodeMirror = {
+ getValue: getValue,
+ setValue: operation(setValue),
+ getSelection: getSelection,
+ replaceSelection: operation(replaceSelection),
+ focus: function(){focusInput(); onFocus(); fastPoll();},
+ setOption: function(option, value) {
+ var oldVal = options[option];
+ options[option] = value;
+ if (option == "mode" || option == "indentUnit") loadMode();
+ else if (option == "readOnly" && value) {onBlur(); input.blur();}
+ else if (option == "theme") themeChanged();
+ else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
+ else if (option == "tabSize") operation(tabsChanged)();
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
+ operation(gutterChanged)();
+ },
+ getOption: function(option) {return options[option];},
+ undo: operation(undo),
+ redo: operation(redo),
+ indentLine: operation(function(n, dir) {
+ if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
+ }),
+ indentSelection: operation(indentSelected),
+ historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
+ clearHistory: function() {history = new History();},
+ matchBrackets: operation(function(){matchBrackets(true);}),
+ getTokenAt: operation(function(pos) {
+ pos = clipPos(pos);
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
+ }),
+ getStateAfter: function(line) {
+ line = clipLine(line == null ? doc.size - 1: line);
+ return getStateBefore(line + 1);
+ },
+ cursorCoords: function(start){
+ if (start == null) start = sel.inverted;
+ return pageCoords(start ? sel.from : sel.to);
+ },
+ charCoords: function(pos){return pageCoords(clipPos(pos));},
+ coordsChar: function(coords) {
+ var off = eltOffset(lineSpace);
+ return coordsChar(coords.x - off.left, coords.y - off.top);
+ },
+ markText: operation(markText),
+ setBookmark: setBookmark,
+ setMarker: operation(addGutterMarker),
+ clearMarker: operation(removeGutterMarker),
+ setLineClass: operation(setLineClass),
+ hideLine: operation(function(h) {return setLineHidden(h, true);}),
+ showLine: operation(function(h) {return setLineHidden(h, false);}),
+ onDeleteLine: function(line, f) {
+ if (typeof line == "number") {
+ if (!isLine(line)) return null;
+ line = getLine(line);
+ }
+ (line.handlers || (line.handlers = [])).push(f);
+ return line;
+ },
+ lineInfo: lineInfo,
+ addWidget: function(pos, node, scroll, vert, horiz) {
+ pos = localCoords(clipPos(pos));
+ var top = pos.yBot, left = pos.x;
+ node.style.position = "absolute";
+ code.appendChild(node);
+ if (vert == "over") top = pos.y;
+ else if (vert == "near") {
+ var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
+ hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
+ if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
+ top = pos.y - node.offsetHeight;
+ if (left + node.offsetWidth > hspace)
+ left = hspace - node.offsetWidth;
+ }
+ node.style.top = (top + paddingTop()) + "px";
+ node.style.left = node.style.right = "";
+ if (horiz == "right") {
+ left = code.clientWidth - node.offsetWidth;
+ node.style.right = "0px";
+ } else {
+ if (horiz == "left") left = 0;
+ else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
+ node.style.left = (left + paddingLeft()) + "px";
+ }
+ if (scroll)
+ scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
+ },
+
+ lineCount: function() {return doc.size;},
+ clipPos: clipPos,
+ getCursor: function(start) {
+ if (start == null) start = sel.inverted;
+ return copyPos(start ? sel.from : sel.to);
+ },
+ somethingSelected: function() {return !posEq(sel.from, sel.to);},
+ setCursor: operation(function(line, ch, user) {
+ if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
+ else setCursor(line, ch, user);
+ }),
+ setSelection: operation(function(from, to, user) {
+ (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
+ }),
+ getLine: function(line) {if (isLine(line)) return getLine(line).text;},
+ getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
+ setLine: operation(function(line, text) {
+ if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
+ }),
+ removeLine: operation(function(line) {
+ if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
+ }),
+ replaceRange: operation(replaceRange),
+ getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
+
+ execCommand: function(cmd) {return commands[cmd](instance);},
+ // Stuff used by commands, probably not much use to outside code.
+ moveH: operation(moveH),
+ deleteH: operation(deleteH),
+ moveV: operation(moveV),
+ toggleOverwrite: function() {overwrite = !overwrite;},
+
+ posFromIndex: function(off) {
+ var lineNo = 0, ch;
+ doc.iter(0, doc.size, function(line) {
+ var sz = line.text.length + 1;
+ if (sz > off) { ch = off; return true; }
+ off -= sz;
+ ++lineNo;
+ });
+ return clipPos({line: lineNo, ch: ch});
+ },
+ indexFromPos: function (coords) {
+ if (coords.line < 0 || coords.ch < 0) return 0;
+ var index = coords.ch;
+ doc.iter(0, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+
+ operation: function(f){return operation(f)();},
+ refresh: function(){updateDisplay(true);},
+ getInputField: function(){return input;},
+ getWrapperElement: function(){return wrapper;},
+ getScrollerElement: function(){return scroller;},
+ getGutterElement: function(){return gutter;}
+ };
+
+ function getLine(n) { return getLineAt(doc, n); }
+ function updateLineHeight(line, height) {
+ gutterDirty = true;
+ var diff = height - line.height;
+ for (var n = line; n; n = n.parent) n.height += diff;
+ }
+
+ function setValue(code) {
+ var top = {line: 0, ch: 0};
+ updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
+ splitLines(code), top, top);
+ updateInput = true;
+ }
+ function getValue(code) {
+ var text = [];
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
+ return text.join("\n");
+ }
+
+ function onMouseDown(e) {
+ setShift(e.shiftKey);
+ // Check whether this is a click in a widget
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
+ if (n.parentNode == code && n != mover) return;
+
+ // See if this is a click in the gutter
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
+ if (n.parentNode == gutterText) {
+ if (options.onGutterClick)
+ options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
+ return e_preventDefault(e);
+ }
+
+ var start = posFromMouse(e);
+
+ switch (e_button(e)) {
+ case 3:
+ if (gecko && !mac) onContextMenu(e);
+ return;
+ case 2:
+ if (start) setCursor(start.line, start.ch, true);
+ return;
+ }
+ // For button 1, if it was clicked inside the editor
+ // (posFromMouse returning non-null), we have to adjust the
+ // selection.
+ if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
+
+ if (!focused) onFocus();
+
+ var now = +new Date;
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
+ e_preventDefault(e);
+ setTimeout(focusInput, 20);
+ return selectLine(start.line);
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
+ lastDoubleClick = {time: now, pos: start};
+ e_preventDefault(e);
+ return selectWordAt(start);
+ } else { lastClick = {time: now, pos: start}; }
+
+ var last = start, going;
+ if (dragAndDrop && !posEq(sel.from, sel.to) &&
+ !posLess(start, sel.from) && !posLess(sel.to, start)) {
+ // Let the drag handler handle this.
+ if (webkit) lineSpace.draggable = true;
+ var up = connect(targetDocument, "mouseup", operation(function(e2) {
+ if (webkit) lineSpace.draggable = false;
+ draggingText = false;
+ up();
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+ e_preventDefault(e2);
+ setCursor(start.line, start.ch, true);
+ focusInput();
+ }
+ }), true);
+ draggingText = true;
+ return;
+ }
+ e_preventDefault(e);
+ setCursor(start.line, start.ch, true);
+
+ function extend(e) {
+ var cur = posFromMouse(e, true);
+ if (cur && !posEq(cur, last)) {
+ if (!focused) onFocus();
+ last = cur;
+ setSelectionUser(start, cur);
+ updateInput = false;
+ var visible = visibleLines();
+ if (cur.line >= visible.to || cur.line < visible.from)
+ going = setTimeout(operation(function(){extend(e);}), 150);
+ }
+ }
+
+ var move = connect(targetDocument, "mousemove", operation(function(e) {
+ clearTimeout(going);
+ e_preventDefault(e);
+ extend(e);
+ }), true);
+ var up = connect(targetDocument, "mouseup", operation(function(e) {
+ clearTimeout(going);
+ var cur = posFromMouse(e);
+ if (cur) setSelectionUser(start, cur);
+ e_preventDefault(e);
+ focusInput();
+ updateInput = true;
+ move(); up();
+ }), true);
+ }
+ function onDoubleClick(e) {
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
+ if (n.parentNode == gutterText) return e_preventDefault(e);
+ var start = posFromMouse(e);
+ if (!start) return;
+ lastDoubleClick = {time: +new Date, pos: start};
+ e_preventDefault(e);
+ selectWordAt(start);
+ }
+ function onDrop(e) {
+ e.preventDefault();
+ var pos = posFromMouse(e, true), files = e.dataTransfer.files;
+ if (!pos || options.readOnly) return;
+ if (files && files.length && window.FileReader && window.File) {
+ function loadFile(file, i) {
+ var reader = new FileReader;
+ reader.onload = function() {
+ text[i] = reader.result;
+ if (++read == n) {
+ pos = clipPos(pos);
+ operation(function() {
+ var end = replaceRange(text.join(""), pos, pos);
+ setSelectionUser(pos, end);
+ })();
+ }
+ };
+ reader.readAsText(file);
+ }
+ var n = files.length, text = Array(n), read = 0;
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
+ }
+ else {
+ try {
+ var text = e.dataTransfer.getData("Text");
+ if (text) {
+ var end = replaceRange(text, pos, pos);
+ var curFrom = sel.from, curTo = sel.to;
+ setSelectionUser(pos, end);
+ if (draggingText) replaceRange("", curFrom, curTo);
+ focusInput();
+ }
+ }
+ catch(e){}
+ }
+ }
+ function onDragStart(e) {
+ var txt = getSelection();
+ // This will reset escapeElement
+ htmlEscape(txt);
+ e.dataTransfer.setDragImage(escapeElement, 0, 0);
+ e.dataTransfer.setData("Text", txt);
+ }
+ function handleKeyBinding(e) {
+ var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
+ if (name == null || e.altGraphKey) {
+ if (next) options.keyMap = next;
+ return null;
+ }
+ if (e.altKey) name = "Alt-" + name;
+ if (e.ctrlKey) name = "Ctrl-" + name;
+ if (e.metaKey) name = "Cmd-" + name;
+ if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
+ dropShift = true;
+ } else {
+ bound = lookupKey(name, options.extraKeys, options.keyMap);
+ }
+ if (typeof bound == "string") {
+ if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
+ else bound = null;
+ }
+ if (next && (bound || !isModifierKey(e))) options.keyMap = next;
+ if (!bound) return false;
+ if (dropShift) {
+ var prevShift = shiftSelecting;
+ shiftSelecting = null;
+ bound(instance);
+ shiftSelecting = prevShift;
+ } else bound(instance);
+ e_preventDefault(e);
+ return true;
+ }
+ var lastStoppedKey = null;
+ function onKeyDown(e) {
+ if (!focused) onFocus();
+ var code = e.keyCode;
+ // IE does strange things with escape.
+ if (ie && code == 27) { e.returnValue = false; }
+ setShift(code == 16 || e.shiftKey);
+ // First give onKeyEvent option a chance to handle this.
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
+ var handled = handleKeyBinding(e);
+ if (window.opera) {
+ lastStoppedKey = handled ? e.keyCode : null;
+ // Opera has no cut event... we try to at least catch the key combo
+ if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
+ replaceSelection("");
+ }
+ }
+ function onKeyPress(e) {
+ if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
+ if (window.opera && !e.which && handleKeyBinding(e)) return;
+ if (options.electricChars && mode.electricChars) {
+ var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
+ if (mode.electricChars.indexOf(ch) > -1)
+ setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
+ }
+ fastPoll();
+ }
+ function onKeyUp(e) {
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
+ if (e.keyCode == 16) shiftSelecting = null;
+ }
+
+ function onFocus() {
+ if (options.readOnly) return;
+ if (!focused) {
+ if (options.onFocus) options.onFocus(instance);
+ focused = true;
+ if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
+ wrapper.className += " CodeMirror-focused";
+ if (!leaveInputAlone) resetInput(true);
+ }
+ slowPoll();
+ restartBlink();
+ }
+ function onBlur() {
+ if (focused) {
+ if (options.onBlur) options.onBlur(instance);
+ focused = false;
+ wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
+ }
+ clearInterval(blinker);
+ setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
+ }
+
+ // Replace the range from from to to by the strings in newText.
+ // Afterwards, set the selection to selFrom, selTo.
+ function updateLines(from, to, newText, selFrom, selTo) {
+ if (history) {
+ var old = [];
+ doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
+ history.addChange(from.line, newText.length, old);
+ while (history.done.length > options.undoDepth) history.done.shift();
+ }
+ updateLinesNoUndo(from, to, newText, selFrom, selTo);
+ }
+ function unredoHelper(from, to) {
+ var change = from.pop();
+ if (change) {
+ var replaced = [], end = change.start + change.added;
+ doc.iter(change.start, end, function(line) { replaced.push(line.text); });
+ to.push({start: change.start, added: change.old.length, old: replaced});
+ var pos = clipPos({line: change.start + change.old.length - 1,
+ ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
+ updateInput = true;
+ }
+ }
+ function undo() {unredoHelper(history.done, history.undone);}
+ function redo() {unredoHelper(history.undone, history.done);}
+
+ function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
+ var recomputeMaxLength = false, maxLineLength = maxLine.length;
+ if (!options.lineWrapping)
+ doc.iter(from.line, to.line, function(line) {
+ if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
+ });
+ if (from.line != to.line || newText.length > 1) gutterDirty = true;
+
+ var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
+ // First adjust the line structure, taking some care to leave highlighting intact.
+ if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ var added = [], prevLine = null;
+ if (from.line) {
+ prevLine = getLine(from.line - 1);
+ prevLine.fixMarkEnds(lastLine);
+ } else lastLine.fixMarkStarts();
+ for (var i = 0, e = newText.length - 1; i < e; ++i)
+ added.push(Line.inheritMarks(newText[i], prevLine));
+ if (nlines) doc.remove(from.line, nlines, callbacks);
+ if (added.length) doc.insert(from.line, added);
+ } else if (firstLine == lastLine) {
+ if (newText.length == 1)
+ firstLine.replace(from.ch, to.ch, newText[0]);
+ else {
+ lastLine = firstLine.split(to.ch, newText[newText.length-1]);
+ firstLine.replace(from.ch, null, newText[0]);
+ firstLine.fixMarkEnds(lastLine);
+ var added = [];
+ for (var i = 1, e = newText.length - 1; i < e; ++i)
+ added.push(Line.inheritMarks(newText[i], firstLine));
+ added.push(lastLine);
+ doc.insert(from.line + 1, added);
+ }
+ } else if (newText.length == 1) {
+ firstLine.replace(from.ch, null, newText[0]);
+ lastLine.replace(null, to.ch, "");
+ firstLine.append(lastLine);
+ doc.remove(from.line + 1, nlines, callbacks);
+ } else {
+ var added = [];
+ firstLine.replace(from.ch, null, newText[0]);
+ lastLine.replace(null, to.ch, newText[newText.length-1]);
+ firstLine.fixMarkEnds(lastLine);
+ for (var i = 1, e = newText.length - 1; i < e; ++i)
+ added.push(Line.inheritMarks(newText[i], firstLine));
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
+ doc.insert(from.line + 1, added);
+ }
+ if (options.lineWrapping) {
+ var perLine = scroller.clientWidth / charWidth() - 3;
+ doc.iter(from.line, from.line + newText.length, function(line) {
+ if (line.hidden) return;
+ var guess = Math.ceil(line.text.length / perLine) || 1;
+ if (guess != line.height) updateLineHeight(line, guess);
+ });
+ } else {
+ doc.iter(from.line, i + newText.length, function(line) {
+ var l = line.text;
+ if (l.length > maxLineLength) {
+ maxLine = l; maxLineLength = l.length; maxWidth = null;
+ recomputeMaxLength = false;
+ }
+ });
+ if (recomputeMaxLength) {
+ maxLineLength = 0; maxLine = ""; maxWidth = null;
+ doc.iter(0, doc.size, function(line) {
+ var l = line.text;
+ if (l.length > maxLineLength) {
+ maxLineLength = l.length; maxLine = l;
+ }
+ });
+ }
+ }
+
+ // Add these lines to the work array, so that they will be
+ // highlighted. Adjust work lines if lines were added/removed.
+ var newWork = [], lendiff = newText.length - nlines - 1;
+ for (var i = 0, l = work.length; i < l; ++i) {
+ var task = work[i];
+ if (task < from.line) newWork.push(task);
+ else if (task > to.line) newWork.push(task + lendiff);
+ }
+ var hlEnd = from.line + Math.min(newText.length, 500);
+ highlightLines(from.line, hlEnd);
+ newWork.push(hlEnd);
+ work = newWork;
+ startWorker(100);
+ // Remember that these lines changed, for updating the display
+ changes.push({from: from.line, to: to.line + 1, diff: lendiff});
+ var changeObj = {from: from, to: to, text: newText};
+ if (textChanged) {
+ for (var cur = textChanged; cur.next; cur = cur.next) {}
+ cur.next = changeObj;
+ } else textChanged = changeObj;
+
+ // Update the selection
+ function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
+ setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
+
+ // Make sure the scroll-size div has the correct height.
+ code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
+ }
+
+ function replaceRange(code, from, to) {
+ from = clipPos(from);
+ if (!to) to = from; else to = clipPos(to);
+ code = splitLines(code);
+ function adjustPos(pos) {
+ if (posLess(pos, from)) return pos;
+ if (!posLess(to, pos)) return end;
+ var line = pos.line + code.length - (to.line - from.line) - 1;
+ var ch = pos.ch;
+ if (pos.line == to.line)
+ ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
+ return {line: line, ch: ch};
+ }
+ var end;
+ replaceRange1(code, from, to, function(end1) {
+ end = end1;
+ return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
+ });
+ return end;
+ }
+ function replaceSelection(code, collapse) {
+ replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
+ if (collapse == "end") return {from: end, to: end};
+ else if (collapse == "start") return {from: sel.from, to: sel.from};
+ else return {from: sel.from, to: end};
+ });
+ }
+ function replaceRange1(code, from, to, computeSel) {
+ var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
+ var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
+ updateLines(from, to, code, newSel.from, newSel.to);
+ }
+
+ function getRange(from, to) {
+ var l1 = from.line, l2 = to.line;
+ if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
+ var code = [getLine(l1).text.slice(from.ch)];
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
+ code.push(getLine(l2).text.slice(0, to.ch));
+ return code.join("\n");
+ }
+ function getSelection() {
+ return getRange(sel.from, sel.to);
+ }
+
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
+ function slowPoll() {
+ if (pollingFast) return;
+ poll.set(options.pollInterval, function() {
+ startOperation();
+ readInput();
+ if (focused) slowPoll();
+ endOperation();
+ });
+ }
+ function fastPoll() {
+ var missed = false;
+ pollingFast = true;
+ function p() {
+ startOperation();
+ var changed = readInput();
+ if (!changed && !missed) {missed = true; poll.set(60, p);}
+ else {pollingFast = false; slowPoll();}
+ endOperation();
+ }
+ poll.set(20, p);
+ }
+
+ // Previnput is a hack to work with IME. If we reset the textarea
+ // on every change, that breaks IME. So we look for changes
+ // compared to the previous content instead. (Modern browsers have
+ // events that indicate IME taking place, but these are not widely
+ // supported or compatible enough yet to rely on.)
+ var prevInput = "";
+ function readInput() {
+ if (leaveInputAlone || !focused || hasSelection(input)) return false;
+ var text = input.value;
+ if (text == prevInput) return false;
+ shiftSelecting = null;
+ var same = 0, l = Math.min(prevInput.length, text.length);
+ while (same < l && prevInput[same] == text[same]) ++same;
+ if (same < prevInput.length)
+ sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
+ else if (overwrite && posEq(sel.from, sel.to))
+ sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
+ replaceSelection(text.slice(same), "end");
+ prevInput = text;
+ return true;
+ }
+ function resetInput(user) {
+ if (!posEq(sel.from, sel.to)) {
+ prevInput = "";
+ input.value = getSelection();
+ input.select();
+ } else if (user) prevInput = input.value = "";
+ }
+
+ function focusInput() {
+ if (!options.readOnly) input.focus();
+ }
+
+ function scrollEditorIntoView() {
+ if (!cursor.getBoundingClientRect) return;
+ var rect = cursor.getBoundingClientRect();
+ // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
+ if (ie && rect.top == rect.bottom) return;
+ var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+ if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
+ }
+ function scrollCursorIntoView() {
+ var cursor = localCoords(sel.inverted ? sel.from : sel.to);
+ var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
+ return scrollIntoView(x, cursor.y, x, cursor.yBot);
+ }
+ function scrollIntoView(x1, y1, x2, y2) {
+ var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
+ y1 += pt; y2 += pt; x1 += pl; x2 += pl;
+ var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
+ if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
+ else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
+
+ var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
+ var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
+ if (x1 < screenleft + gutterw) {
+ if (x1 < 50) x1 = 0;
+ scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
+ scrolled = true;
+ }
+ else if (x2 > screenw + screenleft - 3) {
+ scroller.scrollLeft = x2 + 10 - screenw;
+ scrolled = true;
+ if (x2 > code.clientWidth) result = false;
+ }
+ if (scrolled && options.onScroll) options.onScroll(instance);
+ return result;
+ }
+
+ function visibleLines() {
+ var lh = textHeight(), top = scroller.scrollTop - paddingTop();
+ var from_height = Math.max(0, Math.floor(top / lh));
+ var to_height = Math.ceil((top + scroller.clientHeight) / lh);
+ return {from: lineAtHeight(doc, from_height),
+ to: lineAtHeight(doc, to_height)};
+ }
+ // Uses a set of changes plus the current scroll position to
+ // determine which DOM updates have to be made, and makes the
+ // updates.
+ function updateDisplay(changes, suppressCallback) {
+ if (!scroller.clientWidth) {
+ showingFrom = showingTo = displayOffset = 0;
+ return;
+ }
+ // Compute the new visible window
+ var visible = visibleLines();
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
+ var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
+ if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
+ if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
+
+ // Create a range of theoretically intact lines, and punch holes
+ // in that using the change info.
+ var intact = changes === true ? [] :
+ computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
+ // Clip off the parts that won't be visible
+ var intactLines = 0;
+ for (var i = 0; i < intact.length; ++i) {
+ var range = intact[i];
+ if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
+ if (range.to > to) range.to = to;
+ if (range.from >= range.to) intact.splice(i--, 1);
+ else intactLines += range.to - range.from;
+ }
+ if (intactLines == to - from) return;
+ intact.sort(function(a, b) {return a.domStart - b.domStart;});
+
+ var th = textHeight(), gutterDisplay = gutter.style.display;
+ lineDiv.style.display = gutter.style.display = "none";
+ patchDisplay(from, to, intact);
+ lineDiv.style.display = "";
+
+ // Position the mover div to align with the lines it's supposed
+ // to be showing (which will cover the visible display)
+ var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
+ // This is just a bogus formula that detects when the editor is
+ // resized or the font size changes.
+ if (different) lastSizeC = scroller.clientHeight + th;
+ showingFrom = from; showingTo = to;
+ displayOffset = heightAtLine(doc, from);
+ mover.style.top = (displayOffset * th) + "px";
+ code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
+
+ // Since this is all rather error prone, it is honoured with the
+ // only assertion in the whole file.
+ if (lineDiv.childNodes.length != showingTo - showingFrom)
+ throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
+ " nodes=" + lineDiv.childNodes.length);
+
+ if (options.lineWrapping) {
+ maxWidth = scroller.clientWidth;
+ var curNode = lineDiv.firstChild;
+ doc.iter(showingFrom, showingTo, function(line) {
+ if (!line.hidden) {
+ var height = Math.round(curNode.offsetHeight / th) || 1;
+ if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
+ }
+ curNode = curNode.nextSibling;
+ });
+ } else {
+ if (maxWidth == null) maxWidth = stringWidth(maxLine);
+ if (maxWidth > scroller.clientWidth) {
+ lineSpace.style.width = maxWidth + "px";
+ // Needed to prevent odd wrapping/hiding of widgets placed in here.
+ code.style.width = "";
+ code.style.width = scroller.scrollWidth + "px";
+ } else {
+ lineSpace.style.width = code.style.width = "";
+ }
+ }
+ gutter.style.display = gutterDisplay;
+ if (different || gutterDirty) updateGutter();
+ updateCursor();
+ if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
+ return true;
+ }
+
+ function computeIntact(intact, changes) {
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
+ var range = intact[j];
+ if (change.to <= range.from && change.diff)
+ intact2.push({from: range.from + diff, to: range.to + diff,
+ domStart: range.domStart});
+ else if (change.to <= range.from || change.from >= range.to)
+ intact2.push(range);
+ else {
+ if (change.from > range.from)
+ intact2.push({from: range.from, to: change.from, domStart: range.domStart});
+ if (change.to < range.to)
+ intact2.push({from: change.to + diff, to: range.to + diff,
+ domStart: range.domStart + (change.to - range.from)});
+ }
+ }
+ intact = intact2;
+ }
+ return intact;
+ }
+
+ function patchDisplay(from, to, intact) {
+ // The first pass removes the DOM nodes that aren't intact.
+ if (!intact.length) lineDiv.innerHTML = "";
+ else {
+ function killNode(node) {
+ var tmp = node.nextSibling;
+ node.parentNode.removeChild(node);
+ return tmp;
+ }
+ var domPos = 0, curNode = lineDiv.firstChild, n;
+ for (var i = 0; i < intact.length; ++i) {
+ var cur = intact[i];
+ while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
+ for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
+ }
+ while (curNode) curNode = killNode(curNode);
+ }
+ // This pass fills in the lines that actually changed.
+ var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
+ var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
+ var scratch = targetDocument.createElement("div"), newElt;
+ doc.iter(from, to, function(line) {
+ var ch1 = null, ch2 = null;
+ if (inSel) {
+ ch1 = 0;
+ if (sto == j) {inSel = false; ch2 = sel.to.ch;}
+ } else if (sfrom == j) {
+ if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
+ else {inSel = true; ch1 = sel.from.ch;}
+ }
+ if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
+ if (!nextIntact || nextIntact.from > j) {
+ if (line.hidden) scratch.innerHTML = "";
+ else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
+ lineDiv.insertBefore(scratch.firstChild, curNode);
+ } else {
+ curNode = curNode.nextSibling;
+ }
+ ++j;
+ });
+ }
+
+ function updateGutter() {
+ if (!options.gutter && !options.lineNumbers) return;
+ var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
+ gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
+ var html = [], i = showingFrom;
+ doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
+ if (line.hidden) {
+ html.push("");
+ } else {
+ var marker = line.gutterMarker;
+ var text = options.lineNumbers ? i + options.firstLineNumber : null;
+ if (marker && marker.text)
+ text = marker.text.replace("%N%", text != null ? text : "");
+ else if (text == null)
+ text = "\u00a0";
+ html.push((marker && marker.style ? '' : ""), text);
+ for (var j = 1; j < line.height; ++j) html.push("
");
+ html.push("
");
+ }
+ ++i;
+ });
+ gutter.style.display = "none";
+ gutterText.innerHTML = html.join("");
+ var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
+ while (val.length + pad.length < minwidth) pad += "\u00a0";
+ if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
+ gutter.style.display = "";
+ lineSpace.style.marginLeft = gutter.offsetWidth + "px";
+ gutterDirty = false;
+ }
+ function updateCursor() {
+ var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
+ var pos = localCoords(head, true);
+ var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
+ inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
+ inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
+ if (posEq(sel.from, sel.to)) {
+ cursor.style.top = pos.y + "px";
+ cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
+ cursor.style.display = "";
+ }
+ else cursor.style.display = "none";
+ }
+
+ function setShift(val) {
+ if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
+ else shiftSelecting = null;
+ }
+ function setSelectionUser(from, to) {
+ var sh = shiftSelecting && clipPos(shiftSelecting);
+ if (sh) {
+ if (posLess(sh, from)) from = sh;
+ else if (posLess(to, sh)) to = sh;
+ }
+ setSelection(from, to);
+ userSelChange = true;
+ }
+ // Update the selection. Last two args are only used by
+ // updateLines, since they have to be expressed in the line
+ // numbers before the update.
+ function setSelection(from, to, oldFrom, oldTo) {
+ goalColumn = null;
+ if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
+ if (posEq(sel.from, from) && posEq(sel.to, to)) return;
+ if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
+
+ // Skip over hidden lines.
+ if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
+ if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
+
+ if (posEq(from, to)) sel.inverted = false;
+ else if (posEq(from, sel.to)) sel.inverted = false;
+ else if (posEq(to, sel.from)) sel.inverted = true;
+
+ // Some ugly logic used to only mark the lines that actually did
+ // see a change in selection as changed, rather than the whole
+ // selected range.
+ if (posEq(from, to)) {
+ if (!posEq(sel.from, sel.to))
+ changes.push({from: oldFrom, to: oldTo + 1});
+ }
+ else if (posEq(sel.from, sel.to)) {
+ changes.push({from: from.line, to: to.line + 1});
+ }
+ else {
+ if (!posEq(from, sel.from)) {
+ if (from.line < oldFrom)
+ changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
+ else
+ changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
+ }
+ if (!posEq(to, sel.to)) {
+ if (to.line < oldTo)
+ changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
+ else
+ changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
+ }
+ }
+ sel.from = from; sel.to = to;
+ selectionChanged = true;
+ }
+ function skipHidden(pos, oldLine, oldCh) {
+ function getNonHidden(dir) {
+ var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
+ while (lNo != end) {
+ var line = getLine(lNo);
+ if (!line.hidden) {
+ var ch = pos.ch;
+ if (ch > oldCh || ch > line.text.length) ch = line.text.length;
+ return {line: lNo, ch: ch};
+ }
+ lNo += dir;
+ }
+ }
+ var line = getLine(pos.line);
+ if (!line.hidden) return pos;
+ if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
+ else return getNonHidden(-1) || getNonHidden(1);
+ }
+ function setCursor(line, ch, user) {
+ var pos = clipPos({line: line, ch: ch || 0});
+ (user ? setSelectionUser : setSelection)(pos, pos);
+ }
+
+ function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
+ function clipPos(pos) {
+ if (pos.line < 0) return {line: 0, ch: 0};
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
+ var ch = pos.ch, linelen = getLine(pos.line).text.length;
+ if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
+ else if (ch < 0) return {line: pos.line, ch: 0};
+ else return pos;
+ }
+
+ function findPosH(dir, unit) {
+ var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
+ var lineObj = getLine(line);
+ function findNextLine() {
+ for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
+ var lo = getLine(l);
+ if (!lo.hidden) { line = l; lineObj = lo; return true; }
+ }
+ }
+ function moveOnce(boundToLine) {
+ if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
+ if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
+ else return false;
+ } else ch += dir;
+ return true;
+ }
+ if (unit == "char") moveOnce();
+ else if (unit == "column") moveOnce(true);
+ else if (unit == "word") {
+ var sawWord = false;
+ for (;;) {
+ if (dir < 0) if (!moveOnce()) break;
+ if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
+ else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
+ if (dir > 0) if (!moveOnce()) break;
+ }
+ }
+ return {line: line, ch: ch};
+ }
+ function moveH(dir, unit) {
+ var pos = dir < 0 ? sel.from : sel.to;
+ if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
+ setCursor(pos.line, pos.ch, true);
+ }
+ function deleteH(dir, unit) {
+ if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
+ else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
+ else replaceRange("", sel.from, findPosH(dir, unit));
+ userSelChange = true;
+ }
+ var goalColumn = null;
+ function moveV(dir, unit) {
+ var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
+ if (goalColumn != null) pos.x = goalColumn;
+ if (unit == "page") dist = scroller.clientHeight;
+ else if (unit == "line") dist = textHeight();
+ var target = coordsChar(pos.x, pos.y + dist * dir + 2);
+ setCursor(target.line, target.ch, true);
+ goalColumn = pos.x;
+ }
+
+ function selectWordAt(pos) {
+ var line = getLine(pos.line).text;
+ var start = pos.ch, end = pos.ch;
+ while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
+ while (end < line.length && isWordChar(line.charAt(end))) ++end;
+ setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
+ }
+ function selectLine(line) {
+ setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
+ }
+ function indentSelected(mode) {
+ if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
+ for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
+ }
+
+ function indentLine(n, how) {
+ if (!how) how = "add";
+ if (how == "smart") {
+ if (!mode.indent) how = "prev";
+ else var state = getStateBefore(n);
+ }
+
+ var line = getLine(n), curSpace = line.indentation(options.tabSize),
+ curSpaceString = line.text.match(/^\s*/)[0], indentation;
+ if (how == "prev") {
+ if (n) indentation = getLine(n-1).indentation(options.tabSize);
+ else indentation = 0;
+ }
+ else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ else if (how == "add") indentation = curSpace + options.indentUnit;
+ else if (how == "subtract") indentation = curSpace - options.indentUnit;
+ indentation = Math.max(0, indentation);
+ var diff = indentation - curSpace;
+
+ if (!diff) {
+ if (sel.from.line != n && sel.to.line != n) return;
+ var indentString = curSpaceString;
+ }
+ else {
+ var indentString = "", pos = 0;
+ if (options.indentWithTabs)
+ for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
+ while (pos < indentation) {++pos; indentString += " ";}
+ }
+
+ replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
+ }
+
+ function loadMode() {
+ mode = CodeMirror.getMode(options, options.mode);
+ doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
+ work = [0];
+ startWorker();
+ }
+ function gutterChanged() {
+ var visible = options.gutter || options.lineNumbers;
+ gutter.style.display = visible ? "" : "none";
+ if (visible) gutterDirty = true;
+ else lineDiv.parentNode.style.marginLeft = 0;
+ }
+ function wrappingChanged(from, to) {
+ if (options.lineWrapping) {
+ wrapper.className += " CodeMirror-wrap";
+ var perLine = scroller.clientWidth / charWidth() - 3;
+ doc.iter(0, doc.size, function(line) {
+ if (line.hidden) return;
+ var guess = Math.ceil(line.text.length / perLine) || 1;
+ if (guess != 1) updateLineHeight(line, guess);
+ });
+ lineSpace.style.width = code.style.width = "";
+ } else {
+ wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
+ maxWidth = null; maxLine = "";
+ doc.iter(0, doc.size, function(line) {
+ if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
+ if (line.text.length > maxLine.length) maxLine = line.text;
+ });
+ }
+ changes.push({from: 0, to: doc.size});
+ }
+ function computeTabText() {
+ for (var str = '', i = 0; i < options.tabSize; ++i) str += " ";
+ return str + "";
+ }
+ function tabsChanged() {
+ tabText = computeTabText();
+ updateDisplay(true);
+ }
+ function themeChanged() {
+ scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
+ options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+ }
+
+ function TextMarker() { this.set = []; }
+ TextMarker.prototype.clear = operation(function() {
+ var min = Infinity, max = -Infinity;
+ for (var i = 0, e = this.set.length; i < e; ++i) {
+ var line = this.set[i], mk = line.marked;
+ if (!mk || !line.parent) continue;
+ var lineN = lineNo(line);
+ min = Math.min(min, lineN); max = Math.max(max, lineN);
+ for (var j = 0; j < mk.length; ++j)
+ if (mk[j].set == this.set) mk.splice(j--, 1);
+ }
+ if (min != Infinity)
+ changes.push({from: min, to: max + 1});
+ });
+ TextMarker.prototype.find = function() {
+ var from, to;
+ for (var i = 0, e = this.set.length; i < e; ++i) {
+ var line = this.set[i], mk = line.marked;
+ for (var j = 0; j < mk.length; ++j) {
+ var mark = mk[j];
+ if (mark.set == this.set) {
+ if (mark.from != null || mark.to != null) {
+ var found = lineNo(line);
+ if (found != null) {
+ if (mark.from != null) from = {line: found, ch: mark.from};
+ if (mark.to != null) to = {line: found, ch: mark.to};
+ }
+ }
+ }
+ }
+ }
+ return {from: from, to: to};
+ };
+
+ function markText(from, to, className) {
+ from = clipPos(from); to = clipPos(to);
+ var tm = new TextMarker();
+ function add(line, from, to, className) {
+ getLine(line).addMark(new MarkedText(from, to, className, tm.set));
+ }
+ if (from.line == to.line) add(from.line, from.ch, to.ch, className);
+ else {
+ add(from.line, from.ch, null, className);
+ for (var i = from.line + 1, e = to.line; i < e; ++i)
+ add(i, null, null, className);
+ add(to.line, null, to.ch, className);
+ }
+ changes.push({from: from.line, to: to.line + 1});
+ return tm;
+ }
+
+ function setBookmark(pos) {
+ pos = clipPos(pos);
+ var bm = new Bookmark(pos.ch);
+ getLine(pos.line).addMark(bm);
+ return bm;
+ }
+
+ function addGutterMarker(line, text, className) {
+ if (typeof line == "number") line = getLine(clipLine(line));
+ line.gutterMarker = {text: text, style: className};
+ gutterDirty = true;
+ return line;
+ }
+ function removeGutterMarker(line) {
+ if (typeof line == "number") line = getLine(clipLine(line));
+ line.gutterMarker = null;
+ gutterDirty = true;
+ }
+
+ function changeLine(handle, op) {
+ var no = handle, line = handle;
+ if (typeof handle == "number") line = getLine(clipLine(handle));
+ else no = lineNo(handle);
+ if (no == null) return null;
+ if (op(line, no)) changes.push({from: no, to: no + 1});
+ else return null;
+ return line;
+ }
+ function setLineClass(handle, className) {
+ return changeLine(handle, function(line) {
+ if (line.className != className) {
+ line.className = className;
+ return true;
+ }
+ });
+ }
+ function setLineHidden(handle, hidden) {
+ return changeLine(handle, function(line, no) {
+ if (line.hidden != hidden) {
+ line.hidden = hidden;
+ updateLineHeight(line, hidden ? 0 : 1);
+ if (hidden && (sel.from.line == no || sel.to.line == no))
+ setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
+ skipHidden(sel.to, sel.to.line, sel.to.ch));
+ return (gutterDirty = true);
+ }
+ });
+ }
+
+ function lineInfo(line) {
+ if (typeof line == "number") {
+ if (!isLine(line)) return null;
+ var n = line;
+ line = getLine(line);
+ if (!line) return null;
+ }
+ else {
+ var n = lineNo(line);
+ if (n == null) return null;
+ }
+ var marker = line.gutterMarker;
+ return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
+ markerClass: marker && marker.style, lineClass: line.className};
+ }
+
+ function stringWidth(str) {
+ measure.innerHTML = "x
";
+ measure.firstChild.firstChild.firstChild.nodeValue = str;
+ return measure.firstChild.firstChild.offsetWidth || 10;
+ }
+ // These are used to go from pixel positions to character
+ // positions, taking varying character widths into account.
+ function charFromX(line, x) {
+ if (x <= 0) return 0;
+ var lineObj = getLine(line), text = lineObj.text;
+ function getX(len) {
+ measure.innerHTML = "" + lineObj.getHTML(null, null, false, tabText, len) + "
";
+ return measure.firstChild.firstChild.offsetWidth;
+ }
+ var from = 0, fromX = 0, to = text.length, toX;
+ // Guess a suitable upper bound for our search.
+ var estimated = Math.min(to, Math.ceil(x / charWidth()));
+ for (;;) {
+ var estX = getX(estimated);
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
+ else {toX = estX; to = estimated; break;}
+ }
+ if (x > toX) return to;
+ // Try to guess a suitable lower bound as well.
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
+ if (estX < x) {from = estimated; fromX = estX;}
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
+ var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX;}
+ else {from = middle; fromX = middleX;}
+ }
+ }
+
+ var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
+ function measureLine(line, ch) {
+ var extra = "";
+ // Include extra text at the end to make sure the measured line is wrapped in the right way.
+ if (options.lineWrapping) {
+ var end = line.text.indexOf(" ", ch + 2);
+ extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
+ }
+ measure.innerHTML = "" + line.getHTML(null, null, false, tabText, ch) +
+ '' + htmlEscape(line.text.charAt(ch) || " ") + "" +
+ extra + "
";
+ var elt = document.getElementById("CodeMirror-temp-" + tempId);
+ var top = elt.offsetTop, left = elt.offsetLeft;
+ // Older IEs report zero offsets for spans directly after a wrap
+ if (ie && ch && top == 0 && left == 0) {
+ var backup = document.createElement("span");
+ backup.innerHTML = "x";
+ elt.parentNode.insertBefore(backup, elt.nextSibling);
+ top = backup.offsetTop;
+ }
+ return {top: top, left: left};
+ }
+ function localCoords(pos, inLineWrap) {
+ var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
+ if (pos.ch == 0) x = 0;
+ else {
+ var sp = measureLine(getLine(pos.line), pos.ch);
+ x = sp.left;
+ if (options.lineWrapping) y += Math.max(0, sp.top);
+ }
+ return {x: x, y: y, yBot: y + lh};
+ }
+ // Coords must be lineSpace-local
+ function coordsChar(x, y) {
+ if (y < 0) y = 0;
+ var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
+ var lineNo = lineAtHeight(doc, heightPos);
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
+ var lineObj = getLine(lineNo), text = lineObj.text;
+ var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
+ if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
+ function getX(len) {
+ var sp = measureLine(lineObj, len);
+ if (tw) {
+ var off = Math.round(sp.top / th);
+ return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
+ }
+ return sp.left;
+ }
+ var from = 0, fromX = 0, to = text.length, toX;
+ // Guess a suitable upper bound for our search.
+ var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
+ for (;;) {
+ var estX = getX(estimated);
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
+ else {toX = estX; to = estimated; break;}
+ }
+ if (x > toX) return {line: lineNo, ch: to};
+ // Try to guess a suitable lower bound as well.
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
+ if (estX < x) {from = estimated; fromX = estX;}
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
+ var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX;}
+ else {from = middle; fromX = middleX;}
+ }
+ }
+ function pageCoords(pos) {
+ var local = localCoords(pos, true), off = eltOffset(lineSpace);
+ return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
+ }
+
+ var cachedHeight, cachedHeightFor, measureText;
+ function textHeight() {
+ if (measureText == null) {
+ measureText = "";
+ for (var i = 0; i < 49; ++i) measureText += "x
";
+ measureText += "x
";
+ }
+ var offsetHeight = lineDiv.clientHeight;
+ if (offsetHeight == cachedHeightFor) return cachedHeight;
+ cachedHeightFor = offsetHeight;
+ measure.innerHTML = measureText;
+ cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
+ measure.innerHTML = "";
+ return cachedHeight;
+ }
+ var cachedWidth, cachedWidthFor = 0;
+ function charWidth() {
+ if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
+ cachedWidthFor = scroller.clientWidth;
+ return (cachedWidth = stringWidth("x"));
+ }
+ function paddingTop() {return lineSpace.offsetTop;}
+ function paddingLeft() {return lineSpace.offsetLeft;}
+
+ function posFromMouse(e, liberal) {
+ var offW = eltOffset(scroller, true), x, y;
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
+ // This is a mess of a heuristic to try and determine whether a
+ // scroll-bar was clicked or not, and to return null if one was
+ // (and !liberal).
+ if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
+ return null;
+ var offL = eltOffset(lineSpace, true);
+ return coordsChar(x - offL.left, y - offL.top);
+ }
+ function onContextMenu(e) {
+ var pos = posFromMouse(e);
+ if (!pos || window.opera) return; // Opera is difficult.
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+ operation(setCursor)(pos.line, pos.ch);
+
+ var oldCSS = input.style.cssText;
+ inputDiv.style.position = "absolute";
+ input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ leaveInputAlone = true;
+ var val = input.value = getSelection();
+ focusInput();
+ input.select();
+ function rehide() {
+ var newVal = splitLines(input.value).join("\n");
+ if (newVal != val) operation(replaceSelection)(newVal, "end");
+ inputDiv.style.position = "relative";
+ input.style.cssText = oldCSS;
+ leaveInputAlone = false;
+ resetInput(true);
+ slowPoll();
+ }
+
+ if (gecko) {
+ e_stop(e);
+ var mouseup = connect(window, "mouseup", function() {
+ mouseup();
+ setTimeout(rehide, 20);
+ }, true);
+ }
+ else {
+ setTimeout(rehide, 50);
+ }
+ }
+
+ // Cursor-blinking
+ function restartBlink() {
+ clearInterval(blinker);
+ var on = true;
+ cursor.style.visibility = "";
+ blinker = setInterval(function() {
+ cursor.style.visibility = (on = !on) ? "" : "hidden";
+ }, 650);
+ }
+
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+ function matchBrackets(autoclear) {
+ var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+ if (!match) return;
+ var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
+ for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
+ if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
+
+ var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+ function scan(line, from, to) {
+ if (!line.text) return;
+ var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
+ for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
+ var text = st[i];
+ if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
+ for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
+ if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
+ var match = matching[cur];
+ if (match.charAt(1) == ">" == forward) stack.push(cur);
+ else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+ else if (!stack.length) return {pos: pos, match: true};
+ }
+ }
+ }
+ }
+ for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
+ var line = getLine(i), first = i == head.line;
+ var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
+ if (found) break;
+ }
+ if (!found) found = {pos: null, match: false};
+ var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
+ two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
+ var clear = operation(function(){one.clear(); two && two.clear();});
+ if (autoclear) setTimeout(clear, 800);
+ else bracketHighlighted = clear;
+ }
+
+ // Finds the line to start with when starting a parse. Tries to
+ // find a line with a stateAfter, so that it can start with a
+ // valid state. If that fails, it returns the line with the
+ // smallest indentation, which tends to need the least context to
+ // parse correctly.
+ function findStartLine(n) {
+ var minindent, minline;
+ for (var search = n, lim = n - 40; search > lim; --search) {
+ if (search == 0) return 0;
+ var line = getLine(search-1);
+ if (line.stateAfter) return search;
+ var indented = line.indentation(options.tabSize);
+ if (minline == null || minindent > indented) {
+ minline = search - 1;
+ minindent = indented;
+ }
+ }
+ return minline;
+ }
+ function getStateBefore(n) {
+ var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
+ if (!state) state = startState(mode);
+ else state = copyState(mode, state);
+ doc.iter(start, n, function(line) {
+ line.highlight(mode, state, options.tabSize);
+ line.stateAfter = copyState(mode, state);
+ });
+ if (start < n) changes.push({from: start, to: n});
+ if (n < doc.size && !getLine(n).stateAfter) work.push(n);
+ return state;
+ }
+ function highlightLines(start, end) {
+ var state = getStateBefore(start);
+ doc.iter(start, end, function(line) {
+ line.highlight(mode, state, options.tabSize);
+ line.stateAfter = copyState(mode, state);
+ });
+ }
+ function highlightWorker() {
+ var end = +new Date + options.workTime;
+ var foundWork = work.length;
+ while (work.length) {
+ if (!getLine(showingFrom).stateAfter) var task = showingFrom;
+ else var task = work.pop();
+ if (task >= doc.size) continue;
+ var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
+ if (state) state = copyState(mode, state);
+ else state = startState(mode);
+
+ var unchanged = 0, compare = mode.compareStates, realChange = false,
+ i = start, bail = false;
+ doc.iter(i, doc.size, function(line) {
+ var hadState = line.stateAfter;
+ if (+new Date > end) {
+ work.push(i);
+ startWorker(options.workDelay);
+ if (realChange) changes.push({from: task, to: i + 1});
+ return (bail = true);
+ }
+ var changed = line.highlight(mode, state, options.tabSize);
+ if (changed) realChange = true;
+ line.stateAfter = copyState(mode, state);
+ if (compare) {
+ if (hadState && compare(hadState, state)) return true;
+ } else {
+ if (changed !== false || !hadState) unchanged = 0;
+ else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
+ return true;
+ }
+ ++i;
+ });
+ if (bail) return;
+ if (realChange) changes.push({from: task, to: i + 1});
+ }
+ if (foundWork && options.onHighlightComplete)
+ options.onHighlightComplete(instance);
+ }
+ function startWorker(time) {
+ if (!work.length) return;
+ highlight.set(time, operation(highlightWorker));
+ }
+
+ // Operations are used to wrap changes in such a way that each
+ // change won't have to update the cursor and display (which would
+ // be awkward, slow, and error-prone), but instead updates are
+ // batched and then all combined and executed at once.
+ function startOperation() {
+ updateInput = userSelChange = textChanged = null;
+ changes = []; selectionChanged = false; callbacks = [];
+ }
+ function endOperation() {
+ var reScroll = false, updated;
+ if (selectionChanged) reScroll = !scrollCursorIntoView();
+ if (changes.length) updated = updateDisplay(changes, true);
+ else {
+ if (selectionChanged) updateCursor();
+ if (gutterDirty) updateGutter();
+ }
+ if (reScroll) scrollCursorIntoView();
+ if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
+
+ if (focused && !leaveInputAlone &&
+ (updateInput === true || (updateInput !== false && selectionChanged)))
+ resetInput(userSelChange);
+
+ if (selectionChanged && options.matchBrackets)
+ setTimeout(operation(function() {
+ if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
+ if (posEq(sel.from, sel.to)) matchBrackets(false);
+ }), 20);
+ var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
+ if (selectionChanged && options.onCursorActivity)
+ options.onCursorActivity(instance);
+ if (tc && options.onChange && instance)
+ options.onChange(instance, tc);
+ for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
+ if (updated && options.onUpdate) options.onUpdate(instance);
+ }
+ var nestedOperation = 0;
+ function operation(f) {
+ return function() {
+ if (!nestedOperation++) startOperation();
+ try {var result = f.apply(this, arguments);}
+ finally {if (!--nestedOperation) endOperation();}
+ return result;
+ };
+ }
+
+ for (var ext in extensions)
+ if (extensions.propertyIsEnumerable(ext) &&
+ !instance.propertyIsEnumerable(ext))
+ instance[ext] = extensions[ext];
+ return instance;
+ } // (end of function CodeMirror)
+
+ // The default configuration options.
+ CodeMirror.defaults = {
+ value: "",
+ mode: null,
+ theme: "default",
+ indentUnit: 2,
+ indentWithTabs: false,
+ tabSize: 4,
+ keyMap: "default",
+ extraKeys: null,
+ electricChars: true,
+ onKeyEvent: null,
+ lineWrapping: false,
+ lineNumbers: false,
+ gutter: false,
+ fixedGutter: false,
+ firstLineNumber: 1,
+ readOnly: false,
+ onChange: null,
+ onCursorActivity: null,
+ onGutterClick: null,
+ onHighlightComplete: null,
+ onUpdate: null,
+ onFocus: null, onBlur: null, onScroll: null,
+ matchBrackets: false,
+ workTime: 100,
+ workDelay: 200,
+ pollInterval: 100,
+ undoDepth: 40,
+ tabindex: null,
+ document: window.document
+ };
+
+ var mac = /Mac/.test(navigator.platform);
+ var win = /Win/.test(navigator.platform);
+
+ // Known modes, by name and by MIME
+ var modes = {}, mimeModes = {};
+ CodeMirror.defineMode = function(name, mode) {
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+ modes[name] = mode;
+ };
+ CodeMirror.defineMIME = function(mime, spec) {
+ mimeModes[mime] = spec;
+ };
+ CodeMirror.getMode = function(options, spec) {
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+ spec = mimeModes[spec];
+ if (typeof spec == "string")
+ var mname = spec, config = {};
+ else if (spec != null)
+ var mname = spec.name, config = spec;
+ var mfactory = modes[mname];
+ if (!mfactory) {
+ if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
+ return CodeMirror.getMode(options, "text/plain");
+ }
+ return mfactory(options, config || {});
+ };
+ CodeMirror.listModes = function() {
+ var list = [];
+ for (var m in modes)
+ if (modes.propertyIsEnumerable(m)) list.push(m);
+ return list;
+ };
+ CodeMirror.listMIMEs = function() {
+ var list = [];
+ for (var m in mimeModes)
+ if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
+ return list;
+ };
+
+ var extensions = CodeMirror.extensions = {};
+ CodeMirror.defineExtension = function(name, func) {
+ extensions[name] = func;
+ };
+
+ var commands = CodeMirror.commands = {
+ selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
+ killLine: function(cm) {
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
+ if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
+ else cm.replaceRange("", from, sel ? to : {line: from.line});
+ },
+ deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
+ undo: function(cm) {cm.undo();},
+ redo: function(cm) {cm.redo();},
+ goDocStart: function(cm) {cm.setCursor(0, 0, true);},
+ goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
+ goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
+ goLineStartSmart: function(cm) {
+ var cur = cm.getCursor();
+ var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
+ cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
+ },
+ goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
+ goLineDown: function(cm) {cm.moveV(1, "line");},
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
+ goPageDown: function(cm) {cm.moveV(1, "page");},
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
+ goCharRight: function(cm) {cm.moveH(1, "char");},
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goWordRight: function(cm) {cm.moveH(1, "word");},
+ delCharLeft: function(cm) {cm.deleteH(-1, "char");},
+ delCharRight: function(cm) {cm.deleteH(1, "char");},
+ delWordLeft: function(cm) {cm.deleteH(-1, "word");},
+ delWordRight: function(cm) {cm.deleteH(1, "word");},
+ indentAuto: function(cm) {cm.indentSelection("smart");},
+ indentMore: function(cm) {cm.indentSelection("add");},
+ indentLess: function(cm) {cm.indentSelection("subtract");},
+ insertTab: function(cm) {cm.replaceSelection("\t", "end");},
+ transposeChars: function(cm) {
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
+ if (cur.ch > 0 && cur.ch < line.length - 1)
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+ {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
+ },
+ newlineAndIndent: function(cm) {
+ cm.replaceSelection("\n", "end");
+ cm.indentLine(cm.getCursor().line);
+ },
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+ };
+
+ var keyMap = CodeMirror.keyMap = {};
+ keyMap.basic = {
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
+ };
+ // Note that the save and find-related commands aren't defined by
+ // default. Unknown commands are simply ignored.
+ keyMap.pcDefault = {
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
+ "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+ fallthrough: "basic"
+ };
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
+ "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
+ "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ fallthrough: ["basic", "emacsy"]
+ };
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+ keyMap.emacsy = {
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
+ "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
+ };
+
+ function lookupKey(name, extraMap, map) {
+ function lookup(name, map, ft) {
+ var found = map[name];
+ if (found != null) return found;
+ if (ft == null) ft = map.fallthrough;
+ if (ft == null) return map.catchall;
+ if (typeof ft == "string") return lookup(name, keyMap[ft]);
+ for (var i = 0, e = ft.length; i < e; ++i) {
+ found = lookup(name, keyMap[ft[i]]);
+ if (found != null) return found;
+ }
+ return null;
+ }
+ return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
+ }
+ function isModifierKey(event) {
+ var name = keyNames[event.keyCode];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+ }
+
+ CodeMirror.fromTextArea = function(textarea, options) {
+ if (!options) options = {};
+ options.value = textarea.value;
+ if (!options.tabindex && textarea.tabindex)
+ options.tabindex = textarea.tabindex;
+
+ function save() {textarea.value = instance.getValue();}
+ if (textarea.form) {
+ // Deplorable hack to make the submit method do the right thing.
+ var rmSubmit = connect(textarea.form, "submit", save, true);
+ if (typeof textarea.form.submit == "function") {
+ var realSubmit = textarea.form.submit;
+ function wrappedSubmit() {
+ save();
+ textarea.form.submit = realSubmit;
+ textarea.form.submit();
+ textarea.form.submit = wrappedSubmit;
+ }
+ textarea.form.submit = wrappedSubmit;
+ }
+ }
+
+ textarea.style.display = "none";
+ var instance = CodeMirror(function(node) {
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
+ }, options);
+ instance.save = save;
+ instance.getTextArea = function() { return textarea; };
+ instance.toTextArea = function() {
+ save();
+ textarea.parentNode.removeChild(instance.getWrapperElement());
+ textarea.style.display = "";
+ if (textarea.form) {
+ rmSubmit();
+ if (typeof textarea.form.submit == "function")
+ textarea.form.submit = realSubmit;
+ }
+ };
+ return instance;
+ };
+
+ // Utility functions for working with state. Exported because modes
+ // sometimes need to do this.
+ function copyState(mode, state) {
+ if (state === true) return state;
+ if (mode.copyState) return mode.copyState(state);
+ var nstate = {};
+ for (var n in state) {
+ var val = state[n];
+ if (val instanceof Array) val = val.concat([]);
+ nstate[n] = val;
+ }
+ return nstate;
+ }
+ CodeMirror.copyState = copyState;
+ function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true;
+ }
+ CodeMirror.startState = startState;
+
+ // The character stream used by a mode's parser.
+ function StringStream(string, tabSize) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ }
+ StringStream.prototype = {
+ eol: function() {return this.pos >= this.string.length;},
+ sol: function() {return this.pos == 0;},
+ peek: function() {return this.string.charAt(this.pos);},
+ next: function() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
+ if (ok) {++this.pos; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start;
+ },
+ eatSpace: function() {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+ return this.pos > start;
+ },
+ skipToEnd: function() {this.pos = this.string.length;},
+ skipTo: function(ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true;}
+ },
+ backUp: function(n) {this.pos -= n;},
+ column: function() {return countColumn(this.string, this.start, this.tabSize);},
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
+ match: function(pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
+ if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+ if (consume !== false) this.pos += pattern.length;
+ return true;
+ }
+ }
+ else {
+ var match = this.string.slice(this.pos).match(pattern);
+ if (match && consume !== false) this.pos += match[0].length;
+ return match;
+ }
+ },
+ current: function(){return this.string.slice(this.start, this.pos);}
+ };
+ CodeMirror.StringStream = StringStream;
+
+ function MarkedText(from, to, className, set) {
+ this.from = from; this.to = to; this.style = className; this.set = set;
+ }
+ MarkedText.prototype = {
+ attach: function(line) { this.set.push(line); },
+ detach: function(line) {
+ var ix = indexOf(this.set, line);
+ if (ix > -1) this.set.splice(ix, 1);
+ },
+ split: function(pos, lenBefore) {
+ if (this.to <= pos && this.to != null) return null;
+ var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
+ var to = this.to == null ? null : this.to - pos + lenBefore;
+ return new MarkedText(from, to, this.style, this.set);
+ },
+ dup: function() { return new MarkedText(null, null, this.style, this.set); },
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
+ if (this.from != null && this.from >= from)
+ this.from = Math.max(to, this.from) + diff;
+ if (this.to != null && this.to > from)
+ this.to = to < this.to ? this.to + diff : from;
+ if (fromOpen && to > this.from && (to < this.to || this.to == null))
+ this.from = null;
+ if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
+ this.to = null;
+ },
+ isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
+ sameSet: function(x) { return this.set == x.set; }
+ };
+
+ function Bookmark(pos) {
+ this.from = pos; this.to = pos; this.line = null;
+ }
+ Bookmark.prototype = {
+ attach: function(line) { this.line = line; },
+ detach: function(line) { if (this.line == line) this.line = null; },
+ split: function(pos, lenBefore) {
+ if (pos < this.from) {
+ this.from = this.to = (this.from - pos) + lenBefore;
+ return this;
+ }
+ },
+ isDead: function() { return this.from > this.to; },
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
+ if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
+ this.from = 0; this.to = -1;
+ } else if (this.from > from) {
+ this.from = this.to = Math.max(to, this.from) + diff;
+ }
+ },
+ sameSet: function(x) { return false; },
+ find: function() {
+ if (!this.line || !this.line.parent) return null;
+ return {line: lineNo(this.line), ch: this.from};
+ },
+ clear: function() {
+ if (this.line) {
+ var found = indexOf(this.line.marked, this);
+ if (found != -1) this.line.marked.splice(found, 1);
+ this.line = null;
+ }
+ }
+ };
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ function Line(text, styles) {
+ this.styles = styles || [text, null];
+ this.text = text;
+ this.height = 1;
+ this.marked = this.gutterMarker = this.className = this.handlers = null;
+ this.stateAfter = this.parent = this.hidden = null;
+ }
+ Line.inheritMarks = function(text, orig) {
+ var ln = new Line(text), mk = orig && orig.marked;
+ if (mk) {
+ for (var i = 0; i < mk.length; ++i) {
+ if (mk[i].to == null && mk[i].style) {
+ var newmk = ln.marked || (ln.marked = []), mark = mk[i];
+ var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
+ }
+ }
+ }
+ return ln;
+ }
+ Line.prototype = {
+ // Replace a piece of a line, keeping the styles around it intact.
+ replace: function(from, to_, text) {
+ var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
+ copyStyles(0, from, this.styles, st);
+ if (text) st.push(text, null);
+ copyStyles(to, this.text.length, this.styles, st);
+ this.styles = st;
+ this.text = this.text.slice(0, from) + text + this.text.slice(to);
+ this.stateAfter = null;
+ if (mk) {
+ var diff = text.length - (to - from);
+ for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
+ mark.clipTo(from == null, from || 0, to_ == null, to, diff);
+ if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
+ }
+ }
+ },
+ // Split a part off a line, keeping styles and markers intact.
+ split: function(pos, textBefore) {
+ var st = [textBefore, null], mk = this.marked;
+ copyStyles(pos, this.text.length, this.styles, st);
+ var taken = new Line(textBefore + this.text.slice(pos), st);
+ if (mk) {
+ for (var i = 0; i < mk.length; ++i) {
+ var mark = mk[i];
+ var newmark = mark.split(pos, textBefore.length);
+ if (newmark) {
+ if (!taken.marked) taken.marked = [];
+ taken.marked.push(newmark); newmark.attach(taken);
+ }
+ }
+ }
+ return taken;
+ },
+ append: function(line) {
+ var mylen = this.text.length, mk = line.marked, mymk = this.marked;
+ this.text += line.text;
+ copyStyles(0, line.text.length, line.styles, this.styles);
+ if (mymk) {
+ for (var i = 0; i < mymk.length; ++i)
+ if (mymk[i].to == null) mymk[i].to = mylen;
+ }
+ if (mk && mk.length) {
+ if (!mymk) this.marked = mymk = [];
+ outer: for (var i = 0; i < mk.length; ++i) {
+ var mark = mk[i];
+ if (!mark.from) {
+ for (var j = 0; j < mymk.length; ++j) {
+ var mymark = mymk[j];
+ if (mymark.to == mylen && mymark.sameSet(mark)) {
+ mymark.to = mark.to == null ? null : mark.to + mylen;
+ if (mymark.isDead()) {
+ mymark.detach(this);
+ mk.splice(i--, 1);
+ }
+ continue outer;
+ }
+ }
+ }
+ mymk.push(mark);
+ mark.attach(this);
+ mark.from += mylen;
+ if (mark.to != null) mark.to += mylen;
+ }
+ }
+ },
+ fixMarkEnds: function(other) {
+ var mk = this.marked, omk = other.marked;
+ if (!mk) return;
+ for (var i = 0; i < mk.length; ++i) {
+ var mark = mk[i], close = mark.to == null;
+ if (close && omk) {
+ for (var j = 0; j < omk.length; ++j)
+ if (omk[j].sameSet(mark)) {close = false; break;}
+ }
+ if (close) mark.to = this.text.length;
+ }
+ },
+ fixMarkStarts: function() {
+ var mk = this.marked;
+ if (!mk) return;
+ for (var i = 0; i < mk.length; ++i)
+ if (mk[i].from == null) mk[i].from = 0;
+ },
+ addMark: function(mark) {
+ mark.attach(this);
+ if (this.marked == null) this.marked = [];
+ this.marked.push(mark);
+ this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
+ },
+ // Run the given mode's parser over a line, update the styles
+ // array, which contains alternating fragments of text and CSS
+ // classes.
+ highlight: function(mode, state, tabSize) {
+ var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
+ var changed = false, curWord = st[0], prevWord;
+ if (this.text == "" && mode.blankLine) mode.blankLine(state);
+ while (!stream.eol()) {
+ var style = mode.token(stream, state);
+ var substr = this.text.slice(stream.start, stream.pos);
+ stream.start = stream.pos;
+ if (pos && st[pos-1] == style)
+ st[pos-2] += substr;
+ else if (substr) {
+ if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
+ st[pos++] = substr; st[pos++] = style;
+ prevWord = curWord; curWord = st[pos];
+ }
+ // Give up when line is ridiculously long
+ if (stream.pos > 5000) {
+ st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
+ break;
+ }
+ }
+ if (st.length != pos) {st.length = pos; changed = true;}
+ if (pos && st[pos-2] != prevWord) changed = true;
+ // Short lines with simple highlights return null, and are
+ // counted as changed by the driver because they are likely to
+ // highlight the same way in various contexts.
+ return changed || (st.length < 5 && this.text.length < 10 ? null : false);
+ },
+ // Fetch the parser token for a given character. Useful for hacks
+ // that want to inspect the mode state (say, for completion).
+ getTokenAt: function(mode, state, ch) {
+ var txt = this.text, stream = new StringStream(txt);
+ while (stream.pos < ch && !stream.eol()) {
+ stream.start = stream.pos;
+ var style = mode.token(stream, state);
+ }
+ return {start: stream.start,
+ end: stream.pos,
+ string: stream.current(),
+ className: style || null,
+ state: state};
+ },
+ indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
+ // Produces an HTML fragment for the line, taking selection,
+ // marking, and highlighting into account.
+ getHTML: function(sfrom, sto, includePre, tabText, endAt) {
+ var html = [], first = true;
+ if (includePre)
+ html.push(this.className ? '': "");
+ function span(text, style) {
+ if (!text) return;
+ // Work around a bug where, in some compat modes, IE ignores leading spaces
+ if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
+ first = false;
+ if (style) html.push('', htmlEscape(text).replace(/\t/g, tabText), "");
+ else html.push(htmlEscape(text).replace(/\t/g, tabText));
+ }
+ var st = this.styles, allText = this.text, marked = this.marked;
+ if (sfrom == sto) sfrom = null;
+ var len = allText.length;
+ if (endAt != null) len = Math.min(endAt, len);
+
+ if (!allText && endAt == null)
+ span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
+ else if (!marked && sfrom == null)
+ for (var i = 0, ch = 0; ch < len; i+=2) {
+ var str = st[i], style = st[i+1], l = str.length;
+ if (ch + l > len) str = str.slice(0, len - ch);
+ ch += l;
+ span(str, style && "cm-" + style);
+ }
+ else {
+ var pos = 0, i = 0, text = "", style, sg = 0;
+ var markpos = -1, mark = null;
+ function nextMark() {
+ if (marked) {
+ markpos += 1;
+ mark = (markpos < marked.length) ? marked[markpos] : null;
+ }
+ }
+ nextMark();
+ while (pos < len) {
+ var upto = len;
+ var extraStyle = "";
+ if (sfrom != null) {
+ if (sfrom > pos) upto = sfrom;
+ else if (sto == null || sto > pos) {
+ extraStyle = " CodeMirror-selected";
+ if (sto != null) upto = Math.min(upto, sto);
+ }
+ }
+ while (mark && mark.to != null && mark.to <= pos) nextMark();
+ if (mark) {
+ if (mark.from > pos) upto = Math.min(upto, mark.from);
+ else {
+ extraStyle += " " + mark.style;
+ if (mark.to != null) upto = Math.min(upto, mark.to);
+ }
+ }
+ for (;;) {
+ var end = pos + text.length;
+ var appliedStyle = style;
+ if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
+ span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
+ pos = end;
+ text = st[i++]; style = "cm-" + st[i++];
+ }
+ }
+ if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
+ }
+ if (includePre) html.push("
");
+ return html.join("");
+ },
+ cleanUp: function() {
+ this.parent = null;
+ if (this.marked)
+ for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
+ }
+ };
+ // Utility used by replace and split above
+ function copyStyles(from, to, source, dest) {
+ for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
+ var part = source[i], end = pos + part.length;
+ if (state == 0) {
+ if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
+ if (end >= from) state = 1;
+ }
+ else if (state == 1) {
+ if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
+ else dest.push(part, source[i+1]);
+ }
+ pos = end;
+ }
+ }
+
+ // Data structure that holds the sequence of lines.
+ function LeafChunk(lines) {
+ this.lines = lines;
+ this.parent = null;
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
+ lines[i].parent = this;
+ height += lines[i].height;
+ }
+ this.height = height;
+ }
+ LeafChunk.prototype = {
+ chunkSize: function() { return this.lines.length; },
+ remove: function(at, n, callbacks) {
+ for (var i = at, e = at + n; i < e; ++i) {
+ var line = this.lines[i];
+ this.height -= line.height;
+ line.cleanUp();
+ if (line.handlers)
+ for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
+ }
+ this.lines.splice(at, n);
+ },
+ collapse: function(lines) {
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
+ },
+ insertHeight: function(at, lines, height) {
+ this.height += height;
+ this.lines.splice.apply(this.lines, [at, 0].concat(lines));
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
+ },
+ iterN: function(at, n, op) {
+ for (var e = at + n; at < e; ++at)
+ if (op(this.lines[at])) return true;
+ }
+ };
+ function BranchChunk(children) {
+ this.children = children;
+ var size = 0, height = 0;
+ for (var i = 0, e = children.length; i < e; ++i) {
+ var ch = children[i];
+ size += ch.chunkSize(); height += ch.height;
+ ch.parent = this;
+ }
+ this.size = size;
+ this.height = height;
+ this.parent = null;
+ }
+ BranchChunk.prototype = {
+ chunkSize: function() { return this.size; },
+ remove: function(at, n, callbacks) {
+ this.size -= n;
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
+ child.remove(at, rm, callbacks);
+ this.height -= oldHeight - child.height;
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
+ if ((n -= rm) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ if (this.size - n < 25) {
+ var lines = [];
+ this.collapse(lines);
+ this.children = [new LeafChunk(lines)];
+ }
+ },
+ collapse: function(lines) {
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
+ },
+ insert: function(at, lines) {
+ var height = 0;
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
+ this.insertHeight(at, lines, height);
+ },
+ insertHeight: function(at, lines, height) {
+ this.size += lines.length;
+ this.height += height;
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at <= sz) {
+ child.insertHeight(at, lines, height);
+ if (child.lines && child.lines.length > 50) {
+ while (child.lines.length > 50) {
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
+ var newleaf = new LeafChunk(spilled);
+ child.height -= newleaf.height;
+ this.children.splice(i + 1, 0, newleaf);
+ newleaf.parent = this;
+ }
+ this.maybeSpill();
+ }
+ break;
+ }
+ at -= sz;
+ }
+ },
+ maybeSpill: function() {
+ if (this.children.length <= 10) return;
+ var me = this;
+ do {
+ var spilled = me.children.splice(me.children.length - 5, 5);
+ var sibling = new BranchChunk(spilled);
+ if (!me.parent) { // Become the parent node
+ var copy = new BranchChunk(me.children);
+ copy.parent = me;
+ me.children = [copy, sibling];
+ me = copy;
+ } else {
+ me.size -= sibling.size;
+ me.height -= sibling.height;
+ var myIndex = indexOf(me.parent.children, me);
+ me.parent.children.splice(myIndex + 1, 0, sibling);
+ }
+ sibling.parent = me.parent;
+ } while (me.children.length > 10);
+ me.parent.maybeSpill();
+ },
+ iter: function(from, to, op) { this.iterN(from, to - from, op); },
+ iterN: function(at, n, op) {
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var used = Math.min(n, sz - at);
+ if (child.iterN(at, used, op)) return true;
+ if ((n -= used) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ }
+ };
+
+ function getLineAt(chunk, n) {
+ while (!chunk.lines) {
+ for (var i = 0;; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; break; }
+ n -= sz;
+ }
+ }
+ return chunk.lines[n];
+ }
+ function lineNo(line) {
+ if (line.parent == null) return null;
+ var cur = line.parent, no = indexOf(cur.lines, line);
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+ for (var i = 0, e = chunk.children.length; ; ++i) {
+ if (chunk.children[i] == cur) break;
+ no += chunk.children[i].chunkSize();
+ }
+ }
+ return no;
+ }
+ function lineAtHeight(chunk, h) {
+ var n = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], ch = child.height;
+ if (h < ch) { chunk = child; continue outer; }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n;
+ } while (!chunk.lines);
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) break;
+ h -= lh;
+ }
+ return n + i;
+ }
+ function heightAtLine(chunk, n) {
+ var h = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; continue outer; }
+ n -= sz;
+ h += child.height;
+ }
+ return h;
+ } while (!chunk.lines);
+ for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
+ return h;
+ }
+
+ // The history object 'chunks' changes that are made close together
+ // and at almost the same time into bigger undoable units.
+ function History() {
+ this.time = 0;
+ this.done = []; this.undone = [];
+ }
+ History.prototype = {
+ addChange: function(start, added, old) {
+ this.undone.length = 0;
+ var time = +new Date, last = this.done[this.done.length - 1];
+ if (time - this.time > 400 || !last ||
+ last.start > start + added || last.start + last.added < start - last.added + last.old.length)
+ this.done.push({start: start, added: added, old: old});
+ else {
+ var oldoff = 0;
+ if (start < last.start) {
+ for (var i = last.start - start - 1; i >= 0; --i)
+ last.old.unshift(old[i]);
+ last.added += last.start - start;
+ last.start = start;
+ }
+ else if (last.start < start) {
+ oldoff = start - last.start;
+ added += oldoff;
+ }
+ for (var i = last.added - oldoff, e = old.length; i < e; ++i)
+ last.old.push(old[i]);
+ if (last.added < added) last.added = added;
+ }
+ this.time = time;
+ }
+ };
+
+ function stopMethod() {e_stop(this);}
+ // Ensure an event has a stop method.
+ function addStop(event) {
+ if (!event.stop) event.stop = stopMethod;
+ return event;
+ }
+
+ function e_preventDefault(e) {
+ if (e.preventDefault) e.preventDefault();
+ else e.returnValue = false;
+ }
+ function e_stopPropagation(e) {
+ if (e.stopPropagation) e.stopPropagation();
+ else e.cancelBubble = true;
+ }
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+ CodeMirror.e_stop = e_stop;
+ CodeMirror.e_preventDefault = e_preventDefault;
+ CodeMirror.e_stopPropagation = e_stopPropagation;
+
+ function e_target(e) {return e.target || e.srcElement;}
+ function e_button(e) {
+ if (e.which) return e.which;
+ else if (e.button & 1) return 1;
+ else if (e.button & 2) return 3;
+ else if (e.button & 4) return 2;
+ }
+
+ // Event handler registration. If disconnect is true, it'll return a
+ // function that unregisters the handler.
+ function connect(node, type, handler, disconnect) {
+ if (typeof node.addEventListener == "function") {
+ node.addEventListener(type, handler, false);
+ if (disconnect) return function() {node.removeEventListener(type, handler, false);};
+ }
+ else {
+ var wrapHandler = function(event) {handler(event || window.event);};
+ node.attachEvent("on" + type, wrapHandler);
+ if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
+ }
+ }
+ CodeMirror.connect = connect;
+
+ function Delayed() {this.id = null;}
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
+
+ // Detect drag-and-drop
+ var dragAndDrop = function() {
+ // IE8 has ondragstart and ondrop properties, but doesn't seem to
+ // actually support ondragstart the way it's supposed to work.
+ if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
+ var div = document.createElement('div');
+ return "draggable" in div;
+ }();
+
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
+ var ie = /MSIE \d/.test(navigator.userAgent);
+ var webkit = /WebKit\//.test(navigator.userAgent);
+
+ var lineSep = "\n";
+ // Feature-detect whether newlines in textareas are converted to \r\n
+ (function () {
+ var te = document.createElement("textarea");
+ te.value = "foo\nbar";
+ if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
+ }());
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ function countColumn(string, end, tabSize) {
+ if (end == null) {
+ end = string.search(/[^\s\u00a0]/);
+ if (end == -1) end = string.length;
+ }
+ for (var i = 0, n = 0; i < end; ++i) {
+ if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
+ else ++n;
+ }
+ return n;
+ }
+
+ function computedStyle(elt) {
+ if (elt.currentStyle) return elt.currentStyle;
+ return window.getComputedStyle(elt, null);
+ }
+
+ // Find the position of an element by following the offsetParent chain.
+ // If screen==true, it returns screen (rather than page) coordinates.
+ function eltOffset(node, screen) {
+ var bod = node.ownerDocument.body;
+ var x = 0, y = 0, skipBody = false;
+ for (var n = node; n; n = n.offsetParent) {
+ var ol = n.offsetLeft, ot = n.offsetTop;
+ // Firefox reports weird inverted offsets when the body has a border.
+ if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
+ else { x += ol, y += ot; }
+ if (screen && computedStyle(n).position == "fixed")
+ skipBody = true;
+ }
+ var e = screen && !skipBody ? null : bod;
+ for (var n = node.parentNode; n != e; n = n.parentNode)
+ if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
+ return {left: x, top: y};
+ }
+ // Use the faster and saner getBoundingClientRect method when possible.
+ if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
+ // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
+ // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
+ try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
+ catch(e) { box = {top: 0, left: 0}; }
+ if (!screen) {
+ // Get the toplevel scroll, working around browser differences.
+ if (window.pageYOffset == null) {
+ var t = document.documentElement || document.body.parentNode;
+ if (t.scrollTop == null) t = document.body;
+ box.top += t.scrollTop; box.left += t.scrollLeft;
+ } else {
+ box.top += window.pageYOffset; box.left += window.pageXOffset;
+ }
+ }
+ return box;
+ };
+
+ // Get a node's text content.
+ function eltText(node) {
+ return node.textContent || node.innerText || node.nodeValue || "";
+ }
+
+ // Operations on {line, ch} objects.
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
+ function copyPos(x) {return {line: x.line, ch: x.ch};}
+
+ var escapeElement = document.createElement("pre");
+ function htmlEscape(str) {
+ escapeElement.textContent = str;
+ return escapeElement.innerHTML;
+ }
+ // Recent (late 2011) Opera betas insert bogus newlines at the start
+ // of the textContent, so we strip those.
+ if (htmlEscape("a") == "\na")
+ htmlEscape = function(str) {
+ escapeElement.textContent = str;
+ return escapeElement.innerHTML.slice(1);
+ };
+ // Some IEs don't preserve tabs through innerHTML
+ else if (htmlEscape("\t") != "\t")
+ htmlEscape = function(str) {
+ escapeElement.innerHTML = "";
+ escapeElement.appendChild(document.createTextNode(str));
+ return escapeElement.innerHTML;
+ };
+ CodeMirror.htmlEscape = htmlEscape;
+
+ // Used to position the cursor after an undo/redo by finding the
+ // last edited character.
+ function editEnd(from, to) {
+ if (!to) return from ? from.length : 0;
+ if (!from) return to.length;
+ for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
+ if (from.charAt(i) != to.charAt(j)) break;
+ return j + 1;
+ }
+
+ function indexOf(collection, elt) {
+ if (collection.indexOf) return collection.indexOf(elt);
+ for (var i = 0, e = collection.length; i < e; ++i)
+ if (collection[i] == elt) return i;
+ return -1;
+ }
+ function isWordChar(ch) {
+ return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
+ }
+
+ // See if "".split is the broken IE version, if so, provide an
+ // alternative way to split lines.
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
+ var pos = 0, nl, result = [];
+ while ((nl = string.indexOf("\n", pos)) > -1) {
+ result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
+ pos = nl + 1;
+ }
+ result.push(string.slice(pos));
+ return result;
+ } : function(string){return string.split(/\r?\n/);};
+ CodeMirror.splitLines = splitLines;
+
+ var hasSelection = window.getSelection ? function(te) {
+ try { return te.selectionStart != te.selectionEnd; }
+ catch(e) { return false; }
+ } : function(te) {
+ try {var range = te.ownerDocument.selection.createRange();}
+ catch(e) {}
+ if (!range || range.parentElement() != te) return false;
+ return range.compareEndPoints("StartToEnd", range) != 0;
+ };
+
+ CodeMirror.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+ });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
+ 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
+ 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
+ 63233: "Down", 63302: "Insert", 63272: "Delete"};
+ CodeMirror.keyNames = keyNames;
+ (function() {
+ // Number keys
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
+ // Alphabetic keys
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+ // Function keys
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+ })();
+
+ return CodeMirror;
+})();
+CodeMirror.defineMode("xml", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var Kludges = parserConfig.htmlMode ? {
+ autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
+ "meta": true, "col": true, "frame": true, "base": true, "area": true},
+ doNotIndent: {"pre": true},
+ allowUnquoted: true
+ } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false};
+ var alignCDATA = parserConfig.alignCDATA;
+
+ // Return variables for tokenizers
+ var tagName, type;
+
+ function inText(stream, state) {
+ function chain(parser) {
+ state.tokenize = parser;
+ return parser(stream, state);
+ }
+
+ var ch = stream.next();
+ if (ch == "<") {
+ if (stream.eat("!")) {
+ if (stream.eat("[")) {
+ if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+ else return null;
+ }
+ else if (stream.match("--")) return chain(inBlock("comment", "-->"));
+ else if (stream.match("DOCTYPE", true, true)) {
+ stream.eatWhile(/[\w\._\-]/);
+ return chain(doctype(1));
+ }
+ else return null;
+ }
+ else if (stream.eat("?")) {
+ stream.eatWhile(/[\w\._\-]/);
+ state.tokenize = inBlock("meta", "?>");
+ return "meta";
+ }
+ else {
+ type = stream.eat("/") ? "closeTag" : "openTag";
+ stream.eatSpace();
+ tagName = "";
+ var c;
+ while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
+ state.tokenize = inTag;
+ return "tag";
+ }
+ }
+ else if (ch == "&") {
+ stream.eatWhile(/[^;]/);
+ stream.eat(";");
+ return "atom";
+ }
+ else {
+ stream.eatWhile(/[^&<]/);
+ return null;
+ }
+ }
+
+ function inTag(stream, state) {
+ var ch = stream.next();
+ if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+ state.tokenize = inText;
+ type = ch == ">" ? "endTag" : "selfcloseTag";
+ return "tag";
+ }
+ else if (ch == "=") {
+ type = "equals";
+ return null;
+ }
+ else if (/[\'\"]/.test(ch)) {
+ state.tokenize = inAttribute(ch);
+ return state.tokenize(stream, state);
+ }
+ else {
+ stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
+ return "word";
+ }
+ }
+
+ function inAttribute(quote) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.next() == quote) {
+ state.tokenize = inTag;
+ break;
+ }
+ }
+ return "string";
+ };
+ }
+
+ function inBlock(style, terminator) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.match(terminator)) {
+ state.tokenize = inText;
+ break;
+ }
+ stream.next();
+ }
+ return style;
+ };
+ }
+ function doctype(depth) {
+ return function(stream, state) {
+ var ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == "<") {
+ state.tokenize = doctype(depth + 1);
+ return state.tokenize(stream, state);
+ } else if (ch == ">") {
+ if (depth == 1) {
+ state.tokenize = inText;
+ break;
+ } else {
+ state.tokenize = doctype(depth - 1);
+ return state.tokenize(stream, state);
+ }
+ }
+ }
+ return "meta";
+ };
+ }
+
+ var curState, setStyle;
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+
+ function pushContext(tagName, startOfLine) {
+ var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
+ curState.context = {
+ prev: curState.context,
+ tagName: tagName,
+ indent: curState.indented,
+ startOfLine: startOfLine,
+ noIndent: noIndent
+ };
+ }
+ function popContext() {
+ if (curState.context) curState.context = curState.context.prev;
+ }
+
+ function element(type) {
+ if (type == "openTag") {
+ curState.tagName = tagName;
+ return cont(attributes, endtag(curState.startOfLine));
+ } else if (type == "closeTag") {
+ var err = false;
+ if (curState.context) {
+ err = curState.context.tagName != tagName;
+ } else {
+ err = true;
+ }
+ if (err) setStyle = "error";
+ return cont(endclosetag(err));
+ }
+ return cont();
+ }
+ function endtag(startOfLine) {
+ return function(type) {
+ if (type == "selfcloseTag" ||
+ (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
+ return cont();
+ if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
+ return cont();
+ };
+ }
+ function endclosetag(err) {
+ return function(type) {
+ if (err) setStyle = "error";
+ if (type == "endTag") { popContext(); return cont(); }
+ setStyle = "error";
+ return cont(arguments.callee);
+ }
+ }
+
+ function attributes(type) {
+ if (type == "word") {setStyle = "attribute"; return cont(attributes);}
+ if (type == "equals") return cont(attvalue, attributes);
+ if (type == "string") {setStyle = "error"; return cont(attributes);}
+ return pass();
+ }
+ function attvalue(type) {
+ if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
+ if (type == "string") return cont(attvaluemaybe);
+ return pass();
+ }
+ function attvaluemaybe(type) {
+ if (type == "string") return cont(attvaluemaybe);
+ else return pass();
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ state.startOfLine = true;
+ state.indented = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+
+ setStyle = type = tagName = null;
+ var style = state.tokenize(stream, state);
+ state.type = type;
+ if ((style || type) && style != "comment") {
+ curState = state;
+ while (true) {
+ var comb = state.cc.pop() || element;
+ if (comb(type || style)) break;
+ }
+ }
+ state.startOfLine = false;
+ return setStyle || style;
+ },
+
+ indent: function(state, textAfter, fullLine) {
+ var context = state.context;
+ if ((state.tokenize != inTag && state.tokenize != inText) ||
+ context && context.noIndent)
+ return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+ if (alignCDATA && /!?|]/;
+
+ function chain(stream, state, f) {
+ state.tokenize = f;
+ return f(stream, state);
+ }
+
+ function nextUntilUnescaped(stream, end) {
+ var escaped = false, next;
+ while ((next = stream.next()) != null) {
+ if (next == end && !escaped)
+ return false;
+ escaped = !escaped && next == "\\";
+ }
+ return escaped;
+ }
+
+ // Used as scratch variables to communicate multiple values without
+ // consing up tons of objects.
+ var type, content;
+ function ret(tp, style, cont) {
+ type = tp; content = cont;
+ return style;
+ }
+
+ function jsTokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch == '"' || ch == "'")
+ return chain(stream, state, jsTokenString(ch));
+ else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+ return ret(ch);
+ else if (ch == "0" && stream.eat(/x/i)) {
+ stream.eatWhile(/[\da-f]/i);
+ return ret("number", "number");
+ }
+ else if (/\d/.test(ch)) {
+ stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+ return ret("number", "number");
+ }
+ else if (ch == "/") {
+ if (stream.eat("*")) {
+ return chain(stream, state, jsTokenComment);
+ }
+ else if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ret("comment", "comment");
+ }
+ else if (state.reAllowed) {
+ nextUntilUnescaped(stream, "/");
+ stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
+ return ret("regexp", "string");
+ }
+ else {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ }
+ }
+ else if (ch == "#") {
+ stream.skipToEnd();
+ return ret("error", "error");
+ }
+ else if (isOperatorChar.test(ch)) {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ }
+ else {
+ stream.eatWhile(/[\w\$_]/);
+ var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+ return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
+ ret("variable", "variable", word);
+ }
+ }
+
+ function jsTokenString(quote) {
+ return function(stream, state) {
+ if (!nextUntilUnescaped(stream, quote))
+ state.tokenize = jsTokenBase;
+ return ret("string", "string");
+ };
+ }
+
+ function jsTokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if (ch == "/" && maybeEnd) {
+ state.tokenize = jsTokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ret("comment", "comment");
+ }
+
+ // Parser
+
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+
+ function JSLexical(indented, column, type, align, prev, info) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.prev = prev;
+ this.info = info;
+ if (align != null) this.align = align;
+ }
+
+ function inScope(state, varname) {
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return true;
+ }
+
+ function parseJS(state, style, type, content, stream) {
+ var cc = state.cc;
+ // Communicate our context to the combinators.
+ // (Less wasteful than consing up a hundred closures on every call.)
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = true;
+
+ while(true) {
+ var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+ if (combinator(type, content)) {
+ while(cc.length && cc[cc.length - 1].lex)
+ cc.pop()();
+ if (cx.marked) return cx.marked;
+ if (type == "variable" && inScope(state, content)) return "variable-2";
+ return style;
+ }
+ }
+ }
+
+ // Combinator utils
+
+ var cx = {state: null, column: null, marked: null, cc: null};
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+ function register(varname) {
+ var state = cx.state;
+ if (state.context) {
+ cx.marked = "def";
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return;
+ state.localVars = {name: varname, next: state.localVars};
+ }
+ }
+
+ // Combinators
+
+ var defaultVars = {name: "this", next: {name: "arguments"}};
+ function pushcontext() {
+ if (!cx.state.context) cx.state.localVars = defaultVars;
+ cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+ }
+ function popcontext() {
+ cx.state.localVars = cx.state.context.vars;
+ cx.state.context = cx.state.context.prev;
+ }
+ function pushlex(type, info) {
+ var result = function() {
+ var state = cx.state;
+ state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
+ };
+ result.lex = true;
+ return result;
+ }
+ function poplex() {
+ var state = cx.state;
+ if (state.lexical.prev) {
+ if (state.lexical.type == ")")
+ state.indented = state.lexical.indented;
+ state.lexical = state.lexical.prev;
+ }
+ }
+ poplex.lex = true;
+
+ function expect(wanted) {
+ return function expecting(type) {
+ if (type == wanted) return cont();
+ else if (wanted == ";") return pass();
+ else return cont(arguments.callee);
+ };
+ }
+
+ function statement(type) {
+ if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+ if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+ if (type == "{") return cont(pushlex("}"), block, poplex);
+ if (type == ";") return cont();
+ if (type == "function") return cont(functiondef);
+ if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
+ poplex, statement, poplex);
+ if (type == "variable") return cont(pushlex("stat"), maybelabel);
+ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+ block, poplex, poplex);
+ if (type == "case") return cont(expression, expect(":"));
+ if (type == "default") return cont(expect(":"));
+ if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+ statement, poplex, popcontext);
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
+ }
+ function expression(type) {
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+ if (type == "function") return cont(functiondef);
+ if (type == "keyword c") return cont(maybeexpression);
+ if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
+ if (type == "operator") return cont(expression);
+ if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
+ if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+ return cont();
+ }
+ function maybeexpression(type) {
+ if (type.match(/[;\}\)\],]/)) return pass();
+ return pass(expression);
+ }
+
+ function maybeoperator(type, value) {
+ if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
+ if (type == "operator") return cont(expression);
+ if (type == ";") return;
+ if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
+ if (type == ".") return cont(property, maybeoperator);
+ if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+ }
+ function maybelabel(type) {
+ if (type == ":") return cont(poplex, statement);
+ return pass(maybeoperator, expect(";"), poplex);
+ }
+ function property(type) {
+ if (type == "variable") {cx.marked = "property"; return cont();}
+ }
+ function objprop(type) {
+ if (type == "variable") cx.marked = "property";
+ if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+ }
+ function commasep(what, end) {
+ function proceed(type) {
+ if (type == ",") return cont(what, proceed);
+ if (type == end) return cont();
+ return cont(expect(end));
+ }
+ return function commaSeparated(type) {
+ if (type == end) return cont();
+ else return pass(what, proceed);
+ };
+ }
+ function block(type) {
+ if (type == "}") return cont();
+ return pass(statement, block);
+ }
+ function vardef1(type, value) {
+ if (type == "variable"){register(value); return cont(vardef2);}
+ return cont();
+ }
+ function vardef2(type, value) {
+ if (value == "=") return cont(expression, vardef2);
+ if (type == ",") return cont(vardef1);
+ }
+ function forspec1(type) {
+ if (type == "var") return cont(vardef1, forspec2);
+ if (type == ";") return pass(forspec2);
+ if (type == "variable") return cont(formaybein);
+ return pass(forspec2);
+ }
+ function formaybein(type, value) {
+ if (value == "in") return cont(expression);
+ return cont(maybeoperator, forspec2);
+ }
+ function forspec2(type, value) {
+ if (type == ";") return cont(forspec3);
+ if (value == "in") return cont(expression);
+ return cont(expression, expect(";"), forspec3);
+ }
+ function forspec3(type) {
+ if (type != ")") cont(expression);
+ }
+ function functiondef(type, value) {
+ if (type == "variable") {register(value); return cont(functiondef);}
+ if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
+ }
+ function funarg(type, value) {
+ if (type == "variable") {register(value); return cont();}
+ }
+
+ // Interface
+
+ return {
+ startState: function(basecolumn) {
+ return {
+ tokenize: jsTokenBase,
+ reAllowed: true,
+ kwAllowed: true,
+ cc: [],
+ lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+ localVars: null,
+ context: null,
+ indented: 0
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = false;
+ state.indented = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ if (type == "comment") return style;
+ state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
+ state.kwAllowed = type != '.';
+ return parseJS(state, style, type, content, stream);
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != jsTokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
+ type = lexical.type, closing = firstChar == type;
+ if (type == "vardef") return lexical.indented + 4;
+ else if (type == "form" && firstChar == "{") return lexical.indented;
+ else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
+ else if (lexical.info == "switch" && !closing)
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+ else return lexical.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricChars: ":{}"
+ };
+});
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+
+CodeMirror.defineMode("css", function(config) {
+ var indentUnit = config.indentUnit, type;
+ function ret(style, tp) {type = tp; return style;}
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
+ else if (ch == "/" && stream.eat("*")) {
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ }
+ else if (ch == "<" && stream.eat("!")) {
+ state.tokenize = tokenSGMLComment;
+ return tokenSGMLComment(stream, state);
+ }
+ else if (ch == "=") ret(null, "compare");
+ else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
+ else if (ch == "\"" || ch == "'") {
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+ }
+ else if (ch == "#") {
+ stream.eatWhile(/[\w\\\-]/);
+ return ret("atom", "hash");
+ }
+ else if (ch == "!") {
+ stream.match(/^\s*\w*/);
+ return ret("keyword", "important");
+ }
+ else if (/\d/.test(ch)) {
+ stream.eatWhile(/[\w.%]/);
+ return ret("number", "unit");
+ }
+ else if (/[,.+>*\/]/.test(ch)) {
+ return ret(null, "select-op");
+ }
+ else if (/[;{}:\[\]]/.test(ch)) {
+ return ret(null, ch);
+ }
+ else {
+ stream.eatWhile(/[\w\\\-]/);
+ return ret("variable", "variable");
+ }
+ }
+
+ function tokenCComment(stream, state) {
+ var maybeEnd = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (maybeEnd && ch == "/") {
+ state.tokenize = tokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ret("comment", "comment");
+ }
+
+ function tokenSGMLComment(stream, state) {
+ var dashes = 0, ch;
+ while ((ch = stream.next()) != null) {
+ if (dashes >= 2 && ch == ">") {
+ state.tokenize = tokenBase;
+ break;
+ }
+ dashes = (ch == "-") ? dashes + 1 : 0;
+ }
+ return ret("comment", "comment");
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && !escaped)
+ break;
+ escaped = !escaped && ch == "\\";
+ }
+ if (!escaped) state.tokenize = tokenBase;
+ return ret("string", "string");
+ };
+ }
+
+ return {
+ startState: function(base) {
+ return {tokenize: tokenBase,
+ baseIndent: base || 0,
+ stack: []};
+ },
+
+ token: function(stream, state) {
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+
+ var context = state.stack[state.stack.length-1];
+ if (type == "hash" && context == "rule") style = "atom";
+ else if (style == "variable") {
+ if (context == "rule") style = "number";
+ else if (!context || context == "@media{") style = "tag";
+ }
+
+ if (context == "rule" && /^[\{\};]$/.test(type))
+ state.stack.pop();
+ if (type == "{") {
+ if (context == "@media") state.stack[state.stack.length-1] = "@media{";
+ else state.stack.push("{");
+ }
+ else if (type == "}") state.stack.pop();
+ else if (type == "@media") state.stack.push("@media");
+ else if (context == "{" && type != "comment") state.stack.push("rule");
+ return style;
+ },
+
+ indent: function(state, textAfter) {
+ var n = state.stack.length;
+ if (/^\}/.test(textAfter))
+ n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
+ return state.baseIndent + n * indentUnit;
+ },
+
+ electricChars: "}"
+ };
+});
+
+CodeMirror.defineMIME("text/css", "css");
+CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
+ var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
+ var jsMode = CodeMirror.getMode(config, "javascript");
+ var cssMode = CodeMirror.getMode(config, "css");
+
+ function html(stream, state) {
+ var style = htmlMode.token(stream, state.htmlState);
+ if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
+ if (/^script$/i.test(state.htmlState.context.tagName)) {
+ state.token = javascript;
+ state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
+ state.mode = "javascript";
+ }
+ else if (/^style$/i.test(state.htmlState.context.tagName)) {
+ state.token = css;
+ state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
+ state.mode = "css";
+ }
+ }
+ return style;
+ }
+ function maybeBackup(stream, pat, style) {
+ var cur = stream.current();
+ var close = cur.search(pat);
+ if (close > -1) stream.backUp(cur.length - close);
+ return style;
+ }
+ function javascript(stream, state) {
+ if (stream.match(/^<\/\s*script\s*>/i, false)) {
+ state.token = html;
+ state.curState = null;
+ state.mode = "html";
+ return html(stream, state);
+ }
+ return maybeBackup(stream, /<\/\s*script\s*>/,
+ jsMode.token(stream, state.localState));
+ }
+ function css(stream, state) {
+ if (stream.match(/^<\/\s*style\s*>/i, false)) {
+ state.token = html;
+ state.localState = null;
+ state.mode = "html";
+ return html(stream, state);
+ }
+ return maybeBackup(stream, /<\/\s*style\s*>/,
+ cssMode.token(stream, state.localState));
+ }
+
+ return {
+ startState: function() {
+ var state = htmlMode.startState();
+ return {token: html, localState: null, mode: "html", htmlState: state};
+ },
+
+ copyState: function(state) {
+ if (state.localState)
+ var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
+ return {token: state.token, localState: local, mode: state.mode,
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+ },
+
+ token: function(stream, state) {
+ return state.token(stream, state);
+ },
+
+ indent: function(state, textAfter) {
+ if (state.token == html || /^\s*<\//.test(textAfter))
+ return htmlMode.indent(state.htmlState, textAfter);
+ else if (state.token == javascript)
+ return jsMode.indent(state.localState, textAfter);
+ else
+ return cssMode.indent(state.localState, textAfter);
+ },
+
+ compareStates: function(a, b) {
+ return htmlMode.compareStates(a.htmlState, b.htmlState);
+ },
+
+ electricChars: "/{}:"
+ }
+});
+
+CodeMirror.defineMIME("text/html", "htmlmixed");
diff --git a/assets/third-party/video-js/video-js.swf b/assets/third-party/video-js/video-js.swf
index eef460a..9cf537a 100644
Binary files a/assets/third-party/video-js/video-js.swf and b/assets/third-party/video-js/video-js.swf differ
diff --git a/assets/third-party/xss.min.js b/assets/third-party/xss.min.js
new file mode 100644
index 0000000..48d7880
--- /dev/null
+++ b/assets/third-party/xss.min.js
@@ -0,0 +1 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o/g;var REGEXP_QUOTE=/"/g;var REGEXP_QUOTE_2=/"/g;var REGEXP_ATTR_VALUE_1=/([a-zA-Z0-9]*);?/gim;var REGEXP_ATTR_VALUE_COLON=/:?/gim;var REGEXP_ATTR_VALUE_NEWLINE=/&newline;?/gim;var REGEXP_DEFAULT_ON_TAG_ATTR_3=/\/\*|\*\//gm;var REGEXP_DEFAULT_ON_TAG_ATTR_4=/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_5=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_6=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;var REGEXP_DEFAULT_ON_TAG_ATTR_7=/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_8=/u\s*r\s*l\s*\(.*/gi;function escapeQuote(str){return str.replace(REGEXP_QUOTE,""")}function unescapeQuote(str){return str.replace(REGEXP_QUOTE_2,'"')}function escapeHtmlEntities(str){return str.replace(REGEXP_ATTR_VALUE_1,function replaceUnicode(str,code){return code[0]==="x"||code[0]==="X"?String.fromCharCode(parseInt(code.substr(1),16)):String.fromCharCode(parseInt(code,10))})}function escapeDangerHtml5Entities(str){return str.replace(REGEXP_ATTR_VALUE_COLON,":").replace(REGEXP_ATTR_VALUE_NEWLINE," ")}function clearNonPrintableCharacter(str){var str2="";for(var i=0,len=str.length;i/g;function stripBlankChar(html){var chars=html.split("");chars=chars.filter(function(char){var c=char.charCodeAt(0);if(c===127)return false;if(c<=31){if(c===10||c===13)return true;return false}return true});return chars.join("")}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onTag=onTag;exports.onIgnoreTag=onIgnoreTag;exports.onTagAttr=onTagAttr;exports.onIgnoreTagAttr=onIgnoreTagAttr;exports.safeAttrValue=safeAttrValue;exports.escapeHtml=escapeHtml;exports.escapeQuote=escapeQuote;exports.unescapeQuote=unescapeQuote;exports.escapeHtmlEntities=escapeHtmlEntities;exports.escapeDangerHtml5Entities=escapeDangerHtml5Entities;exports.clearNonPrintableCharacter=clearNonPrintableCharacter;exports.friendlyAttrValue=friendlyAttrValue;exports.escapeAttrValue=escapeAttrValue;exports.onIgnoreTagStripAll=onIgnoreTagStripAll;exports.StripTagBody=StripTagBody;exports.stripCommentTag=stripCommentTag;exports.stripBlankChar=stripBlankChar;exports.cssFilter=defaultCSSFilter},{"./util":4,cssfilter:8}],2:[function(require,module,exports){var DEFAULT=require("./default");var parser=require("./parser");var FilterXSS=require("./xss");function filterXSS(html,options){var xss=new FilterXSS(options);return xss.process(html)}exports=module.exports=filterXSS;exports.FilterXSS=FilterXSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];for(var i in parser)exports[i]=parser[i];if(typeof window!=="undefined"){window.filterXSS=module.exports}},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){var _=require("./util");function getTagName(html){var i=html.indexOf(" ");if(i===-1){var tagName=html.slice(1,-1)}else{var tagName=html.slice(1,i+1)}tagName=_.trim(tagName).toLowerCase();if(tagName.slice(0,1)==="/")tagName=tagName.slice(1);if(tagName.slice(-1)==="/")tagName=tagName.slice(0,-1);return tagName}function isClosing(html){return html.slice(0,2)===""}function parseTag(html,onTag,escapeHtml){"user strict";var rethtml="";var lastPos=0;var tagStart=false;var quoteStart=false;var currentPos=0;var len=html.length;var currentHtml="";var currentTagName="";for(currentPos=0;currentPos"){rethtml+=escapeHtml(html.slice(lastPos,tagStart));currentHtml=html.slice(tagStart,currentPos+1);currentTagName=getTagName(currentHtml);rethtml+=onTag(tagStart,rethtml.length,currentTagName,currentHtml,isClosing(currentHtml));lastPos=currentPos+1;tagStart=false;continue}if((c==='"'||c==="'")&&html.charAt(currentPos-1)==="="){quoteStart=c;continue}}else{if(c===quoteStart){quoteStart=false;continue}}}}if(lastPos0;i--){var c=str[i];if(c===" ")continue;if(c==="=")return i;return-1}}function isQuoteWrapString(text){if(text[0]==='"'&&text[text.length-1]==='"'||text[0]==="'"&&text[text.length-1]==="'"){return true}else{return false}}function stripQuoteWrap(text){if(isQuoteWrapString(text)){return text.substr(1,text.length-2)}else{return text}}exports.parseTag=parseTag;exports.parseAttr=parseAttr},{"./util":4}],4:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i"}var attrs=getAttrs(html);var whiteAttrList=whiteList[tag];var attrsHtml=parseAttr(attrs.html,function(name,value){var isWhiteAttr=_.indexOf(whiteAttrList,name)!==-1;var ret=onTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;if(isWhiteAttr){value=safeAttrValue(tag,name,value,cssFilter);if(value){return name+'="'+value+'"'}else{return name}}else{var ret=onIgnoreTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;return}});var html="<"+tag;if(attrsHtml)html+=" "+attrsHtml;if(attrs.closing)html+=" /";html+=">";return html}else{var ret=onIgnoreTag(tag,html,info);if(!isNull(ret))return ret;return escapeHtml(html)}},escapeHtml);if(stripIgnoreTagBody){retHtml=stripIgnoreTagBody.remove(retHtml)}return retHtml};module.exports=FilterXSS},{"./default":1,"./parser":3,"./util":4,cssfilter:8}],6:[function(require,module,exports){var DEFAULT=require("./default");var parseStyle=require("./parser");var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function FilterCSS(options){options=options||{};options.whiteList=options.whiteList||DEFAULT.whiteList;options.onAttr=options.onAttr||DEFAULT.onAttr;options.onIgnoreAttr=options.onIgnoreAttr||DEFAULT.onIgnoreAttr;this.options=options}FilterCSS.prototype.process=function(css){css=css||"";css=css.toString();if(!css)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onAttr=options.onAttr;var onIgnoreAttr=options.onIgnoreAttr;var retCSS=parseStyle(css,function(sourcePosition,position,name,value,source){var check=whiteList[name];var isWhite=false;if(check===true)isWhite=check;else if(typeof check==="function")isWhite=check(value);else if(check instanceof RegExp)isWhite=check.test(value);if(isWhite!==true)isWhite=false;var opts={position:position,sourcePosition:sourcePosition,source:source,isWhite:isWhite};if(isWhite){var ret=onAttr(name,value,opts);if(isNull(ret)){return name+":"+value}else{return ret}}else{var ret=onIgnoreAttr(name,value,opts);if(!isNull(ret)){return ret}}});return retCSS};module.exports=FilterCSS},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){function getDefaultWhiteList(){var whiteList={};whiteList["align-content"]=false;whiteList["align-items"]=false;whiteList["align-self"]=false;whiteList["alignment-adjust"]=false;whiteList["alignment-baseline"]=false;whiteList["all"]=false;whiteList["anchor-point"]=false;whiteList["animation"]=false;whiteList["animation-delay"]=false;whiteList["animation-direction"]=false;whiteList["animation-duration"]=false;whiteList["animation-fill-mode"]=false;whiteList["animation-iteration-count"]=false;whiteList["animation-name"]=false;whiteList["animation-play-state"]=false;whiteList["animation-timing-function"]=false;whiteList["azimuth"]=false;whiteList["backface-visibility"]=false;whiteList["background"]=true;whiteList["background-attachment"]=true;whiteList["background-clip"]=true;whiteList["background-color"]=true;whiteList["background-image"]=true;whiteList["background-origin"]=true;whiteList["background-position"]=true;whiteList["background-repeat"]=true;whiteList["background-size"]=true;whiteList["baseline-shift"]=false;whiteList["binding"]=false;whiteList["bleed"]=false;whiteList["bookmark-label"]=false;whiteList["bookmark-level"]=false;whiteList["bookmark-state"]=false;whiteList["border"]=true;whiteList["border-bottom"]=true;whiteList["border-bottom-color"]=true;whiteList["border-bottom-left-radius"]=true;whiteList["border-bottom-right-radius"]=true;whiteList["border-bottom-style"]=true;whiteList["border-bottom-width"]=true;whiteList["border-collapse"]=true;whiteList["border-color"]=true;whiteList["border-image"]=true;whiteList["border-image-outset"]=true;whiteList["border-image-repeat"]=true;whiteList["border-image-slice"]=true;whiteList["border-image-source"]=true;whiteList["border-image-width"]=true;whiteList["border-left"]=true;whiteList["border-left-color"]=true;whiteList["border-left-style"]=true;whiteList["border-left-width"]=true;whiteList["border-radius"]=true;whiteList["border-right"]=true;whiteList["border-right-color"]=true;whiteList["border-right-style"]=true;whiteList["border-right-width"]=true;whiteList["border-spacing"]=true;whiteList["border-style"]=true;whiteList["border-top"]=true;whiteList["border-top-color"]=true;whiteList["border-top-left-radius"]=true;whiteList["border-top-right-radius"]=true;whiteList["border-top-style"]=true;whiteList["border-top-width"]=true;whiteList["border-width"]=true;whiteList["bottom"]=false;whiteList["box-decoration-break"]=true;whiteList["box-shadow"]=true;whiteList["box-sizing"]=true;whiteList["box-snap"]=true;whiteList["box-suppress"]=true;whiteList["break-after"]=true;whiteList["break-before"]=true;whiteList["break-inside"]=true;whiteList["caption-side"]=false;whiteList["chains"]=false;whiteList["clear"]=true;whiteList["clip"]=false;whiteList["clip-path"]=false;whiteList["clip-rule"]=false;whiteList["color"]=true;whiteList["color-interpolation-filters"]=true;whiteList["column-count"]=false;whiteList["column-fill"]=false;whiteList["column-gap"]=false;whiteList["column-rule"]=false;whiteList["column-rule-color"]=false;whiteList["column-rule-style"]=false;whiteList["column-rule-width"]=false;whiteList["column-span"]=false;whiteList["column-width"]=false;whiteList["columns"]=false;whiteList["contain"]=false;whiteList["content"]=false;whiteList["counter-increment"]=false;whiteList["counter-reset"]=false;whiteList["counter-set"]=false;whiteList["crop"]=false;whiteList["cue"]=false;whiteList["cue-after"]=false;whiteList["cue-before"]=false;whiteList["cursor"]=false;whiteList["direction"]=false;whiteList["display"]=true;whiteList["display-inside"]=true;whiteList["display-list"]=true;whiteList["display-outside"]=true;whiteList["dominant-baseline"]=false;whiteList["elevation"]=false;whiteList["empty-cells"]=false;whiteList["filter"]=false;whiteList["flex"]=false;whiteList["flex-basis"]=false;whiteList["flex-direction"]=false;whiteList["flex-flow"]=false;whiteList["flex-grow"]=false;whiteList["flex-shrink"]=false;whiteList["flex-wrap"]=false;whiteList["float"]=false;whiteList["float-offset"]=false;whiteList["flood-color"]=false;whiteList["flood-opacity"]=false;whiteList["flow-from"]=false;whiteList["flow-into"]=false;whiteList["font"]=true;whiteList["font-family"]=true;whiteList["font-feature-settings"]=true;whiteList["font-kerning"]=true;whiteList["font-language-override"]=true;whiteList["font-size"]=true;whiteList["font-size-adjust"]=true;whiteList["font-stretch"]=true;whiteList["font-style"]=true;whiteList["font-synthesis"]=true;whiteList["font-variant"]=true;whiteList["font-variant-alternates"]=true;whiteList["font-variant-caps"]=true;whiteList["font-variant-east-asian"]=true;whiteList["font-variant-ligatures"]=true;whiteList["font-variant-numeric"]=true;whiteList["font-variant-position"]=true;whiteList["font-weight"]=true;whiteList["grid"]=false;whiteList["grid-area"]=false;whiteList["grid-auto-columns"]=false;whiteList["grid-auto-flow"]=false;whiteList["grid-auto-rows"]=false;whiteList["grid-column"]=false;whiteList["grid-column-end"]=false;whiteList["grid-column-start"]=false;whiteList["grid-row"]=false;whiteList["grid-row-end"]=false;whiteList["grid-row-start"]=false;whiteList["grid-template"]=false;whiteList["grid-template-areas"]=false;whiteList["grid-template-columns"]=false;whiteList["grid-template-rows"]=false;whiteList["hanging-punctuation"]=false;whiteList["height"]=true;whiteList["hyphens"]=false;whiteList["icon"]=false;whiteList["image-orientation"]=false;whiteList["image-resolution"]=false;whiteList["ime-mode"]=false;whiteList["initial-letters"]=false;whiteList["inline-box-align"]=false;whiteList["justify-content"]=false;whiteList["justify-items"]=false;whiteList["justify-self"]=false;whiteList["left"]=false;whiteList["letter-spacing"]=true;whiteList["lighting-color"]=true;whiteList["line-box-contain"]=false;whiteList["line-break"]=false;whiteList["line-grid"]=false;whiteList["line-height"]=false;whiteList["line-snap"]=false;whiteList["line-stacking"]=false;whiteList["line-stacking-ruby"]=false;whiteList["line-stacking-shift"]=false;whiteList["line-stacking-strategy"]=false;whiteList["list-style"]=true;whiteList["list-style-image"]=true;whiteList["list-style-position"]=true;whiteList["list-style-type"]=true;whiteList["margin"]=true;whiteList["margin-bottom"]=true;whiteList["margin-left"]=true;whiteList["margin-right"]=true;whiteList["margin-top"]=true;whiteList["marker-offset"]=false;whiteList["marker-side"]=false;whiteList["marks"]=false;whiteList["mask"]=false;whiteList["mask-box"]=false;whiteList["mask-box-outset"]=false;whiteList["mask-box-repeat"]=false;whiteList["mask-box-slice"]=false;whiteList["mask-box-source"]=false;whiteList["mask-box-width"]=false;whiteList["mask-clip"]=false;whiteList["mask-image"]=false;whiteList["mask-origin"]=false;whiteList["mask-position"]=false;whiteList["mask-repeat"]=false;whiteList["mask-size"]=false;whiteList["mask-source-type"]=false;whiteList["mask-type"]=false;whiteList["max-height"]=true;whiteList["max-lines"]=false;whiteList["max-width"]=true;whiteList["min-height"]=true;whiteList["min-width"]=true;whiteList["move-to"]=false;whiteList["nav-down"]=false;whiteList["nav-index"]=false;whiteList["nav-left"]=false;whiteList["nav-right"]=false;whiteList["nav-up"]=false;whiteList["object-fit"]=false;whiteList["object-position"]=false;whiteList["opacity"]=false;whiteList["order"]=false;whiteList["orphans"]=false;whiteList["outline"]=false;whiteList["outline-color"]=false;whiteList["outline-offset"]=false;whiteList["outline-style"]=false;whiteList["outline-width"]=false;whiteList["overflow"]=false;whiteList["overflow-wrap"]=false;whiteList["overflow-x"]=false;whiteList["overflow-y"]=false;whiteList["padding"]=true;whiteList["padding-bottom"]=true;whiteList["padding-left"]=true;whiteList["padding-right"]=true;whiteList["padding-top"]=true;whiteList["page"]=false;whiteList["page-break-after"]=false;whiteList["page-break-before"]=false;whiteList["page-break-inside"]=false;whiteList["page-policy"]=false;whiteList["pause"]=false;whiteList["pause-after"]=false;whiteList["pause-before"]=false;whiteList["perspective"]=false;whiteList["perspective-origin"]=false;whiteList["pitch"]=false;whiteList["pitch-range"]=false;whiteList["play-during"]=false;whiteList["position"]=false;whiteList["presentation-level"]=false;whiteList["quotes"]=false;whiteList["region-fragment"]=false;whiteList["resize"]=false;whiteList["rest"]=false;whiteList["rest-after"]=false;whiteList["rest-before"]=false;whiteList["richness"]=false;whiteList["right"]=false;whiteList["rotation"]=false;whiteList["rotation-point"]=false;whiteList["ruby-align"]=false;whiteList["ruby-merge"]=false;whiteList["ruby-position"]=false;whiteList["shape-image-threshold"]=false;whiteList["shape-outside"]=false;whiteList["shape-margin"]=false;whiteList["size"]=false;whiteList["speak"]=false;whiteList["speak-as"]=false;whiteList["speak-header"]=false;whiteList["speak-numeral"]=false;whiteList["speak-punctuation"]=false;whiteList["speech-rate"]=false;whiteList["stress"]=false;whiteList["string-set"]=false;whiteList["tab-size"]=false;whiteList["table-layout"]=false;whiteList["text-align"]=true;whiteList["text-align-last"]=true;whiteList["text-combine-upright"]=true;whiteList["text-decoration"]=true;whiteList["text-decoration-color"]=true;whiteList["text-decoration-line"]=true;whiteList["text-decoration-skip"]=true;whiteList["text-decoration-style"]=true;whiteList["text-emphasis"]=true;whiteList["text-emphasis-color"]=true;whiteList["text-emphasis-position"]=true;whiteList["text-emphasis-style"]=true;whiteList["text-height"]=true;whiteList["text-indent"]=true;whiteList["text-justify"]=true;whiteList["text-orientation"]=true;whiteList["text-overflow"]=true;whiteList["text-shadow"]=true;whiteList["text-space-collapse"]=true;whiteList["text-transform"]=true;whiteList["text-underline-position"]=true;whiteList["text-wrap"]=true;whiteList["top"]=false;whiteList["transform"]=false;whiteList["transform-origin"]=false;whiteList["transform-style"]=false;whiteList["transition"]=false;whiteList["transition-delay"]=false;whiteList["transition-duration"]=false;whiteList["transition-property"]=false;whiteList["transition-timing-function"]=false;whiteList["unicode-bidi"]=false;whiteList["vertical-align"]=false;whiteList["visibility"]=false;whiteList["voice-balance"]=false;whiteList["voice-duration"]=false;whiteList["voice-family"]=false;whiteList["voice-pitch"]=false;whiteList["voice-range"]=false;whiteList["voice-rate"]=false;whiteList["voice-stress"]=false;whiteList["voice-volume"]=false;whiteList["volume"]=false;whiteList["white-space"]=false;whiteList["widows"]=false;whiteList["width"]=true;whiteList["will-change"]=false;whiteList["word-break"]=true;whiteList["word-spacing"]=true;whiteList["word-wrap"]=true;whiteList["wrap-flow"]=false;whiteList["wrap-through"]=false;whiteList["writing-mode"]=false;whiteList["z-index"]=false;return whiteList}function onAttr(name,value,options){}function onIgnoreAttr(name,value,options){}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onAttr=onAttr;exports.onIgnoreAttr=onIgnoreAttr},{}],8:[function(require,module,exports){var DEFAULT=require("./default");var FilterCSS=require("./css");function filterCSS(html,options){var xss=new FilterCSS(options);return xss.process(html)}exports=module.exports=filterCSS;exports.FilterCSS=FilterCSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];if(typeof window!=="undefined"){window.filterCSS=module.exports}},{"./css":6,"./default":7}],9:[function(require,module,exports){var _=require("./util");function parseStyle(css,onAttr){css=_.trimRight(css);if(css[css.length-1]!==";")css+=";";var cssLength=css.length;var isParenthesisOpen=false;var lastPos=0;var i=0;var retCSS="";function addNewAttr(){if(!isParenthesisOpen){var source=_.trim(css.slice(lastPos,i));var j=source.indexOf(":");if(j!==-1){var name=_.trim(source.slice(0,j));var value=_.trim(source.slice(j+1));if(name){var ret=onAttr(lastPos,retCSS.length,name,value,source);if(ret)retCSS+=ret+"; "}}}lastPos=i+1}for(;i -1 ),
-
- /**
- * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
- * @example
- * ```javascript
- * if ( UE.browser.mac ) {
- * console.log( '当前浏览器运行在mac平台下' );
- * }
- * ```
- */
- mac : ( agent.indexOf( 'macintosh' ) > -1 ),
-
- /**
- * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
- * @example
- * ```javascript
- * if ( UE.browser.quirks ) {
- * console.log( '当前浏览器运行处于“怪异模式”' );
- * }
- * ```
- */
- quirks : ( document.compatMode == 'BackCompat' )
- };
-
- /**
- * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
- * @example
- * ```javascript
- * if ( UE.browser.gecko ) {
- * console.log( '当前浏览器内核是gecko内核' );
- * }
- * ```
- */
- browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
-
- var version = 0;
-
- // Internet Explorer 6.0+
- if ( browser.ie ){
-
- var v1 = agent.match(/(?:msie\s([\w.]+))/);
- var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
- if(v1 && v2 && v1[1] && v2[1]){
- version = Math.max(v1[1]*1,v2[1]*1);
- }else if(v1 && v1[1]){
- version = v1[1]*1;
- }else if(v2 && v2[1]){
- version = v2[1]*1;
- }else{
- version = 0;
- }
-
- browser.ie11Compat = document.documentMode == 11;
- /**
- * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
- * @warning 如果浏览器不是IE, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.ie9Compat ) {
- * console.log( '当前浏览器运行在IE9兼容模式下' );
- * }
- * ```
- */
- browser.ie9Compat = document.documentMode == 9;
-
- /**
- * @property { boolean } ie8 检测浏览器是否是IE8浏览器
- * @warning 如果浏览器不是IE, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.ie8 ) {
- * console.log( '当前浏览器是IE8浏览器' );
- * }
- * ```
- */
- browser.ie8 = !!document.documentMode;
-
- /**
- * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
- * @warning 如果浏览器不是IE, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.ie8Compat ) {
- * console.log( '当前浏览器运行在IE8兼容模式下' );
- * }
- * ```
- */
- browser.ie8Compat = document.documentMode == 8;
-
- /**
- * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
- * @warning 如果浏览器不是IE, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.ie7Compat ) {
- * console.log( '当前浏览器运行在IE7兼容模式下' );
- * }
- * ```
- */
- browser.ie7Compat = ( ( version == 7 && !document.documentMode )
- || document.documentMode == 7 );
-
- /**
- * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
- * @warning 如果浏览器不是IE, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.ie6Compat ) {
- * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
- * }
- * ```
- */
- browser.ie6Compat = ( version < 7 || browser.quirks );
-
- browser.ie9above = version > 8;
-
- browser.ie9below = version < 9;
-
- browser.ie11above = version > 10;
-
- browser.ie11below = version < 11;
-
- }
-
- // Gecko.
- if ( browser.gecko ){
- var geckoRelease = agent.match( /rv:([\d\.]+)/ );
- if ( geckoRelease )
- {
- geckoRelease = geckoRelease[1].split( '.' );
- version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
- }
- }
-
- /**
- * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
- * @warning 如果浏览器不是chrome, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.chrome ) {
- * console.log( '当前浏览器是Chrome' );
- * }
- * ```
- */
- if (/chrome\/(\d+\.\d)/i.test(agent)) {
- browser.chrome = + RegExp['\x241'];
- }
-
- /**
- * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
- * @warning 如果浏览器不是safari, 则该值为undefined
- * @example
- * ```javascript
- * if ( UE.browser.safari ) {
- * console.log( '当前浏览器是Safari' );
- * }
- * ```
- */
- if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
- browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
- }
-
-
- // Opera 9.50+
- if ( browser.opera )
- version = parseFloat( opera.version() );
-
- // WebKit 522+ (Safari 3+)
- if ( browser.webkit )
- version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
-
- /**
- * @property { Number } version 检测当前浏览器版本号
- * @remind
- *
- * - IE系列返回值为5,6,7,8,9,10等
- * - gecko系列会返回10900,158900等
- * - webkit系列会返回其build号 (如 522等)
- *
- * @example
- * ```javascript
- * console.log( '当前浏览器版本号是: ' + UE.browser.version );
- * ```
- */
- browser.version = version;
-
- /**
- * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
- * @example
- * ```javascript
- * if ( UE.browser.isCompatible ) {
- * console.log( '浏览器与UEditor能够良好兼容' );
- * }
- * ```
- */
- browser.isCompatible =
- !browser.mobile && (
- ( browser.ie && version >= 6 ) ||
- ( browser.gecko && version >= 10801 ) ||
- ( browser.opera && version >= 9.5 ) ||
- ( browser.air && version >= 1 ) ||
- ( browser.webkit && version >= 522 ) ||
- false );
- return browser;
-}();
-//快捷方式
-var ie = browser.ie,
- webkit = browser.webkit,
- gecko = browser.gecko,
- opera = browser.opera;
-
-// core/utils.js
-/**
- * 工具函数包
- * @file
- * @module UE.utils
- * @since 1.2.6.1
- */
-
-/**
- * UEditor封装使用的静态工具函数
- * @module UE.utils
- * @unfile
- */
-
-var utils = UE.utils = {
-
- /**
- * 用给定的迭代器遍历对象
- * @method each
- * @param { Object } obj 需要遍历的对象
- * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
- * @example
- * ```javascript
- * var demoObj = {
- * key1: 1,
- * key2: 2
- * };
- *
- * //output: key1: 1, key2: 2
- * UE.utils.each( demoObj, funciton ( value, key ) {
- *
- * console.log( key + ":" + value );
- *
- * } );
- * ```
- */
-
- /**
- * 用给定的迭代器遍历数组或类数组对象
- * @method each
- * @param { Array } array 需要遍历的数组或者类数组
- * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
- * @example
- * ```javascript
- * var divs = document.getElmentByTagNames( "div" );
- *
- * //output: 0: DIV, 1: DIV ...
- * UE.utils.each( divs, funciton ( value, key ) {
- *
- * console.log( key + ":" + value.tagName );
- *
- * } );
- * ```
- */
- each : function(obj, iterator, context) {
- if (obj == null) return;
- if (obj.length === +obj.length) {
- for (var i = 0, l = obj.length; i < l; i++) {
- if(iterator.call(context, obj[i], i, obj) === false)
- return false;
- }
- } else {
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- if(iterator.call(context, obj[key], key, obj) === false)
- return false;
- }
- }
- }
- },
-
- /**
- * 以给定对象作为原型创建一个新对象
- * @method makeInstance
- * @param { Object } protoObject 该对象将作为新创建对象的原型
- * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
- * @example
- * ```javascript
- *
- * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
- *
- * var newObject = UE.utils.makeInstance( protoObject );
- * //output: Hello UEditor!
- * newObject.sayHello();
- * ```
- */
- makeInstance:function (obj) {
- var noop = new Function();
- noop.prototype = obj;
- obj = new noop;
- noop.prototype = null;
- return obj;
- },
-
- /**
- * 将source对象中的属性扩展到target对象上
- * @method extend
- * @remind 该方法将强制把source对象上的属性复制到target对象上
- * @see UE.utils.extend(Object,Object,Boolean)
- * @param { Object } target 目标对象, 新的属性将附加到该对象上
- * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
- * @return { Object } 返回target对象
- * @example
- * ```javascript
- *
- * var target = { name: 'target', sex: 1 },
- * source = { name: 'source', age: 17 };
- *
- * UE.utils.extend( target, source );
- *
- * //output: { name: 'source', sex: 1, age: 17 }
- * console.log( target );
- *
- * ```
- */
-
- /**
- * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
- * 源对象属性名相同的属性值。
- * @method extend
- * @param { Object } target 目标对象, 新的属性将附加到该对象上
- * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
- * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
- * @return { Object } 返回target对象
- * @example
- * ```javascript
- *
- * var target = { name: 'target', sex: 1 },
- * source = { name: 'source', age: 17 };
- *
- * UE.utils.extend( target, source, true );
- *
- * //output: { name: 'target', sex: 1, age: 17 }
- * console.log( target );
- *
- * ```
- */
- extend:function (t, s, b) {
- if (s) {
- for (var k in s) {
- if (!b || !t.hasOwnProperty(k)) {
- t[k] = s[k];
- }
- }
- }
- return t;
- },
-
- /**
- * 将给定的多个对象的属性复制到目标对象target上
- * @method extend2
- * @remind 该方法将强制把源对象上的属性复制到target对象上
- * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
- * 将会覆盖掉之前的值。
- * @param { Object } target 目标对象, 新的属性将附加到该对象上
- * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
- * @return { Object } 返回target对象
- * @example
- * ```javascript
- *
- * var target = {},
- * source1 = { name: 'source', age: 17 },
- * source2 = { title: 'dev' };
- *
- * UE.utils.extend2( target, source1, source2 );
- *
- * //output: { name: 'source', age: 17, title: 'dev' }
- * console.log( target );
- *
- * ```
- */
- extend2:function (t) {
- var a = arguments;
- for (var i = 1; i < a.length; i++) {
- var x = a[i];
- for (var k in x) {
- if (!t.hasOwnProperty(k)) {
- t[k] = x[k];
- }
- }
- }
- return t;
- },
-
- /**
- * 模拟继承机制, 使得subClass继承自superClass
- * @method inherits
- * @param { Object } subClass 子类对象
- * @param { Object } superClass 超类对象
- * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
- * @return { Object } 继承superClass后的子类对象
- * @example
- * ```javascript
- * function SuperClass(){
- * this.name = "小李";
- * }
- *
- * SuperClass.prototype = {
- * hello:function(str){
- * console.log(this.name + str);
- * }
- * }
- *
- * function SubClass(){
- * this.name = "小张";
- * }
- *
- * UE.utils.inherits(SubClass,SuperClass);
- *
- * var sub = new SubClass();
- * //output: '小张早上好!
- * sub.hello("早上好!");
- * ```
- */
- inherits:function (subClass, superClass) {
- var oldP = subClass.prototype,
- newP = utils.makeInstance(superClass.prototype);
- utils.extend(newP, oldP, true);
- subClass.prototype = newP;
- return (newP.constructor = subClass);
- },
-
- /**
- * 用指定的context对象作为函数fn的上下文
- * @method bind
- * @param { Function } fn 需要绑定上下文的函数对象
- * @param { Object } content 函数fn新的上下文对象
- * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
- * @example
- * ```javascript
- *
- * var name = 'window',
- * newTest = null;
- *
- * function test () {
- * console.log( this.name );
- * }
- *
- * newTest = UE.utils.bind( test, { name: 'object' } );
- *
- * //output: object
- * newTest();
- *
- * //output: window
- * test();
- *
- * ```
- */
- bind:function (fn, context) {
- return function () {
- return fn.apply(context, arguments);
- };
- },
-
- /**
- * 创建延迟指定时间后执行的函数fn
- * @method defer
- * @param { Function } fn 需要延迟执行的函数对象
- * @param { int } delay 延迟的时间, 单位是毫秒
- * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
- * 而不能保证刚好到达延迟时间时执行。
- * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
- * @example
- * ```javascript
- * var start = 0;
- *
- * function test(){
- * console.log( new Date() - start );
- * }
- *
- * var testDefer = UE.utils.defer( test, 1000 );
- * //
- * start = new Date();
- * //output: (大约在1000毫秒之后输出) 1000
- * testDefer();
- * ```
- */
-
- /**
- * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
- * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
- * @method defer
- * @param { Function } fn 需要延迟执行的函数对象
- * @param { int } delay 延迟的时间, 单位是毫秒
- * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
- * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
- * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
- * 而不能保证刚好到达延迟时间时执行。
- * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
- * @example
- * ```javascript
- *
- * function test(){
- * console.log(1);
- * }
- *
- * var testDefer = UE.utils.defer( test, 1000, true );
- *
- * //output: (两次调用仅有一次输出) 1
- * testDefer();
- * testDefer();
- * ```
- */
- defer:function (fn, delay, exclusion) {
- var timerID;
- return function () {
- if (exclusion) {
- clearTimeout(timerID);
- }
- timerID = setTimeout(fn, delay);
- };
- },
-
- /**
- * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
- * @method indexOf
- * @remind 该方法的匹配过程使用的是恒等“===”
- * @param { Array } array 需要查找的数组对象
- * @param { * } item 需要在目标数组中查找的值
- * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
- * @example
- * ```javascript
- * var item = 1,
- * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
- *
- * //output: 4
- * console.log( UE.utils.indexOf( arr, item ) );
- * ```
- */
-
- /**
- * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
- * @method indexOf
- * @remind 该方法的匹配过程使用的是恒等“===”
- * @param { Array } array 需要查找的数组对象
- * @param { * } item 需要在目标数组中查找的值
- * @param { int } start 搜索的起始位置
- * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
- * @example
- * ```javascript
- * var item = 1,
- * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
- *
- * //output: 9
- * console.log( UE.utils.indexOf( arr, item, 5 ) );
- * ```
- */
- indexOf:function (array, item, start) {
- var index = -1;
- start = this.isNumber(start) ? start : 0;
- this.each(array, function (v, i) {
- if (i >= start && v === item) {
- index = i;
- return false;
- }
- });
- return index;
- },
-
- /**
- * 移除数组array中所有的元素item
- * @method removeItem
- * @param { Array } array 要移除元素的目标数组
- * @param { * } item 将要被移除的元素
- * @remind 该方法的匹配过程使用的是恒等“===”
- * @example
- * ```javascript
- * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
- *
- * UE.utils.removeItem( arr, 4 );
- * //output: [ 5, 7, 1, 3, 6 ]
- * console.log( arr );
- *
- * ```
- */
- removeItem:function (array, item) {
- for (var i = 0, l = array.length; i < l; i++) {
- if (array[i] === item) {
- array.splice(i, 1);
- i--;
- }
- }
- },
-
- /**
- * 删除字符串str的首尾空格
- * @method trim
- * @param { String } str 需要删除首尾空格的字符串
- * @return { String } 删除了首尾的空格后的字符串
- * @example
- * ```javascript
- *
- * var str = " UEdtior ";
- *
- * //output: 9
- * console.log( str.length );
- *
- * //output: 7
- * console.log( UE.utils.trim( " UEdtior " ).length );
- *
- * //output: 9
- * console.log( str.length );
- *
- * ```
- */
- trim:function (str) {
- return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
- },
-
- /**
- * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
- * @method listToMap
- * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
- * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
- * @return { Object } 转化之后的hash对象
- * @example
- * ```javascript
- *
- * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
- * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
- *
- * ```
- */
-
- /**
- * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
- * @method listToMap
- * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
- * @param { Array } arr 字符串数组
- * @return { Object } 转化之后的hash对象
- * @example
- * ```javascript
- *
- * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
- * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
- *
- * ```
- */
- listToMap:function (list) {
- if (!list)return {};
- list = utils.isArray(list) ? list : list.split(',');
- for (var i = 0, ci, obj = {}; ci = list[i++];) {
- obj[ci.toUpperCase()] = obj[ci] = 1;
- }
- return obj;
- },
-
- /**
- * 将str中的html符号转义,将转义“',&,<,",>”五个字符
- * @method unhtml
- * @param { String } str 需要转义的字符串
- * @return { String } 转义后的字符串
- * @example
- * ```javascript
- * var html = '&';
- *
- * //output: <body>&</body>
- * console.log( UE.utils.unhtml( html ) );
- *
- * ```
- */
- unhtml:function (str, reg) {
- return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
- if (b) {
- return a;
- } else {
- return {
- '<':'<',
- '&':'&',
- '"':'"',
- '>':'>',
- "'":'''
- }[a]
- }
-
- }) : '';
- },
-
- /**
- * 将str中的转义字符还原成html字符
- * @see UE.utils.unhtml(String);
- * @method html
- * @param { String } str 需要逆转义的字符串
- * @return { String } 逆转义后的字符串
- * @example
- * ```javascript
- *
- * var str = '<body>&</body>';
- *
- * //output: &
- * console.log( UE.utils.html( str ) );
- *
- * ```
- */
- html:function (str) {
- return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
- return {
- '<':'<',
- '&':'&',
- '"':'"',
- '>':'>',
- ''':"'",
- ' ':' '
- }[m]
- }) : '';
- },
-
- /**
- * 将css样式转换为驼峰的形式
- * @method cssStyleToDomStyle
- * @param { String } cssName 需要转换的css样式名
- * @return { String } 转换成驼峰形式后的css样式名
- * @example
- * ```javascript
- *
- * var str = 'border-top';
- *
- * //output: borderTop
- * console.log( UE.utils.cssStyleToDomStyle( str ) );
- *
- * ```
- */
- cssStyleToDomStyle:function () {
- var test = document.createElement('div').style,
- cache = {
- 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
- };
-
- return function (cssName) {
- return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
- return match.charAt(1).toUpperCase();
- }));
- };
- }(),
-
- /**
- * 动态加载文件到doc中
- * @method loadFile
- * @param { DomDocument } document 需要加载资源文件的文档对象
- * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
- * @example
- * ```javascript
- *
- * UE.utils.loadFile( document, {
- * src:"test.js",
- * tag:"script",
- * type:"text/javascript",
- * defer:"defer"
- * } );
- *
- * ```
- */
-
- /**
- * 动态加载文件到doc中,加载成功后执行的回调函数fn
- * @method loadFile
- * @param { DomDocument } document 需要加载资源文件的文档对象
- * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
- * @param { Function } fn 资源文件加载成功之后执行的回调
- * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
- * 在此之后的所有同一URL的请求, 将会直接触发回调。
- * @example
- * ```javascript
- *
- * UE.utils.loadFile( document, {
- * src:"test.js",
- * tag:"script",
- * type:"text/javascript",
- * defer:"defer"
- * }, function () {
- * console.log('加载成功');
- * } );
- *
- * ```
- */
- loadFile:function () {
- var tmpList = [];
-
- function getItem(doc, obj) {
- try {
- for (var i = 0, ci; ci = tmpList[i++];) {
- if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
- return ci;
- }
- }
- } catch (e) {
- return null;
- }
-
- }
-
- return function (doc, obj, fn) {
- var item = getItem(doc, obj);
- if (item) {
- if (item.ready) {
- fn && fn();
- } else {
- item.funs.push(fn)
- }
- return;
- }
- tmpList.push({
- doc:doc,
- url:obj.src || obj.href,
- funs:[fn]
- });
- if (!doc.body) {
- var html = [];
- for (var p in obj) {
- if (p == 'tag')continue;
- html.push(p + '="' + obj[p] + '"')
- }
- doc.write('<' + obj.tag + ' ' + html.join(' ') + ' >' + obj.tag + '>');
- return;
- }
- if (obj.id && doc.getElementById(obj.id)) {
- return;
- }
- var element = doc.createElement(obj.tag);
- delete obj.tag;
- for (var p in obj) {
- element.setAttribute(p, obj[p]);
- }
- element.onload = element.onreadystatechange = function () {
- if (!this.readyState || /loaded|complete/.test(this.readyState)) {
- item = getItem(doc, obj);
- if (item.funs.length > 0) {
- item.ready = 1;
- for (var fi; fi = item.funs.pop();) {
- fi();
- }
- }
- element.onload = element.onreadystatechange = null;
- }
- };
- element.onerror = function () {
- throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
- };
- doc.getElementsByTagName("head")[0].appendChild(element);
- }
- }(),
-
- /**
- * 判断obj对象是否为空
- * @method isEmptyObject
- * @param { * } obj 需要判断的对象
- * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
- * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
- * @return { Boolean } 对象是否为空
- * @example
- * ```javascript
- *
- * //output: true
- * console.log( UE.utils.isEmptyObject( {} ) );
- *
- * //output: true
- * console.log( UE.utils.isEmptyObject( [] ) );
- *
- * //output: true
- * console.log( UE.utils.isEmptyObject( "" ) );
- *
- * //output: false
- * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
- *
- * //output: false
- * console.log( UE.utils.isEmptyObject( [1] ) );
- *
- * //output: false
- * console.log( UE.utils.isEmptyObject( "1" ) );
- *
- * ```
- */
- isEmptyObject:function (obj) {
- if (obj == null) return true;
- if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
- for (var key in obj) if (obj.hasOwnProperty(key)) return false;
- return true;
- },
-
- /**
- * 把rgb格式的颜色值转换成16进制格式
- * @method fixColor
- * @param { String } rgb格式的颜色值
- * @param { String }
- * @example
- * rgb(255,255,255) => "#ffffff"
- */
- fixColor:function (name, value) {
- if (/color/i.test(name) && /rgba?/.test(value)) {
- var array = value.split(",");
- if (array.length > 3)
- return "";
- value = "#";
- for (var i = 0, color; color = array[i++];) {
- color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
- value += color.length == 1 ? "0" + color : color;
- }
- value = value.toUpperCase();
- }
- return value;
- },
- /**
- * 只针对border,padding,margin做了处理,因为性能问题
- * @public
- * @function
- * @param {String} val style字符串
- */
- optCss:function (val) {
- var padding, margin, border;
- val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
- if (val.split(' ').length == 1) {
- switch (key) {
- case 'padding':
- !padding && (padding = {});
- padding[name] = val;
- return '';
- case 'margin':
- !margin && (margin = {});
- margin[name] = val;
- return '';
- case 'border':
- return val == 'initial' ? '' : str;
- }
- }
- return str;
- });
-
- function opt(obj, name) {
- if (!obj) {
- return '';
- }
- var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
- if (!t || !l || !b || !r) {
- for (var p in obj) {
- val += ';' + name + '-' + p + ':' + obj[p] + ';';
- }
- } else {
- val += ';' + name + ':' +
- (t == b && b == l && l == r ? t :
- t == b && l == r ? (t + ' ' + l) :
- l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
- }
- return val;
- }
-
- val += opt(padding, 'padding') + opt(margin, 'margin');
- return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
- .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
- return b ? b + ";;" : ';'
- });
- },
-
- /**
- * 克隆对象
- * @method clone
- * @param { Object } source 源对象
- * @return { Object } source的一个副本
- */
-
- /**
- * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
- * @method clone
- * @param { Object } source 源对象
- * @param { Object } target 目标对象
- * @return { Object } 附加了source对象所有属性的target对象
- */
- clone:function (source, target) {
- var tmp;
- target = target || {};
- for (var i in source) {
- if (source.hasOwnProperty(i)) {
- tmp = source[i];
- if (typeof tmp == 'object') {
- target[i] = utils.isArray(tmp) ? [] : {};
- utils.clone(source[i], target[i])
- } else {
- target[i] = tmp;
- }
- }
- }
- return target;
- },
-
- /**
- * 把cm/pt为单位的值转换为px为单位的值
- * @method transUnitToPx
- * @param { String } 待转换的带单位的字符串
- * @return { String } 转换为px为计量单位的值的字符串
- * @example
- * ```javascript
- *
- * //output: 500px
- * console.log( UE.utils.transUnitToPx( '20cm' ) );
- *
- * //output: 27px
- * console.log( UE.utils.transUnitToPx( '20pt' ) );
- *
- * ```
- */
- transUnitToPx:function (val) {
- if (!/(pt|cm)/.test(val)) {
- return val
- }
- var unit;
- val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
- val = v;
- unit = u;
- });
- switch (unit) {
- case 'cm':
- val = parseFloat(val) * 25;
- break;
- case 'pt':
- val = Math.round(parseFloat(val) * 96 / 72);
- }
- return val + (val ? 'px' : '');
- },
-
- /**
- * 在dom树ready之后执行给定的回调函数
- * @method domReady
- * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
- * @param { Function } fn dom树ready之后的回调函数
- * @example
- * ```javascript
- *
- * UE.utils.domReady( function () {
- *
- * console.log('123');
- *
- * } );
- *
- * ```
- */
- domReady:function () {
-
- var fnArr = [];
-
- function doReady(doc) {
- //确保onready只执行一次
- doc.isReady = true;
- for (var ci; ci = fnArr.pop(); ci()) {
- }
- }
-
- return function (onready, win) {
- win = win || window;
- var doc = win.document;
- onready && fnArr.push(onready);
- if (doc.readyState === "complete") {
- doReady(doc);
- } else {
- doc.isReady && doReady(doc);
- if (browser.ie && browser.version != 11) {
- (function () {
- if (doc.isReady) return;
- try {
- doc.documentElement.doScroll("left");
- } catch (error) {
- setTimeout(arguments.callee, 0);
- return;
- }
- doReady(doc);
- })();
- win.attachEvent('onload', function () {
- doReady(doc)
- });
- } else {
- doc.addEventListener("DOMContentLoaded", function () {
- doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
- doReady(doc);
- }, false);
- win.addEventListener('load', function () {
- doReady(doc)
- }, false);
- }
- }
-
- }
- }(),
-
- /**
- * 动态添加css样式
- * @method cssRule
- * @param { String } 节点名称
- * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
- * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
- * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
- * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
- * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
- */
- cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
- var indexList, index;
- if(style === undefined || style && style.nodeType && style.nodeType == 9){
- //获取样式
- doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
- indexList = doc.indexList || (doc.indexList = {});
- index = indexList[key];
- if(index !== undefined){
- return doc.styleSheets[index].cssText
- }
- return undefined;
- }
- doc = doc || document;
- indexList = doc.indexList || (doc.indexList = {});
- index = indexList[key];
- //清除样式
- if(style === ''){
- if(index!== undefined){
- doc.styleSheets[index].cssText = '';
- delete indexList[key];
- return true
- }
- return false;
- }
-
- //添加样式
- if(index!== undefined){
- sheetStyle = doc.styleSheets[index];
- }else{
- sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
- indexList[key] = index;
- }
- sheetStyle.cssText = style;
- }: function (key, style, doc) {
- var head, node;
- if(style === undefined || style && style.nodeType && style.nodeType == 9){
- //获取样式
- doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
- node = doc.getElementById(key);
- return node ? node.innerHTML : undefined;
- }
- doc = doc || document;
- node = doc.getElementById(key);
-
- //清除样式
- if(style === ''){
- if(node){
- node.parentNode.removeChild(node);
- return true
- }
- return false;
- }
-
- //添加样式
- if(node){
- node.innerHTML = style;
- }else{
- node = doc.createElement('style');
- node.id = key;
- node.innerHTML = style;
- doc.getElementsByTagName('head')[0].appendChild(node);
- }
- },
- sort:function(array,compareFn){
- compareFn = compareFn || function(item1, item2){ return item1.localeCompare(item2);};
- for(var i= 0,len = array.length; i 0){
- var t = array[i];
- array[i] = array[j];
- array[j] = t;
- }
- }
- }
- return array;
- },
- serializeParam:function (json) {
- var strArr = [];
- for (var i in json) {
- //忽略默认的几个参数
- if(i=="method" || i=="timeout" || i=="async") continue;
- //传递过来的对象和函数不在提交之列
- if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
- strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
- } else if (utils.isArray(json[i])) {
- //支持传数组内容
- for(var j = 0; j < json[i].length; j++) {
- strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
- }
- }
- }
- return strArr.join("&");
- },
- formatUrl:function (url) {
- var u = url.replace(/&&/g, '&');
- u = u.replace(/\?&/g, '?');
- u = u.replace(/&$/g, '');
- u = u.replace(//g, '#');
- u = u.replace(/&+/g, '&');
- return u;
- },
- isCrossDomainUrl:function (url) {
- var a = document.createElement('a');
- a.href = url;
- if (browser.ie) {
- a.href = a.href;
- }
- return !(a.protocol == location.protocol && a.hostname == location.hostname &&
- (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80')));
- },
- clearEmptyAttrs : function(obj){
- for(var p in obj){
- if(obj[p] === ''){
- delete obj[p]
- }
- }
- return obj;
- },
- str2json : function(s){
-
- if (!utils.isString(s)) return null;
- if (window.JSON) {
- return JSON.parse(s);
- } else {
- return (new Function("return " + utils.trim(s || '')))();
- }
-
- },
- json2str : (function(){
-
- if (window.JSON) {
-
- return JSON.stringify;
-
- } else {
-
- var escapeMap = {
- "\b": '\\b',
- "\t": '\\t',
- "\n": '\\n',
- "\f": '\\f',
- "\r": '\\r',
- '"' : '\\"',
- "\\": '\\\\'
- };
-
- function encodeString(source) {
- if (/["\\\x00-\x1f]/.test(source)) {
- source = source.replace(
- /["\\\x00-\x1f]/g,
- function (match) {
- var c = escapeMap[match];
- if (c) {
- return c;
- }
- c = match.charCodeAt();
- return "\\u00"
- + Math.floor(c / 16).toString(16)
- + (c % 16).toString(16);
- });
- }
- return '"' + source + '"';
- }
-
- function encodeArray(source) {
- var result = ["["],
- l = source.length,
- preComma, i, item;
-
- for (i = 0; i < l; i++) {
- item = source[i];
-
- switch (typeof item) {
- case "undefined":
- case "function":
- case "unknown":
- break;
- default:
- if(preComma) {
- result.push(',');
- }
- result.push(utils.json2str(item));
- preComma = 1;
- }
- }
- result.push("]");
- return result.join("");
- }
-
- function pad(source) {
- return source < 10 ? '0' + source : source;
- }
-
- function encodeDate(source){
- return '"' + source.getFullYear() + "-"
- + pad(source.getMonth() + 1) + "-"
- + pad(source.getDate()) + "T"
- + pad(source.getHours()) + ":"
- + pad(source.getMinutes()) + ":"
- + pad(source.getSeconds()) + '"';
- }
-
- return function (value) {
- switch (typeof value) {
- case 'undefined':
- return 'undefined';
-
- case 'number':
- return isFinite(value) ? String(value) : "null";
-
- case 'string':
- return encodeString(value);
-
- case 'boolean':
- return String(value);
-
- default:
- if (value === null) {
- return 'null';
- } else if (utils.isArray(value)) {
- return encodeArray(value);
- } else if (utils.isDate(value)) {
- return encodeDate(value);
- } else {
- var result = ['{'],
- encode = utils.json2str,
- preComma,
- item;
-
- for (var key in value) {
- if (Object.prototype.hasOwnProperty.call(value, key)) {
- item = value[key];
- switch (typeof item) {
- case 'undefined':
- case 'unknown':
- case 'function':
- break;
- default:
- if (preComma) {
- result.push(',');
- }
- preComma = 1;
- result.push(encode(key) + ':' + encode(item));
- }
- }
- }
- result.push('}');
- return result.join('');
- }
- }
- };
- }
-
- })()
-
-};
-/**
- * 判断给定的对象是否是字符串
- * @method isString
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是字符串
- */
-
-/**
- * 判断给定的对象是否是数组
- * @method isArray
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是数组
- */
-
-/**
- * 判断给定的对象是否是一个Function
- * @method isFunction
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是Function
- */
-
-/**
- * 判断给定的对象是否是Number
- * @method isNumber
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是Number
- */
-
-/**
- * 判断给定的对象是否是一个正则表达式
- * @method isRegExp
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是正则表达式
- */
-
-/**
- * 判断给定的对象是否是一个普通对象
- * @method isObject
- * @param { * } object 需要判断的对象
- * @return { Boolean } 给定的对象是否是普通对象
- */
-utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) {
- UE.utils['is' + v] = function (obj) {
- return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
- }
-});
-
-// core/EventBase.js
-/**
- * UE采用的事件基类
- * @file
- * @module UE
- * @class EventBase
- * @since 1.2.6.1
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @unfile
- * @module UE
- */
-
-/**
- * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
- * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
- * @unfile
- * @module UE
- * @class EventBase
- */
-
-/**
- * 通过此构造器,子类可以继承EventBase获取事件监听的方法
- * @constructor
- * @example
- * ```javascript
- * UE.EventBase.call(editor);
- * ```
- */
-var EventBase = UE.EventBase = function () {};
-
-EventBase.prototype = {
-
- /**
- * 注册事件监听器
- * @method addListener
- * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
- * @param { Function } fn 监听的事件被触发时,会执行该回调函数
- * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
- * @example
- * ```javascript
- * editor.addListener('selectionchange',function(){
- * console.log("选区已经变化!");
- * })
- * editor.addListener('beforegetcontent aftergetcontent',function(type){
- * if(type == 'beforegetcontent'){
- * //do something
- * }else{
- * //do something
- * }
- * console.log(this.getContent) // this是注册的事件的编辑器实例
- * })
- * ```
- * @see UE.EventBase:fireEvent(String)
- */
- addListener:function (types, listener) {
- types = utils.trim(types).split(/\s+/);
- for (var i = 0, ti; ti = types[i++];) {
- getListener(this, ti, true).push(listener);
- }
- },
-
- on : function(types, listener){
- return this.addListener(types,listener);
- },
- off : function(types, listener){
- return this.removeListener(types, listener)
- },
- trigger:function(){
- return this.fireEvent.apply(this,arguments);
- },
- /**
- * 移除事件监听器
- * @method removeListener
- * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
- * @param { Function } fn 移除监听事件的函数引用
- * @example
- * ```javascript
- * //changeCallback为方法体
- * editor.removeListener("selectionchange",changeCallback);
- * ```
- */
- removeListener:function (types, listener) {
- types = utils.trim(types).split(/\s+/);
- for (var i = 0, ti; ti = types[i++];) {
- utils.removeItem(getListener(this, ti) || [], listener);
- }
- },
-
- /**
- * 触发事件
- * @method fireEvent
- * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
- * @remind 该方法会触发addListener
- * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
- * @example
- * ```javascript
- * editor.fireEvent("selectionchange");
- * ```
- */
-
- /**
- * 触发事件
- * @method fireEvent
- * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
- * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
- * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
- * @example
- * ```javascript
- *
- * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
- *
- * console.log( arg1 + " " + arg2 );
- *
- * } );
- *
- * //触发selectionchange事件, 会执行上面的事件监听器
- * //output: Hello World
- * editor.fireEvent("selectionchange", "Hello", "World");
- * ```
- */
- fireEvent:function () {
- var types = arguments[0];
- types = utils.trim(types).split(' ');
- for (var i = 0, ti; ti = types[i++];) {
- var listeners = getListener(this, ti),
- r, t, k;
- if (listeners) {
- k = listeners.length;
- while (k--) {
- if(!listeners[k])continue;
- t = listeners[k].apply(this, arguments);
- if(t === true){
- return t;
- }
- if (t !== undefined) {
- r = t;
- }
- }
- }
- if (t = this['on' + ti.toLowerCase()]) {
- r = t.apply(this, arguments);
- }
- }
- return r;
- }
-};
-/**
- * 获得对象所拥有监听类型的所有监听器
- * @unfile
- * @module UE
- * @since 1.2.6.1
- * @method getListener
- * @public
- * @param { Object } obj 查询监听器的对象
- * @param { String } type 事件类型
- * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
- * @return { Array } 监听器数组
- */
-function getListener(obj, type, force) {
- var allListeners;
- type = type.toLowerCase();
- return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
- && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
-}
-
-
-
-// core/dtd.js
-///import editor.js
-///import core/dom/dom.js
-///import core/utils.js
-/**
- * dtd html语义化的体现类
- * @constructor
- * @namespace dtd
- */
-var dtd = dom.dtd = (function() {
- function _( s ) {
- for (var k in s) {
- s[k.toUpperCase()] = s[k];
- }
- return s;
- }
- var X = utils.extend2;
- var A = _({isindex:1,fieldset:1}),
- B = _({input:1,button:1,select:1,textarea:1,label:1}),
- C = X( _({a:1}), B ),
- D = X( {iframe:1}, C ),
- E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
- F = _({ins:1,del:1,script:1,style:1}),
- G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
- H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
- I = X( _({p:1}), H ),
- J = X( _({iframe:1}), H, B ),
- K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
-
- L = X( _({a:0}), J ),//a不能被切开,所以把他
- M = _({tr:1}),
- N = _({'#':1}),
- O = X( _({param:1}), K ),
- P = X( _({form:1}), A, D, E, I ),
- Q = _({li:1,ol:1,ul:1}),
- R = _({style:1,script:1}),
- S = _({base:1,link:1,meta:1,title:1}),
- T = X( S, R ),
- U = _({head:1,body:1}),
- V = _({html:1});
-
- var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),
-
- empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
-
- return _({
-
- // $ 表示自定的属性
-
- // body外的元素列表.
- $nonBodyContent: X( V, U, S ),
-
- //块结构元素列表
- $block : block,
-
- //内联元素列表
- $inline : L,
-
- $inlineWithA : X(_({a:1}),L),
-
- $body : X( _({script:1,style:1}), block ),
-
- $cdata : _({script:1,style:1}),
-
- //自闭和元素
- $empty : empty,
-
- //不是自闭合,但不能让range选中里边
- $nonChild : _({iframe:1,textarea:1}),
- //列表元素列表
- $listItem : _({dd:1,dt:1,li:1}),
-
- //列表根元素列表
- $list: _({ul:1,ol:1,dl:1}),
-
- //不能认为是空的元素
- $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
-
- //如果没有子节点就可以删除的元素列表,像span,a
- $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
-
- $removeEmptyBlock : _({'p':1,'div':1}),
-
- //在table元素里的元素列表
- $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
- //不转换的标签
- $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
- html: U,
- head: T,
- style: N,
- script: N,
- body: P,
- base: {},
- link: {},
- meta: {},
- title: N,
- col : {},
- tr : _({td:1,th:1}),
- img : {},
- embed: {},
- colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
- noscript : P,
- td : P,
- br : {},
- th : P,
- center : P,
- kbd : L,
- button : X( I, E ),
- basefont : {},
- h5 : L,
- h4 : L,
- samp : L,
- h6 : L,
- ol : Q,
- h1 : L,
- h3 : L,
- option : N,
- h2 : L,
- form : X( A, D, E, I ),
- select : _({optgroup:1,option:1}),
- font : L,
- ins : L,
- menu : Q,
- abbr : L,
- label : L,
- table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
- code : L,
- tfoot : M,
- cite : L,
- li : P,
- input : {},
- iframe : P,
- strong : L,
- textarea : N,
- noframes : P,
- big : L,
- small : L,
- //trace:
- span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
- hr : L,
- dt : L,
- sub : L,
- optgroup : _({option:1}),
- param : {},
- bdo : L,
- 'var' : L,
- div : P,
- object : O,
- sup : L,
- dd : P,
- strike : L,
- area : {},
- dir : Q,
- map : X( _({area:1,form:1,p:1}), A, F, E ),
- applet : O,
- dl : _({dt:1,dd:1}),
- del : L,
- isindex : {},
- fieldset : X( _({legend:1}), K ),
- thead : M,
- ul : Q,
- acronym : L,
- b : L,
- a : X( _({a:1}), J ),
- blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
- caption : L,
- i : L,
- u : L,
- tbody : M,
- s : L,
- address : X( D, I ),
- tt : L,
- legend : L,
- q : L,
- pre : X( G, C ),
- p : X(_({'a':1}),L),
- em :L,
- dfn : L
- });
-})();
-
-
-// core/domUtils.js
-/**
- * Dom操作工具包
- * @file
- * @module UE.dom.domUtils
- * @since 1.2.6.1
- */
-
-/**
- * Dom操作工具包
- * @unfile
- * @module UE.dom.domUtils
- */
-function getDomNode(node, start, ltr, startFromChild, fn, guard) {
- var tmpNode = startFromChild && node[start],
- parent;
- !tmpNode && (tmpNode = node[ltr]);
- while (!tmpNode && (parent = (parent || node).parentNode)) {
- if (parent.tagName == 'BODY' || guard && !guard(parent)) {
- return null;
- }
- tmpNode = parent[ltr];
- }
- if (tmpNode && fn && !fn(tmpNode)) {
- return getDomNode(tmpNode, start, ltr, false, fn);
- }
- return tmpNode;
-}
-var attrFix = ie && browser.version < 9 ? {
- tabindex:"tabIndex",
- readonly:"readOnly",
- "for":"htmlFor",
- "class":"className",
- maxlength:"maxLength",
- cellspacing:"cellSpacing",
- cellpadding:"cellPadding",
- rowspan:"rowSpan",
- colspan:"colSpan",
- usemap:"useMap",
- frameborder:"frameBorder"
- } : {
- tabindex:"tabIndex",
- readonly:"readOnly"
- },
- styleBlock = utils.listToMap([
- '-webkit-box', '-moz-box', 'block' ,
- 'list-item' , 'table' , 'table-row-group' ,
- 'table-header-group', 'table-footer-group' ,
- 'table-row' , 'table-column-group' , 'table-column' ,
- 'table-cell' , 'table-caption'
- ]);
-var domUtils = dom.domUtils = {
- //节点常量
- NODE_ELEMENT:1,
- NODE_DOCUMENT:9,
- NODE_TEXT:3,
- NODE_COMMENT:8,
- NODE_DOCUMENT_FRAGMENT:11,
-
- //位置关系
- POSITION_IDENTICAL:0,
- POSITION_DISCONNECTED:1,
- POSITION_FOLLOWING:2,
- POSITION_PRECEDING:4,
- POSITION_IS_CONTAINED:8,
- POSITION_CONTAINS:16,
- //ie6使用其他的会有一段空白出现
- fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B',
- //-------------------------Node部分--------------------------------
- keys:{
- /*Backspace*/ 8:1, /*Delete*/ 46:1,
- /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
- 37:1, 38:1, 39:1, 40:1,
- 13:1 /*enter*/
- },
- /**
- * 获取节点A相对于节点B的位置关系
- * @method getPosition
- * @param { Node } nodeA 需要查询位置关系的节点A
- * @param { Node } nodeB 需要查询位置关系的节点B
- * @return { Number } 节点A与节点B的关系
- * @example
- * ```javascript
- * //output: 20
- * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
- *
- * switch ( position ) {
- *
- * //0
- * case UE.dom.domUtils.POSITION_IDENTICAL:
- * console.log('元素相同');
- * break;
- * //1
- * case UE.dom.domUtils.POSITION_DISCONNECTED:
- * console.log('两个节点在不同的文档中');
- * break;
- * //2
- * case UE.dom.domUtils.POSITION_FOLLOWING:
- * console.log('节点A在节点B之后');
- * break;
- * //4
- * case UE.dom.domUtils.POSITION_PRECEDING;
- * console.log('节点A在节点B之前');
- * break;
- * //8
- * case UE.dom.domUtils.POSITION_IS_CONTAINED:
- * console.log('节点A被节点B包含');
- * break;
- * case 10:
- * console.log('节点A被节点B包含且节点A在节点B之后');
- * break;
- * //16
- * case UE.dom.domUtils.POSITION_CONTAINS:
- * console.log('节点A包含节点B');
- * break;
- * case 20:
- * console.log('节点A包含节点B且节点A在节点B之前');
- * break;
- *
- * }
- * ```
- */
- getPosition:function (nodeA, nodeB) {
- // 如果两个节点是同一个节点
- if (nodeA === nodeB) {
- // domUtils.POSITION_IDENTICAL
- return 0;
- }
- var node,
- parentsA = [nodeA],
- parentsB = [nodeB];
- node = nodeA;
- while (node = node.parentNode) {
- // 如果nodeB是nodeA的祖先节点
- if (node === nodeB) {
- // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
- return 10;
- }
- parentsA.push(node);
- }
- node = nodeB;
- while (node = node.parentNode) {
- // 如果nodeA是nodeB的祖先节点
- if (node === nodeA) {
- // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
- return 20;
- }
- parentsB.push(node);
- }
- parentsA.reverse();
- parentsB.reverse();
- if (parentsA[0] !== parentsB[0]) {
- // domUtils.POSITION_DISCONNECTED
- return 1;
- }
- var i = -1;
- while (i++, parentsA[i] === parentsB[i]) {
- }
- nodeA = parentsA[i];
- nodeB = parentsB[i];
- while (nodeA = nodeA.nextSibling) {
- if (nodeA === nodeB) {
- // domUtils.POSITION_PRECEDING
- return 4
- }
- }
- // domUtils.POSITION_FOLLOWING
- return 2;
- },
-
- /**
- * 检测节点node在父节点中的索引位置
- * @method getNodeIndex
- * @param { Node } node 需要检测的节点对象
- * @return { Number } 该节点在父节点中的位置
- * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
- */
-
- /**
- * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
- * @method getNodeIndex
- * @param { Node } node 需要检测的节点对象
- * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
- * @return { Number } 该节点在父节点中的位置
- * @example
- * ```javascript
- *
- * var node = document.createElement("div");
- *
- * node.appendChild( document.createTextNode( "hello" ) );
- * node.appendChild( document.createTextNode( "world" ) );
- * node.appendChild( node = document.createElement( "div" ) );
- *
- * //output: 2
- * console.log( UE.dom.domUtils.getNodeIndex( node ) );
- *
- * //output: 1
- * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
- *
- * ```
- */
- getNodeIndex:function (node, ignoreTextNode) {
- var preNode = node,
- i = 0;
- while (preNode = preNode.previousSibling) {
- if (ignoreTextNode && preNode.nodeType == 3) {
- if(preNode.nodeType != preNode.nextSibling.nodeType ){
- i++;
- }
- continue;
- }
- i++;
- }
- return i;
- },
-
- /**
- * 检测节点node是否在给定的document对象上
- * @method inDoc
- * @param { Node } node 需要检测的节点对象
- * @param { DomDocument } doc 需要检测的document对象
- * @return { Boolean } 该节点node是否在给定的document的dom树上
- * @example
- * ```javascript
- *
- * var node = document.createElement("div");
- *
- * //output: false
- * console.log( UE.do.domUtils.inDoc( node, document ) );
- *
- * document.body.appendChild( node );
- *
- * //output: true
- * console.log( UE.do.domUtils.inDoc( node, document ) );
- *
- * ```
- */
- inDoc:function (node, doc) {
- return domUtils.getPosition(node, doc) == 10;
- },
- /**
- * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
- * 查找的起点是给定node节点的父节点。
- * @method findParent
- * @param { Node } node 需要查找的节点
- * @param { Function } filterFn 自定义的过滤方法。
- * @warning 查找的终点是到body节点为止
- * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
- * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
- * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
- *
- * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
- * return node.tagName === "HTML";
- *
- * } );
- *
- * //output: true
- * console.log( filterNode === null );
- * ```
- */
-
- /**
- * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
- * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
- * @method findParent
- * @param { Node } node 需要查找的节点
- * @param { Function } filterFn 自定义的过滤方法。
- * @param { Boolean } includeSelf 查找过程是否包含自身
- * @warning 查找的终点是到body节点为止
- * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
- * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
- * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
- * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
- * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- *
- * ```
- */
- findParent:function (node, filterFn, includeSelf) {
- if (node && !domUtils.isBody(node)) {
- node = includeSelf ? node : node.parentNode;
- while (node) {
- if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
- return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
- }
- node = node.parentNode;
- }
- }
- return null;
- },
- /**
- * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
- * @method findParentByTagName
- * @param { Node } node 需要查找的节点对象
- * @param { Array } tagNames 需要查找的父节点的名称数组
- * @warning 查找的终点是到body节点为止
- * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
- * //output: BODY
- * console.log( node.tagName );
- * ```
- */
-
- /**
- * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
- * 否则, 起点是node的父节点。
- * @method findParentByTagName
- * @param { Node } node 需要查找的节点对象
- * @param { Array } tagNames 需要查找的父节点的名称数组
- * @param { Boolean } includeSelf 查找过程是否包含node节点自身
- * @warning 查找的终点是到body节点为止
- * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var queryTarget = document.getElementsByTagName("div")[0];
- * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
- * //output: true
- * console.log( queryTarget === node );
- * ```
- */
- findParentByTagName:function (node, tagNames, includeSelf, excludeFn) {
- tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
- return domUtils.findParent(node, function (node) {
- return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
- }, includeSelf);
- },
- /**
- * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
- * @method findParents
- * @param { Node } node 需要查找的节点对象
- * @return { Array } 给定节点的祖先节点数组
- * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
- * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
- * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
- * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
- */
-
- /**
- * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
- * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
- * @method findParents
- * @param { Node } node 需要查找的节点对象
- * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
- * @return { Array } 给定节点的祖先节点数组
- */
- findParents:function (node, includeSelf, filterFn, closerFirst) {
- var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
- while (node = domUtils.findParent(node, filterFn)) {
- parents.push(node);
- }
- return closerFirst ? parents : parents.reverse();
- },
-
- /**
- * 在节点node后面插入新节点newNode
- * @method insertAfter
- * @param { Node } node 目标节点
- * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
- * @return { Node } 新插入的节点
- */
- insertAfter:function (node, newNode) {
- return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling):
- node.parentNode.appendChild(newNode);
- },
-
- /**
- * 删除节点node及其下属的所有节点
- * @method remove
- * @param { Node } node 需要删除的节点对象
- * @return { Node } 返回刚删除的节点对象
- * @example
- * ```html
- *
- *
- * ```
- */
-
- /**
- * 删除节点node,并根据keepChildren的值决定是否保留子节点
- * @method remove
- * @param { Node } node 需要删除的节点对象
- * @param { Boolean } keepChildren 是否需要保留子节点
- * @return { Node } 返回刚删除的节点对象
- * @example
- * ```html
- *
- *
- * ```
- */
- remove:function (node, keepChildren) {
- var parent = node.parentNode,
- child;
- if (parent) {
- if (keepChildren && node.hasChildNodes()) {
- while (child = node.firstChild) {
- parent.insertBefore(child, node);
- }
- }
- parent.removeChild(node);
- }
- return node;
- },
-
- /**
- * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
- * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
- * @method getNextDomNode
- * @param { Node } node 需要获取其后的兄弟节点的节点对象
- * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```html
- *
- *
- *
- *
- * xxx
- *
- *
- * ```
- * @example
- * ```html
- *
- *
- *
- * xxx
- *
- * xxx
- *
- *
- * ```
- */
-
- /**
- * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
- * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
- * 则执行getNextDomNode(Node node)的查找过程。
- * @method getNextDomNode
- * @param { Node } node 需要获取其后的兄弟节点的节点对象
- * @param { Boolean } startFromChild 查找过程是否从其子节点开始
- * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
- * @see UE.dom.domUtils.getNextDomNode(Node)
- */
- getNextDomNode:function (node, startFromChild, filterFn, guard) {
- return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
- },
- getPreDomNode:function (node, startFromChild, filterFn, guard) {
- return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
- },
- /**
- * 检测节点node是否属是UEditor定义的bookmark节点
- * @method isBookmarkNode
- * @private
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 是否是bookmark节点
- * @example
- * ```html
- *
- *
- * ```
- */
- isBookmarkNode:function (node) {
- return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
- },
- /**
- * 获取节点node所属的window对象
- * @method getWindow
- * @param { Node } node 节点对象
- * @return { Window } 当前节点所属的window对象
- * @example
- * ```javascript
- * //output: true
- * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
- * ```
- */
- getWindow:function (node) {
- var doc = node.ownerDocument || node;
- return doc.defaultView || doc.parentWindow;
- },
- /**
- * 获取离nodeA与nodeB最近的公共的祖先节点
- * @method getCommonAncestor
- * @param { Node } nodeA 第一个节点
- * @param { Node } nodeB 第二个节点
- * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
- * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
- * @example
- * ```javascript
- * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
- * //output: true
- * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
- * ```
- */
- getCommonAncestor:function (nodeA, nodeB) {
- if (nodeA === nodeB)
- return nodeA;
- var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
- while (parent = parent.parentNode) {
- if (parent === nodeB) {
- return parent;
- }
- parentsA.push(parent);
- }
- parent = nodeB;
- while (parent = parent.parentNode) {
- if (parent === nodeA)
- return parent;
- parentsB.push(parent);
- }
- parentsA.reverse();
- parentsB.reverse();
- while (i++, parentsA[i] === parentsB[i]) {
- }
- return i == 0 ? null : parentsA[i - 1];
-
- },
- /**
- * 清除node节点左右连续为空的兄弟inline节点
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * 则这些兄弟节点将被删除
- * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
- * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
- * @example
- * ```html
- *
- *
- *
- *
- *
- * xxx
- *
- *
- *
- * ```
- */
-
- /**
- * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
- * 则忽略对右边兄弟节点的操作。
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
- * 则这些兄弟节点将被删除
- * @see UE.dom.domUtils.clearEmptySibling(Node)
- */
-
- /**
- * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
- * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
- * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
- * 则这些兄弟节点将被删除
- * @see UE.dom.domUtils.clearEmptySibling(Node)
- */
- clearEmptySibling:function (node, ignoreNext, ignorePre) {
- function clear(next, dir) {
- var tmpNode;
- while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
- //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
- || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
- tmpNode = next[dir];
- domUtils.remove(next);
- next = tmpNode;
- }
- }
- !ignoreNext && clear(node.nextSibling, 'nextSibling');
- !ignorePre && clear(node.previousSibling, 'previousSibling');
- },
- /**
- * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
- * @method split
- * @param { Node } textNode 需要拆分的文本节点对象
- * @param { int } offset 需要拆分的位置, 位置计算从0开始
- * @return { Node } 拆分后形成的新节点
- * @example
- * ```html
- * abcdef
- *
- * ```
- */
- split:function (node, offset) {
- var doc = node.ownerDocument;
- if (browser.ie && offset == node.nodeValue.length) {
- var next = doc.createTextNode('');
- return domUtils.insertAfter(node, next);
- }
- var retval = node.splitText(offset);
- //ie8下splitText不会跟新childNodes,我们手动触发他的更新
- if (browser.ie8) {
- var tmpNode = doc.createTextNode('');
- domUtils.insertAfter(retval, tmpNode);
- domUtils.remove(tmpNode);
- }
- return retval;
- },
-
- /**
- * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
- * @method isWhitespace
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 检测的节点是否为空
- * @example
- * ```html
- *
- *
- *
- *
- * ```
- */
- isWhitespace:function (node) {
- return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
- },
- /**
- * 获取元素element相对于viewport的位置坐标
- * @method getXY
- * @param { Node } element 需要计算位置的节点对象
- * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
- * y代表垂直偏移距离。
- *
- * @example
- * ```javascript
- * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
- * //output: test的坐标为: 12, 24
- * console.log( 'test的坐标为: ', location.x, ',', location.y );
- * ```
- */
- getXY:function (element) {
- var x = 0, y = 0;
- while (element.offsetParent) {
- y += element.offsetTop;
- x += element.offsetLeft;
- element = element.offsetParent;
- }
- return { 'x':x, 'y':y};
- },
- /**
- * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
- * @method on
- * @param { Node } element 需要绑定事件的节点对象
- * @param { String } type 绑定的事件类型
- * @param { Function } handler 事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.on(document.body,"click",function(e){
- * //e为事件对象,this为被点击元素对戏那个
- * });
- * ```
- */
-
- /**
- * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
- * @method on
- * @param { Node } element 需要绑定事件的节点对象
- * @param { Array } type 绑定的事件类型数组
- * @param { Function } handler 事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
- on:function (element, type, handler) {
-
- var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
- k = types.length;
- if (k) while (k--) {
- type = types[k];
- if (element.addEventListener) {
- element.addEventListener(type, handler, false);
- } else {
- if (!handler._d) {
- handler._d = {
- els : []
- };
- }
- var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element);
- if (!handler._d[key] || index == -1) {
- if(index == -1){
- handler._d.els.push(element);
- }
- if(!handler._d[key]){
- handler._d[key] = function (evt) {
- return handler.call(evt.srcElement, evt || window.event);
- };
- }
-
-
- element.attachEvent('on' + type, handler._d[key]);
- }
- }
- }
- element = null;
- },
- /**
- * 解除DOM事件绑定
- * @method un
- * @param { Node } element 需要解除事件绑定的节点对象
- * @param { String } type 需要接触绑定的事件类型
- * @param { Function } handler 对应的事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.un(document.body,"click",function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
-
- /**
- * 解除DOM事件绑定
- * @method un
- * @param { Node } element 需要解除事件绑定的节点对象
- * @param { Array } type 需要接触绑定的事件类型数组
- * @param { Function } handler 对应的事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
- un:function (element, type, handler) {
- var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
- k = types.length;
- if (k) while (k--) {
- type = types[k];
- if (element.removeEventListener) {
- element.removeEventListener(type, handler, false);
- } else {
- var key = type + handler.toString();
- try{
- element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
- }catch(e){}
- if (handler._d && handler._d[key]) {
- var index = utils.indexOf(handler._d.els,element);
- if(index!=-1){
- handler._d.els.splice(index,1);
- }
- handler._d.els.length == 0 && delete handler._d[key];
- }
- }
- }
- },
-
- /**
- * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
- * @method isSameElement
- * @param { Node } nodeA 需要比较的节点
- * @param { Node } nodeB 需要比较的节点
- * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
- * @example
- * ```html
- * ssss
- * bbbbb
- * ssss
- * bbbbb
- *
- *
- * ```
- */
- isSameElement:function (nodeA, nodeB) {
- if (nodeA.tagName != nodeB.tagName) {
- return false;
- }
- var thisAttrs = nodeA.attributes,
- otherAttrs = nodeB.attributes;
- if (!ie && thisAttrs.length != otherAttrs.length) {
- return false;
- }
- var attrA, attrB, al = 0, bl = 0;
- for (var i = 0; attrA = thisAttrs[i++];) {
- if (attrA.nodeName == 'style') {
- if (attrA.specified) {
- al++;
- }
- if (domUtils.isSameStyle(nodeA, nodeB)) {
- continue;
- } else {
- return false;
- }
- }
- if (ie) {
- if (attrA.specified) {
- al++;
- attrB = otherAttrs.getNamedItem(attrA.nodeName);
- } else {
- continue;
- }
- } else {
- attrB = nodeB.attributes[attrA.nodeName];
- }
- if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
- return false;
- }
- }
- // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
- if (ie) {
- for (i = 0; attrB = otherAttrs[i++];) {
- if (attrB.specified) {
- bl++;
- }
- }
- if (al != bl) {
- return false;
- }
- }
- return true;
- },
-
- /**
- * 判断节点nodeA与节点nodeB的元素的style属性是否一致
- * @method isSameStyle
- * @param { Node } nodeA 需要比较的节点
- * @param { Node } nodeB 需要比较的节点
- * @return { Boolean } 两个节点是否具有相同的style属性值
- * @example
- * ```html
- * ssss
- * bbbbb
- * ssss
- * bbbbb
- *
- *
- * ```
- */
- isSameStyle:function (nodeA, nodeB) {
- var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
- styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
- if (browser.opera) {
- styleA = nodeA.style;
- styleB = nodeB.style;
- if (styleA.length != styleB.length)
- return false;
- for (var p in styleA) {
- if (/^(\d+|csstext)$/i.test(p)) {
- continue;
- }
- if (styleA[p] != styleB[p]) {
- return false;
- }
- }
- return true;
- }
- if (!styleA || !styleB) {
- return styleA == styleB;
- }
- styleA = styleA.split(';');
- styleB = styleB.split(';');
- if (styleA.length != styleB.length) {
- return false;
- }
- for (var i = 0, ci; ci = styleA[i++];) {
- if (utils.indexOf(styleB, ci) == -1) {
- return false;
- }
- }
- return true;
- },
- /**
- * 检查节点node是否为block元素
- * @method isBlockElm
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 是否是block元素节点
- * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
- * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- isBlockElm:function (node) {
- return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
- },
- /**
- * 检测node节点是否为body节点
- * @method isBody
- * @param { Element } node 需要检测的dom元素
- * @return { Boolean } 给定的元素是否是body元素
- * @example
- * ```javascript
- * //output: true
- * console.log( UE.dom.domUtils.isBody( document.body ) );
- * ```
- */
- isBody:function (node) {
- return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
- },
- /**
- * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
- * 拆分形成的两个节点之间是node节点
- * @method breakParent
- * @param { Node } node 作为分界的节点对象
- * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
- * @return { Node } 给定的node分界节点
- * @example
- * ```javascript
- *
- * var node = document.createElement("span"),
- * wrapNode = document.createElement( "div" ),
- * parent = document.createElement("p");
- *
- * parent.appendChild( node );
- * wrapNode.appendChild( parent );
- *
- * //拆分前
- * //output:
- * console.log( wrapNode.innerHTML );
- *
- *
- * UE.dom.domUtils.breakParent( node, parent );
- * //拆分后
- * //output:
- * console.log( wrapNode.innerHTML );
- *
- * ```
- */
- breakParent:function (node, parent) {
- var tmpNode,
- parentClone = node,
- clone = node,
- leftNodes,
- rightNodes;
- do {
- parentClone = parentClone.parentNode;
- if (leftNodes) {
- tmpNode = parentClone.cloneNode(false);
- tmpNode.appendChild(leftNodes);
- leftNodes = tmpNode;
- tmpNode = parentClone.cloneNode(false);
- tmpNode.appendChild(rightNodes);
- rightNodes = tmpNode;
- } else {
- leftNodes = parentClone.cloneNode(false);
- rightNodes = leftNodes.cloneNode(false);
- }
- while (tmpNode = clone.previousSibling) {
- leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
- }
- while (tmpNode = clone.nextSibling) {
- rightNodes.appendChild(tmpNode);
- }
- clone = parentClone;
- } while (parent !== parentClone);
- tmpNode = parent.parentNode;
- tmpNode.insertBefore(leftNodes, parent);
- tmpNode.insertBefore(rightNodes, parent);
- tmpNode.insertBefore(node, rightNodes);
- domUtils.remove(parent);
- return node;
- },
- /**
- * 检查节点node是否是空inline节点
- * @method isEmptyInlineElement
- * @param { Node } node 需要检测的节点对象
- * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
- * @example
- * ```html
- * => 1
- * => 1
- * => 1
- * xx => 0
- * ```
- */
- isEmptyInlineElement:function (node) {
- if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
- return 0;
- }
- node = node.firstChild;
- while (node) {
- //如果是创建的bookmark就跳过
- if (domUtils.isBookmarkNode(node)) {
- return 0;
- }
- if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
- node.nodeType == 3 && !domUtils.isWhitespace(node)
- ) {
- return 0;
- }
- node = node.nextSibling;
- }
- return 1;
-
- },
-
- /**
- * 删除node节点下首尾两端的空白文本子节点
- * @method trimWhiteTextNode
- * @param { Element } node 需要执行删除操作的元素对象
- * @example
- * ```javascript
- * var node = document.createElement("div");
- *
- * node.appendChild( document.createTextNode( "" ) );
- *
- * node.appendChild( document.createElement("div") );
- *
- * node.appendChild( document.createTextNode( "" ) );
- *
- * //3
- * console.log( node.childNodes.length );
- *
- * UE.dom.domUtils.trimWhiteTextNode( node );
- *
- * //1
- * console.log( node.childNodes.length );
- * ```
- */
- trimWhiteTextNode:function (node) {
- function remove(dir) {
- var child;
- while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
- node.removeChild(child);
- }
- }
- remove('firstChild');
- remove('lastChild');
- },
-
- /**
- * 合并node节点下相同的子节点
- * @name mergeChild
- * @desc
- * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
- * @example
- * xxaaxx
- * ==> UE.dom.domUtils.mergeChild(node,'span')
- * xxaaxx
- */
- mergeChild:function (node, tagName, attrs) {
- var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
- for (var i = 0, ci; ci = list[i++];) {
- if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
- continue;
- }
- //span单独处理
- if (ci.tagName.toLowerCase() == 'span') {
- if (node === ci.parentNode) {
- domUtils.trimWhiteTextNode(node);
- if (node.childNodes.length == 1) {
- node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
- domUtils.remove(ci, true);
- continue;
- }
- }
- ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
- if (attrs) {
- var style = attrs.style;
- if (style) {
- style = style.split(';');
- for (var j = 0, s; s = style[j++];) {
- ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
- }
- }
- }
- if (domUtils.isSameStyle(ci, node)) {
- domUtils.remove(ci, true);
- }
- continue;
- }
- if (domUtils.isSameElement(node, ci)) {
- domUtils.remove(ci, true);
- }
- }
- },
-
- /**
- * 原生方法getElementsByTagName的封装
- * @method getElementsByTagName
- * @param { Node } node 目标节点对象
- * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
- * @return { Array } 符合条件的节点集合
- */
- getElementsByTagName:function (node, name,filter) {
- if(filter && utils.isString(filter)){
- var className = filter;
- filter = function(node){return domUtils.hasClass(node,className)}
- }
- name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' ');
- var arr = [];
- for(var n = 0,ni;ni=name[n++];){
- var list = node.getElementsByTagName(ni);
- for (var i = 0, ci; ci = list[i++];) {
- if(!filter || filter(ci))
- arr.push(ci);
- }
- }
-
- return arr;
- },
- /**
- * 将节点node提取到父节点上
- * @method mergeToParent
- * @param { Element } node 需要提取的元素对象
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- mergeToParent:function (node) {
- var parent = node.parentNode;
- while (parent && dtd.$removeEmpty[parent.tagName]) {
- if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
- domUtils.trimWhiteTextNode(parent);
- //span需要特殊处理 不处理这样的情况 xxxxxxxxx
- if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
- || (parent.tagName == 'A' && node.tagName == 'SPAN')) {
- if (parent.childNodes.length > 1 || parent !== node.parentNode) {
- node.style.cssText = parent.style.cssText + ";" + node.style.cssText;
- parent = parent.parentNode;
- continue;
- } else {
- parent.style.cssText += ";" + node.style.cssText;
- //trace:952 a标签要保持下划线
- if (parent.tagName == 'A') {
- parent.style.textDecoration = 'underline';
- }
- }
- }
- if (parent.tagName != 'A') {
- parent === node.parentNode && domUtils.remove(node, true);
- break;
- }
- }
- parent = parent.parentNode;
- }
- },
- /**
- * 合并节点node的左右兄弟节点
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
-
- /**
- * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @param { Boolean } ignorePre 是否忽略合并左节点
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
-
- /**
- * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @param { Boolean } ignorePre 是否忽略合并左节点
- * @param { Boolean } ignoreNext 是否忽略合并右节点
- * @remind 如果同时忽略左右节点, 则该操作什么也不会做
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
- mergeSibling:function (node, ignorePre, ignoreNext) {
- function merge(rtl, start, node) {
- var next;
- if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
- while (next.firstChild) {
- if (start == 'firstChild') {
- node.insertBefore(next.lastChild, node.firstChild);
- } else {
- node.appendChild(next.firstChild);
- }
- }
- domUtils.remove(next);
- }
- }
- !ignorePre && merge('previousSibling', 'firstChild', node);
- !ignoreNext && merge('nextSibling', 'lastChild', node);
- },
-
- /**
- * 设置节点node及其子节点不会被选中
- * @method unSelectable
- * @param { Element } node 需要执行操作的dom元素
- * @remind 执行该操作后的节点, 将不能被鼠标选中
- * @example
- * ```javascript
- * UE.dom.domUtils.unSelectable( document.body );
- * ```
- */
- unSelectable:ie && browser.ie9below || browser.opera ? function (node) {
- //for ie9
- node.onselectstart = function () {
- return false;
- };
- node.onclick = node.onkeyup = node.onkeydown = function () {
- return false;
- };
- node.unselectable = 'on';
- node.setAttribute("unselectable", "on");
- for (var i = 0, ci; ci = node.all[i++];) {
- switch (ci.tagName.toLowerCase()) {
- case 'iframe' :
- case 'textarea' :
- case 'input' :
- case 'select' :
- break;
- default :
- ci.unselectable = 'on';
- node.setAttribute("unselectable", "on");
- }
- }
- } : function (node) {
- node.style.MozUserSelect =
- node.style.webkitUserSelect =
- node.style.msUserSelect =
- node.style.KhtmlUserSelect = 'none';
- },
- /**
- * 删除节点node上的指定属性名称的属性
- * @method removeAttributes
- * @param { Node } node 需要删除属性的节点对象
- * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
- * @example
- * ```html
- *
- * xxxxx
- *
- *
- *
- * ```
- */
-
- /**
- * 删除节点node上的指定属性名称的属性
- * @method removeAttributes
- * @param { Node } node 需要删除属性的节点对象
- * @param { Array } attrNames 需要删除的属性名数组
- * @example
- * ```html
- *
- * xxxxx
- *
- *
- *
- * ```
- */
- removeAttributes:function (node, attrNames) {
- attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' ');
- for (var i = 0, ci; ci = attrNames[i++];) {
- ci = attrFix[ci] || ci;
- switch (ci) {
- case 'className':
- node[ci] = '';
- break;
- case 'style':
- node.style.cssText = '';
- var val = node.getAttributeNode('style');
- !browser.ie && val && node.removeAttributeNode(val);
- }
- node.removeAttribute(ci);
- }
- },
- /**
- * 在doc下创建一个标签名为tag,属性为attrs的元素
- * @method createElement
- * @param { DomDocument } doc 新创建的元素属于该document节点创建
- * @param { String } tagName 需要创建的元素的标签名
- * @param { Object } attrs 新创建的元素的属性key-value集合
- * @return { Element } 新创建的元素对象
- * @example
- * ```javascript
- * var ele = UE.dom.domUtils.createElement( document, 'div', {
- * id: 'test'
- * } );
- *
- * //output: DIV
- * console.log( ele.tagName );
- *
- * //output: test
- * console.log( ele.id );
- *
- * ```
- */
- createElement:function (doc, tag, attrs) {
- return domUtils.setAttributes(doc.createElement(tag), attrs)
- },
- /**
- * 为节点node添加属性attrs,attrs为属性键值对
- * @method setAttributes
- * @param { Element } node 需要设置属性的元素对象
- * @param { Object } attrs 需要设置的属性名-值对
- * @return { Element } 设置属性的元素对象
- * @example
- * ```html
- *
- *
- *
- *
- */
- setAttributes:function (node, attrs) {
- for (var attr in attrs) {
- if(attrs.hasOwnProperty(attr)){
- var value = attrs[attr];
- switch (attr) {
- case 'class':
- //ie下要这样赋值,setAttribute不起作用
- node.className = value;
- break;
- case 'style' :
- node.style.cssText = node.style.cssText + ";" + value;
- break;
- case 'innerHTML':
- node[attr] = value;
- break;
- case 'value':
- node.value = value;
- break;
- default:
- node.setAttribute(attrFix[attr] || attr, value);
- }
- }
- }
- return node;
- },
-
- /**
- * 获取元素element经过计算后的样式值
- * @method getComputedStyle
- * @param { Element } element 需要获取样式的元素对象
- * @param { String } styleName 需要获取的样式名
- * @return { String } 获取到的样式值
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- getComputedStyle:function (element, styleName) {
- //一下的属性单独处理
- var pros = 'width height top left';
-
- if(pros.indexOf(styleName) > -1){
- return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px';
- }
- //忽略文本节点
- if (element.nodeType == 3) {
- element = element.parentNode;
- }
- //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
- if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize &&
- !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
- var span = element.ownerDocument.createElement('span');
- span.style.cssText = 'padding:0;border:0;font-family:simsun;';
- span.innerHTML = '.';
- element.appendChild(span);
- var result = span.offsetHeight;
- element.removeChild(span);
- span = null;
- return result + 'px';
- }
- try {
- var value = domUtils.getStyle(element, styleName) ||
- (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
- ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]);
-
- } catch (e) {
- return "";
- }
- return utils.transUnitToPx(utils.fixColor(styleName, value));
- },
- /**
- * 删除元素element指定的className
- * @method removeClasses
- * @param { Element } ele 需要删除class的元素节点
- * @param { String } classNames 需要删除的className, 多个className之间以空格分开
- * @example
- * ```html
- * xxx
- *
- *
- * ```
- */
-
- /**
- * 删除元素element指定的className
- * @method removeClasses
- * @param { Element } ele 需要删除class的元素节点
- * @param { Array } classNames 需要删除的className数组
- * @example
- * ```html
- * xxx
- *
- *
- * ```
- */
- removeClasses:function (elm, classNames) {
- classNames = utils.isArray(classNames) ? classNames :
- utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
- for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
- cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'')
- }
- cls = utils.trim(cls).replace(/[ ]{2,}/g,' ');
- if(cls){
- elm.className = cls;
- }else{
- domUtils.removeAttributes(elm,['class']);
- }
- },
- /**
- * 给元素element添加className
- * @method addClass
- * @param { Node } ele 需要增加className的元素
- * @param { String } classNames 需要添加的className, 多个className之间以空格分割
- * @remind 相同的类名不会被重复添加
- * @example
- * ```html
- *
- *
- *
- * ```
- */
-
- /**
- * 判断元素element是否包含给定的样式类名className
- * @method hasClass
- * @param { Node } ele 需要检测的元素
- * @param { Array } classNames 需要检测的className数组
- * @return { Boolean } 元素是否包含所有给定的className
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- hasClass:function (element, className) {
- if(utils.isRegExp(className)){
- return className.test(element.className)
- }
- className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' ');
- for(var i = 0,ci,cls = element.className;ci=className[i++];){
- if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){
- return false;
- }
- }
- return i - 1 == className.length;
- },
-
- /**
- * 阻止事件默认行为
- * @method preventDefault
- * @param { Event } evt 需要阻止默认行为的事件对象
- * @example
- * ```javascript
- * UE.dom.domUtils.preventDefault( evt );
- * ```
- */
- preventDefault:function (evt) {
- evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
- },
- /**
- * 删除元素element指定的样式
- * @method removeStyle
- * @param { Element } element 需要删除样式的元素
- * @param { String } styleName 需要删除的样式名
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- removeStyle:function (element, name) {
- if(browser.ie ){
- //针对color先单独处理一下
- if(name == 'color'){
- name = '(^|;)' + name;
- }
- element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
- }else{
- if (element.style.removeProperty) {
- element.style.removeProperty (name);
- }else {
- element.style.removeAttribute (utils.cssStyleToDomStyle(name));
- }
- }
-
-
- if (!element.style.cssText) {
- domUtils.removeAttributes(element, ['style']);
- }
- },
- /**
- * 获取元素element的style属性的指定值
- * @method getStyle
- * @param { Element } element 需要获取属性值的元素
- * @param { String } styleName 需要获取的style的名称
- * @warning 该方法仅获取元素style属性中所标明的值
- * @return { String } 该元素包含指定的style属性值
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- getStyle:function (element, name) {
- var value = element.style[ utils.cssStyleToDomStyle(name) ];
- return utils.fixColor(name, value);
- },
- /**
- * 为元素element设置样式属性值
- * @method setStyle
- * @param { Element } element 需要设置样式的元素
- * @param { String } styleName 样式名
- * @param { String } styleValue 样式值
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setStyle:function (element, name, value) {
- element.style[utils.cssStyleToDomStyle(name)] = value;
- if(!utils.trim(element.style.cssText)){
- this.removeAttributes(element,'style')
- }
- },
- /**
- * 为元素element设置多个样式属性值
- * @method setStyles
- * @param { Element } element 需要设置样式的元素
- * @param { Object } styles 样式名值对
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setStyles:function (element, styles) {
- for (var name in styles) {
- if (styles.hasOwnProperty(name)) {
- domUtils.setStyle(element, name, styles[name]);
- }
- }
- },
- /**
- * 删除_moz_dirty属性
- * @private
- * @method removeDirtyAttr
- */
- removeDirtyAttr:function (node) {
- for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
- ci.removeAttribute('_moz_dirty');
- }
- node.removeAttribute('_moz_dirty');
- },
- /**
- * 获取子节点的数量
- * @method getChildCount
- * @param { Element } node 需要检测的元素
- * @return { Number } 给定的node元素的子节点数量
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 根据给定的过滤规则, 获取符合条件的子节点的数量
- * @method getChildCount
- * @param { Element } node 需要检测的元素
- * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
- * @return { Number } 符合过滤条件的node元素的子节点数量
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- getChildCount:function (node, fn) {
- var count = 0, first = node.firstChild;
- fn = fn || function () {
- return 1;
- };
- while (first) {
- if (fn(first)) {
- count++;
- }
- first = first.nextSibling;
- }
- return count;
- },
-
- /**
- * 判断给定节点是否为空节点
- * @method isEmptyNode
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 节点是否为空
- * @example
- * ```javascript
- * UE.dom.domUtils.isEmptyNode( document.body );
- * ```
- */
- isEmptyNode:function (node) {
- return !node.firstChild || domUtils.getChildCount(node, function (node) {
- return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
- }) == 0
- },
- clearSelectedArr:function (nodes) {
- var node;
- while (node = nodes.pop()) {
- domUtils.removeAttributes(node, ['class']);
- }
- },
- /**
- * 将显示区域滚动到指定节点的位置
- * @method scrollToView
- * @param {Node} node 节点
- * @param {window} win window对象
- * @param {Number} offsetTop 距离上方的偏移量
- */
- scrollToView:function (node, win, offsetTop) {
- var getViewPaneSize = function () {
- var doc = win.document,
- mode = doc.compatMode == 'CSS1Compat';
- return {
- width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
- height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
- };
- },
- getScrollPosition = function (win) {
- if ('pageXOffset' in win) {
- return {
- x:win.pageXOffset || 0,
- y:win.pageYOffset || 0
- };
- }
- else {
- var doc = win.document;
- return {
- x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
- y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
- };
- }
- };
- var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
- offset += (node.offsetHeight || 0);
- var elementPosition = domUtils.getXY(node);
- offset += elementPosition.y;
- var currentScroll = getScrollPosition(win).y;
- // offset += 50;
- if (offset > currentScroll || offset < currentScroll - winHeight) {
- win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
- }
- },
- /**
- * 判断给定节点是否为br
- * @method isBr
- * @param { Node } node 需要判断的节点对象
- * @return { Boolean } 给定的节点是否是br节点
- */
- isBr:function (node) {
- return node.nodeType == 1 && node.tagName == 'BR';
- },
- /**
- * 判断给定的节点是否是一个“填充”节点
- * @private
- * @method isFillChar
- * @param { Node } node 需要判断的节点
- * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
- * @returns { Boolean } 节点是否是填充节点
- */
- isFillChar:function (node,isInStart) {
- if(node.nodeType != 3)
- return false;
- var text = node.nodeValue;
- if(isInStart){
- return new RegExp('^' + domUtils.fillChar).test(text)
- }
- return !text.replace(new RegExp(domUtils.fillChar,'g'), '').length
- },
- isStartInblock:function (range) {
- var tmpRange = range.cloneRange(),
- flag = 0,
- start = tmpRange.startContainer,
- tmp;
- if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){
- start = start.childNodes[tmpRange.startOffset];
- var pre = start.previousSibling;
- while(pre && domUtils.isFillChar(pre)){
- start = pre;
- pre = pre.previousSibling;
- }
- }
- if(this.isFillChar(start,true) && tmpRange.startOffset == 1){
- tmpRange.setStartBefore(start);
- start = tmpRange.startContainer;
- }
-
- while (start && domUtils.isFillChar(start)) {
- tmp = start;
- start = start.previousSibling
- }
- if (tmp) {
- tmpRange.setStartBefore(tmp);
- start = tmpRange.startContainer;
- }
- if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
- tmpRange.setStart(start, 0).collapse(true);
- }
- while (!tmpRange.startOffset) {
- start = tmpRange.startContainer;
- if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
- flag = 1;
- break;
- }
- var pre = tmpRange.startContainer.previousSibling,
- tmpNode;
- if (!pre) {
- tmpRange.setStartBefore(tmpRange.startContainer);
- } else {
- while (pre && domUtils.isFillChar(pre)) {
- tmpNode = pre;
- pre = pre.previousSibling;
- }
- if (tmpNode) {
- tmpRange.setStartBefore(tmpNode);
- } else {
- tmpRange.setStartBefore(tmpRange.startContainer);
- }
- }
- }
- return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
- },
-
- /**
- * 判断给定的元素是否是一个空元素
- * @method isEmptyBlock
- * @param { Element } node 需要判断的元素
- * @return { Boolean } 是否是空元素
- * @example
- * ```html
- *
- *
- *
- * ```
- */
-
- /**
- * 根据指定的判断规则判断给定的元素是否是一个空元素
- * @method isEmptyBlock
- * @param { Element } node 需要判断的元素
- * @param { RegExp } reg 对内容执行判断的正则表达式对象
- * @return { Boolean } 是否是空元素
- */
- isEmptyBlock:function (node,reg) {
- if(node.nodeType != 1)
- return 0;
- reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g');
-
- if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
- return 0;
- }
- for (var n in dtd.$isNotEmpty) {
- if (node.getElementsByTagName(n).length) {
- return 0;
- }
- }
- return 1;
- },
-
- /**
- * 移动元素使得该元素的位置移动指定的偏移量的距离
- * @method setViewportOffset
- * @param { Element } element 需要设置偏移量的元素
- * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
- * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
- * offset.top的距离
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setViewportOffset:function (element, offset) {
- var left = parseInt(element.style.left) | 0;
- var top = parseInt(element.style.top) | 0;
- var rect = element.getBoundingClientRect();
- var offsetLeft = offset.left - rect.left;
- var offsetTop = offset.top - rect.top;
- if (offsetLeft) {
- element.style.left = left + offsetLeft + 'px';
- }
- if (offsetTop) {
- element.style.top = top + offsetTop + 'px';
- }
- },
-
- /**
- * 用“填充字符”填充节点
- * @method fillNode
- * @private
- * @param { DomDocument } doc 填充的节点所在的docment对象
- * @param { Node } node 需要填充的节点对象
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- fillNode:function (doc, node) {
- var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
- node.innerHTML = '';
- node.appendChild(tmpNode);
- },
-
- /**
- * 把节点src的所有子节点追加到另一个节点tag上去
- * @method moveChild
- * @param { Node } src 源节点, 该节点下的所有子节点将被移除
- * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
- * @method moveChild
- * @param { Node } src 源节点, 该节点下的所有子节点将被移除
- * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
- * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- * ```
- */
- moveChild:function (src, tag, dir) {
- while (src.firstChild) {
- if (dir && tag.firstChild) {
- tag.insertBefore(src.lastChild, tag.firstChild);
- } else {
- tag.appendChild(src.firstChild);
- }
- }
- },
-
- /**
- * 判断节点的标签上是否不存在任何属性
- * @method hasNoAttributes
- * @private
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 节点是否不包含任何属性
- * @example
- * ```html
- * xxxx
- *
- *
- * ```
- */
- hasNoAttributes:function (node) {
- return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0;
- },
-
- /**
- * 检测节点是否是UEditor所使用的辅助节点
- * @method isCustomeNode
- * @private
- * @param { Node } node 需要检测的节点
- * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
- * @return { Boolean } 给定的节点是否是一个辅助节点
- */
- isCustomeNode:function (node) {
- return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
- },
-
- /**
- * 检测节点的标签是否是给定的标签
- * @method isTagNode
- * @param { Node } node 需要检测的节点对象
- * @param { String } tagName 标签
- * @return { Boolean } 节点的标签是否是给定的标签
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- isTagNode:function (node, tagNames) {
- return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b','i').test(tagNames)
- },
-
- /**
- * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
- * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: null
- * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() !== 'div';
- * } ) );
- * ```
- */
-
- /**
- * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
- * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: null
- * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
- * ```
- */
-
- /**
- * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
- * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
- * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
- * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
- * 过滤条件的节点数组或第一个节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: 3(假定有3个div)
- * console.log( divNodes.length );
- *
- * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() === 'div';
- * }, true );
- *
- * //output: 3
- * console.log( nodes.length );
- *
- * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() === 'div';
- * }, false );
- *
- * //output: div
- * console.log( node.nodeName );
- * ```
- */
- filterNodeList : function(nodelist,filter,forAll){
- var results = [];
- if(!utils .isFunction(filter)){
- var str = filter;
- filter = function(n){
- return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1
- };
- }
- utils.each(nodelist,function(n){
- filter(n) && results.push(n)
- });
- return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
- },
-
- /**
- * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
- * @method isInNodeEndBoundary
- * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
- * @param node 需要检测的节点对象
- * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
- */
- isInNodeEndBoundary : function (rng,node){
- var start = rng.startContainer;
- if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){
- return 0;
- }
- if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){
- return 0;
- }
- while(start !== node){
- if(start.nextSibling){
- return 0
- };
- start = start.parentNode;
- }
- return 1;
- },
- isBoundaryNode : function (node,dir){
- var tmp;
- while(!domUtils.isBody(node)){
- tmp = node;
- node = node.parentNode;
- if(tmp !== node[dir]){
- return false;
- }
- }
- return true;
- },
- fillHtml : browser.ie11below ? ' ' : '
'
-};
-var fillCharReg = new RegExp(domUtils.fillChar, 'g');
-
-// core/Range.js
-/**
- * Range封装
- * @file
- * @module UE.dom
- * @class Range
- * @since 1.2.6.1
- */
-
-/**
- * dom操作封装
- * @unfile
- * @module UE.dom
- */
-
-/**
- * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
- * @unfile
- * @module UE.dom
- * @class Range
- */
-
-
-(function () {
- var guid = 0,
- fillChar = domUtils.fillChar,
- fillData;
-
- /**
- * 更新range的collapse状态
- * @param {Range} range range对象
- */
- function updateCollapse(range) {
- range.collapsed =
- range.startContainer && range.endContainer &&
- range.startContainer === range.endContainer &&
- range.startOffset == range.endOffset;
- }
-
- function selectOneNode(rng){
- return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
- }
- function setEndPoint(toStart, node, offset, range) {
- //如果node是自闭合标签要处理
- if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
- offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
- node = node.parentNode;
- }
- if (toStart) {
- range.startContainer = node;
- range.startOffset = offset;
- if (!range.endContainer) {
- range.collapse(true);
- }
- } else {
- range.endContainer = node;
- range.endOffset = offset;
- if (!range.startContainer) {
- range.collapse(false);
- }
- }
- updateCollapse(range);
- return range;
- }
-
- function execContentsAction(range, action) {
- //调整边界
- //range.includeBookmark();
- var start = range.startContainer,
- end = range.endContainer,
- startOffset = range.startOffset,
- endOffset = range.endOffset,
- doc = range.document,
- frag = doc.createDocumentFragment(),
- tmpStart, tmpEnd;
- if (start.nodeType == 1) {
- start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
- }
- if (end.nodeType == 1) {
- end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
- }
- if (start === end && start.nodeType == 3) {
- frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
- //is not clone
- if (action) {
- start.deleteData(startOffset, endOffset - startOffset);
- range.collapse(true);
- }
- return frag;
- }
- var current, currentLevel, clone = frag,
- startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
- for (var i = 0; startParents[i] == endParents[i];) {
- i++;
- }
- for (var j = i, si; si = startParents[j]; j++) {
- current = si.nextSibling;
- if (si == start) {
- if (!tmpStart) {
- if (range.startContainer.nodeType == 3) {
- clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
- //is not clone
- if (action) {
- start.deleteData(startOffset, start.nodeValue.length - startOffset);
- }
- } else {
- clone.appendChild(!action ? start.cloneNode(true) : start);
- }
- }
- } else {
- currentLevel = si.cloneNode(false);
- clone.appendChild(currentLevel);
- }
- while (current) {
- if (current === end || current === endParents[j]) {
- break;
- }
- si = current.nextSibling;
- clone.appendChild(!action ? current.cloneNode(true) : current);
- current = si;
- }
- clone = currentLevel;
- }
- clone = frag;
- if (!startParents[i]) {
- clone.appendChild(startParents[i - 1].cloneNode(false));
- clone = clone.firstChild;
- }
- for (var j = i, ei; ei = endParents[j]; j++) {
- current = ei.previousSibling;
- if (ei == end) {
- if (!tmpEnd && range.endContainer.nodeType == 3) {
- clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
- //is not clone
- if (action) {
- end.deleteData(0, endOffset);
- }
- }
- } else {
- currentLevel = ei.cloneNode(false);
- clone.appendChild(currentLevel);
- }
- //如果两端同级,右边第一次已经被开始做了
- if (j != i || !startParents[i]) {
- while (current) {
- if (current === start) {
- break;
- }
- ei = current.previousSibling;
- clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
- current = ei;
- }
- }
- clone = currentLevel;
- }
- if (action) {
- range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
- }
- tmpStart && domUtils.remove(tmpStart);
- tmpEnd && domUtils.remove(tmpEnd);
- return frag;
- }
-
- /**
- * 创建一个跟document绑定的空的Range实例
- * @constructor
- * @param { Document } document 新建的选区所属的文档对象
- */
-
- /**
- * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
- */
-
- /**
- * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
- * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
- */
-
- /**
- * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
- */
-
- /**
- * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
- * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
- */
-
- /**
- * @property { Boolean } collapsed 当前Range是否闭合
- * @default true
- * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
- */
-
- /**
- * @property { Document } document 当前Range所属的Document对象
- * @remind 不同range的的document属性可以是不同的
- */
- var Range = dom.Range = function (document) {
- var me = this;
- me.startContainer =
- me.startOffset =
- me.endContainer =
- me.endOffset = null;
- me.document = document;
- me.collapsed = true;
- };
-
- /**
- * 删除fillData
- * @param doc
- * @param excludeNode
- */
- function removeFillData(doc, excludeNode) {
- try {
- if (fillData && domUtils.inDoc(fillData, doc)) {
- if (!fillData.nodeValue.replace(fillCharReg, '').length) {
- var tmpNode = fillData.parentNode;
- domUtils.remove(fillData);
- while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
- //safari的contains有bug
- (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
- ) {
- fillData = tmpNode.parentNode;
- domUtils.remove(tmpNode);
- tmpNode = fillData;
- }
- } else {
- fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
- }
- }
- } catch (e) {
- }
- }
-
- /**
- * @param node
- * @param dir
- */
- function mergeSibling(node, dir) {
- var tmpNode;
- node = node[dir];
- while (node && domUtils.isFillChar(node)) {
- tmpNode = node[dir];
- domUtils.remove(node);
- node = tmpNode;
- }
- }
-
- Range.prototype = {
-
- /**
- * 克隆选区的内容到一个DocumentFragment里
- * @method cloneContents
- * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- * ```
- */
- cloneContents:function () {
- return this.collapsed ? null : execContentsAction(this, 0);
- },
-
- /**
- * 删除当前选区范围中的所有内容
- * @method deleteContents
- * @remind 执行完该操作后, 当前Range对象变成了闭合状态
- * @return { UE.dom.Range } 当前操作的Range对象
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- * ```
- */
- deleteContents:function () {
- var txt;
- if (!this.collapsed) {
- execContentsAction(this, 1);
- }
- if (browser.webkit) {
- txt = this.startContainer;
- if (txt.nodeType == 3 && !txt.nodeValue.length) {
- this.setStartBefore(txt).collapse(true);
- domUtils.remove(txt);
- }
- }
- return this;
- },
-
- /**
- * 将当前选区的内容提取到一个DocumentFragment里
- * @method extractContents
- * @remind 执行该操作后, 选区将变成闭合状态
- * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
- * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- */
- extractContents:function () {
- return this.collapsed ? null : execContentsAction(this, 2);
- },
-
- /**
- * 设置Range的开始容器节点和偏移量
- * @method setStart
- * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
- * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
- * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
- * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
- * 中的索引
- * @param { Node } node 将被设为当前选区开始边界容器的节点对象
- * @param { int } offset 选区的开始位置偏移量
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxxxxxxxxxx[xxx]
- *
- *
- * ```
- * @example
- * ```html
- *
- * xxx[xx]x
- *
- *
- * ```
- */
- setStart:function (node, offset) {
- return setEndPoint(true, node, offset, this);
- },
-
- /**
- * 设置Range的结束容器和偏移量
- * @method setEnd
- * @param { Node } node 作为当前选区结束边界容器的节点对象
- * @param { int } offset 结束边界的偏移量
- * @see UE.dom.Range:setStart(Node,int)
- * @return { UE.dom.Range } 当前range对象
- */
- setEnd:function (node, offset) {
- return setEndPoint(false, node, offset, this);
- },
-
- /**
- * 将Range开始位置设置到node节点之后
- * @method setStartAfter
- * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
- * @param { Node } node 选区的开始边界将紧接着该节点之后
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxxxx[xxxx]
- *
- *
- * ```
- */
- setStartAfter:function (node) {
- return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
- },
-
- /**
- * 将Range开始位置设置到node节点之前
- * @method setStartBefore
- * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
- * @param { Node } node 新的选区开始位置在该节点之前
- * @see UE.dom.Range:setStartAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setStartBefore:function (node) {
- return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
- },
-
- /**
- * 将Range结束位置设置到node节点之后
- * @method setEndAfter
- * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * [xxxxxxx]xxxx
- *
- *
- * ```
- */
- setEndAfter:function (node) {
- return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
- },
-
- /**
- * 将Range结束位置设置到node节点之前
- * @method setEndBefore
- * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setEndAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndBefore:function (node) {
- return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
- },
-
- /**
- * 设置Range的开始位置到node节点内的第一个子节点之前
- * @method setStartAtFirst
- * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
- * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartBefore(Node)
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- setStartAtFirst:function (node) {
- return this.setStart(node, 0);
- },
-
- /**
- * 设置Range的开始位置到node节点内的最后一个节点之后
- * @method setStartAtLast
- * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
- * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setStartAtLast:function (node) {
- return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
- },
-
- /**
- * 设置Range的结束位置到node节点内的第一个节点之前
- * @method setEndAtFirst
- * @param { Node } node 目标节点
- * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
- * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndAtFirst:function (node) {
- return this.setEnd(node, 0);
- },
-
- /**
- * 设置Range的结束位置到node节点内的最后一个节点之后
- * @method setEndAtLast
- * @param { Node } node 目标节点
- * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
- * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndAtLast:function (node) {
- return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
- },
-
- /**
- * 选中给定节点
- * @method selectNode
- * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
- * 而endOffset为startOffset+1
- * @param { Node } node 需要选中的节点
- * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- selectNode:function (node) {
- return this.setStartBefore(node).setEndAfter(node);
- },
-
- /**
- * 选中给定节点内部的所有节点
- * @method selectNodeContents
- * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
- * 而endOffset是该节点的子节点数。
- * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
- * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- selectNodeContents:function (node) {
- return this.setStart(node, 0).setEndAtLast(node);
- },
-
- /**
- * clone当前Range对象
- * @method cloneRange
- * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
- * @return { UE.dom.Range } 当前range对象的一个副本
- */
- cloneRange:function () {
- var me = this;
- return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
-
- },
-
- /**
- * 向当前选区的结束处闭合选区
- * @method collapse
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
-
- /**
- * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
- * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
- * @method collapse
- * @param { Boolean } toStart 是否向选区开始处闭合
- * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
- * @see UE.dom.Range:collapse()
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- collapse:function (toStart) {
- var me = this;
- if (toStart) {
- me.endContainer = me.startContainer;
- me.endOffset = me.startOffset;
- } else {
- me.startContainer = me.endContainer;
- me.startOffset = me.endOffset;
- }
- me.collapsed = true;
- return me;
- },
-
- /**
- * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
- * @method shrinkBoundary
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- * xxxx[xxxxx] => xxxx[xxxxx]
- * ```
- *
- * @example
- * ```html
- *
- * x[xx]xxx
- *
- *
- * ```
- *
- * @example
- * ```html
- * [xxxxxxxxxxx] => [xxxxxxxxxxx]
- * ```
- */
-
- /**
- * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
- * 如果ignoreEnd的值为true,则忽略对结束位置的调整
- * @method shrinkBoundary
- * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.domUtils.Range:shrinkBoundary()
- */
- shrinkBoundary:function (ignoreEnd) {
- var me = this, child,
- collapsed = me.collapsed;
- function check(node){
- return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
- }
- while (me.startContainer.nodeType == 1 //是element
- && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
- && check(child)) {
- me.setStart(child, 0);
- }
- if (collapsed) {
- return me.collapse(true);
- }
- if (!ignoreEnd) {
- while (me.endContainer.nodeType == 1//是element
- && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
- && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
- && check(child)) {
- me.setEnd(child, child.childNodes.length);
- }
- }
- return me;
- },
-
- /**
- * 获取离当前选区内包含的所有节点最近的公共祖先节点,
- * @method getCommonAncestor
- * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @example
- * ```html
- * //选区示例
- * xxxx[xxx]xxxxxx
- *
- * ```
- */
-
- /**
- * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
- * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
- * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
- * @method getCommonAncestor
- * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @see UE.dom.Range:getCommonAncestor()
- * @example
- * ```html
- *
- *
- *
- * xxxxxxxxx[xxx]xxxxxxxx
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
- * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
- * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
- * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
- * @method getCommonAncestor
- * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
- * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @see UE.dom.Range:getCommonAncestor()
- * @see UE.dom.Range:getCommonAncestor(Boolean)
- * @example
- * ```html
- *
- *
- *
- * xxxxxxxx[x]xxxxxxxxxxx
- *
- *
- *
- *
- * ```
- */
- getCommonAncestor:function (includeSelf, ignoreTextNode) {
- var me = this,
- start = me.startContainer,
- end = me.endContainer;
- if (start === end) {
- if (includeSelf && selectOneNode(this)) {
- start = start.childNodes[me.startOffset];
- if(start.nodeType == 1)
- return start;
- }
- //只有在上来就相等的情况下才会出现是文本的情况
- return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
- }
- return domUtils.getCommonAncestor(start, end);
- },
-
- /**
- * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
- * @method trimBoundary
- * @remind 该操作有可能会引起文本节点被切开
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * //选区示例
- * xxx[xxxxx]xxx
- *
- *
- * ```
- */
-
- /**
- * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
- * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
- * @method trimBoundary
- * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * //选区示例
- * xxx[xxxxx]xxx
- *
- *
- * ```
- */
- trimBoundary:function (ignoreEnd) {
- this.txtToElmBoundary();
- var start = this.startContainer,
- offset = this.startOffset,
- collapsed = this.collapsed,
- end = this.endContainer;
- if (start.nodeType == 3) {
- if (offset == 0) {
- this.setStartBefore(start);
- } else {
- if (offset >= start.nodeValue.length) {
- this.setStartAfter(start);
- } else {
- var textNode = domUtils.split(start, offset);
- //跟新结束边界
- if (start === end) {
- this.setEnd(textNode, this.endOffset - offset);
- } else if (start.parentNode === end) {
- this.endOffset += 1;
- }
- this.setStartBefore(textNode);
- }
- }
- if (collapsed) {
- return this.collapse(true);
- }
- }
- if (!ignoreEnd) {
- offset = this.endOffset;
- end = this.endContainer;
- if (end.nodeType == 3) {
- if (offset == 0) {
- this.setEndBefore(end);
- } else {
- offset < end.nodeValue.length && domUtils.split(end, offset);
- this.setEndAfter(end);
- }
- }
- }
- return this;
- },
-
- /**
- * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
- * @method txtToElmBoundary
- * @remind 该操作不会修改dom节点
- * @return { UE.dom.Range } 当前range对象
- */
-
- /**
- * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
- * ignoreCollapsed 的值决定是否执行该调整
- * @method txtToElmBoundary
- * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
- * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
- * @return { UE.dom.Range } 当前range对象
- */
- txtToElmBoundary:function (ignoreCollapsed) {
- function adjust(r, c) {
- var container = r[c + 'Container'],
- offset = r[c + 'Offset'];
- if (container.nodeType == 3) {
- if (!offset) {
- r['set' + c.replace(/(\w)/, function (a) {
- return a.toUpperCase();
- }) + 'Before'](container);
- } else if (offset >= container.nodeValue.length) {
- r['set' + c.replace(/(\w)/, function (a) {
- return a.toUpperCase();
- }) + 'After' ](container);
- }
- }
- }
-
- if (ignoreCollapsed || !this.collapsed) {
- adjust(this, 'start');
- adjust(this, 'end');
- }
- return this;
- },
-
- /**
- * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
- * @method insertNode
- * @param { Node } node 需要插入的节点
- * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
- * @return { UE.dom.Range } 当前range对象
- */
- insertNode:function (node) {
- var first = node, length = 1;
- if (node.nodeType == 11) {
- first = node.firstChild;
- length = node.childNodes.length;
- }
- this.trimBoundary(true);
- var start = this.startContainer,
- offset = this.startOffset;
- var nextNode = start.childNodes[ offset ];
- if (nextNode) {
- start.insertBefore(node, nextNode);
- } else {
- start.appendChild(node);
- }
- if (first.parentNode === this.endContainer) {
- this.endOffset = this.endOffset + length;
- }
- return this.setStartBefore(first);
- },
-
- /**
- * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
- * @method setCursor
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:collapse()
- */
-
- /**
- * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
- * @method setCursor
- * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
- * 反之,则向开始容器方向闭合
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:collapse(Boolean)
- */
- setCursor:function (toEnd, noFillData) {
- return this.collapse(!toEnd).select(noFillData);
- },
-
- /**
- * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
- * @method createBookmark
- * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
- * 返回标记位置的ID, 反之则返回标记位置节点的引用
- * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
- * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
- * 返回的记录的类型为ID, 反之则为引用
- */
- createBookmark:function (serialize, same) {
- var endNode,
- startNode = this.document.createElement('span');
- startNode.style.cssText = 'display:none;line-height:0px;';
- startNode.appendChild(this.document.createTextNode('\u200D'));
- startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
-
- if (!this.collapsed) {
- endNode = startNode.cloneNode(true);
- endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
- }
- this.insertNode(startNode);
- if (endNode) {
- this.collapse().insertNode(endNode).setEndBefore(endNode);
- }
- this.setStartAfter(startNode);
- return {
- start:serialize ? startNode.id : startNode,
- end:endNode ? serialize ? endNode.id : endNode : null,
- id:serialize
- }
- },
-
- /**
- * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
- * @method moveToBookmark
- * @param { BookMark } bookmark createBookmark所创建的标签对象
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:createBookmark(Boolean)
- */
- moveToBookmark:function (bookmark) {
- var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
- end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
- this.setStartBefore(start);
- domUtils.remove(start);
- if (end) {
- this.setEndBefore(end);
- domUtils.remove(end);
- } else {
- this.collapse(true);
- }
- return this;
- },
-
- /**
- * 调整range的边界,使其"放大"到最近的父节点
- * @method enlarge
- * @remind 会引起选区的变化
- * @return { UE.dom.Range } 当前range对象
- */
-
- /**
- * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
- * 要求扩大之后的父节点是block节点
- * @method enlarge
- * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
- * @return { UE.dom.Range } 当前range对象
- */
- enlarge:function (toBlock, stopFn) {
- var isBody = domUtils.isBody,
- pre, node, tmp = this.document.createTextNode('');
- if (toBlock) {
- node = this.startContainer;
- if (node.nodeType == 1) {
- if (node.childNodes[this.startOffset]) {
- pre = node = node.childNodes[this.startOffset]
- } else {
- node.appendChild(tmp);
- pre = node = tmp;
- }
- } else {
- pre = node;
- }
- while (1) {
- if (domUtils.isBlockElm(node)) {
- node = pre;
- while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
- node = pre;
- }
- this.setStartBefore(node);
- break;
- }
- pre = node;
- node = node.parentNode;
- }
- node = this.endContainer;
- if (node.nodeType == 1) {
- if (pre = node.childNodes[this.endOffset]) {
- node.insertBefore(tmp, pre);
- } else {
- node.appendChild(tmp);
- }
- pre = node = tmp;
- } else {
- pre = node;
- }
- while (1) {
- if (domUtils.isBlockElm(node)) {
- node = pre;
- while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
- node = pre;
- }
- this.setEndAfter(node);
- break;
- }
- pre = node;
- node = node.parentNode;
- }
- if (tmp.parentNode === this.endContainer) {
- this.endOffset--;
- }
- domUtils.remove(tmp);
- }
-
- // 扩展边界到最大
- if (!this.collapsed) {
- while (this.startOffset == 0) {
- if (stopFn && stopFn(this.startContainer)) {
- break;
- }
- if (isBody(this.startContainer)) {
- break;
- }
- this.setStartBefore(this.startContainer);
- }
- while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) {
- if (stopFn && stopFn(this.endContainer)) {
- break;
- }
- if (isBody(this.endContainer)) {
- break;
- }
- this.setEndAfter(this.endContainer);
- }
- }
- return this;
- },
- enlargeToBlockElm:function(ignoreEnd){
- while(!domUtils.isBlockElm(this.startContainer)){
- this.setStartBefore(this.startContainer);
- }
- if(!ignoreEnd){
- while(!domUtils.isBlockElm(this.endContainer)){
- this.setEndAfter(this.endContainer);
- }
- }
- return this;
- },
- /**
- * 调整Range的边界,使其"缩小"到最合适的位置
- * @method adjustmentBoundary
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:shrinkBoundary()
- */
- adjustmentBoundary:function () {
- if (!this.collapsed) {
- while (!domUtils.isBody(this.startContainer) &&
- this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
- this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
- ) {
-
- this.setStartAfter(this.startContainer);
- }
- while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
- this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
- ) {
- this.setEndBefore(this.endContainer);
- }
- }
- return this;
- },
-
- /**
- * 给range选区中的内容添加给定的inline标签
- * @method applyInlineStyle
- * @param { String } tagName 需要添加的标签名
- * @example
- * ```html
- * xxxx[xxxx]x
==> range.applyInlineStyle("strong") ==> xxxx[xxxx]x
- * ```
- */
-
- /**
- * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
- * @method applyInlineStyle
- * @param { String } tagName 需要添加的标签名
- * @param { Object } attrs 跟随新添加的标签的属性
- * @return { UE.dom.Range } 当前选区
- * @example
- * ```html
- * xxxx[xxxx]x
- *
- * ==>
- *
- *
- * range.applyInlineStyle("strong",{"style":"font-size:12px"})
- *
- * ==>
- *
- * xxxx[xxxx]x
- * ```
- */
- applyInlineStyle:function (tagName, attrs, list) {
- if (this.collapsed)return this;
- this.trimBoundary().enlarge(false,
- function (node) {
- return node.nodeType == 1 && domUtils.isBlockElm(node)
- }).adjustmentBoundary();
- var bookmark = this.createBookmark(),
- end = bookmark.end,
- filterFn = function (node) {
- return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
- },
- current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
- node,
- pre,
- range = this.cloneRange();
- while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
- if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
- range.setStartBefore(current);
- node = current;
- while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
- pre = node;
- node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
- return dtd[tagName][parent.tagName];
- });
- }
- var frag = range.setEndAfter(pre).extractContents(), elm;
- if (list && list.length > 0) {
- var level, top;
- top = level = list[0].cloneNode(false);
- for (var i = 1, ci; ci = list[i++];) {
- level.appendChild(ci.cloneNode(false));
- level = level.firstChild;
- }
- elm = level;
- } else {
- elm = range.document.createElement(tagName);
- }
- if (attrs) {
- domUtils.setAttributes(elm, attrs);
- }
- elm.appendChild(frag);
- range.insertNode(list ? top : elm);
- //处理下滑线在a上的情况
- var aNode;
- if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
- domUtils.setAttributes(aNode, attrs);
- domUtils.remove(elm, true);
- elm = aNode;
- } else {
- domUtils.mergeSibling(elm);
- domUtils.clearEmptySibling(elm);
- }
- //去除子节点相同的
- domUtils.mergeChild(elm, attrs);
- current = domUtils.getNextDomNode(elm, false, filterFn);
- domUtils.mergeToParent(elm);
- if (node === end) {
- break;
- }
- } else {
- current = domUtils.getNextDomNode(current, true, filterFn);
- }
- }
- return this.moveToBookmark(bookmark);
- },
-
- /**
- * 移除当前选区内指定的inline标签,但保留其中的内容
- * @method removeInlineStyle
- * @param { String } tagName 需要移除的标签名
- * @return { UE.dom.Range } 当前的range对象
- * @example
- * ```html
- * xx[xxxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[xxxxyyyzz]z
- * ```
- */
-
- /**
- * 移除当前选区内指定的一组inline标签,但保留其中的内容
- * @method removeInlineStyle
- * @param { Array } tagNameArr 需要移除的标签名的数组
- * @return { UE.dom.Range } 当前的range对象
- * @see UE.dom.Range:removeInlineStyle(String)
- */
- removeInlineStyle:function (tagNames) {
- if (this.collapsed)return this;
- tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
- this.shrinkBoundary().adjustmentBoundary();
- var start = this.startContainer, end = this.endContainer;
- while (1) {
- if (start.nodeType == 1) {
- if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
- break;
- }
- if (start.tagName.toLowerCase() == 'body') {
- start = null;
- break;
- }
- }
- start = start.parentNode;
- }
- while (1) {
- if (end.nodeType == 1) {
- if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
- break;
- }
- if (end.tagName.toLowerCase() == 'body') {
- end = null;
- break;
- }
- }
- end = end.parentNode;
- }
- var bookmark = this.createBookmark(),
- frag,
- tmpRange;
- if (start) {
- tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
- frag = tmpRange.extractContents();
- tmpRange.insertNode(frag);
- domUtils.clearEmptySibling(start, true);
- start.parentNode.insertBefore(bookmark.start, start);
- }
- if (end) {
- tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
- frag = tmpRange.extractContents();
- tmpRange.insertNode(frag);
- domUtils.clearEmptySibling(end, false, true);
- end.parentNode.insertBefore(bookmark.end, end.nextSibling);
- }
- var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
- return node.nodeType == 1;
- }), next;
- while (current && current !== bookmark.end) {
- next = domUtils.getNextDomNode(current, true, function (node) {
- return node.nodeType == 1;
- });
- if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
- domUtils.remove(current, true);
- }
- current = next;
- }
- return this.moveToBookmark(bookmark);
- },
-
- /**
- * 获取当前选中的自闭合的节点
- * @method getClosedNode
- * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
- */
- getClosedNode:function () {
- var node;
- if (!this.collapsed) {
- var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
- if (selectOneNode(range)) {
- var child = range.startContainer.childNodes[range.startOffset];
- if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
- node = child;
- }
- }
- }
- return node;
- },
-
- /**
- * 在页面上高亮range所表示的选区
- * @method select
- * @return { UE.dom.Range } 返回当前Range对象
- */
- //这里不区分ie9以上,trace:3824
- select:browser.ie ? function (noFillData, textRange) {
- var nativeRange;
- if (!this.collapsed)
- this.shrinkBoundary();
- var node = this.getClosedNode();
- if (node && !textRange) {
- try {
- nativeRange = this.document.body.createControlRange();
- nativeRange.addElement(node);
- nativeRange.select();
- } catch (e) {}
- return this;
- }
- var bookmark = this.createBookmark(),
- start = bookmark.start,
- end;
- nativeRange = this.document.body.createTextRange();
- nativeRange.moveToElementText(start);
- nativeRange.moveStart('character', 1);
- if (!this.collapsed) {
- var nativeRangeEnd = this.document.body.createTextRange();
- end = bookmark.end;
- nativeRangeEnd.moveToElementText(end);
- nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
- } else {
- if (!noFillData && this.startContainer.nodeType != 3) {
- //使用|x固定住光标
- var tmpText = this.document.createTextNode(fillChar),
- tmp = this.document.createElement('span');
- tmp.appendChild(this.document.createTextNode(fillChar));
- start.parentNode.insertBefore(tmp, start);
- start.parentNode.insertBefore(tmpText, start);
- //当点b,i,u时,不能清除i上边的b
- removeFillData(this.document, tmpText);
- fillData = tmpText;
- mergeSibling(tmp, 'previousSibling');
- mergeSibling(start, 'nextSibling');
- nativeRange.moveStart('character', -1);
- nativeRange.collapse(true);
- }
- }
- this.moveToBookmark(bookmark);
- tmp && domUtils.remove(tmp);
- //IE在隐藏状态下不支持range操作,catch一下
- try {
- nativeRange.select();
- } catch (e) {
- }
- return this;
- } : function (notInsertFillData) {
- function checkOffset(rng){
-
- function check(node,offset,dir){
- if(node.nodeType == 3 && node.nodeValue.length < offset){
- rng[dir + 'Offset'] = node.nodeValue.length
- }
- }
- check(rng.startContainer,rng.startOffset,'start');
- check(rng.endContainer,rng.endOffset,'end');
- }
- var win = domUtils.getWindow(this.document),
- sel = win.getSelection(),
- txtNode;
- //FF下关闭自动长高时滚动条在关闭dialog时会跳
- //ff下如果不body.focus将不能定位闭合光标到编辑器内
- browser.gecko ? this.document.body.focus() : win.focus();
- if (sel) {
- sel.removeAllRanges();
- // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
- // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
- if (this.collapsed && !notInsertFillData) {
-// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
-// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
-// var tmp = this.document.createTextNode('');
-// this.insertNode(tmp).setStart(tmp, 0).collapse(true);
-// }
-//
- //处理光标落在文本节点的情况
- //处理以下的情况
- //|xxxx
- //xxxx|xxxx
- //xxxx|
- var start = this.startContainer,child = start;
- if(start.nodeType == 1){
- child = start.childNodes[this.startOffset];
-
- }
- if( !(start.nodeType == 3 && this.startOffset) &&
- (child ?
- (!child.previousSibling || child.previousSibling.nodeType != 3)
- :
- (!start.lastChild || start.lastChild.nodeType != 3)
- )
- ){
- txtNode = this.document.createTextNode(fillChar);
- //跟着前边走
- this.insertNode(txtNode);
- removeFillData(this.document, txtNode);
- mergeSibling(txtNode, 'previousSibling');
- mergeSibling(txtNode, 'nextSibling');
- fillData = txtNode;
- this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
- }
- }
- var nativeRange = this.document.createRange();
- if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
- var child = this.startContainer.childNodes[this.startOffset];
- if(!child){
- //往前靠拢
- child = this.startContainer.lastChild;
- if( child && domUtils.isBr(child)){
- this.setStartBefore(child).collapse(true);
- }
- }else{
- //向后靠拢
- while(child && domUtils.isBlockElm(child)){
- if(child.nodeType == 1 && child.childNodes[0]){
- child = child.childNodes[0]
- }else{
- break;
- }
- }
- child && this.setStartBefore(child).collapse(true)
- }
-
- }
- //是createAddress最后一位算的不准,现在这里进行微调
- checkOffset(this);
- nativeRange.setStart(this.startContainer, this.startOffset);
- nativeRange.setEnd(this.endContainer, this.endOffset);
- sel.addRange(nativeRange);
- }
- return this;
- },
-
- /**
- * 滚动到当前range开始的位置
- * @method scrollToView
- * @param { Window } win 当前range对象所属的window对象
- * @return { UE.dom.Range } 当前Range对象
- */
-
- /**
- * 滚动到距离当前range开始位置 offset 的位置处
- * @method scrollToView
- * @param { Window } win 当前range对象所属的window对象
- * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
- * @return { UE.dom.Range } 当前Range对象
- */
- scrollToView:function (win, offset) {
- win = win ? window : domUtils.getWindow(this.document);
- var me = this,
- span = me.document.createElement('span');
- //trace:717
- span.innerHTML = ' ';
- me.cloneRange().insertNode(span);
- domUtils.scrollToView(span, win, offset);
- domUtils.remove(span);
- return me;
- },
-
- /**
- * 判断当前选区内容是否占位符
- * @private
- * @method inFillChar
- * @return { Boolean } 如果是占位符返回true,否则返回false
- */
- inFillChar : function(){
- var start = this.startContainer;
- if(this.collapsed && start.nodeType == 3
- && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
- ){
- return true;
- }
- return false;
- },
-
- /**
- * 保存
- * @method createAddress
- * @private
- * @return { Boolean } 返回开始和结束的位置
- * @example
- * ```html
- *
- *
- * aaaa
- *
- *
- * bbbb
- *
- *
- *
- *
- *
- *
- * ```
- */
- createAddress : function(ignoreEnd,ignoreTxt){
- var addr = {},me = this;
-
- function getAddress(isStart){
- var node = isStart ? me.startContainer : me.endContainer;
- var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
- addrs = [];
- for(var i = 0,ci;ci = parents[i++];){
- addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
- }
- var firstIndex = 0;
-
- if(ignoreTxt){
- if(node.nodeType == 3){
- var tmpNode = node.previousSibling;
- while(tmpNode && tmpNode.nodeType == 3){
- firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
- tmpNode = tmpNode.previousSibling;
- }
- firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
- }else{
- node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
- if(node){
- firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
- }else{
- node = isStart ? me.startContainer : me.endContainer;
- var first = node.firstChild;
- while(first){
- if(domUtils.isFillChar(first)){
- first = first.nextSibling;
- continue;
- }
- firstIndex++;
- if(first.nodeType == 3){
- while( first && first.nodeType == 3){
- first = first.nextSibling;
- }
- }else{
- first = first.nextSibling;
- }
- }
- }
- }
-
- }else{
- firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
- }
- if(firstIndex < 0){
- firstIndex = 0;
- }
- addrs.push(firstIndex);
- return addrs;
- }
- addr.startAddress = getAddress(true);
- if(!ignoreEnd){
- addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
- }
- return addr;
- },
-
- /**
- * 保存
- * @method createAddress
- * @private
- * @return { Boolean } 返回开始和结束的位置
- * @example
- * ```html
- *
- *
- * aaaa
- *
- *
- * bbbb
- *
- *
- *
- *
- *
- *
- * ```
- */
- moveToAddress : function(addr,ignoreEnd){
- var me = this;
- function getNode(address,isStart){
- var tmpNode = me.document.body,
- parentNode,offset;
- for(var i= 0,ci,l=address.length;i
- *
- *
- *
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 遍历range内的节点。
- * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
- * 作为其参数。
- * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
- * 发doFn函数的执行
- * @method traversal
- * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
- * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
- * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
- * 会触发doFn。
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:traversal(Function)
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * ```
- */
- traversal:function(doFn,filterFn){
- if (this.collapsed)
- return this;
- var bookmark = this.createBookmark(),
- end = bookmark.end,
- current = domUtils.getNextDomNode(bookmark.start, false, filterFn);
- while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
- var tmpNode = domUtils.getNextDomNode(current,false,filterFn);
- doFn(current);
- current = tmpNode;
- }
- return this.moveToBookmark(bookmark);
- }
- };
-})();
-
-// core/Selection.js
-/**
- * 选集
- * @file
- * @module UE.dom
- * @class Selection
- * @since 1.2.6.1
- */
-
-/**
- * 选区集合
- * @unfile
- * @module UE.dom
- * @class Selection
- */
-(function () {
-
- function getBoundaryInformation( range, start ) {
- var getIndex = domUtils.getNodeIndex;
- range = range.duplicate();
- range.collapse( start );
- var parent = range.parentElement();
- //如果节点里没有子节点,直接退出
- if ( !parent.hasChildNodes() ) {
- return {container:parent, offset:0};
- }
- var siblings = parent.children,
- child,
- testRange = range.duplicate(),
- startIndex = 0, endIndex = siblings.length - 1, index = -1,
- distance;
- while ( startIndex <= endIndex ) {
- index = Math.floor( (startIndex + endIndex) / 2 );
- child = siblings[index];
- testRange.moveToElementText( child );
- var position = testRange.compareEndPoints( 'StartToStart', range );
- if ( position > 0 ) {
- endIndex = index - 1;
- } else if ( position < 0 ) {
- startIndex = index + 1;
- } else {
- //trace:1043
- return {container:parent, offset:getIndex( child )};
- }
- }
- if ( index == -1 ) {
- testRange.moveToElementText( parent );
- testRange.setEndPoint( 'StartToStart', range );
- distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
- siblings = parent.childNodes;
- if ( !distance ) {
- child = siblings[siblings.length - 1];
- return {container:child, offset:child.nodeValue.length};
- }
-
- var i = siblings.length;
- while ( distance > 0 ){
- distance -= siblings[ --i ].nodeValue.length;
- }
- return {container:siblings[i], offset:-distance};
- }
- testRange.collapse( position > 0 );
- testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
- distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
- if ( !distance ) {
- return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
- {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
- {container:child, offset:position > 0 ? 0 : child.childNodes.length}
- }
- while ( distance > 0 ) {
- try {
- var pre = child;
- child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
- distance -= child.nodeValue.length;
- } catch ( e ) {
- return {container:parent, offset:getIndex( pre )};
- }
- }
- return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
- }
-
- /**
- * 将ieRange转换为Range对象
- * @param {Range} ieRange ieRange对象
- * @param {Range} range Range对象
- * @return {Range} range 返回转换后的Range对象
- */
- function transformIERangeToRange( ieRange, range ) {
- if ( ieRange.item ) {
- range.selectNode( ieRange.item( 0 ) );
- } else {
- var bi = getBoundaryInformation( ieRange, true );
- range.setStart( bi.container, bi.offset );
- if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
- bi = getBoundaryInformation( ieRange, false );
- range.setEnd( bi.container, bi.offset );
- }
- }
- return range;
- }
-
- /**
- * 获得ieRange
- * @param {Selection} sel Selection对象
- * @return {ieRange} 得到ieRange
- */
- function _getIERange( sel ) {
- var ieRange;
- //ie下有可能报错
- try {
- ieRange = sel.getNative().createRange();
- } catch ( e ) {
- return null;
- }
- var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
- if ( ( el.ownerDocument || el ) === sel.document ) {
- return ieRange;
- }
- return null;
- }
-
- var Selection = dom.Selection = function ( doc ) {
- var me = this, iframe;
- me.document = doc;
- if ( browser.ie9below ) {
- iframe = domUtils.getWindow( doc ).frameElement;
- domUtils.on( iframe, 'beforedeactivate', function () {
- me._bakIERange = me.getIERange();
- } );
- domUtils.on( iframe, 'activate', function () {
- try {
- if ( !_getIERange( me ) && me._bakIERange ) {
- me._bakIERange.select();
- }
- } catch ( ex ) {
- }
- me._bakIERange = null;
- } );
- }
- iframe = doc = null;
- };
-
- Selection.prototype = {
-
- rangeInBody : function(rng,txtRange){
- var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
-
- return node === this.document.body || domUtils.inDoc(node,this.document);
- },
-
- /**
- * 获取原生seleciton对象
- * @method getNative
- * @return { Object } 获得selection对象
- * @example
- * ```javascript
- * editor.selection.getNative();
- * ```
- */
- getNative:function () {
- var doc = this.document;
- try {
- return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow( doc ).getSelection();
- } catch ( e ) {
- return null;
- }
- },
-
- /**
- * 获得ieRange
- * @method getIERange
- * @return { Object } 返回ie原生的Range
- * @example
- * ```javascript
- * editor.selection.getIERange();
- * ```
- */
- getIERange:function () {
- var ieRange = _getIERange( this );
- if ( !ieRange ) {
- if ( this._bakIERange ) {
- return this._bakIERange;
- }
- }
- return ieRange;
- },
-
- /**
- * 缓存当前选区的range和选区的开始节点
- * @method cache
- */
- cache:function () {
- this.clear();
- this._cachedRange = this.getRange();
- this._cachedStartElement = this.getStart();
- this._cachedStartElementPath = this.getStartElementPath();
- },
-
- /**
- * 获取选区开始位置的父节点到body
- * @method getStartElementPath
- * @return { Array } 返回父节点集合
- * @example
- * ```javascript
- * editor.selection.getStartElementPath();
- * ```
- */
- getStartElementPath:function () {
- if ( this._cachedStartElementPath ) {
- return this._cachedStartElementPath;
- }
- var start = this.getStart();
- if ( start ) {
- return domUtils.findParents( start, true, null, true )
- }
- return [];
- },
-
- /**
- * 清空缓存
- * @method clear
- */
- clear:function () {
- this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
- },
-
- /**
- * 编辑器是否得到了选区
- * @method isFocus
- */
- isFocus:function () {
- try {
- if(browser.ie9below){
-
- var nativeRange = _getIERange(this);
- return !!(nativeRange && this.rangeInBody(nativeRange));
- }else{
- return !!this.getNative().rangeCount;
- }
- } catch ( e ) {
- return false;
- }
-
- },
-
- /**
- * 获取选区对应的Range
- * @method getRange
- * @return { Object } 得到Range对象
- * @example
- * ```javascript
- * editor.selection.getRange();
- * ```
- */
- getRange:function () {
- var me = this;
- function optimze( range ) {
- var child = me.document.body.firstChild,
- collapsed = range.collapsed;
- while ( child && child.firstChild ) {
- range.setStart( child, 0 );
- child = child.firstChild;
- }
- if ( !range.startContainer ) {
- range.setStart( me.document.body, 0 )
- }
- if ( collapsed ) {
- range.collapse( true );
- }
- }
-
- if ( me._cachedRange != null ) {
- return this._cachedRange;
- }
- var range = new baidu.editor.dom.Range( me.document );
-
- if ( browser.ie9below ) {
- var nativeRange = me.getIERange();
- if ( nativeRange ) {
- //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
- try{
- transformIERangeToRange( nativeRange, range );
- }catch(e){
- optimze( range );
- }
-
- } else {
- optimze( range );
- }
- } else {
- var sel = me.getNative();
- if ( sel && sel.rangeCount ) {
- var firstRange = sel.getRangeAt( 0 );
- var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
- range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
- if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
- optimze( range );
- }
- } else {
- //trace:1734 有可能已经不在dom树上了,标识的节点
- if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){
- return this._bakRange;
- }
- optimze( range );
- }
- }
- return this._bakRange = range;
- },
-
- /**
- * 获取开始元素,用于状态反射
- * @method getStart
- * @return { Element } 获得开始元素
- * @example
- * ```javascript
- * editor.selection.getStart();
- * ```
- */
- getStart:function () {
- if ( this._cachedStartElement ) {
- return this._cachedStartElement;
- }
- var range = browser.ie9below ? this.getIERange() : this.getRange(),
- tmpRange,
- start, tmp, parent;
- if ( browser.ie9below ) {
- if ( !range ) {
- //todo 给第一个值可能会有问题
- return this.document.body.firstChild;
- }
- //control元素
- if ( range.item ){
- return range.item( 0 );
- }
- tmpRange = range.duplicate();
- //修正ie下x[xx] 闭合后 x|xx
- tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
- tmpRange.collapse( 1 );
- start = tmpRange.parentElement();
- parent = tmp = range.parentElement();
- while ( tmp = tmp.parentNode ) {
- if ( tmp == start ) {
- start = parent;
- break;
- }
- }
- } else {
- range.shrinkBoundary();
- start = range.startContainer;
- if ( start.nodeType == 1 && start.hasChildNodes() ){
- start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
- }
- if ( start.nodeType == 3 ){
- return start.parentNode;
- }
- }
- return start;
- },
-
- /**
- * 得到选区中的文本
- * @method getText
- * @return { String } 选区中包含的文本
- * @example
- * ```javascript
- * editor.selection.getText();
- * ```
- */
- getText:function () {
- var nativeSel, nativeRange;
- if ( this.isFocus() && (nativeSel = this.getNative()) ) {
- nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
- return browser.ie9below ? nativeRange.text : nativeRange.toString();
- }
- return '';
- },
-
- /**
- * 清除选区
- * @method clearRange
- * @example
- * ```javascript
- * editor.selection.clearRange();
- * ```
- */
- clearRange : function(){
- this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
- }
- };
-})();
-
-// core/Editor.js
-/**
- * 编辑器主类,包含编辑器提供的大部分公用接口
- * @file
- * @module UE
- * @class Editor
- * @since 1.2.6.1
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @unfile
- * @module UE
- */
-
-/**
- * UEditor的核心类,为用户提供与编辑器交互的接口。
- * @unfile
- * @module UE
- * @class Editor
- */
-
-(function () {
- var uid = 0, _selectionChangeTimer;
-
- /**
- * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
- * @private
- * @method setValue
- * @param { UE.Editor } editor 编辑器事例
- */
- function setValue(form, editor) {
- var textarea;
- if (editor.textarea) {
- if (utils.isString(editor.textarea)) {
- for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
- if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
- textarea = ti;
- break;
- }
- }
- } else {
- textarea = editor.textarea;
- }
- }
- if (!textarea) {
- form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
- 'name': editor.options.textarea,
- 'id': 'ueditor_textarea_' + editor.options.textarea,
- 'style': "display:none"
- }));
- //不要产生多个textarea
- editor.textarea = textarea;
- }
- !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea );
- textarea.value = editor.hasContents() ?
- (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
- ''
- }
- function loadPlugins(me){
- //初始化插件
- for (var pi in UE.plugins) {
- UE.plugins[pi].call(me);
- }
-
- }
- function checkCurLang(I18N){
- for(var lang in I18N){
- return lang
- }
- }
-
- function langReadied(me){
- me.langIsReady = true;
-
- me.fireEvent("langReady");
- }
-
- /**
- * 编辑器准备就绪后会触发该事件
- * @module UE
- * @class Editor
- * @event ready
- * @remind render方法执行完成之后,会触发该事件
- * @remind
- * @example
- * ```javascript
- * editor.addListener( 'ready', function( editor ) {
- * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
- * } );
- * ```
- */
- /**
- * 执行destroy方法,会触发该事件
- * @module UE
- * @class Editor
- * @event destroy
- * @see UE.Editor:destroy()
- */
- /**
- * 执行reset方法,会触发该事件
- * @module UE
- * @class Editor
- * @event reset
- * @see UE.Editor:reset()
- */
- /**
- * 执行focus方法,会触发该事件
- * @module UE
- * @class Editor
- * @event focus
- * @see UE.Editor:focus(Boolean)
- */
- /**
- * 语言加载完成会触发该事件
- * @module UE
- * @class Editor
- * @event langReady
- */
- /**
- * 运行命令之后会触发该命令
- * @module UE
- * @class Editor
- * @event beforeExecCommand
- */
- /**
- * 运行命令之后会触发该命令
- * @module UE
- * @class Editor
- * @event afterExecCommand
- */
- /**
- * 运行命令之前会触发该命令
- * @module UE
- * @class Editor
- * @event firstBeforeExecCommand
- */
- /**
- * 在getContent方法执行之前会触发该事件
- * @module UE
- * @class Editor
- * @event beforeGetContent
- * @see UE.Editor:getContent()
- */
- /**
- * 在getContent方法执行之后会触发该事件
- * @module UE
- * @class Editor
- * @event afterGetContent
- * @see UE.Editor:getContent()
- */
- /**
- * 在getAllHtml方法执行时会触发该事件
- * @module UE
- * @class Editor
- * @event getAllHtml
- * @see UE.Editor:getAllHtml()
- */
- /**
- * 在setContent方法执行之前会触发该事件
- * @module UE
- * @class Editor
- * @event beforeSetContent
- * @see UE.Editor:setContent(String)
- */
- /**
- * 在setContent方法执行之后会触发该事件
- * @module UE
- * @class Editor
- * @event afterSetContent
- * @see UE.Editor:setContent(String)
- */
- /**
- * 每当编辑器内部选区发生改变时,将触发该事件
- * @event selectionchange
- * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
- * @example
- * ```javascript
- * editor.addListener( 'selectionchange', function( editor ) {
- * console.log('选区发生改变');
- * }
- */
- /**
- * 在所有selectionchange的监听函数执行之前,会触发该事件
- * @module UE
- * @class Editor
- * @event beforeSelectionChange
- * @see UE.Editor:selectionchange
- */
- /**
- * 在所有selectionchange的监听函数执行完之后,会触发该事件
- * @module UE
- * @class Editor
- * @event afterSelectionChange
- * @see UE.Editor:selectionchange
- */
- /**
- * 编辑器内容发生改变时会触发该事件
- * @module UE
- * @class Editor
- * @event contentChange
- */
-
-
- /**
- * 以默认参数构建一个编辑器实例
- * @constructor
- * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
- * @example
- * ```javascript
- * var editor = new UE.Editor();
- * editor.execCommand('blod');
- * ```
- * @see UE.Config
- */
-
- /**
- * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
- * @constructor
- * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
- * @param { Object } setting 创建编辑器的参数
- * @example
- * ```javascript
- * var editor = new UE.Editor();
- * editor.execCommand('blod');
- * ```
- * @see UE.Config
- */
- var Editor = UE.Editor = function (options) {
- var me = this;
- me.uid = uid++;
- EventBase.call(me);
- me.commands = {};
- me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true);
- me.shortcutkeys = {};
- me.inputRules = [];
- me.outputRules = [];
- //设置默认的常用属性
- me.setOpt(Editor.defaultOptions(me));
-
- /* 尝试异步加载后台配置 */
- me.loadServerConfig();
-
- if(!utils.isEmptyObject(UE.I18N)){
- //修改默认的语言类型
- me.options.lang = checkCurLang(UE.I18N);
- UE.plugin.load(me);
- langReadied(me);
-
- }else{
- utils.loadFile(document, {
- src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
- tag: "script",
- type: "text/javascript",
- defer: "defer"
- }, function () {
- UE.plugin.load(me);
- langReadied(me);
- });
- }
-
- UE.instants['ueditorInstant' + me.uid] = me;
- };
- Editor.prototype = {
- registerCommand : function(name,obj){
- this.commands[name] = obj;
- },
- /**
- * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
- * @method ready
- * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
- * 立即触发该回调。
- * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
- * @example
- * ```javascript
- * editor.ready( function( editor ) {
- * editor.setContent('初始化完毕');
- * } );
- * ```
- * @see UE.Editor.event:ready
- */
- ready: function (fn) {
- var me = this;
- if (fn) {
- me.isReady ? fn.apply(me) : me.addListener('ready', fn);
- }
- },
-
- /**
- * 该方法是提供给插件里面使用,设置配置项默认值
- * @method setOpt
- * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
- * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
- * @param { String } key 编辑器的可接受的选项名称
- * @param { * } val 该选项可接受的值
- * @example
- * ```javascript
- * editor.setOpt( 'initContent', '欢迎使用编辑器' );
- * ```
- */
-
- /**
- * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
- * @method setOpt
- * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
- * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
- * @param { Object } options 将要设置的选项的键值对对象
- * @example
- * ```javascript
- * editor.setOpt( {
- * 'initContent': '欢迎使用编辑器'
- * } );
- * ```
- */
- setOpt: function (key, val) {
- var obj = {};
- if (utils.isString(key)) {
- obj[key] = val
- } else {
- obj = key;
- }
- utils.extend(this.options, obj, true);
- },
- getOpt:function(key){
- return this.options[key]
- },
- /**
- * 销毁编辑器实例,使用textarea代替
- * @method destroy
- * @example
- * ```javascript
- * editor.destroy();
- * ```
- */
- destroy: function () {
-
- var me = this;
- me.fireEvent('destroy');
- var container = me.container.parentNode;
- var textarea = me.textarea;
- if (!textarea) {
- textarea = document.createElement('textarea');
- container.parentNode.insertBefore(textarea, container);
- } else {
- textarea.style.display = ''
- }
-
- textarea.style.width = me.iframe.offsetWidth + 'px';
- textarea.style.height = me.iframe.offsetHeight + 'px';
- textarea.value = me.getContent();
- textarea.id = me.key;
- container.innerHTML = '';
- domUtils.remove(container);
- var key = me.key;
- //trace:2004
- for (var p in me) {
- if (me.hasOwnProperty(p)) {
- delete this[p];
- }
- }
- UE.delEditor(key);
- },
-
- /**
- * 渲染编辑器的DOM到指定容器
- * @method render
- * @param { String } containerId 指定一个容器ID
- * @remind 执行该方法,会触发ready事件
- * @warning 必须且只能调用一次
- */
-
- /**
- * 渲染编辑器的DOM到指定容器
- * @method render
- * @param { Element } containerDom 直接指定容器对象
- * @remind 执行该方法,会触发ready事件
- * @warning 必须且只能调用一次
- */
- render: function (container) {
- var me = this,
- options = me.options,
- getStyleValue=function(attr){
- return parseInt(domUtils.getComputedStyle(container,attr));
- };
- if (utils.isString(container)) {
- container = document.getElementById(container);
- }
- if (container) {
- if(options.initialFrameWidth){
- options.minFrameWidth = options.initialFrameWidth
- }else{
- options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
- }
- if(options.initialFrameHeight){
- options.minFrameHeight = options.initialFrameHeight
- }else{
- options.initialFrameHeight = options.minFrameHeight = container.offsetHeight;
- }
-
- container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth-
- getStyleValue("padding-left")- getStyleValue("padding-right") +'px';
- container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight -
- getStyleValue("padding-top")- getStyleValue("padding-bottom") +'px';
-
- container.style.zIndex = options.zIndex;
-
- var html = ( ie && browser.version < 9 ? '' : '') +
- '' +
- '' +
- ( options.iframeCssUrl ? '' : '' ) +
- (options.initialStyle ? '' : '') +
- '' +
- '';
- container.appendChild(domUtils.createElement(document, 'iframe', {
- id: 'ueditor_' + me.uid,
- width: "100%",
- height: "100%",
- frameborder: "0",
- //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
-// scrolling :'no',
- src: 'javascript:void(function(){document.open();' + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : '') +
- 'document.write("' + html + '");document.close();}())'
- }));
- container.style.overflow = 'hidden';
- //解决如果是给定的百分比,会导致高度算不对的问题
- setTimeout(function(){
- if( /%$/.test(options.initialFrameWidth)){
- options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
- //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
-// container.style.width = options.initialFrameWidth + 'px';
- }
- if(/%$/.test(options.initialFrameHeight)){
- options.minFrameHeight = options.initialFrameHeight = container.offsetHeight;
- container.style.height = options.initialFrameHeight + 'px';
- }
- })
- }
- },
-
- /**
- * 编辑器初始化
- * @method _setup
- * @private
- * @param { Element } doc 编辑器Iframe中的文档对象
- */
- _setup: function (doc) {
-
- var me = this,
- options = me.options;
- if (ie) {
- doc.body.disabled = true;
- doc.body.contentEditable = true;
- doc.body.disabled = false;
- } else {
- doc.body.contentEditable = true;
- }
- doc.body.spellcheck = false;
- me.document = doc;
- me.window = doc.defaultView || doc.parentWindow;
- me.iframe = me.window.frameElement;
- me.body = doc.body;
- me.selection = new dom.Selection(doc);
- //gecko初始化就能得到range,无法判断isFocus了
- var geckoSel;
- if (browser.gecko && (geckoSel = this.selection.getNative())) {
- geckoSel.removeAllRanges();
- }
- this._initEvents();
- //为form提交提供一个隐藏的textarea
- for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) {
- if (form.tagName == 'FORM') {
- me.form = form;
- if(me.options.autoSyncData){
- domUtils.on(me.window,'blur',function(){
- setValue(form,me);
- });
- }else{
- domUtils.on(form, 'submit', function () {
- setValue(this, me);
- });
- }
- break;
- }
- }
- if (options.initialContent) {
- if (options.autoClearinitialContent) {
- var oldExecCommand = me.execCommand;
- me.execCommand = function () {
- me.fireEvent('firstBeforeExecCommand');
- return oldExecCommand.apply(me, arguments);
- };
- this._setDefaultContent(options.initialContent);
- } else
- this.setContent(options.initialContent, false, true);
- }
-
- //编辑器不能为空内容
-
- if (domUtils.isEmptyNode(me.body)) {
- me.body.innerHTML = '' + (browser.ie ? '' : '
') + '
';
- }
- //如果要求focus, 就把光标定位到内容开始
- if (options.focus) {
- setTimeout(function () {
- me.focus(me.options.focusInEnd);
- //如果自动清除开着,就不需要做selectionchange;
- !me.options.autoClearinitialContent && me._selectionChange();
- }, 0);
- }
- if (!me.container) {
- me.container = this.iframe.parentNode;
- }
- if (options.fullscreen && me.ui) {
- me.ui.setFullScreen(true);
- }
-
- try {
- me.document.execCommand('2D-position', false, false);
- } catch (e) {
- }
- try {
- me.document.execCommand('enableInlineTableEditing', false, false);
- } catch (e) {
- }
- try {
- me.document.execCommand('enableObjectResizing', false, false);
- } catch (e) {
- }
-
- //挂接快捷键
- me._bindshortcutKeys();
- me.isReady = 1;
- me.fireEvent('ready');
- options.onready && options.onready.call(me);
- if (!browser.ie9below) {
- domUtils.on(me.window, ['blur', 'focus'], function (e) {
- //chrome下会出现alt+tab切换时,导致选区位置不对
- if (e.type == 'blur') {
- me._bakRange = me.selection.getRange();
- try {
- me._bakNativeRange = me.selection.getNative().getRangeAt(0);
- me.selection.getNative().removeAllRanges();
- } catch (e) {
- me._bakNativeRange = null;
- }
-
- } else {
- try {
- me._bakRange && me._bakRange.select();
- } catch (e) {
- }
- }
- });
- }
- //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
- if (browser.gecko && browser.version <= 10902) {
- //修复ff3.6初始化进来,不能点击获得焦点
- me.body.contentEditable = false;
- setTimeout(function () {
- me.body.contentEditable = true;
- }, 100);
- setInterval(function () {
- me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
- }, 100)
- }
-
- !options.isShow && me.setHide();
- options.readonly && me.setDisabled();
- },
-
- /**
- * 同步数据到编辑器所在的form
- * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
- * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
- * @method sync
- * @example
- * ```javascript
- * editor.sync();
- * form.sumbit(); //form变量已经指向了form元素
- * ```
- */
-
- /**
- * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
- * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
- * @method sync
- * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
- */
- sync: function (formId) {
- var me = this,
- form = formId ? document.getElementById(formId) :
- domUtils.findParent(me.iframe.parentNode, function (node) {
- return node.tagName == 'FORM'
- }, true);
- form && setValue(form, me);
- },
-
- /**
- * 设置编辑器高度
- * @method setHeight
- * @remind 当配置项autoHeightEnabled为真时,该方法无效
- * @param { Number } number 设置的高度值,纯数值,不带单位
- * @example
- * ```javascript
- * editor.setHeight(number);
- * ```
- */
- setHeight: function (height,notSetHeight) {
- if (height !== parseInt(this.iframe.parentNode.style.height)) {
- this.iframe.parentNode.style.height = height + 'px';
- }
- !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height);
- this.body.style.height = height + 'px';
- !notSetHeight && this.trigger('setHeight')
- },
-
- /**
- * 为编辑器的编辑命令提供快捷键
- * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
- * @method addshortcutkey
- * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
- * @example
- * ```javascript
- * editor.addshortcutkey({
- * "Bold" : "ctrl+66",//^B
- * "Italic" : "ctrl+73", //^I
- * });
- * ```
- */
- /**
- * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
- * @method addshortcutkey
- * @param { String } cmd 触发快捷键时,响应的命令
- * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
- * @example
- * ```javascript
- * editor.addshortcutkey("Underline", "ctrl+85"); //^U
- * ```
- */
- addshortcutkey: function (cmd, keys) {
- var obj = {};
- if (keys) {
- obj[cmd] = keys
- } else {
- obj = cmd;
- }
- utils.extend(this.shortcutkeys, obj)
- },
-
- /**
- * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
- * @method _bindshortcutKeys
- * @private
- */
- _bindshortcutKeys: function () {
- var me = this, shortcutkeys = this.shortcutkeys;
- me.addListener('keydown', function (type, e) {
- var keyCode = e.keyCode || e.which;
- for (var i in shortcutkeys) {
- var tmp = shortcutkeys[i].split(',');
- for (var t = 0, ti; ti = tmp[t++];) {
- ti = ti.split(':');
- var key = ti[0], param = ti[1];
- if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
- if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
- && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
- && keyCode == RegExp.$3
- ) ||
- keyCode == RegExp.$1
- ) {
- if (me.queryCommandState(i,param) != -1)
- me.execCommand(i, param);
- domUtils.preventDefault(e);
- }
- }
- }
-
- }
- });
- },
-
- /**
- * 获取编辑器的内容
- * @method getContent
- * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
- * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”<p><br/></p>“), 则返回空字符串
- * @example
- * ```javascript
- * //编辑器html内容:123456
- * var content = editor.getContent(); //返回值:123456
- * ```
- */
-
- /**
- * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
- * @method getContent
- * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
- * 代表当前编辑器的内容是否空,
- * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
- * 经过内置过滤规则处理后的内容。
- * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
- * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
- * @return { String } 编辑器的内容字符串
- * @example
- * ```javascript
- * // editor 是一个编辑器的实例
- * var content = editor.getContent( function ( editor ) {
- * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
- * } );
- * ```
- */
- getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
- var me = this;
- if (cmd && utils.isFunction(cmd)) {
- fn = cmd;
- cmd = '';
- }
- if (fn ? !fn() : !this.hasContents()) {
- return '';
- }
- me.fireEvent('beforegetcontent');
- var root = UE.htmlparser(me.body.innerHTML,ignoreBlank);
- me.filterOutputRule(root);
- me.fireEvent('aftergetcontent', cmd,root);
- return root.toHtml(formatter);
- },
-
- /**
- * 取得完整的html代码,可以直接显示成完整的html文档
- * @method getAllHtml
- * @return { String } 编辑器的内容html文档字符串
- * @eaxmple
- * ```javascript
- * editor.getAllHtml(); //返回格式大致是: ......
- * ```
- */
- getAllHtml: function () {
- var me = this,
- headHtml = [],
- html = '';
- me.fireEvent('getAllHtml', headHtml);
- if (browser.ie && browser.version > 8) {
- var headHtmlForIE9 = '';
- utils.each(me.document.styleSheets, function (si) {
- headHtmlForIE9 += ( si.href ? '' : '');
- });
- utils.each(me.document.getElementsByTagName('script'), function (si) {
- headHtmlForIE9 += si.outerHTML;
- });
-
- }
- return '' + (me.options.charset ? '' : '')
- + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + ''
- + '' + me.getContent(null, null, true) + '';
- },
-
- /**
- * 得到编辑器的纯文本内容,但会保留段落格式
- * @method getPlainTxt
- * @return { String } 编辑器带段落格式的纯文本内容字符串
- * @example
- * ```javascript
- * //编辑器html内容:1
2
- * console.log(editor.getPlainTxt()); //输出:"1\n2\n
- * ```
- */
- getPlainTxt: function () {
- var reg = new RegExp(domUtils.fillChar, 'g'),
- html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
- html = html.replace(/<(p|div)[^>]*>(
| )<\/\1>/gi, '\n')
- .replace(/
/gi, '\n')
- .replace(/<[^>/]+>/g, '')
- .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
- return dtd.$block[c] ? '\n' : b ? b : '';
- });
- //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
- return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/ /g, ' ');
- },
-
- /**
- * 获取编辑器中的纯文本内容,没有段落格式
- * @method getContentTxt
- * @return { String } 编辑器不带段落格式的纯文本内容字符串
- * @example
- * ```javascript
- * //编辑器html内容:1
2
- * console.log(editor.getPlainTxt()); //输出:"12
- * ```
- */
- getContentTxt: function () {
- var reg = new RegExp(domUtils.fillChar, 'g');
- //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
- return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
- },
-
- /**
- * 设置编辑器的内容,可修改编辑器当前的html内容
- * @method setContent
- * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
- * @warning 该方法会触发selectionchange事件
- * @param { String } html 要插入的html内容
- * @example
- * ```javascript
- * editor.getContent('test
');
- * ```
- */
-
- /**
- * 设置编辑器的内容,可修改编辑器当前的html内容
- * @method setContent
- * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
- * @warning 该方法会触发selectionchange事件
- * @param { String } html 要插入的html内容
- * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
- * @example
- * ```javascript
- * //假设设置前的编辑器内容是 old text
- * editor.setContent('new text
', true); //插入的结果是old text
new text
- * ```
- */
- setContent: function (html, isAppendTo, notFireSelectionchange) {
- var me = this;
-
- me.fireEvent('beforesetcontent', html);
- var root = UE.htmlparser(html);
- me.filterInputRule(root);
- html = root.toHtml();
-
- me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
-
-
- function isCdataDiv(node){
- return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
- }
- //给文本或者inline节点套p标签
- if (me.options.enterTag == 'p') {
-
- var child = this.body.firstChild, tmpNode;
- if (!child || child.nodeType == 1 &&
- (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
- domUtils.isCustomeNode(child)
- )
- && child === this.body.lastChild) {
- this.body.innerHTML = '' + (browser.ie ? ' ' : '
') + '
' + this.body.innerHTML;
-
- } else {
- var p = me.document.createElement('p');
- while (child) {
- while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
- tmpNode = child.nextSibling;
- p.appendChild(child);
- child = tmpNode;
- }
- if (p.firstChild) {
- if (!child) {
- me.body.appendChild(p);
- break;
- } else {
- child.parentNode.insertBefore(p, child);
- p = me.document.createElement('p');
- }
- }
- child = child.nextSibling;
- }
- }
- }
- me.fireEvent('aftersetcontent');
- me.fireEvent('contentchange');
-
- !notFireSelectionchange && me._selectionChange();
- //清除保存的选区
- me._bakRange = me._bakIERange = me._bakNativeRange = null;
- //trace:1742 setContent后gecko能得到焦点问题
- var geckoSel;
- if (browser.gecko && (geckoSel = this.selection.getNative())) {
- geckoSel.removeAllRanges();
- }
- if(me.options.autoSyncData){
- me.form && setValue(me.form,me);
- }
- },
-
- /**
- * 让编辑器获得焦点,默认focus到编辑器头部
- * @method focus
- * @example
- * ```javascript
- * editor.focus()
- * ```
- */
-
- /**
- * 让编辑器获得焦点,toEnd确定focus位置
- * @method focus
- * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
- * @example
- * ```javascript
- * editor.focus(true)
- * ```
- */
- focus: function (toEnd) {
- try {
- var me = this,
- rng = me.selection.getRange();
- if (toEnd) {
- var node = me.body.lastChild;
- if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
- if(domUtils.isEmptyBlock(node)){
- rng.setStartAtFirst(node)
- }else{
- rng.setStartAtLast(node)
- }
- rng.collapse(true);
- }
- rng.setCursor(true);
- } else {
- if(!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0){
-
- var node = me.body.firstChild;
- if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
- rng.setStartAtFirst(node).collapse(true);
- }
- }
-
- rng.select(true);
-
- }
- this.fireEvent('focus selectionchange');
- } catch (e) {
- }
-
- },
- isFocus:function(){
- return this.selection.isFocus();
- },
- blur:function(){
- var sel = this.selection.getNative();
- if(sel.empty && browser.ie){
- var nativeRng = document.body.createTextRange();
- nativeRng.moveToElementText(document.body);
- nativeRng.collapse(true);
- nativeRng.select();
- sel.empty()
- }else{
- sel.removeAllRanges()
- }
-
- //this.fireEvent('blur selectionchange');
- },
- /**
- * 初始化UE事件及部分事件代理
- * @method _initEvents
- * @private
- */
- _initEvents: function () {
- var me = this,
- doc = me.document,
- win = me.window;
- me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
- domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me._proxyDomEvent);
- domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent);
- domUtils.on(me.body,'drop',function(e){
- //阻止ff下默认的弹出新页面打开图片
- if(browser.gecko && e.stopPropagation) { e.stopPropagation(); }
- me.fireEvent('contentchange')
- });
- domUtils.on(doc, ['mouseup', 'keydown'], function (evt) {
- //特殊键不触发selectionchange
- if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
- return;
- }
- if (evt.button == 2)return;
- me._selectionChange(250, evt);
- });
- },
- /**
- * 触发事件代理
- * @method _proxyDomEvent
- * @private
- * @return { * } fireEvent的返回值
- * @see UE.EventBase:fireEvent(String)
- */
- _proxyDomEvent: function (evt) {
- if(this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false){
- return false;
- }
- if(this.fireEvent(evt.type.replace(/^on/, ''), evt) === false){
- return false;
- }
- return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase())
- },
- /**
- * 变化选区
- * @method _selectionChange
- * @private
- */
- _selectionChange: function (delay, evt) {
- var me = this;
- //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
-// if ( !me.selection.isFocus() ){
-// return;
-// }
-
-
- var hackForMouseUp = false;
- var mouseX, mouseY;
- if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
- var range = this.selection.getRange();
- if (!range.collapsed) {
- hackForMouseUp = true;
- mouseX = evt.clientX;
- mouseY = evt.clientY;
- }
- }
- clearTimeout(_selectionChangeTimer);
- _selectionChangeTimer = setTimeout(function () {
- if (!me.selection || !me.selection.getNative()) {
- return;
- }
- //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
- //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
- var ieRange;
- if (hackForMouseUp && me.selection.getNative().type == 'None') {
- ieRange = me.document.body.createTextRange();
- try {
- ieRange.moveToPoint(mouseX, mouseY);
- } catch (ex) {
- ieRange = null;
- }
- }
- var bakGetIERange;
- if (ieRange) {
- bakGetIERange = me.selection.getIERange;
- me.selection.getIERange = function () {
- return ieRange;
- };
- }
- me.selection.cache();
- if (bakGetIERange) {
- me.selection.getIERange = bakGetIERange;
- }
- if (me.selection._cachedRange && me.selection._cachedStartElement) {
- me.fireEvent('beforeselectionchange');
- // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
- me.fireEvent('selectionchange', !!evt);
- me.fireEvent('afterselectionchange');
- me.selection.clear();
- }
- }, delay || 50);
- },
-
- /**
- * 执行编辑命令
- * @method _callCmdFn
- * @private
- * @param { String } fnName 函数名称
- * @param { * } args 传给命令函数的参数
- * @return { * } 返回命令函数运行的返回值
- */
- _callCmdFn: function (fnName, args) {
- var cmdName = args[0].toLowerCase(),
- cmd, cmdFn;
- cmd = this.commands[cmdName] || UE.commands[cmdName];
- cmdFn = cmd && cmd[fnName];
- //没有querycommandstate或者没有command的都默认返回0
- if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
- return 0;
- } else if (cmdFn) {
- return cmdFn.apply(this, args);
- }
- },
-
- /**
- * 执行编辑命令cmdName,完成富文本编辑效果
- * @method execCommand
- * @param { String } cmdName 需要执行的命令
- * @remind 具体命令的使用请参考命令列表
- * @return { * } 返回命令函数运行的返回值
- * @example
- * ```javascript
- * editor.execCommand(cmdName);
- * ```
- */
- execCommand: function (cmdName) {
- cmdName = cmdName.toLowerCase();
- var me = this,
- result,
- cmd = me.commands[cmdName] || UE.commands[cmdName];
- if (!cmd || !cmd.execCommand) {
- return null;
- }
- if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
- me.__hasEnterExecCommand = true;
- if (me.queryCommandState.apply(me,arguments) != -1) {
- me.fireEvent('saveScene');
- me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments));
- result = this._callCmdFn('execCommand', arguments);
- //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
-// (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
- me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments));
- me.fireEvent('saveScene');
- }
- me.__hasEnterExecCommand = false;
- } else {
- result = this._callCmdFn('execCommand', arguments);
- (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
- }
- (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
- return result;
- },
-
- /**
- * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
- * @method queryCommandState
- * @param { String } cmdName 需要查询的命令名称
- * @remind 具体命令的使用请参考命令列表
- * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
- * @example
- * ```javascript
- * editor.queryCommandState(cmdName) => (-1|0|1)
- * ```
- * @see COMMAND.LIST
- */
- queryCommandState: function (cmdName) {
- return this._callCmdFn('queryCommandState', arguments);
- },
-
- /**
- * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
- * @method queryCommandValue
- * @param { String } cmdName 需要查询的命令名称
- * @remind 具体命令的使用请参考命令列表
- * @remind 只有部分插件有此方法
- * @return { * } 返回每个命令特定的当前状态值
- * @grammar editor.queryCommandValue(cmdName) => {*}
- * @see COMMAND.LIST
- */
- queryCommandValue: function (cmdName) {
- return this._callCmdFn('queryCommandValue', arguments);
- },
-
- /**
- * 检查编辑区域中是否有内容
- * @method hasContents
- * @remind 默认有文本内容,或者有以下节点都不认为是空
- * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
- * @return { Boolean } 检查有内容返回true,否则返回false
- * @example
- * ```javascript
- * editor.hasContents()
- * ```
- */
-
- /**
- * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
- * @method hasContents
- * @param { Array } tags 传入数组判断时用到的节点类型
- * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
- * @example
- * ```javascript
- * editor.hasContents(['span']);
- * ```
- */
- hasContents: function (tags) {
- if (tags) {
- for (var i = 0, ci; ci = tags[i++];) {
- if (this.document.getElementsByTagName(ci).length > 0) {
- return true;
- }
- }
- }
- if (!domUtils.isEmptyBlock(this.body)) {
- return true
- }
- //随时添加,定义的特殊标签如果存在,不能认为是空
- tags = ['div'];
- for (i = 0; ci = tags[i++];) {
- var nodes = domUtils.getElementsByTagName(this.document, ci);
- for (var n = 0, cn; cn = nodes[n++];) {
- if (domUtils.isCustomeNode(cn)) {
- return true;
- }
- }
- }
- return false;
- },
-
- /**
- * 重置编辑器,可用来做多个tab使用同一个编辑器实例
- * @method reset
- * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
- * @example
- * ```javascript
- * editor.reset()
- * ```
- */
- reset: function () {
- this.fireEvent('reset');
- },
-
- /**
- * 设置当前编辑区域可以编辑
- * @method setEnabled
- * @example
- * ```javascript
- * editor.setEnabled()
- * ```
- */
- setEnabled: function () {
- var me = this, range;
- if (me.body.contentEditable == 'false') {
- me.body.contentEditable = true;
- range = me.selection.getRange();
- //有可能内容丢失了
- try {
- range.moveToBookmark(me.lastBk);
- delete me.lastBk
- } catch (e) {
- range.setStartAtFirst(me.body).collapse(true)
- }
- range.select(true);
- if (me.bkqueryCommandState) {
- me.queryCommandState = me.bkqueryCommandState;
- delete me.bkqueryCommandState;
- }
- if (me.bkqueryCommandValue) {
- me.queryCommandValue = me.bkqueryCommandValue;
- delete me.bkqueryCommandValue;
- }
- me.fireEvent('selectionchange');
- }
- },
- enable: function () {
- return this.setEnabled();
- },
-
- /** 设置当前编辑区域不可编辑
- * @method setDisabled
- */
-
- /** 设置当前编辑区域不可编辑,except中的命令除外
- * @method setDisabled
- * @param { String } except 例外命令的字符串
- * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
- * @example
- * ```javascript
- * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
- * ```
- */
-
- /** 设置当前编辑区域不可编辑,except中的命令除外
- * @method setDisabled
- * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
- * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
- * @example
- * ```javascript
- * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
- * ```
- */
- setDisabled: function (except) {
- var me = this;
- except = except ? utils.isArray(except) ? except : [except] : [];
- if (me.body.contentEditable == 'true') {
- if (!me.lastBk) {
- me.lastBk = me.selection.getRange().createBookmark(true);
- }
- me.body.contentEditable = false;
- me.bkqueryCommandState = me.queryCommandState;
- me.bkqueryCommandValue = me.queryCommandValue;
- me.queryCommandState = function (type) {
- if (utils.indexOf(except, type) != -1) {
- return me.bkqueryCommandState.apply(me, arguments);
- }
- return -1;
- };
- me.queryCommandValue = function (type) {
- if (utils.indexOf(except, type) != -1) {
- return me.bkqueryCommandValue.apply(me, arguments);
- }
- return null;
- };
- me.fireEvent('selectionchange');
- }
- },
- disable: function (except) {
- return this.setDisabled(except);
- },
-
- /**
- * 设置默认内容
- * @method _setDefaultContent
- * @private
- * @param { String } cont 要存入的内容
- */
- _setDefaultContent: function () {
- function clear() {
- var me = this;
- if (me.document.getElementById('initContent')) {
- me.body.innerHTML = '' + (ie ? '' : '
') + '
';
- me.removeListener('firstBeforeExecCommand focus', clear);
- setTimeout(function () {
- me.focus();
- me._selectionChange();
- }, 0)
- }
- }
-
- return function (cont) {
- var me = this;
- me.body.innerHTML = '' + cont + '
';
-
- me.addListener('firstBeforeExecCommand focus', clear);
- }
- }(),
-
- /**
- * 显示编辑器
- * @method setShow
- * @example
- * ```javascript
- * editor.setShow()
- * ```
- */
- setShow: function () {
- var me = this, range = me.selection.getRange();
- if (me.container.style.display == 'none') {
- //有可能内容丢失了
- try {
- range.moveToBookmark(me.lastBk);
- delete me.lastBk
- } catch (e) {
- range.setStartAtFirst(me.body).collapse(true)
- }
- //ie下focus实效,所以做了个延迟
- setTimeout(function () {
- range.select(true);
- }, 100);
- me.container.style.display = '';
- }
-
- },
- show: function () {
- return this.setShow();
- },
- /**
- * 隐藏编辑器
- * @method setHide
- * @example
- * ```javascript
- * editor.setHide()
- * ```
- */
- setHide: function () {
- var me = this;
- if (!me.lastBk) {
- me.lastBk = me.selection.getRange().createBookmark(true);
- }
- me.container.style.display = 'none'
- },
- hide: function () {
- return this.setHide();
- },
-
- /**
- * 根据指定的路径,获取对应的语言资源
- * @method getLang
- * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
- * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
- * @example
- * ```javascript
- * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
- * ```
- */
- getLang: function (path) {
- var lang = UE.I18N[this.options.lang];
- if (!lang) {
- throw Error("not import language file");
- }
- path = (path || "").split(".");
- for (var i = 0, ci; ci = path[i++];) {
- lang = lang[ci];
- if (!lang)break;
- }
- return lang;
- },
-
- /**
- * 计算编辑器html内容字符串的长度
- * @method getContentLength
- * @return { Number } 返回计算的长度
- * @example
- * ```javascript
- * //编辑器html内容132
- * editor.getContentLength() //返回27
- * ```
- */
- /**
- * 计算编辑器当前纯文本内容的长度
- * @method getContentLength
- * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
- * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
- * @example
- * ```javascript
- * //编辑器html内容132
- * editor.getContentLength() //返回3
- * ```
- */
- getContentLength: function (ingoneHtml, tagNames) {
- var count = this.getContent(false,false,true).length;
- if (ingoneHtml) {
- tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
- count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
- for (var i = 0, ci; ci = tagNames[i++];) {
- count += this.document.getElementsByTagName(ci).length;
- }
- }
- return count;
- },
-
- /**
- * 注册输入过滤规则
- * @method addInputRule
- * @param { Function } rule 要添加的过滤规则
- * @example
- * ```javascript
- * editor.addInputRule(function(root){
- * $.each(root.getNodesByTagName('div'),function(i,node){
- * node.tagName="p";
- * });
- * });
- * ```
- */
- addInputRule: function (rule) {
- this.inputRules.push(rule);
- },
-
- /**
- * 执行注册的过滤规则
- * @method filterInputRule
- * @param { UE.uNode } root 要过滤的uNode节点
- * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
- * @example
- * ```javascript
- * editor.filterInputRule(editor.body);
- * ```
- * @see UE.Editor:addInputRule
- */
- filterInputRule: function (root) {
- for (var i = 0, ci; ci = this.inputRules[i++];) {
- ci.call(this, root)
- }
- },
-
- /**
- * 注册输出过滤规则
- * @method addOutputRule
- * @param { Function } rule 要添加的过滤规则
- * @example
- * ```javascript
- * editor.addOutputRule(function(root){
- * $.each(root.getNodesByTagName('p'),function(i,node){
- * node.tagName="div";
- * });
- * });
- * ```
- */
- addOutputRule: function (rule) {
- this.outputRules.push(rule)
- },
-
- /**
- * 根据输出过滤规则,过滤编辑器内容
- * @method filterOutputRule
- * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
- * @param { UE.uNode } root 要过滤的uNode节点
- * @example
- * ```javascript
- * editor.filterOutputRule(editor.body);
- * ```
- * @see UE.Editor:addOutputRule
- */
- filterOutputRule: function (root) {
- for (var i = 0, ci; ci = this.outputRules[i++];) {
- ci.call(this, root)
- }
- },
-
- /**
- * 根据action名称获取请求的路径
- * @method getActionUrl
- * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
- * @param { String } action action名称
- * @example
- * ```javascript
- * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
- * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
- * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
- * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
- * ```
- */
- getActionUrl: function(action){
- var actionName = this.getOpt(action) || action,
- imageUrl = this.getOpt('imageUrl'),
- serverUrl = this.getOpt('serverUrl');
-
- if(!serverUrl && imageUrl) {
- serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2');
- }
-
- if(serverUrl) {
- serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?':'&') + 'action=' + (actionName || '');
- return utils.formatUrl(serverUrl);
- } else {
- return '';
- }
- }
- };
- utils.inherits(Editor, EventBase);
-})();
-
-
-// core/Editor.defaultoptions.js
-//维护编辑器一下默认的不在插件中的配置项
-UE.Editor.defaultOptions = function(editor){
-
- var _url = editor.options.UEDITOR_HOME_URL;
- return {
- isShow: true,
- initialContent: '',
- initialStyle:'',
- autoClearinitialContent: false,
- iframeCssUrl: _url + 'themes/iframe.css',
- textarea: 'editorValue',
- focus: false,
- focusInEnd: true,
- autoClearEmptyNode: true,
- fullscreen: false,
- readonly: false,
- zIndex: 999,
- imagePopup: true,
- enterTag: 'p',
- customDomain: false,
- lang: 'zh-cn',
- langPath: _url + 'lang/',
- theme: 'default',
- themePath: _url + 'themes/',
- allHtmlEnabled: false,
- scaleEnabled: false,
- tableNativeEditInFF: false,
- autoSyncData : true,
- fileNameFormat: '{time}{rand:6}'
- }
-};
-
-// core/loadconfig.js
-(function(){
-
- UE.Editor.prototype.loadServerConfig = function(){
- var me = this;
- setTimeout(function(){
- try{
- me.options.imageUrl && me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'));
-
- var configUrl = me.getActionUrl('config'),
- isJsonp = utils.isCrossDomainUrl(configUrl);
-
- /* 发出ajax请求 */
- me._serverConfigLoaded = false;
-
- configUrl && UE.ajax.request(configUrl,{
- 'method': 'GET',
- 'dataType': isJsonp ? 'jsonp':'',
- 'onsuccess':function(r){
- try {
- var config = isJsonp ? r:eval("("+r.responseText+")");
- utils.extend(me.options, config);
- me.fireEvent('serverConfigLoaded');
- me._serverConfigLoaded = true;
- } catch (e) {
- showErrorMsg(me.getLang('loadconfigFormatError'));
- }
- },
- 'onerror':function(){
- showErrorMsg(me.getLang('loadconfigHttpError'));
- }
- });
- } catch(e){
- showErrorMsg(me.getLang('loadconfigError'));
- }
- });
-
- function showErrorMsg(msg) {
- console && console.error(msg);
- //me.fireEvent('showMessage', {
- // 'title': msg,
- // 'type': 'error'
- //});
- }
- };
-
- UE.Editor.prototype.isServerConfigLoaded = function(){
- var me = this;
- return me._serverConfigLoaded || false;
- };
-
- UE.Editor.prototype.afterConfigReady = function(handler){
- if (!handler || !utils.isFunction(handler)) return;
- var me = this;
- var readyHandler = function(){
- handler.apply(me, arguments);
- me.removeListener('serverConfigLoaded', readyHandler);
- };
-
- if (me.isServerConfigLoaded()) {
- handler.call(me, 'serverConfigLoaded');
- } else {
- me.addListener('serverConfigLoaded', readyHandler);
- }
- };
-
-})();
-
-
-// core/ajax.js
-/**
- * @file
- * @module UE.ajax
- * @since 1.2.6.1
- */
-
-/**
- * 提供对ajax请求的支持
- * @module UE.ajax
- */
-UE.ajax = function() {
-
- //创建一个ajaxRequest对象
- var fnStr = 'XMLHttpRequest()';
- try {
- new ActiveXObject("Msxml2.XMLHTTP");
- fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')';
- } catch (e) {
- try {
- new ActiveXObject("Microsoft.XMLHTTP");
- fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
- } catch (e) {
- }
- }
- var creatAjaxRequest = new Function('return new ' + fnStr);
-
-
- /**
- * 将json参数转化成适合ajax提交的参数列表
- * @param json
- */
- function json2str(json) {
- var strArr = [];
- for (var i in json) {
- //忽略默认的几个参数
- if(i=="method" || i=="timeout" || i=="async" || i=="dataType" || i=="callback") continue;
- //忽略控制
- if(json[i] == undefined || json[i] == null) continue;
- //传递过来的对象和函数不在提交之列
- if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
- strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
- } else if (utils.isArray(json[i])) {
- //支持传数组内容
- for(var j = 0; j < json[i].length; j++) {
- strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
- }
- }
- }
- return strArr.join("&");
- }
-
- function doAjax(url, ajaxOptions) {
- var xhr = creatAjaxRequest(),
- //是否超时
- timeIsOut = false,
- //默认参数
- defaultAjaxOptions = {
- method:"POST",
- timeout:5000,
- async:true,
- data:{},//需要传递对象的话只能覆盖
- onsuccess:function() {
- },
- onerror:function() {
- }
- };
-
- if (typeof url === "object") {
- ajaxOptions = url;
- url = ajaxOptions.url;
- }
- if (!xhr || !url) return;
- var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions,ajaxOptions) : defaultAjaxOptions;
-
- var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
- //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
- if (!utils.isEmptyObject(ajaxOpts.data)){
- submitStr += (submitStr? "&":"") + json2str(ajaxOpts.data);
- }
- //超时检测
- var timerID = setTimeout(function() {
- if (xhr.readyState != 4) {
- timeIsOut = true;
- xhr.abort();
- clearTimeout(timerID);
- }
- }, ajaxOpts.timeout);
-
- var method = ajaxOpts.method.toUpperCase();
- var str = url + (url.indexOf("?")==-1?"?":"&") + (method=="POST"?"":submitStr+ "&noCache=" + +new Date);
- xhr.open(method, str, ajaxOpts.async);
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4) {
- if (!timeIsOut && xhr.status == 200) {
- ajaxOpts.onsuccess(xhr);
- } else {
- ajaxOpts.onerror(xhr);
- }
- }
- };
- if (method == "POST") {
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- xhr.send(submitStr);
- } else {
- xhr.send(null);
- }
- }
-
- function doJsonp(url, opts) {
-
- var successhandler = opts.onsuccess || function(){},
- scr = document.createElement('SCRIPT'),
- options = opts || {},
- charset = options['charset'],
- callbackField = options['jsonp'] || 'callback',
- callbackFnName,
- timeOut = options['timeOut'] || 0,
- timer,
- reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'),
- matches;
-
- if (utils.isFunction(successhandler)) {
- callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36);
- window[callbackFnName] = getCallBack(0);
- } else if(utils.isString(successhandler)){
- callbackFnName = successhandler;
- } else {
- if (matches = reg.exec(url)) {
- callbackFnName = matches[2];
- }
- }
-
- url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName);
-
- if (url.search(reg) < 0) {
- url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName;
- }
-
- var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
- //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
- if (!utils.isEmptyObject(opts.data)){
- queryStr += (queryStr? "&":"") + json2str(opts.data);
- }
- if (queryStr) {
- url = url.replace(/\?/, '?' + queryStr + '&');
- }
-
- scr.onerror = getCallBack(1);
- if( timeOut ){
- timer = setTimeout(getCallBack(1), timeOut);
- }
- createScriptTag(scr, url, charset);
-
- function createScriptTag(scr, url, charset) {
- scr.setAttribute('type', 'text/javascript');
- scr.setAttribute('defer', 'defer');
- charset && scr.setAttribute('charset', charset);
- scr.setAttribute('src', url);
- document.getElementsByTagName('head')[0].appendChild(scr);
- }
-
- function getCallBack(onTimeOut){
- return function(){
- try {
- if(onTimeOut){
- options.onerror && options.onerror();
- }else{
- try{
- clearTimeout(timer);
- successhandler.apply(window, arguments);
- } catch (e){}
- }
- } catch (exception) {
- options.onerror && options.onerror.call(window, exception);
- } finally {
- options.oncomplete && options.oncomplete.apply(window, arguments);
- scr.parentNode && scr.parentNode.removeChild(scr);
- window[callbackFnName] = null;
- try {
- delete window[callbackFnName];
- }catch(e){}
- }
- }
- }
- }
-
- return {
- /**
- * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
- * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
- * @method request
- * @param { URLString } url ajax请求的url地址
- * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
- * @example
- * ```javascript
- * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
- * UE.ajax.requeset( 'sayhello.php', {
- *
- * //请求方法。可选值: 'GET', 'POST',默认值是'POST'
- * method: 'GET',
- *
- * //超时时间。 默认为5000, 单位是ms
- * timeout: 10000,
- *
- * //是否是异步请求。 true为异步请求, false为同步请求
- * async: true,
- *
- * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
- * data: {
- * name: 'ueditor'
- * },
- *
- * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
- * onsuccess: function ( xhr ) {
- * console.log( xhr.responseText );
- * },
- *
- * //请求失败或者超时后的回调。
- * onerror: function ( xhr ) {
- * alert( 'Ajax请求失败' );
- * }
- *
- * } );
- * ```
- */
-
- /**
- * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
- * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
- * @method request
- * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
- * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
- * @example
- * ```javascript
- *
- * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
- * UE.ajax.requeset( 'sayhello.php', {
- *
- * //请求的地址, 该项是必须的。
- * url: 'sayhello.php'
- *
- * } );
- * ```
- */
- request:function(url, opts) {
- if (opts && opts.dataType == 'jsonp') {
- doJsonp(url, opts);
- } else {
- doAjax(url, opts);
- }
- },
- getJSONP:function(url, data, fn) {
- var opts = {
- 'data': data,
- 'oncomplete': fn
- };
- doJsonp(url, opts);
- }
- };
-
-
-}();
-
-
-// core/filterword.js
-/**
- * UE过滤word的静态方法
- * @file
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @module UE
- */
-
-
-/**
- * 根据传入html字符串过滤word
- * @module UE
- * @since 1.2.6.1
- * @method filterWord
- * @param { String } html html字符串
- * @return { String } 已过滤后的结果字符串
- * @example
- * ```javascript
- * UE.filterWord(html);
- * ```
- */
-var filterWord = UE.filterWord = function () {
-
- //是否是word过来的内容
- function isWordDocument( str ) {
- return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
- }
- //去掉小数
- function transUnit( v ) {
- v = v.replace( /[\d.]+\w+/g, function ( m ) {
- return utils.transUnitToPx(m);
- } );
- return v;
- }
-
- function filterPasteWord( str ) {
- return str.replace(/[\t\r\n]+/g,' ')
- .replace( //ig, "" )
- //转换图片
- .replace(/]*>[\s\S]*?.<\/v:shape>/gi,function(str){
- //opera能自己解析出image所这里直接返回空
- if(browser.opera){
- return '';
- }
- try{
- //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
- if(/Bitmap/i.test(str)){
- return '';
- }
- var width = str.match(/width:([ \d.]*p[tx])/i)[1],
- height = str.match(/height:([ \d.]*p[tx])/i)[1],
- src = str.match(/src=\s*"([^"]*)"/i)[1];
- return '';
- } catch(e){
- return '';
- }
- })
- //针对wps添加的多余标签处理
- .replace(/<\/?div[^>]*>/g,'')
- //去掉多余的属性
- .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
- .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
- .replace( /]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "
$1
" )
- //去掉多余的属性
- .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
- //保留list的标示
- return name == 'class' && val == 'MsoListParagraph' ? str : ''
- })
- //清除多余的font/span不能匹配 有可能是空格
- .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
- return c.replace(/[\t\r\n ]+/g,' ')
- })
- //处理style的问题
- .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
- var n = [],
- s = style.replace( /^\s+|\s+$/, '' )
- .replace(/'/g,'\'')
- .replace( /"/gi, "'" )
- .replace(/[\d.]+(cm|pt)/g,function(str){
- return utils.transUnitToPx(str)
- })
- .split( /;\s*/g );
-
- for ( var i = 0,v; v = s[i];i++ ) {
-
- var name, value,
- parts = v.split( ":" );
-
- if ( parts.length == 2 ) {
- name = parts[0].toLowerCase();
- value = parts[1].toLowerCase();
- if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
- ||
- /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
- ){
- continue;
- }
-
- switch ( name ) {
- case "mso-padding-alt":
- case "mso-padding-top-alt":
- case "mso-padding-right-alt":
- case "mso-padding-bottom-alt":
- case "mso-padding-left-alt":
- case "mso-margin-alt":
- case "mso-margin-top-alt":
- case "mso-margin-right-alt":
- case "mso-margin-bottom-alt":
- case "mso-margin-left-alt":
- //ie下会出现挤到一起的情况
- //case "mso-table-layout-alt":
- case "mso-height":
- case "mso-width":
- case "mso-vertical-align-alt":
- //trace:1819 ff下会解析出padding在table上
- if(!/]/.test(html)) {
- return UE.htmlparser(html).children[0]
- } else {
- return new uNode({
- type:'element',
- children:[],
- tagName:html
- })
- }
- };
- uNode.createText = function (data,noTrans) {
- return new UE.uNode({
- type:'text',
- 'data':noTrans ? data : utils.unhtml(data || '')
- })
- };
- function nodeToHtml(node, arr, formatter, current) {
- switch (node.type) {
- case 'root':
- for (var i = 0, ci; ci = node.children[i++];) {
- //插入新行
- if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
- insertLine(arr, current, true);
- insertIndent(arr, current)
- }
- nodeToHtml(ci, arr, formatter, current)
- }
- break;
- case 'text':
- isText(node, arr);
- break;
- case 'element':
- isElement(node, arr, formatter, current);
- break;
- case 'comment':
- isComment(node, arr, formatter);
- }
- return arr;
- }
-
- function isText(node, arr) {
- if(node.parentNode.tagName == 'pre'){
- //源码模式下输入html标签,不能做转换处理,直接输出
- arr.push(node.data)
- }else{
- arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' '))
- }
-
- }
-
- function isElement(node, arr, formatter, current) {
- var attrhtml = '';
- if (node.attrs) {
- attrhtml = [];
- var attrs = node.attrs;
- for (var a in attrs) {
- //这里就针对
- //'
- //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
- //有可能做的不够
- attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
- return '"'
- }) : utils.unhtml(attrs[a])) + '"' : ''))
- }
- attrhtml = attrhtml.join(' ');
- }
- arr.push('<' + node.tagName +
- (attrhtml ? ' ' + attrhtml : '') +
- (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
- );
- //插入新行
- if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
- if(node.children && node.children.length){
- current = insertLine(arr, current, true);
- insertIndent(arr, current)
- }
-
- }
- if (node.children && node.children.length) {
- for (var i = 0, ci; ci = node.children[i++];) {
- if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
- insertLine(arr, current);
- insertIndent(arr, current)
- }
- nodeToHtml(ci, arr, formatter, current)
- }
- }
- if (!dtd.$empty[node.tagName]) {
- if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
-
- if(node.children && node.children.length){
- current = insertLine(arr, current);
- insertIndent(arr, current)
- }
- }
- arr.push('<\/' + node.tagName + '>');
- }
-
- }
-
- function isComment(node, arr) {
- arr.push('');
- }
-
- function getNodeById(root, id) {
- var node;
- if (root.type == 'element' && root.getAttr('id') == id) {
- return root;
- }
- if (root.children && root.children.length) {
- for (var i = 0, ci; ci = root.children[i++];) {
- if (node = getNodeById(ci, id)) {
- return node;
- }
- }
- }
- }
-
- function getNodesByTagName(node, tagName, arr) {
- if (node.type == 'element' && node.tagName == tagName) {
- arr.push(node);
- }
- if (node.children && node.children.length) {
- for (var i = 0, ci; ci = node.children[i++];) {
- getNodesByTagName(ci, tagName, arr)
- }
- }
- }
- function nodeTraversal(root,fn){
- if(root.children && root.children.length){
- for(var i= 0,ci;ci=root.children[i];){
- nodeTraversal(ci,fn);
- //ci被替换的情况,这里就不再走 fn了
- if(ci.parentNode ){
- if(ci.children && ci.children.length){
- fn(ci)
- }
- if(ci.parentNode) i++
- }
- }
- }else{
- fn(root)
- }
-
- }
- uNode.prototype = {
-
- /**
- * 当前节点对象,转换成html文本
- * @method toHtml
- * @return { String } 返回转换后的html字符串
- * @example
- * ```javascript
- * node.toHtml();
- * ```
- */
-
- /**
- * 当前节点对象,转换成html文本
- * @method toHtml
- * @param { Boolean } formatter 是否格式化返回值
- * @return { String } 返回转换后的html字符串
- * @example
- * ```javascript
- * node.toHtml( true );
- * ```
- */
- toHtml:function (formatter) {
- var arr = [];
- nodeToHtml(this, arr, formatter, 0);
- return arr.join('')
- },
-
- /**
- * 获取节点的html内容
- * @method innerHTML
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @return { String } 返回节点的html内容
- * @example
- * ```javascript
- * var htmlstr = node.innerHTML();
- * ```
- */
-
- /**
- * 设置节点的html内容
- * @method innerHTML
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @param { String } htmlstr 传入要设置的html内容
- * @return { UE.uNode } 返回节点本身
- * @example
- * ```javascript
- * node.innerHTML('text');
- * ```
- */
- innerHTML:function (htmlstr) {
- if (this.type != 'element' || dtd.$empty[this.tagName]) {
- return this;
- }
- if (utils.isString(htmlstr)) {
- if(this.children){
- for (var i = 0, ci; ci = this.children[i++];) {
- ci.parentNode = null;
- }
- }
- this.children = [];
- var tmpRoot = UE.htmlparser(htmlstr);
- for (var i = 0, ci; ci = tmpRoot.children[i++];) {
- this.children.push(ci);
- ci.parentNode = this;
- }
- return this;
- } else {
- var tmpRoot = new UE.uNode({
- type:'root',
- children:this.children
- });
- return tmpRoot.toHtml();
- }
- },
-
- /**
- * 获取节点的纯文本内容
- * @method innerText
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @return { String } 返回节点的存文本内容
- * @example
- * ```javascript
- * var textStr = node.innerText();
- * ```
- */
-
- /**
- * 设置节点的纯文本内容
- * @method innerText
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @param { String } textStr 传入要设置的文本内容
- * @return { UE.uNode } 返回节点本身
- * @example
- * ```javascript
- * node.innerText('text');
- * ```
- */
- innerText:function (textStr,noTrans) {
- if (this.type != 'element' || dtd.$empty[this.tagName]) {
- return this;
- }
- if (textStr) {
- if(this.children){
- for (var i = 0, ci; ci = this.children[i++];) {
- ci.parentNode = null;
- }
- }
- this.children = [];
- this.appendChild(uNode.createText(textStr,noTrans));
- return this;
- } else {
- return this.toHtml().replace(/<[^>]+>/g, '');
- }
- },
-
- /**
- * 获取当前对象的data属性
- * @method getData
- * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
- * @example
- * ```javascript
- * node.getData();
- * ```
- */
- getData:function () {
- if (this.type == 'element')
- return '';
- return this.data
- },
-
- /**
- * 获取当前节点下的第一个子节点
- * @method firstChild
- * @return { UE.uNode } 返回第一个子节点
- * @example
- * ```javascript
- * node.firstChild(); //返回第一个子节点
- * ```
- */
- firstChild:function () {
-// if (this.type != 'element' || dtd.$empty[this.tagName]) {
-// return this;
-// }
- return this.children ? this.children[0] : null;
- },
-
- /**
- * 获取当前节点下的最后一个子节点
- * @method lastChild
- * @return { UE.uNode } 返回最后一个子节点
- * @example
- * ```javascript
- * node.lastChild(); //返回最后一个子节点
- * ```
- */
- lastChild:function () {
-// if (this.type != 'element' || dtd.$empty[this.tagName] ) {
-// return this;
-// }
- return this.children ? this.children[this.children.length - 1] : null;
- },
-
- /**
- * 获取和当前节点有相同父亲节点的前一个节点
- * @method previousSibling
- * @return { UE.uNode } 返回前一个节点
- * @example
- * ```javascript
- * node.children[2].previousSibling(); //返回子节点node.children[1]
- * ```
- */
- previousSibling : function(){
- var parent = this.parentNode;
- for (var i = 0, ci; ci = parent.children[i]; i++) {
- if (ci === this) {
- return i == 0 ? null : parent.children[i-1];
- }
- }
-
- },
-
- /**
- * 获取和当前节点有相同父亲节点的后一个节点
- * @method nextSibling
- * @return { UE.uNode } 返回后一个节点,找不到返回null
- * @example
- * ```javascript
- * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
- * ```
- */
- nextSibling : function(){
- var parent = this.parentNode;
- for (var i = 0, ci; ci = parent.children[i++];) {
- if (ci === this) {
- return parent.children[i];
- }
- }
- },
-
- /**
- * 用新的节点替换当前节点
- * @method replaceChild
- * @param { UE.uNode } target 要替换成该节点参数
- * @param { UE.uNode } source 要被替换掉的节点
- * @return { UE.uNode } 返回替换之后的节点对象
- * @example
- * ```javascript
- * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
- * ```
- */
- replaceChild:function (target, source) {
- if (this.children) {
- if(target.parentNode){
- target.parentNode.removeChild(target);
- }
- for (var i = 0, ci; ci = this.children[i]; i++) {
- if (ci === source) {
- this.children.splice(i, 1, target);
- source.parentNode = null;
- target.parentNode = this;
- return target;
- }
- }
- }
- },
-
- /**
- * 在节点的子节点列表最后位置插入一个节点
- * @method appendChild
- * @param { UE.uNode } node 要插入的节点
- * @return { UE.uNode } 返回刚插入的子节点
- * @example
- * ```javascript
- * node.appendChild( newNode ); //在node内插入子节点newNode
- * ```
- */
- appendChild:function (node) {
- if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
- if (!this.children) {
- this.children = []
- }
- if(node.parentNode){
- node.parentNode.removeChild(node);
- }
- for (var i = 0, ci; ci = this.children[i]; i++) {
- if (ci === node) {
- this.children.splice(i, 1);
- break;
- }
- }
- this.children.push(node);
- node.parentNode = this;
- return node;
- }
-
-
- },
-
- /**
- * 在传入节点的前面插入一个节点
- * @method insertBefore
- * @param { UE.uNode } target 要插入的节点
- * @param { UE.uNode } source 在该参数节点前面插入
- * @return { UE.uNode } 返回刚插入的子节点
- * @example
- * ```javascript
- * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
- * ```
- */
- insertBefore:function (target, source) {
- if (this.children) {
- if(target.parentNode){
- target.parentNode.removeChild(target);
- }
- for (var i = 0, ci; ci = this.children[i]; i++) {
- if (ci === source) {
- this.children.splice(i, 0, target);
- target.parentNode = this;
- return target;
- }
- }
-
- }
- },
-
- /**
- * 在传入节点的后面插入一个节点
- * @method insertAfter
- * @param { UE.uNode } target 要插入的节点
- * @param { UE.uNode } source 在该参数节点后面插入
- * @return { UE.uNode } 返回刚插入的子节点
- * @example
- * ```javascript
- * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
- * ```
- */
- insertAfter:function (target, source) {
- if (this.children) {
- if(target.parentNode){
- target.parentNode.removeChild(target);
- }
- for (var i = 0, ci; ci = this.children[i]; i++) {
- if (ci === source) {
- this.children.splice(i + 1, 0, target);
- target.parentNode = this;
- return target;
- }
-
- }
- }
- },
-
- /**
- * 从当前节点的子节点列表中,移除节点
- * @method removeChild
- * @param { UE.uNode } node 要移除的节点引用
- * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
- * @return { * } 返回刚移除的子节点
- * @example
- * ```javascript
- * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
- * ```
- */
- removeChild:function (node,keepChildren) {
- if (this.children) {
- for (var i = 0, ci; ci = this.children[i]; i++) {
- if (ci === node) {
- this.children.splice(i, 1);
- ci.parentNode = null;
- if(keepChildren && ci.children && ci.children.length){
- for(var j= 0,cj;cj=ci.children[j];j++){
- this.children.splice(i+j,0,cj);
- cj.parentNode = this;
-
- }
- }
- return ci;
- }
- }
- }
- },
-
- /**
- * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
- * @method getAttr
- * @param { String } attrName 要获取的属性名称
- * @return { * } 返回attrs对象下的属性值
- * @example
- * ```javascript
- * node.getAttr('title');
- * ```
- */
- getAttr:function (attrName) {
- return this.attrs && this.attrs[attrName.toLowerCase()]
- },
-
- /**
- * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
- * @method setAttr
- * @param { String } attrName 要设置的属性名称
- * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
- * @return { * } 返回attrs对象下的属性值
- * @example
- * ```javascript
- * node.setAttr('title','标题');
- * ```
- */
- setAttr:function (attrName, attrVal) {
- if (!attrName) {
- delete this.attrs;
- return;
- }
- if(!this.attrs){
- this.attrs = {};
- }
- if (utils.isObject(attrName)) {
- for (var a in attrName) {
- if (!attrName[a]) {
- delete this.attrs[a]
- } else {
- this.attrs[a.toLowerCase()] = attrName[a];
- }
- }
- } else {
- if (!attrVal) {
- delete this.attrs[attrName]
- } else {
- this.attrs[attrName.toLowerCase()] = attrVal;
- }
-
- }
- },
-
- /**
- * 获取当前节点在父节点下的位置索引
- * @method getIndex
- * @return { Number } 返回索引数值,如果没有父节点,返回-1
- * @example
- * ```javascript
- * node.getIndex();
- * ```
- */
- getIndex:function(){
- var parent = this.parentNode;
- for(var i= 0,ci;ci=parent.children[i];i++){
- if(ci === this){
- return i;
- }
- }
- return -1;
- },
-
- /**
- * 在当前节点下,根据id查找节点
- * @method getNodeById
- * @param { String } id 要查找的id
- * @return { UE.uNode } 返回找到的节点
- * @example
- * ```javascript
- * node.getNodeById('textId');
- * ```
- */
- getNodeById:function (id) {
- var node;
- if (this.children && this.children.length) {
- for (var i = 0, ci; ci = this.children[i++];) {
- if (node = getNodeById(ci, id)) {
- return node;
- }
- }
- }
- },
-
- /**
- * 在当前节点下,根据元素名称查找节点列表
- * @method getNodesByTagName
- * @param { String } tagNames 要查找的元素名称
- * @return { Array } 返回找到的节点列表
- * @example
- * ```javascript
- * node.getNodesByTagName('span');
- * ```
- */
- getNodesByTagName:function (tagNames) {
- tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
- var arr = [], me = this;
- utils.each(tagNames, function (tagName) {
- if (me.children && me.children.length) {
- for (var i = 0, ci; ci = me.children[i++];) {
- getNodesByTagName(ci, tagName, arr)
- }
- }
- });
- return arr;
- },
-
- /**
- * 根据样式名称,获取节点的样式值
- * @method getStyle
- * @param { String } name 要获取的样式名称
- * @return { String } 返回样式值
- * @example
- * ```javascript
- * node.getStyle('font-size');
- * ```
- */
- getStyle:function (name) {
- var cssStyle = this.getAttr('style');
- if (!cssStyle) {
- return ''
- }
- var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
- var match = cssStyle.match(reg);
- if (match && match[0]) {
- return match[2]
- }
- return '';
- },
-
- /**
- * 给节点设置样式
- * @method setStyle
- * @param { String } name 要设置的的样式名称
- * @param { String } val 要设置的的样值
- * @example
- * ```javascript
- * node.setStyle('font-size', '12px');
- * ```
- */
- setStyle:function (name, val) {
- function exec(name, val) {
- var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
- cssStyle = cssStyle.replace(reg, '$1');
- if (val) {
- cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
- }
-
- }
-
- var cssStyle = this.getAttr('style');
- if (!cssStyle) {
- cssStyle = '';
- }
- if (utils.isObject(name)) {
- for (var a in name) {
- exec(a, name[a])
- }
- } else {
- exec(name, val)
- }
- this.setAttr('style', utils.trim(cssStyle))
- },
-
- /**
- * 传入一个函数,递归遍历当前节点下的所有节点
- * @method traversal
- * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
- * @example
- * ```javascript
- * traversal(node, function(){
- * console.log(node.type);
- * });
- * ```
- */
- traversal:function(fn){
- if(this.children && this.children.length){
- nodeTraversal(this,fn);
- }
- return this;
- }
- }
-})();
-
-
-// core/htmlparser.js
-/**
- * html字符串转换成uNode节点
- * @file
- * @module UE
- * @since 1.2.6.1
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @unfile
- * @module UE
- */
-
-/**
- * html字符串转换成uNode节点的静态方法
- * @method htmlparser
- * @param { String } htmlstr 要转换的html代码
- * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
- * @return { uNode } 给定的html片段转换形成的uNode对象
- * @example
- * ```javascript
- * var root = UE.htmlparser('htmlparser
', true);
- * ```
- */
-
-var htmlparser = UE.htmlparser = function (htmlstr,ignoreBlank) {
- //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 这样的标签了
- //先去掉了,加上的原因忘了,这里先记录
- var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
- re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
-
- //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
- var allowEmptyTags = {
- b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
- sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
- };
- htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
- if(!ignoreBlank){
- htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
- //br暂时单独处理
- if(b && allowEmptyTags[b.toLowerCase()]){
- return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
- }
- return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
- });
- }
-
- var notTransAttrs = {
- 'href':1,
- 'src':1
- };
-
- var uNode = UE.uNode,
- needParentNode = {
- 'td':'tr',
- 'tr':['tbody','thead','tfoot'],
- 'tbody':'table',
- 'th':'tr',
- 'thead':'table',
- 'tfoot':'table',
- 'caption':'table',
- 'li':['ul', 'ol'],
- 'dt':'dl',
- 'dd':'dl',
- 'option':'select'
- },
- needChild = {
- 'ol':'li',
- 'ul':'li'
- };
-
- function text(parent, data) {
-
- if(needChild[parent.tagName]){
- var tmpNode = uNode.createElement(needChild[parent.tagName]);
- parent.appendChild(tmpNode);
- tmpNode.appendChild(uNode.createText(data));
- parent = tmpNode;
- }else{
-
- parent.appendChild(uNode.createText(data));
- }
- }
-
- function element(parent, tagName, htmlattr) {
- var needParentTag;
- if (needParentTag = needParentNode[tagName]) {
- var tmpParent = parent,hasParent;
- while(tmpParent.type != 'root'){
- if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
- parent = tmpParent;
- hasParent = true;
- break;
- }
- tmpParent = tmpParent.parentNode;
- }
- if(!hasParent){
- parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
- }
- }
- //按dtd处理嵌套
-// if(parent.type != 'root' && !dtd[parent.tagName][tagName])
-// parent = parent.parentNode;
- var elm = new uNode({
- parentNode:parent,
- type:'element',
- tagName:tagName.toLowerCase(),
- //是自闭合的处理一下
- children:dtd.$empty[tagName] ? null : []
- });
- //如果属性存在,处理属性
- if (htmlattr) {
- var attrs = {}, match;
- while (match = re_attr.exec(htmlattr)) {
- attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
- }
- elm.attrs = attrs;
- }
- //trace:3970
-// //如果parent下不能放elm
-// if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
-// parent = parent.parentNode;
-// elm.parentNode = parent;
-// }
- parent.children.push(elm);
- //如果是自闭合节点返回父亲节点
- return dtd.$empty[tagName] ? parent : elm
- }
-
- function comment(parent, data) {
- parent.children.push(new uNode({
- type:'comment',
- data:data,
- parentNode:parent
- }));
- }
-
- var match, currentIndex = 0, nextIndex = 0;
- //设置根节点
- var root = new uNode({
- type:'root',
- children:[]
- });
- var currentParent = root;
-
- while (match = re_tag.exec(htmlstr)) {
- currentIndex = match.index;
- try{
- if (currentIndex > nextIndex) {
- //text node
- text(currentParent, htmlstr.slice(nextIndex, currentIndex));
- }
- if (match[3]) {
-
- if(dtd.$cdata[currentParent.tagName]){
- text(currentParent, match[0]);
- }else{
- //start tag
- currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
- }
-
-
- } else if (match[1]) {
- if(currentParent.type != 'root'){
- if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
- text(currentParent, match[0]);
- }else{
- var tmpParent = currentParent;
- while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
- currentParent = currentParent.parentNode;
- if(currentParent.type == 'root'){
- currentParent = tmpParent;
- throw 'break'
- }
- }
- //end tag
- currentParent = currentParent.parentNode;
- }
-
- }
-
- } else if (match[2]) {
- //comment
- comment(currentParent, match[2])
- }
- }catch(e){}
-
- nextIndex = re_tag.lastIndex;
-
- }
- //如果结束是文本,就有可能丢掉,所以这里手动判断一下
- //例如 sdfsdfsdfsdfsdfsdfsdf
- if (nextIndex < htmlstr.length) {
- text(currentParent, htmlstr.slice(nextIndex));
- }
- return root;
-};
-
-// core/filternode.js
-/**
- * UE过滤节点的静态方法
- * @file
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @module UE
- */
-
-
-/**
- * 根据传入节点和过滤规则过滤相应节点
- * @module UE
- * @since 1.2.6.1
- * @method filterNode
- * @param { Object } root 指定root节点
- * @param { Object } rules 过滤规则json对象
- * @example
- * ```javascript
- * UE.filterNode(root,editor.options.filterRules);
- * ```
- */
-var filterNode = UE.filterNode = function () {
- function filterNode(node,rules){
- switch (node.type) {
- case 'text':
- break;
- case 'element':
- var val;
- if(val = rules[node.tagName]){
- if(val === '-'){
- node.parentNode.removeChild(node)
- }else if(utils.isFunction(val)){
- var parentNode = node.parentNode,
- index = node.getIndex();
- val(node);
- if(node.parentNode){
- if(node.children){
- for(var i = 0,ci;ci=node.children[i];){
- filterNode(ci,rules);
- if(ci.parentNode){
- i++;
- }
- }
- }
- }else{
- for(var i = index,ci;ci=parentNode.children[i];){
- filterNode(ci,rules);
- if(ci.parentNode){
- i++;
- }
- }
- }
-
-
- }else{
- var attrs = val['$'];
- if(attrs && node.attrs){
- var tmpAttrs = {},tmpVal;
- for(var a in attrs){
- tmpVal = node.getAttr(a);
- //todo 只先对style单独处理
- if(a == 'style' && utils.isArray(attrs[a])){
- var tmpCssStyle = [];
- utils.each(attrs[a],function(v){
- var tmp;
- if(tmp = node.getStyle(v)){
- tmpCssStyle.push(v + ':' + tmp);
- }
- });
- tmpVal = tmpCssStyle.join(';')
- }
- if(tmpVal){
- tmpAttrs[a] = tmpVal;
- }
-
- }
- node.attrs = tmpAttrs;
- }
- if(node.children){
- for(var i = 0,ci;ci=node.children[i];){
- filterNode(ci,rules);
- if(ci.parentNode){
- i++;
- }
- }
- }
- }
- }else{
- //如果不在名单里扣出子节点并删除该节点,cdata除外
- if(dtd.$cdata[node.tagName]){
- node.parentNode.removeChild(node)
- }else{
- var parentNode = node.parentNode,
- index = node.getIndex();
- node.parentNode.removeChild(node,true);
- for(var i = index,ci;ci=parentNode.children[i];){
- filterNode(ci,rules);
- if(ci.parentNode){
- i++;
- }
- }
- }
- }
- break;
- case 'comment':
- node.parentNode.removeChild(node)
- }
-
- }
- return function(root,rules){
- if(utils.isEmptyObject(rules)){
- return root;
- }
- var val;
- if(val = rules['-']){
- utils.each(val.split(' '),function(k){
- rules[k] = '-'
- })
- }
- for(var i= 0,ci;ci=root.children[i];){
- filterNode(ci,rules);
- if(ci.parentNode){
- i++;
- }
- }
- return root;
- }
-}();
-
-// core/plugin.js
-/**
- * Created with JetBrains PhpStorm.
- * User: campaign
- * Date: 10/8/13
- * Time: 6:15 PM
- * To change this template use File | Settings | File Templates.
- */
-UE.plugin = function(){
- var _plugins = {};
- return {
- register : function(pluginName,fn,oldOptionName,afterDisabled){
- if(oldOptionName && utils.isFunction(oldOptionName)){
- afterDisabled = oldOptionName;
- oldOptionName = null
- }
- _plugins[pluginName] = {
- optionName : oldOptionName || pluginName,
- execFn : fn,
- //当插件被禁用时执行
- afterDisabled : afterDisabled
- }
- },
- load : function(editor){
- utils.each(_plugins,function(plugin){
- var _export = plugin.execFn.call(editor);
- if(editor.options[plugin.optionName] !== false){
- if(_export){
- //后边需要再做扩展
- utils.each(_export,function(v,k){
- switch(k.toLowerCase()){
- case 'shortcutkey':
- editor.addshortcutkey(v);
- break;
- case 'bindevents':
- utils.each(v,function(fn,eventName){
- editor.addListener(eventName,fn);
- });
- break;
- case 'bindmultievents':
- utils.each(utils.isArray(v) ? v:[v],function(event){
- var types = utils.trim(event.type).split(/\s+/);
- utils.each(types,function(eventName){
- editor.addListener(eventName, event.handler);
- });
- });
- break;
- case 'commands':
- utils.each(v,function(execFn,execName){
- editor.commands[execName] = execFn
- });
- break;
- case 'outputrule':
- editor.addOutputRule(v);
- break;
- case 'inputrule':
- editor.addInputRule(v);
- break;
- case 'defaultoptions':
- editor.setOpt(v)
- }
- })
- }
-
- }else if(plugin.afterDisabled){
- plugin.afterDisabled.call(editor)
- }
-
- });
- //向下兼容
- utils.each(UE.plugins,function(plugin){
- plugin.call(editor);
- });
- },
- run : function(pluginName,editor){
- var plugin = _plugins[pluginName];
- if(plugin){
- plugin.exeFn.call(editor)
- }
- }
- }
-}();
-
-// core/keymap.js
-var keymap = UE.keymap = {
- 'Backspace' : 8,
- 'Tab' : 9,
- 'Enter' : 13,
-
- 'Shift':16,
- 'Control':17,
- 'Alt':18,
- 'CapsLock':20,
-
- 'Esc':27,
-
- 'Spacebar':32,
-
- 'PageUp':33,
- 'PageDown':34,
- 'End':35,
- 'Home':36,
-
- 'Left':37,
- 'Up':38,
- 'Right':39,
- 'Down':40,
-
- 'Insert':45,
-
- 'Del':46,
-
- 'NumLock':144,
-
- 'Cmd':91,
-
- '=':187,
- '-':189,
-
- "b":66,
- 'i':73,
- //回退
- 'z':90,
- 'y':89,
- //粘贴
- 'v' : 86,
- 'x' : 88,
-
- 's' : 83,
-
- 'n' : 78
-};
-
-// core/localstorage.js
-//存储媒介封装
-var LocalStorage = UE.LocalStorage = (function () {
-
- var storage = window.localStorage || getUserData() || null,
- LOCAL_FILE = 'localStorage';
-
- return {
-
- saveLocalData: function (key, data) {
-
- if (storage && data) {
- storage.setItem(key, data);
- return true;
- }
-
- return false;
-
- },
-
- getLocalData: function (key) {
-
- if (storage) {
- return storage.getItem(key);
- }
-
- return null;
-
- },
-
- removeItem: function (key) {
-
- storage && storage.removeItem(key);
-
- }
-
- };
-
- function getUserData() {
-
- var container = document.createElement("div");
- container.style.display = "none";
-
- if (!container.addBehavior) {
- return null;
- }
-
- container.addBehavior("#default#userdata");
-
- return {
-
- getItem: function (key) {
-
- var result = null;
-
- try {
- document.body.appendChild(container);
- container.load(LOCAL_FILE);
- result = container.getAttribute(key);
- document.body.removeChild(container);
- } catch (e) {
- }
-
- return result;
-
- },
-
- setItem: function (key, value) {
-
- document.body.appendChild(container);
- container.setAttribute(key, value);
- container.save(LOCAL_FILE);
- document.body.removeChild(container);
-
- },
-
- //// 暂时没有用到
- //clear: function () {
- //
- // var expiresTime = new Date();
- // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
- // document.body.appendChild(container);
- // container.expires = expiresTime.toUTCString();
- // container.save(LOCAL_FILE);
- // document.body.removeChild(container);
- //
- //},
-
- removeItem: function (key) {
-
- document.body.appendChild(container);
- container.removeAttribute(key);
- container.save(LOCAL_FILE);
- document.body.removeChild(container);
-
- }
-
- };
-
- }
-
-})();
-
-(function () {
-
- var ROOTKEY = 'ueditor_preference';
-
- UE.Editor.prototype.setPreferences = function(key,value){
- var obj = {};
- if (utils.isString(key)) {
- obj[ key ] = value;
- } else {
- obj = key;
- }
- var data = LocalStorage.getLocalData(ROOTKEY);
- if (data && (data = utils.str2json(data))) {
- utils.extend(data, obj);
- } else {
- data = obj;
- }
- data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
- };
-
- UE.Editor.prototype.getPreferences = function(key){
- var data = LocalStorage.getLocalData(ROOTKEY);
- if (data && (data = utils.str2json(data))) {
- return key ? data[key] : data
- }
- return null;
- };
-
- UE.Editor.prototype.removePreferences = function (key) {
- var data = LocalStorage.getLocalData(ROOTKEY);
- if (data && (data = utils.str2json(data))) {
- data[key] = undefined;
- delete data[key]
- }
- data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
- };
-
-})();
-
-
-// plugins/defaultfilter.js
-///import core
-///plugin 编辑器默认的过滤转换机制
-
-UE.plugins['defaultfilter'] = function () {
- var me = this;
- me.setOpt({
- 'allowDivTransToP':true,
- 'disabledTableInTable':true
- });
- //默认的过滤处理
- //进入编辑器的内容处理
- me.addInputRule(function (root) {
- var allowDivTransToP = this.options.allowDivTransToP;
- var val;
- function tdParent(node){
- while(node && node.type == 'element'){
- if(node.tagName == 'td'){
- return true;
- }
- node = node.parentNode;
- }
- return false;
- }
- //进行默认的处理
- root.traversal(function (node) {
- if (node.type == 'element') {
- if (!dtd.$cdata[node.tagName] && me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
- if (!node.firstChild()) node.parentNode.removeChild(node);
- else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
- node.parentNode.removeChild(node, true)
- }
- return;
- }
- switch (node.tagName) {
- case 'style':
- case 'script':
- node.setAttr({
- cdata_tag: node.tagName,
- cdata_data: (node.innerHTML() || ''),
- '_ue_custom_node_':'true'
- });
- node.tagName = 'div';
- node.innerHTML('');
- break;
- case 'a':
- if (val = node.getAttr('href')) {
- node.setAttr('_href', val)
- }
- break;
- case 'img':
- //todo base64暂时去掉,后边做远程图片上传后,干掉这个
- if (val = node.getAttr('src')) {
- if (/^data:/.test(val)) {
- node.parentNode.removeChild(node);
- break;
- }
- }
- node.setAttr('_src', node.getAttr('src'));
- break;
- case 'span':
- if (browser.webkit && (val = node.getStyle('white-space'))) {
- if (/nowrap|normal/.test(val)) {
- node.setStyle('white-space', '');
- if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
- node.parentNode.removeChild(node, true)
- }
- }
- }
- val = node.getAttr('id');
- if(val && /^_baidu_bookmark_/i.test(val)){
- node.parentNode.removeChild(node)
- }
- break;
- case 'p':
- if (val = node.getAttr('align')) {
- node.setAttr('align');
- node.setStyle('text-align', val)
- }
- //trace:3431
-// var cssStyle = node.getAttr('style');
-// if (cssStyle) {
-// cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
-// node.setAttr('style', cssStyle)
-//
-// }
- //p标签不允许嵌套
- utils.each(node.children,function(n){
- if(n.type == 'element' && n.tagName == 'p'){
- var next = n.nextSibling();
- node.parentNode.insertAfter(n,node);
- var last = n;
- while(next){
- var tmp = next.nextSibling();
- node.parentNode.insertAfter(next,last);
- last = next;
- next = tmp;
- }
- return false;
- }
- });
- if (!node.firstChild()) {
- node.innerHTML(browser.ie ? ' ' : ' ')
- }
- break;
- case 'div':
- if(node.getAttr('cdata_tag')){
- break;
- }
- //针对代码这里不处理插入代码的div
- val = node.getAttr('class');
- if(val && /^line number\d+/.test(val)){
- break;
- }
- if(!allowDivTransToP){
- break;
- }
- var tmpNode, p = UE.uNode.createElement('p');
- while (tmpNode = node.firstChild()) {
- if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
- p.appendChild(tmpNode);
- } else {
- if (p.firstChild()) {
- node.parentNode.insertBefore(p, node);
- p = UE.uNode.createElement('p');
- } else {
- node.parentNode.insertBefore(tmpNode, node);
- }
- }
- }
- if (p.firstChild()) {
- node.parentNode.insertBefore(p, node);
- }
- node.parentNode.removeChild(node);
- break;
- case 'dl':
- node.tagName = 'ul';
- break;
- case 'dt':
- case 'dd':
- node.tagName = 'li';
- break;
- case 'li':
- var className = node.getAttr('class');
- if (!className || !/list\-/.test(className)) {
- node.setAttr()
- }
- var tmpNodes = node.getNodesByTagName('ol ul');
- UE.utils.each(tmpNodes, function (n) {
- node.parentNode.insertAfter(n, node);
- });
- break;
- case 'td':
- case 'th':
- case 'caption':
- if(!node.children || !node.children.length){
- node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br'))
- }
- break;
- case 'table':
- if(me.options.disabledTableInTable && tdParent(node)){
- node.parentNode.insertBefore(UE.uNode.createText(node.innerText()),node);
- node.parentNode.removeChild(node)
- }
- }
-
- }
-// if(node.type == 'comment'){
-// node.parentNode.removeChild(node);
-// }
- })
-
- });
-
- //从编辑器出去的内容处理
- me.addOutputRule(function (root) {
-
- var val;
- root.traversal(function (node) {
- if (node.type == 'element') {
-
- if (me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
-
- if (!node.firstChild()) node.parentNode.removeChild(node);
- else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
- node.parentNode.removeChild(node, true)
- }
- return;
- }
- switch (node.tagName) {
- case 'div':
- if (val = node.getAttr('cdata_tag')) {
- node.tagName = val;
- node.appendChild(UE.uNode.createText(node.getAttr('cdata_data')));
- node.setAttr({cdata_tag: '', cdata_data: '','_ue_custom_node_':''});
- }
- break;
- case 'a':
- if (val = node.getAttr('_href')) {
- node.setAttr({
- 'href': utils.html(val),
- '_href': ''
- })
- }
- break;
- break;
- case 'span':
- val = node.getAttr('id');
- if(val && /^_baidu_bookmark_/i.test(val)){
- node.parentNode.removeChild(node)
- }
- break;
- case 'img':
- if (val = node.getAttr('_src')) {
- node.setAttr({
- 'src': node.getAttr('_src'),
- '_src': ''
- })
- }
-
-
- }
- }
-
- })
-
-
- });
-};
-
-
-// plugins/inserthtml.js
-/**
- * 插入html字符串插件
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 插入html代码
- * @command inserthtml
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } html 插入的html字符串
- * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
- * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
- * @example
- * ```javascript
- * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
- * //执行命令,插入CC
- * //插入后的效果 xxxCCxxx
- * //xx|xxx 当前选区为闭合状态
- * //插入CC
- * //结果 xx CC xxx
- * //xxxx |xxx 当前选区在两个p标签之间
- * //插入 xxxx
- * //结果 xxxx xxxx xxx
- * ```
- */
-
-UE.commands['inserthtml'] = {
- execCommand: function (command,html,notNeedFilter){
- var me = this,
- range,
- div;
- if(!html){
- return;
- }
- if(me.fireEvent('beforeinserthtml',html) === true){
- return;
- }
- range = me.selection.getRange();
- div = range.document.createElement( 'div' );
- div.style.display = 'inline';
-
- if (!notNeedFilter) {
- var root = UE.htmlparser(html);
- //如果给了过滤规则就先进行过滤
- if(me.options.filterRules){
- UE.filterNode(root,me.options.filterRules);
- }
- //执行默认的处理
- me.filterInputRule(root);
- html = root.toHtml()
- }
- div.innerHTML = utils.trim( html );
-
- if ( !range.collapsed ) {
- var tmpNode = range.startContainer;
- if(domUtils.isFillChar(tmpNode)){
- range.setStartBefore(tmpNode)
- }
- tmpNode = range.endContainer;
- if(domUtils.isFillChar(tmpNode)){
- range.setEndAfter(tmpNode)
- }
- range.txtToElmBoundary();
- //结束边界可能放到了br的前边,要把br包含进来
- // x[xxx]
- if(range.endContainer && range.endContainer.nodeType == 1){
- tmpNode = range.endContainer.childNodes[range.endOffset];
- if(tmpNode && domUtils.isBr(tmpNode)){
- range.setEndAfter(tmpNode);
- }
- }
- if(range.startOffset == 0){
- tmpNode = range.startContainer;
- if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
- tmpNode = range.endContainer;
- if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
- me.body.innerHTML = ''+(browser.ie ? '' : ' ')+' ';
- range.setStart(me.body.firstChild,0).collapse(true)
-
- }
- }
- }
- !range.collapsed && range.deleteContents();
- if(range.startContainer.nodeType == 1){
- var child = range.startContainer.childNodes[range.startOffset],pre;
- if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
- range.setEnd(pre,pre.childNodes.length).collapse();
- while(child.firstChild){
- pre.appendChild(child.firstChild);
- }
- domUtils.remove(child);
- }
- }
-
- }
-
-
- var child,parent,pre,tmp,hadBreak = 0, nextNode;
- //如果当前位置选中了fillchar要干掉,要不会产生空行
- if(range.inFillChar()){
- child = range.startContainer;
- if(domUtils.isFillChar(child)){
- range.setStartBefore(child).collapse(true);
- domUtils.remove(child);
- }else if(domUtils.isFillChar(child,true)){
- child.nodeValue = child.nodeValue.replace(fillCharReg,'');
- range.startOffset--;
- range.collapsed && range.collapse(true)
- }
- }
- //列表单独处理
- var li = domUtils.findParentByTagName(range.startContainer,'li',true);
- if(li){
- var next,last;
- while(child = div.firstChild){
- //针对hr单独处理一下先
- while(child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName=='HR' )){
- next = child.nextSibling;
- range.insertNode( child).collapse();
- last = child;
- child = next;
-
- }
- if(child){
- if(/^(ol|ul)$/i.test(child.tagName)){
- while(child.firstChild){
- last = child.firstChild;
- domUtils.insertAfter(li,child.firstChild);
- li = li.nextSibling;
- }
- domUtils.remove(child)
- }else{
- var tmpLi;
- next = child.nextSibling;
- tmpLi = me.document.createElement('li');
- domUtils.insertAfter(li,tmpLi);
- tmpLi.appendChild(child);
- last = child;
- child = next;
- li = tmpLi;
- }
- }
- }
- li = domUtils.findParentByTagName(range.startContainer,'li',true);
- if(domUtils.isEmptyBlock(li)){
- domUtils.remove(li)
- }
- if(last){
-
- range.setStartAfter(last).collapse(true).select(true)
- }
- }else{
- while ( child = div.firstChild ) {
- if(hadBreak){
- var p = me.document.createElement('p');
- while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
- nextNode = child.nextSibling;
- p.appendChild(child);
- child = nextNode;
- }
- if(p.firstChild){
-
- child = p
- }
- }
- range.insertNode( child );
- nextNode = child.nextSibling;
- if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
-
- parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
- if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
- if(!dtd[parent.tagName][child.nodeName]){
- pre = parent;
- }else{
- tmp = child.parentNode;
- while (tmp !== parent){
- pre = tmp;
- tmp = tmp.parentNode;
-
- }
- }
-
-
- domUtils.breakParent( child, pre || tmp );
- //去掉break后前一个多余的节点 |<[p> ==> |
- var pre = child.previousSibling;
- domUtils.trimWhiteTextNode(pre);
- if(!pre.childNodes.length){
- domUtils.remove(pre);
- }
- //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
-
- if(!browser.ie &&
- (next = child.nextSibling) &&
- domUtils.isBlockElm(next) &&
- next.lastChild &&
- !domUtils.isBr(next.lastChild)){
- next.appendChild(me.document.createElement('br'));
- }
- hadBreak = 1;
- }
- }
- var next = child.nextSibling;
- if(!div.firstChild && next && domUtils.isBlockElm(next)){
-
- range.setStart(next,0).collapse(true);
- break;
- }
- range.setEndAfter( child ).collapse();
-
- }
-
- child = range.startContainer;
-
- if(nextNode && domUtils.isBr(nextNode)){
- domUtils.remove(nextNode)
- }
- //用chrome可能有空白展位符
- if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
- if(nextNode = child.nextSibling){
- domUtils.remove(child);
- if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
-
- range.setStart(nextNode,0).collapse(true).shrinkBoundary()
- }
- }else{
-
- try{
- child.innerHTML = browser.ie ? domUtils.fillChar : ' ';
- }catch(e){
- range.setStartBefore(child);
- domUtils.remove(child)
- }
-
- }
-
- }
- //加上true因为在删除表情等时会删两次,第一次是删的fillData
- try{
- range.select(true);
- }catch(e){}
-
- }
-
-
-
- setTimeout(function(){
- range = me.selection.getRange();
- range.scrollToView(me.autoHeightEnabled,me.autoHeightEnabled ? domUtils.getXY(me.iframe).y:0);
- me.fireEvent('afterinserthtml', html);
- },200);
- }
-};
-
-
-// plugins/autotypeset.js
-/**
- * 自动排版
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。
- * @command autotypeset
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'autotypeset' );
- * ```
- */
-
-UE.plugins['autotypeset'] = function(){
-
- this.setOpt({'autotypeset': {
- mergeEmptyline: true, //合并空行
- removeClass: true, //去掉冗余的class
- removeEmptyline: false, //去掉空行
- textAlign:"left", //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
- imageBlockLine: 'center', //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
- pasteFilter: false, //根据规则过滤没事粘贴进来的内容
- clearFontSize: false, //去掉所有的内嵌字号,使用编辑器默认的字号
- clearFontFamily: false, //去掉所有的内嵌字体,使用编辑器默认的字体
- removeEmptyNode: false, // 去掉空节点
- //可以去掉的标签
- removeTagNames: utils.extend({div:1},dtd.$removeEmpty),
- indent: false, // 行首缩进
- indentValue : '2em', //行首缩进的大小
- bdc2sb: false,
- tobdc: false
- }});
-
- var me = this,
- opt = me.options.autotypeset,
- remainClass = {
- 'selectTdClass':1,
- 'pagebreak':1,
- 'anchorclass':1
- },
- remainTag = {
- 'li':1
- },
- tags = {
- div:1,
- p:1,
- //trace:2183 这些也认为是行
- blockquote:1,center:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,
- span:1
- },
- highlightCont;
- //升级了版本,但配置项目里没有autotypeset
- if(!opt){
- return;
- }
-
- readLocalOpts();
-
- function isLine(node,notEmpty){
- if(!node || node.nodeType == 3)
- return 0;
- if(domUtils.isBr(node))
- return 1;
- if(node && node.parentNode && tags[node.tagName.toLowerCase()]){
- if(highlightCont && highlightCont.contains(node)
- ||
- node.getAttribute('pagebreak')
- ){
- return 0;
- }
-
- return notEmpty ? !domUtils.isEmptyBlock(node) : domUtils.isEmptyBlock(node,new RegExp('[\\s'+domUtils.fillChar
- +']','g'));
- }
- }
-
- function removeNotAttributeSpan(node){
- if(!node.style.cssText){
- domUtils.removeAttributes(node,['style']);
- if(node.tagName.toLowerCase() == 'span' && domUtils.hasNoAttributes(node)){
- domUtils.remove(node,true);
- }
- }
- }
- function autotype(type,html){
-
- var me = this,cont;
- if(html){
- if(!opt.pasteFilter){
- return;
- }
- cont = me.document.createElement('div');
- cont.innerHTML = html.html;
- }else{
- cont = me.document.body;
- }
- var nodes = domUtils.getElementsByTagName(cont,'*');
-
- // 行首缩进,段落方向,段间距,段内间距
- for(var i=0,ci;ci=nodes[i++];){
-
- if(me.fireEvent('excludeNodeinautotype',ci) === true){
- continue;
- }
- //font-size
- if(opt.clearFontSize && ci.style.fontSize){
- domUtils.removeStyle(ci,'font-size');
-
- removeNotAttributeSpan(ci);
-
- }
- //font-family
- if(opt.clearFontFamily && ci.style.fontFamily){
- domUtils.removeStyle(ci,'font-family');
- removeNotAttributeSpan(ci);
- }
-
- if(isLine(ci)){
- //合并空行
- if(opt.mergeEmptyline ){
- var next = ci.nextSibling,tmpNode,isBr = domUtils.isBr(ci);
- while(isLine(next)){
- tmpNode = next;
- next = tmpNode.nextSibling;
- if(isBr && (!next || next && !domUtils.isBr(next))){
- break;
- }
- domUtils.remove(tmpNode);
- }
-
- }
- //去掉空行,保留占位的空行
- if(opt.removeEmptyline && domUtils.inDoc(ci,cont) && !remainTag[ci.parentNode.tagName.toLowerCase()] ){
- if(domUtils.isBr(ci)){
- next = ci.nextSibling;
- if(next && !domUtils.isBr(next)){
- continue;
- }
- }
- domUtils.remove(ci);
- continue;
-
- }
-
- }
- if(isLine(ci,true) && ci.tagName != 'SPAN'){
- if(opt.indent){
- ci.style.textIndent = opt.indentValue;
- }
- if(opt.textAlign){
- ci.style.textAlign = opt.textAlign;
- }
- // if(opt.lineHeight)
- // ci.style.lineHeight = opt.lineHeight + 'cm';
-
- }
-
- //去掉class,保留的class不去掉
- if(opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()]){
-
- if(highlightCont && highlightCont.contains(ci)){
- continue;
- }
- domUtils.removeAttributes(ci,['class']);
- }
-
- //表情不处理
- if(opt.imageBlockLine && ci.tagName.toLowerCase() == 'img' && !ci.getAttribute('emotion')){
- if(html){
- var img = ci;
- switch (opt.imageBlockLine){
- case 'left':
- case 'right':
- case 'none':
- var pN = img.parentNode,tmpNode,pre,next;
- while(dtd.$inline[pN.tagName] || pN.tagName == 'A'){
- pN = pN.parentNode;
- }
- tmpNode = pN;
- if(tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode,'text-align') == 'center'){
- if(!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1){
- pre = tmpNode.previousSibling;
- next = tmpNode.nextSibling;
- if(pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)){
- pre.appendChild(tmpNode.firstChild);
- while(next.firstChild){
- pre.appendChild(next.firstChild);
- }
- domUtils.remove(tmpNode);
- domUtils.remove(next);
- }else{
- domUtils.setStyle(tmpNode,'text-align','');
- }
-
-
- }
-
-
- }
- domUtils.setStyle(img,'float', opt.imageBlockLine);
- break;
- case 'center':
- if(me.queryCommandValue('imagefloat') != 'center'){
- pN = img.parentNode;
- domUtils.setStyle(img,'float','none');
- tmpNode = img;
- while(pN && domUtils.getChildCount(pN,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1
- && (dtd.$inline[pN.tagName] || pN.tagName == 'A')){
- tmpNode = pN;
- pN = pN.parentNode;
- }
- var pNode = me.document.createElement('p');
- domUtils.setAttributes(pNode,{
-
- style:'text-align:center'
- });
- tmpNode.parentNode.insertBefore(pNode,tmpNode);
- pNode.appendChild(tmpNode);
- domUtils.setStyle(tmpNode,'float','');
-
- }
-
-
- }
- } else {
- var range = me.selection.getRange();
- range.selectNode(ci).select();
- me.execCommand('imagefloat', opt.imageBlockLine);
- }
-
- }
-
- //去掉冗余的标签
- if(opt.removeEmptyNode){
- if(opt.removeTagNames[ci.tagName.toLowerCase()] && domUtils.hasNoAttributes(ci) && domUtils.isEmptyBlock(ci)){
- domUtils.remove(ci);
- }
- }
- }
- if(opt.tobdc){
- var root = UE.htmlparser(cont.innerHTML);
- root.traversal(function(node){
- if(node.type == 'text'){
- node.data = ToDBC(node.data)
- }
- });
- cont.innerHTML = root.toHtml()
- }
- if(opt.bdc2sb){
- var root = UE.htmlparser(cont.innerHTML);
- root.traversal(function(node){
- if(node.type == 'text'){
- node.data = DBC2SB(node.data)
- }
- });
- cont.innerHTML = root.toHtml()
- }
- if(html){
- html.html = cont.innerHTML;
- }
- }
- if(opt.pasteFilter){
- me.addListener('beforepaste',autotype);
- }
-
- function DBC2SB(str) {
- var result = '';
- for (var i = 0; i < str.length; i++) {
- var code = str.charCodeAt(i); //获取当前字符的unicode编码
- if (code >= 65281 && code <= 65373)//在这个unicode编码范围中的是所有的英文字母已经各种字符
- {
- result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码
- } else if (code == 12288)//空格
- {
- result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
- } else {
- result += str.charAt(i);
- }
- }
- return result;
- }
- function ToDBC(txtstring) {
- txtstring = utils.html(txtstring);
- var tmp = "";
- var mark = "";/*用于判断,如果是html尖括里的标记,则不进行全角的转换*/
- for (var i = 0; i < txtstring.length; i++) {
- if (txtstring.charCodeAt(i) == 32) {
- tmp = tmp + String.fromCharCode(12288);
- }
- else if (txtstring.charCodeAt(i) < 127) {
- tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248);
- }
- else {
- tmp += txtstring.charAt(i);
- }
- }
- return tmp;
- }
-
- function readLocalOpts() {
- var cookieOpt = me.getPreferences('autotypeset');
- utils.extend(me.options.autotypeset, cookieOpt);
- }
-
- me.commands['autotypeset'] = {
- execCommand:function () {
- me.removeListener('beforepaste',autotype);
- if(opt.pasteFilter){
- me.addListener('beforepaste',autotype);
- }
- autotype.call(me)
- }
-
- };
-
-};
-
-
-
-// plugins/autosubmit.js
-/**
- * 快捷键提交
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 提交表单
- * @command autosubmit
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'autosubmit' );
- * ```
- */
-
-UE.plugin.register('autosubmit',function(){
- return {
- shortcutkey:{
- "autosubmit":"ctrl+13" //手动提交
- },
- commands:{
- 'autosubmit':{
- execCommand:function () {
- var me=this,
- form = domUtils.findParentByTagName(me.iframe,"form", false);
- if (form){
- if(me.fireEvent("beforesubmit")===false){
- return;
- }
- me.sync();
- form.submit();
- }
- }
- }
- }
- }
-});
-
-// plugins/background.js
-/**
- * 背景插件,为UEditor提供设置背景功能
- * @file
- * @since 1.2.6.1
- */
-UE.plugin.register('background', function () {
- var me = this,
- cssRuleId = 'editor_background',
- isSetColored,
- reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i');
-
- function stringToObj(str) {
- var obj = {}, styles = str.split(';');
- utils.each(styles, function (v) {
- var index = v.indexOf(':'),
- key = utils.trim(v.substr(0, index)).toLowerCase();
- key && (obj[key] = utils.trim(v.substr(index + 1) || ''));
- });
- return obj;
- }
-
- function setBackground(obj) {
- if (obj) {
- var styles = [];
- for (var name in obj) {
- if (obj.hasOwnProperty(name)) {
- styles.push(name + ":" + obj[name] + '; ');
- }
- }
- utils.cssRule(cssRuleId, styles.length ? ('body{' + styles.join("") + '}') : '', me.document);
- } else {
- utils.cssRule(cssRuleId, '', me.document)
- }
- }
- //重写editor.hasContent方法
-
- var orgFn = me.hasContents;
- me.hasContents = function(){
- if(me.queryCommandValue('background')){
- return true
- }
- return orgFn.apply(me,arguments);
- };
- return {
- bindEvents: {
- 'getAllHtml': function (type, headHtml) {
- var body = this.body,
- su = domUtils.getComputedStyle(body, "background-image"),
- url = "";
- if (su.indexOf(me.options.imagePath) > 0) {
- url = su.substring(su.indexOf(me.options.imagePath), su.length - 1).replace(/"|\(|\)/ig, "");
- } else {
- url = su != "none" ? su.replace(/url\("?|"?\)/ig, "") : "";
- }
- var html = ' ';
- headHtml.push(html);
- },
- 'aftersetcontent': function () {
- if(isSetColored == false) setBackground();
- }
- },
- inputRule: function (root) {
- isSetColored = false;
- utils.each(root.getNodesByTagName('p'), function (p) {
- var styles = p.getAttr('data-background');
- if (styles) {
- isSetColored = true;
- setBackground(stringToObj(styles));
- p.parentNode.removeChild(p);
- }
- })
- },
- outputRule: function (root) {
- var me = this,
- styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
- if (styles) {
- root.appendChild(UE.uNode.createElement('
'));
- }
- },
- commands: {
- 'background': {
- execCommand: function (cmd, obj) {
- setBackground(obj);
- },
- queryCommandValue: function () {
- var me = this,
- styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
- return styles ? stringToObj(styles[1]) : null;
- },
- notNeedUndo: true
- }
- }
- }
-});
-
-// plugins/image.js
-/**
- * 图片插入、排版插件
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 图片对齐方式
- * @command imagefloat
- * @method execCommand
- * @remind 值center为独占一行居中
- * @param { String } cmd 命令字符串
- * @param { String } align 对齐方式,可传left、right、none、center
- * @remaind center表示图片独占一行
- * @example
- * ```javascript
- * editor.execCommand( 'imagefloat', 'center' );
- * ```
- */
-
-/**
- * 如果选区所在位置是图片区域
- * @command imagefloat
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回图片对齐方式
- * @example
- * ```javascript
- * editor.queryCommandValue( 'imagefloat' );
- * ```
- */
-
-UE.commands['imagefloat'] = {
- execCommand:function (cmd, align) {
- var me = this,
- range = me.selection.getRange();
- if (!range.collapsed) {
- var img = range.getClosedNode();
- if (img && img.tagName == 'IMG') {
- switch (align) {
- case 'left':
- case 'right':
- case 'none':
- var pN = img.parentNode, tmpNode, pre, next;
- while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
- pN = pN.parentNode;
- }
- tmpNode = pN;
- if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
- if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) {
- return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
- }) == 1) {
- pre = tmpNode.previousSibling;
- next = tmpNode.nextSibling;
- if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) {
- pre.appendChild(tmpNode.firstChild);
- while (next.firstChild) {
- pre.appendChild(next.firstChild);
- }
- domUtils.remove(tmpNode);
- domUtils.remove(next);
- } else {
- domUtils.setStyle(tmpNode, 'text-align', '');
- }
-
-
- }
-
- range.selectNode(img).select();
- }
- domUtils.setStyle(img, 'float', align == 'none' ? '' : align);
- if(align == 'none'){
- domUtils.removeAttributes(img,'align');
- }
-
- break;
- case 'center':
- if (me.queryCommandValue('imagefloat') != 'center') {
- pN = img.parentNode;
- domUtils.setStyle(img, 'float', '');
- domUtils.removeAttributes(img,'align');
- tmpNode = img;
- while (pN && domUtils.getChildCount(pN, function (node) {
- return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
- }) == 1
- && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) {
- tmpNode = pN;
- pN = pN.parentNode;
- }
- range.setStartBefore(tmpNode).setCursor(false);
- pN = me.document.createElement('div');
- pN.appendChild(tmpNode);
- domUtils.setStyle(tmpNode, 'float', '');
-
- me.execCommand('insertHtml', '' + pN.innerHTML + ' ');
-
- tmpNode = me.document.getElementById('_img_parent_tmp');
- tmpNode.removeAttribute('id');
- tmpNode = tmpNode.firstChild;
- range.selectNode(tmpNode).select();
- //去掉后边多余的元素
- next = tmpNode.parentNode.nextSibling;
- if (next && domUtils.isEmptyNode(next)) {
- domUtils.remove(next);
- }
-
- }
-
- break;
- }
-
- }
- }
- },
- queryCommandValue:function () {
- var range = this.selection.getRange(),
- startNode, floatStyle;
- if (range.collapsed) {
- return 'none';
- }
- startNode = range.getClosedNode();
- if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
- floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align');
-
- if (floatStyle == 'none') {
- floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle;
- }
- return {
- left:1,
- right:1,
- center:1
- }[floatStyle] ? floatStyle : 'none';
- }
- return 'none';
-
-
- },
- queryCommandState:function () {
- var range = this.selection.getRange(),
- startNode;
-
- if (range.collapsed) return -1;
-
- startNode = range.getClosedNode();
- if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
- return 0;
- }
- return -1;
- }
-};
-
-
-/**
- * 插入图片
- * @command insertimage
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
- * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
- * 此时数组的每一个元素都是一个Object类型的图片属性集合。
- * @example
- * ```javascript
- * editor.execCommand( 'insertimage', {
- * src:'a/b/c.jpg',
- * width:'100',
- * height:'100'
- * } );
- * ```
- * @example
- * ```javascript
- * editor.execCommand( 'insertimage', [{
- * src:'a/b/c.jpg',
- * width:'100',
- * height:'100'
- * },{
- * src:'a/b/d.jpg',
- * width:'100',
- * height:'100'
- * }] );
- * ```
- */
-
-UE.commands['insertimage'] = {
- execCommand:function (cmd, opt) {
-
- opt = utils.isArray(opt) ? opt : [opt];
- if (!opt.length) {
- return;
- }
- var me = this,
- range = me.selection.getRange(),
- img = range.getClosedNode();
-
- if(me.fireEvent('beforeinsertimage', opt) === true){
- return;
- }
-
- if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1) && !img.getAttribute("word_img")) {
- var first = opt.shift();
- var floatStyle = first['floatStyle'];
- delete first['floatStyle'];
-//// img.style.border = (first.border||0) +"px solid #000";
-//// img.style.margin = (first.margin||0) +"px";
-// img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
- domUtils.setAttributes(img, first);
- me.execCommand('imagefloat', floatStyle);
- if (opt.length > 0) {
- range.setStartAfter(img).setCursor(false, true);
- me.execCommand('insertimage', opt);
- }
-
- } else {
- var html = [], str = '', ci;
- ci = opt[0];
- if (opt.length == 1) {
- str = '';
- if (ci['floatStyle'] == 'center') {
- str = '' + str + ' ';
- }
- html.push(str);
-
- } else {
- for (var i = 0; ci = opt[i++];) {
- str = '';
- html.push(str);
- }
- }
-
- me.execCommand('insertHtml', html.join(''));
- }
-
- me.fireEvent('afterinsertimage', opt)
- }
-};
-
-// plugins/justify.js
-/**
- * 段落格式
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 段落对齐方式
- * @command justify
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
- * @example
- * ```javascript
- * editor.execCommand( 'justify', 'center' );
- * ```
- */
-/**
- * 如果选区所在位置是段落区域,返回当前段落对齐方式
- * @command justify
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回段落对齐方式
- * @example
- * ```javascript
- * editor.queryCommandValue( 'justify' );
- * ```
- */
-
-UE.plugins['justify']=function(){
- var me=this,
- block = domUtils.isBlockElm,
- defaultValue = {
- left:1,
- right:1,
- center:1,
- justify:1
- },
- doJustify = function (range, style) {
- var bookmark = range.createBookmark(),
- filterFn = function (node) {
- return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
- };
-
- range.enlarge(true);
- var bookmark2 = range.createBookmark(),
- current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
- tmpRange = range.cloneRange(),
- tmpNode;
- while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
- if (current.nodeType == 3 || !block(current)) {
- tmpRange.setStartBefore(current);
- while (current && current !== bookmark2.end && !block(current)) {
- tmpNode = current;
- current = domUtils.getNextDomNode(current, false, null, function (node) {
- return !block(node);
- });
- }
- tmpRange.setEndAfter(tmpNode);
- var common = tmpRange.getCommonAncestor();
- if (!domUtils.isBody(common) && block(common)) {
- domUtils.setStyles(common, utils.isString(style) ? {'text-align':style} : style);
- current = common;
- } else {
- var p = range.document.createElement('p');
- domUtils.setStyles(p, utils.isString(style) ? {'text-align':style} : style);
- var frag = tmpRange.extractContents();
- p.appendChild(frag);
- tmpRange.insertNode(p);
- current = p;
- }
- current = domUtils.getNextDomNode(current, false, filterFn);
- } else {
- current = domUtils.getNextDomNode(current, true, filterFn);
- }
- }
- return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
- };
-
- UE.commands['justify'] = {
- execCommand:function (cmdName, align) {
- var range = this.selection.getRange(),
- txt;
-
- //闭合时单独处理
- if (range.collapsed) {
- txt = this.document.createTextNode('p');
- range.insertNode(txt);
- }
- doJustify(range, align);
- if (txt) {
- range.setStartBefore(txt).collapse(true);
- domUtils.remove(txt);
- }
-
- range.select();
-
-
- return true;
- },
- queryCommandValue:function () {
- var startNode = this.selection.getStart(),
- value = domUtils.getComputedStyle(startNode, 'text-align');
- return defaultValue[value] ? value : 'left';
- },
- queryCommandState:function () {
- var start = this.selection.getStart(),
- cell = start && domUtils.findParentByTagName(start, ["td", "th","caption"], true);
-
- return cell? -1:0;
- }
-
- };
-};
-
-
-// plugins/font.js
-/**
- * 字体颜色,背景色,字号,字体,下划线,删除线
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 字体颜色
- * @command forecolor
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } value 色值(必须十六进制)
- * @example
- * ```javascript
- * editor.execCommand( 'forecolor', '#000' );
- * ```
- */
-/**
- * 返回选区字体颜色
- * @command forecolor
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回字体颜色
- * @example
- * ```javascript
- * editor.queryCommandValue( 'forecolor' );
- * ```
- */
-
-/**
- * 字体背景颜色
- * @command backcolor
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } value 色值(必须十六进制)
- * @example
- * ```javascript
- * editor.execCommand( 'backcolor', '#000' );
- * ```
- */
-/**
- * 返回选区字体颜色
- * @command backcolor
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回字体背景颜色
- * @example
- * ```javascript
- * editor.queryCommandValue( 'backcolor' );
- * ```
- */
-
-/**
- * 字体大小
- * @command fontsize
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } value 字体大小
- * @example
- * ```javascript
- * editor.execCommand( 'fontsize', '14px' );
- * ```
- */
-/**
- * 返回选区字体大小
- * @command fontsize
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回字体大小
- * @example
- * ```javascript
- * editor.queryCommandValue( 'fontsize' );
- * ```
- */
-
-/**
- * 字体样式
- * @command fontfamily
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } value 字体样式
- * @example
- * ```javascript
- * editor.execCommand( 'fontfamily', '微软雅黑' );
- * ```
- */
-/**
- * 返回选区字体样式
- * @command fontfamily
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回字体样式
- * @example
- * ```javascript
- * editor.queryCommandValue( 'fontfamily' );
- * ```
- */
-
-/**
- * 字体下划线,与删除线互斥
- * @command underline
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'underline' );
- * ```
- */
-
-/**
- * 字体删除线,与下划线互斥
- * @command strikethrough
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'strikethrough' );
- * ```
- */
-
-/**
- * 字体边框
- * @command fontborder
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'fontborder' );
- * ```
- */
-
-UE.plugins['font'] = function () {
- var me = this,
- fonts = {
- 'forecolor': 'color',
- 'backcolor': 'background-color',
- 'fontsize': 'font-size',
- 'fontfamily': 'font-family',
- 'underline': 'text-decoration',
- 'strikethrough': 'text-decoration',
- 'fontborder': 'border'
- },
- needCmd = {'underline': 1, 'strikethrough': 1, 'fontborder': 1},
- needSetChild = {
- 'forecolor': 'color',
- 'backcolor': 'background-color',
- 'fontsize': 'font-size',
- 'fontfamily': 'font-family'
-
- };
- me.setOpt({
- 'fontfamily': [
- { name: 'songti', val: '宋体,SimSun'},
- { name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
- { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
- { name: 'heiti', val: '黑体, SimHei'},
- { name: 'lishu', val: '隶书, SimLi'},
- { name: 'andaleMono', val: 'andale mono'},
- { name: 'arial', val: 'arial, helvetica,sans-serif'},
- { name: 'arialBlack', val: 'arial black,avant garde'},
- { name: 'comicSansMs', val: 'comic sans ms'},
- { name: 'impact', val: 'impact,chicago'},
- { name: 'timesNewRoman', val: 'times new roman'}
- ],
- 'fontsize': [10, 11, 12, 14, 16, 18, 20, 24, 36]
- });
-
- function mergeWithParent(node){
- var parent;
- while(parent = node.parentNode){
- if(parent.tagName == 'SPAN' && domUtils.getChildCount(parent,function(child){
- return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
- }) == 1) {
- parent.style.cssText += node.style.cssText;
- domUtils.remove(node,true);
- node = parent;
-
- }else{
- break;
- }
- }
-
- }
- function mergeChild(rng,cmdName,value){
- if(needSetChild[cmdName]){
- rng.adjustmentBoundary();
- if(!rng.collapsed && rng.startContainer.nodeType == 1){
- var start = rng.startContainer.childNodes[rng.startOffset];
- if(start && domUtils.isTagNode(start,'span')){
- var bk = rng.createBookmark();
- utils.each(domUtils.getElementsByTagName(start, 'span'), function (span) {
- if (!span.parentNode || domUtils.isBookmarkNode(span))return;
- if(cmdName == 'backcolor' && domUtils.getComputedStyle(span,'background-color').toLowerCase() === value){
- return;
- }
- domUtils.removeStyle(span,needSetChild[cmdName]);
- if(span.style.cssText.replace(/^\s+$/,'').length == 0){
- domUtils.remove(span,true)
- }
- });
- rng.moveToBookmark(bk)
- }
- }
- }
-
- }
- function mergesibling(rng,cmdName,value) {
- var collapsed = rng.collapsed,
- bk = rng.createBookmark(), common;
- if (collapsed) {
- common = bk.start.parentNode;
- while (dtd.$inline[common.tagName]) {
- common = common.parentNode;
- }
- } else {
- common = domUtils.getCommonAncestor(bk.start, bk.end);
- }
- utils.each(domUtils.getElementsByTagName(common, 'span'), function (span) {
- if (!span.parentNode || domUtils.isBookmarkNode(span))return;
- if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
- if(/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)){
- domUtils.remove(span, true);
- }else{
- domUtils.removeStyle(span,'border');
- }
- return
- }
- if (/border/i.test(span.style.cssText) && span.parentNode.tagName == 'SPAN' && /border/i.test(span.parentNode.style.cssText)) {
- span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, '');
- }
- if(!(cmdName=='fontborder' && value=='none')){
- var next = span.nextSibling;
- while (next && next.nodeType == 1 && next.tagName == 'SPAN' ) {
- if(domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
- span.appendChild(next);
- next = span.nextSibling;
- continue;
- }
- if (next.style.cssText == span.style.cssText) {
- domUtils.moveChild(next, span);
- domUtils.remove(next);
- }
- if (span.nextSibling === next)
- break;
- next = span.nextSibling;
- }
- }
-
-
- mergeWithParent(span);
- if(browser.ie && browser.version > 8 ){
- //拷贝父亲们的特别的属性,这里只做背景颜色的处理
- var parent = domUtils.findParent(span,function(n){return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)});
- if(parent && !/background-color/.test(span.style.cssText)){
- span.style.backgroundColor = parent.style.backgroundColor;
- }
- }
-
- });
- rng.moveToBookmark(bk);
- mergeChild(rng,cmdName,value)
- }
-
- me.addInputRule(function (root) {
- utils.each(root.getNodesByTagName('u s del font strike'), function (node) {
- if (node.tagName == 'font') {
- var cssStyle = [];
- for (var p in node.attrs) {
- switch (p) {
- case 'size':
- cssStyle.push('font-size:' +
- ({
- '1':'10',
- '2':'12',
- '3':'16',
- '4':'18',
- '5':'24',
- '6':'32',
- '7':'48'
- }[node.attrs[p]] || node.attrs[p]) + 'px');
- break;
- case 'color':
- cssStyle.push('color:' + node.attrs[p]);
- break;
- case 'face':
- cssStyle.push('font-family:' + node.attrs[p]);
- break;
- case 'style':
- cssStyle.push(node.attrs[p]);
- }
- }
- node.attrs = {
- 'style': cssStyle.join(';')
- };
- } else {
- var val = node.tagName == 'u' ? 'underline' : 'line-through';
- node.attrs = {
- 'style': (node.getAttr('style') || '') + 'text-decoration:' + val + ';'
- }
- }
- node.tagName = 'span';
- });
-// utils.each(root.getNodesByTagName('span'), function (node) {
-// var val;
-// if(val = node.getAttr('class')){
-// if(/fontstrikethrough/.test(val)){
-// node.setStyle('text-decoration','line-through');
-// if(node.attrs['class']){
-// node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
-// }else{
-// node.setAttr('class')
-// }
-// }
-// if(/fontborder/.test(val)){
-// node.setStyle('border','1px solid #000');
-// if(node.attrs['class']){
-// node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
-// }else{
-// node.setAttr('class')
-// }
-// }
-// }
-// });
- });
-// me.addOutputRule(function(root){
-// utils.each(root.getNodesByTagName('span'), function (node) {
-// var val;
-// if(val = node.getStyle('text-decoration')){
-// if(/line-through/.test(val)){
-// if(node.attrs['class']){
-// node.attrs['class'] += ' fontstrikethrough';
-// }else{
-// node.setAttr('class','fontstrikethrough')
-// }
-// }
-//
-// node.setStyle('text-decoration')
-// }
-// if(val = node.getStyle('border')){
-// if(/1px/.test(val) && /solid/.test(val)){
-// if(node.attrs['class']){
-// node.attrs['class'] += ' fontborder';
-//
-// }else{
-// node.setAttr('class','fontborder')
-// }
-// }
-// node.setStyle('border')
-//
-// }
-// });
-// });
- for (var p in fonts) {
- (function (cmd, style) {
- UE.commands[cmd] = {
- execCommand: function (cmdName, value) {
- value = value || (this.queryCommandState(cmdName) ? 'none' : cmdName == 'underline' ? 'underline' :
- cmdName == 'fontborder' ? '1px solid #000' :
- 'line-through');
- var me = this,
- range = this.selection.getRange(),
- text;
-
- if (value == 'default') {
-
- if (range.collapsed) {
- text = me.document.createTextNode('font');
- range.insertNode(text).select();
-
- }
- me.execCommand('removeFormat', 'span,a', style);
- if (text) {
- range.setStartBefore(text).collapse(true);
- domUtils.remove(text);
- }
- mergesibling(range,cmdName,value);
- range.select()
- } else {
- if (!range.collapsed) {
- if (needCmd[cmd] && me.queryCommandValue(cmd)) {
- me.execCommand('removeFormat', 'span,a', style);
- }
- range = me.selection.getRange();
-
- range.applyInlineStyle('span', {'style': style + ':' + value});
- mergesibling(range, cmdName,value);
- range.select();
- } else {
-
- var span = domUtils.findParentByTagName(range.startContainer, 'span', true);
- text = me.document.createTextNode('font');
- if (span && !span.children.length && !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length) {
- //for ie hack when enter
- range.insertNode(text);
- if (needCmd[cmd]) {
- range.selectNode(text).select();
- me.execCommand('removeFormat', 'span,a', style, null);
-
- span = domUtils.findParentByTagName(text, 'span', true);
- range.setStartBefore(text);
-
- }
- span && (span.style.cssText += ';' + style + ':' + value);
- range.collapse(true).select();
-
-
- } else {
- range.insertNode(text);
- range.selectNode(text).select();
- span = range.document.createElement('span');
-
- if (needCmd[cmd]) {
- //a标签内的不处理跳过
- if (domUtils.findParentByTagName(text, 'a', true)) {
- range.setStartBefore(text).setCursor();
- domUtils.remove(text);
- return;
- }
- me.execCommand('removeFormat', 'span,a', style);
- }
-
- span.style.cssText = style + ':' + value;
-
-
- text.parentNode.insertBefore(span, text);
- //修复,span套span 但样式不继承的问题
- if (!browser.ie || browser.ie && browser.version == 9) {
- var spanParent = span.parentNode;
- while (!domUtils.isBlockElm(spanParent)) {
- if (spanParent.tagName == 'SPAN') {
- //opera合并style不会加入";"
- span.style.cssText = spanParent.style.cssText + ";" + span.style.cssText;
- }
- spanParent = spanParent.parentNode;
- }
- }
-
-
- if (opera) {
- setTimeout(function () {
- range.setStart(span, 0).collapse(true);
- mergesibling(range, cmdName,value);
- range.select();
- });
- } else {
- range.setStart(span, 0).collapse(true);
- mergesibling(range,cmdName,value);
- range.select();
- }
-
- //trace:981
- //domUtils.mergeToParent(span)
- }
- domUtils.remove(text);
- }
-
-
- }
- return true;
- },
- queryCommandValue: function (cmdName) {
- var startNode = this.selection.getStart();
-
- //trace:946
- if (cmdName == 'underline' || cmdName == 'strikethrough') {
- var tmpNode = startNode, value;
- while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) {
- if (tmpNode.nodeType == 1) {
- value = domUtils.getComputedStyle(tmpNode, style);
- if (value != 'none') {
- return value;
- }
- }
-
- tmpNode = tmpNode.parentNode;
- }
- return 'none';
- }
- if (cmdName == 'fontborder') {
- var tmp = startNode, val;
- while (tmp && dtd.$inline[tmp.tagName]) {
- if (val = domUtils.getComputedStyle(tmp, 'border')) {
-
- if (/1px/.test(val) && /solid/.test(val)) {
- return val;
- }
- }
- tmp = tmp.parentNode;
- }
- return ''
- }
-
- if( cmdName == 'FontSize' ) {
- var styleVal = domUtils.getComputedStyle(startNode, style),
- tmp = /^([\d\.]+)(\w+)$/.exec( styleVal );
-
- if( tmp ) {
-
- return Math.floor( tmp[1] ) + tmp[2];
-
- }
-
- return styleVal;
-
- }
-
- return domUtils.getComputedStyle(startNode, style);
- },
- queryCommandState: function (cmdName) {
- if (!needCmd[cmdName])
- return 0;
- var val = this.queryCommandValue(cmdName);
- if (cmdName == 'fontborder') {
- return /1px/.test(val) && /solid/.test(val)
- } else {
- return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val);
-
- }
-
- }
- };
- })(p, fonts[p]);
- }
-};
-
-// plugins/link.js
-/**
- * 超链接
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 插入超链接
- * @command link
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { Object } options 设置自定义属性,例如:url、title、target
- * @example
- * ```javascript
- * editor.execCommand( 'link', '{
- * url:'ueditor.baidu.com',
- * title:'ueditor',
- * target:'_blank'
- * }' );
- * ```
- */
-/**
- * 返回当前选中的第一个超链接节点
- * @command link
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { Element } 超链接节点
- * @example
- * ```javascript
- * editor.queryCommandValue( 'link' );
- * ```
- */
-
-/**
- * 取消超链接
- * @command unlink
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'unlink');
- * ```
- */
-
-UE.plugins['link'] = function(){
- function optimize( range ) {
- var start = range.startContainer,end = range.endContainer;
-
- if ( start = domUtils.findParentByTagName( start, 'a', true ) ) {
- range.setStartBefore( start );
- }
- if ( end = domUtils.findParentByTagName( end, 'a', true ) ) {
- range.setEndAfter( end );
- }
- }
-
-
- UE.commands['unlink'] = {
- execCommand : function() {
- var range = this.selection.getRange(),
- bookmark;
- if(range.collapsed && !domUtils.findParentByTagName( range.startContainer, 'a', true )){
- return;
- }
- bookmark = range.createBookmark();
- optimize( range );
- range.removeInlineStyle( 'a' ).moveToBookmark( bookmark ).select();
- },
- queryCommandState : function(){
- return !this.highlight && this.queryCommandValue('link') ? 0 : -1;
- }
-
- };
- function doLink(range,opt,me){
- var rngClone = range.cloneRange(),
- link = me.queryCommandValue('link');
- optimize( range = range.adjustmentBoundary() );
- var start = range.startContainer;
- if(start.nodeType == 1 && link){
- start = start.childNodes[range.startOffset];
- if(start && start.nodeType == 1 && start.tagName == 'A' && /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie?'innerText':'textContent'])){
- start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue||opt.href);
-
- }
- }
- if( !rngClone.collapsed || link){
- range.removeInlineStyle( 'a' );
- rngClone = range.cloneRange();
- }
-
- if ( rngClone.collapsed ) {
- var a = range.document.createElement( 'a'),
- text = '';
- if(opt.textValue){
-
- text = utils.html(opt.textValue);
- delete opt.textValue;
- }else{
- text = utils.html(opt.href);
-
- }
- domUtils.setAttributes( a, opt );
- start = domUtils.findParentByTagName( rngClone.startContainer, 'a', true );
- if(start && domUtils.isInNodeEndBoundary(rngClone,start)){
- range.setStartAfter(start).collapse(true);
-
- }
- a[browser.ie ? 'innerText' : 'textContent'] = text;
- range.insertNode(a).selectNode( a );
- } else {
- range.applyInlineStyle( 'a', opt );
-
- }
- }
- UE.commands['link'] = {
- execCommand : function( cmdName, opt ) {
- var range;
- opt._href && (opt._href = utils.unhtml(opt._href,/[<">]/g));
- opt.href && (opt.href = utils.unhtml(opt.href,/[<">]/g));
- opt.textValue && (opt.textValue = utils.unhtml(opt.textValue,/[<">]/g));
- doLink(range=this.selection.getRange(),opt,this);
- //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
- range.collapse().select(true);
-
- },
- queryCommandValue : function() {
- var range = this.selection.getRange(),
- node;
- if ( range.collapsed ) {
-// node = this.selection.getStart();
- //在ie下getstart()取值偏上了
- node = range.startContainer;
- node = node.nodeType == 1 ? node : node.parentNode;
-
- if ( node && (node = domUtils.findParentByTagName( node, 'a', true )) && ! domUtils.isInNodeEndBoundary(range,node)) {
-
- return node;
- }
- } else {
- //trace:1111 如果是xx startContainer是p就会找不到a
- range.shrinkBoundary();
- var start = range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset] ? range.startContainer : range.startContainer.childNodes[range.startOffset],
- end = range.endContainer.nodeType == 3 || range.endOffset == 0 ? range.endContainer : range.endContainer.childNodes[range.endOffset-1],
- common = range.getCommonAncestor();
- node = domUtils.findParentByTagName( common, 'a', true );
- if ( !node && common.nodeType == 1){
-
- var as = common.getElementsByTagName( 'a' ),
- ps,pe;
-
- for ( var i = 0,ci; ci = as[i++]; ) {
- ps = domUtils.getPosition( ci, start ),pe = domUtils.getPosition( ci,end);
- if ( (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
- &&
- (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
- ) {
- node = ci;
- break;
- }
- }
- }
- return node;
- }
-
- },
- queryCommandState : function() {
- //判断如果是视频的话连接不可用
- //fix 853
- var img = this.selection.getRange().getClosedNode(),
- flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1);
- return flag ? -1 : 0;
- }
- };
-};
-
-// plugins/iframe.js
-///import core
-///import plugins\inserthtml.js
-///commands 插入框架
-///commandsName InsertFrame
-///commandsTitle 插入Iframe
-///commandsDialog dialogs\insertframe
-
-UE.plugins['insertframe'] = function() {
- var me =this;
- function deleteIframe(){
- me._iframe && delete me._iframe;
- }
-
- me.addListener("selectionchange",function(){
- deleteIframe();
- });
-
-};
-
-
-
-// plugins/scrawl.js
-///import core
-///commands 涂鸦
-///commandsName Scrawl
-///commandsTitle 涂鸦
-///commandsDialog dialogs\scrawl
-UE.commands['scrawl'] = {
- queryCommandState : function(){
- return ( browser.ie && browser.version <= 8 ) ? -1 :0;
- }
-};
-
-
-// plugins/removeformat.js
-/**
- * 清除格式
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 清除文字样式
- * @command removeformat
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param {String} tags 以逗号隔开的标签。如:strong
- * @param {String} style 样式如:color
- * @param {String} attrs 属性如:width
- * @example
- * ```javascript
- * editor.execCommand( 'removeformat', 'strong','color','width' );
- * ```
- */
-
-UE.plugins['removeformat'] = function(){
- var me = this;
- me.setOpt({
- 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
- 'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
- });
- me.commands['removeformat'] = {
- execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
-
- var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
- removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
- range = new dom.Range( this.document ),
- bookmark,node,parent,
- filter = function( node ) {
- return node.nodeType == 1;
- };
-
- function isRedundantSpan (node) {
- if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
- return 0;
- }
- if (browser.ie) {
- //ie 下判断实效,所以只能简单用style来判断
- //return node.style.cssText == '' ? 1 : 0;
- var attrs = node.attributes;
- if ( attrs.length ) {
- for ( var i = 0,l = attrs.length; i
- var node = range.startContainer,
- tmp,
- collapsed = range.collapsed;
- while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
- tmp = node.parentNode;
- range.setStartBefore(node);
- //trace:937
- //更新结束边界
- if(range.startContainer === range.endContainer){
- range.endOffset--;
- }
- domUtils.remove(node);
- node = tmp;
- }
-
- if(!collapsed){
- node = range.endContainer;
- while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
- tmp = node.parentNode;
- range.setEndBefore(node);
- domUtils.remove(node);
-
- node = tmp;
- }
-
-
- }
- }
-
-
-
- range = this.selection.getRange();
- doRemove( range );
- range.select();
-
- }
-
- };
-
-};
-
-
-// plugins/blockquote.js
-/**
- * 添加引用
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 添加引用
- * @command blockquote
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'blockquote' );
- * ```
- */
-
-/**
- * 添加引用
- * @command blockquote
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { Object } attrs 节点属性
- * @example
- * ```javascript
- * editor.execCommand( 'blockquote',{
- * style: "color: red;"
- * } );
- * ```
- */
-
-
-UE.plugins['blockquote'] = function(){
- var me = this;
- function getObj(editor){
- return domUtils.filterNodeList(editor.selection.getStartElementPath(),'blockquote');
- }
- me.commands['blockquote'] = {
- execCommand : function( cmdName, attrs ) {
- var range = this.selection.getRange(),
- obj = getObj(this),
- blockquote = dtd.blockquote,
- bookmark = range.createBookmark();
-
- if ( obj ) {
-
- var start = range.startContainer,
- startBlock = domUtils.isBlockElm(start) ? start : domUtils.findParent(start,function(node){return domUtils.isBlockElm(node)}),
-
- end = range.endContainer,
- endBlock = domUtils.isBlockElm(end) ? end : domUtils.findParent(end,function(node){return domUtils.isBlockElm(node)});
-
- //处理一下li
- startBlock = domUtils.findParentByTagName(startBlock,'li',true) || startBlock;
- endBlock = domUtils.findParentByTagName(endBlock,'li',true) || endBlock;
-
-
- if(startBlock.tagName == 'LI' || startBlock.tagName == 'TD' || startBlock === obj || domUtils.isBody(startBlock)){
- domUtils.remove(obj,true);
- }else{
- domUtils.breakParent(startBlock,obj);
- }
-
- if(startBlock !== endBlock){
- obj = domUtils.findParentByTagName(endBlock,'blockquote');
- if(obj){
- if(endBlock.tagName == 'LI' || endBlock.tagName == 'TD'|| domUtils.isBody(endBlock)){
- obj.parentNode && domUtils.remove(obj,true);
- }else{
- domUtils.breakParent(endBlock,obj);
- }
-
- }
- }
-
- var blockquotes = domUtils.getElementsByTagName(this.document,'blockquote');
- for(var i=0,bi;bi=blockquotes[i++];){
- if(!bi.childNodes.length){
- domUtils.remove(bi);
- }else if(domUtils.getPosition(bi,startBlock)&domUtils.POSITION_FOLLOWING && domUtils.getPosition(bi,endBlock)&domUtils.POSITION_PRECEDING){
- domUtils.remove(bi,true);
- }
- }
-
-
-
-
- } else {
-
- var tmpRange = range.cloneRange(),
- node = tmpRange.startContainer.nodeType == 1 ? tmpRange.startContainer : tmpRange.startContainer.parentNode,
- preNode = node,
- doEnd = 1;
-
- //调整开始
- while ( 1 ) {
- if ( domUtils.isBody(node) ) {
- if ( preNode !== node ) {
- if ( range.collapsed ) {
- tmpRange.selectNode( preNode );
- doEnd = 0;
- } else {
- tmpRange.setStartBefore( preNode );
- }
- }else{
- tmpRange.setStart(node,0);
- }
-
- break;
- }
- if ( !blockquote[node.tagName] ) {
- if ( range.collapsed ) {
- tmpRange.selectNode( preNode );
- } else{
- tmpRange.setStartBefore( preNode);
- }
- break;
- }
-
- preNode = node;
- node = node.parentNode;
- }
-
- //调整结束
- if ( doEnd ) {
- preNode = node = node = tmpRange.endContainer.nodeType == 1 ? tmpRange.endContainer : tmpRange.endContainer.parentNode;
- while ( 1 ) {
-
- if ( domUtils.isBody( node ) ) {
- if ( preNode !== node ) {
-
- tmpRange.setEndAfter( preNode );
-
- } else {
- tmpRange.setEnd( node, node.childNodes.length );
- }
-
- break;
- }
- if ( !blockquote[node.tagName] ) {
- tmpRange.setEndAfter( preNode );
- break;
- }
-
- preNode = node;
- node = node.parentNode;
- }
-
- }
-
-
- node = range.document.createElement( 'blockquote' );
- domUtils.setAttributes( node, attrs );
- node.appendChild( tmpRange.extractContents() );
- tmpRange.insertNode( node );
- //去除重复的
- var childs = domUtils.getElementsByTagName(node,'blockquote');
- for(var i=0,ci;ci=childs[i++];){
- if(ci.parentNode){
- domUtils.remove(ci,true);
- }
- }
-
- }
- range.moveToBookmark( bookmark ).select();
- },
- queryCommandState : function() {
- return getObj(this) ? 1 : 0;
- }
- };
-};
-
-
-
-// plugins/convertcase.js
-/**
- * 大小写转换
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 把选区内文本变大写,与“tolowercase”命令互斥
- * @command touppercase
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'touppercase' );
- * ```
- */
-
-/**
- * 把选区内文本变小写,与“touppercase”命令互斥
- * @command tolowercase
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'tolowercase' );
- * ```
- */
-UE.commands['touppercase'] =
-UE.commands['tolowercase'] = {
- execCommand:function (cmd) {
- var me = this;
- var rng = me.selection.getRange();
- if(rng.collapsed){
- return rng;
- }
- var bk = rng.createBookmark(),
- bkEnd = bk.end,
- filterFn = function( node ) {
- return !domUtils.isBr(node) && !domUtils.isWhitespace( node );
- },
- curNode = domUtils.getNextDomNode( bk.start, false, filterFn );
- while ( curNode && (domUtils.getPosition( curNode, bkEnd ) & domUtils.POSITION_PRECEDING) ) {
-
- if ( curNode.nodeType == 3 ) {
- curNode.nodeValue = curNode.nodeValue[cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase']();
- }
- curNode = domUtils.getNextDomNode( curNode, true, filterFn );
- if(curNode === bkEnd){
- break;
- }
-
- }
- rng.moveToBookmark(bk).select();
- }
-};
-
-
-
-// plugins/indent.js
-/**
- * 首行缩进
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 缩进
- * @command indent
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'indent' );
- * ```
- */
-UE.commands['indent'] = {
- execCommand : function() {
- var me = this,value = me.queryCommandState("indent") ? "0em" : (me.options.indentValue || '2em');
- me.execCommand('Paragraph','p',{style:'text-indent:'+ value});
- },
- queryCommandState : function() {
- var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6');
- return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0;
- }
-
-};
-
-
-// plugins/print.js
-/**
- * 打印
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 打印
- * @command print
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'print' );
- * ```
- */
-UE.commands['print'] = {
- execCommand : function(){
- this.window.print();
- },
- notNeedUndo : 1
-};
-
-
-
-// plugins/preview.js
-/**
- * 预览
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 预览
- * @command preview
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'preview' );
- * ```
- */
-UE.commands['preview'] = {
- execCommand : function(){
- var w = window.open('', '_blank', ''),
- d = w.document;
- d.open();
- d.write(''+this.getContent(null,null,true)+' ');
- d.close();
- },
- notNeedUndo : 1
-};
-
-
-// plugins/selectall.js
-/**
- * 全选
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 选中所有内容
- * @command selectall
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'selectall' );
- * ```
- */
-UE.plugins['selectall'] = function(){
- var me = this;
- me.commands['selectall'] = {
- execCommand : function(){
- //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
- var me = this,body = me.body,
- range = me.selection.getRange();
- range.selectNodeContents(body);
- if(domUtils.isEmptyBlock(body)){
- //opera不能自动合并到元素的里边,要手动处理一下
- if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
- range.setStartAtFirst(body.firstChild);
- }
- range.collapse(true);
- }
- range.select(true);
- },
- notNeedUndo : 1
- };
-
-
- //快捷键
- me.addshortcutkey({
- "selectAll" : "ctrl+65"
- });
-};
-
-
-// plugins/paragraph.js
-/**
- * 段落样式
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 段落格式
- * @command paragraph
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
- * @param {Object} attrs 标签的属性
- * @example
- * ```javascript
- * editor.execCommand( 'Paragraph','h1','{
- * class:'test'
- * }' );
- * ```
- */
-
-/**
- * 返回选区内节点标签名
- * @command paragraph
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 节点标签名
- * @example
- * ```javascript
- * editor.queryCommandValue( 'Paragraph' );
- * ```
- */
-
-UE.plugins['paragraph'] = function() {
- var me = this,
- block = domUtils.isBlockElm,
- notExchange = ['TD','LI','PRE'],
-
- doParagraph = function(range,style,attrs,sourceCmdName){
- var bookmark = range.createBookmark(),
- filterFn = function( node ) {
- return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace( node );
- },
- para;
-
- range.enlarge( true );
- var bookmark2 = range.createBookmark(),
- current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ),
- tmpRange = range.cloneRange(),
- tmpNode;
- while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) {
- if ( current.nodeType == 3 || !block( current ) ) {
- tmpRange.setStartBefore( current );
- while ( current && current !== bookmark2.end && !block( current ) ) {
- tmpNode = current;
- current = domUtils.getNextDomNode( current, false, null, function( node ) {
- return !block( node );
- } );
- }
- tmpRange.setEndAfter( tmpNode );
-
- para = range.document.createElement( style );
- if(attrs){
- domUtils.setAttributes(para,attrs);
- if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){
- para.style.cssText = attrs.style;
- }
- }
- para.appendChild( tmpRange.extractContents() );
- //需要内容占位
- if(domUtils.isEmptyNode(para)){
- domUtils.fillChar(range.document,para);
-
- }
-
- tmpRange.insertNode( para );
-
- var parent = para.parentNode;
- //如果para上一级是一个block元素且不是body,td就删除它
- if ( block( parent ) && !domUtils.isBody( para.parentNode ) && utils.indexOf(notExchange,parent.tagName)==-1) {
- //存储dir,style
- if(!(sourceCmdName && sourceCmdName == 'customstyle')){
- parent.getAttribute('dir') && para.setAttribute('dir',parent.getAttribute('dir'));
- //trace:1070
- parent.style.cssText && (para.style.cssText = parent.style.cssText + ';' + para.style.cssText);
- //trace:1030
- parent.style.textAlign && !para.style.textAlign && (para.style.textAlign = parent.style.textAlign);
- parent.style.textIndent && !para.style.textIndent && (para.style.textIndent = parent.style.textIndent);
- parent.style.padding && !para.style.padding && (para.style.padding = parent.style.padding);
- }
-
- //trace:1706 选择的就是h1-6要删除
- if(attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName) ){
- domUtils.setAttributes(parent,attrs);
- if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){
- parent.style.cssText = attrs.style;
- }
- domUtils.remove(para,true);
- para = parent;
- }else{
- domUtils.remove( para.parentNode, true );
- }
-
- }
- if( utils.indexOf(notExchange,parent.tagName)!=-1){
- current = parent;
- }else{
- current = para;
- }
-
-
- current = domUtils.getNextDomNode( current, false, filterFn );
- } else {
- current = domUtils.getNextDomNode( current, true, filterFn );
- }
- }
- return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark );
- };
- me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});
- me.commands['paragraph'] = {
- execCommand : function( cmdName, style,attrs,sourceCmdName ) {
- var range = this.selection.getRange();
- //闭合时单独处理
- if(range.collapsed){
- var txt = this.document.createTextNode('p');
- range.insertNode(txt);
- //去掉冗余的fillchar
- if(browser.ie){
- var node = txt.previousSibling;
- if(node && domUtils.isWhitespace(node)){
- domUtils.remove(node);
- }
- node = txt.nextSibling;
- if(node && domUtils.isWhitespace(node)){
- domUtils.remove(node);
- }
- }
-
- }
- range = doParagraph(range,style,attrs,sourceCmdName);
- if(txt){
- range.setStartBefore(txt).collapse(true);
- pN = txt.parentNode;
-
- domUtils.remove(txt);
-
- if(domUtils.isBlockElm(pN)&&domUtils.isEmptyNode(pN)){
- domUtils.fillNode(this.document,pN);
- }
-
- }
-
- if(browser.gecko && range.collapsed && range.startContainer.nodeType == 1){
- var child = range.startContainer.childNodes[range.startOffset];
- if(child && child.nodeType == 1 && child.tagName.toLowerCase() == style){
- range.setStart(child,0).collapse(true);
- }
- }
- //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
- range.select();
-
-
- return true;
- },
- queryCommandValue : function() {
- var node = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6');
- return node ? node.tagName.toLowerCase() : '';
- }
- };
-};
-
-
-// plugins/directionality.js
-/**
- * 设置文字输入的方向的插件
- * @file
- * @since 1.2.6.1
- */
-(function() {
- var block = domUtils.isBlockElm ,
- getObj = function(editor){
-// var startNode = editor.selection.getStart(),
-// parents;
-// if ( startNode ) {
-// //查找所有的是block的父亲节点
-// parents = domUtils.findParents( startNode, true, block, true );
-// for ( var i = 0,ci; ci = parents[i++]; ) {
-// if ( ci.getAttribute( 'dir' ) ) {
-// return ci;
-// }
-// }
-// }
- return domUtils.filterNodeList(editor.selection.getStartElementPath(),function(n){return n && n.nodeType == 1 && n.getAttribute('dir')});
-
- },
- doDirectionality = function(range,editor,forward){
-
- var bookmark,
- filterFn = function( node ) {
- return node.nodeType == 1 ? !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
- },
-
- obj = getObj( editor );
-
- if ( obj && range.collapsed ) {
- obj.setAttribute( 'dir', forward );
- return range;
- }
- bookmark = range.createBookmark();
- range.enlarge( true );
- var bookmark2 = range.createBookmark(),
- current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ),
- tmpRange = range.cloneRange(),
- tmpNode;
- while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) {
- if ( current.nodeType == 3 || !block( current ) ) {
- tmpRange.setStartBefore( current );
- while ( current && current !== bookmark2.end && !block( current ) ) {
- tmpNode = current;
- current = domUtils.getNextDomNode( current, false, null, function( node ) {
- return !block( node );
- } );
- }
- tmpRange.setEndAfter( tmpNode );
- var common = tmpRange.getCommonAncestor();
- if ( !domUtils.isBody( common ) && block( common ) ) {
- //遍历到了block节点
- common.setAttribute( 'dir', forward );
- current = common;
- } else {
- //没有遍历到,添加一个block节点
- var p = range.document.createElement( 'p' );
- p.setAttribute( 'dir', forward );
- var frag = tmpRange.extractContents();
- p.appendChild( frag );
- tmpRange.insertNode( p );
- current = p;
- }
-
- current = domUtils.getNextDomNode( current, false, filterFn );
- } else {
- current = domUtils.getNextDomNode( current, true, filterFn );
- }
- }
- return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark );
- };
-
- /**
- * 文字输入方向
- * @command directionality
- * @method execCommand
- * @param { String } cmdName 命令字符串
- * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
- * @example
- * ```javascript
- * editor.execCommand( 'directionality', 'ltr');
- * ```
- */
-
- /**
- * 查询当前选区的文字输入方向
- * @command directionality
- * @method queryCommandValue
- * @param { String } cmdName 命令字符串
- * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
- * @example
- * ```javascript
- * editor.queryCommandValue( 'directionality');
- * ```
- */
- UE.commands['directionality'] = {
- execCommand : function( cmdName,forward ) {
- var range = this.selection.getRange();
- //闭合时单独处理
- if(range.collapsed){
- var txt = this.document.createTextNode('d');
- range.insertNode(txt);
- }
- doDirectionality(range,this,forward);
- if(txt){
- range.setStartBefore(txt).collapse(true);
- domUtils.remove(txt);
- }
-
- range.select();
- return true;
- },
- queryCommandValue : function() {
- var node = getObj(this);
- return node ? node.getAttribute('dir') : 'ltr';
- }
- };
-})();
-
-
-
-// plugins/horizontal.js
-/**
- * 插入分割线插件
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 插入分割线
- * @command horizontal
- * @method execCommand
- * @param { String } cmdName 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'horizontal' );
- * ```
- */
-UE.plugins['horizontal'] = function(){
- var me = this;
- me.commands['horizontal'] = {
- execCommand : function( cmdName ) {
- var me = this;
- if(me.queryCommandState(cmdName)!==-1){
- me.execCommand('insertHtml',' ');
- var range = me.selection.getRange(),
- start = range.startContainer;
- if(start.nodeType == 1 && !start.childNodes[range.startOffset] ){
-
- var tmp;
- if(tmp = start.childNodes[range.startOffset - 1]){
- if(tmp.nodeType == 1 && tmp.tagName == 'HR'){
- if(me.options.enterTag == 'p'){
- tmp = me.document.createElement('p');
- range.insertNode(tmp);
- range.setStart(tmp,0).setCursor();
-
- }else{
- tmp = me.document.createElement('br');
- range.insertNode(tmp);
- range.setStartBefore(tmp).setCursor();
- }
- }
- }
-
- }
- return true;
- }
-
- },
- //边界在table里不能加分隔线
- queryCommandState : function() {
- return domUtils.filterNodeList(this.selection.getStartElementPath(),'table') ? -1 : 0;
- }
- };
-// me.addListener('delkeyup',function(){
-// var rng = this.selection.getRange();
-// if(browser.ie && browser.version > 8){
-// rng.txtToElmBoundary(true);
-// if(domUtils.isStartInblock(rng)){
-// var tmpNode = rng.startContainer;
-// var pre = tmpNode.previousSibling;
-// if(pre && domUtils.isTagNode(pre,'hr')){
-// domUtils.remove(pre);
-// rng.select();
-// return;
-// }
-// }
-// }
-// if(domUtils.isBody(rng.startContainer)){
-// var hr = rng.startContainer.childNodes[rng.startOffset -1];
-// if(hr && hr.nodeName == 'HR'){
-// var next = hr.nextSibling;
-// if(next){
-// rng.setStart(next,0)
-// }else if(hr.previousSibling){
-// rng.setStartAtLast(hr.previousSibling)
-// }else{
-// var p = this.document.createElement('p');
-// hr.parentNode.insertBefore(p,hr);
-// domUtils.fillNode(this.document,p);
-// rng.setStart(p,0);
-// }
-// domUtils.remove(hr);
-// rng.setCursor(false,true);
-// }
-// }
-// })
- me.addListener('delkeydown',function(name,evt){
- var rng = this.selection.getRange();
- rng.txtToElmBoundary(true);
- if(domUtils.isStartInblock(rng)){
- var tmpNode = rng.startContainer;
- var pre = tmpNode.previousSibling;
- if(pre && domUtils.isTagNode(pre,'hr')){
- domUtils.remove(pre);
- rng.select();
- domUtils.preventDefault(evt);
- return true;
-
- }
- }
-
- })
-};
-
-
-
-// plugins/time.js
-/**
- * 插入时间和日期
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 插入时间,默认格式:12:59:59
- * @command time
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'time');
- * ```
- */
-
-/**
- * 插入日期,默认格式:2013-08-30
- * @command date
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'date');
- * ```
- */
-UE.commands['time'] = UE.commands["date"] = {
- execCommand : function(cmd, format){
- var date = new Date;
-
- function formatTime(date, format) {
- var hh = ('0' + date.getHours()).slice(-2),
- ii = ('0' + date.getMinutes()).slice(-2),
- ss = ('0' + date.getSeconds()).slice(-2);
- format = format || 'hh:ii:ss';
- return format.replace(/hh/ig, hh).replace(/ii/ig, ii).replace(/ss/ig, ss);
- }
- function formatDate(date, format) {
- var yyyy = ('000' + date.getFullYear()).slice(-4),
- yy = yyyy.slice(-2),
- mm = ('0' + (date.getMonth()+1)).slice(-2),
- dd = ('0' + date.getDate()).slice(-2);
- format = format || 'yyyy-mm-dd';
- return format.replace(/yyyy/ig, yyyy).replace(/yy/ig, yy).replace(/mm/ig, mm).replace(/dd/ig, dd);
- }
-
- this.execCommand('insertHtml',cmd == "time" ? formatTime(date, format):formatDate(date, format) );
- }
-};
-
-
-// plugins/rowspacing.js
-/**
- * 段前段后间距插件
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 设置段间距
- * @command rowspacing
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } value 段间距的值,以px为单位
- * @param { String } dir 间距位置,top或bottom,分别表示段前和段后
- * @example
- * ```javascript
- * editor.execCommand( 'rowspacing', '10', 'top' );
- * ```
- */
-
-UE.plugins['rowspacing'] = function(){
- var me = this;
- me.setOpt({
- 'rowspacingtop':['5', '10', '15', '20', '25'],
- 'rowspacingbottom':['5', '10', '15', '20', '25']
-
- });
- me.commands['rowspacing'] = {
- execCommand : function( cmdName,value,dir ) {
- this.execCommand('paragraph','p',{style:'margin-'+dir+':'+value + 'px'});
- return true;
- },
- queryCommandValue : function(cmdName,dir) {
- var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node) }),
- value;
- //trace:1026
- if(pN){
- value = domUtils.getComputedStyle(pN,'margin-'+dir).replace(/[^\d]/g,'');
- return !value ? 0 : value;
- }
- return 0;
-
- }
- };
-};
-
-
-
-
-// plugins/lineheight.js
-/**
- * 设置行内间距
- * @file
- * @since 1.2.6.1
- */
-UE.plugins['lineheight'] = function(){
- var me = this;
- me.setOpt({'lineheight':['1', '1.5','1.75','2', '3', '4', '5']});
-
- /**
- * 行距
- * @command lineheight
- * @method execCommand
- * @param { String } cmdName 命令字符串
- * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75
- * @example
- * ```javascript
- * editor.execCommand( 'lineheight', 1.5);
- * ```
- */
- /**
- * 查询当前选区内容的行高大小
- * @command lineheight
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回当前行高大小
- * @example
- * ```javascript
- * editor.queryCommandValue( 'lineheight' );
- * ```
- */
-
- me.commands['lineheight'] = {
- execCommand : function( cmdName,value ) {
- this.execCommand('paragraph','p',{style:'line-height:'+ (value == "1" ? "normal" : value + 'em') });
- return true;
- },
- queryCommandValue : function() {
- var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node)});
- if(pN){
- var value = domUtils.getComputedStyle(pN,'line-height');
- return value == 'normal' ? 1 : value.replace(/[^\d.]*/ig,"");
- }
- }
- };
-};
-
-
-
-
-// plugins/insertcode.js
-/**
- * 插入代码插件
- * @file
- * @since 1.2.6.1
- */
-
-UE.plugins['insertcode'] = function() {
- var me = this;
- me.ready(function(){
- utils.cssRule('pre','pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}',
- me.document)
- });
- me.setOpt('insertcode',{
- 'as3':'ActionScript3',
- 'bash':'Bash/Shell',
- 'cpp':'C/C++',
- 'css':'Css',
- 'cf':'CodeFunction',
- 'c#':'C#',
- 'delphi':'Delphi',
- 'diff':'Diff',
- 'erlang':'Erlang',
- 'groovy':'Groovy',
- 'html':'Html',
- 'java':'Java',
- 'jfx':'JavaFx',
- 'js':'Javascript',
- 'pl':'Perl',
- 'php':'Php',
- 'plain':'Plain Text',
- 'ps':'PowerShell',
- 'python':'Python',
- 'ruby':'Ruby',
- 'scala':'Scala',
- 'sql':'Sql',
- 'vb':'Vb',
- 'xml':'Xml'
- });
-
- /**
- * 插入代码
- * @command insertcode
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } lang 插入代码的语言
- * @example
- * ```javascript
- * editor.execCommand( 'insertcode', 'javascript' );
- * ```
- */
-
- /**
- * 如果选区所在位置是插入插入代码区域,返回代码的语言
- * @command insertcode
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回代码的语言
- * @example
- * ```javascript
- * editor.queryCommandValue( 'insertcode' );
- * ```
- */
-
- me.commands['insertcode'] = {
- execCommand : function(cmd,lang){
- var me = this,
- rng = me.selection.getRange(),
- pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
- if(pre){
- pre.className = 'brush:'+lang+';toolbar:false;';
- }else{
- var code = '';
- if(rng.collapsed){
- code = browser.ie && browser.ie11below ? (browser.version <= 8 ? ' ':''):' ';
- }else{
- var frag = rng.extractContents();
- var div = me.document.createElement('div');
- div.appendChild(frag);
-
- utils.each(UE.filterNode(UE.htmlparser(div.innerHTML.replace(/[\r\t]/g,'')),me.options.filterTxtRules).children,function(node){
- if(browser.ie && browser.ie11below && browser.version > 8){
-
- if(node.type =='element'){
- if(node.tagName == 'br'){
- code += '\n'
- }else if(!dtd.$empty[node.tagName]){
- utils.each(node.children,function(cn){
- if(cn.type =='element'){
- if(cn.tagName == 'br'){
- code += '\n'
- }else if(!dtd.$empty[node.tagName]){
- code += cn.innerText();
- }
- }else{
- code += cn.data
- }
- })
- if(!/\n$/.test(code)){
- code += '\n';
- }
- }
- }else{
- code += node.data + '\n'
- }
- if(!node.nextSibling() && /\n$/.test(code)){
- code = code.replace(/\n$/,'');
- }
- }else{
- if(browser.ie && browser.ie11below){
-
- if(node.type =='element'){
- if(node.tagName == 'br'){
- code += ' '
- }else if(!dtd.$empty[node.tagName]){
- utils.each(node.children,function(cn){
- if(cn.type =='element'){
- if(cn.tagName == 'br'){
- code += ' '
- }else if(!dtd.$empty[node.tagName]){
- code += cn.innerText();
- }
- }else{
- code += cn.data
- }
- });
- if(!/br>$/.test(code)){
- code += ' ';
- }
- }
- }else{
- code += node.data + ' '
- }
- if(!node.nextSibling() && / $/.test(code)){
- code = code.replace(/ $/,'');
- }
-
- }else{
- code += (node.type == 'element' ? (dtd.$empty[node.tagName] ? '' : node.innerText()) : node.data);
- if(!/br\/?\s*>$/.test(code)){
- if(!node.nextSibling())
- return;
- code += ' '
- }
- }
-
- }
-
- });
- }
- me.execCommand('inserthtml',''+code+' ',true);
-
- pre = me.document.getElementById('coder');
- domUtils.removeAttributes(pre,'id');
- var tmpNode = pre.previousSibling;
-
- if(tmpNode && (tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6 || domUtils.isEmptyBlock(tmpNode))){
-
- domUtils.remove(tmpNode)
- }
- var rng = me.selection.getRange();
- if(domUtils.isEmptyBlock(pre)){
- rng.setStart(pre,0).setCursor(false,true)
- }else{
- rng.selectNodeContents(pre).select()
- }
- }
-
-
-
- },
- queryCommandValue : function(){
- var path = this.selection.getStartElementPath();
- var lang = '';
- utils.each(path,function(node){
- if(node.nodeName =='PRE'){
- var match = node.className.match(/brush:([^;]+)/);
- lang = match && match[1] ? match[1] : '';
- return false;
- }
- });
- return lang;
- }
- };
-
- me.addInputRule(function(root){
- utils.each(root.getNodesByTagName('pre'),function(pre){
- var brs = pre.getNodesByTagName('br');
- if(brs.length){
- browser.ie && browser.ie11below && browser.version > 8 && utils.each(brs,function(br){
- var txt = UE.uNode.createText('\n');
- br.parentNode.insertBefore(txt,br);
- br.parentNode.removeChild(br);
- });
- return;
- }
- if(browser.ie && browser.ie11below && browser.version > 8)
- return;
- var code = pre.innerText().split(/\n/);
- pre.innerHTML('');
- utils.each(code,function(c){
- if(c.length){
- pre.appendChild(UE.uNode.createText(c));
- }
- pre.appendChild(UE.uNode.createElement('br'))
- })
- })
- });
- me.addOutputRule(function(root){
- utils.each(root.getNodesByTagName('pre'),function(pre){
- var code = '';
- utils.each(pre.children,function(n){
- if(n.type == 'text'){
- //在ie下文本内容有可能末尾带有\n要去掉
- //trace:3396
- code += n.data.replace(/[ ]/g,' ').replace(/\n$/,'');
- }else{
- if(n.tagName == 'br'){
- code += '\n'
- }else{
- code += (!dtd.$empty[n.tagName] ? '' : n.innerText());
- }
-
- }
-
- });
-
- pre.innerText(code.replace(/( |\n)+$/,''))
- })
- });
- //不需要判断highlight的command列表
- me.notNeedCodeQuery ={
- help:1,
- undo:1,
- redo:1,
- source:1,
- print:1,
- searchreplace:1,
- fullscreen:1,
- preview:1,
- insertparagraph:1,
- elementpath:1,
- insertcode:1,
- inserthtml:1,
- selectall:1
- };
- //将queyCommamndState重置
- var orgQuery = me.queryCommandState;
- me.queryCommandState = function(cmd){
- var me = this;
-
- if(!me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue('insertcode')){
- return -1;
- }
- return UE.Editor.prototype.queryCommandState.apply(this,arguments)
- };
- me.addListener('beforeenterkeydown',function(){
- var rng = me.selection.getRange();
- var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
- if(pre){
- me.fireEvent('saveScene');
- if(!rng.collapsed){
- rng.deleteContents();
- }
- if(!browser.ie || browser.ie9above){
- var tmpNode = me.document.createElement('br'),pre;
- rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true);
- var next = tmpNode.nextSibling;
- if(!next && (!browser.ie || browser.version > 10)){
- rng.insertNode(tmpNode.cloneNode(false));
- }else{
- rng.setStartAfter(tmpNode);
- }
- pre = tmpNode.previousSibling;
- var tmp;
- while(pre ){
- tmp = pre;
- pre = pre.previousSibling;
- if(!pre || pre.nodeName == 'BR'){
- pre = tmp;
- break;
- }
- }
- if(pre){
- var str = '';
- while(pre && pre.nodeName != 'BR' && new RegExp('^[\\s'+domUtils.fillChar+']*$').test(pre.nodeValue)){
- str += pre.nodeValue;
- pre = pre.nextSibling;
- }
- if(pre.nodeName != 'BR'){
- var match = pre.nodeValue.match(new RegExp('^([\\s'+domUtils.fillChar+']+)'));
- if(match && match[1]){
- str += match[1]
- }
-
- }
- if(str){
- str = me.document.createTextNode(str);
- rng.insertNode(str).setStartAfter(str);
- }
- }
- rng.collapse(true).select(true);
- }else{
- if(browser.version > 8){
-
- var txt = me.document.createTextNode('\n');
- var start = rng.startContainer;
- if(rng.startOffset == 0){
- var preNode = start.previousSibling;
- if(preNode){
- rng.insertNode(txt);
- var fillchar = me.document.createTextNode(' ');
- rng.setStartAfter(txt).insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true)
- }
- }else{
- rng.insertNode(txt).setStartAfter(txt);
- var fillchar = me.document.createTextNode(' ');
- start = rng.startContainer.childNodes[rng.startOffset];
- if(start && !/^\n/.test(start.nodeValue)){
- rng.setStartBefore(txt)
- }
- rng.insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true)
- }
-
- }else{
- var tmpNode = me.document.createElement('br');
- rng.insertNode(tmpNode);
- rng.insertNode(me.document.createTextNode(domUtils.fillChar));
- rng.setStartAfter(tmpNode);
- pre = tmpNode.previousSibling;
- var tmp;
- while(pre ){
- tmp = pre;
- pre = pre.previousSibling;
- if(!pre || pre.nodeName == 'BR'){
- pre = tmp;
- break;
- }
- }
- if(pre){
- var str = '';
- while(pre && pre.nodeName != 'BR' && new RegExp('^[ '+domUtils.fillChar+']*$').test(pre.nodeValue)){
- str += pre.nodeValue;
- pre = pre.nextSibling;
- }
- if(pre.nodeName != 'BR'){
- var match = pre.nodeValue.match(new RegExp('^([ '+domUtils.fillChar+']+)'));
- if(match && match[1]){
- str += match[1]
- }
-
- }
-
- str = me.document.createTextNode(str);
- rng.insertNode(str).setStartAfter(str);
- }
- rng.collapse(true).select();
- }
-
-
- }
- me.fireEvent('saveScene');
- return true;
- }
-
-
- });
-
- me.addListener('tabkeydown',function(cmd,evt){
- var rng = me.selection.getRange();
- var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
- if(pre){
- me.fireEvent('saveScene');
- if(evt.shiftKey){
-
- }else{
- if(!rng.collapsed){
- var bk = rng.createBookmark();
- var start = bk.start.previousSibling;
-
- while(start){
- if(pre.firstChild === start && !domUtils.isBr(start)){
- pre.insertBefore(me.document.createTextNode(' '),start);
-
- break;
- }
- if(domUtils.isBr(start)){
- pre.insertBefore(me.document.createTextNode(' '),start.nextSibling);
-
- break;
- }
- start = start.previousSibling;
- }
- var end = bk.end;
- start = bk.start.nextSibling;
- if(pre.firstChild === bk.start){
- pre.insertBefore(me.document.createTextNode(' '),start.nextSibling)
-
- }
- while(start && start !== end){
- if(domUtils.isBr(start) && start.nextSibling){
- if(start.nextSibling === end){
- break;
- }
- pre.insertBefore(me.document.createTextNode(' '),start.nextSibling)
- }
-
- start = start.nextSibling;
- }
- rng.moveToBookmark(bk).select();
- }else{
- var tmpNode = me.document.createTextNode(' ');
- rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true).select(true);
- }
- }
-
-
- me.fireEvent('saveScene');
- return true;
- }
-
-
- });
-
-
- me.addListener('beforeinserthtml',function(evtName,html){
- var me = this,
- rng = me.selection.getRange(),
- pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
- if(pre){
- if(!rng.collapsed){
- rng.deleteContents()
- }
- var htmlstr = '';
- if(browser.ie && browser.version > 8){
-
- utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){
- if(node.type =='element'){
- if(node.tagName == 'br'){
- htmlstr += '\n'
- }else if(!dtd.$empty[node.tagName]){
- utils.each(node.children,function(cn){
- if(cn.type =='element'){
- if(cn.tagName == 'br'){
- htmlstr += '\n'
- }else if(!dtd.$empty[node.tagName]){
- htmlstr += cn.innerText();
- }
- }else{
- htmlstr += cn.data
- }
- })
- if(!/\n$/.test(htmlstr)){
- htmlstr += '\n';
- }
- }
- }else{
- htmlstr += node.data + '\n'
- }
- if(!node.nextSibling() && /\n$/.test(htmlstr)){
- htmlstr = htmlstr.replace(/\n$/,'');
- }
- });
- var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/ /g,' ')));
- rng.insertNode(tmpNode).selectNode(tmpNode).select();
- }else{
- var frag = me.document.createDocumentFragment();
-
- utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){
- if(node.type =='element'){
- if(node.tagName == 'br'){
- frag.appendChild(me.document.createElement('br'))
- }else if(!dtd.$empty[node.tagName]){
- utils.each(node.children,function(cn){
- if(cn.type =='element'){
- if(cn.tagName == 'br'){
-
- frag.appendChild(me.document.createElement('br'))
- }else if(!dtd.$empty[node.tagName]){
- frag.appendChild(me.document.createTextNode(utils.html(cn.innerText().replace(/ /g,' '))));
-
- }
- }else{
- frag.appendChild(me.document.createTextNode(utils.html( cn.data.replace(/ /g,' '))));
-
- }
- })
- if(frag.lastChild.nodeName != 'BR'){
- frag.appendChild(me.document.createElement('br'))
- }
- }
- }else{
- frag.appendChild(me.document.createTextNode(utils.html( node.data.replace(/ /g,' '))));
- }
- if(!node.nextSibling() && frag.lastChild.nodeName == 'BR'){
- frag.removeChild(frag.lastChild)
- }
-
-
- });
- rng.insertNode(frag).select();
-
- }
-
- return true;
- }
- });
- //方向键的处理
- me.addListener('keydown',function(cmd,evt){
- var me = this,keyCode = evt.keyCode || evt.which;
- if(keyCode == 40){
- var rng = me.selection.getRange(),pre,start = rng.startContainer;
- if(rng.collapsed && (pre = domUtils.findParentByTagName(rng.startContainer,'pre',true)) && !pre.nextSibling){
- var last = pre.lastChild
- while(last && last.nodeName == 'BR'){
- last = last.previousSibling;
- }
- if(last === start || rng.startContainer === pre && rng.startOffset == pre.childNodes.length){
- me.execCommand('insertparagraph');
- domUtils.preventDefault(evt)
- }
-
- }
- }
- });
- //trace:3395
- me.addListener('delkeydown',function(type,evt){
- var rng = this.selection.getRange();
- rng.txtToElmBoundary(true);
- var start = rng.startContainer;
- if(domUtils.isTagNode(start,'pre') && rng.collapsed && domUtils.isStartInblock(rng)){
- var p = me.document.createElement('p');
- domUtils.fillNode(me.document,p);
- start.parentNode.insertBefore(p,start);
- domUtils.remove(start);
- rng.setStart(p,0).setCursor(false,true);
- domUtils.preventDefault(evt);
- return true;
- }
- })
-};
-
-
-// plugins/cleardoc.js
-/**
- * 清空文档插件
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 清空文档
- * @command cleardoc
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * //editor 是编辑器实例
- * editor.execCommand('cleardoc');
- * ```
- */
-
-UE.commands['cleardoc'] = {
- execCommand : function( cmdName) {
- var me = this,
- enterTag = me.options.enterTag,
- range = me.selection.getRange();
- if(enterTag == "br"){
- me.body.innerHTML = " ";
- range.setStart(me.body,0).setCursor();
- }else{
- me.body.innerHTML = ""+(ie ? "" : " ")+" ";
- range.setStart(me.body.firstChild,0).setCursor(false,true);
- }
- setTimeout(function(){
- me.fireEvent("clearDoc");
- },0);
-
- }
-};
-
-
-
-// plugins/anchor.js
-/**
- * 锚点插件,为UEditor提供插入锚点支持
- * @file
- * @since 1.2.6.1
- */
-UE.plugin.register('anchor', function (){
-
- return {
- bindEvents:{
- 'ready':function(){
- utils.cssRule('anchor',
- '.anchorclass{background: url(\''
- + this.options.themePath
- + this.options.theme +'/images/anchor.gif\') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}',
- this.document);
- }
- },
- outputRule: function(root){
- utils.each(root.getNodesByTagName('img'),function(a){
- var val;
- if(val = a.getAttr('anchorname')){
- a.tagName = 'a';
- a.setAttr({
- anchorname : '',
- name : val,
- 'class' : ''
- })
- }
- })
- },
- inputRule:function(root){
- utils.each(root.getNodesByTagName('a'),function(a){
- var val;
- if((val = a.getAttr('name')) && !a.getAttr('href')){
- a.tagName = 'img';
- a.setAttr({
- anchorname :a.getAttr('name'),
- 'class' : 'anchorclass'
- });
- a.setAttr('name')
-
- }
- })
-
- },
- commands:{
- /**
- * 插入锚点
- * @command anchor
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @param { String } name 锚点名称字符串
- * @example
- * ```javascript
- * //editor 是编辑器实例
- * editor.execCommand('anchor', 'anchor1');
- * ```
- */
- 'anchor':{
- execCommand:function (cmd, name) {
- var range = this.selection.getRange(),img = range.getClosedNode();
- if (img && img.getAttribute('anchorname')) {
- if (name) {
- img.setAttribute('anchorname', name);
- } else {
- range.setStartBefore(img).setCursor();
- domUtils.remove(img);
- }
- } else {
- if (name) {
- //只在选区的开始插入
- var anchor = this.document.createElement('img');
- range.collapse(true);
- domUtils.setAttributes(anchor,{
- 'anchorname':name,
- 'class':'anchorclass'
- });
- range.insertNode(anchor).setStartAfter(anchor).setCursor(false,true);
- }
- }
- }
- }
- }
- }
-});
-
-
-// plugins/wordcount.js
-///import core
-///commands 字数统计
-///commandsName WordCount,wordCount
-///commandsTitle 字数统计
-/*
- * Created by JetBrains WebStorm.
- * User: taoqili
- * Date: 11-9-7
- * Time: 下午8:18
- * To change this template use File | Settings | File Templates.
- */
-
-UE.plugins['wordcount'] = function(){
- var me = this;
- me.setOpt('wordCount',true);
- me.addListener('contentchange',function(){
- me.fireEvent('wordcount');
- });
- var timer;
- me.addListener('ready',function(){
- var me = this;
- domUtils.on(me.body,"keyup",function(evt){
- var code = evt.keyCode||evt.which,
- //忽略的按键,ctr,alt,shift,方向键
- ignores = {"16":1,"18":1,"20":1,"37":1,"38":1,"39":1,"40":1};
- if(code in ignores) return;
- clearTimeout(timer);
- timer = setTimeout(function(){
- me.fireEvent('wordcount');
- },200)
- })
- });
-};
-
-
-// plugins/pagebreak.js
-/**
- * 分页功能插件
- * @file
- * @since 1.2.6.1
- */
-UE.plugins['pagebreak'] = function () {
- var me = this,
- notBreakTags = ['td'];
- me.setOpt('pageBreakTag','_ueditor_page_break_tag_');
-
- function fillNode(node){
- if(domUtils.isEmptyBlock(node)){
- var firstChild = node.firstChild,tmpNode;
-
- while(firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)){
- tmpNode = firstChild;
- firstChild = firstChild.firstChild;
- }
- !tmpNode && (tmpNode = node);
- domUtils.fillNode(me.document,tmpNode);
- }
- }
- //分页符样式添加
-
- me.ready(function(){
- utils.cssRule('pagebreak','.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',me.document);
- });
- function isHr(node){
- return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak';
- }
- me.addInputRule(function(root){
- root.traversal(function(node){
- if(node.type == 'text' && node.data == me.options.pageBreakTag){
- var hr = UE.uNode.createElement(' ');
- node.parentNode.insertBefore(hr,node);
- node.parentNode.removeChild(node)
- }
- })
- });
- me.addOutputRule(function(node){
- utils.each(node.getNodesByTagName('hr'),function(n){
- if(n.getAttr('class') == 'pagebreak'){
- var txt = UE.uNode.createText(me.options.pageBreakTag);
- n.parentNode.insertBefore(txt,n);
- n.parentNode.removeChild(n);
- }
- })
-
- });
-
- /**
- * 插入分页符
- * @command pagebreak
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @remind 在表格中插入分页符会把表格切分成两部分
- * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串,
- * 以便于提交数据到服务器端后处理分页。
- * @example
- * ```javascript
- * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak
- * ```
- */
-
- me.commands['pagebreak'] = {
- execCommand:function () {
- var range = me.selection.getRange(),hr = me.document.createElement('hr');
- domUtils.setAttributes(hr,{
- 'class' : 'pagebreak',
- noshade:"noshade",
- size:"5"
- });
- domUtils.unSelectable(hr);
- //table单独处理
- var node = domUtils.findParentByTagName(range.startContainer, notBreakTags, true),
-
- parents = [], pN;
- if (node) {
- switch (node.tagName) {
- case 'TD':
- pN = node.parentNode;
- if (!pN.previousSibling) {
- var table = domUtils.findParentByTagName(pN, 'table');
-// var tableWrapDiv = table.parentNode;
-// if(tableWrapDiv && tableWrapDiv.nodeType == 1
-// && tableWrapDiv.tagName == 'DIV'
-// && tableWrapDiv.getAttribute('dropdrag')
-// ){
-// domUtils.remove(tableWrapDiv,true);
-// }
- table.parentNode.insertBefore(hr, table);
- parents = domUtils.findParents(hr, true);
-
- } else {
- pN.parentNode.insertBefore(hr, pN);
- parents = domUtils.findParents(hr);
-
- }
- pN = parents[1];
- if (hr !== pN) {
- domUtils.breakParent(hr, pN);
-
- }
- //table要重写绑定一下拖拽
- me.fireEvent('afteradjusttable',me.document);
- }
-
- } else {
-
- if (!range.collapsed) {
- range.deleteContents();
- var start = range.startContainer;
- while ( !domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start)) {
- range.setStartBefore(start).collapse(true);
- domUtils.remove(start);
- start = range.startContainer;
- }
-
- }
- range.insertNode(hr);
-
- var pN = hr.parentNode, nextNode;
- while (!domUtils.isBody(pN)) {
- domUtils.breakParent(hr, pN);
- nextNode = hr.nextSibling;
- if (nextNode && domUtils.isEmptyBlock(nextNode)) {
- domUtils.remove(nextNode);
- }
- pN = hr.parentNode;
- }
- nextNode = hr.nextSibling;
- var pre = hr.previousSibling;
- if(isHr(pre)){
- domUtils.remove(pre);
- }else{
- pre && fillNode(pre);
- }
-
- if(!nextNode){
- var p = me.document.createElement('p');
-
- hr.parentNode.appendChild(p);
- domUtils.fillNode(me.document,p);
- range.setStart(p,0).collapse(true);
- }else{
- if(isHr(nextNode)){
- domUtils.remove(nextNode);
- }else{
- fillNode(nextNode);
- }
- range.setEndAfter(hr).collapse(false);
- }
-
- range.select(true);
-
- }
-
- }
- };
-};
-
-// plugins/wordimage.js
-///import core
-///commands 本地图片引导上传
-///commandsName WordImage
-///commandsTitle 本地图片引导上传
-///commandsDialog dialogs\wordimage
-
-UE.plugin.register('wordimage',function(){
- var me = this,
- images = [];
- return {
- commands : {
- 'wordimage':{
- execCommand:function () {
- var images = domUtils.getElementsByTagName(me.body, "img");
- var urlList = [];
- for (var i = 0, ci; ci = images[i++];) {
- var url = ci.getAttribute("word_img");
- url && urlList.push(url);
- }
- return urlList;
- },
- queryCommandState:function () {
- images = domUtils.getElementsByTagName(me.body, "img");
- for (var i = 0, ci; ci = images[i++];) {
- if (ci.getAttribute("word_img")) {
- return 1;
- }
- }
- return -1;
- },
- notNeedUndo:true
- }
- },
- inputRule : function (root) {
- utils.each(root.getNodesByTagName('img'), function (img) {
- var attrs = img.attrs,
- flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
- opt = me.options,
- src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif';
- if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) {
- img.setAttr({
- width:attrs.width,
- height:attrs.height,
- alt:attrs.alt,
- word_img: attrs.src,
- src:src,
- 'style':'background:url(' + ( flag ? opt.themePath + opt.theme + '/images/word.gif' : opt.langPath + opt.lang + '/images/localimage.png') + ') no-repeat center center;border:1px solid #ddd'
- })
- }
- })
- }
- }
-});
-
-// plugins/dragdrop.js
-UE.plugins['dragdrop'] = function (){
-
- var me = this;
- me.ready(function(){
- domUtils.on(this.body,'dragend',function(){
- var rng = me.selection.getRange();
- var node = rng.getClosedNode()||me.selection.getStart();
-
- if(node && node.tagName == 'IMG'){
-
- var pre = node.previousSibling,next;
- while(next = node.nextSibling){
- if(next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild){
- domUtils.remove(next)
- }else{
- break;
- }
- }
-
-
- if((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre) || !pre) && (!next || next && !domUtils.isEmptyBlock(next))){
- if(pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)){
- pre.appendChild(node);
- domUtils.moveChild(next,pre);
- domUtils.remove(next);
- }else if(next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)){
- next.insertBefore(node,next.firstChild);
- }
-
- if(pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)){
- domUtils.remove(pre)
- }
- if(next && next.tagName == 'P' && domUtils.isEmptyBlock(next)){
- domUtils.remove(next)
- }
- rng.selectNode(node).select();
- me.fireEvent('saveScene');
-
- }
-
- }
-
- })
- });
- me.addListener('keyup', function(type, evt) {
- var keyCode = evt.keyCode || evt.which;
- if (keyCode == 13) {
- var rng = me.selection.getRange(),node;
- if(node = domUtils.findParentByTagName(rng.startContainer,'p',true)){
- if(domUtils.getComputedStyle(node,'text-align') == 'center'){
- domUtils.removeStyle(node,'text-align')
- }
- }
- }
- })
-};
-
-
-// plugins/undo.js
-/**
- * undo redo
- * @file
- * @since 1.2.6.1
- */
-
-/**
- * 撤销上一次执行的命令
- * @command undo
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'undo' );
- * ```
- */
-
-/**
- * 重做上一次执行的命令
- * @command redo
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'redo' );
- * ```
- */
-
-UE.plugins['undo'] = function () {
- var saveSceneTimer;
- var me = this,
- maxUndoCount = me.options.maxUndoCount || 20,
- maxInputCount = me.options.maxInputCount || 20,
- fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的
- var noNeedFillCharTags = {
- ol:1,ul:1,table:1,tbody:1,tr:1,body:1
- };
- var orgState = me.options.autoClearEmptyNode;
- function compareAddr(indexA, indexB) {
- if (indexA.length != indexB.length)
- return 0;
- for (var i = 0, l = indexA.length; i < l; i++) {
- if (indexA[i] != indexB[i])
- return 0
- }
- return 1;
- }
-
- function compareRangeAddress(rngAddrA, rngAddrB) {
- if (rngAddrA.collapsed != rngAddrB.collapsed) {
- return 0;
- }
- if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
- return 0;
- }
- return 1;
- }
-
- function UndoManager() {
- this.list = [];
- this.index = 0;
- this.hasUndo = false;
- this.hasRedo = false;
- this.undo = function () {
- if (this.hasUndo) {
- if (!this.list[this.index - 1] && this.list.length == 1) {
- this.reset();
- return;
- }
- while (this.list[this.index].content == this.list[this.index - 1].content) {
- this.index--;
- if (this.index == 0) {
- return this.restore(0);
- }
- }
- this.restore(--this.index);
- }
- };
- this.redo = function () {
- if (this.hasRedo) {
- while (this.list[this.index].content == this.list[this.index + 1].content) {
- this.index++;
- if (this.index == this.list.length - 1) {
- return this.restore(this.index);
- }
- }
- this.restore(++this.index);
- }
- };
-
- this.restore = function () {
- var me = this.editor;
- var scene = this.list[this.index];
- var root = UE.htmlparser(scene.content.replace(fillchar, ''));
- me.options.autoClearEmptyNode = false;
- me.filterInputRule(root);
- me.options.autoClearEmptyNode = orgState;
- //trace:873
- //去掉展位符
- me.document.body.innerHTML = root.toHtml();
- me.fireEvent('afterscencerestore');
- //处理undo后空格不展位的问题
- if (browser.ie) {
- utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
- if(domUtils.isEmptyNode(node)){
- domUtils.fillNode(me.document, node);
- }
- })
- }
-
- try{
- var rng = new dom.Range(me.document).moveToAddress(scene.address);
- rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
- }catch(e){}
-
- this.update();
- this.clearKey();
- //不能把自己reset了
- me.fireEvent('reset', true);
- };
-
- this.getScene = function () {
- var me = this.editor;
- var rng = me.selection.getRange(),
- rngAddress = rng.createAddress(false,true);
- me.fireEvent('beforegetscene');
- var root = UE.htmlparser(me.body.innerHTML);
- me.options.autoClearEmptyNode = false;
- me.filterOutputRule(root);
- me.options.autoClearEmptyNode = orgState;
- var cont = root.toHtml();
- //trace:3461
- //这个会引起回退时导致空格丢失的情况
-// browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>'));
- me.fireEvent('aftergetscene');
-
- return {
- address:rngAddress,
- content:cont
- }
- };
- this.save = function (notCompareRange,notSetCursor) {
- clearTimeout(saveSceneTimer);
- var currentScene = this.getScene(notSetCursor),
- lastScene = this.list[this.index];
-
- if(lastScene && lastScene.content != currentScene.content){
- me.trigger('contentchange')
- }
- //内容相同位置相同不存
- if (lastScene && lastScene.content == currentScene.content &&
- ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
- ) {
- return;
- }
- this.list = this.list.slice(0, this.index + 1);
- this.list.push(currentScene);
- //如果大于最大数量了,就把最前的剔除
- if (this.list.length > maxUndoCount) {
- this.list.shift();
- }
- this.index = this.list.length - 1;
- this.clearKey();
- //跟新undo/redo状态
- this.update();
-
- };
- this.update = function () {
- this.hasRedo = !!this.list[this.index + 1];
- this.hasUndo = !!this.list[this.index - 1];
- };
- this.reset = function () {
- this.list = [];
- this.index = 0;
- this.hasUndo = false;
- this.hasRedo = false;
- this.clearKey();
- };
- this.clearKey = function () {
- keycont = 0;
- lastKeyCode = null;
- };
- }
-
- me.undoManger = new UndoManager();
- me.undoManger.editor = me;
- function saveScene() {
- this.undoManger.save();
- }
-
- me.addListener('saveScene', function () {
- var args = Array.prototype.splice.call(arguments,1);
- this.undoManger.save.apply(this.undoManger,args);
- });
-
-// me.addListener('beforeexeccommand', saveScene);
-// me.addListener('afterexeccommand', saveScene);
-
- me.addListener('reset', function (type, exclude) {
- if (!exclude) {
- this.undoManger.reset();
- }
- });
- me.commands['redo'] = me.commands['undo'] = {
- execCommand:function (cmdName) {
- this.undoManger[cmdName]();
- },
- queryCommandState:function (cmdName) {
- return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
- },
- notNeedUndo:1
- };
-
- var keys = {
- // /*Backspace*/ 8:1, /*Delete*/ 46:1,
- /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
- 37:1, 38:1, 39:1, 40:1
-
- },
- keycont = 0,
- lastKeyCode;
- //输入法状态下不计算字符数
- var inputType = false;
- me.addListener('ready', function () {
- domUtils.on(this.body, 'compositionstart', function () {
- inputType = true;
- });
- domUtils.on(this.body, 'compositionend', function () {
- inputType = false;
- })
- });
- //快捷键
- me.addshortcutkey({
- "Undo":"ctrl+90", //undo
- "Redo":"ctrl+89" //redo
-
- });
- var isCollapsed = true;
- me.addListener('keydown', function (type, evt) {
-
- var me = this;
- var keyCode = evt.keyCode || evt.which;
- if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
- if (inputType)
- return;
-
- if(!me.selection.getRange().collapsed){
- me.undoManger.save(false,true);
- isCollapsed = false;
- return;
- }
- if (me.undoManger.list.length == 0) {
- me.undoManger.save(true);
- }
- clearTimeout(saveSceneTimer);
- function save(cont){
- cont.undoManger.save(false,true);
- cont.fireEvent('selectionchange');
- }
- saveSceneTimer = setTimeout(function(){
- if(inputType){
- var interalTimer = setInterval(function(){
- if(!inputType){
- save(me);
- clearInterval(interalTimer)
- }
- },300)
- return;
- }
- save(me);
- },200);
-
- lastKeyCode = keyCode;
- keycont++;
- if (keycont >= maxInputCount ) {
- save(me)
- }
- }
- });
- me.addListener('keyup', function (type, evt) {
- var keyCode = evt.keyCode || evt.which;
- if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
- if (inputType)
- return;
- if(!isCollapsed){
- this.undoManger.save(false,true);
- isCollapsed = true;
- }
- }
- });
- //扩展实例,添加关闭和开启命令undo
- me.stopCmdUndo = function(){
- me.__hasEnterExecCommand = true;
- };
- me.startCmdUndo = function(){
- me.__hasEnterExecCommand = false;
- }
-};
-
-
-// plugins/copy.js
-UE.plugin.register('copy', function () {
-
- var me = this;
-
- function initZeroClipboard() {
-
- ZeroClipboard.config({
- debug: false,
- swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf'
- });
-
- var client = me.zeroclipboard = new ZeroClipboard();
-
- // 复制内容
- client.on('copy', function (e) {
- var client = e.client,
- rng = me.selection.getRange(),
- div = document.createElement('div');
-
- div.appendChild(rng.cloneContents());
- client.setText(div.innerText || div.textContent);
- client.setHtml(div.innerHTML);
- rng.select();
- });
- // hover事件传递到target
- client.on('mouseover mouseout', function (e) {
- var target = e.target;
- if (e.type == 'mouseover') {
- domUtils.addClass(target, 'edui-state-hover');
- } else if (e.type == 'mouseout') {
- domUtils.removeClasses(target, 'edui-state-hover');
- }
- });
- // flash加载不成功
- client.on('wrongflash noflash', function () {
- ZeroClipboard.destroy();
- });
- }
-
- return {
- bindEvents: {
- 'ready': function () {
- if (!browser.ie) {
- if (window.ZeroClipboard) {
- initZeroClipboard();
- } else {
- utils.loadFile(document, {
- src: me.options.UEDITOR_HOME_URL + "third-party/zeroclipboard/ZeroClipboard.js",
- tag: "script",
- type: "text/javascript",
- defer: "defer"
- }, function () {
- initZeroClipboard();
- });
- }
- }
- }
- },
- commands: {
- 'copy': {
- execCommand: function (cmd) {
- if (!me.document.execCommand('copy')) {
- alert(me.getLang('copymsg'));
- }
- }
- }
- }
- }
-});
-
-
-// plugins/paste.js
-///import core
-///import plugins/inserthtml.js
-///import plugins/undo.js
-///import plugins/serialize.js
-///commands 粘贴
-///commandsName PastePlain
-///commandsTitle 纯文本粘贴模式
-/**
- * @description 粘贴
- * @author zhanyi
- */
-UE.plugins['paste'] = function () {
- function getClipboardData(callback) {
- var doc = this.document;
- if (doc.getElementById('baidu_pastebin')) {
- return;
- }
- var range = this.selection.getRange(),
- bk = range.createBookmark(),
- //创建剪贴的容器div
- pastebin = doc.createElement('div');
- pastebin.id = 'baidu_pastebin';
- // Safari 要求div必须有内容,才能粘贴内容进来
- browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
- doc.body.appendChild(pastebin);
- //trace:717 隐藏的span不能得到top
- //bk.start.innerHTML = ' ';
- bk.start.style.display = '';
- pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
- //要在现在光标平行的位置加入,否则会出现跳动的问题
- domUtils.getXY(bk.start).y + 'px';
-
- range.selectNodeContents(pastebin).select(true);
-
- setTimeout(function () {
- if (browser.webkit) {
- for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
- if (domUtils.isEmptyNode(pi)) {
- domUtils.remove(pi);
- } else {
- pastebin = pi;
- break;
- }
- }
- }
- try {
- pastebin.parentNode.removeChild(pastebin);
- } catch (e) {
- }
- range.moveToBookmark(bk).select(true);
- callback(pastebin);
- }, 0);
- }
-
- var me = this;
-
- me.setOpt({
- retainOnlyLabelPasted : false
- });
-
- var txtContent, htmlContent, address;
-
- function getPureHtml(html){
- return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (a, b, tagName, attrs) {
- tagName = tagName.toLowerCase();
- if ({img: 1}[tagName]) {
- return a;
- }
- attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function (str, atr, val) {
- if ({
- 'src': 1,
- 'href': 1,
- 'name': 1
- }[atr.toLowerCase()]) {
- return atr + '=' + val + ' '
- }
- return ''
- });
- if ({
- 'span': 1,
- 'div': 1
- }[tagName]) {
- return ''
- } else {
-
- return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
- }
-
- });
- }
- function filter(div) {
- var html;
- if (div.firstChild) {
- //去掉cut中添加的边界值
- var nodes = domUtils.getElementsByTagName(div, 'span');
- for (var i = 0, ni; ni = nodes[i++];) {
- if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
- domUtils.remove(ni);
- }
- }
-
- if (browser.webkit) {
-
- var brs = div.querySelectorAll('div br');
- for (var i = 0, bi; bi = brs[i++];) {
- var pN = bi.parentNode;
- if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
- pN.innerHTML = '
';
- domUtils.remove(pN);
- }
- }
- var divs = div.querySelectorAll('#baidu_pastebin');
- for (var i = 0, di; di = divs[i++];) {
- var tmpP = me.document.createElement('p');
- di.parentNode.insertBefore(tmpP, di);
- while (di.firstChild) {
- tmpP.appendChild(di.firstChild);
- }
- domUtils.remove(di);
- }
-
- var metas = div.querySelectorAll('meta');
- for (var i = 0, ci; ci = metas[i++];) {
- domUtils.remove(ci);
- }
-
- var brs = div.querySelectorAll('br');
- for (i = 0; ci = brs[i++];) {
- if (/^apple-/i.test(ci.className)) {
- domUtils.remove(ci);
- }
- }
- }
- if (browser.gecko) {
- var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
- for (i = 0; ci = dirtyNodes[i++];) {
- ci.removeAttribute('_moz_dirty');
- }
- }
- if (!browser.ie) {
- var spans = div.querySelectorAll('span.Apple-style-span');
- for (var i = 0, ci; ci = spans[i++];) {
- domUtils.remove(ci, true);
- }
- }
-
- //ie下使用innerHTML会产生多余的\r\n字符,也会产生 这里过滤掉
- html = div.innerHTML;//.replace(/>(?:(\s| )*?)<');
-
- //过滤word粘贴过来的冗余属性
- html = UE.filterWord(html);
- //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
- var root = UE.htmlparser(html);
- //如果给了过滤规则就先进行过滤
- if (me.options.filterRules) {
- UE.filterNode(root, me.options.filterRules);
- }
- //执行默认的处理
- me.filterInputRule(root);
- //针对chrome的处理
- if (browser.webkit) {
- var br = root.lastChild();
- if (br && br.type == 'element' && br.tagName == 'br') {
- root.removeChild(br)
- }
- utils.each(me.body.querySelectorAll('div'), function (node) {
- if (domUtils.isEmptyBlock(node)) {
- domUtils.remove(node,true)
- }
- })
- }
- html = {'html': root.toHtml()};
- me.fireEvent('beforepaste', html, root);
- //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
- if(!html.html){
- return;
- }
- root = UE.htmlparser(html.html,true);
- //如果开启了纯文本模式
- if (me.queryCommandState('pasteplain') === 1) {
- me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true);
- } else {
- //文本模式
- UE.filterNode(root, me.options.filterTxtRules);
- txtContent = root.toHtml();
- //完全模式
- htmlContent = html.html;
-
- address = me.selection.getRange().createAddress(true);
- me.execCommand('insertHtml', me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent, true);
- }
- me.fireEvent("afterpaste", html);
- }
- }
-
- me.addListener('pasteTransfer', function (cmd, plainType) {
-
- if (address && txtContent && htmlContent && txtContent != htmlContent) {
- var range = me.selection.getRange();
- range.moveToAddress(address, true);
-
- if (!range.collapsed) {
-
- while (!domUtils.isBody(range.startContainer)
- ) {
- var start = range.startContainer;
- if(start.nodeType == 1){
- start = start.childNodes[range.startOffset];
- if(!start){
- range.setStartBefore(range.startContainer);
- continue;
- }
- var pre = start.previousSibling;
-
- if(pre && pre.nodeType == 3 && new RegExp('^[\n\r\t '+domUtils.fillChar+']*$').test(pre.nodeValue)){
- range.setStartBefore(pre)
- }
- }
- if(range.startOffset == 0){
- range.setStartBefore(range.startContainer);
- }else{
- break;
- }
-
- }
- while (!domUtils.isBody(range.endContainer)
- ) {
- var end = range.endContainer;
- if(end.nodeType == 1){
- end = end.childNodes[range.endOffset];
- if(!end){
- range.setEndAfter(range.endContainer);
- continue;
- }
- var next = end.nextSibling;
- if(next && next.nodeType == 3 && new RegExp('^[\n\r\t'+domUtils.fillChar+']*$').test(next.nodeValue)){
- range.setEndAfter(next)
- }
- }
- if(range.endOffset == range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length){
- range.setEndAfter(range.endContainer);
- }else{
- break;
- }
-
- }
-
- }
-
- range.deleteContents();
- range.select(true);
- me.__hasEnterExecCommand = true;
- var html = htmlContent;
- if (plainType === 2 ) {
- html = getPureHtml(html);
- } else if (plainType) {
- html = txtContent;
- }
- me.execCommand('inserthtml', html, true);
- me.__hasEnterExecCommand = false;
- var rng = me.selection.getRange();
- while (!domUtils.isBody(rng.startContainer) && !rng.startOffset &&
- rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
- ) {
- rng.setStartBefore(rng.startContainer);
- }
- var tmpAddress = rng.createAddress(true);
- address.endAddress = tmpAddress.startAddress;
- }
- });
-
- me.addListener('ready', function () {
- domUtils.on(me.body, 'cut', function () {
- var range = me.selection.getRange();
- if (!range.collapsed && me.undoManger) {
- me.undoManger.save();
- }
- });
-
- //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
- domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
- if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
- return;
- }
- getClipboardData.call(me, function (div) {
- filter(div);
- });
- });
-
- });
-
- me.commands['paste'] = {
- execCommand: function (cmd) {
- if (browser.ie) {
- getClipboardData.call(me, function (div) {
- filter(div);
- });
- me.document.execCommand('paste');
- } else {
- alert(me.getLang('pastemsg'));
- }
- }
- }
-};
-
-
-
-// plugins/puretxtpaste.js
-/**
- * 纯文本粘贴插件
- * @file
- * @since 1.2.6.1
- */
-
-UE.plugins['pasteplain'] = function(){
- var me = this;
- me.setOpt({
- 'pasteplain':false,
- 'filterTxtRules' : function(){
- function transP(node){
- node.tagName = 'p';
- node.setStyle();
- }
- function removeNode(node){
- node.parentNode.removeChild(node,true)
- }
- return {
- //直接删除及其字节点内容
- '-' : 'script style object iframe embed input select',
- 'p': {$:{}},
- 'br':{$:{}},
- div: function (node) {
- var tmpNode, p = UE.uNode.createElement('p');
- while (tmpNode = node.firstChild()) {
- if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
- p.appendChild(tmpNode);
- } else {
- if (p.firstChild()) {
- node.parentNode.insertBefore(p, node);
- p = UE.uNode.createElement('p');
- } else {
- node.parentNode.insertBefore(tmpNode, node);
- }
- }
- }
- if (p.firstChild()) {
- node.parentNode.insertBefore(p, node);
- }
- node.parentNode.removeChild(node);
- },
- ol: removeNode,
- ul: removeNode,
- dl:removeNode,
- dt:removeNode,
- dd:removeNode,
- 'li':removeNode,
- 'caption':transP,
- 'th':transP,
- 'tr':transP,
- 'h1':transP,'h2':transP,'h3':transP,'h4':transP,'h5':transP,'h6':transP,
- 'td':function(node){
- //没有内容的td直接删掉
- var txt = !!node.innerText();
- if(txt){
- node.parentNode.insertAfter(UE.uNode.createText(' '),node);
- }
- node.parentNode.removeChild(node,node.innerText())
- }
- }
- }()
- });
- //暂时这里支持一下老版本的属性
- var pasteplain = me.options.pasteplain;
-
- /**
- * 启用或取消纯文本粘贴模式
- * @command pasteplain
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.queryCommandState( 'pasteplain' );
- * ```
- */
-
- /**
- * 查询当前是否处于纯文本粘贴模式
- * @command pasteplain
- * @method queryCommandState
- * @param { String } cmd 命令字符串
- * @return { int } 如果处于纯文本模式,返回1,否则,返回0
- * @example
- * ```javascript
- * editor.queryCommandState( 'pasteplain' );
- * ```
- */
- me.commands['pasteplain'] = {
- queryCommandState: function (){
- return pasteplain ? 1 : 0;
- },
- execCommand: function (){
- pasteplain = !pasteplain|0;
- },
- notNeedUndo : 1
- };
-};
-
-// plugins/list.js
-/**
- * 有序列表,无序列表插件
- * @file
- * @since 1.2.6.1
- */
-
-UE.plugins['list'] = function () {
- var me = this,
- notExchange = {
- 'TD':1,
- 'PRE':1,
- 'BLOCKQUOTE':1
- };
- var customStyle = {
- 'cn' : 'cn-1-',
- 'cn1' : 'cn-2-',
- 'cn2' : 'cn-3-',
- 'num': 'num-1-',
- 'num1' : 'num-2-',
- 'num2' : 'num-3-',
- 'dash' : 'dash',
- 'dot':'dot'
- };
-
- me.setOpt( {
- 'autoTransWordToList':false,
- 'insertorderedlist':{
- 'num':'',
- 'num1':'',
- 'num2':'',
- 'cn':'',
- 'cn1':'',
- 'cn2':'',
- 'decimal':'',
- 'lower-alpha':'',
- 'lower-roman':'',
- 'upper-alpha':'',
- 'upper-roman':''
- },
- 'insertunorderedlist':{
- 'circle':'',
- 'disc':'',
- 'square':'',
- 'dash' : '',
- 'dot':''
- },
- listDefaultPaddingLeft : '30',
- listiconpath : 'http://bs.baidu.com/listicon/',
- maxListLevel : -1,//-1不限制
- disablePInList:false
- } );
- function listToArray(list){
- var arr = [];
- for(var p in list){
- arr.push(p)
- }
- return arr;
- }
- var listStyle = {
- 'OL':listToArray(me.options.insertorderedlist),
- 'UL':listToArray(me.options.insertunorderedlist)
- };
- var liiconpath = me.options.listiconpath;
-
- //根据用户配置,调整customStyle
- for(var s in customStyle){
- if(!me.options.insertorderedlist.hasOwnProperty(s) && !me.options.insertunorderedlist.hasOwnProperty(s)){
- delete customStyle[s];
- }
- }
-
- me.ready(function () {
- var customCss = [];
- for(var p in customStyle){
- if(p == 'dash' || p == 'dot'){
- customCss.push('li.list-' + customStyle[p] + '{background-image:url(' + liiconpath +customStyle[p]+'.gif)}');
- customCss.push('ul.custom_'+p+'{list-style:none;}ul.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}');
- }else{
- for(var i= 0;i<99;i++){
- customCss.push('li.list-' + customStyle[p] + i + '{background-image:url(' + liiconpath + 'list-'+customStyle[p] + i + '.gif)}')
- }
- customCss.push('ol.custom_'+p+'{list-style:none;}ol.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}');
- }
- switch(p){
- case 'cn':
- customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}');
- customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
- customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}');
- break;
- case 'cn1':
- customCss.push('li.list-'+p+'-paddingleft-1{padding-left:30px}');
- customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
- customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}');
- break;
- case 'cn2':
- customCss.push('li.list-'+p+'-paddingleft-1{padding-left:40px}');
- customCss.push('li.list-'+p+'-paddingleft-2{padding-left:55px}');
- customCss.push('li.list-'+p+'-paddingleft-3{padding-left:68px}');
- break;
- case 'num':
- case 'num1':
- customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}');
- break;
- case 'num2':
- customCss.push('li.list-'+p+'-paddingleft-1{padding-left:35px}');
- customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
- break;
- case 'dash':
- customCss.push('li.list-'+p+'-paddingleft{padding-left:35px}');
- break;
- case 'dot':
- customCss.push('li.list-'+p+'-paddingleft{padding-left:20px}');
- }
- }
- customCss.push('.list-paddingleft-1{padding-left:0}');
- customCss.push('.list-paddingleft-2{padding-left:'+me.options.listDefaultPaddingLeft+'px}');
- customCss.push('.list-paddingleft-3{padding-left:'+me.options.listDefaultPaddingLeft*2+'px}');
- //如果不给宽度会在自定应样式里出现滚动条
- utils.cssRule('list', 'ol,ul{margin:0;pading:0;'+(browser.ie ? '' : 'width:95%')+'}li{clear:both;}'+customCss.join('\n'), me.document);
- });
- //单独处理剪切的问题
- me.ready(function(){
- domUtils.on(me.body,'cut',function(){
- setTimeout(function(){
- var rng = me.selection.getRange(),li;
- //trace:3416
- if(!rng.collapsed){
- if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){
- if(!li.nextSibling && domUtils.isEmptyBlock(li)){
- var pn = li.parentNode,node;
- if(node = pn.previousSibling){
- domUtils.remove(pn);
- rng.setStartAtLast(node).collapse(true);
- rng.select(true);
- }else if(node = pn.nextSibling){
- domUtils.remove(pn);
- rng.setStartAtFirst(node).collapse(true);
- rng.select(true);
- }else{
- var tmpNode = me.document.createElement('p');
- domUtils.fillNode(me.document,tmpNode);
- pn.parentNode.insertBefore(tmpNode,pn);
- domUtils.remove(pn);
- rng.setStart(tmpNode,0).collapse(true);
- rng.select(true);
- }
- }
- }
- }
-
- })
- })
- });
-
- function getStyle(node){
- var cls = node.className;
- if(domUtils.hasClass(node,/custom_/)){
- return cls.match(/custom_(\w+)/)[1]
- }
- return domUtils.getStyle(node, 'list-style-type')
-
- }
-
- me.addListener('beforepaste',function(type,html){
- var me = this,
- rng = me.selection.getRange(),li;
- var root = UE.htmlparser(html.html,true);
- if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){
- var list = li.parentNode,tagName = list.tagName == 'OL' ? 'ul':'ol';
- utils.each(root.getNodesByTagName(tagName),function(n){
- n.tagName = list.tagName;
- n.setAttr();
- if(n.parentNode === root){
- type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
- }else{
- var className = n.parentNode.getAttr('class');
- if(className && /custom_/.test(className)){
- type = className.match(/custom_(\w+)/)[1]
- }else{
- type = n.parentNode.getStyle('list-style-type');
- }
- if(!type){
- type = list.tagName == 'OL' ? 'decimal' : 'disc';
- }
- }
- var index = utils.indexOf(listStyle[list.tagName], type);
- if(n.parentNode !== root)
- index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
- var currentStyle = listStyle[list.tagName][index];
- if(customStyle[currentStyle]){
- n.setAttr('class', 'custom_' + currentStyle)
-
- }else{
- n.setStyle('list-style-type',currentStyle)
- }
- })
-
- }
-
- html.html = root.toHtml();
- });
- //导出时,去掉p标签
- me.getOpt('disablePInList') === true && me.addOutputRule(function(root){
- utils.each(root.getNodesByTagName('li'),function(li){
- var newChildrens = [],index=0;
- utils.each(li.children,function(n){
- if(n.tagName == 'p'){
- var tmpNode;
- while(tmpNode = n.children.pop()) {
- newChildrens.splice(index,0,tmpNode);
- tmpNode.parentNode = li;
- lastNode = tmpNode;
- }
- tmpNode = newChildrens[newChildrens.length-1];
- if(!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br'){
- var br = UE.uNode.createElement('br');
- br.parentNode = li;
- newChildrens.push(br);
- }
-
- index = newChildrens.length;
- }
- });
- if(newChildrens.length){
- li.children = newChildrens;
- }
- });
- });
- //进入编辑器的li要套p标签
- me.addInputRule(function(root){
- utils.each(root.getNodesByTagName('li'),function(li){
- var tmpP = UE.uNode.createElement('p');
- for(var i= 0,ci;ci=li.children[i];){
- if(ci.type == 'text' || dtd.p[ci.tagName]){
- tmpP.appendChild(ci);
- }else{
- if(tmpP.firstChild()){
- li.insertBefore(tmpP,ci);
- tmpP = UE.uNode.createElement('p');
- i = i + 2;
- }else{
- i++;
- }
-
- }
- }
- if(tmpP.firstChild() && !tmpP.parentNode || !li.firstChild()){
- li.appendChild(tmpP);
- }
- //trace:3357
- //p不能为空
- if (!tmpP.firstChild()) {
- tmpP.innerHTML(browser.ie ? ' ' : ' ')
- }
- //去掉末尾的空白
- var p = li.firstChild();
- var lastChild = p.lastChild();
- if(lastChild && lastChild.type == 'text' && /^\s*$/.test(lastChild.data)){
- p.removeChild(lastChild)
- }
- });
- if(me.options.autoTransWordToList){
- var orderlisttype = {
- 'num1':/^\d+\)/,
- 'decimal':/^\d+\./,
- 'lower-alpha':/^[a-z]+\)/,
- 'upper-alpha':/^[A-Z]+\./,
- 'cn':/^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
- 'cn2':/^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
- },
- unorderlisttype = {
- 'square':'n'
- };
- function checkListType(content,container){
- var span = container.firstChild();
- if(span && span.type == 'element' && span.tagName == 'span' && /Wingdings|Symbol/.test(span.getStyle('font-family'))){
- for(var p in unorderlisttype){
- if(unorderlisttype[p] == span.data){
- return p
- }
- }
- return 'disc'
- }
- for(var p in orderlisttype){
- if(orderlisttype[p].test(content)){
- return p;
- }
- }
-
- }
- utils.each(root.getNodesByTagName('p'),function(node){
- if(node.getAttr('class') != 'MsoListParagraph'){
- return
- }
-
- //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视
- node.setStyle('margin','');
- node.setStyle('margin-left','');
- node.setAttr('class','');
-
- function appendLi(list,p,type){
- if(list.tagName == 'ol'){
- if(browser.ie){
- var first = p.firstChild();
- if(first.type =='element' && first.tagName == 'span' && orderlisttype[type].test(first.innerText())){
- p.removeChild(first);
- }
- }else{
- p.innerHTML(p.innerHTML().replace(orderlisttype[type],''));
- }
- }else{
- p.removeChild(p.firstChild())
- }
-
- var li = UE.uNode.createElement('li');
- li.appendChild(p);
- list.appendChild(li);
- }
- var tmp = node,type,cacheNode = node;
-
- if(node.parentNode.tagName != 'li' && (type = checkListType(node.innerText(),node))){
-
- var list = UE.uNode.createElement(me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul');
- if(customStyle[type]){
- list.setAttr('class','custom_'+type)
- }else{
- list.setStyle('list-style-type',type)
- }
- while(node && node.parentNode.tagName != 'li' && checkListType(node.innerText(),node)){
- tmp = node.nextSibling();
- if(!tmp){
- node.parentNode.insertBefore(list,node)
- }
- appendLi(list,node,type);
- node = tmp;
- }
- if(!list.parentNode && node && node.parentNode){
- node.parentNode.insertBefore(list,node)
- }
- }
- var span = cacheNode.firstChild();
- if(span && span.type == 'element' && span.tagName == 'span' && /^\s*( )+\s*$/.test(span.innerText())){
- span.parentNode.removeChild(span)
- }
- })
- }
-
- });
-
- //调整索引标签
- me.addListener('contentchange',function(){
- adjustListStyle(me.document)
- });
-
- function adjustListStyle(doc,ignore){
- utils.each(domUtils.getElementsByTagName(doc,'ol ul'),function(node){
-
- if(!domUtils.inDoc(node,doc))
- return;
-
- var parent = node.parentNode;
- if(parent.tagName == node.tagName){
- var nodeStyleType = getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc'),
- parentStyleType = getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc');
- if(nodeStyleType == parentStyleType){
- var styleIndex = utils.indexOf(listStyle[node.tagName], nodeStyleType);
- styleIndex = styleIndex + 1 == listStyle[node.tagName].length ? 0 : styleIndex + 1;
- setListStyle(node,listStyle[node.tagName][styleIndex])
- }
-
- }
- var index = 0,type = 2;
- if( domUtils.hasClass(node,/custom_/)){
- if(!(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/))){
- type = 1;
- }
- }else{
- if(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/)){
- type = 3;
- }
- }
-
- var style = domUtils.getStyle(node, 'list-style-type');
- style && (node.style.cssText = 'list-style-type:' + style);
- node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/,'')) + ' list-paddingleft-' + type;
- utils.each(domUtils.getElementsByTagName(node,'li'),function(li){
- li.style.cssText && (li.style.cssText = '');
- if(!li.firstChild){
- domUtils.remove(li);
- return;
- }
- if(li.parentNode !== node){
- return;
- }
- index++;
- if(domUtils.hasClass(node,/custom_/) ){
- var paddingLeft = 1,currentStyle = getStyle(node);
- if(node.tagName == 'OL'){
- if(currentStyle){
- switch(currentStyle){
- case 'cn' :
- case 'cn1':
- case 'cn2':
- if(index > 10 && (index % 10 == 0 || index > 10 && index < 20)){
- paddingLeft = 2
- }else if(index > 20){
- paddingLeft = 3
- }
- break;
- case 'num2' :
- if(index > 9){
- paddingLeft = 2
- }
- }
- }
- li.className = 'list-'+customStyle[currentStyle]+ index + ' ' + 'list-'+currentStyle+'-paddingleft-' + paddingLeft;
- }else{
- li.className = 'list-'+customStyle[currentStyle] + ' ' + 'list-'+currentStyle+'-paddingleft';
- }
- }else{
- li.className = li.className.replace(/list-[\w\-]+/gi,'');
- }
- var className = li.getAttribute('class');
- if(className !== null && !className.replace(/\s/g,'')){
- domUtils.removeAttributes(li,'class')
- }
- });
- !ignore && adjustList(node,node.tagName.toLowerCase(),getStyle(node)||domUtils.getStyle(node, 'list-style-type'),true);
- })
- }
- function adjustList(list, tag, style,ignoreEmpty) {
- var nextList = list.nextSibling;
- if (nextList && nextList.nodeType == 1 && nextList.tagName.toLowerCase() == tag && (getStyle(nextList) || domUtils.getStyle(nextList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
- domUtils.moveChild(nextList, list);
- if (nextList.childNodes.length == 0) {
- domUtils.remove(nextList);
- }
- }
- if(nextList && domUtils.isFillChar(nextList)){
- domUtils.remove(nextList);
- }
- var preList = list.previousSibling;
- if (preList && preList.nodeType == 1 && preList.tagName.toLowerCase() == tag && (getStyle(preList) || domUtils.getStyle(preList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
- domUtils.moveChild(list, preList);
- }
- if(preList && domUtils.isFillChar(preList)){
- domUtils.remove(preList);
- }
- !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list);
- if(getStyle(list)){
- adjustListStyle(list.ownerDocument,true)
- }
- }
-
- function setListStyle(list,style){
- if(customStyle[style]){
- list.className = 'custom_' + style;
- }
- try{
- domUtils.setStyle(list, 'list-style-type', style);
- }catch(e){}
- }
- function clearEmptySibling(node) {
- var tmpNode = node.previousSibling;
- if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
- domUtils.remove(tmpNode);
- }
- tmpNode = node.nextSibling;
- if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
- domUtils.remove(tmpNode);
- }
- }
-
- me.addListener('keydown', function (type, evt) {
- function preventAndSave() {
- evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
- me.fireEvent('contentchange');
- me.undoManger && me.undoManger.save();
- }
- function findList(node,filterFn){
- while(node && !domUtils.isBody(node)){
- if(filterFn(node)){
- return null
- }
- if(node.nodeType == 1 && /[ou]l/i.test(node.tagName)){
- return node;
- }
- node = node.parentNode;
- }
- return null;
- }
- var keyCode = evt.keyCode || evt.which;
- if (keyCode == 13 && !evt.shiftKey) {//回车
- var rng = me.selection.getRange(),
- parent = domUtils.findParent(rng.startContainer,function(node){return domUtils.isBlockElm(node)},true),
- li = domUtils.findParentByTagName(rng.startContainer,'li',true);
- if(parent && parent.tagName != 'PRE' && !li){
- var html = parent.innerHTML.replace(new RegExp(domUtils.fillChar, 'g'),'');
- if(/^\s*1\s*\.[^\d]/.test(html)){
- parent.innerHTML = html.replace(/^\s*1\s*\./,'');
- rng.setStartAtLast(parent).collapse(true).select();
- me.__hasEnterExecCommand = true;
- me.execCommand('insertorderedlist');
- me.__hasEnterExecCommand = false;
- }
- }
- var range = me.selection.getRange(),
- start = findList(range.startContainer,function (node) {
- return node.tagName == 'TABLE';
- }),
- end = range.collapsed ? start : findList(range.endContainer,function (node) {
- return node.tagName == 'TABLE';
- });
-
- if (start && end && start === end) {
-
- if (!range.collapsed) {
- start = domUtils.findParentByTagName(range.startContainer, 'li', true);
- end = domUtils.findParentByTagName(range.endContainer, 'li', true);
- if (start && end && start === end) {
- range.deleteContents();
- li = domUtils.findParentByTagName(range.startContainer, 'li', true);
- if (li && domUtils.isEmptyBlock(li)) {
-
- pre = li.previousSibling;
- next = li.nextSibling;
- p = me.document.createElement('p');
-
- domUtils.fillNode(me.document, p);
- parentList = li.parentNode;
- if (pre && next) {
- range.setStart(next, 0).collapse(true).select(true);
- domUtils.remove(li);
-
- } else {
- if (!pre && !next || !pre) {
-
- parentList.parentNode.insertBefore(p, parentList);
-
-
- } else {
- li.parentNode.parentNode.insertBefore(p, parentList.nextSibling);
- }
- domUtils.remove(li);
- if (!parentList.firstChild) {
- domUtils.remove(parentList);
- }
- range.setStart(p, 0).setCursor();
-
-
- }
- preventAndSave();
- return;
-
- }
- } else {
- var tmpRange = range.cloneRange(),
- bk = tmpRange.collapse(false).createBookmark();
-
- range.deleteContents();
- tmpRange.moveToBookmark(bk);
- var li = domUtils.findParentByTagName(tmpRange.startContainer, 'li', true);
-
- clearEmptySibling(li);
- tmpRange.select();
- preventAndSave();
- return;
- }
- }
-
-
- li = domUtils.findParentByTagName(range.startContainer, 'li', true);
-
- if (li) {
- if (domUtils.isEmptyBlock(li)) {
- bk = range.createBookmark();
- var parentList = li.parentNode;
- if (li !== parentList.lastChild) {
- domUtils.breakParent(li, parentList);
- clearEmptySibling(li);
- } else {
-
- parentList.parentNode.insertBefore(li, parentList.nextSibling);
- if (domUtils.isEmptyNode(parentList)) {
- domUtils.remove(parentList);
- }
- }
- //嵌套不处理
- if (!dtd.$list[li.parentNode.tagName]) {
-
- if (!domUtils.isBlockElm(li.firstChild)) {
- p = me.document.createElement('p');
- li.parentNode.insertBefore(p, li);
- while (li.firstChild) {
- p.appendChild(li.firstChild);
- }
- domUtils.remove(li);
- } else {
- domUtils.remove(li, true);
- }
- }
- range.moveToBookmark(bk).select();
-
-
- } else {
- var first = li.firstChild;
- if (!first || !domUtils.isBlockElm(first)) {
- var p = me.document.createElement('p');
-
- !li.firstChild && domUtils.fillNode(me.document, p);
- while (li.firstChild) {
-
- p.appendChild(li.firstChild);
- }
- li.appendChild(p);
- first = p;
- }
-
- var span = me.document.createElement('span');
-
- range.insertNode(span);
- domUtils.breakParent(span, li);
-
- var nextLi = span.nextSibling;
- first = nextLi.firstChild;
-
- if (!first) {
- p = me.document.createElement('p');
-
- domUtils.fillNode(me.document, p);
- nextLi.appendChild(p);
- first = p;
- }
- if (domUtils.isEmptyNode(first)) {
- first.innerHTML = '';
- domUtils.fillNode(me.document, first);
- }
-
- range.setStart(first, 0).collapse(true).shrinkBoundary().select();
- domUtils.remove(span);
- var pre = nextLi.previousSibling;
- if (pre && domUtils.isEmptyBlock(pre)) {
- pre.innerHTML = '';
- domUtils.fillNode(me.document, pre.firstChild);
- }
-
- }
-// }
- preventAndSave();
- }
-
-
- }
-
-
- }
- if (keyCode == 8) {
- //修中ie中li下的问题
- range = me.selection.getRange();
- if (range.collapsed && domUtils.isStartInblock(range)) {
- tmpRange = range.cloneRange().trimBoundary();
- li = domUtils.findParentByTagName(range.startContainer, 'li', true);
- //要在li的最左边,才能处理
- if (li && domUtils.isStartInblock(tmpRange)) {
- start = domUtils.findParentByTagName(range.startContainer, 'p', true);
- if (start && start !== li.firstChild) {
- var parentList = domUtils.findParentByTagName(start,['ol','ul']);
- domUtils.breakParent(start,parentList);
- clearEmptySibling(start);
- me.fireEvent('contentchange');
- range.setStart(start,0).setCursor(false,true);
- me.fireEvent('saveScene');
- domUtils.preventDefault(evt);
- return;
- }
-
- if (li && (pre = li.previousSibling)) {
- if (keyCode == 46 && li.childNodes.length) {
- return;
- }
- //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
- if (dtd.$list[pre.tagName]) {
- pre = pre.lastChild;
- }
- me.undoManger && me.undoManger.save();
- first = li.firstChild;
- if (domUtils.isBlockElm(first)) {
- if (domUtils.isEmptyNode(first)) {
-// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
- pre.appendChild(first);
- range.setStart(first, 0).setCursor(false, true);
- //first不是唯一的节点
- while (li.firstChild) {
- pre.appendChild(li.firstChild);
- }
- } else {
-
- span = me.document.createElement('span');
- range.insertNode(span);
- //判断pre是否是空的节点,如果是
类型的空节点,干掉p标签防止它占位
- if (domUtils.isEmptyBlock(pre)) {
- pre.innerHTML = '';
- }
- domUtils.moveChild(li, pre);
- range.setStartBefore(span).collapse(true).select(true);
-
- domUtils.remove(span);
-
- }
- } else {
- if (domUtils.isEmptyNode(li)) {
- var p = me.document.createElement('p');
- pre.appendChild(p);
- range.setStart(p, 0).setCursor();
-// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
- } else {
- range.setEnd(pre, pre.childNodes.length).collapse().select(true);
- while (li.firstChild) {
- pre.appendChild(li.firstChild);
- }
- }
- }
- domUtils.remove(li);
- me.fireEvent('contentchange');
- me.fireEvent('saveScene');
- domUtils.preventDefault(evt);
- return;
-
- }
- //trace:980
-
- if (li && !li.previousSibling) {
- var parentList = li.parentNode;
- var bk = range.createBookmark();
- if(domUtils.isTagNode(parentList.parentNode,'ol ul')){
- parentList.parentNode.insertBefore(li,parentList);
- if(domUtils.isEmptyNode(parentList)){
- domUtils.remove(parentList)
- }
- }else{
-
- while(li.firstChild){
- parentList.parentNode.insertBefore(li.firstChild,parentList);
- }
-
- domUtils.remove(li);
- if(domUtils.isEmptyNode(parentList)){
- domUtils.remove(parentList)
- }
-
- }
- range.moveToBookmark(bk).setCursor(false,true);
- me.fireEvent('contentchange');
- me.fireEvent('saveScene');
- domUtils.preventDefault(evt);
- return;
-
- }
-
-
- }
-
-
- }
-
- }
- });
-
- me.addListener('keyup',function(type, evt){
- var keyCode = evt.keyCode || evt.which;
- if (keyCode == 8) {
- var rng = me.selection.getRange(),list;
- if(list = domUtils.findParentByTagName(rng.startContainer,['ol', 'ul'],true)){
- adjustList(list,list.tagName.toLowerCase(),getStyle(list)||domUtils.getComputedStyle(list,'list-style-type'),true)
- }
- }
- });
- //处理tab键
- me.addListener('tabkeydown',function(){
-
- var range = me.selection.getRange();
-
- //控制级数
- function checkLevel(li){
- if(me.options.maxListLevel != -1){
- var level = li.parentNode,levelNum = 0;
- while(/[ou]l/i.test(level.tagName)){
- levelNum++;
- level = level.parentNode;
- }
- if(levelNum >= me.options.maxListLevel){
- return true;
- }
- }
- }
- //只以开始为准
- //todo 后续改进
- var li = domUtils.findParentByTagName(range.startContainer, 'li', true);
- if(li){
-
- var bk;
- if(range.collapsed){
- if(checkLevel(li))
- return true;
- var parentLi = li.parentNode,
- list = me.document.createElement(parentLi.tagName),
- index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type'));
- index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
- var currentStyle = listStyle[list.tagName][index];
- setListStyle(list,currentStyle);
- if(domUtils.isStartInblock(range)){
- me.fireEvent('saveScene');
- bk = range.createBookmark();
- parentLi.insertBefore(list, li);
- list.appendChild(li);
- adjustList(list,list.tagName.toLowerCase(),currentStyle);
- me.fireEvent('contentchange');
- range.moveToBookmark(bk).select(true);
- return true;
- }
- }else{
- me.fireEvent('saveScene');
- bk = range.createBookmark();
- for(var i= 0,closeList,parents = domUtils.findParents(li),ci;ci=parents[i++];){
- if(domUtils.isTagNode(ci,'ol ul')){
- closeList = ci;
- break;
- }
- }
- var current = li;
- if(bk.end){
- while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){
- if(checkLevel(current)){
- current = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList});
- continue;
- }
- var parentLi = current.parentNode,
- list = me.document.createElement(parentLi.tagName),
- index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type'));
- var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
- var currentStyle = listStyle[list.tagName][currentIndex];
- setListStyle(list,currentStyle);
- parentLi.insertBefore(list, current);
- while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){
- li = current.nextSibling;
- list.appendChild(current);
- if(!li || domUtils.isTagNode(li,'ol ul')){
- if(li){
- while(li = li.firstChild){
- if(li.tagName == 'LI'){
- break;
- }
- }
- }else{
- li = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList});
- }
- break;
- }
- current = li;
- }
- adjustList(list,list.tagName.toLowerCase(),currentStyle);
- current = li;
- }
- }
- me.fireEvent('contentchange');
- range.moveToBookmark(bk).select();
- return true;
- }
- }
-
- });
- function getLi(start){
- while(start && !domUtils.isBody(start)){
- if(start.nodeName == 'TABLE'){
- return null;
- }
- if(start.nodeName == 'LI'){
- return start
- }
- start = start.parentNode;
- }
- }
-
- /**
- * 有序列表,与“insertunorderedlist”命令互斥
- * @command insertorderedlist
- * @method execCommand
- * @param { String } command 命令字符串
- * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
- * @example
- * ```javascript
- * editor.execCommand( 'insertorderedlist','decimal');
- * ```
- */
- /**
- * 查询当前选区内容是否有序列表
- * @command insertorderedlist
- * @method queryCommandState
- * @param { String } cmd 命令字符串
- * @return { int } 如果当前选区是有序列表返回1,否则返回0
- * @example
- * ```javascript
- * editor.queryCommandState( 'insertorderedlist' );
- * ```
- */
- /**
- * 查询当前选区内容是否有序列表
- * @command insertorderedlist
- * @method queryCommandValue
- * @param { String } cmd 命令字符串
- * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
- * @example
- * ```javascript
- * editor.queryCommandValue( 'insertorderedlist' );
- * ```
- */
-
- /**
- * 无序列表,与“insertorderedlist”命令互斥
- * @command insertunorderedlist
- * @method execCommand
- * @param { String } command 命令字符串
- * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
- * @example
- * ```javascript
- * editor.execCommand( 'insertunorderedlist','circle');
- * ```
- */
- /**
- * 查询当前是否有word文档粘贴进来的图片
- * @command insertunorderedlist
- * @method insertunorderedlist
- * @param { String } command 命令字符串
- * @return { int } 如果当前选区是无序列表返回1,否则返回0
- * @example
- * ```javascript
- * editor.queryCommandState( 'insertunorderedlist' );
- * ```
- */
- /**
- * 查询当前选区内容是否有序列表
- * @command insertunorderedlist
- * @method queryCommandValue
- * @param { String } command 命令字符串
- * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
- * @example
- * ```javascript
- * editor.queryCommandValue( 'insertunorderedlist' );
- * ```
- */
-
- me.commands['insertorderedlist'] =
- me.commands['insertunorderedlist'] = {
- execCommand:function (command, style) {
-
- if (!style) {
- style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc';
- }
- var me = this,
- range = this.selection.getRange(),
- filterFn = function (node) {
- return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
- },
- tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul',
- frag = me.document.createDocumentFragment();
- //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
- //range.shrinkBoundary();//.adjustmentBoundary();
- range.adjustmentBoundary().shrinkBoundary();
- var bko = range.createBookmark(true),
- start = getLi(me.document.getElementById(bko.start)),
- modifyStart = 0,
- end = getLi(me.document.getElementById(bko.end)),
- modifyEnd = 0,
- startParent, endParent,
- list, tmp;
-
- if (start || end) {
- start && (startParent = start.parentNode);
- if (!bko.end) {
- end = start;
- }
- end && (endParent = end.parentNode);
-
- if (startParent === endParent) {
- while (start !== end) {
- tmp = start;
- start = start.nextSibling;
- if (!domUtils.isBlockElm(tmp.firstChild)) {
- var p = me.document.createElement('p');
- while (tmp.firstChild) {
- p.appendChild(tmp.firstChild);
- }
- tmp.appendChild(p);
- }
- frag.appendChild(tmp);
- }
- tmp = me.document.createElement('span');
- startParent.insertBefore(tmp, end);
- if (!domUtils.isBlockElm(end.firstChild)) {
- p = me.document.createElement('p');
- while (end.firstChild) {
- p.appendChild(end.firstChild);
- }
- end.appendChild(p);
- }
- frag.appendChild(end);
- domUtils.breakParent(tmp, startParent);
- if (domUtils.isEmptyNode(tmp.previousSibling)) {
- domUtils.remove(tmp.previousSibling);
- }
- if (domUtils.isEmptyNode(tmp.nextSibling)) {
- domUtils.remove(tmp.nextSibling)
- }
- var nodeStyle = getStyle(startParent) || domUtils.getComputedStyle(startParent, 'list-style-type') || (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc');
- if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
- for (var i = 0, ci, tmpFrag = me.document.createDocumentFragment(); ci = frag.firstChild;) {
- if(domUtils.isTagNode(ci,'ol ul')){
-// 删除时,子列表不处理
-// utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
-// while(li.firstChild){
-// tmpFrag.appendChild(li.firstChild);
-// }
-//
-// });
- tmpFrag.appendChild(ci);
- }else{
- while (ci.firstChild) {
-
- tmpFrag.appendChild(ci.firstChild);
- domUtils.remove(ci);
- }
- }
-
- }
- tmp.parentNode.insertBefore(tmpFrag, tmp);
- } else {
- list = me.document.createElement(tag);
- setListStyle(list,style);
- list.appendChild(frag);
- tmp.parentNode.insertBefore(list, tmp);
- }
-
- domUtils.remove(tmp);
- list && adjustList(list, tag, style);
- range.moveToBookmark(bko).select();
- return;
- }
- //开始
- if (start) {
- while (start) {
- tmp = start.nextSibling;
- if (domUtils.isTagNode(start, 'ol ul')) {
- frag.appendChild(start);
- } else {
- var tmpfrag = me.document.createDocumentFragment(),
- hasBlock = 0;
- while (start.firstChild) {
- if (domUtils.isBlockElm(start.firstChild)) {
- hasBlock = 1;
- }
- tmpfrag.appendChild(start.firstChild);
- }
- if (!hasBlock) {
- var tmpP = me.document.createElement('p');
- tmpP.appendChild(tmpfrag);
- frag.appendChild(tmpP);
- } else {
- frag.appendChild(tmpfrag);
- }
- domUtils.remove(start);
- }
-
- start = tmp;
- }
- startParent.parentNode.insertBefore(frag, startParent.nextSibling);
- if (domUtils.isEmptyNode(startParent)) {
- range.setStartBefore(startParent);
- domUtils.remove(startParent);
- } else {
- range.setStartAfter(startParent);
- }
- modifyStart = 1;
- }
-
- if (end && domUtils.inDoc(endParent, me.document)) {
- //结束
- start = endParent.firstChild;
- while (start && start !== end) {
- tmp = start.nextSibling;
- if (domUtils.isTagNode(start, 'ol ul')) {
- frag.appendChild(start);
- } else {
- tmpfrag = me.document.createDocumentFragment();
- hasBlock = 0;
- while (start.firstChild) {
- if (domUtils.isBlockElm(start.firstChild)) {
- hasBlock = 1;
- }
- tmpfrag.appendChild(start.firstChild);
- }
- if (!hasBlock) {
- tmpP = me.document.createElement('p');
- tmpP.appendChild(tmpfrag);
- frag.appendChild(tmpP);
- } else {
- frag.appendChild(tmpfrag);
- }
- domUtils.remove(start);
- }
- start = tmp;
- }
- var tmpDiv = domUtils.createElement(me.document, 'div', {
- 'tmpDiv':1
- });
- domUtils.moveChild(end, tmpDiv);
-
- frag.appendChild(tmpDiv);
- domUtils.remove(end);
- endParent.parentNode.insertBefore(frag, endParent);
- range.setEndBefore(endParent);
- if (domUtils.isEmptyNode(endParent)) {
- domUtils.remove(endParent);
- }
-
- modifyEnd = 1;
- }
-
-
- }
-
- if (!modifyStart) {
- range.setStartBefore(me.document.getElementById(bko.start));
- }
- if (bko.end && !modifyEnd) {
- range.setEndAfter(me.document.getElementById(bko.end));
- }
- range.enlarge(true, function (node) {
- return notExchange[node.tagName];
- });
-
- frag = me.document.createDocumentFragment();
-
- var bk = range.createBookmark(),
- current = domUtils.getNextDomNode(bk.start, false, filterFn),
- tmpRange = range.cloneRange(),
- tmpNode,
- block = domUtils.isBlockElm;
-
- while (current && current !== bk.end && (domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING)) {
-
- if (current.nodeType == 3 || dtd.li[current.tagName]) {
- if (current.nodeType == 1 && dtd.$list[current.tagName]) {
- while (current.firstChild) {
- frag.appendChild(current.firstChild);
- }
- tmpNode = domUtils.getNextDomNode(current, false, filterFn);
- domUtils.remove(current);
- current = tmpNode;
- continue;
-
- }
- tmpNode = current;
- tmpRange.setStartBefore(current);
-
- while (current && current !== bk.end && (!block(current) || domUtils.isBookmarkNode(current) )) {
- tmpNode = current;
- current = domUtils.getNextDomNode(current, false, null, function (node) {
- return !notExchange[node.tagName];
- });
- }
-
- if (current && block(current)) {
- tmp = domUtils.getNextDomNode(tmpNode, false, filterFn);
- if (tmp && domUtils.isBookmarkNode(tmp)) {
- current = domUtils.getNextDomNode(tmp, false, filterFn);
- tmpNode = tmp;
- }
- }
- tmpRange.setEndAfter(tmpNode);
-
- current = domUtils.getNextDomNode(tmpNode, false, filterFn);
-
- var li = range.document.createElement('li');
-
- li.appendChild(tmpRange.extractContents());
- if(domUtils.isEmptyNode(li)){
- var tmpNode = range.document.createElement('p');
- while(li.firstChild){
- tmpNode.appendChild(li.firstChild)
- }
- li.appendChild(tmpNode);
- }
- frag.appendChild(li);
- } else {
- current = domUtils.getNextDomNode(current, true, filterFn);
- }
- }
- range.moveToBookmark(bk).collapse(true);
- list = me.document.createElement(tag);
- setListStyle(list,style);
- list.appendChild(frag);
- range.insertNode(list);
- //当前list上下看能否合并
- adjustList(list, tag, style);
- //去掉冗余的tmpDiv
- for (var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div'); ci = tmpDivs[i++];) {
- if (ci.getAttribute('tmpDiv')) {
- domUtils.remove(ci, true)
- }
- }
- range.moveToBookmark(bko).select();
-
- },
- queryCommandState:function (command) {
- var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
- var path = this.selection.getStartElementPath();
- for(var i= 0,ci;ci = path[i++];){
- if(ci.nodeName == 'TABLE'){
- return 0
- }
- if(tag == ci.nodeName.toLowerCase()){
- return 1
- };
- }
- return 0;
-
- },
- queryCommandValue:function (command) {
- var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
- var path = this.selection.getStartElementPath(),
- node;
- for(var i= 0,ci;ci = path[i++];){
- if(ci.nodeName == 'TABLE'){
- node = null;
- break;
- }
- if(tag == ci.nodeName.toLowerCase()){
- node = ci;
- break;
- };
- }
- return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null;
- }
- };
-};
-
-
-
-// plugins/source.js
-/**
- * 源码编辑插件
- * @file
- * @since 1.2.6.1
- */
-
-(function (){
- var sourceEditors = {
- textarea: function (editor, holder){
- var textarea = holder.ownerDocument.createElement('textarea');
- textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;';
- // todo: IE下只有onresize属性可用... 很纠结
- if (browser.ie && browser.version < 8) {
- textarea.style.width = holder.offsetWidth + 'px';
- textarea.style.height = holder.offsetHeight + 'px';
- holder.onresize = function (){
- textarea.style.width = holder.offsetWidth + 'px';
- textarea.style.height = holder.offsetHeight + 'px';
- };
- }
- holder.appendChild(textarea);
- return {
- setContent: function (content){
- textarea.value = content;
- },
- getContent: function (){
- return textarea.value;
- },
- select: function (){
- var range;
- if (browser.ie) {
- range = textarea.createTextRange();
- range.collapse(true);
- range.select();
- } else {
- //todo: chrome下无法设置焦点
- textarea.setSelectionRange(0, 0);
- textarea.focus();
- }
- },
- dispose: function (){
- holder.removeChild(textarea);
- // todo
- holder.onresize = null;
- textarea = null;
- holder = null;
- }
- };
- },
- codemirror: function (editor, holder){
-
- var codeEditor = window.CodeMirror(holder, {
- mode: "text/html",
- tabMode: "indent",
- lineNumbers: true,
- lineWrapping:true
- });
- var dom = codeEditor.getWrapperElement();
- dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
- codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;';
- codeEditor.refresh();
- return {
- getCodeMirror:function(){
- return codeEditor;
- },
- setContent: function (content){
- codeEditor.setValue(content);
- },
- getContent: function (){
- return codeEditor.getValue();
- },
- select: function (){
- codeEditor.focus();
- },
- dispose: function (){
- holder.removeChild(dom);
- dom = null;
- codeEditor = null;
- }
- };
- }
- };
-
- UE.plugins['source'] = function (){
- var me = this;
- var opt = this.options;
- var sourceMode = false;
- var sourceEditor;
- var orgSetContent;
- opt.sourceEditor = browser.ie ? 'textarea' : (opt.sourceEditor || 'codemirror');
-
- me.setOpt({
- sourceEditorFirst:false
- });
- function createSourceEditor(holder){
- return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder);
- }
-
- var bakCssText;
- //解决在源码模式下getContent不能得到最新的内容问题
- var oldGetContent,
- bakAddress;
-
- /**
- * 切换源码模式和编辑模式
- * @command source
- * @method execCommand
- * @param { String } cmd 命令字符串
- * @example
- * ```javascript
- * editor.execCommand( 'source');
- * ```
- */
-
- /**
- * 查询当前编辑区域的状态是源码模式还是可视化模式
- * @command source
- * @method queryCommandState
- * @param { String } cmd 命令字符串
- * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
- * @example
- * ```javascript
- * editor.queryCommandState( 'source' );
- * ```
- */
-
- me.commands['source'] = {
- execCommand: function (){
-
- sourceMode = !sourceMode;
- if (sourceMode) {
- bakAddress = me.selection.getRange().createAddress(false,true);
- me.undoManger && me.undoManger.save(true);
- if(browser.gecko){
- me.body.contentEditable = false;
- }
-
- bakCssText = me.iframe.style.cssText;
- me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;';
-
-
- me.fireEvent('beforegetcontent');
- var root = UE.htmlparser(me.body.innerHTML);
- me.filterOutputRule(root);
- root.traversal(function (node) {
- if (node.type == 'element') {
- switch (node.tagName) {
- case 'td':
- case 'th':
- case 'caption':
- if(node.children && node.children.length == 1){
- if(node.firstChild().tagName == 'br' ){
- node.removeChild(node.firstChild())
- }
- };
- break;
- case 'pre':
- node.innerText(node.innerText().replace(/ /g,' '))
-
- }
- }
- });
-
- me.fireEvent('aftergetcontent');
-
- var content = root.toHtml(true);
-
- sourceEditor = createSourceEditor(me.iframe.parentNode);
-
- sourceEditor.setContent(content);
-
- orgSetContent = me.setContent;
-
- me.setContent = function(html){
- //这里暂时不触发事件,防止报错
- var root = UE.htmlparser(html);
- me.filterInputRule(root);
- html = root.toHtml();
- sourceEditor.setContent(html);
- };
-
- setTimeout(function (){
- sourceEditor.select();
- me.addListener('fullscreenchanged', function(){
- try{
- sourceEditor.getCodeMirror().refresh()
- }catch(e){}
- });
- });
-
- //重置getContent,源码模式下取值也能是最新的数据
- oldGetContent = me.getContent;
- me.getContent = function (){
- return sourceEditor.getContent() || '' + (browser.ie ? '' : ' ')+' ';
- };
- } else {
- me.iframe.style.cssText = bakCssText;
- var cont = sourceEditor.getContent() || '' + (browser.ie ? '' : ' ')+' ';
- //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
- cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
- if(b && !dtd.$inlineWithA[b.toLowerCase()]){
- return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
- }
- return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
- });
-
- me.setContent = orgSetContent;
-
- me.setContent(cont);
- sourceEditor.dispose();
- sourceEditor = null;
- //还原getContent方法
- me.getContent = oldGetContent;
- var first = me.body.firstChild;
- //trace:1106 都删除空了,下边会报错,所以补充一个p占位
- if(!first){
- me.body.innerHTML = ''+(browser.ie?'':' ')+' ';
- first = me.body.firstChild;
- }
-
-
- //要在ifm为显示时ff才能取到selection,否则报错
- //这里不能比较位置了
- me.undoManger && me.undoManger.save(true);
-
- if(browser.gecko){
-
- var input = document.createElement('input');
- input.style.cssText = 'position:absolute;left:0;top:-32768px';
-
- document.body.appendChild(input);
-
- me.body.contentEditable = false;
- setTimeout(function(){
- domUtils.setViewportOffset(input, { left: -32768, top: 0 });
- input.focus();
- setTimeout(function(){
- me.body.contentEditable = true;
- me.selection.getRange().moveToAddress(bakAddress).select(true);
- domUtils.remove(input);
- });
-
- });
- }else{
- //ie下有可能报错,比如在代码顶头的情况
- try{
- me.selection.getRange().moveToAddress(bakAddress).select(true);
- }catch(e){}
-
- }
- }
- this.fireEvent('sourcemodechanged', sourceMode);
- },
- queryCommandState: function (){
- return sourceMode|0;
- },
- notNeedUndo : 1
- };
- var oldQueryCommandState = me.queryCommandState;
-
- me.queryCommandState = function (cmdName){
- cmdName = cmdName.toLowerCase();
- if (sourceMode) {
- //源码模式下可以开启的命令
- return cmdName in {
- 'source' : 1,
- 'fullscreen' : 1
- } ? 1 : -1
- }
- return oldQueryCommandState.apply(this, arguments);
- };
-
- if(opt.sourceEditor == "codemirror"){
-
- me.addListener("ready",function(){
- utils.loadFile(document,{
- src : opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
- tag : "script",
- type : "text/javascript",
- defer : "defer"
- },function(){
- if(opt.sourceEditorFirst){
- setTimeout(function(){
- me.execCommand("source");
- },0);
- }
- });
- utils.loadFile(document,{
- tag : "link",
- rel : "stylesheet",
- type : "text/css",
- href : opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
- });
-
- });
- }
-
- };
-
-})();
-
-// plugins/enterkey.js
-///import core
-///import plugins/undo.js
-///commands 设置回车标签p或br
-///commandsName EnterKey
-///commandsTitle 设置回车标签p或br
-/**
- * @description 处理回车
- * @author zhanyi
- */
-UE.plugins['enterkey'] = function() {
- var hTag,
- me = this,
- tag = me.options.enterTag;
- me.addListener('keyup', function(type, evt) {
-
- var keyCode = evt.keyCode || evt.which;
- if (keyCode == 13) {
- var range = me.selection.getRange(),
- start = range.startContainer,
- doSave;
-
- //修正在h1-h6里边回车后不能嵌套p的问题
- if (!browser.ie) {
-
- if (/h\d/i.test(hTag)) {
- if (browser.gecko) {
- var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
- if (!h) {
- me.document.execCommand('formatBlock', false, '');
- doSave = 1;
- }
- } else {
- //chrome remove div
- if (start.nodeType == 1) {
- var tmp = me.document.createTextNode(''),div;
- range.insertNode(tmp);
- div = domUtils.findParentByTagName(tmp, 'div', true);
- if (div) {
- var p = me.document.createElement('p');
- while (div.firstChild) {
- p.appendChild(div.firstChild);
- }
- div.parentNode.insertBefore(p, div);
- domUtils.remove(div);
- range.setStartBefore(tmp).setCursor();
- doSave = 1;
- }
- domUtils.remove(tmp);
-
- }
- }
-
- if (me.undoManger && doSave) {
- me.undoManger.save();
- }
- }
- //没有站位符,会出现多行的问题
- browser.opera && range.select();
- }else{
- me.fireEvent('saveScene',true,true)
- }
- }
- });
-
- me.addListener('keydown', function(type, evt) {
- var keyCode = evt.keyCode || evt.which;
- if (keyCode == 13) {//回车
- if(me.fireEvent('beforeenterkeydown')){
- domUtils.preventDefault(evt);
- return;
- }
- me.fireEvent('saveScene',true,true);
- hTag = '';
-
-
- var range = me.selection.getRange();
-
- if (!range.collapsed) {
- //跨td不能删
- var start = range.startContainer,
- end = range.endContainer,
- startTd = domUtils.findParentByTagName(start, 'td', true),
- endTd = domUtils.findParentByTagName(end, 'td', true);
- if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
- evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
- return;
- }
- }
- if (tag == 'p') {
-
-
- if (!browser.ie) {
-
- start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
-
- //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
- //trace:2431
- if (!start && !browser.opera) {
-
- me.document.execCommand('formatBlock', false, ' ');
-
- if (browser.gecko) {
- range = me.selection.getRange();
- start = domUtils.findParentByTagName(range.startContainer, 'p', true);
- start && domUtils.removeDirtyAttr(start);
- }
-
-
- } else {
- hTag = start.tagName;
- start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
- }
-
- }
-
- } else {
- evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
-
- if (!range.collapsed) {
- range.deleteContents();
- start = range.startContainer;
- if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
- while (start.nodeType == 1) {
- if (dtd.$empty[start.tagName]) {
- range.setStartBefore(start).setCursor();
- if (me.undoManger) {
- me.undoManger.save();
- }
- return false;
- }
- if (!start.firstChild) {
- var br = range.document.createElement('br');
- start.appendChild(br);
- range.setStart(start, 0).setCursor();
- if (me.undoManger) {
- me.undoManger.save();
- }
- return false;
- }
- start = start.firstChild;
- }
- if (start === range.startContainer.childNodes[range.startOffset]) {
- br = range.document.createElement('br');
- range.insertNode(br).setCursor();
-
- } else {
- range.setStart(start, 0).setCursor();
- }
-
-
- } else {
- br = range.document.createElement('br');
- range.insertNode(br).setStartAfter(br).setCursor();
- }
-
-
- } else {
- br = range.document.createElement('br');
- range.insertNode(br);
- var parent = br.parentNode;
- if (parent.lastChild === br) {
- br.parentNode.insertBefore(br.cloneNode(true), br);
- range.setStartBefore(br);
- } else {
- range.setStartAfter(br);
- }
- range.setCursor();
-
- }
-
- }
-
- }
- });
-};
-
-
-// plugins/keystrokes.js
-/* 处理特殊键的兼容性问题 */
-UE.plugins['keystrokes'] = function() {
- var me = this;
- var collapsed = true;
- me.addListener('keydown', function(type, evt) {
- var keyCode = evt.keyCode || evt.which,
- rng = me.selection.getRange();
-
- //处理全选的情况
- if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
- || keyCode >= 48 && keyCode <= 57 ||
- keyCode >= 96 && keyCode <= 111 || {
- 13:1,
- 8:1,
- 46:1
- }[keyCode])
- ){
-
- var tmpNode = rng.startContainer;
- if(domUtils.isFillChar(tmpNode)){
- rng.setStartBefore(tmpNode)
- }
- tmpNode = rng.endContainer;
- if(domUtils.isFillChar(tmpNode)){
- rng.setEndAfter(tmpNode)
- }
- rng.txtToElmBoundary();
- //结束边界可能放到了br的前边,要把br包含进来
- // x[xxx]
- if(rng.endContainer && rng.endContainer.nodeType == 1){
- tmpNode = rng.endContainer.childNodes[rng.endOffset];
- if(tmpNode && domUtils.isBr(tmpNode)){
- rng.setEndAfter(tmpNode);
- }
- }
- if(rng.startOffset == 0){
- tmpNode = rng.startContainer;
- if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
- tmpNode = rng.endContainer;
- if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
- me.fireEvent('saveScene');
- me.body.innerHTML = ' '+(browser.ie ? '' : ' ')+' ';
- rng.setStart(me.body.firstChild,0).setCursor(false,true);
- me._selectionChange();
- return;
- }
- }
- }
- }
-
- //处理backspace
- if (keyCode == keymap.Backspace) {
- rng = me.selection.getRange();
- collapsed = rng.collapsed;
- if(me.fireEvent('delkeydown',evt)){
- return;
- }
- var start,end;
- //避免按两次删除才能生效的问题
- if(rng.collapsed && rng.inFillChar()){
- start = rng.startContainer;
-
- if(domUtils.isFillChar(start)){
- rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
- domUtils.remove(start)
- }else{
- start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
- rng.startOffset--;
- rng.collapse(true).select(true)
- }
- }
-
- //解决选中control元素不能删除的问题
- if (start = rng.getClosedNode()) {
- me.fireEvent('saveScene');
- rng.setStartBefore(start);
- domUtils.remove(start);
- rng.setCursor();
- me.fireEvent('saveScene');
- domUtils.preventDefault(evt);
- return;
- }
- //阻止在table上的删除
- if (!browser.ie) {
- start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
- end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
- if (start && !end || !start && end || start !== end) {
- evt.preventDefault();
- return;
- }
- }
-
- }
- //处理tab键的逻辑
- if (keyCode == keymap.Tab) {
- //不处理以下标签
- var excludeTagNameForTabKey = {
- 'ol' : 1,
- 'ul' : 1,
- 'table':1
- };
- //处理组件里的tab按下事件
- if(me.fireEvent('tabkeydown',evt)){
- domUtils.preventDefault(evt);
- return;
- }
- var range = me.selection.getRange();
- me.fireEvent('saveScene');
- for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode = me.options.tabNode || ' '; i < tabSize; i++) {
- txt += tabNode;
- }
- var span = me.document.createElement('span');
- span.innerHTML = txt + domUtils.fillChar;
- if (range.collapsed) {
- range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
- } else {
- var filterFn = function(node) {
- return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
-
- };
- //普通的情况
- start = domUtils.findParent(range.startContainer, filterFn,true);
- end = domUtils.findParent(range.endContainer, filterFn,true);
- if (start && end && start === end) {
- range.deleteContents();
- range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
- } else {
- var bookmark = range.createBookmark();
- range.enlarge(true);
- var bookmark2 = range.createBookmark(),
- current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
- while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
- current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
- current = domUtils.getNextDomNode(current, false, filterFn);
- }
- range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
- }
- }
- domUtils.preventDefault(evt)
- }
- //trace:1634
- //ff的del键在容器空的时候,也会删除
- if(browser.gecko && keyCode == 46){
- range = me.selection.getRange();
- if(range.collapsed){
- start = range.startContainer;
- if(domUtils.isEmptyBlock(start)){
- var parent = start.parentNode;
- while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
- start = parent;
- parent = parent.parentNode;
- }
- if(start === parent.lastChild)
- evt.preventDefault();
- return;
- }
- }
- }
- });
- me.addListener('keyup', function(type, evt) {
- var keyCode = evt.keyCode || evt.which,
- rng,me = this;
- if(keyCode == keymap.Backspace){
- if(me.fireEvent('delkeyup')){
- return;
- }
- rng = me.selection.getRange();
- if(rng.collapsed){
- var tmpNode,
- autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
- if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
- if(domUtils.isEmptyBlock(tmpNode)){
- var pre = tmpNode.previousSibling;
- if(pre && pre.nodeName != 'TABLE'){
- domUtils.remove(tmpNode);
- rng.setStartAtLast(pre).setCursor(false,true);
- return;
- }else{
- var next = tmpNode.nextSibling;
- if(next && next.nodeName != 'TABLE'){
- domUtils.remove(tmpNode);
- rng.setStartAtFirst(next).setCursor(false,true);
- return;
- }
- }
- }
- }
- //处理当删除到body时,要重新给p标签展位
- if(domUtils.isBody(rng.startContainer)){
- var tmpNode = domUtils.createElement(me.document,'p',{
- 'innerHTML' : browser.ie ? domUtils.fillChar : ' '
- });
- rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
- }
- }
-
-
- //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
- if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
- if(browser.ie){
- var span = rng.document.createElement('span');
- rng.insertNode(span).setStartBefore(span).collapse(true);
- rng.select();
- domUtils.remove(span)
- }else{
- rng.select()
- }
-
- }
- }
-
-
- })
-};
-
-// plugins/fiximgclick.js
-///import core
-///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
-///commandsName FixImgClick
-///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小
-//修复chrome下图片不能点击的问题,出现八个角可改变大小
-
-UE.plugins['fiximgclick'] = (function () {
-
- var elementUpdated = false;
- function Scale() {
- this.editor = null;
- this.resizer = null;
- this.cover = null;
- this.doc = document;
- this.prePos = {x: 0, y: 0};
- this.startPos = {x: 0, y: 0};
- }
-
- (function () {
- var rect = [
- //[left, top, width, height]
- [0, 0, -1, -1],
- [0, 0, 0, -1],
- [0, 0, 1, -1],
- [0, 0, -1, 0],
- [0, 0, 1, 0],
- [0, 0, -1, 1],
- [0, 0, 0, 1],
- [0, 0, 1, 1]
- ];
-
- Scale.prototype = {
- init: function (editor) {
- var me = this;
- me.editor = editor;
- me.startPos = this.prePos = {x: 0, y: 0};
- me.dragId = -1;
-
- var hands = [],
- cover = me.cover = document.createElement('div'),
- resizer = me.resizer = document.createElement('div');
-
- cover.id = me.editor.ui.id + '_imagescale_cover';
- cover.style.cssText = 'position:absolute;display:none;z-index:' + (me.editor.options.zIndex) + ';filter:alpha(opacity=0); opacity:0;background:#CCC;';
- domUtils.on(cover, 'mousedown click', function () {
- me.hide();
- });
-
- for (i = 0; i < 8; i++) {
- hands.push('');
- }
- resizer.id = me.editor.ui.id + '_imagescale';
- resizer.className = 'edui-editor-imagescale';
- resizer.innerHTML = hands.join('');
- resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + (me.editor.options.zIndex) + ';';
-
- me.editor.ui.getDom().appendChild(cover);
- me.editor.ui.getDom().appendChild(resizer);
-
- me.initStyle();
- me.initEvents();
- },
- initStyle: function () {
- utils.cssRule('imagescale', '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' +
- '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
- + '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
- },
- initEvents: function () {
- var me = this;
-
- me.startPos.x = me.startPos.y = 0;
- me.isDraging = false;
- },
- _eventHandler: function (e) {
- var me = this;
- switch (e.type) {
- case 'mousedown':
- var hand = e.target || e.srcElement, hand;
- if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) {
- me.dragId = hand.className.slice(-1);
- me.startPos.x = me.prePos.x = e.clientX;
- me.startPos.y = me.prePos.y = e.clientY;
- domUtils.on(me.doc,'mousemove', me.proxy(me._eventHandler, me));
- }
- break;
- case 'mousemove':
- if (me.dragId != -1) {
- me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
- me.prePos.x = e.clientX;
- me.prePos.y = e.clientY;
- elementUpdated = true;
- me.updateTargetElement();
-
- }
- break;
- case 'mouseup':
- if (me.dragId != -1) {
- me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
- me.updateTargetElement();
- if (me.target.parentNode) me.attachTo(me.target);
- me.dragId = -1;
- }
- domUtils.un(me.doc,'mousemove', me.proxy(me._eventHandler, me));
- //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
- if(elementUpdated){
- elementUpdated = false;
- me.editor.fireEvent('contentchange');
- }
-
- break;
- default:
- break;
- }
- },
- updateTargetElement: function () {
- var me = this;
- domUtils.setStyles(me.target, {
- 'width': me.resizer.style.width,
- 'height': me.resizer.style.height
- });
- me.target.width = parseInt(me.resizer.style.width);
- me.target.height = parseInt(me.resizer.style.height);
- me.attachTo(me.target);
- },
- updateContainerStyle: function (dir, offset) {
- var me = this,
- dom = me.resizer, tmp;
-
- if (rect[dir][0] != 0) {
- tmp = parseInt(dom.style.left) + offset.x;
- dom.style.left = me._validScaledProp('left', tmp) + 'px';
- }
- if (rect[dir][1] != 0) {
- tmp = parseInt(dom.style.top) + offset.y;
- dom.style.top = me._validScaledProp('top', tmp) + 'px';
- }
- if (rect[dir][2] != 0) {
- tmp = dom.clientWidth + rect[dir][2] * offset.x;
- dom.style.width = me._validScaledProp('width', tmp) + 'px';
- }
- if (rect[dir][3] != 0) {
- tmp = dom.clientHeight + rect[dir][3] * offset.y;
- dom.style.height = me._validScaledProp('height', tmp) + 'px';
- }
- },
- _validScaledProp: function (prop, value) {
- var ele = this.resizer,
- wrap = document;
-
- value = isNaN(value) ? 0 : value;
- switch (prop) {
- case 'left':
- return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value;
- case 'top':
- return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value;
- case 'width':
- return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value;
- case 'height':
- return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value;
- }
- },
- hideCover: function () {
- this.cover.style.display = 'none';
- },
- showCover: function () {
- var me = this,
- editorPos = domUtils.getXY(me.editor.ui.getDom()),
- iframePos = domUtils.getXY(me.editor.iframe);
-
- domUtils.setStyles(me.cover, {
- 'width': me.editor.iframe.offsetWidth + 'px',
- 'height': me.editor.iframe.offsetHeight + 'px',
- 'top': iframePos.y - editorPos.y + 'px',
- 'left': iframePos.x - editorPos.x + 'px',
- 'position': 'absolute',
- 'display': ''
- })
- },
- show: function (targetObj) {
- var me = this;
- me.resizer.style.display = 'block';
- if(targetObj) me.attachTo(targetObj);
-
- domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me));
- domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
-
- me.showCover();
- me.editor.fireEvent('afterscaleshow', me);
- me.editor.fireEvent('saveScene');
- },
- hide: function () {
- var me = this;
- me.hideCover();
- me.resizer.style.display = 'none';
-
- domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me));
- domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
- me.editor.fireEvent('afterscalehide', me);
- },
- proxy: function( fn, context ) {
- return function(e) {
- return fn.apply( context || this, arguments);
- };
- },
- attachTo: function (targetObj) {
- var me = this,
- target = me.target = targetObj,
- resizer = this.resizer,
- imgPos = domUtils.getXY(target),
- iframePos = domUtils.getXY(me.editor.iframe),
- editorPos = domUtils.getXY(resizer.parentNode);
-
- domUtils.setStyles(resizer, {
- 'width': target.width + 'px',
- 'height': target.height + 'px',
- 'left': iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + 'px',
- 'top': iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + 'px'
- });
- }
- }
- })();
-
- return function () {
- var me = this,
- imageScale;
-
- me.setOpt('imageScaleEnabled', true);
-
- if ( !browser.ie && me.options.imageScaleEnabled) {
- me.addListener('click', function (type, e) {
-
- var range = me.selection.getRange(),
- img = range.getClosedNode();
-
- if (img && img.tagName == 'IMG' && me.body.contentEditable!="false") {
-
- if (img.className.indexOf("edui-faked-music") != -1 ||
- img.getAttribute("anchorname") ||
- domUtils.hasClass(img, 'loadingclass') ||
- domUtils.hasClass(img, 'loaderrorclass')) { return }
-
- if (!imageScale) {
- imageScale = new Scale();
- imageScale.init(me);
- me.ui.getDom().appendChild(imageScale.resizer);
-
- var _keyDownHandler = function (e) {
- imageScale.hide();
- if(imageScale.target) me.selection.getRange().selectNode(imageScale.target).select();
- }, _mouseDownHandler = function (e) {
- var ele = e.target || e.srcElement;
- if (ele && (ele.className===undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) {
- _keyDownHandler(e);
- }
- }, timer;
-
- me.addListener('afterscaleshow', function (e) {
- me.addListener('beforekeydown', _keyDownHandler);
- me.addListener('beforemousedown', _mouseDownHandler);
- domUtils.on(document, 'keydown', _keyDownHandler);
- domUtils.on(document,'mousedown', _mouseDownHandler);
- me.selection.getNative().removeAllRanges();
- });
- me.addListener('afterscalehide', function (e) {
- me.removeListener('beforekeydown', _keyDownHandler);
- me.removeListener('beforemousedown', _mouseDownHandler);
- domUtils.un(document, 'keydown', _keyDownHandler);
- domUtils.un(document,'mousedown', _mouseDownHandler);
- var target = imageScale.target;
- if (target.parentNode) {
- me.selection.getRange().selectNode(target).select();
- }
- });
- //TODO 有iframe的情况,mousedown不能往下传。。
- domUtils.on(imageScale.resizer, 'mousedown', function (e) {
- me.selection.getNative().removeAllRanges();
- var ele = e.target || e.srcElement;
- if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
- timer = setTimeout(function () {
- imageScale.hide();
- if(imageScale.target) me.selection.getRange().selectNode(ele).select();
- }, 200);
- }
- });
- domUtils.on(imageScale.resizer, 'mouseup', function (e) {
- var ele = e.target || e.srcElement;
- if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
- clearTimeout(timer);
- }
- });
- }
- imageScale.show(img);
- } else {
- if (imageScale && imageScale.resizer.style.display != 'none') imageScale.hide();
- }
- });
- }
-
- if (browser.webkit) {
- me.addListener('click', function (type, e) {
- if (e.target.tagName == 'IMG' && me.body.contentEditable!="false") {
- var range = new dom.Range(me.document);
- range.selectNode(e.target).select();
- }
- });
- }
- }
-})();
-
-// plugins/autolink.js
-///import core
-///commands 为非ie浏览器自动添加a标签
-///commandsName AutoLink
-///commandsTitle 自动增加链接
-/**
- * @description 为非ie浏览器自动添加a标签
- * @author zhanyi
- */
-
-UE.plugin.register('autolink',function(){
- var cont = 0;
-
- return !browser.ie ? {
-
- bindEvents:{
- 'reset' : function(){
- cont = 0;
- },
- 'keydown':function(type, evt) {
- var me = this;
- var keyCode = evt.keyCode || evt.which;
-
- if (keyCode == 32 || keyCode == 13) {
-
- var sel = me.selection.getNative(),
- range = sel.getRangeAt(0).cloneRange(),
- offset,
- charCode;
-
- var start = range.startContainer;
- while (start.nodeType == 1 && range.startOffset > 0) {
- start = range.startContainer.childNodes[range.startOffset - 1];
- if (!start){
- break;
- }
- range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length);
- range.collapse(true);
- start = range.startContainer;
- }
-
- do{
- if (range.startOffset == 0) {
- start = range.startContainer.previousSibling;
-
- while (start && start.nodeType == 1) {
- start = start.lastChild;
- }
- if (!start || domUtils.isFillChar(start)){
- break;
- }
- offset = start.nodeValue.length;
- } else {
- start = range.startContainer;
- offset = range.startOffset;
- }
- range.setStart(start, offset - 1);
- charCode = range.toString().charCodeAt(0);
- } while (charCode != 160 && charCode != 32);
-
- if (range.toString().replace(new RegExp(domUtils.fillChar, 'g'), '').match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)) {
- while(range.toString().length){
- if(/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())){
- break;
- }
- try{
- range.setStart(range.startContainer,range.startOffset+1);
- }catch(e){
- //trace:2121
- var start = range.startContainer;
- while(!(next = start.nextSibling)){
- if(domUtils.isBody(start)){
- return;
- }
- start = start.parentNode;
-
- }
- range.setStart(next,0);
-
- }
-
- }
- //range的开始边界已经在a标签里的不再处理
- if(domUtils.findParentByTagName(range.startContainer,'a',true)){
- return;
- }
- var a = me.document.createElement('a'),text = me.document.createTextNode(' '),href;
-
- me.undoManger && me.undoManger.save();
- a.appendChild(range.extractContents());
- a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g,'');
- href = a.getAttribute("href").replace(new RegExp(domUtils.fillChar,'g'),'');
- href = /^(?:https?:\/\/)/ig.test(href) ? href : "http://"+ href;
- a.setAttribute('_src',utils.html(href));
- a.href = utils.html(href);
-
- range.insertNode(a);
- a.parentNode.insertBefore(text, a.nextSibling);
- range.setStart(text, 0);
- range.collapse(true);
- sel.removeAllRanges();
- sel.addRange(range);
- me.undoManger && me.undoManger.save();
- }
- }
- }
- }
- }:{}
- },function(){
- var keyCodes = {
- 37:1, 38:1, 39:1, 40:1,
- 13:1,32:1
- };
- function checkIsCludeLink(node){
- if(node.nodeType == 3){
- return null
- }
- if(node.nodeName == 'A'){
- return node;
- }
- var lastChild = node.lastChild;
-
- while(lastChild){
- if(lastChild.nodeName == 'A'){
- return lastChild;
- }
- if(lastChild.nodeType == 3){
- if(domUtils.isWhitespace(lastChild)){
- lastChild = lastChild.previousSibling;
- continue;
- }
- return null
- }
- lastChild = lastChild.lastChild;
- }
- }
- browser.ie && this.addListener('keyup',function(cmd,evt){
- var me = this,keyCode = evt.keyCode;
- if(keyCodes[keyCode]){
- var rng = me.selection.getRange();
- var start = rng.startContainer;
-
- if(keyCode == 13){
- while(start && !domUtils.isBody(start) && !domUtils.isBlockElm(start)){
- start = start.parentNode;
- }
- if(start && !domUtils.isBody(start) && start.nodeName == 'P'){
- var pre = start.previousSibling;
- if(pre && pre.nodeType == 1){
- var pre = checkIsCludeLink(pre);
- if(pre && !pre.getAttribute('_href')){
- domUtils.remove(pre,true);
- }
- }
- }
- }else if(keyCode == 32 ){
- if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){
- start = start.previousSibling;
- if(start && start.nodeName == 'A' && !start.getAttribute('_href')){
- domUtils.remove(start,true);
- }
- }
- }else {
- start = domUtils.findParentByTagName(start,'a',true);
- if(start && !start.getAttribute('_href')){
- var bk = rng.createBookmark();
-
- domUtils.remove(start,true);
- rng.moveToBookmark(bk).select(true)
- }
- }
-
- }
-
-
- });
- }
-);
-
-// plugins/autoheight.js
-///import core
-///commands 当输入内容超过编辑器高度时,编辑器自动增高
-///commandsName AutoHeight,autoHeightEnabled
-///commandsTitle 自动增高
-/**
- * @description 自动伸展
- * @author zhanyi
- */
-UE.plugins['autoheight'] = function () {
- var me = this;
- //提供开关,就算加载也可以关闭
- me.autoHeightEnabled = me.options.autoHeightEnabled !== false;
- if (!me.autoHeightEnabled) {
- return;
- }
-
- var bakOverflow,
- lastHeight = 0,
- options = me.options,
- currentHeight,
- timer;
-
- function adjustHeight() {
- var me = this;
- clearTimeout(timer);
- if(isFullscreen)return;
- if (!me.queryCommandState || me.queryCommandState && me.queryCommandState('source') != 1) {
- timer = setTimeout(function(){
-
- var node = me.body.lastChild;
- while(node && node.nodeType != 1){
- node = node.previousSibling;
- }
- if(node && node.nodeType == 1){
- node.style.clear = 'both';
- currentHeight = Math.max(domUtils.getXY(node).y + node.offsetHeight + 25 ,Math.max(options.minFrameHeight, options.initialFrameHeight)) ;
- if (currentHeight != lastHeight) {
- if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
- me.iframe.parentNode.style.height = currentHeight + 'px';
- }
- me.body.style.height = currentHeight + 'px';
- lastHeight = currentHeight;
- }
- domUtils.removeStyle(node,'clear');
- }
-
-
- },50)
- }
- }
- var isFullscreen;
- me.addListener('fullscreenchanged',function(cmd,f){
- isFullscreen = f
- });
- me.addListener('destroy', function () {
- me.removeListener('contentchange afterinserthtml keyup mouseup',adjustHeight)
- });
- me.enableAutoHeight = function () {
- var me = this;
- if (!me.autoHeightEnabled) {
- return;
- }
- var doc = me.document;
- me.autoHeightEnabled = true;
- bakOverflow = doc.body.style.overflowY;
- doc.body.style.overflowY = 'hidden';
- me.addListener('contentchange afterinserthtml keyup mouseup',adjustHeight);
- //ff不给事件算得不对
-
- setTimeout(function () {
- adjustHeight.call(me);
- }, browser.gecko ? 100 : 0);
- me.fireEvent('autoheightchanged', me.autoHeightEnabled);
- };
- me.disableAutoHeight = function () {
-
- me.body.style.overflowY = bakOverflow || '';
-
- me.removeListener('contentchange', adjustHeight);
- me.removeListener('keyup', adjustHeight);
- me.removeListener('mouseup', adjustHeight);
- me.autoHeightEnabled = false;
- me.fireEvent('autoheightchanged', me.autoHeightEnabled);
- };
-
- me.on('setHeight',function(){
- me.disableAutoHeight()
- });
- me.addListener('ready', function () {
- me.enableAutoHeight();
- //trace:1764
- var timer;
- domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function () {
- clearTimeout(timer);
- timer = setTimeout(function () {
- //trace:3681
- adjustHeight.call(me);
- }, 100);
-
- });
- //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
- var lastScrollY;
- window.onscroll = function(){
- if(lastScrollY === null){
- lastScrollY = this.scrollY
- }else if(this.scrollY == 0 && lastScrollY != 0){
- me.window.scrollTo(0,0);
- lastScrollY = null;
- }
- }
- });
-
-
-};
-
-
-
-// plugins/autofloat.js
-///import core
-///commands 悬浮工具栏
-///commandsName AutoFloat,autoFloatEnabled
-///commandsTitle 悬浮工具栏
-/**
- * modified by chengchao01
- * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉!
- */
-UE.plugins['autofloat'] = function() {
- var me = this,
- lang = me.getLang();
- me.setOpt({
- topOffset:0
- });
- var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
- topOffset = me.options.topOffset;
-
-
- //如果不固定toolbar的位置,则直接退出
- if(!optsAutoFloatEnabled){
- return;
- }
- var uiUtils = UE.ui.uiUtils,
- LteIE6 = browser.ie && browser.version <= 6,
- quirks = browser.quirks;
-
- function checkHasUI(){
- if(!UE.ui){
- alert(lang.autofloatMsg);
- return 0;
- }
- return 1;
- }
- function fixIE6FixedPos(){
- var docStyle = document.body.style;
- docStyle.backgroundImage = 'url("about:blank")';
- docStyle.backgroundAttachment = 'fixed';
- }
- var bakCssText,
- placeHolder = document.createElement('div'),
- toolbarBox,orgTop,
- getPosition,
- flag =true; //ie7模式下需要偏移
- function setFloating(){
- var toobarBoxPos = domUtils.getXY(toolbarBox),
- origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
- origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
- toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
- toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
- toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
- if (LteIE6 || (quirks && browser.ie)) {
- if(toolbarBox.style.position != 'absolute'){
- toolbarBox.style.position = 'absolute';
- }
- toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px';
- } else {
- if (browser.ie7Compat && flag) {
- flag = false;
- toolbarBox.style.left = domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left+2 + 'px';
- }
- if(toolbarBox.style.position != 'fixed'){
- toolbarBox.style.position = 'fixed';
- toolbarBox.style.top = topOffset +"px";
- ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
- }
- }
- }
- function unsetFloating(){
- flag = true;
- if(placeHolder.parentNode){
- placeHolder.parentNode.removeChild(placeHolder);
- }
-
- toolbarBox.style.cssText = bakCssText;
- }
-
- function updateFloating(){
- var rect3 = getPosition(me.container);
- var offset=me.options.toolbarTopOffset||0;
- if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
- setFloating();
- }else{
- unsetFloating();
- }
- }
- var defer_updateFloating = utils.defer(function(){
- updateFloating();
- },browser.ie ? 200 : 100,true);
-
- me.addListener('destroy',function(){
- domUtils.un(window, ['scroll','resize'], updateFloating);
- me.removeListener('keydown', defer_updateFloating);
- });
-
- me.addListener('ready', function(){
- if(checkHasUI(me)){
- //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断
- if(!me.ui){
- return;
- }
- getPosition = uiUtils.getClientRect;
- toolbarBox = me.ui.getDom('toolbarbox');
- orgTop = getPosition(toolbarBox).top;
- bakCssText = toolbarBox.style.cssText;
- placeHolder.style.height = toolbarBox.offsetHeight + 'px';
- if(LteIE6){
- fixIE6FixedPos();
- }
- domUtils.on(window, ['scroll','resize'], updateFloating);
- me.addListener('keydown', defer_updateFloating);
-
- me.addListener('beforefullscreenchange', function (t, enabled){
- if (enabled) {
- unsetFloating();
- }
- });
- me.addListener('fullscreenchanged', function (t, enabled){
- if (!enabled) {
- updateFloating();
- }
- });
- me.addListener('sourcemodechanged', function (t, enabled){
- setTimeout(function (){
- updateFloating();
- },0);
- });
- me.addListener("clearDoc",function(){
- setTimeout(function(){
- updateFloating();
- },0);
-
- })
- }
- });
-};
-
-
-// plugins/video.js
-/**
- * video插件, 为UEditor提供视频插入支持
- * @file
- * @since 1.2.6.1
- */
-
-UE.plugins['video'] = function (){
- var me =this;
-
- /**
- * 创建插入视频字符窜
- * @param url 视频地址
- * @param width 视频宽度
- * @param height 视频高度
- * @param align 视频对齐
- * @param toEmbed 是否以flash代替显示
- * @param addParagraph 是否需要添加P 标签
- */
- function creatInsertStr(url,width,height,id,align,classname,type){
- var str;
- switch (type){
- case 'image':
- str = ''
- break;
- case 'embed':
- str = ' |