リビジョン | 1582 (tree) |
---|---|
日時 | 2018-05-07 12:42:45 |
作者 | takahashi_m |
(メッセージはありません)
@@ -0,0 +1,69 @@ | ||
1 | +/** | |
2 | + CLEditorのカスタマイズ | |
3 | + 下記の処理を実施 | |
4 | + ・ツールバーの日本語化 | |
5 | + ・ツールバー空白箇所のツールチップ表示 | |
6 | + ・特殊文字のエンコード | |
7 | +*/ | |
8 | + | |
9 | +(function($) | |
10 | +{ | |
11 | + // アイコンツールチップの日本語化 | |
12 | + $.cleditor.buttons.bold.title = '太字'; | |
13 | + $.cleditor.buttons.size.title = 'フォントサイズ'; | |
14 | + $.cleditor.buttons.color.title = 'フォントの色'; | |
15 | + $.cleditor.buttons.highlight.title = '背景色'; | |
16 | + $.cleditor.buttons.removeformat.title = '書式の削除'; | |
17 | + $.cleditor.buttons.undo.title = '元に戻す' | |
18 | + $.cleditor.buttons.redo.title = 'やり直し' | |
19 | + $.cleditor.buttons.italic.title = '斜体' | |
20 | + $.cleditor.buttons.underline.title = '下線' | |
21 | + $.cleditor.buttons.strikethrough.title = '打ち消し線' | |
22 | + $.cleditor.buttons.style.title = 'スタイル' | |
23 | + $.cleditor.buttons.bullets.title = '箇条書き' | |
24 | + $.cleditor.buttons.numbering.title = '段落番号' | |
25 | + $.cleditor.buttons.outdent.title = 'インデントを減らす' | |
26 | + $.cleditor.buttons.indent.title = 'インデントを増やす' | |
27 | + $.cleditor.buttons.alignleft.title = '左揃え' | |
28 | + $.cleditor.buttons.center.title = '中央揃え' | |
29 | + $.cleditor.buttons.alignright.title = '右揃え' | |
30 | + $.cleditor.buttons.justify.title = '両端揃え' | |
31 | + $.cleditor.buttons.link.title = 'リンク' | |
32 | + $.cleditor.buttons.unlink.title = 'リンクを削除' | |
33 | + $.cleditor.buttons.source.title = 'ソース表示'; | |
34 | + $.cleditor.buttons.cut.title = '切り取り'; | |
35 | + $.cleditor.buttons.copy.title = 'コピー'; | |
36 | + $.cleditor.buttons.paste.title = '貼り付け'; | |
37 | + $.cleditor.buttons.print.title = '印刷'; | |
38 | + | |
39 | + // メニューツールチップの表示 | |
40 | + $(function(){ | |
41 | + $('div.cleditorToolbar').attr('title','IEでの改行:Ctrl+Enter'); | |
42 | + }); | |
43 | + | |
44 | + | |
45 | + // 特殊文字のエンコード | |
46 | + $.cleditor.defaultOptions.updateTextArea = function(html) | |
47 | + { | |
48 | + return $.cleditor.convertHTMLtoTextArea(html); | |
49 | + } | |
50 | + | |
51 | + $.cleditor.convertHTMLtoTextArea = function(html) | |
52 | + { | |
53 | + $.each([ | |
54 | + [/</g, "<"], | |
55 | + [/>/g, ">"] | |
56 | + ], | |
57 | + function(index, item) | |
58 | + { | |
59 | + // Keep running the replace untill all matches are | |
60 | + // replaced. Needed for the nested bbcodes. | |
61 | + var oldHtml=html; | |
62 | + | |
63 | + while((html = html.replace(item[0],item[1])) != oldHtml) | |
64 | + oldHtml=html; | |
65 | + }); | |
66 | + | |
67 | + return html; | |
68 | + } | |
69 | +})(jQuery); | |
\ No newline at end of file |
@@ -0,0 +1,1190 @@ | ||
1 | +/*! | |
2 | + CLEditor WYSIWYG HTML Editor v1.4.5 | |
3 | + http://premiumsoftware.net/CLEditor | |
4 | + requires jQuery v1.4.2 or later | |
5 | + | |
6 | + Copyright 2010, Chris Landowski, Premium Software, LLC | |
7 | + Dual licensed under the MIT or GPL Version 2 licenses. | |
8 | +*/ | |
9 | + | |
10 | +(function ($) { | |
11 | + | |
12 | + //============== | |
13 | + // jQuery Plugin | |
14 | + //============== | |
15 | + | |
16 | + $.cleditor = { | |
17 | + | |
18 | + // Define the defaults used for all new cleditor instances | |
19 | + defaultOptions: { | |
20 | + width: 'auto', // width not including margins, borders or padding | |
21 | + height: 250, // height not including margins, borders or padding | |
22 | + controls: // controls to add to the toolbar | |
23 | + "bold italic underline strikethrough subscript superscript | font size " + | |
24 | + "style | color highlight removeformat | bullets numbering | outdent " + | |
25 | + "indent | alignleft center alignright justify | undo redo | " + | |
26 | + "rule image link unlink | cut copy paste pastetext | print source", | |
27 | + colors: // colors in the color popup | |
28 | + "FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF " + | |
29 | + "CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F " + | |
30 | + "BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C " + | |
31 | + "999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C " + | |
32 | + "666 900 C60 C93 990 090 399 33F 60C 939 " + | |
33 | + "333 600 930 963 660 060 366 009 339 636 " + | |
34 | + "000 300 630 633 330 030 033 006 309 303", | |
35 | + fonts: // font names in the font popup | |
36 | + "Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond," + | |
37 | + "Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana", | |
38 | + sizes: // sizes in the font size popup | |
39 | + "1,2,3,4,5,6,7", | |
40 | + styles: // styles in the style popup | |
41 | + [["Paragraph", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"], | |
42 | + ["Header 3", "<h3>"], ["Header 4", "<h4>"], ["Header 5", "<h5>"], | |
43 | + ["Header 6", "<h6>"]], | |
44 | + useCSS: true, // use CSS to style HTML when possible (not supported in ie) | |
45 | + docType: // Document type contained within the editor | |
46 | + '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', | |
47 | + docCSSFile: // CSS file used to style the document contained within the editor | |
48 | + "", | |
49 | + bodyStyle: // style to assign to document body contained within the editor | |
50 | + "margin:4px; font:10pt Arial,Verdana; cursor:text" | |
51 | + }, | |
52 | + | |
53 | + // Define all usable toolbar buttons - the init string property is | |
54 | + // expanded during initialization back into the buttons object and | |
55 | + // separate object properties are created for each button. | |
56 | + // e.g. buttons.size.title = "Font Size" | |
57 | + buttons: { | |
58 | + // name,title,command,popupName (""=use name) | |
59 | + init: | |
60 | + "bold,,|" + | |
61 | + "italic,,|" + | |
62 | + "underline,,|" + | |
63 | + "strikethrough,,|" + | |
64 | + "subscript,,|" + | |
65 | + "superscript,,|" + | |
66 | + "font,,fontname,|" + | |
67 | + "size,Font Size,fontsize,|" + | |
68 | + "style,,formatblock,|" + | |
69 | + "color,Font Color,forecolor,|" + | |
70 | + "highlight,Text Highlight Color,hilitecolor,color|" + | |
71 | + "removeformat,Remove Formatting,|" + | |
72 | + "bullets,,insertunorderedlist|" + | |
73 | + "numbering,,insertorderedlist|" + | |
74 | + "outdent,,|" + | |
75 | + "indent,,|" + | |
76 | + "alignleft,Align Text Left,justifyleft|" + | |
77 | + "center,,justifycenter|" + | |
78 | + "alignright,Align Text Right,justifyright|" + | |
79 | + "justify,,justifyfull|" + | |
80 | + "undo,,|" + | |
81 | + "redo,,|" + | |
82 | + "rule,Insert Horizontal Rule,inserthorizontalrule|" + | |
83 | + "image,Insert Image,insertimage,url|" + | |
84 | + "link,Insert Hyperlink,createlink,url|" + | |
85 | + "unlink,Remove Hyperlink,|" + | |
86 | + "cut,,|" + | |
87 | + "copy,,|" + | |
88 | + "paste,,|" + | |
89 | + "pastetext,Paste as Text,inserthtml,|" + | |
90 | + "print,,|" + | |
91 | + "source,Show Source" | |
92 | + }, | |
93 | + | |
94 | + // imagesPath - returns the path to the images folder | |
95 | + imagesPath: function () { return imagesPath(); } | |
96 | + | |
97 | + }; | |
98 | + | |
99 | + // cleditor - creates a new editor for each of the matched textareas | |
100 | + $.fn.cleditor = function (options) { | |
101 | + | |
102 | + // Create a new jQuery object to hold the results | |
103 | + var $result = $([]); | |
104 | + | |
105 | + // Loop through all matching textareas and create the editors | |
106 | + this.each(function (idx, elem) { | |
107 | + if (elem.tagName.toUpperCase() === "TEXTAREA") { | |
108 | + var data = $.data(elem, CLEDITOR); | |
109 | + if (!data) data = new cleditor(elem, options); | |
110 | + $result = $result.add(data); | |
111 | + } | |
112 | + }); | |
113 | + | |
114 | + // return the new jQuery object | |
115 | + return $result; | |
116 | + | |
117 | + }; | |
118 | + | |
119 | + //================== | |
120 | + // Private Variables | |
121 | + //================== | |
122 | + | |
123 | + var | |
124 | + | |
125 | + // Misc constants | |
126 | + BACKGROUND_COLOR = "backgroundColor", | |
127 | + BLURRED = "blurred", | |
128 | + BUTTON = "button", | |
129 | + BUTTON_NAME = "buttonName", | |
130 | + CHANGE = "change", | |
131 | + CLEDITOR = "cleditor", | |
132 | + CLICK = "click", | |
133 | + DISABLED = "disabled", | |
134 | + DIV_TAG = "<div>", | |
135 | + FOCUSED = "focused", | |
136 | + TRANSPARENT = "transparent", | |
137 | + UNSELECTABLE = "unselectable", | |
138 | + | |
139 | + // Class name constants | |
140 | + MAIN_CLASS = "cleditorMain", // main containing div | |
141 | + TOOLBAR_CLASS = "cleditorToolbar", // toolbar div inside main div | |
142 | + GROUP_CLASS = "cleditorGroup", // group divs inside the toolbar div | |
143 | + BUTTON_CLASS = "cleditorButton", // button divs inside group div | |
144 | + DISABLED_CLASS = "cleditorDisabled",// disabled button divs | |
145 | + DIVIDER_CLASS = "cleditorDivider", // divider divs inside group div | |
146 | + POPUP_CLASS = "cleditorPopup", // popup divs inside body | |
147 | + LIST_CLASS = "cleditorList", // list popup divs inside body | |
148 | + COLOR_CLASS = "cleditorColor", // color popup div inside body | |
149 | + PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body | |
150 | + MSG_CLASS = "cleditorMsg", // message popup div inside body | |
151 | + | |
152 | + // Browser detection | |
153 | + ua = navigator.userAgent.toLowerCase(), | |
154 | + ie = /msie/.test(ua), | |
155 | + ie6 = /msie\s6/.test(ua), | |
156 | + iege11 = /(trident)(?:.*rv:([\w.]+))?/.test(ua), | |
157 | + webkit = /webkit/.test(ua), | |
158 | + | |
159 | + // Test for iPhone/iTouch/iPad | |
160 | + iOS = /iPhone|iPad|iPod/i.test(ua), | |
161 | + | |
162 | + // Popups are created once as needed and shared by all editor instances | |
163 | + popups = {}, | |
164 | + | |
165 | + // Used to prevent the document click event from being bound more than once | |
166 | + documentClickAssigned, | |
167 | + | |
168 | + // Local copy of the buttons object | |
169 | + buttons = $.cleditor.buttons; | |
170 | + | |
171 | + //=============== | |
172 | + // Initialization | |
173 | + //=============== | |
174 | + | |
175 | + // Expand the buttons.init string back into the buttons object | |
176 | + // and create seperate object properties for each button. | |
177 | + // e.g. buttons.size.title = "Font Size" | |
178 | + $.each(buttons.init.split("|"), function (idx, button) { | |
179 | + var items = button.split(","), name = items[0]; | |
180 | + buttons[name] = { | |
181 | + stripIndex: idx, | |
182 | + name: name, | |
183 | + title: items[1] === "" ? name.charAt(0).toUpperCase() + name.substr(1) : items[1], | |
184 | + command: items[2] === "" ? name : items[2], | |
185 | + popupName: items[3] === "" ? name : items[3] | |
186 | + }; | |
187 | + }); | |
188 | + delete buttons.init; | |
189 | + | |
190 | + //============ | |
191 | + // Constructor | |
192 | + //============ | |
193 | + | |
194 | + // cleditor - creates a new editor for the passed in textarea element | |
195 | + cleditor = function (area, options) { | |
196 | + | |
197 | + var editor = this; | |
198 | + | |
199 | + // Get the defaults and override with options | |
200 | + editor.options = options = $.extend({}, $.cleditor.defaultOptions, options); | |
201 | + | |
202 | + // Hide the textarea and associate it with this editor | |
203 | + var $area = editor.$area = $(area) | |
204 | + .css({ border: "none", margin: 0, padding: 0 }) // Needed for IE6 & 7 (won't work in CSS file) | |
205 | + .hide() | |
206 | + .data(CLEDITOR, editor) | |
207 | + .blur(function () { | |
208 | + // Update the iframe when the textarea loses focus | |
209 | + updateFrame(editor, true); | |
210 | + }); | |
211 | + | |
212 | + // Create the main container | |
213 | + var $main = editor.$main = $(DIV_TAG) | |
214 | + .addClass(MAIN_CLASS) | |
215 | + .width(options.width) | |
216 | + .height(options.height); | |
217 | + | |
218 | + // Create the toolbar | |
219 | + var $toolbar = editor.$toolbar = $(DIV_TAG) | |
220 | + .addClass(TOOLBAR_CLASS) | |
221 | + .appendTo($main); | |
222 | + | |
223 | + // Add the first group to the toolbar | |
224 | + var $group = $(DIV_TAG) | |
225 | + .addClass(GROUP_CLASS) | |
226 | + .appendTo($toolbar); | |
227 | + | |
228 | + // Initialize the group width | |
229 | + var groupWidth = 0; | |
230 | + | |
231 | + // Add the buttons to the toolbar | |
232 | + $.each(options.controls.split(" "), function (idx, buttonName) { | |
233 | + if (buttonName === "") return true; | |
234 | + | |
235 | + // Divider | |
236 | + if (buttonName === "|") { | |
237 | + | |
238 | + // Add a new divider to the group | |
239 | + var $div = $(DIV_TAG) | |
240 | + .addClass(DIVIDER_CLASS) | |
241 | + .appendTo($group); | |
242 | + | |
243 | + // Update the group width | |
244 | + $group.width(groupWidth + 1); | |
245 | + groupWidth = 0; | |
246 | + | |
247 | + // Create a new group | |
248 | + $group = $(DIV_TAG) | |
249 | + .addClass(GROUP_CLASS) | |
250 | + .appendTo($toolbar); | |
251 | + | |
252 | + } | |
253 | + | |
254 | + // Button | |
255 | + else { | |
256 | + | |
257 | + // Get the button definition | |
258 | + var button = buttons[buttonName]; | |
259 | + | |
260 | + // Add a new button to the group | |
261 | + var $buttonDiv = $(DIV_TAG) | |
262 | + .data(BUTTON_NAME, button.name) | |
263 | + .addClass(BUTTON_CLASS) | |
264 | + .attr("title", button.title) | |
265 | + .bind(CLICK, $.proxy(buttonClick, editor)) | |
266 | + .appendTo($group) | |
267 | + .hover(hoverEnter, hoverLeave); | |
268 | + | |
269 | + // Update the group width | |
270 | + groupWidth += 24; | |
271 | + $group.width(groupWidth + 1); | |
272 | + | |
273 | + // Prepare the button image | |
274 | + var map = {}; | |
275 | + if (button.css) map = button.css; | |
276 | + else if (button.image) map.backgroundImage = imageUrl(button.image); | |
277 | + if (button.stripIndex) map.backgroundPosition = button.stripIndex * -24; | |
278 | + $buttonDiv.css(map); | |
279 | + | |
280 | + // Add the unselectable attribute for ie | |
281 | + if (ie) | |
282 | + $buttonDiv.attr(UNSELECTABLE, "on"); | |
283 | + | |
284 | + // Create the popup | |
285 | + if (button.popupName) | |
286 | + createPopup(button.popupName, options, button.popupClass, | |
287 | + button.popupContent, button.popupHover); | |
288 | + | |
289 | + } | |
290 | + | |
291 | + }); | |
292 | + | |
293 | + // Add the main div to the DOM and append the textarea | |
294 | + $main.insertBefore($area) | |
295 | + .append($area); | |
296 | + | |
297 | + // Bind the document click event handler | |
298 | + if (!documentClickAssigned) { | |
299 | + $(document).click(function (e) { | |
300 | + // Dismiss all non-prompt popups | |
301 | + var $target = $(e.target); | |
302 | + if (!$target.add($target.parents()).is("." + PROMPT_CLASS)) | |
303 | + hidePopups(); | |
304 | + }); | |
305 | + documentClickAssigned = true; | |
306 | + } | |
307 | + | |
308 | + // Bind the window resize event when the width or height is auto or % | |
309 | + if (/auto|%/.test("" + options.width + options.height)) | |
310 | + $(window).bind("resize.cleditor", function () { refresh(editor); }); | |
311 | + | |
312 | + // Create the iframe and resize the controls | |
313 | + refresh(editor); | |
314 | + | |
315 | + }; | |
316 | + | |
317 | + //=============== | |
318 | + // Public Methods | |
319 | + //=============== | |
320 | + | |
321 | + var fn = cleditor.prototype, | |
322 | + | |
323 | + // Expose the following private functions as methods on the cleditor object. | |
324 | + // The closure compiler will rename the private functions. However, the | |
325 | + // exposed method names on the cleditor object will remain fixed. | |
326 | + methods = [ | |
327 | + ["clear", clear], | |
328 | + ["disable", disable], | |
329 | + ["execCommand", execCommand], | |
330 | + ["focus", focus], | |
331 | + ["hidePopups", hidePopups], | |
332 | + ["sourceMode", sourceMode, true], | |
333 | + ["refresh", refresh], | |
334 | + ["select", select], | |
335 | + ["selectedHTML", selectedHTML, true], | |
336 | + ["selectedText", selectedText, true], | |
337 | + ["showMessage", showMessage], | |
338 | + ["updateFrame", updateFrame], | |
339 | + ["updateTextArea", updateTextArea] | |
340 | + ]; | |
341 | + | |
342 | + $.each(methods, function (idx, method) { | |
343 | + fn[method[0]] = function () { | |
344 | + var editor = this, args = [editor]; | |
345 | + // using each here would cast booleans into objects! | |
346 | + for (var x = 0; x < arguments.length; x++) { args.push(arguments[x]); } | |
347 | + var result = method[1].apply(editor, args); | |
348 | + if (method[2]) return result; | |
349 | + return editor; | |
350 | + }; | |
351 | + }); | |
352 | + | |
353 | + // blurred - shortcut for .bind("blurred", handler) or .trigger("blurred") | |
354 | + fn.blurred = function (handler) { | |
355 | + var $this = $(this); | |
356 | + return handler ? $this.bind(BLURRED, handler) : $this.trigger(BLURRED); | |
357 | + }; | |
358 | + | |
359 | + // change - shortcut for .bind("change", handler) or .trigger("change") | |
360 | + fn.change = function change(handler) { | |
361 | + var $this = $(this); | |
362 | + return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE); | |
363 | + }; | |
364 | + | |
365 | + // focused - shortcut for .bind("focused", handler) or .trigger("focused") | |
366 | + fn.focused = function (handler) { | |
367 | + var $this = $(this); | |
368 | + return handler ? $this.bind(FOCUSED, handler) : $this.trigger(FOCUSED); | |
369 | + }; | |
370 | + | |
371 | + //=============== | |
372 | + // Event Handlers | |
373 | + //=============== | |
374 | + | |
375 | + // buttonClick - click event handler for toolbar buttons | |
376 | + function buttonClick(e) { | |
377 | + | |
378 | + var editor = this, | |
379 | + buttonDiv = e.target, | |
380 | + buttonName = $.data(buttonDiv, BUTTON_NAME), | |
381 | + button = buttons[buttonName], | |
382 | + popupName = button.popupName, | |
383 | + popup = popups[popupName]; | |
384 | + | |
385 | + // Check if disabled | |
386 | + if (editor.disabled || $(buttonDiv).attr(DISABLED) === DISABLED) | |
387 | + return; | |
388 | + | |
389 | + // Fire the buttonClick event | |
390 | + var data = { | |
391 | + editor: editor, | |
392 | + button: buttonDiv, | |
393 | + buttonName: buttonName, | |
394 | + popup: popup, | |
395 | + popupName: popupName, | |
396 | + command: button.command, | |
397 | + useCSS: editor.options.useCSS | |
398 | + }; | |
399 | + | |
400 | + if (button.buttonClick && button.buttonClick(e, data) === false) | |
401 | + return false; | |
402 | + | |
403 | + // Toggle source | |
404 | + if (buttonName === "source") { | |
405 | + | |
406 | + // Show the iframe | |
407 | + if (sourceMode(editor)) { | |
408 | + delete editor.range; | |
409 | + editor.$area.hide(); | |
410 | + editor.$frame.show(); | |
411 | + buttonDiv.title = button.title; | |
412 | + } | |
413 | + | |
414 | + // Show the textarea | |
415 | + else { | |
416 | + editor.$frame.hide(); | |
417 | + editor.$area.show(); | |
418 | + buttonDiv.title = "Show Rich Text"; | |
419 | + } | |
420 | + | |
421 | + } | |
422 | + | |
423 | + // Check for rich text mode | |
424 | + else if (!sourceMode(editor)) { | |
425 | + | |
426 | + // Handle popups | |
427 | + if (popupName) { | |
428 | + var $popup = $(popup); | |
429 | + | |
430 | + // URL | |
431 | + if (popupName === "url") { | |
432 | + | |
433 | + // Check for selection before showing the link url popup | |
434 | + if (buttonName === "link" && selectedText(editor) === "") { | |
435 | + showMessage(editor, "A selection is required when inserting a link.", buttonDiv); | |
436 | + return false; | |
437 | + } | |
438 | + | |
439 | + // Wire up the submit button click event handler | |
440 | + $popup.children(":button") | |
441 | + .unbind(CLICK) | |
442 | + .bind(CLICK, function () { | |
443 | + | |
444 | + // Insert the image or link if a url was entered | |
445 | + var $text = $popup.find(":text"), | |
446 | + url = $.trim($text.val()); | |
447 | + if (url !== "") | |
448 | + execCommand(editor, data.command, url, null, data.button); | |
449 | + | |
450 | + // Reset the text, hide the popup and set focus | |
451 | + $text.val("http://"); | |
452 | + hidePopups(); | |
453 | + focus(editor); | |
454 | + | |
455 | + }); | |
456 | + | |
457 | + } | |
458 | + | |
459 | + // Paste as Text | |
460 | + else if (popupName === "pastetext") { | |
461 | + | |
462 | + // Wire up the submit button click event handler | |
463 | + $popup.children(":button") | |
464 | + .unbind(CLICK) | |
465 | + .bind(CLICK, function () { | |
466 | + | |
467 | + // Insert the unformatted text replacing new lines with break tags | |
468 | + var $textarea = $popup.find("textarea"), | |
469 | + text = $textarea.val().replace(/\n/g, "<br />"); | |
470 | + if (text !== "") | |
471 | + execCommand(editor, data.command, text, null, data.button); | |
472 | + | |
473 | + // Reset the text, hide the popup and set focus | |
474 | + $textarea.val(""); | |
475 | + hidePopups(); | |
476 | + focus(editor); | |
477 | + | |
478 | + }); | |
479 | + | |
480 | + } | |
481 | + | |
482 | + // Show the popup if not already showing for this button | |
483 | + if (buttonDiv !== $.data(popup, BUTTON)) { | |
484 | + showPopup(editor, popup, buttonDiv); | |
485 | + return false; // stop propagination to document click | |
486 | + } | |
487 | + | |
488 | + // propaginate to document click | |
489 | + return; | |
490 | + | |
491 | + } | |
492 | + | |
493 | ||
494 | + else if (buttonName === "print") | |
495 | + editor.$frame[0].contentWindow.print(); | |
496 | + | |
497 | + // All other buttons | |
498 | + else if (!execCommand(editor, data.command, data.value, data.useCSS, buttonDiv)) | |
499 | + return false; | |
500 | + | |
501 | + } | |
502 | + | |
503 | + // Focus the editor | |
504 | + focus(editor); | |
505 | + | |
506 | + } | |
507 | + | |
508 | + // hoverEnter - mouseenter event handler for buttons and popup items | |
509 | + function hoverEnter(e) { | |
510 | + var $div = $(e.target).closest("div"); | |
511 | + $div.css(BACKGROUND_COLOR, $div.data(BUTTON_NAME) ? "#FFF" : "#FFC"); | |
512 | + } | |
513 | + | |
514 | + // hoverLeave - mouseleave event handler for buttons and popup items | |
515 | + function hoverLeave(e) { | |
516 | + $(e.target).closest("div").css(BACKGROUND_COLOR, "transparent"); | |
517 | + } | |
518 | + | |
519 | + // popupClick - click event handler for popup items | |
520 | + function popupClick(e) { | |
521 | + | |
522 | + var editor = this, | |
523 | + popup = e.data.popup, | |
524 | + target = e.target; | |
525 | + | |
526 | + // Check for message and prompt popups | |
527 | + if (popup === popups.msg || $(popup).hasClass(PROMPT_CLASS)) | |
528 | + return; | |
529 | + | |
530 | + // Get the button info | |
531 | + var buttonDiv = $.data(popup, BUTTON), | |
532 | + buttonName = $.data(buttonDiv, BUTTON_NAME), | |
533 | + button = buttons[buttonName], | |
534 | + command = button.command, | |
535 | + value, | |
536 | + useCSS = editor.options.useCSS; | |
537 | + | |
538 | + // Get the command value | |
539 | + if (buttonName === "font") | |
540 | + // Opera returns the fontfamily wrapped in quotes | |
541 | + value = target.style.fontFamily.replace(/"/g, ""); | |
542 | + else if (buttonName === "size") { | |
543 | + if (target.tagName.toUpperCase() === "DIV") | |
544 | + target = target.children[0]; | |
545 | + value = target.innerHTML; | |
546 | + } | |
547 | + else if (buttonName === "style") | |
548 | + value = "<" + target.tagName + ">"; | |
549 | + else if (buttonName === "color") | |
550 | + value = hex(target.style.backgroundColor); | |
551 | + else if (buttonName === "highlight") { | |
552 | + value = hex(target.style.backgroundColor); | |
553 | + if (ie) command = 'backcolor'; | |
554 | + else useCSS = true; | |
555 | + } | |
556 | + | |
557 | + // Fire the popupClick event | |
558 | + var data = { | |
559 | + editor: editor, | |
560 | + button: buttonDiv, | |
561 | + buttonName: buttonName, | |
562 | + popup: popup, | |
563 | + popupName: button.popupName, | |
564 | + command: command, | |
565 | + value: value, | |
566 | + useCSS: useCSS | |
567 | + }; | |
568 | + | |
569 | + if (button.popupClick && button.popupClick(e, data) === false) | |
570 | + return; | |
571 | + | |
572 | + // Execute the command | |
573 | + if (data.command && !execCommand(editor, data.command, data.value, data.useCSS, buttonDiv)) | |
574 | + return false; | |
575 | + | |
576 | + // Hide the popup and focus the editor | |
577 | + hidePopups(); | |
578 | + focus(editor); | |
579 | + | |
580 | + } | |
581 | + | |
582 | + //================== | |
583 | + // Private Functions | |
584 | + //================== | |
585 | + | |
586 | + // checksum - returns a checksum using the Adler-32 method | |
587 | + function checksum(text) { | |
588 | + var a = 1, b = 0; | |
589 | + for (var index = 0; index < text.length; ++index) { | |
590 | + a = (a + text.charCodeAt(index)) % 65521; | |
591 | + b = (b + a) % 65521; | |
592 | + } | |
593 | + return (b << 16) | a; | |
594 | + } | |
595 | + | |
596 | + // clear - clears the contents of the editor | |
597 | + function clear(editor) { | |
598 | + editor.$area.val(""); | |
599 | + updateFrame(editor); | |
600 | + } | |
601 | + | |
602 | + // createPopup - creates a popup and adds it to the body | |
603 | + function createPopup(popupName, options, popupTypeClass, popupContent, popupHover) { | |
604 | + | |
605 | + // Check if popup already exists | |
606 | + if (popups[popupName]) | |
607 | + return popups[popupName]; | |
608 | + | |
609 | + // Create the popup | |
610 | + var $popup = $(DIV_TAG) | |
611 | + .hide() | |
612 | + .addClass(POPUP_CLASS) | |
613 | + .appendTo("body"); | |
614 | + | |
615 | + // Add the content | |
616 | + | |
617 | + // Custom popup | |
618 | + if (popupContent) | |
619 | + $popup.html(popupContent); | |
620 | + | |
621 | + // Color | |
622 | + else if (popupName === "color") { | |
623 | + var colors = options.colors.split(" "); | |
624 | + if (colors.length < 10) | |
625 | + $popup.width("auto"); | |
626 | + $.each(colors, function (idx, color) { | |
627 | + $(DIV_TAG).appendTo($popup) | |
628 | + .css(BACKGROUND_COLOR, "#" + color); | |
629 | + }); | |
630 | + popupTypeClass = COLOR_CLASS; | |
631 | + } | |
632 | + | |
633 | + // Font | |
634 | + else if (popupName === "font") | |
635 | + $.each(options.fonts.split(","), function (idx, font) { | |
636 | + $(DIV_TAG).appendTo($popup) | |
637 | + .css("fontFamily", font) | |
638 | + .html(font); | |
639 | + }); | |
640 | + | |
641 | + // Size | |
642 | + else if (popupName === "size") | |
643 | + $.each(options.sizes.split(","), function (idx, size) { | |
644 | + $(DIV_TAG).appendTo($popup) | |
645 | + .html('<font size="' + size + '">' + size + '</font>'); | |
646 | + }); | |
647 | + | |
648 | + // Style | |
649 | + else if (popupName === "style") | |
650 | + $.each(options.styles, function (idx, style) { | |
651 | + $(DIV_TAG).appendTo($popup) | |
652 | + .html(style[1] + style[0] + style[1].replace("<", "</")); | |
653 | + }); | |
654 | + | |
655 | + // URL | |
656 | + else if (popupName === "url") { | |
657 | + $popup.html('<label>Enter URL:<br /><input type="text" value="http://" style="width:200px" /></label><br /><input type="button" value="Submit" />'); | |
658 | + popupTypeClass = PROMPT_CLASS; | |
659 | + } | |
660 | + | |
661 | + // Paste as Text | |
662 | + else if (popupName === "pastetext") { | |
663 | + $popup.html('<label>Paste your content here:<br /><textarea rows="3" style="width:200px"></textarea></label><br /><input type="button" value="Submit" />'); | |
664 | + popupTypeClass = PROMPT_CLASS; | |
665 | + } | |
666 | + | |
667 | + // Add the popup type class name | |
668 | + if (!popupTypeClass && !popupContent) | |
669 | + popupTypeClass = LIST_CLASS; | |
670 | + $popup.addClass(popupTypeClass); | |
671 | + | |
672 | + // Add the unselectable attribute to all items | |
673 | + if (ie) { | |
674 | + $popup.attr(UNSELECTABLE, "on") | |
675 | + .find("div,font,p,h1,h2,h3,h4,h5,h6") | |
676 | + .attr(UNSELECTABLE, "on"); | |
677 | + } | |
678 | + | |
679 | + // Add the hover effect to all items | |
680 | + if ($popup.hasClass(LIST_CLASS) || popupHover === true) | |
681 | + $popup.children().hover(hoverEnter, hoverLeave); | |
682 | + | |
683 | + // Add the popup to the array and return it | |
684 | + popups[popupName] = $popup[0]; | |
685 | + return $popup[0]; | |
686 | + | |
687 | + } | |
688 | + | |
689 | + // disable - enables or disables the editor | |
690 | + function disable(editor, disabled) { | |
691 | + | |
692 | + // Update the textarea and save the state | |
693 | + if (disabled) { | |
694 | + editor.$area.attr(DISABLED, DISABLED); | |
695 | + editor.disabled = true; | |
696 | + } | |
697 | + else { | |
698 | + editor.$area.removeAttr(DISABLED); | |
699 | + delete editor.disabled; | |
700 | + } | |
701 | + | |
702 | + // Switch the iframe into design mode. | |
703 | + // ie6 does not support designMode. | |
704 | + // ie7 & ie8 do not properly support designMode="off". | |
705 | + try { | |
706 | + if (ie) editor.doc.body.contentEditable = !disabled; | |
707 | + else editor.doc.designMode = !disabled ? "on" : "off"; | |
708 | + } | |
709 | + // Firefox 1.5 throws an exception that can be ignored | |
710 | + // when toggling designMode from off to on. | |
711 | + catch (err) { } | |
712 | + | |
713 | + // Enable or disable the toolbar buttons | |
714 | + refreshButtons(editor); | |
715 | + | |
716 | + } | |
717 | + | |
718 | + // execCommand - executes a designMode command | |
719 | + function execCommand(editor, command, value, useCSS, button) { | |
720 | + | |
721 | + // Restore the current ie selection | |
722 | + restoreRange(editor); | |
723 | + | |
724 | + // Set the styling method | |
725 | + if (!ie) { | |
726 | + if (useCSS === undefined || useCSS === null) | |
727 | + useCSS = editor.options.useCSS; | |
728 | + editor.doc.execCommand("styleWithCSS", 0, useCSS.toString()); | |
729 | + } | |
730 | + | |
731 | + // Execute the command and check for error | |
732 | + var inserthtml = command.toLowerCase() === "inserthtml"; | |
733 | + if (ie && inserthtml) | |
734 | + getRange(editor).pasteHTML(value); | |
735 | + | |
736 | + else if (iege11 && inserthtml) { | |
737 | + var selection = getSelection(editor), | |
738 | + range = selection.getRangeAt(0); | |
739 | + range.deleteContents(); | |
740 | + range.insertNode(range.createContextualFragment(value)); | |
741 | + selection.removeAllRanges(); | |
742 | + selection.addRange(range); | |
743 | + } | |
744 | + | |
745 | + else { | |
746 | + var success = true, message; | |
747 | + try { success = editor.doc.execCommand(command, 0, value || null); } | |
748 | + catch (err) { message = err.message; success = false; } | |
749 | + if (!success) { | |
750 | + if ("cutcopypaste".indexOf(command) > -1) | |
751 | + showMessage(editor, "For security reasons, your browser does not support the " + | |
752 | + command + " command. Try using the keyboard shortcut or context menu instead.", | |
753 | + button); | |
754 | + else | |
755 | + showMessage(editor, | |
756 | + (message ? message : "Error executing the " + command + " command."), | |
757 | + button); | |
758 | + } | |
759 | + } | |
760 | + | |
761 | + // Enable the buttons and update the textarea | |
762 | + refreshButtons(editor); | |
763 | + updateTextArea(editor, true); | |
764 | + return success; | |
765 | + | |
766 | + } | |
767 | + | |
768 | + // focus - sets focus to either the textarea or iframe | |
769 | + function focus(editor) { | |
770 | + setTimeout(function () { | |
771 | + if (sourceMode(editor)) editor.$area.focus(); | |
772 | + else editor.$frame[0].contentWindow.focus(); | |
773 | + refreshButtons(editor); | |
774 | + }, 0); | |
775 | + } | |
776 | + | |
777 | + // getRange - gets the current text range object | |
778 | + function getRange(editor) { | |
779 | + if (ie) return getSelection(editor).createRange(); | |
780 | + return getSelection(editor).getRangeAt(0); | |
781 | + } | |
782 | + | |
783 | + // getSelection - gets the current text range object | |
784 | + function getSelection(editor) { | |
785 | + if (ie) return editor.doc.selection; | |
786 | + return editor.$frame[0].contentWindow.getSelection(); | |
787 | + } | |
788 | + | |
789 | + // hex - returns the hex value for the passed in color string | |
790 | + function hex(s) { | |
791 | + | |
792 | + // hex("rgb(255, 0, 0)") returns #FF0000 | |
793 | + var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s); | |
794 | + if (m) { | |
795 | + s = (m[1] << 16 | m[2] << 8 | m[3]).toString(16); | |
796 | + while (s.length < 6) | |
797 | + s = "0" + s; | |
798 | + return "#" + s; | |
799 | + } | |
800 | + | |
801 | + // hex("#F00") returns #FF0000 | |
802 | + var c = s.split(""); | |
803 | + if (s.length === 4) | |
804 | + return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3]; | |
805 | + | |
806 | + // hex("#FF0000") returns #FF0000 | |
807 | + return s; | |
808 | + | |
809 | + } | |
810 | + | |
811 | + // hidePopups - hides all popups | |
812 | + function hidePopups() { | |
813 | + $.each(popups, function (idx, popup) { | |
814 | + $(popup) | |
815 | + .hide() | |
816 | + .unbind(CLICK) | |
817 | + .removeData(BUTTON); | |
818 | + }); | |
819 | + } | |
820 | + | |
821 | + // imagesPath - returns the path to the images folder | |
822 | + function imagesPath() { | |
823 | + var href = $("link[href*=cleditor]").attr("href"); | |
824 | + return href.replace(/^(.*\/)[^\/]+$/, '$1') + "images/"; | |
825 | + } | |
826 | + | |
827 | + // imageUrl - Returns the css url string for a filemane | |
828 | + function imageUrl(filename) { | |
829 | + return "url(" + imagesPath() + filename + ")"; | |
830 | + } | |
831 | + | |
832 | + // refresh - creates the iframe and resizes the controls | |
833 | + function refresh(editor) { | |
834 | + | |
835 | + var $main = editor.$main, | |
836 | + options = editor.options; | |
837 | + | |
838 | + // Remove the old iframe | |
839 | + if (editor.$frame) | |
840 | + editor.$frame.remove(); | |
841 | + | |
842 | + // Create a new iframe | |
843 | + var $frame = editor.$frame = $('<iframe frameborder="0" src="javascript:true;" />') | |
844 | + .hide() | |
845 | + .appendTo($main); | |
846 | + | |
847 | + // Load the iframe document content | |
848 | + var contentWindow = $frame[0].contentWindow, | |
849 | + doc = editor.doc = contentWindow.document, | |
850 | + $doc = $(doc); | |
851 | + | |
852 | + doc.open(); | |
853 | + doc.write( | |
854 | + options.docType + | |
855 | + '<html>' + | |
856 | + ((options.docCSSFile === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCSSFile + '" /></head>') + | |
857 | + '<body style="' + options.bodyStyle + '"></body></html>' | |
858 | + ); | |
859 | + doc.close(); | |
860 | + | |
861 | + // Work around for bug in IE which causes the editor to lose | |
862 | + // focus when clicking below the end of the document. | |
863 | + if (ie || iege11) | |
864 | + $doc.click(function () { focus(editor); }); | |
865 | + | |
866 | + // Load the content | |
867 | + updateFrame(editor); | |
868 | + | |
869 | + // Bind the ie specific iframe event handlers | |
870 | + if (ie || iege11) { | |
871 | + | |
872 | + // Save the current user selection. This code is needed since IE will | |
873 | + // reset the selection just after the beforedeactivate event and just | |
874 | + // before the beforeactivate event. | |
875 | + $doc.bind("beforedeactivate beforeactivate selectionchange keypress keyup", function (e) { | |
876 | + | |
877 | + // Flag the editor as inactive | |
878 | + if (e.type === "beforedeactivate") | |
879 | + editor.inactive = true; | |
880 | + | |
881 | + // Get rid of the bogus selection and flag the editor as active | |
882 | + else if (e.type === "beforeactivate") { | |
883 | + if (!editor.inactive && editor.range && editor.range.length > 1) | |
884 | + editor.range.shift(); | |
885 | + delete editor.inactive; | |
886 | + } | |
887 | + | |
888 | + // Save the selection when the editor is active | |
889 | + else if (!editor.inactive) { | |
890 | + if (!editor.range) | |
891 | + editor.range = []; | |
892 | + editor.range.unshift(getRange(editor)); | |
893 | + | |
894 | + // We only need the last 2 selections | |
895 | + while (editor.range.length > 2) | |
896 | + editor.range.pop(); | |
897 | + } | |
898 | + | |
899 | + }); | |
900 | + | |
901 | + // Restore the text range and trigger focused event when the iframe gains focus | |
902 | + $frame.focus(function () { | |
903 | + restoreRange(editor); | |
904 | + $(editor).triggerHandler(FOCUSED); | |
905 | + }); | |
906 | + | |
907 | + // Trigger blurred event when the iframe looses focus | |
908 | + $frame.blur(function () { | |
909 | + $(editor).triggerHandler(BLURRED); | |
910 | + }); | |
911 | + | |
912 | + } | |
913 | + | |
914 | + // Trigger focused and blurred events for all other browsers | |
915 | + else { | |
916 | + $($frame[0].contentWindow) | |
917 | + .focus(function () { $(editor).triggerHandler(FOCUSED); }) | |
918 | + .blur(function () { $(editor).triggerHandler(BLURRED); }); | |
919 | + } | |
920 | + | |
921 | + // Enable the toolbar buttons and update the textarea as the user types or clicks | |
922 | + $doc.click(hidePopups) | |
923 | + .keydown(function (e) { | |
924 | + // Prevent Internet Explorer from going to prior page when an image | |
925 | + // is selected and the backspace key is pressed. | |
926 | + if (ie && getSelection(editor).type == "Control" && e.keyCode == 8) { | |
927 | + getSelection(editor).clear(); | |
928 | + e.preventDefault(); | |
929 | + } | |
930 | + }) | |
931 | + .bind("keyup mouseup", function () { | |
932 | + refreshButtons(editor); | |
933 | + updateTextArea(editor, true); | |
934 | + }); | |
935 | + | |
936 | + // Show the textarea for iPhone/iTouch/iPad or | |
937 | + // the iframe when design mode is supported. | |
938 | + if (iOS) editor.$area.show(); | |
939 | + else $frame.show(); | |
940 | + | |
941 | + // Wait for the layout to finish - shortcut for $(document).ready() | |
942 | + $(function () { | |
943 | + | |
944 | + var $toolbar = editor.$toolbar, | |
945 | + $group = $toolbar.children("div:last"), | |
946 | + wid = $main.width(); | |
947 | + | |
948 | + // Resize the toolbar | |
949 | + var hgt = $group.offset().top + $group.outerHeight() - $toolbar.offset().top + 1; | |
950 | + $toolbar.height(hgt); | |
951 | + | |
952 | + // Resize the iframe | |
953 | + hgt = (/%/.test("" + options.height) ? $main.height() : parseInt(options.height, 10)) - hgt; | |
954 | + $frame.width(wid).height(hgt); | |
955 | + | |
956 | + // Resize the textarea. IE6 textareas have a 1px top | |
957 | + // & bottom margin that cannot be removed using css. | |
958 | + editor.$area.width(wid).height(ie6 ? hgt - 2 : hgt); | |
959 | + | |
960 | + // Switch the iframe into design mode if enabled | |
961 | + disable(editor, editor.disabled); | |
962 | + | |
963 | + // Enable or disable the toolbar buttons | |
964 | + refreshButtons(editor); | |
965 | + | |
966 | + }); | |
967 | + | |
968 | + } | |
969 | + | |
970 | + // refreshButtons - enables or disables buttons based on availability | |
971 | + function refreshButtons(editor) { | |
972 | + | |
973 | + // Webkit requires focus before queryCommandEnabled will return anything but false | |
974 | + if (!iOS && webkit && !editor.focused) { | |
975 | + editor.$frame[0].contentWindow.focus(); | |
976 | + window.focus(); | |
977 | + editor.focused = true; | |
978 | + } | |
979 | + | |
980 | + // Get the object used for checking queryCommandEnabled | |
981 | + var queryObj = editor.doc; | |
982 | + if (ie) queryObj = getRange(editor); | |
983 | + | |
984 | + // Loop through each button | |
985 | + var inSourceMode = sourceMode(editor); | |
986 | + $.each(editor.$toolbar.find("." + BUTTON_CLASS), function (idx, elem) { | |
987 | + | |
988 | + var $elem = $(elem), | |
989 | + button = $.cleditor.buttons[$.data(elem, BUTTON_NAME)], | |
990 | + command = button.command, | |
991 | + enabled = true; | |
992 | + | |
993 | + // Determine the state | |
994 | + if (editor.disabled) | |
995 | + enabled = false; | |
996 | + else if (button.getEnabled) { | |
997 | + var data = { | |
998 | + editor: editor, | |
999 | + button: elem, | |
1000 | + buttonName: button.name, | |
1001 | + popup: popups[button.popupName], | |
1002 | + popupName: button.popupName, | |
1003 | + command: button.command, | |
1004 | + useCSS: editor.options.useCSS | |
1005 | + }; | |
1006 | + enabled = button.getEnabled(data); | |
1007 | + if (enabled === undefined) | |
1008 | + enabled = true; | |
1009 | + } | |
1010 | + else if (((inSourceMode || iOS) && button.name !== "source") || | |
1011 | + (ie && (command === "undo" || command === "redo"))) | |
1012 | + enabled = false; | |
1013 | + else if (command && command !== "print") { | |
1014 | + if (ie && command === "hilitecolor") | |
1015 | + command = "backcolor"; | |
1016 | + // IE does not support inserthtml, so it's always enabled | |
1017 | + if ((!ie && !iege11) || command !== "inserthtml") { | |
1018 | + try { enabled = queryObj.queryCommandEnabled(command); } | |
1019 | + catch (err) { enabled = false; } | |
1020 | + } | |
1021 | + } | |
1022 | + | |
1023 | + // Enable or disable the button | |
1024 | + if (enabled) { | |
1025 | + $elem.removeClass(DISABLED_CLASS); | |
1026 | + $elem.removeAttr(DISABLED); | |
1027 | + } | |
1028 | + else { | |
1029 | + $elem.addClass(DISABLED_CLASS); | |
1030 | + $elem.attr(DISABLED, DISABLED); | |
1031 | + } | |
1032 | + | |
1033 | + }); | |
1034 | + } | |
1035 | + | |
1036 | + // restoreRange - restores the current ie selection | |
1037 | + function restoreRange(editor) { | |
1038 | + if (editor.range) { | |
1039 | + if (ie) | |
1040 | + editor.range[0].select(); | |
1041 | + else if (iege11) | |
1042 | + getSelection(editor).addRange(editor.range[0]); | |
1043 | + } | |
1044 | + } | |
1045 | + | |
1046 | + // select - selects all the text in either the textarea or iframe | |
1047 | + function select(editor) { | |
1048 | + setTimeout(function () { | |
1049 | + if (sourceMode(editor)) editor.$area.select(); | |
1050 | + else execCommand(editor, "selectall"); | |
1051 | + }, 0); | |
1052 | + } | |
1053 | + | |
1054 | + // selectedHTML - returns the current HTML selection or and empty string | |
1055 | + function selectedHTML(editor) { | |
1056 | + restoreRange(editor); | |
1057 | + var range = getRange(editor); | |
1058 | + if (ie) | |
1059 | + return range.htmlText; | |
1060 | + var layer = $("<layer>")[0]; | |
1061 | + layer.appendChild(range.cloneContents()); | |
1062 | + var html = layer.innerHTML; | |
1063 | + layer = null; | |
1064 | + return html; | |
1065 | + } | |
1066 | + | |
1067 | + // selectedText - returns the current text selection or and empty string | |
1068 | + function selectedText(editor) { | |
1069 | + restoreRange(editor); | |
1070 | + if (ie) return getRange(editor).text; | |
1071 | + return getSelection(editor).toString(); | |
1072 | + } | |
1073 | + | |
1074 | + // showMessage - alert replacement | |
1075 | + function showMessage(editor, message, button) { | |
1076 | + var popup = createPopup("msg", editor.options, MSG_CLASS); | |
1077 | + popup.innerHTML = message; | |
1078 | + showPopup(editor, popup, button); | |
1079 | + } | |
1080 | + | |
1081 | + // showPopup - shows a popup | |
1082 | + function showPopup(editor, popup, button) { | |
1083 | + | |
1084 | + var offset, left, top, $popup = $(popup); | |
1085 | + | |
1086 | + // Determine the popup location | |
1087 | + if (button) { | |
1088 | + var $button = $(button); | |
1089 | + offset = $button.offset(); | |
1090 | + left = --offset.left; | |
1091 | + top = offset.top + $button.height(); | |
1092 | + } | |
1093 | + else { | |
1094 | + var $toolbar = editor.$toolbar; | |
1095 | + offset = $toolbar.offset(); | |
1096 | + left = Math.floor(($toolbar.width() - $popup.width()) / 2) + offset.left; | |
1097 | + top = offset.top + $toolbar.height() - 2; | |
1098 | + } | |
1099 | + | |
1100 | + // Position and show the popup | |
1101 | + hidePopups(); | |
1102 | + $popup.css({ left: left, top: top }) | |
1103 | + .show(); | |
1104 | + | |
1105 | + // Assign the popup button and click event handler | |
1106 | + if (button) { | |
1107 | + $.data(popup, BUTTON, button); | |
1108 | + $popup.bind(CLICK, { popup: popup }, $.proxy(popupClick, editor)); | |
1109 | + } | |
1110 | + | |
1111 | + // Focus the first input element if any | |
1112 | + setTimeout(function () { | |
1113 | + $popup.find(":text,textarea").eq(0).focus().select(); | |
1114 | + }, 100); | |
1115 | + | |
1116 | + } | |
1117 | + | |
1118 | + // sourceMode - returns true if the textarea is showing | |
1119 | + function sourceMode(editor) { | |
1120 | + return editor.$area.is(":visible"); | |
1121 | + } | |
1122 | + | |
1123 | + // updateFrame - updates the iframe with the textarea contents | |
1124 | + function updateFrame(editor, checkForChange) { | |
1125 | + | |
1126 | + var code = editor.$area.val(), | |
1127 | + options = editor.options, | |
1128 | + updateFrameCallback = options.updateFrame, | |
1129 | + $body = $(editor.doc.body); | |
1130 | + | |
1131 | + // Check for textarea change to avoid unnecessary firing | |
1132 | + // of potentially heavy updateFrame callbacks. | |
1133 | + if (updateFrameCallback) { | |
1134 | + var sum = checksum(code); | |
1135 | + if (checkForChange && editor.areaChecksum === sum) | |
1136 | + return; | |
1137 | + editor.areaChecksum = sum; | |
1138 | + } | |
1139 | + | |
1140 | + // Convert the textarea source code into iframe html | |
1141 | + var html = updateFrameCallback ? updateFrameCallback(code) : code; | |
1142 | + | |
1143 | + // Prevent script injection attacks by html encoding script tags | |
1144 | + html = html.replace(/<(?=\/?script)/ig, "<"); | |
1145 | + | |
1146 | + // Update the iframe checksum | |
1147 | + if (options.updateTextArea) | |
1148 | + editor.frameChecksum = checksum(html); | |
1149 | + | |
1150 | + // Update the iframe and trigger the change event | |
1151 | + if (html !== $body.html()) { | |
1152 | + $body.html(html); | |
1153 | + $(editor).triggerHandler(CHANGE); | |
1154 | + } | |
1155 | + | |
1156 | + } | |
1157 | + | |
1158 | + // updateTextArea - updates the textarea with the iframe contents | |
1159 | + function updateTextArea(editor, checkForChange) { | |
1160 | + | |
1161 | + var html = $(editor.doc.body).html(), | |
1162 | + options = editor.options, | |
1163 | + updateTextAreaCallback = options.updateTextArea, | |
1164 | + $area = editor.$area; | |
1165 | + | |
1166 | + // Check for iframe change to avoid unnecessary firing | |
1167 | + // of potentially heavy updateTextArea callbacks. | |
1168 | + if (updateTextAreaCallback) { | |
1169 | + var sum = checksum(html); | |
1170 | + if (checkForChange && editor.frameChecksum === sum) | |
1171 | + return; | |
1172 | + editor.frameChecksum = sum; | |
1173 | + } | |
1174 | + | |
1175 | + // Convert the iframe html into textarea source code | |
1176 | + var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html; | |
1177 | + | |
1178 | + // Update the textarea checksum | |
1179 | + if (options.updateFrame) | |
1180 | + editor.areaChecksum = checksum(code); | |
1181 | + | |
1182 | + // Update the textarea and trigger the change event | |
1183 | + if (code !== $area.val()) { | |
1184 | + $area.val(code); | |
1185 | + $(editor).triggerHandler(CHANGE); | |
1186 | + } | |
1187 | + | |
1188 | + } | |
1189 | + | |
1190 | +})(jQuery); |