cleditor_rails 0.0.2 → 0.0.3

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