wysihat-engine 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ]);