wysihat-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.gitignore +5 -0
  2. data/CHANGELOG +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +20 -0
  5. data/Rakefile +41 -0
  6. data/TODO +3 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/wysihat_files_controller.rb +26 -0
  9. data/app/helpers/wysihat_files_helper.rb +2 -0
  10. data/app/models/wysihat_file.rb +3 -0
  11. data/app/views/wysihat_files/_form.html.erb +13 -0
  12. data/app/views/wysihat_files/_wysihat_file.html.erb +3 -0
  13. data/app/views/wysihat_files/index.html.erb +5 -0
  14. data/app/views/wysihat_files/new.html.erb +1 -0
  15. data/generators/wysihat/templates/images/arrow_redo.png +0 -0
  16. data/generators/wysihat/templates/images/arrow_undo.png +0 -0
  17. data/generators/wysihat/templates/images/b.png +0 -0
  18. data/generators/wysihat/templates/images/bl.png +0 -0
  19. data/generators/wysihat/templates/images/br.png +0 -0
  20. data/generators/wysihat/templates/images/closelabel.gif +0 -0
  21. data/generators/wysihat/templates/images/exclamation.png +0 -0
  22. data/generators/wysihat/templates/images/film.png +0 -0
  23. data/generators/wysihat/templates/images/image.png +0 -0
  24. data/generators/wysihat/templates/images/link.png +0 -0
  25. data/generators/wysihat/templates/images/loading.gif +0 -0
  26. data/generators/wysihat/templates/images/page_code.png +0 -0
  27. data/generators/wysihat/templates/images/page_white_flash.png +0 -0
  28. data/generators/wysihat/templates/images/paste_plain.png +0 -0
  29. data/generators/wysihat/templates/images/text_align_center.png +0 -0
  30. data/generators/wysihat/templates/images/text_align_left.png +0 -0
  31. data/generators/wysihat/templates/images/text_align_right.png +0 -0
  32. data/generators/wysihat/templates/images/text_bold.png +0 -0
  33. data/generators/wysihat/templates/images/text_heading_1.png +0 -0
  34. data/generators/wysihat/templates/images/text_heading_2.png +0 -0
  35. data/generators/wysihat/templates/images/text_heading_3.png +0 -0
  36. data/generators/wysihat/templates/images/text_italic.png +0 -0
  37. data/generators/wysihat/templates/images/text_list_bullets.png +0 -0
  38. data/generators/wysihat/templates/images/text_list_numbers.png +0 -0
  39. data/generators/wysihat/templates/images/text_strikethrough.png +0 -0
  40. data/generators/wysihat/templates/images/text_underline.png +0 -0
  41. data/generators/wysihat/templates/images/tl.png +0 -0
  42. data/generators/wysihat/templates/images/tr.png +0 -0
  43. data/generators/wysihat/templates/javascripts/facebox.js +190 -0
  44. data/generators/wysihat/templates/javascripts/wysihat.js +2098 -0
  45. data/generators/wysihat/templates/migrations/create_wysihat_files.rb +15 -0
  46. data/generators/wysihat/templates/stylesheets/facebox.css +95 -0
  47. data/generators/wysihat/templates/stylesheets/wysihat.css +76 -0
  48. data/generators/wysihat/wysihat_generator.rb +50 -0
  49. data/lib/wysihat-engine.rb +132 -0
  50. data/vendor/plugins/responds_to_parent/MIT-LICENSE +20 -0
  51. data/vendor/plugins/responds_to_parent/README +42 -0
  52. data/vendor/plugins/responds_to_parent/Rakefile +22 -0
  53. data/vendor/plugins/responds_to_parent/init.rb +2 -0
  54. data/vendor/plugins/responds_to_parent/lib/parent_selector_assertion.rb +144 -0
  55. data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +37 -0
  56. data/vendor/plugins/responds_to_parent/rails/init.rb +2 -0
  57. data/vendor/plugins/responds_to_parent/responds-to-parent.gemspec +15 -0
  58. data/vendor/plugins/responds_to_parent/test/assert_select_parent_test.rb +319 -0
  59. data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +116 -0
  60. data/wysihat-engine.gemspec +101 -0
  61. metadata +133 -0
@@ -0,0 +1,2098 @@
1
+ /* WysiHat - WYSIWYG JavaScript framework, version 0.2
2
+ * (c) 2008-2009 Joshua Peek
3
+ *
4
+ * WysiHat is freely distributable under the terms of an MIT-style license.
5
+ *--------------------------------------------------------------------------*/
6
+
7
+
8
+ var WysiHat = {};
9
+
10
+ WysiHat.Editor = {
11
+ attach: function(textarea, options, block) {
12
+ options = $H(options);
13
+ textarea = $(textarea);
14
+ textarea.hide();
15
+
16
+ var model = options.get('model') || WysiHat.iFrame;
17
+ var initializer = block;
18
+
19
+ return model.create(textarea, function(editArea) {
20
+ var document = editArea.getDocument();
21
+ var window = editArea.getWindow();
22
+
23
+ editArea.load();
24
+
25
+ Event.observe(window, 'focus', function(event) { editArea.focus(); });
26
+ Event.observe(window, 'blur', function(event) { editArea.blur(); });
27
+
28
+ editArea._observeEvents();
29
+
30
+ if (Prototype.Browser.Gecko) {
31
+ editArea.execCommand('undo', false, null);
32
+ }
33
+
34
+ if (initializer)
35
+ initializer(editArea);
36
+
37
+ editArea.focus();
38
+ });
39
+ },
40
+
41
+ include: function(module) {
42
+ this.includedModules = this.includedModules || $A([]);
43
+ this.includedModules.push(module);
44
+ },
45
+
46
+ extend: function(object) {
47
+ var modules = this.includedModules || $A([]);
48
+ modules.each(function(module) {
49
+ Object.extend(object, module);
50
+ });
51
+ }
52
+ };
53
+
54
+ WysiHat.Commands = (function() {
55
+ function boldSelection() {
56
+ this.execCommand('bold', false, null);
57
+ }
58
+
59
+ function boldSelected() {
60
+ return this.queryCommandState('bold');
61
+ }
62
+
63
+ function underlineSelection() {
64
+ this.execCommand('underline', false, null);
65
+ }
66
+
67
+ function underlineSelected() {
68
+ return this.queryCommandState('underline');
69
+ }
70
+
71
+ function italicSelection() {
72
+ this.execCommand('italic', false, null);
73
+ }
74
+
75
+ function italicSelected() {
76
+ return this.queryCommandState('italic');
77
+ }
78
+
79
+ function strikethroughSelection() {
80
+ this.execCommand('strikethrough', false, null);
81
+ }
82
+
83
+ function blockquoteSelection() {
84
+ this.execCommand('blockquote', false, null);
85
+ }
86
+
87
+ function fontSelection(font) {
88
+ this.execCommand('fontname', false, font);
89
+ }
90
+
91
+ function fontSelected() {
92
+ var node = this.selection.getNode();
93
+ return Element.getStyle(node, 'fontFamily');
94
+ }
95
+
96
+ function fontSizeSelection(fontSize) {
97
+ this.execCommand('fontsize', false, fontSize);
98
+ }
99
+
100
+ function fontSizeSelected() {
101
+ var node = this.selection.getNode();
102
+ return standardizeFontSize(Element.getStyle(node, 'fontSize'));
103
+ }
104
+
105
+ function colorSelection(color) {
106
+ this.execCommand('forecolor', false, color);
107
+ }
108
+
109
+ function colorSelected() {
110
+ var node = this.selection.getNode();
111
+ return standardizeColor(Element.getStyle(node, 'color'));
112
+ }
113
+
114
+ function backgroundColorSelection(color) {
115
+ if(Prototype.Browser.Gecko) {
116
+ this.execCommand('hilitecolor', false, color);
117
+ } else {
118
+ this.execCommand('backcolor', false, color);
119
+ }
120
+ }
121
+
122
+ function backgroundColorSelected() {
123
+ var node = this.selection.getNode();
124
+ return standardizeColor(Element.getStyle(node, 'backgroundColor'));
125
+ }
126
+
127
+ function alignSelection(alignment) {
128
+ this.execCommand('justify' + alignment);
129
+ }
130
+
131
+ function alignSelected() {
132
+ var node = this.selection.getNode();
133
+ return Element.getStyle(node, 'textAlign');
134
+ }
135
+
136
+ function linkSelection(url) {
137
+ this.execCommand('createLink', false, url);
138
+ }
139
+
140
+ function unlinkSelection() {
141
+ var node = this.selection.getNode();
142
+ if (this.linkSelected())
143
+ this.selection.selectNode(node);
144
+
145
+ this.execCommand('unlink', false, null);
146
+ }
147
+
148
+ function linkSelected() {
149
+ var node = this.selection.getNode();
150
+ return node ? node.tagName.toUpperCase() == 'A' : false;
151
+ }
152
+
153
+ function insertOrderedList() {
154
+ this.execCommand('insertorderedlist', false, null);
155
+ }
156
+
157
+ function insertUnorderedList() {
158
+ this.execCommand('insertunorderedlist', false, null);
159
+ }
160
+
161
+ function insertImage(url) {
162
+ this.execCommand('insertImage', false, url);
163
+ }
164
+
165
+ function insertHTML(html) {
166
+ if (Prototype.Browser.IE) {
167
+ var range = this._selection.getRange();
168
+ range.pasteHTML(html);
169
+ range.collapse(false);
170
+ range.select();
171
+ } else {
172
+ this.execCommand('insertHTML', false, html);
173
+ }
174
+ }
175
+
176
+ function execCommand(command, ui, value) {
177
+ var document = this.getDocument();
178
+
179
+ if (Prototype.Browser.IE) this.selection.restore();
180
+
181
+ var handler = this.commands.get(command)
182
+ if (handler)
183
+ handler.bind(this)(value);
184
+ else
185
+ document.execCommand(command, ui, value);
186
+ }
187
+
188
+ function queryCommandState(state) {
189
+ var document = this.getDocument();
190
+
191
+ var handler = this.queryCommands.get(state)
192
+ if (handler)
193
+ return handler.bind(this)();
194
+ else
195
+ return document.queryCommandState(state);
196
+ }
197
+ var fontSizeNames = $w('xxx-small xx-small x-small small medium large x-large xx-large');
198
+ var fontSizePixels = $w('9px 10px 13px 16px 18px 24px 32px 48px');
199
+
200
+ if (Prototype.Browser.WebKit) {
201
+ fontSizeNames.shift();
202
+ fontSizeNames.push('-webkit-xxx-large');
203
+ }
204
+
205
+ function standardizeFontSize(fontSize) {
206
+ var newSize = fontSizeNames.indexOf(fontSize);
207
+ if (newSize >= 0) return newSize;
208
+
209
+ newSize = fontSizePixels.indexOf(fontSize);
210
+ if (newSize >= 0) return newSize;
211
+ return parseInt(fontSize);
212
+ }
213
+
214
+ function standardizeColor(color) {
215
+ if (!color || color.match(/[0-9a-f]{6}/i)) return color;
216
+ var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
217
+ if(m){
218
+ var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1];
219
+ if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
220
+ var r = c[0];
221
+ if(r.charAt(r.length - 1) == "%"){
222
+ var a = c.map(function(x){
223
+ return parseFloat(x) * 2.56;
224
+ });
225
+ if(l == 4){ a[3] = c[3]; }
226
+ return _colorFromArray(a);
227
+ }
228
+ return _colorFromArray(c);
229
+ }
230
+ if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
231
+ var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
232
+ S = parseFloat(c[1]) / 100,
233
+ L = parseFloat(c[2]) / 100,
234
+ m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
235
+ m1 = 2 * L - m2,
236
+ a = [_hue2rgb(m1, m2, H + 1 / 3) * 256,
237
+ _hue2rgb(m1, m2, H) * 256, _hue2rgb(m1, m2, H - 1 / 3) * 256, 1];
238
+ if(l == 4){ a[3] = c[3]; }
239
+ return _colorFromArray(a);
240
+ }
241
+ }
242
+ return null; // dojo.Color
243
+ }
244
+
245
+ function _colorFromArray(a) {
246
+ var arr = a.slice(0, 3).map(function(x){
247
+ var s = parseInt(x).toString(16);
248
+ return s.length < 2 ? "0" + s : s;
249
+ });
250
+ return "#" + arr.join(""); // String
251
+ }
252
+
253
+ function _hue2rgb(m1, m2, h){
254
+ if(h < 0){ ++h; }
255
+ if(h > 1){ --h; }
256
+ var h6 = 6 * h;
257
+ if(h6 < 1){ return m1 + (m2 - m1) * h6; }
258
+ if(2 * h < 1){ return m2; }
259
+ if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
260
+ return m1;
261
+ }
262
+
263
+ return {
264
+ boldSelection: boldSelection,
265
+ boldSelected: boldSelected,
266
+ underlineSelection: underlineSelection,
267
+ underlineSelected: underlineSelected,
268
+ italicSelection: italicSelection,
269
+ italicSelected: italicSelected,
270
+ strikethroughSelection: strikethroughSelection,
271
+ blockquoteSelection: blockquoteSelection,
272
+ fontSelection: fontSelection,
273
+ fontSelected: fontSelected,
274
+ fontSizeSelection: fontSizeSelection,
275
+ fontSizeSelected: fontSizeSelected,
276
+ colorSelection: colorSelection,
277
+ colorSelected: colorSelected,
278
+ backgroundColorSelection: backgroundColorSelection,
279
+ backgroundColorSelected: backgroundColorSelected,
280
+ alignSelection: alignSelection,
281
+ alignSelected: alignSelected,
282
+ linkSelection: linkSelection,
283
+ unlinkSelection: unlinkSelection,
284
+ linkSelected: linkSelected,
285
+ insertOrderedList: insertOrderedList,
286
+ insertUnorderedList: insertUnorderedList,
287
+ insertImage: insertImage,
288
+ insertHTML: insertHTML,
289
+ execCommand: execCommand,
290
+ queryCommandState: queryCommandState,
291
+
292
+ commands: $H({}),
293
+
294
+ queryCommands: $H({
295
+ link: linkSelected
296
+ })
297
+ };
298
+ })();
299
+
300
+ WysiHat.Editor.include(WysiHat.Commands);
301
+ WysiHat.Events = (function() {
302
+ var eventsToFoward = [
303
+ 'click',
304
+ 'dblclick',
305
+ 'mousedown',
306
+ 'mouseup',
307
+ 'mouseover',
308
+ 'mousemove',
309
+ 'mouseout',
310
+ 'keypress',
311
+ 'keydown',
312
+ 'keyup'
313
+ ];
314
+
315
+ function forwardEvents(document, editor) {
316
+ eventsToFoward.each(function(event) {
317
+ Event.observe(document, event, function(e) {
318
+ editor.fire('wysihat:' + event);
319
+ });
320
+ });
321
+ }
322
+
323
+ function observePasteEvent(window, document, editor) {
324
+ Event.observe(document, 'keydown', function(event) {
325
+ if (event.keyCode == 86)
326
+ editor.fire("wysihat:paste");
327
+ });
328
+
329
+ Event.observe(window, 'paste', function(event) {
330
+ editor.fire("wysihat:paste");
331
+ });
332
+ }
333
+
334
+ function observeFocus(window, editor) {
335
+ Event.observe(window, 'focus', function(event) {
336
+ editor.fire("wysihat:focus");
337
+ });
338
+
339
+ Event.observe(window, 'blur', function(event) {
340
+ editor.fire("wysihat:blur");
341
+ });
342
+ }
343
+
344
+ function observeSelections(document, editor) {
345
+ Event.observe(document, 'mouseup', function(event) {
346
+ var range = editor.selection.getRange();
347
+ if (!range.collapsed)
348
+ editor.fire("wysihat:select");
349
+ });
350
+ }
351
+
352
+ function observeChanges(document, editor) {
353
+ var previousContents = editor.rawContent();
354
+ Event.observe(document, 'keyup', function(event) {
355
+ var contents = editor.rawContent();
356
+ if (previousContents != contents) {
357
+ editor.fire("wysihat:change");
358
+ previousContents = contents;
359
+ }
360
+ });
361
+ }
362
+
363
+ function observeCursorMovements(document, editor) {
364
+ var previousRange = editor.selection.getRange();
365
+ var handler = function(event) {
366
+ var range = editor.selection.getRange();
367
+ if (previousRange != range) {
368
+ editor.fire("wysihat:cursormove");
369
+ previousRange = range;
370
+ }
371
+ };
372
+
373
+ Event.observe(document, 'keyup', handler);
374
+ Event.observe(document, 'mouseup', handler);
375
+ }
376
+
377
+ function observeEvents() {
378
+ if (this._observers_setup)
379
+ return;
380
+
381
+ var document = this.getDocument();
382
+ var window = this.getWindow();
383
+
384
+ forwardEvents(document, this);
385
+ observePasteEvent(window, document, this);
386
+ observeFocus(window, this);
387
+ observeSelections(document, this);
388
+ observeChanges(document, this);
389
+ observeCursorMovements(document, this);
390
+
391
+ this._observers_setup = true;
392
+ }
393
+
394
+ return {
395
+ _observeEvents: observeEvents
396
+ };
397
+ })();
398
+
399
+ WysiHat.Editor.include(WysiHat.Events);
400
+ WysiHat.Persistence = (function() {
401
+ function outputFilter(text) {
402
+ return text.formatHTMLOutput();
403
+ }
404
+
405
+ function inputFilter(text) {
406
+ return text.formatHTMLInput();
407
+ }
408
+
409
+ function content() {
410
+ return this.outputFilter(this.rawContent());
411
+ }
412
+
413
+ function setContent(text) {
414
+ this.setRawContent(this.inputFilter(text));
415
+ }
416
+
417
+ function save() {
418
+ this.textarea.value = this.content();
419
+ }
420
+
421
+ function load() {
422
+ this.setContent(this.textarea.value);
423
+ }
424
+
425
+ function reload() {
426
+ this.selection.setBookmark();
427
+ this.save();
428
+ this.load();
429
+ this.selection.moveToBookmark();
430
+ }
431
+
432
+ return {
433
+ outputFilter: outputFilter,
434
+ inputFilter: inputFilter,
435
+ content: content,
436
+ setContent: setContent,
437
+ save: save,
438
+ load: load,
439
+ reload: reload
440
+ };
441
+ })();
442
+
443
+ WysiHat.Editor.include(WysiHat.Persistence);
444
+ WysiHat.Window = (function() {
445
+ function getDocument() {
446
+ return this.contentDocument || this.contentWindow.document;
447
+ }
448
+
449
+ function getWindow() {
450
+ if (this.contentDocument)
451
+ return this.contentDocument.defaultView;
452
+ else if (this.contentWindow.document)
453
+ return this.contentWindow;
454
+ else
455
+ return null;
456
+ }
457
+
458
+ function focus() {
459
+ this.getWindow().focus();
460
+
461
+ if (this.hasFocus)
462
+ return;
463
+
464
+ this.hasFocus = true;
465
+ }
466
+
467
+ function blur() {
468
+ this.hasFocus = false;
469
+ }
470
+
471
+ return {
472
+ getDocument: getDocument,
473
+ getWindow: getWindow,
474
+ focus: focus,
475
+ blur: blur
476
+ };
477
+ })();
478
+
479
+ WysiHat.Editor.include(WysiHat.Window);
480
+ WysiHat.iFrame = {
481
+ create: function(textarea, callback) {
482
+ var editArea = new Element('iframe', { 'id': textarea.id + '_editor', 'class': 'editor' });
483
+
484
+ Object.extend(editArea, WysiHat.iFrame.Methods);
485
+ WysiHat.Editor.extend(editArea);
486
+
487
+ editArea.attach(textarea, callback);
488
+ textarea.insert({before: editArea});
489
+
490
+ return editArea;
491
+ }
492
+ };
493
+
494
+ WysiHat.iFrame.Methods = {
495
+ attach: function(element, callback) {
496
+ this.textarea = element;
497
+
498
+ this.observe('load', function() {
499
+ try {
500
+ var document = this.getDocument();
501
+ } catch(e) { return; } // No iframe, just stop
502
+
503
+ this.selection = new WysiHat.Selection(this);
504
+
505
+ if (this.ready && document.designMode == 'on')
506
+ return;
507
+
508
+ this.setStyle({});
509
+ document.designMode = 'on';
510
+ callback(this);
511
+ this.ready = true;
512
+ this.fire('wysihat:ready');
513
+ });
514
+ },
515
+
516
+ unattach: function() {
517
+ this.remove();
518
+ },
519
+
520
+ whenReady: function(callback) {
521
+ if (this.ready) {
522
+ callback(this);
523
+ } else {
524
+ var editor = this;
525
+ editor.observe('wysihat:ready', function() { callback(editor); });
526
+ }
527
+ return this;
528
+ },
529
+
530
+ setStyle: function(styles) {
531
+ var document = this.getDocument();
532
+
533
+ var element = this;
534
+ if (!this.ready)
535
+ return setTimeout(function() { element.setStyle(styles); }, 1);
536
+
537
+ if (Prototype.Browser.IE) {
538
+ var style = document.createStyleSheet();
539
+ style.addRule("body", "border: 0");
540
+ style.addRule("p", "margin: 0");
541
+
542
+ $H(styles).each(function(pair) {
543
+ var value = pair.first().underscore().dasherize() + ": " + pair.last();
544
+ style.addRule("body", value);
545
+ });
546
+ } else if (Prototype.Browser.Opera) {
547
+ var style = Element('style').update("p { margin: 0; }");
548
+ var head = document.getElementsByTagName('head')[0];
549
+ head.appendChild(style);
550
+ } else {
551
+ Element.setStyle(document.body, styles);
552
+ }
553
+
554
+ return this;
555
+ },
556
+
557
+
558
+ linkStyleSheet: function(href) {
559
+ this.whenReady(function(editor){
560
+ var document = editor.getDocument();
561
+ if(document.createStyleSheet) { // IE
562
+ document.createStyleSheet(css);
563
+ } else {
564
+ var head = document.documentElement.getElementsByTagName('head')[0];
565
+ if (!head) {
566
+ head=document.createElement('head');
567
+ document.documentElement.insertBefore(head,document.getElementsByTagName('body')[0]);
568
+ }
569
+ var link='<link href="'+href+'" media="screen" rel="stylesheet" type="text/css"/>';
570
+ head=$(head);
571
+ if (head.insert) { // Safari
572
+ $(head).insert(link);
573
+ } else { // everyone else
574
+ head.innerHTML=head.innerHTML+link;
575
+ }
576
+ }
577
+ });
578
+ },
579
+
580
+
581
+
582
+ /**
583
+ * WysiHat.iFrame.Methods#getStyle(style) -> string
584
+ * - style specificication (i.e. backgroundColor)
585
+ *
586
+ * Returns the style from the element based on the given style
587
+ */
588
+ getStyle: function(style) {
589
+ var document = this.getDocument();
590
+ return Element.getStyle(document.body, style);
591
+ },
592
+
593
+ rawContent: function() {
594
+ var document = this.getDocument();
595
+
596
+ if (document.body)
597
+ return document.body.innerHTML;
598
+ else
599
+ return "";
600
+ },
601
+
602
+ setRawContent: function(text) {
603
+ var document = this.getDocument();
604
+ if (document.body)
605
+ document.body.innerHTML = text;
606
+ }
607
+ };
608
+ WysiHat.Editable = {
609
+ create: function(textarea, callback) {
610
+ var editArea = new Element('div', {
611
+ 'id': textarea.id + '_editor',
612
+ 'class': 'editor',
613
+ 'contenteditable': 'true'
614
+ });
615
+ editArea.textarea = textarea;
616
+
617
+ WysiHat.Editor.extend(editArea);
618
+ Object.extend(editArea, WysiHat.Editable.Methods);
619
+
620
+ callback(editArea);
621
+
622
+ textarea.insert({before: editArea});
623
+
624
+ return editArea;
625
+ }
626
+ };
627
+
628
+ WysiHat.Editable.Methods = {
629
+ getDocument: function() {
630
+ return document;
631
+ },
632
+
633
+ getWindow: function() {
634
+ return window;
635
+ },
636
+
637
+ rawContent: function() {
638
+ return this.innerHTML;
639
+ },
640
+
641
+ setRawContent: function(text) {
642
+ this.innerHTML = text;
643
+ }
644
+ };
645
+
646
+ Object.extend(String.prototype, (function() {
647
+ function formatHTMLOutput() {
648
+ var text = String(this);
649
+ text = text.tidyXHTML();
650
+
651
+ if (Prototype.Browser.WebKit) {
652
+ text = text.replace(/(<div>)+/g, "\n");
653
+ text = text.replace(/(<\/div>)+/g, "");
654
+
655
+ text = text.replace(/<p>\s*<\/p>/g, "");
656
+
657
+ text = text.replace(/<br \/>(\n)*/g, "\n");
658
+ } else if (Prototype.Browser.Gecko) {
659
+ text = text.replace(/<p>/g, "");
660
+ text = text.replace(/<\/p>(\n)?/g, "\n");
661
+
662
+ text = text.replace(/<br \/>(\n)*/g, "\n");
663
+ } else if (Prototype.Browser.IE || Prototype.Browser.Opera) {
664
+ text = text.replace(/<p>(&nbsp;|&#160;|\s)<\/p>/g, "<p></p>");
665
+
666
+ text = text.replace(/<br \/>/g, "");
667
+
668
+ text = text.replace(/<p>/g, '');
669
+
670
+ text = text.replace(/&nbsp;/g, '');
671
+
672
+ text = text.replace(/<\/p>(\n)?/g, "\n");
673
+
674
+ text = text.gsub(/^<p>/, '');
675
+ text = text.gsub(/<\/p>$/, '');
676
+ }
677
+
678
+ text = text.gsub(/<b>/, "<strong>");
679
+ text = text.gsub(/<\/b>/, "</strong>");
680
+
681
+ text = text.gsub(/<i>/, "<em>");
682
+ text = text.gsub(/<\/i>/, "</em>");
683
+
684
+ text = text.replace(/\n\n+/g, "</p>\n\n<p>");
685
+
686
+ text = text.gsub(/(([^\n])(\n))(?=([^\n]))/, "#{2}<br />\n");
687
+
688
+ text = '<p>' + text + '</p>';
689
+
690
+ text = text.replace(/<p>\s*/g, "<p>");
691
+ text = text.replace(/\s*<\/p>/g, "</p>");
692
+
693
+ var element = Element("body");
694
+ element.innerHTML = text;
695
+
696
+ if (Prototype.Browser.WebKit || Prototype.Browser.Gecko) {
697
+ var replaced;
698
+ do {
699
+ replaced = false;
700
+ element.select('span').each(function(span) {
701
+ if (span.hasClassName('Apple-style-span')) {
702
+ span.removeClassName('Apple-style-span');
703
+ if (span.className == '')
704
+ span.removeAttribute('class');
705
+ replaced = true;
706
+ } else if (span.getStyle('fontWeight') == 'bold') {
707
+ span.setStyle({fontWeight: ''});
708
+ if (span.style.length == 0)
709
+ span.removeAttribute('style');
710
+ span.update('<strong>' + span.innerHTML + '</strong>');
711
+ replaced = true;
712
+ } else if (span.getStyle('fontStyle') == 'italic') {
713
+ span.setStyle({fontStyle: ''});
714
+ if (span.style.length == 0)
715
+ span.removeAttribute('style');
716
+ span.update('<em>' + span.innerHTML + '</em>');
717
+ replaced = true;
718
+ } else if (span.getStyle('textDecoration') == 'underline') {
719
+ span.setStyle({textDecoration: ''});
720
+ if (span.style.length == 0)
721
+ span.removeAttribute('style');
722
+ span.update('<u>' + span.innerHTML + '</u>');
723
+ replaced = true;
724
+ } else if (span.attributes.length == 0) {
725
+ span.replace(span.innerHTML);
726
+ replaced = true;
727
+ }
728
+ });
729
+ } while (replaced);
730
+
731
+ }
732
+
733
+ var acceptableBlankTags = $A(['BR', 'IMG']);
734
+
735
+ for (var i = 0; i < element.descendants().length; i++) {
736
+ var node = element.descendants()[i];
737
+ if (node.innerHTML.blank() && !acceptableBlankTags.include(node.nodeName) && node.id != 'bookmark')
738
+ node.remove();
739
+ }
740
+
741
+ text = element.innerHTML;
742
+ text = text.tidyXHTML();
743
+
744
+ text = text.replace(/<br \/>(\n)*/g, "<br />\n");
745
+ text = text.replace(/<\/p>\n<p>/g, "</p>\n\n<p>");
746
+
747
+ text = text.replace(/<p>\s*<\/p>/g, "");
748
+
749
+ text = text.replace(/\s*$/g, "");
750
+
751
+ return text;
752
+ }
753
+
754
+ function formatHTMLInput() {
755
+ var text = String(this);
756
+
757
+ var element = Element("body");
758
+ element.innerHTML = text;
759
+
760
+ if (Prototype.Browser.Gecko || Prototype.Browser.WebKit) {
761
+ element.select('strong').each(function(element) {
762
+ element.replace('<span style="font-weight: bold;">' + element.innerHTML + '</span>');
763
+ });
764
+ element.select('em').each(function(element) {
765
+ element.replace('<span style="font-style: italic;">' + element.innerHTML + '</span>');
766
+ });
767
+ element.select('u').each(function(element) {
768
+ element.replace('<span style="text-decoration: underline;">' + element.innerHTML + '</span>');
769
+ });
770
+ }
771
+
772
+ if (Prototype.Browser.WebKit)
773
+ element.select('span').each(function(span) {
774
+ if (span.getStyle('fontWeight') == 'bold')
775
+ span.addClassName('Apple-style-span');
776
+
777
+ if (span.getStyle('fontStyle') == 'italic')
778
+ span.addClassName('Apple-style-span');
779
+
780
+ if (span.getStyle('textDecoration') == 'underline')
781
+ span.addClassName('Apple-style-span');
782
+ });
783
+
784
+ text = element.innerHTML;
785
+ text = text.tidyXHTML();
786
+
787
+ text = text.replace(/<\/p>(\n)*<p>/g, "\n\n");
788
+
789
+ text = text.replace(/(\n)?<br( \/)?>(\n)?/g, "\n");
790
+
791
+ text = text.replace(/^<p>/g, '');
792
+ text = text.replace(/<\/p>$/g, '');
793
+
794
+ if (Prototype.Browser.Gecko) {
795
+ text = text.replace(/\n/g, "<br>");
796
+ text = text + '<br>';
797
+ } else if (Prototype.Browser.WebKit) {
798
+ text = text.replace(/\n/g, "</div><div>");
799
+ text = '<div>' + text + '</div>';
800
+ text = text.replace(/<div><\/div>/g, "<div><br></div>");
801
+ } else if (Prototype.Browser.IE || Prototype.Browser.Opera) {
802
+ text = text.replace(/\n/g, "</p>\n<p>");
803
+ text = '<p>' + text + '</p>';
804
+ text = text.replace(/<p><\/p>/g, "<p>&nbsp;</p>");
805
+ text = text.replace(/(<p>&nbsp;<\/p>)+$/g, "");
806
+ }
807
+
808
+ return text;
809
+ }
810
+
811
+ function tidyXHTML() {
812
+ var text = String(this);
813
+
814
+ text = text.gsub(/\r\n?/, "\n");
815
+
816
+ text = text.gsub(/<([A-Z]+)([^>]*)>/, function(match) {
817
+ return '<' + match[1].toLowerCase() + match[2] + '>';
818
+ });
819
+
820
+ text = text.gsub(/<\/([A-Z]+)>/, function(match) {
821
+ return '</' + match[1].toLowerCase() + '>';
822
+ });
823
+
824
+ text = text.replace(/<br>/g, "<br />");
825
+
826
+ return text;
827
+ }
828
+
829
+ return {
830
+ formatHTMLOutput: formatHTMLOutput,
831
+ formatHTMLInput: formatHTMLInput,
832
+ tidyXHTML: tidyXHTML
833
+ };
834
+ })());
835
+ Object.extend(String.prototype, {
836
+ sanitize: function(options) {
837
+ return Element("div").update(this).sanitize(options).innerHTML.tidyXHTML();
838
+ }
839
+ });
840
+
841
+ Element.addMethods({
842
+ sanitize: function(element, options) {
843
+ element = $(element);
844
+ options = $H(options);
845
+ var allowed_tags = $A(options.get('tags') || []);
846
+ var allowed_attributes = $A(options.get('attributes') || []);
847
+ var sanitized = Element(element.nodeName);
848
+
849
+ $A(element.childNodes).each(function(child) {
850
+ if (child.nodeType == 1) {
851
+ var children = $(child).sanitize(options).childNodes;
852
+
853
+ if (allowed_tags.include(child.nodeName.toLowerCase())) {
854
+ var new_child = Element(child.nodeName);
855
+ allowed_attributes.each(function(attribute) {
856
+ if ((value = child.readAttribute(attribute)))
857
+ new_child.writeAttribute(attribute, value);
858
+ });
859
+ sanitized.appendChild(new_child);
860
+
861
+ $A(children).each(function(grandchild) { new_child.appendChild(grandchild); });
862
+ } else {
863
+ $A(children).each(function(grandchild) { sanitized.appendChild(grandchild); });
864
+ }
865
+ } else if (child.nodeType == 3) {
866
+ sanitized.appendChild(child);
867
+ }
868
+ });
869
+ return sanitized;
870
+ }
871
+ });
872
+
873
+
874
+ if (typeof Range == 'undefined') {
875
+ Range = function(ownerDocument) {
876
+ this.ownerDocument = ownerDocument;
877
+
878
+ this.startContainer = this.ownerDocument.documentElement;
879
+ this.startOffset = 0;
880
+ this.endContainer = this.ownerDocument.documentElement;
881
+ this.endOffset = 0;
882
+
883
+ this.collapsed = true;
884
+ this.commonAncestorContainer = this._commonAncestorContainer(this.startContainer, this.endContainer);
885
+
886
+ this.detached = false;
887
+
888
+ this.START_TO_START = 0;
889
+ this.START_TO_END = 1;
890
+ this.END_TO_END = 2;
891
+ this.END_TO_START = 3;
892
+ }
893
+
894
+ Range.CLONE_CONTENTS = 0;
895
+ Range.DELETE_CONTENTS = 1;
896
+ Range.EXTRACT_CONTENTS = 2;
897
+
898
+ if (!document.createRange) {
899
+ document.createRange = function() {
900
+ return new Range(this);
901
+ };
902
+ }
903
+
904
+ Object.extend(Range.prototype, (function() {
905
+ function cloneContents() {
906
+ return _processContents(this, Range.CLONE_CONTENTS);
907
+ }
908
+
909
+ function cloneRange() {
910
+ try {
911
+ var clone = new Range(this.ownerDocument);
912
+ clone.startContainer = this.startContainer;
913
+ clone.startOffset = this.startOffset;
914
+ clone.endContainer = this.endContainer;
915
+ clone.endOffset = this.endOffset;
916
+ clone.collapsed = this.collapsed;
917
+ clone.commonAncestorContainer = this.commonAncestorContainer;
918
+ clone.detached = this.detached;
919
+
920
+ return clone;
921
+
922
+ } catch (e) {
923
+ return null;
924
+ };
925
+ }
926
+
927
+ function collapse(toStart) {
928
+ if (toStart) {
929
+ this.endContainer = this.startContainer;
930
+ this.endOffset = this.startOffset;
931
+ this.collapsed = true;
932
+ } else {
933
+ this.startContainer = this.endContainer;
934
+ this.startOffset = this.endOffset;
935
+ this.collapsed = true;
936
+ }
937
+ }
938
+
939
+ function compareBoundaryPoints(compareHow, sourceRange) {
940
+ try {
941
+ var cmnSelf, cmnSource, rootSelf, rootSource;
942
+
943
+ cmnSelf = this.commonAncestorContainer;
944
+ cmnSource = sourceRange.commonAncestorContainer;
945
+
946
+ rootSelf = cmnSelf;
947
+ while (rootSelf.parentNode) {
948
+ rootSelf = rootSelf.parentNode;
949
+ }
950
+
951
+ rootSource = cmnSource;
952
+ while (rootSource.parentNode) {
953
+ rootSource = rootSource.parentNode;
954
+ }
955
+
956
+ switch (compareHow) {
957
+ case this.START_TO_START:
958
+ return _compareBoundaryPoints(this, this.startContainer, this.startOffset, sourceRange.startContainer, sourceRange.startOffset);
959
+ break;
960
+ case this.START_TO_END:
961
+ return _compareBoundaryPoints(this, this.startContainer, this.startOffset, sourceRange.endContainer, sourceRange.endOffset);
962
+ break;
963
+ case this.END_TO_END:
964
+ return _compareBoundaryPoints(this, this.endContainer, this.endOffset, sourceRange.endContainer, sourceRange.endOffset);
965
+ break;
966
+ case this.END_TO_START:
967
+ return _compareBoundaryPoints(this, this.endContainer, this.endOffset, sourceRange.startContainer, sourceRange.startOffset);
968
+ break;
969
+ }
970
+ } catch (e) {};
971
+
972
+ return null;
973
+ }
974
+
975
+ function deleteContents() {
976
+ try {
977
+ _processContents(this, Range.DELETE_CONTENTS);
978
+ } catch (e) {}
979
+ }
980
+
981
+ function detach() {
982
+ this.detached = true;
983
+ }
984
+
985
+ function extractContents() {
986
+ try {
987
+ return _processContents(this, Range.EXTRACT_CONTENTS);
988
+ } catch (e) {
989
+ return null;
990
+ };
991
+ }
992
+
993
+ function insertNode(newNode) {
994
+ try {
995
+ var n, newText, offset;
996
+
997
+ switch (this.startContainer.nodeType) {
998
+ case Node.CDATA_SECTION_NODE:
999
+ case Node.TEXT_NODE:
1000
+ newText = this.startContainer.splitText(this.startOffset);
1001
+ this.startContainer.parentNode.insertBefore(newNode, newText);
1002
+ break;
1003
+ default:
1004
+ if (this.startContainer.childNodes.length == 0) {
1005
+ offset = null;
1006
+ } else {
1007
+ offset = this.startContainer.childNodes(this.startOffset);
1008
+ }
1009
+ this.startContainer.insertBefore(newNode, offset);
1010
+ }
1011
+ } catch (e) {}
1012
+ }
1013
+
1014
+ function selectNode(refNode) {
1015
+ this.setStartBefore(refNode);
1016
+ this.setEndAfter(refNode);
1017
+ }
1018
+
1019
+ function selectNodeContents(refNode) {
1020
+ this.setStart(refNode, 0);
1021
+ this.setEnd(refNode, refNode.childNodes.length);
1022
+ }
1023
+
1024
+ function setStart(refNode, offset) {
1025
+ try {
1026
+ var endRootContainer, startRootContainer;
1027
+
1028
+ this.startContainer = refNode;
1029
+ this.startOffset = offset;
1030
+
1031
+ endRootContainer = this.endContainer;
1032
+ while (endRootContainer.parentNode) {
1033
+ endRootContainer = endRootContainer.parentNode;
1034
+ }
1035
+ startRootContainer = this.startContainer;
1036
+ while (startRootContainer.parentNode) {
1037
+ startRootContainer = startRootContainer.parentNode;
1038
+ }
1039
+ if (startRootContainer != endRootContainer) {
1040
+ this.collapse(true);
1041
+ } else {
1042
+ if (_compareBoundaryPoints(this, this.startContainer, this.startOffset, this.endContainer, this.endOffset) > 0) {
1043
+ this.collapse(true);
1044
+ }
1045
+ }
1046
+
1047
+ this.collapsed = _isCollapsed(this);
1048
+
1049
+ this.commonAncestorContainer = _commonAncestorContainer(this.startContainer, this.endContainer);
1050
+ } catch (e) {}
1051
+ }
1052
+
1053
+ function setStartAfter(refNode) {
1054
+ this.setStart(refNode.parentNode, _nodeIndex(refNode) + 1);
1055
+ }
1056
+
1057
+ function setStartBefore(refNode) {
1058
+ this.setStart(refNode.parentNode, _nodeIndex(refNode));
1059
+ }
1060
+
1061
+ function setEnd(refNode, offset) {
1062
+ try {
1063
+ this.endContainer = refNode;
1064
+ this.endOffset = offset;
1065
+
1066
+ endRootContainer = this.endContainer;
1067
+ while (endRootContainer.parentNode) {
1068
+ endRootContainer = endRootContainer.parentNode;
1069
+ }
1070
+ startRootContainer = this.startContainer;
1071
+ while (startRootContainer.parentNode) {
1072
+ startRootContainer = startRootContainer.parentNode;
1073
+ }
1074
+ if (startRootContainer != endRootContainer) {
1075
+ this.collapse(false);
1076
+ } else {
1077
+ if (_compareBoundaryPoints(this, this.startContainer, this.startOffset, this.endContainer, this.endOffset) > 0) {
1078
+ this.collapse(false);
1079
+ }
1080
+ }
1081
+
1082
+ this.collapsed = _isCollapsed(this);
1083
+
1084
+ this.commonAncestorContainer = _commonAncestorContainer(this.startContainer, this.endContainer);
1085
+
1086
+ } catch (e) {}
1087
+ }
1088
+
1089
+ function setEndAfter(refNode) {
1090
+ this.setEnd(refNode.parentNode, _nodeIndex(refNode) + 1);
1091
+ }
1092
+
1093
+ function setEndBefore(refNode) {
1094
+ this.setEnd(refNode.parentNode, _nodeIndex(refNode));
1095
+ }
1096
+
1097
+ function surroundContents(newParent) {
1098
+ try {
1099
+ var n, fragment;
1100
+
1101
+ while (newParent.firstChild) {
1102
+ newParent.removeChild(newParent.firstChild);
1103
+ }
1104
+
1105
+ fragment = this.extractContents();
1106
+ this.insertNode(newParent);
1107
+ newParent.appendChild(fragment);
1108
+ this.selectNode(newParent);
1109
+ } catch (e) {}
1110
+ }
1111
+
1112
+ function _compareBoundaryPoints(range, containerA, offsetA, containerB, offsetB) {
1113
+ var c, offsetC, n, cmnRoot, childA;
1114
+ if (containerA == containerB) {
1115
+ if (offsetA == offsetB) {
1116
+ return 0; // equal
1117
+ } else if (offsetA < offsetB) {
1118
+ return -1; // before
1119
+ } else {
1120
+ return 1; // after
1121
+ }
1122
+ }
1123
+
1124
+ c = containerB;
1125
+ while (c && c.parentNode != containerA) {
1126
+ c = c.parentNode;
1127
+ }
1128
+ if (c) {
1129
+ offsetC = 0;
1130
+ n = containerA.firstChild;
1131
+ while (n != c && offsetC < offsetA) {
1132
+ offsetC++;
1133
+ n = n.nextSibling;
1134
+ }
1135
+ if (offsetA <= offsetC) {
1136
+ return -1; // before
1137
+ } else {
1138
+ return 1; // after
1139
+ }
1140
+ }
1141
+
1142
+ c = containerA;
1143
+ while (c && c.parentNode != containerB) {
1144
+ c = c.parentNode;
1145
+ }
1146
+ if (c) {
1147
+ offsetC = 0;
1148
+ n = containerB.firstChild;
1149
+ while (n != c && offsetC < offsetB) {
1150
+ offsetC++;
1151
+ n = n.nextSibling;
1152
+ }
1153
+ if (offsetC < offsetB) {
1154
+ return -1; // before
1155
+ } else {
1156
+ return 1; // after
1157
+ }
1158
+ }
1159
+
1160
+ cmnRoot = range._commonAncestorContainer(containerA, containerB);
1161
+ childA = containerA;
1162
+ while (childA && childA.parentNode != cmnRoot) {
1163
+ childA = childA.parentNode;
1164
+ }
1165
+ if (!childA) {
1166
+ childA = cmnRoot;
1167
+ }
1168
+ childB = containerB;
1169
+ while (childB && childB.parentNode != cmnRoot) {
1170
+ childB = childB.parentNode;
1171
+ }
1172
+ if (!childB) {
1173
+ childB = cmnRoot;
1174
+ }
1175
+
1176
+ if (childA == childB) {
1177
+ return 0; // equal
1178
+ }
1179
+
1180
+ n = cmnRoot.firstChild;
1181
+ while (n) {
1182
+ if (n == childA) {
1183
+ return -1; // before
1184
+ }
1185
+ if (n == childB) {
1186
+ return 1; // after
1187
+ }
1188
+ n = n.nextSibling;
1189
+ }
1190
+
1191
+ return null;
1192
+ }
1193
+
1194
+ function _commonAncestorContainer(containerA, containerB) {
1195
+ var parentStart = containerA, parentEnd;
1196
+ while (parentStart) {
1197
+ parentEnd = containerB;
1198
+ while (parentEnd && parentStart != parentEnd) {
1199
+ parentEnd = parentEnd.parentNode;
1200
+ }
1201
+ if (parentStart == parentEnd) {
1202
+ break;
1203
+ }
1204
+ parentStart = parentStart.parentNode;
1205
+ }
1206
+
1207
+ if (!parentStart && containerA.ownerDocument) {
1208
+ return containerA.ownerDocument.documentElement;
1209
+ }
1210
+
1211
+ return parentStart;
1212
+ }
1213
+
1214
+ function _isCollapsed(range) {
1215
+ return (range.startContainer == range.endContainer && range.startOffset == range.endOffset);
1216
+ }
1217
+
1218
+ function _offsetInCharacters(node) {
1219
+ switch (node.nodeType) {
1220
+ case Node.CDATA_SECTION_NODE:
1221
+ case Node.COMMENT_NODE:
1222
+ case Node.ELEMENT_NODE:
1223
+ case Node.PROCESSING_INSTRUCTION_NODE:
1224
+ return true;
1225
+ default:
1226
+ return false;
1227
+ }
1228
+ }
1229
+
1230
+ function _processContents(range, action) {
1231
+ try {
1232
+
1233
+ var cmnRoot, partialStart = null, partialEnd = null, fragment, n, c, i;
1234
+ var leftContents, leftParent, leftContentsParent;
1235
+ var rightContents, rightParent, rightContentsParent;
1236
+ var next, prev;
1237
+ var processStart, processEnd;
1238
+ if (range.collapsed) {
1239
+ return null;
1240
+ }
1241
+
1242
+ cmnRoot = range.commonAncestorContainer;
1243
+
1244
+ if (range.startContainer != cmnRoot) {
1245
+ partialStart = range.startContainer;
1246
+ while (partialStart.parentNode != cmnRoot) {
1247
+ partialStart = partialStart.parentNode;
1248
+ }
1249
+ }
1250
+
1251
+ if (range.endContainer != cmnRoot) {
1252
+ partialEnd = range.endContainer;
1253
+ while (partialEnd.parentNode != cmnRoot) {
1254
+ partialEnd = partialEnd.parentNode;
1255
+ }
1256
+ }
1257
+
1258
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1259
+ fragment = range.ownerDocument.createDocumentFragment();
1260
+ }
1261
+
1262
+ if (range.startContainer == range.endContainer) {
1263
+ switch (range.startContainer.nodeType) {
1264
+ case Node.CDATA_SECTION_NODE:
1265
+ case Node.COMMENT_NODE:
1266
+ case Node.TEXT_NODE:
1267
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1268
+ c = range.startContainer.cloneNode();
1269
+ c.deleteData(range.endOffset, range.startContainer.data.length - range.endOffset);
1270
+ c.deleteData(0, range.startOffset);
1271
+ fragment.appendChild(c);
1272
+ }
1273
+ if (action == Range.EXTRACT_CONTENTS || action == Range.DELETE_CONTENTS) {
1274
+ range.startContainer.deleteData(range.startOffset, range.endOffset - range.startOffset);
1275
+ }
1276
+ break;
1277
+ case Node.PROCESSING_INSTRUCTION_NODE:
1278
+ break;
1279
+ default:
1280
+ n = range.startContainer.firstChild;
1281
+ for (i = 0; i < range.startOffset; i++) {
1282
+ n = n.nextSibling;
1283
+ }
1284
+ while (n && i < range.endOffset) {
1285
+ next = n.nextSibling;
1286
+ if (action == Range.EXTRACT_CONTENTS) {
1287
+ fragment.appendChild(n);
1288
+ } else if (action == Range.CLONE_CONTENTS) {
1289
+ fragment.appendChild(n.cloneNode());
1290
+ } else {
1291
+ range.startContainer.removeChild(n);
1292
+ }
1293
+ n = next;
1294
+ i++;
1295
+ }
1296
+ }
1297
+ range.collapse(true);
1298
+ return fragment;
1299
+ }
1300
+
1301
+
1302
+ if (range.startContainer != cmnRoot) {
1303
+ switch (range.startContainer.nodeType) {
1304
+ case Node.CDATA_SECTION_NODE:
1305
+ case Node.COMMENT_NODE:
1306
+ case Node.TEXT_NODE:
1307
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1308
+ c = range.startContainer.cloneNode(true);
1309
+ c.deleteData(0, range.startOffset);
1310
+ leftContents = c;
1311
+ }
1312
+ if (action == Range.EXTRACT_CONTENTS || action == Range.DELETE_CONTENTS) {
1313
+ range.startContainer.deleteData(range.startOffset, range.startContainer.data.length - range.startOffset);
1314
+ }
1315
+ break;
1316
+ case Node.PROCESSING_INSTRUCTION_NODE:
1317
+ break;
1318
+ default:
1319
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1320
+ leftContents = range.startContainer.cloneNode(false);
1321
+ }
1322
+ n = range.startContainer.firstChild;
1323
+ for (i = 0; i < range.startOffset; i++) {
1324
+ n = n.nextSibling;
1325
+ }
1326
+ while (n && i < range.endOffset) {
1327
+ next = n.nextSibling;
1328
+ if (action == Range.EXTRACT_CONTENTS) {
1329
+ fragment.appendChild(n);
1330
+ } else if (action == Range.CLONE_CONTENTS) {
1331
+ fragment.appendChild(n.cloneNode());
1332
+ } else {
1333
+ range.startContainer.removeChild(n);
1334
+ }
1335
+ n = next;
1336
+ i++;
1337
+ }
1338
+ }
1339
+
1340
+ leftParent = range.startContainer.parentNode;
1341
+ n = range.startContainer.nextSibling;
1342
+ for(; leftParent != cmnRoot; leftParent = leftParent.parentNode) {
1343
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1344
+ leftContentsParent = leftParent.cloneNode(false);
1345
+ leftContentsParent.appendChild(leftContents);
1346
+ leftContents = leftContentsParent;
1347
+ }
1348
+
1349
+ for (; n; n = next) {
1350
+ next = n.nextSibling;
1351
+ if (action == Range.EXTRACT_CONTENTS) {
1352
+ leftContents.appendChild(n);
1353
+ } else if (action == Range.CLONE_CONTENTS) {
1354
+ leftContents.appendChild(n.cloneNode(true));
1355
+ } else {
1356
+ leftParent.removeChild(n);
1357
+ }
1358
+ }
1359
+ n = leftParent.nextSibling;
1360
+ }
1361
+ }
1362
+
1363
+ if (range.endContainer != cmnRoot) {
1364
+ switch (range.endContainer.nodeType) {
1365
+ case Node.CDATA_SECTION_NODE:
1366
+ case Node.COMMENT_NODE:
1367
+ case Node.TEXT_NODE:
1368
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1369
+ c = range.endContainer.cloneNode(true);
1370
+ c.deleteData(range.endOffset, range.endContainer.data.length - range.endOffset);
1371
+ rightContents = c;
1372
+ }
1373
+ if (action == Range.EXTRACT_CONTENTS || action == Range.DELETE_CONTENTS) {
1374
+ range.endContainer.deleteData(0, range.endOffset);
1375
+ }
1376
+ break;
1377
+ case Node.PROCESSING_INSTRUCTION_NODE:
1378
+ break;
1379
+ default:
1380
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1381
+ rightContents = range.endContainer.cloneNode(false);
1382
+ }
1383
+ n = range.endContainer.firstChild;
1384
+ if (n && range.endOffset) {
1385
+ for (i = 0; i+1 < range.endOffset; i++) {
1386
+ next = n.nextSibling;
1387
+ if (!next) {
1388
+ break;
1389
+ }
1390
+ n = next;
1391
+ }
1392
+ for (; n; n = prev) {
1393
+ prev = n.previousSibling;
1394
+ if (action == Range.EXTRACT_CONTENTS) {
1395
+ rightContents.insertBefore(n, rightContents.firstChild);
1396
+ } else if (action == Range.CLONE_CONTENTS) {
1397
+ rightContents.insertBefore(n.cloneNode(True), rightContents.firstChild);
1398
+ } else {
1399
+ range.endContainer.removeChild(n);
1400
+ }
1401
+ }
1402
+ }
1403
+ }
1404
+
1405
+ rightParent = range.endContainer.parentNode;
1406
+ n = range.endContainer.previousSibling;
1407
+ for(; rightParent != cmnRoot; rightParent = rightParent.parentNode) {
1408
+ if (action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) {
1409
+ rightContentsParent = rightContents.cloneNode(false);
1410
+ rightContentsParent.appendChild(rightContents);
1411
+ rightContents = rightContentsParent;
1412
+ }
1413
+
1414
+ for (; n; n = prev) {
1415
+ prev = n.previousSibling;
1416
+ if (action == Range.EXTRACT_CONTENTS) {
1417
+ rightContents.insertBefore(n, rightContents.firstChild);
1418
+ } else if (action == Range.CLONE_CONTENTS) {
1419
+ rightContents.appendChild(n.cloneNode(true), rightContents.firstChild);
1420
+ } else {
1421
+ rightParent.removeChild(n);
1422
+ }
1423
+ }
1424
+ n = rightParent.previousSibling;
1425
+ }
1426
+ }
1427
+
1428
+ if (range.startContainer == cmnRoot) {
1429
+ processStart = range.startContainer.firstChild;
1430
+ for (i = 0; i < range.startOffset; i++) {
1431
+ processStart = processStart.nextSibling;
1432
+ }
1433
+ } else {
1434
+ processStart = range.startContainer;
1435
+ while (processStart.parentNode != cmnRoot) {
1436
+ processStart = processStart.parentNode;
1437
+ }
1438
+ processStart = processStart.nextSibling;
1439
+ }
1440
+ if (range.endContainer == cmnRoot) {
1441
+ processEnd = range.endContainer.firstChild;
1442
+ for (i = 0; i < range.endOffset; i++) {
1443
+ processEnd = processEnd.nextSibling;
1444
+ }
1445
+ } else {
1446
+ processEnd = range.endContainer;
1447
+ while (processEnd.parentNode != cmnRoot) {
1448
+ processEnd = processEnd.parentNode;
1449
+ }
1450
+ }
1451
+
1452
+ if ((action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) && leftContents) {
1453
+ fragment.appendChild(leftContents);
1454
+ }
1455
+
1456
+ if (processStart) {
1457
+ for (n = processStart; n && n != processEnd; n = next) {
1458
+ next = n.nextSibling;
1459
+ if (action == Range.EXTRACT_CONTENTS) {
1460
+ fragment.appendChild(n);
1461
+ } else if (action == Range.CLONE_CONTENTS) {
1462
+ fragment.appendChild(n.cloneNode(true));
1463
+ } else {
1464
+ cmnRoot.removeChild(n);
1465
+ }
1466
+ }
1467
+ }
1468
+
1469
+ if ((action == Range.EXTRACT_CONTENTS || action == Range.CLONE_CONTENTS) && rightContents) {
1470
+ fragment.appendChild(rightContents);
1471
+ }
1472
+
1473
+ if (action == Range.EXTRACT_CONTENTS || action == Range.DELETE_CONTENTS) {
1474
+ if (!partialStart && !partialEnd) {
1475
+ range.collapse(true);
1476
+ } else if (partialStart) {
1477
+ range.startContainer = partialStart.parentNode;
1478
+ range.endContainer = partialStart.parentNode;
1479
+ range.startOffset = range.endOffset = range._nodeIndex(partialStart) + 1;
1480
+ } else if (partialEnd) {
1481
+ range.startContainer = partialEnd.parentNode;
1482
+ range.endContainer = partialEnd.parentNode;
1483
+ range.startOffset = range.endOffset = range._nodeIndex(partialEnd);
1484
+ }
1485
+ }
1486
+
1487
+ return fragment;
1488
+
1489
+ } catch (e) {
1490
+ return null;
1491
+ };
1492
+ }
1493
+
1494
+ function _nodeIndex(refNode) {
1495
+ var nodeIndex = 0;
1496
+ while (refNode.previousSibling) {
1497
+ nodeIndex++;
1498
+ refNode = refNode.previousSibling;
1499
+ }
1500
+ return nodeIndex;
1501
+ }
1502
+
1503
+ return {
1504
+ setStart: setStart,
1505
+ setEnd: setEnd,
1506
+ setStartBefore: setStartBefore,
1507
+ setStartAfter: setStartAfter,
1508
+ setEndBefore: setEndBefore,
1509
+ setEndAfter: setEndAfter,
1510
+
1511
+ collapse: collapse,
1512
+
1513
+ selectNode: selectNode,
1514
+ selectNodeContents: selectNodeContents,
1515
+
1516
+ compareBoundaryPoints: compareBoundaryPoints,
1517
+
1518
+ deleteContents: deleteContents,
1519
+ extractContents: extractContents,
1520
+ cloneContents: cloneContents,
1521
+
1522
+ insertNode: insertNode,
1523
+ surroundContents: surroundContents,
1524
+
1525
+ cloneRange: cloneRange,
1526
+ toString: toString,
1527
+ detach: detach,
1528
+
1529
+ _commonAncestorContainer: _commonAncestorContainer
1530
+ };
1531
+ })());
1532
+ }
1533
+
1534
+ if (!window.getSelection) {
1535
+ window.getSelection = function() {
1536
+ return Selection.getInstance();
1537
+ };
1538
+
1539
+ SelectionImpl = function() {
1540
+ this.anchorNode = null;
1541
+ this.anchorOffset = 0;
1542
+ this.focusNode = null;
1543
+ this.focusOffset = 0;
1544
+ this.isCollapsed = true;
1545
+ this.rangeCount = 0;
1546
+ this.ranges = [];
1547
+ }
1548
+
1549
+ Object.extend(SelectionImpl.prototype, (function() {
1550
+ function addRange(r) {
1551
+ return true;
1552
+ }
1553
+
1554
+ function collapse() {
1555
+ return true;
1556
+ }
1557
+
1558
+ function collapseToStart() {
1559
+ return true;
1560
+ }
1561
+
1562
+ function collapseToEnd() {
1563
+ return true;
1564
+ }
1565
+
1566
+ function getRangeAt() {
1567
+ return true;
1568
+ }
1569
+
1570
+ function removeAllRanges() {
1571
+ this.anchorNode = null;
1572
+ this.anchorOffset = 0;
1573
+ this.focusNode = null;
1574
+ this.focusOffset = 0;
1575
+ this.isCollapsed = true;
1576
+ this.rangeCount = 0;
1577
+ this.ranges = [];
1578
+ }
1579
+
1580
+ function _addRange(r) {
1581
+ if (r.startContainer.nodeType != Node.TEXT_NODE) {
1582
+ var start = this._getRightStart(r.startContainer);
1583
+ var startOffset = 0;
1584
+ } else {
1585
+ var start = r.startContainer;
1586
+ var startOffset = r.startOffset;
1587
+ }
1588
+ if (r.endContainer.nodeType != Node.TEXT_NODE) {
1589
+ var end = this._getRightEnd(r.endContainer);
1590
+ var endOffset = end.data.length;
1591
+ } else {
1592
+ var end = r.endContainer;
1593
+ var endOffset = r.endOffset;
1594
+ }
1595
+
1596
+ var rStart = this._selectStart(start, startOffset);
1597
+ var rEnd = this._selectEnd(end,endOffset);
1598
+ rStart.setEndPoint('EndToStart', rEnd);
1599
+ rStart.select();
1600
+ document.selection._selectedRange = r;
1601
+ }
1602
+
1603
+ function _getRightStart(start, offset) {
1604
+ if (start.nodeType != Node.TEXT_NODE) {
1605
+ if (start.nodeType == Node.ELEMENT_NODE) {
1606
+ start = start.childNodes(offset);
1607
+ }
1608
+ return getNextTextNode(start);
1609
+ } else {
1610
+ return null;
1611
+ }
1612
+ }
1613
+
1614
+ function _getRightEnd(end, offset) {
1615
+ if (end.nodeType != Node.TEXT_NODE) {
1616
+ if (end.nodeType == Node.ELEMENT_NODE) {
1617
+ end = end.childNodes(offset);
1618
+ }
1619
+ return getPreviousTextNode(end);
1620
+ } else {
1621
+ return null;
1622
+ }
1623
+ }
1624
+
1625
+ function _selectStart(start, offset) {
1626
+ var r = document.body.createTextRange();
1627
+ if (start.nodeType == Node.TEXT_NODE) {
1628
+ var moveCharacters = offset, node = start;
1629
+ var moveToNode = null, collapse = true;
1630
+ while (node.previousSibling) {
1631
+ switch (node.previousSibling.nodeType) {
1632
+ case Node.ELEMENT_NODE:
1633
+ moveToNode = node.previousSibling;
1634
+ collapse = false;
1635
+ break;
1636
+ case Node.TEXT_NODE:
1637
+ moveCharacters += node.previousSibling.data.length;
1638
+ }
1639
+ if (moveToNode != null) {
1640
+ break;
1641
+ }
1642
+ node = node.previousSibling;
1643
+ }
1644
+ if (moveToNode == null) {
1645
+ moveToNode = start.parentNode;
1646
+ }
1647
+
1648
+ r.moveToElementText(moveToNode);
1649
+ r.collapse(collapse);
1650
+ r.move('Character', moveCharacters);
1651
+ return r;
1652
+ } else {
1653
+ return null;
1654
+ }
1655
+ }
1656
+
1657
+ function _selectEnd(end, offset) {
1658
+ var r = document.body.createTextRange(), node = end;
1659
+ if (end.nodeType == 3) {
1660
+ var moveCharacters = end.data.length - offset;
1661
+ var moveToNode = null, collapse = false;
1662
+ while (node.nextSibling) {
1663
+ switch (node.nextSibling.nodeType) {
1664
+ case Node.ELEMENT_NODE:
1665
+ moveToNode = node.nextSibling;
1666
+ collapse = true;
1667
+ break;
1668
+ case Node.TEXT_NODE:
1669
+ moveCharacters += node.nextSibling.data.length;
1670
+ break;
1671
+ }
1672
+ if (moveToNode != null) {
1673
+ break;
1674
+ }
1675
+ node = node.nextSibling;
1676
+ }
1677
+ if (moveToNode == null) {
1678
+ moveToNode = end.parentNode;
1679
+ collapse = false;
1680
+ }
1681
+
1682
+ switch (moveToNode.nodeName.toLowerCase()) {
1683
+ case 'p':
1684
+ case 'div':
1685
+ case 'h1':
1686
+ case 'h2':
1687
+ case 'h3':
1688
+ case 'h4':
1689
+ case 'h5':
1690
+ case 'h6':
1691
+ moveCharacters++;
1692
+ }
1693
+
1694
+ r.moveToElementText(moveToNode);
1695
+ r.collapse(collapse);
1696
+
1697
+ r.move('Character', -moveCharacters);
1698
+ return r;
1699
+ }
1700
+
1701
+ return null;
1702
+ }
1703
+
1704
+ function getPreviousTextNode(node) {
1705
+ var stack = [];
1706
+ var current = null;
1707
+ while (node) {
1708
+ stack = [];
1709
+ current = node;
1710
+ while (current) {
1711
+ while (current) {
1712
+ if (current.nodeType == 3 && current.data.replace(/^\s+|\s+$/, '').length) {
1713
+ return current;
1714
+ }
1715
+ if (current.previousSibling) {
1716
+ stack.push (current.previousSibling);
1717
+ }
1718
+ current = current.lastChild;
1719
+ }
1720
+ current = stack.pop();
1721
+ }
1722
+ node = node.previousSibling;
1723
+ }
1724
+ return null;
1725
+ }
1726
+
1727
+ function getNextTextNode(node) {
1728
+ var stack = [];
1729
+ var current = null;
1730
+ while (node) {
1731
+ stack = [];
1732
+ current = node;
1733
+ while (current) {
1734
+ while (current) {
1735
+ if (current.nodeType == 3 && current.data.replace(/^\s+|\s+$/, '').length) {
1736
+ return current;
1737
+ }
1738
+ if (current.nextSibling) {
1739
+ stack.push (current.nextSibling);
1740
+ }
1741
+ current = current.firstChild;
1742
+ }
1743
+ current = stack.pop();
1744
+ }
1745
+ node = node.nextSibling;
1746
+ }
1747
+ return null;
1748
+ }
1749
+
1750
+ return {
1751
+ removeAllRanges: removeAllRanges,
1752
+
1753
+ _addRange: _addRange,
1754
+ _getRightStart: _getRightStart,
1755
+ _getRightEnd: _getRightEnd,
1756
+ _selectStart: _selectStart,
1757
+ _selectEnd: _selectEnd
1758
+ };
1759
+ })());
1760
+
1761
+ Selection = new function() {
1762
+ var instance = null;
1763
+ this.getInstance = function() {
1764
+ if (instance == null) {
1765
+ return (instance = new SelectionImpl());
1766
+ } else {
1767
+ return instance;
1768
+ }
1769
+ };
1770
+ };
1771
+ }
1772
+
1773
+ Object.extend(Range.prototype, (function() {
1774
+ function getNode() {
1775
+ var node = this.commonAncestorContainer;
1776
+
1777
+ if (this.startContainer == this.endContainer)
1778
+ if (this.startOffset - this.endOffset < 2)
1779
+ node = this.startContainer.childNodes[this.startOffset];
1780
+
1781
+ while (node.nodeType == Node.TEXT_NODE)
1782
+ node = node.parentNode;
1783
+
1784
+ return node;
1785
+ }
1786
+
1787
+ return {
1788
+ getNode: getNode
1789
+ };
1790
+ })());
1791
+ WysiHat.Selection = Class.create((function() {
1792
+ function initialize(editor) {
1793
+ this.window = editor.getWindow();
1794
+ this.document = editor.getDocument();
1795
+
1796
+ if (Prototype.Browser.IE) {
1797
+ editor.observe('wysihat:cursormove', saveRange.bind(this));
1798
+ editor.observe('wysihat:focus', restoreRange);
1799
+ }
1800
+ }
1801
+
1802
+ function getSelection() {
1803
+ return this.window.getSelection ? this.window.getSelection() : this.document.selection;
1804
+ }
1805
+
1806
+ function getRange() {
1807
+ var range = null, selection = this.getSelection();
1808
+
1809
+ try {
1810
+ if (selection.getRangeAt)
1811
+ range = selection.getRangeAt(0);
1812
+ else
1813
+ range = selection.createRange();
1814
+ } catch(e) { return null; }
1815
+
1816
+ if (Prototype.Browser.WebKit) {
1817
+ range.setStart(selection.baseNode, selection.baseOffset);
1818
+ range.setEnd(selection.extentNode, selection.extentOffset);
1819
+ }
1820
+
1821
+ return range;
1822
+ }
1823
+
1824
+ function selectNode(node) {
1825
+ var selection = this.getSelection();
1826
+
1827
+ if (Prototype.Browser.IE) {
1828
+ var range = createRangeFromElement(this.document, node);
1829
+ range.select();
1830
+ } else if (Prototype.Browser.WebKit) {
1831
+ selection.setBaseAndExtent(node, 0, node, node.innerText.length);
1832
+ } else if (Prototype.Browser.Opera) {
1833
+ range = this.document.createRange();
1834
+ range.selectNode(node);
1835
+ selection.removeAllRanges();
1836
+ selection.addRange(range);
1837
+ } else {
1838
+ var range = createRangeFromElement(this.document, node);
1839
+ selection.removeAllRanges();
1840
+ selection.addRange(range);
1841
+ }
1842
+ }
1843
+
1844
+ function getNode() {
1845
+ var nodes = null, candidates = [], children, el;
1846
+ var range = this.getRange();
1847
+
1848
+ if (!range)
1849
+ return null;
1850
+
1851
+ var parent;
1852
+ if (range.parentElement)
1853
+ parent = range.parentElement();
1854
+ else
1855
+ parent = range.commonAncestorContainer;
1856
+
1857
+ if (parent) {
1858
+ while (parent.nodeType != 1) parent = parent.parentNode;
1859
+ if (parent.nodeName.toLowerCase() != "body") {
1860
+ el = parent;
1861
+ do {
1862
+ el = el.parentNode;
1863
+ candidates[candidates.length] = el;
1864
+ } while (el.nodeName.toLowerCase() != "body");
1865
+ }
1866
+ children = parent.all || parent.getElementsByTagName("*");
1867
+ for (var j = 0; j < children.length; j++)
1868
+ candidates[candidates.length] = children[j];
1869
+ nodes = [parent];
1870
+ for (var ii = 0, r2; ii < candidates.length; ii++) {
1871
+ r2 = createRangeFromElement(this.document, candidates[ii]);
1872
+ if (r2 && compareRanges(range, r2))
1873
+ nodes[nodes.length] = candidates[ii];
1874
+ }
1875
+ }
1876
+
1877
+ return nodes.first();
1878
+ }
1879
+
1880
+ function createRangeFromElement(document, node) {
1881
+ if (document.body.createTextRange) {
1882
+ var range = document.body.createTextRange();
1883
+ range.moveToElementText(node);
1884
+ } else if (document.createRange) {
1885
+ var range = document.createRange();
1886
+ range.selectNodeContents(node);
1887
+ }
1888
+ return range;
1889
+ }
1890
+
1891
+ function compareRanges(r1, r2) {
1892
+ if (r1.compareEndPoints) {
1893
+ return !(
1894
+ r2.compareEndPoints('StartToStart', r1) == 1 &&
1895
+ r2.compareEndPoints('EndToEnd', r1) == 1 &&
1896
+ r2.compareEndPoints('StartToEnd', r1) == 1 &&
1897
+ r2.compareEndPoints('EndToStart', r1) == 1
1898
+ ||
1899
+ r2.compareEndPoints('StartToStart', r1) == -1 &&
1900
+ r2.compareEndPoints('EndToEnd', r1) == -1 &&
1901
+ r2.compareEndPoints('StartToEnd', r1) == -1 &&
1902
+ r2.compareEndPoints('EndToStart', r1) == -1
1903
+ );
1904
+ } else if (r1.compareBoundaryPoints) {
1905
+ return !(
1906
+ r2.compareBoundaryPoints(0, r1) == 1 &&
1907
+ r2.compareBoundaryPoints(2, r1) == 1 &&
1908
+ r2.compareBoundaryPoints(1, r1) == 1 &&
1909
+ r2.compareBoundaryPoints(3, r1) == 1
1910
+ ||
1911
+ r2.compareBoundaryPoints(0, r1) == -1 &&
1912
+ r2.compareBoundaryPoints(2, r1) == -1 &&
1913
+ r2.compareBoundaryPoints(1, r1) == -1 &&
1914
+ r2.compareBoundaryPoints(3, r1) == -1
1915
+ );
1916
+ }
1917
+
1918
+ return null;
1919
+ };
1920
+
1921
+ function setBookmark() {
1922
+ var bookmark = this.document.getElementById('bookmark');
1923
+ if (bookmark)
1924
+ bookmark.parentNode.removeChild(bookmark);
1925
+
1926
+ bookmark = this.document.createElement('span');
1927
+ bookmark.id = 'bookmark';
1928
+ bookmark.innerHTML = '&nbsp;';
1929
+
1930
+ if (Prototype.Browser.IE) {
1931
+ var range = this.document.selection.createRange();
1932
+ var parent = this.document.createElement('div');
1933
+ parent.appendChild(bookmark);
1934
+ range.collapse();
1935
+ range.pasteHTML(parent.innerHTML);
1936
+ }
1937
+ else {
1938
+ var range = this.getRange();
1939
+ range.insertNode(bookmark);
1940
+ }
1941
+ }
1942
+
1943
+ function moveToBookmark() {
1944
+ var bookmark = this.document.getElementById('bookmark');
1945
+ if (!bookmark)
1946
+ return;
1947
+
1948
+ if (Prototype.Browser.IE) {
1949
+ var range = this.getRange();
1950
+ range.moveToElementText(bookmark);
1951
+ range.collapse();
1952
+ range.select();
1953
+ } else if (Prototype.Browser.WebKit) {
1954
+ var selection = this.getSelection();
1955
+ selection.setBaseAndExtent(bookmark, 0, bookmark, 0);
1956
+ } else {
1957
+ var range = this.getRange();
1958
+ range.setStartBefore(bookmark);
1959
+ }
1960
+
1961
+ bookmark.parentNode.removeChild(bookmark);
1962
+ }
1963
+
1964
+ var savedRange = null;
1965
+ function saveRange() {
1966
+ savedRange = this.getRange();
1967
+ }
1968
+
1969
+ function restoreRange() {
1970
+ if (savedRange) savedRange.select();
1971
+ }
1972
+
1973
+ return {
1974
+ initialize: initialize,
1975
+ getSelection: getSelection,
1976
+ getRange: getRange,
1977
+ getNode: getNode,
1978
+ selectNode: selectNode,
1979
+ setBookmark: setBookmark,
1980
+ moveToBookmark: moveToBookmark,
1981
+ restore: restoreRange
1982
+ };
1983
+ })());
1984
+ WysiHat.Toolbar = Class.create((function() {
1985
+ function initialize(editor) {
1986
+ this.editor = editor;
1987
+ this.element = this.createToolbarElement();
1988
+ }
1989
+
1990
+ function createToolbarElement() {
1991
+ var toolbar = new Element('div', { 'class': 'editor_toolbar' });
1992
+ this.editor.insert({before: toolbar});
1993
+ return toolbar;
1994
+ }
1995
+
1996
+ function addButtonSet(set) {
1997
+ var toolbar = this;
1998
+ $A(set).each(function(button){
1999
+ toolbar.addButton(button);
2000
+ });
2001
+ }
2002
+
2003
+ function addButton(options, handler) {
2004
+ options = $H(options);
2005
+
2006
+ if (!options.get('name'))
2007
+ options.set('name', options.get('label').toLowerCase());
2008
+ var name = options.get('name');
2009
+
2010
+ var button = this.createButtonElement(this.element, options);
2011
+
2012
+ var handler = this.buttonHandler(name, options);
2013
+ this.observeButtonClick(button, handler);
2014
+
2015
+ var handler = this.buttonStateHandler(name, options);
2016
+ this.observeStateChanges(button, name, handler)
2017
+ }
2018
+
2019
+ function createButtonElement(toolbar, options) {
2020
+ var button = Element('a', {
2021
+ 'class': 'button', 'href': '#'
2022
+ });
2023
+ button.update('<span>' + options.get('label') + '</span>');
2024
+ button.addClassName(options.get('name'));
2025
+
2026
+ toolbar.appendChild(button);
2027
+
2028
+ return button;
2029
+ }
2030
+
2031
+ function buttonHandler(name, options) {
2032
+ if (options.handler)
2033
+ return options.handler;
2034
+ else if (options.get('handler'))
2035
+ return options.get('handler');
2036
+ else
2037
+ return function(editor) { editor.execCommand(name); };
2038
+ }
2039
+
2040
+ function observeButtonClick(element, handler) {
2041
+ var toolbar = this;
2042
+ element.observe('click', function(event) {
2043
+ handler(toolbar.editor);
2044
+ toolbar.editor.fire("wysihat:change");
2045
+ toolbar.editor.fire("wysihat:cursormove");
2046
+ Event.stop(event);
2047
+ });
2048
+ }
2049
+
2050
+ function buttonStateHandler(name, options) {
2051
+ if (options.query)
2052
+ return options.query;
2053
+ else if (options.get('query'))
2054
+ return options.get('query');
2055
+ else
2056
+ return function(editor) { return editor.queryCommandState(name); };
2057
+ }
2058
+
2059
+ function observeStateChanges(element, name, handler) {
2060
+ var toolbar = this;
2061
+ var previousState = false;
2062
+ toolbar.editor.observe("wysihat:cursormove", function(event) {
2063
+ var state = handler(toolbar.editor);
2064
+ if (state != previousState) {
2065
+ previousState = state;
2066
+ toolbar.updateButtonState(element, name, state);
2067
+ }
2068
+ });
2069
+ }
2070
+
2071
+ function updateButtonState(element, name, state) {
2072
+ if (state)
2073
+ element.addClassName('selected');
2074
+ else
2075
+ element.removeClassName('selected');
2076
+ }
2077
+
2078
+ return {
2079
+ initialize: initialize,
2080
+ createToolbarElement: createToolbarElement,
2081
+ addButtonSet: addButtonSet,
2082
+ addButton: addButton,
2083
+ createButtonElement: createButtonElement,
2084
+ buttonHandler: buttonHandler,
2085
+ observeButtonClick: observeButtonClick,
2086
+ buttonStateHandler: buttonStateHandler,
2087
+ observeStateChanges: observeStateChanges,
2088
+ updateButtonState: updateButtonState
2089
+ };
2090
+ })());
2091
+
2092
+ WysiHat.Toolbar.ButtonSets = {};
2093
+
2094
+ WysiHat.Toolbar.ButtonSets.Basic = $A([
2095
+ { label: "Bold" },
2096
+ { label: "Underline" },
2097
+ { label: "Italic" }
2098
+ ]);