ThinkEditor.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. (function ($) {
  2. var ie = $.browser.msie,
  3. iOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
  4. $.TE = {
  5. version:'1.0', // 版本号
  6. debug: 1, //调试开关
  7. timeOut: 3000, //加载单个文件超时时间,单位为毫秒。
  8. defaults: {
  9. //默认参数controls,noRigths,plugins,定义加载插件
  10. controls: "source,|,undo,redo,|,cut,copy,paste,pastetext,selectAll,blockquote,|,image,flash,table,hr,pagebreak,face,code,|,link,unlink,|,print,fullscreen,|,eq,|,style,font,fontsize,|,fontcolor,backcolor,|,bold,italic,underline,strikethrough,unformat,|,leftalign,centeralign,rightalign,blockjustify,|,orderedlist,unorderedlist,indent,outdent,|,subscript,superscript",
  11. //noRights:"underline,strikethrough,superscript",
  12. width: 740,
  13. height: 500,
  14. skins: "default",
  15. resizeType: 2,
  16. face_path: ['qq_face', 'qq_face'],
  17. minHeight: 200,
  18. minWidth: 500,
  19. uploadURL: 'about:blank',
  20. theme: 'default'
  21. },
  22. buttons: {
  23. //按钮属性
  24. //eq: {title: '等于',cmd: 'bold'},
  25. bold: { title: "加粗", cmd: "bold" },
  26. pastetext: { title: "粘贴无格式", cmd: "bold" },
  27. pastefromword: { title: "粘贴word格式", cmd: "bold" },
  28. selectAll: { title: "全选", cmd: "selectall" },
  29. blockquote: { title: "引用" },
  30. find: { title: "查找", cmd: "bold" },
  31. flash: { title: "插入flash", cmd: "bold" },
  32. media: { title: "插入多媒体", cmd: "bold" },
  33. table: { title: "插入表格" },
  34. pagebreak: { title: "插入分页符" },
  35. face: { title: "插入表情", cmd: "bold" },
  36. code: { title: "插入源码", cmd: "bold" },
  37. print: { title: "打印", cmd: "print" },
  38. about: { title: "关于", cmd: "bold" },
  39. fullscreen: { title: "全屏", cmd: "fullscreen" },
  40. source: { title: "HTML代码", cmd: "source" },
  41. undo: { title: "后退", cmd: "undo" },
  42. redo: { title: "前进", cmd: "redo" },
  43. cut: { title: "剪贴", cmd: "cut" },
  44. copy: { title: "复制", cmd: "copy" },
  45. paste: { title: "粘贴", cmd: "paste" },
  46. hr: { title: "插入横线", cmd: "inserthorizontalrule" },
  47. link: { title: "创建链接", cmd: "createlink" },
  48. unlink: { title: "删除链接", cmd: "unlink" },
  49. italic: { title: "斜体", cmd: "italic" },
  50. underline: { title: "下划线", cmd: "underline" },
  51. strikethrough: { title: "删除线", cmd: "strikethrough" },
  52. unformat: { title: "清除格式", cmd: "removeformat" },
  53. subscript: { title: "下标", cmd: "subscript" },
  54. superscript: { title: "上标", cmd: "superscript" },
  55. orderedlist: { title: "有序列表", cmd: "insertorderedlist" },
  56. unorderedlist: { title: "无序列表", cmd: "insertunorderedlist" },
  57. indent: { title: "增加缩进", cmd: "indent" },
  58. outdent: { title: "减少缩进", cmd: "outdent" },
  59. leftalign: { title: "左对齐", cmd: "justifyleft" },
  60. centeralign: { title: "居中对齐", cmd: "justifycenter" },
  61. rightalign: { title: "右对齐", cmd: "justifyright" },
  62. blockjustify: { title: "两端对齐", cmd: "justifyfull" },
  63. font: { title: "字体", cmd: "fontname", value: "微软雅黑" },
  64. fontsize: { title: "字号", cmd: "fontsize", value: "4" },
  65. style: { title: "段落标题", cmd: "formatblock", value: "" },
  66. fontcolor: { title: "前景颜色", cmd: "forecolor", value: "#ff6600" },
  67. backcolor: { title: "背景颜色", cmd: "hilitecolor", value: "#ff6600" },
  68. image: { title: "插入图片", cmd: "insertimage", value: "" }
  69. },
  70. defaultEvent: {
  71. event: "click mouseover mouseout",
  72. click: function (e) {
  73. this.exec(e);
  74. },
  75. mouseover: function (e) {
  76. var opt = this.editor.opt;
  77. this.$btn.addClass(opt.cssname.mouseover);
  78. },
  79. mouseout: function (e) { },
  80. noRight: function (e) { },
  81. init: function (e) { },
  82. exec: function () {
  83. this.editor.restoreRange();
  84. //执行命令
  85. if ($.isFunction(this[this.cmd])) {
  86. this[this.cmd](); //如果有已当前cmd为名的方法,则执行
  87. } else {
  88. this.editor.doc.execCommand(this.cmd, 0, this.value || null);
  89. }
  90. this.editor.focus();
  91. this.editor.refreshBtn();
  92. this.editor.hideDialog();
  93. },
  94. createDialog: function (v) {
  95. //创建对话框
  96. var editor = this.editor,
  97. opt = editor.opt,
  98. $btn = this.$btn,
  99. _self = this;
  100. var defaults = {
  101. body: "", //对话框内容
  102. closeBtn: opt.cssname.dialogCloseBtn,
  103. okBtn: opt.cssname.dialogOkBtn,
  104. ok: function () {
  105. //点击ok按钮后执行函数
  106. },
  107. setDialog: function ($dialog) {
  108. //设置对话框(位置)
  109. var y = $btn.offset().top + $btn.outerHeight();
  110. var x = $btn.offset().left;
  111. $dialog.offset({
  112. top: y,
  113. left: x
  114. });
  115. }
  116. };
  117. var options = $.extend(defaults, v);
  118. //初始化对话框
  119. editor.$dialog.empty();
  120. //加入内容
  121. $body = $.type(options.body) == "string" ? $(options.body) : options.body;
  122. $dialog = $body.appendTo(editor.$dialog);
  123. $dialog.find("." + options.closeBtn).click(function () { _self.hideDialog(); });
  124. $dialog.find("." + options.okBtn).click(options.ok);
  125. //设置对话框
  126. editor.$dialog.show();
  127. options.setDialog(editor.$dialog);
  128. },
  129. hideDialog: function () {
  130. this.editor.hideDialog();
  131. }
  132. //getEnable:function(){return false},
  133. //disable:function(e){alert('disable')}
  134. },
  135. plugin: function (name, v) {
  136. //新增或修改插件。
  137. $.TE.buttons[name] = $.extend($.TE.buttons[name], v);
  138. },
  139. config: function (name, value) {
  140. var _fn = arguments.callee;
  141. if (!_fn.conf) _fn.conf = {};
  142. if (value) {
  143. _fn.conf[name] = value;
  144. return true;
  145. } else {
  146. return name == 'default' ? $.TE.defaults : _fn.conf[name];
  147. }
  148. },
  149. systemPlugins: ['system', 'upload_interface'], //系统自带插件
  150. basePath: function () {
  151. var jsFile = "ThinkEditor.js";
  152. var src = $("script[src$='" + jsFile + "']").attr("src");
  153. return src.substr(0, src.length - jsFile.length);
  154. }
  155. };
  156. $.fn.extend({
  157. //调用插件
  158. ThinkEditor: function (v) {
  159. //配置处理
  160. var conf = '',
  161. temp = '';
  162. conf = v ? $.extend($.TE.config(v.theme ? v.theme : 'default'), v) : $.TE.config('default');
  163. v = conf;
  164. //配置处理完成
  165. //载入皮肤
  166. var skins = v.skins || $.TE.defaults.skins; //获得皮肤参数
  167. var skinsDir = $.TE.basePath() + "skins/" + skins + "/",
  168. jsFile = "@" + skinsDir + "config.js",
  169. cssFile = "@" + skinsDir + "style.css";
  170. var _self = this;
  171. //加载插件
  172. if ($.defined(v.plugins)) {
  173. var myPlugins = $.type(v.plugins) == "string" ? [v.plugins] : v.plugins;
  174. var files = $.merge($.merge([], $.TE.systemPlugins), myPlugins);
  175. } else {
  176. var files = $.TE.systemPlugins;
  177. }
  178. $.each(files, function (i, v) {
  179. files[i] = v + ".js";
  180. })
  181. files.push(jsFile, cssFile);
  182. files.push("@" + skinsDir + "dialog/css/base.css");
  183. files.push("@" + skinsDir + "dialog/css/te_dialog.css");
  184. $.loadFile(files, function () {
  185. //设置css参数
  186. v.cssname = $.extend({}, TECSS, v.cssname);
  187. //创建编辑器,存储对象
  188. $(_self).each(function (idx, elem) {
  189. var data = $(elem).data("editorData");
  190. if (!data) {
  191. data = new ThinkEditor(elem, v);
  192. $(elem).data("editorData", data);
  193. }
  194. });
  195. });
  196. }
  197. });
  198. //编辑器对象。
  199. function ThinkEditor(area, v) {
  200. //添加随机序列数防冲突
  201. var _fn = arguments.callee;
  202. this.guid = !_fn.guid ? _fn.guid = 1 : _fn.guid += 1;
  203. //生成参数
  204. var opt = this.opt = $.extend({}, $.TE.defaults, v);
  205. var _self = this;
  206. //结构:主层,工具层,分组层,按钮层,底部,dialog层
  207. var $main = this.$main = $("<div></div>").addClass(opt.cssname.main),
  208. $toolbar_box = $('<div></div>').addClass(opt.cssname.toolbar_box).appendTo($main),
  209. $toolbar = this.$toolbar = $("<div></div>").addClass(opt.cssname.toolbar).appendTo($toolbar_box),
  210. /*$toolbar=this.$toolbar=$("<div></div>").addClass(opt.cssname.toolbar).appendTo($main),*/
  211. $group = $("<div></div>").addClass(opt.cssname.group).appendTo($toolbar),
  212. $bottom = this.$bottom = $("<div></div>").addClass(opt.cssname.bottom),
  213. $dialog = this.$dialog = $("<div></div>").addClass(opt.cssname.dialog),
  214. $area = $(area).hide(),
  215. $frame = $('<iframe frameborder="0"></iframe>');
  216. opt.noRights = opt.noRights || "";
  217. var noRights = opt.noRights.split(",");
  218. //调整结构
  219. $main.insertBefore($area)
  220. .append($area);
  221. //加入frame
  222. $frame.appendTo($main);
  223. //加入bottom
  224. if (opt.resizeType != 0) {
  225. //拖动改变编辑器高度
  226. $("<div></div>").addClass(opt.cssname.resizeCenter).mousedown(function (e) {
  227. var y = e.pageY,
  228. x = e.pageX,
  229. height = _self.$main.height(),
  230. width = _self.$main.width();
  231. $(document).add(_self.doc).mousemove(function (e) {
  232. var mh = e.pageY - y;
  233. _self.resize(width, height + mh);
  234. });
  235. $(document).add(_self.doc).mouseup(function (e) {
  236. $(document).add(_self.doc).unbind("mousemove");
  237. $(document).add(_self.doc).unbind("mousemup");
  238. });
  239. }).appendTo($bottom);
  240. }
  241. if (opt.resizeType == 2) {
  242. //拖动改变编辑器高度和宽度
  243. $("<div></div>").addClass(opt.cssname.resizeLeft).mousedown(function (e) {
  244. var y = e.pageY,
  245. x = e.pageX,
  246. height = _self.$main.height(),
  247. width = _self.$main.width();
  248. $(document).add(_self.doc).mousemove(function (e) {
  249. var mh = e.pageY - y,
  250. mw = e.pageX - x;
  251. _self.resize(width + mw, height + mh);
  252. });
  253. $(document).add(_self.doc).mouseup(function (e) {
  254. $(document).add(_self.doc).unbind("mousemove");
  255. $(document).add(_self.doc).unbind("mousemup");
  256. });
  257. }).appendTo($bottom);
  258. }
  259. $bottom.appendTo($main);
  260. $dialog.appendTo($main);
  261. //循环按钮处理。
  262. //TODO 默认参数处理
  263. $.each(opt.controls.split(","), function (idx, bname) {
  264. var _fn = arguments.callee;
  265. if (_fn.count == undefined) {
  266. _fn.count = 0;
  267. }
  268. //处理分组
  269. if (bname == "|") {
  270. //设定分组宽
  271. if (_fn.count) {
  272. $toolbar.find('.' + opt.cssname.group + ':last').css('width', (opt.cssname.btnWidth * _fn.count + opt.cssname.lineWidth) + 'px');
  273. _fn.count = 0;
  274. }
  275. //分组宽结束
  276. $group = $("<div></div>").addClass(opt.cssname.group).appendTo($toolbar);
  277. $("<div>&nbsp;</div>").addClass(opt.cssname.line).appendTo($group);
  278. } else {
  279. //更新统计数
  280. _fn.count += 1;
  281. //获取按钮属性
  282. var btn = $.extend({}, $.TE.defaultEvent, $.TE.buttons[bname]);
  283. //标记无权限
  284. var noRightCss = "", noRightTitle = "";
  285. if ($.inArray(bname, noRights) != -1) {
  286. noRightCss = " " + opt.cssname.noRight;
  287. noRightTitle = "(无权限)";
  288. }
  289. $btn = $("<div></div>").addClass(opt.cssname.btn + " " + opt.cssname.btnpre + bname + noRightCss)
  290. .data("bname", bname)
  291. .attr("title", btn.title + noRightTitle)
  292. .appendTo($group)
  293. .bind(btn.event, function (e) {
  294. //不可用触发
  295. if ($(this).is("." + opt.cssname.disable)) {
  296. if ($.isFunction(btn.disable)) btn.disable.call(btn, e);
  297. return false;
  298. }
  299. //判断权限和是否可用
  300. if ($(this).is("." + opt.cssname.noRight)) {
  301. //点击时触发无权限说明
  302. btn['noRight'].call(btn, e);
  303. return false;
  304. }
  305. if ($.isFunction(btn[e.type])) {
  306. //触发事件
  307. btn[e.type].call(btn, e);
  308. //TODO 刷新按钮
  309. }
  310. });
  311. if ($.isFunction(btn.init)) btn.init.call(btn); //初始化
  312. if (ie) $btn.attr("unselectable", "on");
  313. btn.editor = _self;
  314. btn.$btn = $btn;
  315. }
  316. });
  317. //调用核心
  318. this.core = new editorCore($frame, $area);
  319. this.doc = this.core.doc;
  320. this.$frame = this.core.$frame;
  321. this.$area = this.core.$area;
  322. this.restoreRange = this.core.restoreRange;
  323. this.selectedHTML = function () { return this.core.selectedHTML(); }
  324. this.selectedText = function () { return this.core.selectedText(); }
  325. this.pasteHTML = function (v) { this.core.pasteHTML(v); }
  326. this.sourceMode = this.core.sourceMode;
  327. this.focus = this.core.focus;
  328. //监控变化
  329. $(this.core.doc).click(function () {
  330. //隐藏对话框
  331. _self.hideDialog();
  332. }).bind("keyup mouseup", function () {
  333. _self.refreshBtn();
  334. })
  335. this.refreshBtn();
  336. //调整大小
  337. this.resize(opt.width, opt.height);
  338. //获取DOM层级
  339. this.core.focus();
  340. }
  341. //end ThinkEditor
  342. ThinkEditor.prototype.resize = function (w, h) {
  343. //最小高度和宽度
  344. var opt = this.opt,
  345. h = h < opt.minHeight ? opt.minHeight : h,
  346. w = w < opt.minWidth ? opt.minWidth : w;
  347. this.$main.width(w).height(h);
  348. var height = h - (this.$toolbar.parent().outerHeight() + this.$bottom.height());
  349. this.$frame.height(height).width("100%");
  350. this.$area.height(height).width("100%");
  351. };
  352. //隐藏对话框
  353. ThinkEditor.prototype.hideDialog = function () {
  354. var opt = this.opt;
  355. $("." + opt.cssname.dialog).hide();
  356. };
  357. //刷新按钮
  358. ThinkEditor.prototype.refreshBtn = function () {
  359. var sourceMode = this.sourceMode(); // 标记状态。
  360. var opt = this.opt;
  361. if (!iOS && $.browser.webkit && !this.focused) {
  362. this.$frame[0].contentWindow.focus();
  363. window.focus();
  364. this.focused = true;
  365. }
  366. var queryObj = this.doc;
  367. if (ie) queryObj = this.core.getRange();
  368. //循环按钮
  369. //TODO undo,redo等判断
  370. this.$toolbar.find("." + opt.cssname.btn + ":not(." + opt.cssname.noRight + ")").each(function () {
  371. var enabled = true,
  372. btnName = $(this).data("bname"),
  373. btn = $.extend({}, $.TE.defaultEvent, $.TE.buttons[btnName]),
  374. command = btn.cmd;
  375. if (sourceMode && btnName != "source") {
  376. enabled = false;
  377. } else if ($.isFunction(btn.getEnable)) {
  378. enabled = btn.getEnable.call(btn);
  379. } else if ($.isFunction(btn[command])) {
  380. enabled = true; //如果命令为自定义命令,默认为可用
  381. } else {
  382. if (!ie || btn.cmd != "inserthtml") {
  383. try {
  384. enabled = queryObj.queryCommandEnabled(command);
  385. $.debug(enabled.toString(), "命令:" + command);
  386. }
  387. catch (err) {
  388. enabled = false;
  389. }
  390. }
  391. //判断该功能是否有实现 @TODO 代码胶着
  392. if ($.TE.buttons[btnName]) enabled = true;
  393. }
  394. if (enabled) {
  395. $(this).removeClass(opt.cssname.disable);
  396. } else {
  397. $(this).addClass(opt.cssname.disable);
  398. }
  399. });
  400. };
  401. //core code start
  402. function editorCore($frame, $area, v) {
  403. //TODO 参数改为全局的。
  404. var defaults = {
  405. docType: '<!DOCTYPE HTML>',
  406. docCss: "",
  407. bodyStyle: "margin:4px; font:10pt Arial,Verdana; cursor:text",
  408. focusExt: function (editor) {
  409. //触发编辑器获得焦点时执行,比如刷新按钮
  410. },
  411. //textarea内容更新到iframe的处理函数
  412. updateFrame: function (code) {
  413. //翻转flash为占位符
  414. code = code.replace(/(<embed[^>]*?type="application\/x-shockwave-flash" [^>]*?>)/ig, function ($1) {
  415. var ret = '<img class="_flash_position" src="' + $.TE.basePath() + 'skins/default/img/spacer.gif" style="',
  416. _width = $1.match(/width="(\d+)"/),
  417. _height = $1.match(/height="(\d+)"/),
  418. _src = $1.match(/src="([^"]+)"/),
  419. _wmode = $1.match(/wmode="(\w+)"/),
  420. _data = '';
  421. _width = _width && _width[1] ? parseInt(_width[1]) : 0;
  422. _height = _height && _height[1] ? parseInt(_height[1]) : 0;
  423. _src = _src && _src[1] ? _src[1] : '';
  424. _wmode = _wmode && _wmode[1] ? true : false;
  425. _data = "{'src':'" + _src + "','width':'" + _width + "','height':'" + _height + "','wmode':" + (_wmode) + "}";
  426. if (_width) ret += 'width:' + _width + 'px;';
  427. if (_height) ret += 'height:' + _height + 'px;';
  428. ret += 'border:1px solid #DDD; display:inline-block;text-align:center;line-height:' + _height + 'px;" ';
  429. ret += '_data="' + _data + '"';
  430. ret += ' alt="flash占位符" />';
  431. return ret;
  432. });
  433. return code;
  434. },
  435. //iframe更新到text的, TODO 去掉
  436. updateTextArea: function (html) {
  437. //翻转占位符为flash
  438. html = html.replace(/(<img[^>]*?class=(?:"|)_flash_position(?:"|)[^>]*?>)/ig, function ($1) {
  439. var ret = '',
  440. data = $1.match(/_data="([^"]*)"/);
  441. if (data && data[1]) {
  442. data = eval('(' + data + ')');
  443. }
  444. ret += '<embed type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" ';
  445. ret += 'src="' + data.src + '" ';
  446. ret += 'width="' + data.width + '" ';
  447. ret += 'height="' + data.height + '" ';
  448. if (data.wmode) ret += 'wmode="transparent" ';
  449. ret += '/>';
  450. return ret;
  451. });
  452. return html;
  453. }
  454. };
  455. options = $.extend({}, defaults, v);
  456. //存储属性
  457. this.opt = options;
  458. this.$frame = $frame;
  459. this.$area = $area;
  460. var contentWindow = $frame[0].contentWindow,
  461. doc = this.doc = contentWindow.document,
  462. $doc = $(doc);
  463. var _self = this;
  464. //初始化
  465. doc.open();
  466. doc.write(
  467. options.docType +
  468. '<html>' +
  469. ((options.docCss === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCss + '" /></head>') +
  470. '<body style="' + options.bodyStyle + '"></body></html>'
  471. );
  472. doc.close();
  473. //设置frame编辑模式
  474. try {
  475. if (ie) {
  476. doc.body.contentEditable = true;
  477. }
  478. else {
  479. doc.designMode = "on";
  480. }
  481. } catch (err) {
  482. $.debug(err, "创建编辑模式错误");
  483. }
  484. //统一 IE FF 等的 execCommand 行为
  485. try {
  486. this.e.execCommand("styleWithCSS", 0, 0)
  487. }
  488. catch (e) {
  489. try {
  490. this.e.execCommand("useCSS", 0, 1);
  491. } catch (e) { }
  492. }
  493. //监听
  494. if (ie)
  495. $doc.click(function () {
  496. _self.focus();
  497. });
  498. this.updateFrame(); //更新内容
  499. if (ie) {
  500. $doc.bind("beforedeactivate beforeactivate selectionchange keypress", function (e) {
  501. if (e.type == "beforedeactivate")
  502. _self.inactive = true;
  503. else if (e.type == "beforeactivate") {
  504. if (!_self.inactive && _self.range && _self.range.length > 1)
  505. _self.range.shift();
  506. delete _self.inactive;
  507. }
  508. else if (!_self.inactive) {
  509. if (!_self.range)
  510. _self.range = [];
  511. _self.range.unshift(_self.getRange());
  512. while (_self.range.length > 2)
  513. _self.range.pop();
  514. }
  515. });
  516. // Restore the text range when the iframe gains focus
  517. $frame.focus(function () {
  518. _self.restoreRange();
  519. });
  520. }
  521. ($.browser.mozilla ? $doc : $(contentWindow)).blur(function () {
  522. _self.updateTextArea(true);
  523. });
  524. this.$area.blur(function () {
  525. // Update the iframe when the textarea loses focus
  526. _self.updateFrame(true);
  527. });
  528. /*
  529. * //自动添加p标签
  530. * $doc.keydown(function(e){
  531. * if(e.keyCode == 13){
  532. * //_self.pasteHTML('<p>&nbsp;</p>');
  533. * //this.execCommand( 'formatblock', false, '<p>' );
  534. * }
  535. * });
  536. */
  537. }
  538. //是否为源码模式
  539. editorCore.prototype.sourceMode = function () {
  540. return this.$area.is(":visible");
  541. };
  542. //编辑器获得焦点
  543. editorCore.prototype.focus = function () {
  544. var opt = this.opt;
  545. if (this.sourceMode()) {
  546. this.$area.focus();
  547. }
  548. else {
  549. this.$frame[0].contentWindow.focus();
  550. }
  551. if ($.isFunction(opt.focusExt)) opt.focusExt(this);
  552. };
  553. //textarea内容更新到iframe
  554. editorCore.prototype.updateFrame = function (checkForChange) {
  555. var code = this.$area.val(),
  556. options = this.opt,
  557. updateFrameCallback = options.updateFrame,
  558. $body = $(this.doc.body);
  559. //判断是否已经修改
  560. if (updateFrameCallback) {
  561. var sum = checksum(code);
  562. if (checkForChange && this.areaChecksum == sum)
  563. return;
  564. this.areaChecksum = sum;
  565. }
  566. //回调函数处理
  567. var html = updateFrameCallback ? updateFrameCallback(code) : code;
  568. // 禁止script标签
  569. html = html.replace(/<(?=\/?script)/ig, "&lt;");
  570. // TODO,判断是否有作用
  571. if (options.updateTextArea)
  572. this.frameChecksum = checksum(html);
  573. if (html != $body.html()) {
  574. $body.html(html);
  575. }
  576. };
  577. editorCore.prototype.getRange = function () {
  578. if (ie) return this.getSelection().createRange();
  579. return this.getSelection().getRangeAt(0);
  580. };
  581. editorCore.prototype.getSelection = function () {
  582. if (ie) return this.doc.selection;
  583. return this.$frame[0].contentWindow.getSelection();
  584. };
  585. editorCore.prototype.restoreRange = function () {
  586. if (ie && this.range)
  587. this.range[0].select();
  588. };
  589. editorCore.prototype.selectedHTML = function () {
  590. this.restoreRange();
  591. var range = this.getRange();
  592. if (ie)
  593. return range.htmlText;
  594. var layer = $("<layer>")[0];
  595. layer.appendChild(range.cloneContents());
  596. var html = layer.innerHTML;
  597. layer = null;
  598. return html;
  599. };
  600. editorCore.prototype.selectedText = function () {
  601. this.restoreRange();
  602. if (ie) return this.getRange().text;
  603. return this.getSelection().toString();
  604. };
  605. editorCore.prototype.pasteHTML = function (value) {
  606. this.restoreRange();
  607. if (ie) {
  608. this.getRange().pasteHTML(value);
  609. } else {
  610. this.doc.execCommand("inserthtml", 0, value || null);
  611. }
  612. //获得焦点
  613. this.$frame[0].contentWindow.focus();
  614. }
  615. editorCore.prototype.updateTextArea = function (checkForChange) {
  616. var html = $(this.doc.body).html(),
  617. options = this.opt,
  618. updateTextAreaCallback = options.updateTextArea,
  619. $area = this.$area;
  620. if (updateTextAreaCallback) {
  621. var sum = checksum(html);
  622. if (checkForChange && this.frameChecksum == sum)
  623. return;
  624. this.frameChecksum = sum;
  625. }
  626. var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html;
  627. // TODO 判断是否有必要
  628. if (options.updateFrame)
  629. this.areaChecksum = checksum(code);
  630. if (code != $area.val()) {
  631. $area.val(code);
  632. }
  633. };
  634. function checksum(text) {
  635. var a = 1, b = 0;
  636. for (var index = 0; index < text.length; ++index) {
  637. a = (a + text.charCodeAt(index)) % 65521;
  638. b = (b + a) % 65521;
  639. }
  640. return (b << 16) | a;
  641. }
  642. $.extend({
  643. teExt: {
  644. //扩展配置
  645. },
  646. debug: function (msg, group) {
  647. //判断是否有console对象
  648. if ($.TE.debug && window.console !== undefined) {
  649. //分组开始
  650. if (group) console.group(group);
  651. if ($.type(msg) == "string") {
  652. //是否为执行特殊函数,用双冒号隔开
  653. if (msg.indexOf("::") != -1) {
  654. var arr = msg.split("::");
  655. eval("console." + arr[0] + "('" + arr[1] + "')");
  656. } else {
  657. console.debug(msg);
  658. }
  659. } else {
  660. if ($(msg).html() == null) {
  661. console.dir(msg); //输出对象或数组
  662. } else {
  663. console.dirxml($(msg)[0]); //输出dom对象
  664. }
  665. }
  666. //记录trace信息
  667. if ($.TE.debug == 2) {
  668. console.group("trace 信息:");
  669. console.trace();
  670. console.groupEnd();
  671. }
  672. //分组结束
  673. if (group) console.groupEnd();
  674. }
  675. },
  676. //end debug
  677. defined: function (variable) {
  678. return $.type(variable) == "undefined" ? false : true;
  679. },
  680. isTag: function (tn) {
  681. if (!tn) return false;
  682. return $(this)[0].tagName.toLowerCase() == tn ? true : false;
  683. },
  684. //end istag
  685. include: function (file) {
  686. if (!$.defined($.TE.loadUrl)) $.TE.loadUrl = {};
  687. //定义皮肤路径和插件路径。
  688. var basePath = $.TE.basePath(),
  689. skinsDir = basePath + "skins/",
  690. pluginDir = basePath + "plugins/";
  691. var files = $.type(file) == "string" ? [file] : file;
  692. for (var i = 0; i < files.length; i++) {
  693. var loadurl = name = $.trim(files[i]);
  694. //判断是否已经加载过
  695. if ($.TE.loadUrl[loadurl]) {
  696. continue;
  697. }
  698. //判断是否有@
  699. var at = false;
  700. if (name.indexOf("@") != -1) {
  701. at = true;
  702. name = name.substr(1);
  703. }
  704. var att = name.split('.');
  705. var ext = att[att.length - 1].toLowerCase();
  706. if (ext == "css") {
  707. //加载css
  708. var filepath = at ? name : skinsDir + name;
  709. var newNode = document.createElement("link");
  710. newNode.setAttribute('type', 'text/css');
  711. newNode.setAttribute('rel', 'stylesheet');
  712. newNode.setAttribute('href', filepath);
  713. $.TE.loadUrl[loadurl] = 1;
  714. } else {
  715. var filepath = at ? name : pluginDir + name;
  716. //$("<scri"+"pt>"+"</scr"+"ipt>").attr({src:filepath,type:'text/javascript'}).appendTo('head');
  717. var newNode = document.createElement("script");
  718. newNode.type = "text/javascript";
  719. newNode.src = filepath;
  720. newNode.id = loadurl; //实现批量加载
  721. newNode.onload = function () {
  722. $.TE.loadUrl[this.id] = 1;
  723. };
  724. newNode.onreadystatechange = function () {
  725. //针对ie
  726. if ((newNode.readyState == 'loaded' || newNode.readyState == 'complete')) {
  727. $.TE.loadUrl[this.id] = 1;
  728. }
  729. };
  730. }
  731. $("head")[0].appendChild(newNode);
  732. }
  733. },
  734. //end include
  735. loadedFile: function (file) {
  736. //判断是否加载
  737. if (!$.defined($.TE.loadUrl)) return false;
  738. var files = $.type(file) == "string" ? [file] : file,
  739. result = true;
  740. $.each(files, function (i, name) {
  741. if (!$.TE.loadUrl[name]) result = false;
  742. //alert(name+':'+result);
  743. });
  744. return result;
  745. },
  746. //end loaded
  747. loadFile: function (file, fun) {
  748. //加载文件,加载完毕后执行fun函数。
  749. $.include(file);
  750. var time = 0;
  751. var check = function () {
  752. //alert($.loadedFile(file));
  753. if ($.loadedFile(file)) {
  754. if ($.isFunction(fun)) fun();
  755. } else {
  756. //alert(time);
  757. if (time >= $.TE.timeOut) {
  758. // TODO 细化哪些文件加载失败。
  759. $.debug(file, "文件加载失败");
  760. } else {
  761. //alert('time:'+time);
  762. setTimeout(check, 50);
  763. time += 50;
  764. }
  765. }
  766. };
  767. check();
  768. }
  769. //end loadFile
  770. });
  771. })(jQuery);
  772. jQuery.TE.config( 'mini', {
  773. 'controls' : 'font,fontsize,fontcolor,backcolor,bold,italic,underline,unformat,leftalign,centeralign,rightalign,orderedlist,unorderedlist',
  774. 'width':498,
  775. 'height':400,
  776. 'resizeType':1
  777. } );