cobweb 0.0.38 → 0.0.39

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 (160) hide show
  1. data/README.textile +1 -1
  2. data/lib/cobweb.rb +3 -1
  3. data/lib/cobweb_crawler.rb +98 -100
  4. data/lib/crawl_job.rb +3 -4
  5. data/lib/server.rb +98 -0
  6. data/lib/stats.rb +141 -64
  7. data/public/css/accordion.css +45 -0
  8. data/public/css/custom.css +13 -0
  9. data/public/css/datatable.css +189 -0
  10. data/public/css/datepicker.css +171 -0
  11. data/public/css/form-buttons.css +180 -0
  12. data/public/css/forms.css +489 -0
  13. data/public/css/jquery.fancybox-1.3.4.css +282 -0
  14. data/public/css/jquery.treeview.css +103 -0
  15. data/public/css/link-buttons.css +187 -0
  16. data/public/css/login.css +110 -0
  17. data/public/css/menu.css +156 -0
  18. data/public/css/messages.css +58 -0
  19. data/public/css/modalbox.css +63 -0
  20. data/public/css/statics.css +57 -0
  21. data/public/css/style.css +497 -0
  22. data/public/css/style_text.css +128 -0
  23. data/public/css/tabs.css +58 -0
  24. data/public/css/wysiwyg-editor.css +18 -0
  25. data/public/css/wysiwyg.css +149 -0
  26. data/public/css/wysiwyg.modal.css +69 -0
  27. data/public/gfx/back-menu.gif +0 -0
  28. data/public/gfx/back-submenu.gif +0 -0
  29. data/public/gfx/background.gif +0 -0
  30. data/public/gfx/box-hide.png +0 -0
  31. data/public/gfx/box-search.png +0 -0
  32. data/public/gfx/box-title.gif +0 -0
  33. data/public/gfx/code.gif +0 -0
  34. data/public/gfx/datepicker-arrows.gif +0 -0
  35. data/public/gfx/fancybox/blank.gif +0 -0
  36. data/public/gfx/fancybox/fancy_close.png +0 -0
  37. data/public/gfx/fancybox/fancy_loading.png +0 -0
  38. data/public/gfx/fancybox/fancy_nav_left.png +0 -0
  39. data/public/gfx/fancybox/fancy_nav_right.png +0 -0
  40. data/public/gfx/fancybox/fancy_title_left.png +0 -0
  41. data/public/gfx/fancybox/fancy_title_main.png +0 -0
  42. data/public/gfx/fancybox/fancy_title_over.png +0 -0
  43. data/public/gfx/fancybox/fancy_title_right.png +0 -0
  44. data/public/gfx/fancybox/fancybox-x.png +0 -0
  45. data/public/gfx/fancybox/fancybox.png +0 -0
  46. data/public/gfx/forms/date-next.gif +0 -0
  47. data/public/gfx/forms/date-prev.gif +0 -0
  48. data/public/gfx/forms/forms-checkbox.gif +0 -0
  49. data/public/gfx/forms/forms-date.gif +0 -0
  50. data/public/gfx/forms/forms-file.gif +0 -0
  51. data/public/gfx/forms/forms-input-big.gif +0 -0
  52. data/public/gfx/forms/forms-input-medium.gif +0 -0
  53. data/public/gfx/forms/forms-input-small.gif +0 -0
  54. data/public/gfx/forms/forms-input-xl.gif +0 -0
  55. data/public/gfx/forms/forms-radio.gif +0 -0
  56. data/public/gfx/forms/forms-selectbox-small.gif +0 -0
  57. data/public/gfx/forms/forms-selectbox.gif +0 -0
  58. data/public/gfx/forms/forms-textarea-big.gif +0 -0
  59. data/public/gfx/forms/forms-textarea-medium.gif +0 -0
  60. data/public/gfx/forms/forms-textarea-small.gif +0 -0
  61. data/public/gfx/forms/forms-textarea-xl.gif +0 -0
  62. data/public/gfx/icon-delete.png +0 -0
  63. data/public/gfx/icon-edit.png +0 -0
  64. data/public/gfx/icon-home.gif +0 -0
  65. data/public/gfx/img-delete.png +0 -0
  66. data/public/gfx/img-hover.png +0 -0
  67. data/public/gfx/img-zoom.png +0 -0
  68. data/public/gfx/jquery.wysiwyg.gif +0 -0
  69. data/public/gfx/label-icons.gif +0 -0
  70. data/public/gfx/label.gif +0 -0
  71. data/public/gfx/li-down.gif +0 -0
  72. data/public/gfx/li.gif +0 -0
  73. data/public/gfx/link-button-big.gif +0 -0
  74. data/public/gfx/link-button-medium.gif +0 -0
  75. data/public/gfx/link-button.gif +0 -0
  76. data/public/gfx/loading-2.gif +0 -0
  77. data/public/gfx/loading.gif +0 -0
  78. data/public/gfx/logo.png +0 -0
  79. data/public/gfx/modal-title.gif +0 -0
  80. data/public/gfx/photos/00.jpg +0 -0
  81. data/public/gfx/photos/01.jpg +0 -0
  82. data/public/gfx/photos/01xl.jpg +0 -0
  83. data/public/gfx/photos/02.jpg +0 -0
  84. data/public/gfx/photos/02xl.jpg +0 -0
  85. data/public/gfx/photos/03.jpg +0 -0
  86. data/public/gfx/photos/03xl.jpg +0 -0
  87. data/public/gfx/photos/04.jpg +0 -0
  88. data/public/gfx/photos/04xl.jpg +0 -0
  89. data/public/gfx/photos/05.jpg +0 -0
  90. data/public/gfx/photos/05xl.jpg +0 -0
  91. data/public/gfx/photos/06.jpg +0 -0
  92. data/public/gfx/photos/06xl.jpg +0 -0
  93. data/public/gfx/photos/07.jpg +0 -0
  94. data/public/gfx/photos/07xl.jpg +0 -0
  95. data/public/gfx/photos/08.jpg +0 -0
  96. data/public/gfx/photos/08xl.jpg +0 -0
  97. data/public/gfx/photos/09.jpg +0 -0
  98. data/public/gfx/photos/09xl.jpg +0 -0
  99. data/public/gfx/photos/10.jpg +0 -0
  100. data/public/gfx/photos/10xl.jpg +0 -0
  101. data/public/gfx/photos/11.jpg +0 -0
  102. data/public/gfx/photos/11xl.jpg +0 -0
  103. data/public/gfx/photos/12.jpg +0 -0
  104. data/public/gfx/photos/12xl.jpg +0 -0
  105. data/public/gfx/photos/13.jpg +0 -0
  106. data/public/gfx/photos/13xl.jpg +0 -0
  107. data/public/gfx/photos/14.jpg +0 -0
  108. data/public/gfx/photos/14xl.jpg +0 -0
  109. data/public/gfx/photos/15.jpg +0 -0
  110. data/public/gfx/photos/15xl.jpg +0 -0
  111. data/public/gfx/search-button.gif +0 -0
  112. data/public/gfx/search-input.gif +0 -0
  113. data/public/gfx/slider-button.gif +0 -0
  114. data/public/gfx/system-messages.gif +0 -0
  115. data/public/gfx/table-asc-arrow.gif +0 -0
  116. data/public/gfx/table-desc-arrow.gif +0 -0
  117. data/public/gfx/table-first.gif +0 -0
  118. data/public/gfx/table-last.gif +0 -0
  119. data/public/gfx/table-next.gif +0 -0
  120. data/public/gfx/table-number.gif +0 -0
  121. data/public/gfx/table-prev.gif +0 -0
  122. data/public/gfx/table-rows.gif +0 -0
  123. data/public/gfx/table-search.gif +0 -0
  124. data/public/gfx/table-thead.gif +0 -0
  125. data/public/gfx/tooltip.gif +0 -0
  126. data/public/gfx/treeview/ajax-loader.gif +0 -0
  127. data/public/gfx/treeview/file.gif +0 -0
  128. data/public/gfx/treeview/folder-closed.gif +0 -0
  129. data/public/gfx/treeview/folder.gif +0 -0
  130. data/public/gfx/treeview/minus.gif +0 -0
  131. data/public/gfx/treeview/plus.gif +0 -0
  132. data/public/gfx/treeview/treeview-default-line.gif +0 -0
  133. data/public/gfx/treeview/treeview-default.gif +0 -0
  134. data/public/js/controls/wysiwyg.image.js +284 -0
  135. data/public/js/controls/wysiwyg.link.js +210 -0
  136. data/public/js/controls/wysiwyg.table.js +151 -0
  137. data/public/js/customInput.jquery.js +68 -0
  138. data/public/js/excanvas.min.js +1 -0
  139. data/public/js/hoverIntent.js +84 -0
  140. data/public/js/inline.js +392 -0
  141. data/public/js/jquery-1.7.1.min.js +4 -0
  142. data/public/js/jquery-ui-select.js +522 -0
  143. data/public/js/jquery-ui-timepicker-addon.js +1299 -0
  144. data/public/js/jquery-ui.js +791 -0
  145. data/public/js/jquery.dataTables.js +7440 -0
  146. data/public/js/jquery.fancybox-1.3.4.js +1156 -0
  147. data/public/js/jquery.filestyle.mini.js +2 -0
  148. data/public/js/jquery.flot.js +2600 -0
  149. data/public/js/jquery.flot.resize.min.js +60 -0
  150. data/public/js/jquery.graphtable-0.2.js +179 -0
  151. data/public/js/jquery.tipsy.js +104 -0
  152. data/public/js/jquery.treeview.js +256 -0
  153. data/public/js/jquery.wysiwyg.js +2454 -0
  154. data/public/js/plugins/wysiwyg.rmFormat.js +348 -0
  155. data/public/js/superfish.js +121 -0
  156. data/public/js/supersubs.js +90 -0
  157. data/views/home.haml +54 -0
  158. data/views/layout.haml +89 -0
  159. data/views/statistics.haml +251 -71
  160. metadata +175 -22
@@ -0,0 +1,2454 @@
1
+ /**
2
+ * WYSIWYG - jQuery plugin 0.97
3
+ * (0.97.2 - From infinity)
4
+ *
5
+ * Copyright (c) 2008-2009 Juan M Martinez, 2010-2011 Akzhan Abdulin and all contributors
6
+ * https://github.com/akzhan/jwysiwyg
7
+ *
8
+ * Dual licensed under the MIT and GPL licenses:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ * http://www.gnu.org/licenses/gpl.html
11
+ *
12
+ */
13
+
14
+ /*jslint browser: true, forin: true */
15
+
16
+ (function ($) {
17
+ "use strict";
18
+ /* Wysiwyg namespace: private properties and methods */
19
+
20
+ var console = window.console ? window.console : {
21
+ log: $.noop,
22
+ error: function (msg) {
23
+ $.error(msg);
24
+ }
25
+ };
26
+ var supportsProp = (('prop' in $.fn) && ('removeProp' in $.fn));
27
+
28
+ function Wysiwyg() {
29
+ // - the item is added by this.ui.appendControls and then appendItem
30
+ // - click triggers this.triggerControl
31
+ // cmd or[key] - designMode exec function name
32
+ // tags - activates control for these tags (@see checkTargets)
33
+ // css - activates control if one of css is applied
34
+ this.controls = {
35
+ bold: {
36
+ groupIndex: 0,
37
+ visible: true,
38
+ tags: ["b", "strong"],
39
+ css: {
40
+ fontWeight: "bold"
41
+ },
42
+ tooltip: "Bold",
43
+ hotkey: {"ctrl": 1, "key": 66}
44
+ },
45
+
46
+ copy: {
47
+ groupIndex: 8,
48
+ visible: false,
49
+ tooltip: "Copy"
50
+ },
51
+
52
+ createLink: {
53
+ groupIndex: 6,
54
+ visible: true,
55
+ exec: function () {
56
+ var self = this;
57
+ if ($.wysiwyg.controls && $.wysiwyg.controls.link) {
58
+ $.wysiwyg.controls.link.init(this);
59
+ } else if ($.wysiwyg.autoload) {
60
+ $.wysiwyg.autoload.control("wysiwyg.link.js", function () {
61
+ self.controls.createLink.exec.apply(self);
62
+ });
63
+ } else {
64
+ console.error("$.wysiwyg.controls.link not defined. You need to include wysiwyg.link.js file");
65
+ }
66
+ },
67
+ tags: ["a"],
68
+ tooltip: "Create link"
69
+ },
70
+
71
+ cut: {
72
+ groupIndex: 8,
73
+ visible: false,
74
+ tooltip: "Cut"
75
+ },
76
+
77
+ decreaseFontSize: {
78
+ groupIndex: 9,
79
+ visible: false,
80
+ tags: ["small"],
81
+ tooltip: "Decrease font size",
82
+ exec: function () {
83
+ this.decreaseFontSize();
84
+ }
85
+ },
86
+
87
+ h1: {
88
+ groupIndex: 7,
89
+ visible: true,
90
+ className: "h1",
91
+ command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
92
+ "arguments": ($.browser.msie || $.browser.safari) ? "<h1>" : "h1",
93
+ tags: ["h1"],
94
+ tooltip: "Header 1"
95
+ },
96
+
97
+ h2: {
98
+ groupIndex: 7,
99
+ visible: true,
100
+ className: "h2",
101
+ command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
102
+ "arguments": ($.browser.msie || $.browser.safari) ? "<h2>" : "h2",
103
+ tags: ["h2"],
104
+ tooltip: "Header 2"
105
+ },
106
+
107
+ h3: {
108
+ groupIndex: 7,
109
+ visible: true,
110
+ className: "h3",
111
+ command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
112
+ "arguments": ($.browser.msie || $.browser.safari) ? "<h3>" : "h3",
113
+ tags: ["h3"],
114
+ tooltip: "Header 3"
115
+ },
116
+
117
+ highlight: {
118
+ tooltip: "Highlight",
119
+ className: "highlight",
120
+ groupIndex: 1,
121
+ visible: false,
122
+ css: {
123
+ backgroundColor: "rgb(255, 255, 102)"
124
+ },
125
+ exec: function () {
126
+ var command, node, selection, args;
127
+
128
+ if ($.browser.msie || $.browser.safari) {
129
+ command = "backcolor";
130
+ } else {
131
+ command = "hilitecolor";
132
+ }
133
+
134
+ if ($.browser.msie) {
135
+ node = this.getInternalRange().parentElement();
136
+ } else {
137
+ selection = this.getInternalSelection();
138
+ node = selection.extentNode || selection.focusNode;
139
+
140
+ while (node.style === undefined) {
141
+ node = node.parentNode;
142
+ if (node.tagName && node.tagName.toLowerCase() === "body") {
143
+ return;
144
+ }
145
+ }
146
+ }
147
+
148
+ if (node.style.backgroundColor === "rgb(255, 255, 102)" ||
149
+ node.style.backgroundColor === "#ffff66") {
150
+ args = "#ffffff";
151
+ } else {
152
+ args = "#ffff66";
153
+ }
154
+
155
+ this.editorDoc.execCommand(command, false, args);
156
+ }
157
+ },
158
+
159
+ html: {
160
+ groupIndex: 10,
161
+ visible: true,
162
+ exec: function () {
163
+ var elementHeight;
164
+
165
+ if (this.options.resizeOptions && $.fn.resizable) {
166
+ elementHeight = this.element.height();
167
+ }
168
+
169
+ if (this.viewHTML) { //textarea is shown
170
+ this.setContent(this.original.value);
171
+
172
+ $(this.original).hide();
173
+ this.editor.show();
174
+
175
+ if (this.options.resizeOptions && $.fn.resizable) {
176
+ // if element.height still the same after frame was shown
177
+ if (elementHeight === this.element.height()) {
178
+ this.element.height(elementHeight + this.editor.height());
179
+ }
180
+
181
+ this.element.resizable($.extend(true, {
182
+ alsoResize: this.editor
183
+ }, this.options.resizeOptions));
184
+ }
185
+
186
+ this.ui.toolbar.find("li").each(function () {
187
+ var li = $(this);
188
+
189
+ if (li.hasClass("html")) {
190
+ li.removeClass("active");
191
+ } else {
192
+ li.removeClass('disabled');
193
+ }
194
+ });
195
+ } else { //wysiwyg is shown
196
+ this.saveContent();
197
+
198
+ $(this.original).css({
199
+ width: this.element.outerWidth() - 6,
200
+ height: this.element.height() - this.ui.toolbar.height() - 6,
201
+ resize: "none"
202
+ }).show();
203
+ this.editor.hide();
204
+
205
+ if (this.options.resizeOptions && $.fn.resizable) {
206
+ // if element.height still the same after frame was hidden
207
+ if (elementHeight === this.element.height()) {
208
+ this.element.height(this.ui.toolbar.height());
209
+ }
210
+
211
+ this.element.resizable("destroy");
212
+ }
213
+
214
+ this.ui.toolbar.find("li").each(function () {
215
+ var li = $(this);
216
+
217
+ if (li.hasClass("html")) {
218
+ li.addClass("active");
219
+ } else {
220
+ if (false === li.hasClass("fullscreen")) {
221
+ li.removeClass("active").addClass('disabled');
222
+ }
223
+ }
224
+ });
225
+ }
226
+
227
+ this.viewHTML = !(this.viewHTML);
228
+ },
229
+ tooltip: "View source code"
230
+ },
231
+
232
+ increaseFontSize: {
233
+ groupIndex: 9,
234
+ visible: false,
235
+ tags: ["big"],
236
+ tooltip: "Increase font size",
237
+ exec: function () {
238
+ this.increaseFontSize();
239
+ }
240
+ },
241
+
242
+ indent: {
243
+ groupIndex: 2,
244
+ visible: true,
245
+ tooltip: "Indent"
246
+ },
247
+
248
+ insertHorizontalRule: {
249
+ groupIndex: 6,
250
+ visible: true,
251
+ tags: ["hr"],
252
+ tooltip: "Insert Horizontal Rule"
253
+ },
254
+
255
+ insertImage: {
256
+ groupIndex: 6,
257
+ visible: true,
258
+ exec: function () {
259
+ var self = this;
260
+
261
+ if ($.wysiwyg.controls && $.wysiwyg.controls.image) {
262
+ $.wysiwyg.controls.image.init(this);
263
+ } else if ($.wysiwyg.autoload) {
264
+ $.wysiwyg.autoload.control("wysiwyg.image.js", function () {
265
+ self.controls.insertImage.exec.apply(self);
266
+ });
267
+ } else {
268
+ console.error("$.wysiwyg.controls.image not defined. You need to include wysiwyg.image.js file");
269
+ }
270
+ },
271
+ tags: ["img"],
272
+ tooltip: "Insert image"
273
+ },
274
+
275
+ insertOrderedList: {
276
+ groupIndex: 5,
277
+ visible: true,
278
+ tags: ["ol"],
279
+ tooltip: "Insert Ordered List"
280
+ },
281
+
282
+ insertTable: {
283
+ groupIndex: 6,
284
+ visible: true,
285
+ exec: function () {
286
+ var self = this;
287
+
288
+ if ($.wysiwyg.controls && $.wysiwyg.controls.table) {
289
+ $.wysiwyg.controls.table(this);
290
+ } else if ($.wysiwyg.autoload) {
291
+ $.wysiwyg.autoload.control("wysiwyg.table.js", function () {
292
+ self.controls.insertTable.exec.apply(self);
293
+ });
294
+ } else {
295
+ console.error("$.wysiwyg.controls.table not defined. You need to include wysiwyg.table.js file");
296
+ }
297
+ },
298
+ tags: ["table"],
299
+ tooltip: "Insert table"
300
+ },
301
+
302
+ insertUnorderedList: {
303
+ groupIndex: 5,
304
+ visible: true,
305
+ tags: ["ul"],
306
+ tooltip: "Insert Unordered List"
307
+ },
308
+
309
+ italic: {
310
+ groupIndex: 0,
311
+ visible: true,
312
+ tags: ["i", "em"],
313
+ css: {
314
+ fontStyle: "italic"
315
+ },
316
+ tooltip: "Italic",
317
+ hotkey: {"ctrl": 1, "key": 73}
318
+ },
319
+
320
+ justifyCenter: {
321
+ groupIndex: 1,
322
+ visible: true,
323
+ tags: ["center"],
324
+ css: {
325
+ textAlign: "center"
326
+ },
327
+ tooltip: "Justify Center"
328
+ },
329
+
330
+ justifyFull: {
331
+ groupIndex: 1,
332
+ visible: true,
333
+ css: {
334
+ textAlign: "justify"
335
+ },
336
+ tooltip: "Justify Full"
337
+ },
338
+
339
+ justifyLeft: {
340
+ visible: true,
341
+ groupIndex: 1,
342
+ css: {
343
+ textAlign: "left"
344
+ },
345
+ tooltip: "Justify Left"
346
+ },
347
+
348
+ justifyRight: {
349
+ groupIndex: 1,
350
+ visible: true,
351
+ css: {
352
+ textAlign: "right"
353
+ },
354
+ tooltip: "Justify Right"
355
+ },
356
+
357
+ ltr: {
358
+ groupIndex: 10,
359
+ visible: false,
360
+ exec: function () {
361
+ var p = this.dom.getElement("p");
362
+
363
+ if (!p) {
364
+ return false;
365
+ }
366
+
367
+ $(p).attr("dir", "ltr");
368
+ return true;
369
+ },
370
+ tooltip : "Left to Right"
371
+ },
372
+
373
+ outdent: {
374
+ groupIndex: 2,
375
+ visible: true,
376
+ tooltip: "Outdent"
377
+ },
378
+
379
+ paragraph: {
380
+ groupIndex: 7,
381
+ visible: false,
382
+ className: "paragraph",
383
+ command: "FormatBlock",
384
+ "arguments": ($.browser.msie || $.browser.safari) ? "<p>" : "p",
385
+ tags: ["p"],
386
+ tooltip: "Paragraph"
387
+ },
388
+
389
+ paste: {
390
+ groupIndex: 8,
391
+ visible: false,
392
+ tooltip: "Paste"
393
+ },
394
+
395
+ redo: {
396
+ groupIndex: 4,
397
+ visible: true,
398
+ tooltip: "Redo"
399
+ },
400
+
401
+ removeFormat: {
402
+ groupIndex: 10,
403
+ visible: true,
404
+ exec: function () {
405
+ this.removeFormat();
406
+ },
407
+ tooltip: "Remove formatting"
408
+ },
409
+
410
+ rtl: {
411
+ groupIndex: 10,
412
+ visible: false,
413
+ exec: function () {
414
+ var p = this.dom.getElement("p");
415
+
416
+ if (!p) {
417
+ return false;
418
+ }
419
+
420
+ $(p).attr("dir", "rtl");
421
+ return true;
422
+ },
423
+ tooltip : "Right to Left"
424
+ },
425
+
426
+ strikeThrough: {
427
+ groupIndex: 0,
428
+ visible: true,
429
+ tags: ["s", "strike"],
430
+ css: {
431
+ textDecoration: "line-through"
432
+ },
433
+ tooltip: "Strike-through"
434
+ },
435
+
436
+ subscript: {
437
+ groupIndex: 3,
438
+ visible: true,
439
+ tags: ["sub"],
440
+ tooltip: "Subscript"
441
+ },
442
+
443
+ superscript: {
444
+ groupIndex: 3,
445
+ visible: true,
446
+ tags: ["sup"],
447
+ tooltip: "Superscript"
448
+ },
449
+
450
+ underline: {
451
+ groupIndex: 0,
452
+ visible: true,
453
+ tags: ["u"],
454
+ css: {
455
+ textDecoration: "underline"
456
+ },
457
+ tooltip: "Underline",
458
+ hotkey: {"ctrl": 1, "key": 85}
459
+ },
460
+
461
+ undo: {
462
+ groupIndex: 4,
463
+ visible: true,
464
+ tooltip: "Undo"
465
+ },
466
+
467
+ code: {
468
+ visible : true,
469
+ groupIndex: 6,
470
+ tooltip: "Code snippet",
471
+ exec: function () {
472
+ var range = this.getInternalRange(),
473
+ common = $(range.commonAncestorContainer),
474
+ $nodeName = range.commonAncestorContainer.nodeName.toLowerCase();
475
+ if (common.parent("code").length) {
476
+ common.unwrap();
477
+ } else {
478
+ if ($nodeName !== "body") {
479
+ common.wrap("<code/>");
480
+ }
481
+ }
482
+ }
483
+ },
484
+
485
+ cssWrap: {
486
+ visible : false,
487
+ groupIndex: 6,
488
+ tooltip: "CSS Wrapper",
489
+ exec: function () {
490
+ $.wysiwyg.controls.cssWrap.init(this);
491
+ }
492
+ }
493
+
494
+ };
495
+
496
+ this.defaults = {
497
+ html: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" style="margin:0"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body style="margin:0;">INITIAL_CONTENT</body></html>',
498
+ debug: false,
499
+ controls: {},
500
+ css: {},
501
+ events: {},
502
+ autoGrow: false,
503
+ autoSave: true,
504
+ brIE: true, // http://code.google.com/p/jwysiwyg/issues/detail?id=15
505
+ formHeight: 270,
506
+ formWidth: 440,
507
+ iFrameClass: null,
508
+ initialContent: "",
509
+ maxHeight: 10000, // see autoGrow
510
+ maxLength: 0,
511
+ messages: {
512
+ nonSelection: "Select the text you wish to link"
513
+ },
514
+ toolbarHtml: '<ul role="menu" class="toolbar"></ul>',
515
+ removeHeadings: false,
516
+ replaceDivWithP: false,
517
+ resizeOptions: false,
518
+ rmUnusedControls: false, // https://github.com/akzhan/jwysiwyg/issues/52
519
+ rmUnwantedBr: true, // http://code.google.com/p/jwysiwyg/issues/detail?id=11
520
+ tableFiller: "Lorem ipsum",
521
+ initialMinHeight: null,
522
+
523
+ controlImage: {
524
+ forceRelativeUrls: false
525
+ },
526
+
527
+ controlLink: {
528
+ forceRelativeUrls: false
529
+ },
530
+
531
+ plugins: { // placeholder for plugins settings
532
+ autoload: false,
533
+ i18n: false,
534
+ rmFormat: {
535
+ rmMsWordMarkup: false
536
+ }
537
+ },
538
+
539
+ dialog : "default"
540
+ };
541
+
542
+ //these properties are set from control hashes
543
+ this.availableControlProperties = [
544
+ "arguments",
545
+ "callback",
546
+ "className",
547
+ "command",
548
+ "css",
549
+ "custom",
550
+ "exec",
551
+ "groupIndex",
552
+ "hotkey",
553
+ "icon",
554
+ "tags",
555
+ "tooltip",
556
+ "visible"
557
+ ];
558
+
559
+ this.editor = null; //jquery iframe holder
560
+ this.editorDoc = null;
561
+ this.element = null;
562
+ this.options = {};
563
+ this.original = null;
564
+ this.savedRange = null;
565
+ this.timers = [];
566
+ this.validKeyCodes = [8, 9, 13, 16, 17, 18, 19, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46];
567
+
568
+ this.isDestroyed = false;
569
+
570
+ this.dom = { // DOM related properties and methods
571
+ ie: {
572
+ parent: null // link to dom
573
+ },
574
+ w3c: {
575
+ parent: null // link to dom
576
+ }
577
+ };
578
+ this.dom.parent = this;
579
+ this.dom.ie.parent = this.dom;
580
+ this.dom.w3c.parent = this.dom;
581
+
582
+ this.ui = {}; // UI related properties and methods
583
+ this.ui.self = this;
584
+ this.ui.toolbar = null;
585
+ this.ui.initialHeight = null; // ui.grow
586
+
587
+ this.dom.getAncestor = function (element, filterTagName) {
588
+ filterTagName = filterTagName.toLowerCase();
589
+
590
+ while (element && typeof element.tagName != "undefined" && "body" !== element.tagName.toLowerCase()) {
591
+ if (filterTagName === element.tagName.toLowerCase()) {
592
+ return element;
593
+ }
594
+
595
+ element = element.parentNode;
596
+ }
597
+ if(!element.tagName && (element.previousSibling || element.nextSibling)) {
598
+ if(element.previousSibling) {
599
+ if(element.previousSibling.tagName.toLowerCase() == filterTagName) {
600
+ return element.previousSibling;
601
+ }
602
+ }
603
+ if(element.nextSibling) {
604
+ if(element.nextSibling.tagName.toLowerCase() == filterTagName) {
605
+ return element.nextSibling;
606
+ }
607
+ }
608
+ }
609
+
610
+ return null;
611
+ };
612
+
613
+ this.dom.getElement = function (filterTagName) {
614
+ var dom = this;
615
+
616
+ filterTagName = filterTagName.toLowerCase();
617
+
618
+ if (window.getSelection) {
619
+ return dom.w3c.getElement(filterTagName);
620
+ } else {
621
+ return dom.ie.getElement(filterTagName);
622
+ }
623
+ };
624
+
625
+ this.dom.ie.getElement = function (filterTagName) {
626
+ var dom = this.parent,
627
+ selection = dom.parent.getInternalSelection(),
628
+ range = selection.createRange(),
629
+ element;
630
+
631
+ if ("Control" === selection.type) {
632
+ // control selection
633
+ if (1 === range.length) {
634
+ element = range.item(0);
635
+ } else {
636
+ // multiple control selection
637
+ return null;
638
+ }
639
+ } else {
640
+ element = range.parentElement();
641
+ }
642
+
643
+ return dom.getAncestor(element, filterTagName);
644
+ };
645
+
646
+ this.dom.w3c.getElement = function (filterTagName) {
647
+ var dom = this.parent,
648
+ range = dom.parent.getInternalRange(),
649
+ element;
650
+
651
+ if (!range) {
652
+ return null;
653
+ }
654
+
655
+ element = range.commonAncestorContainer;
656
+
657
+ if (3 === element.nodeType) {
658
+ element = element.parentNode;
659
+ }
660
+
661
+ // if startContainer not Text, Comment, or CDATASection element then
662
+ // startOffset is the number of child nodes between the start of the
663
+ // startContainer and the boundary point of the Range
664
+ if (element === range.startContainer) {
665
+ element = element.childNodes[range.startOffset];
666
+ }
667
+
668
+ if(!element.tagName && (element.previousSibiling || element.nextSibling)) {
669
+ if(element.previousSibiling) {
670
+ if(element.previousSibiling.tagName.toLowerCase() == filterTagName) {
671
+ return element.previousSibiling;
672
+ }
673
+ }
674
+ if(element.nextSibling) {
675
+ if(element.nextSibling.tagName.toLowerCase() == filterTagName) {
676
+ return element.nextSibling;
677
+ }
678
+ }
679
+ }
680
+
681
+ return dom.getAncestor(element, filterTagName);
682
+ };
683
+
684
+ this.ui.addHoverClass = function () {
685
+ $(this).addClass("wysiwyg-button-hover");
686
+ };
687
+
688
+ this.ui.appendControls = function () {
689
+ var ui = this,
690
+ self = this.self,
691
+ controls = self.parseControls(),
692
+ hasVisibleControls = true, // to prevent separator before first item
693
+ groups = [],
694
+ controlsByGroup = {},
695
+ i,
696
+ currentGroupIndex, // jslint wants all vars at top of function
697
+ iterateGroup = function (controlName, control) { //called for every group when adding
698
+ if (control.groupIndex && currentGroupIndex !== control.groupIndex) {
699
+ currentGroupIndex = control.groupIndex;
700
+ hasVisibleControls = false;
701
+ }
702
+
703
+ if (!control.visible) {
704
+ return;
705
+ }
706
+
707
+ if (!hasVisibleControls) {
708
+ ui.appendItemSeparator();
709
+ hasVisibleControls = true;
710
+ }
711
+
712
+ if (control.custom) {
713
+ ui.appendItemCustom(controlName, control);
714
+ } else {
715
+ ui.appendItem(controlName, control);
716
+ }
717
+ };
718
+
719
+ $.each(controls, function (name, c) { //sort by groupIndex
720
+ var index = "empty";
721
+
722
+ if (undefined !== c.groupIndex) {
723
+ if ("" === c.groupIndex) {
724
+ index = "empty";
725
+ } else {
726
+ index = c.groupIndex;
727
+ }
728
+ }
729
+
730
+ if (undefined === controlsByGroup[index]) {
731
+ groups.push(index);
732
+ controlsByGroup[index] = {};
733
+ }
734
+ controlsByGroup[index][name] = c;
735
+ });
736
+
737
+ groups.sort(function (a, b) { //just sort group indexes by
738
+ if ("number" === typeof (a) && typeof (a) === typeof (b)) {
739
+ return (a - b);
740
+ } else {
741
+ a = a.toString();
742
+ b = b.toString();
743
+
744
+ if (a > b) {
745
+ return 1;
746
+ }
747
+
748
+ if (a === b) {
749
+ return 0;
750
+ }
751
+
752
+ return -1;
753
+ }
754
+ });
755
+
756
+ if (0 < groups.length) {
757
+ // set to first index in groups to proper placement of separator
758
+ currentGroupIndex = groups[0];
759
+ }
760
+
761
+ for (i = 0; i < groups.length; i += 1) {
762
+ $.each(controlsByGroup[groups[i]], iterateGroup);
763
+ }
764
+ };
765
+
766
+ this.ui.appendItem = function (name, control) {
767
+ var self = this.self,
768
+ className = control.className || control.command || name || "empty",
769
+ tooltip = control.tooltip || control.command || name || "";
770
+
771
+ return $('<li role="menuitem" unselectable="on">' + (className) + "</li>")
772
+ .addClass(className)
773
+ .attr("title", tooltip)
774
+ .hover(this.addHoverClass, this.removeHoverClass)
775
+ .click(function (event) {
776
+ if ($(this).hasClass("disabled")) {
777
+ return false;
778
+ }
779
+
780
+ self.triggerControl.apply(self, [name, control]);
781
+
782
+ /**
783
+ * @link https://github.com/akzhan/jwysiwyg/issues/219
784
+ */
785
+ var $target = $(event.target);
786
+ for (var controlName in self.controls) {
787
+ if ($target.hasClass(controlName)) {
788
+ self.ui.toolbar.find("." + controlName).toggleClass("active");
789
+ self.editorDoc.rememberCommand = true;
790
+ break;
791
+ }
792
+ }
793
+
794
+ this.blur();
795
+ self.ui.returnRange();
796
+ self.ui.focus();
797
+ return true;
798
+ })
799
+ .appendTo(self.ui.toolbar);
800
+ };
801
+
802
+ this.ui.appendItemCustom = function (name, control) {
803
+ var self = this.self,
804
+ tooltip = control.tooltip || control.command || name || "";
805
+
806
+ if (control.callback) {
807
+ $(window).bind("trigger-" + name + ".wysiwyg", control.callback);
808
+ }
809
+
810
+ return $('<li role="menuitem" unselectable="on" style="background: url(\'' + control.icon + '\') no-repeat;"></li>')
811
+ .addClass("custom-command-" + name)
812
+ .addClass("wysiwyg-custom-command")
813
+ .addClass(name)
814
+ .attr("title", tooltip)
815
+ .hover(this.addHoverClass, this.removeHoverClass)
816
+ .click(function () {
817
+ if ($(this).hasClass("disabled")) {
818
+ return false;
819
+ }
820
+
821
+ self.triggerControl.apply(self, [name, control]);
822
+
823
+ this.blur();
824
+ self.ui.returnRange();
825
+ self.ui.focus();
826
+
827
+ self.triggerControlCallback(name);
828
+ return true;
829
+ })
830
+ .appendTo(self.ui.toolbar);
831
+ };
832
+
833
+ this.ui.appendItemSeparator = function () {
834
+ var self = this.self;
835
+ return $('<li role="separator" class="separator"></li>').appendTo(self.ui.toolbar);
836
+ };
837
+
838
+ this.autoSaveFunction = function () {
839
+ this.saveContent();
840
+ };
841
+
842
+ //called after click in wysiwyg "textarea"
843
+ this.ui.checkTargets = function (element) {
844
+ var self = this.self;
845
+
846
+ //activate controls
847
+ $.each(self.options.controls, function (name, control) {
848
+ var className = control.className || control.command || name || "empty",
849
+ tags,
850
+ elm,
851
+ css,
852
+ el,
853
+ checkActiveStatus = function (cssProperty, cssValue) {
854
+ var handler;
855
+
856
+ if ("function" === typeof (cssValue)) {
857
+ handler = cssValue;
858
+ if (handler(el.css(cssProperty).toString().toLowerCase(), self)) {
859
+ self.ui.toolbar.find("." + className).addClass("active");
860
+ }
861
+ } else {
862
+ if (el.css(cssProperty).toString().toLowerCase() === cssValue) {
863
+ self.ui.toolbar.find("." + className).addClass("active");
864
+ }
865
+ }
866
+ };
867
+
868
+ if ("fullscreen" !== className) {
869
+ self.ui.toolbar.find("." + className).removeClass("active");
870
+ }
871
+
872
+ //activate by allowed tags
873
+ if (control.tags || (control.options && control.options.tags)) {
874
+ tags = control.tags || (control.options && control.options.tags);
875
+
876
+ elm = element;
877
+ while (elm) {
878
+ if (elm.nodeType !== 1) {
879
+ break;
880
+ }
881
+
882
+ if ($.inArray(elm.tagName.toLowerCase(), tags) !== -1) {
883
+ self.ui.toolbar.find("." + className).addClass("active");
884
+ }
885
+
886
+ elm = elm.parentNode;
887
+ }
888
+ }
889
+
890
+ //activate by supposed css
891
+ if (control.css || (control.options && control.options.css)) {
892
+ css = control.css || (control.options && control.options.css);
893
+ el = $(element);
894
+
895
+ while (el) {
896
+ if (el[0].nodeType !== 1) {
897
+ break;
898
+ }
899
+ $.each(css, checkActiveStatus);
900
+
901
+ el = el.parent();
902
+ }
903
+ }
904
+ });
905
+ };
906
+
907
+ this.ui.designMode = function () {
908
+ var attempts = 3,
909
+ self = this.self,
910
+ runner;
911
+ runner = function (attempts) {
912
+ if ("on" === self.editorDoc.designMode) {
913
+ if (self.timers.designMode) {
914
+ window.clearTimeout(self.timers.designMode);
915
+ }
916
+
917
+ // IE needs to reget the document element (this.editorDoc) after designMode was set
918
+ if (self.innerDocument() !== self.editorDoc) {
919
+ self.ui.initFrame();
920
+ }
921
+
922
+ return;
923
+ }
924
+
925
+ try {
926
+ self.editorDoc.designMode = "on";
927
+ } catch (e) {
928
+ }
929
+
930
+ attempts -= 1;
931
+ if (attempts > 0) {
932
+ self.timers.designMode = window.setTimeout(function () { runner(attempts); }, 100);
933
+ }
934
+ };
935
+
936
+ runner(attempts);
937
+ };
938
+
939
+ this.destroy = function () {
940
+ this.isDestroyed = true;
941
+
942
+ var i, $form = this.element.closest("form");
943
+
944
+ for (i = 0; i < this.timers.length; i += 1) {
945
+ window.clearTimeout(this.timers[i]);
946
+ }
947
+
948
+ // Remove bindings
949
+ $form.unbind(".wysiwyg");
950
+ this.element.remove();
951
+ $.removeData(this.original, "wysiwyg");
952
+ $(this.original).show();
953
+ return this;
954
+ };
955
+
956
+ this.getRangeText = function () {
957
+ var r = this.getInternalRange();
958
+
959
+ if (r.toString) {
960
+ r = r.toString();
961
+ } else if (r.text) { // IE
962
+ r = r.text;
963
+ }
964
+
965
+ return r;
966
+ };
967
+ //not used?
968
+ this.execute = function (command, arg) {
969
+ if (typeof (arg) === "undefined") {
970
+ arg = null;
971
+ }
972
+ this.editorDoc.execCommand(command, false, arg);
973
+ };
974
+
975
+ this.extendOptions = function (options) {
976
+ var controls = {};
977
+
978
+ /**
979
+ * If the user set custom controls, we catch it, and merge with the
980
+ * defaults controls later.
981
+ */
982
+ if ("object" === typeof options.controls) {
983
+ controls = options.controls;
984
+ delete options.controls;
985
+ }
986
+
987
+ options = $.extend(true, {}, this.defaults, options);
988
+ options.controls = $.extend(true, {}, controls, this.controls, controls);
989
+
990
+ if (options.rmUnusedControls) {
991
+ $.each(options.controls, function (controlName) {
992
+ if (!controls[controlName]) {
993
+ delete options.controls[controlName];
994
+ }
995
+ });
996
+ }
997
+
998
+ return options;
999
+ };
1000
+
1001
+ this.ui.focus = function () {
1002
+ var self = this.self;
1003
+
1004
+ self.editor.get(0).contentWindow.focus();
1005
+ return self;
1006
+ };
1007
+
1008
+ this.ui.returnRange = function () {
1009
+ var self = this.self, sel;
1010
+
1011
+ if (self.savedRange !== null) {
1012
+ if (window.getSelection) { //non IE and there is already a selection
1013
+ sel = window.getSelection();
1014
+ if (sel.rangeCount > 0) {
1015
+ sel.removeAllRanges();
1016
+ }
1017
+ try {
1018
+ sel.addRange(self.savedRange);
1019
+ } catch (e) {
1020
+ console.error(e);
1021
+ }
1022
+ } else if (window.document.createRange) { // non IE and no selection
1023
+ window.getSelection().addRange(self.savedRange);
1024
+ } else if (window.document.selection) { //IE
1025
+ self.savedRange.select();
1026
+ }
1027
+
1028
+ self.savedRange = null;
1029
+ }
1030
+ };
1031
+
1032
+ this.increaseFontSize = function () {
1033
+ if ($.browser.mozilla || $.browser.opera) {
1034
+ this.editorDoc.execCommand("increaseFontSize", false, null);
1035
+ } else if ($.browser.safari) {
1036
+ var Range = this.getInternalRange(),
1037
+ Selection = this.getInternalSelection(),
1038
+ newNode = this.editorDoc.createElement("big");
1039
+
1040
+ // If cursor placed on text node
1041
+ if (true === Range.collapsed && 3 === Range.commonAncestorContainer.nodeType) {
1042
+ var text = Range.commonAncestorContainer.nodeValue.toString(),
1043
+ start = text.lastIndexOf(" ", Range.startOffset) + 1,
1044
+ end = (-1 === text.indexOf(" ", Range.startOffset)) ? text : text.indexOf(" ", Range.startOffset);
1045
+
1046
+ Range.setStart(Range.commonAncestorContainer, start);
1047
+ Range.setEnd(Range.commonAncestorContainer, end);
1048
+
1049
+ Range.surroundContents(newNode);
1050
+ Selection.addRange(Range);
1051
+ } else {
1052
+ Range.surroundContents(newNode);
1053
+ Selection.removeAllRanges();
1054
+ Selection.addRange(Range);
1055
+ }
1056
+ } else {
1057
+ console.error("Internet Explorer?");
1058
+ }
1059
+ };
1060
+
1061
+ this.decreaseFontSize = function () {
1062
+ if ($.browser.mozilla || $.browser.opera) {
1063
+ this.editorDoc.execCommand("decreaseFontSize", false, null);
1064
+ } else if ($.browser.safari) {
1065
+ var Range = this.getInternalRange(),
1066
+ Selection = this.getInternalSelection(),
1067
+ newNode = this.editorDoc.createElement("small");
1068
+
1069
+ // If cursor placed on text node
1070
+ if (true === Range.collapsed && 3 === Range.commonAncestorContainer.nodeType) {
1071
+ var text = Range.commonAncestorContainer.nodeValue.toString(),
1072
+ start = text.lastIndexOf(" ", Range.startOffset) + 1,
1073
+ end = (-1 === text.indexOf(" ", Range.startOffset)) ? text : text.indexOf(" ", Range.startOffset);
1074
+
1075
+ Range.setStart(Range.commonAncestorContainer, start);
1076
+ Range.setEnd(Range.commonAncestorContainer, end);
1077
+
1078
+ Range.surroundContents(newNode);
1079
+ Selection.addRange(Range);
1080
+ } else {
1081
+ Range.surroundContents(newNode);
1082
+ Selection.removeAllRanges();
1083
+ Selection.addRange(Range);
1084
+ }
1085
+ } else {
1086
+ console.error("Internet Explorer?");
1087
+ }
1088
+ };
1089
+
1090
+ this.getContent = function () {
1091
+ if (this.viewHTML) {
1092
+ this.setContent(this.original.value);
1093
+ }
1094
+ return this.events.filter('getContent', this.editorDoc.body.innerHTML);
1095
+ };
1096
+
1097
+ /**
1098
+ * A jWysiwyg specific event system.
1099
+ *
1100
+ * Example:
1101
+ *
1102
+ * $("#editor").getWysiwyg().events.bind("getContent", function (orig) {
1103
+ * return "<div id='content'>"+orgi+"</div>";
1104
+ * });
1105
+ *
1106
+ * This makes it so that when ever getContent is called, it is wrapped in a div#content.
1107
+ */
1108
+ this.events = {
1109
+ _events : {},
1110
+
1111
+ /**
1112
+ * Similar to jQuery's bind, but for jWysiwyg only.
1113
+ */
1114
+ bind : function (eventName, callback) {
1115
+ if (typeof (this._events.eventName) !== "object") {
1116
+ this._events[eventName] = [];
1117
+ }
1118
+ this._events[eventName].push(callback);
1119
+ },
1120
+
1121
+ /**
1122
+ * Similar to jQuery's trigger, but for jWysiwyg only.
1123
+ */
1124
+ trigger : function (eventName, args) {
1125
+ if (typeof (this._events.eventName) === "object") {
1126
+ var editor = this.editor;
1127
+ $.each(this._events[eventName], function (k, v) {
1128
+ if (typeof (v) === "function") {
1129
+ v.apply(editor, args);
1130
+ }
1131
+ });
1132
+ }
1133
+ },
1134
+
1135
+ /**
1136
+ * This "filters" `originalText` by passing it as the first argument to every callback
1137
+ * with the name `eventName` and taking the return value and passing it to the next function.
1138
+ *
1139
+ * This function returns the result after all the callbacks have been applied to `originalText`.
1140
+ */
1141
+ filter : function (eventName, originalText) {
1142
+ if (typeof (this._events[eventName]) === "object") {
1143
+ var editor = this.editor,
1144
+ args = Array.prototype.slice.call(arguments, 1);
1145
+
1146
+ $.each(this._events[eventName], function (k, v) {
1147
+ if (typeof (v) === "function") {
1148
+ originalText = v.apply(editor, args);
1149
+ }
1150
+ });
1151
+ }
1152
+ return originalText;
1153
+ }
1154
+ };
1155
+
1156
+ this.getElementByAttributeValue = function (tagName, attributeName, attributeValue) {
1157
+ var i, value, elements = this.editorDoc.getElementsByTagName(tagName);
1158
+
1159
+ for (i = 0; i < elements.length; i += 1) {
1160
+ value = elements[i].getAttribute(attributeName);
1161
+
1162
+ if ($.browser.msie) {
1163
+ /** IE add full path, so I check by the last chars. */
1164
+ value = value.substr(value.length - attributeValue.length);
1165
+ }
1166
+
1167
+ if (value === attributeValue) {
1168
+ return elements[i];
1169
+ }
1170
+ }
1171
+
1172
+ return false;
1173
+ };
1174
+
1175
+ this.getInternalRange = function () {
1176
+ var selection = this.getInternalSelection();
1177
+
1178
+ if (!selection) {
1179
+ return null;
1180
+ }
1181
+
1182
+ if (selection.rangeCount && selection.rangeCount > 0) { // w3c
1183
+ return selection.getRangeAt(0);
1184
+ } else if (selection.createRange) { // ie
1185
+ return selection.createRange();
1186
+ }
1187
+
1188
+ return null;
1189
+ };
1190
+
1191
+ this.getInternalSelection = function () {
1192
+ // firefox: document.getSelection is deprecated
1193
+ if (this.editor.get(0).contentWindow) {
1194
+ if (this.editor.get(0).contentWindow.getSelection) {
1195
+ return this.editor.get(0).contentWindow.getSelection();
1196
+ }
1197
+ if (this.editor.get(0).contentWindow.selection) {
1198
+ return this.editor.get(0).contentWindow.selection;
1199
+ }
1200
+ }
1201
+ if (this.editorDoc.getSelection) {
1202
+ return this.editorDoc.getSelection();
1203
+ }
1204
+ if (this.editorDoc.selection) {
1205
+ return this.editorDoc.selection;
1206
+ }
1207
+
1208
+ return null;
1209
+ };
1210
+
1211
+ this.getRange = function () {
1212
+ var selection = this.getSelection();
1213
+
1214
+ if (!selection) {
1215
+ return null;
1216
+ }
1217
+
1218
+ if (selection.rangeCount && selection.rangeCount > 0) { // w3c
1219
+ selection.getRangeAt(0);
1220
+ } else if (selection.createRange) { // ie
1221
+ return selection.createRange();
1222
+ }
1223
+
1224
+ return null;
1225
+ };
1226
+
1227
+ this.getSelection = function () {
1228
+ return (window.getSelection) ? window.getSelection() : window.document.selection;
1229
+ };
1230
+
1231
+ // :TODO: you can type long string and letters will be hidden because of overflow
1232
+ this.ui.grow = function () {
1233
+ var self = this.self,
1234
+ innerBody = $(self.editorDoc.body),
1235
+ innerHeight = $.browser.msie ? innerBody[0].scrollHeight : innerBody.height() + 2 + 20, // 2 - borders, 20 - to prevent content jumping on grow
1236
+ minHeight = self.ui.initialHeight,
1237
+ height = Math.max(innerHeight, minHeight);
1238
+
1239
+ height = Math.min(height, self.options.maxHeight);
1240
+
1241
+ self.editor.attr("scrolling", height < self.options.maxHeight ? "no" : "auto"); // hide scrollbar firefox
1242
+ innerBody.css("overflow", height < self.options.maxHeight ? "hidden" : ""); // hide scrollbar chrome
1243
+
1244
+ self.editor.get(0).height = height;
1245
+
1246
+ return self;
1247
+ };
1248
+
1249
+ this.init = function (element, options) {
1250
+ var self = this,
1251
+ $form = $(element).closest("form"),
1252
+ newX = (element.width || element.clientWidth || 0),
1253
+ newY = (element.height || element.clientHeight || 0)
1254
+ ;
1255
+
1256
+ this.options = this.extendOptions(options);
1257
+ this.original = element;
1258
+ this.ui.toolbar = $(this.options.toolbarHtml);
1259
+
1260
+ if ($.browser.msie && parseInt($.browser.version, 10) < 8) {
1261
+ this.options.autoGrow = false;
1262
+ }
1263
+
1264
+ if (newX === 0 && element.cols) {
1265
+ newX = (element.cols * 8) + 21;
1266
+ }
1267
+ if (newY === 0 && element.rows) {
1268
+ newY = (element.rows * 16) + 16;
1269
+ }
1270
+
1271
+ this.editor = $(window.location.protocol === "https:" ? '<iframe src="javascript:false;"></iframe>' : "<iframe></iframe>").attr("frameborder", "0");
1272
+
1273
+ if (this.options.iFrameClass) {
1274
+ this.editor.addClass(this.options.iFrameClass);
1275
+ } else {
1276
+ this.editor.css({
1277
+ minHeight: (newY - 6).toString() + "px",
1278
+ // fix for issue 12 ( http://github.com/akzhan/jwysiwyg/issues/issue/12 )
1279
+ width: "101%"
1280
+ });
1281
+ if ($.browser.msie && parseInt($.browser.version, 10) < 7) {
1282
+ this.editor.css("height", newY.toString() + "px");
1283
+ }
1284
+ }
1285
+ /**
1286
+ * Automagically add id to iframe if textarea has its own when possible
1287
+ * ( http://github.com/akzhan/jwysiwyg/issues/245 )
1288
+ */
1289
+ if (element.id) {
1290
+ var proposedId = element.id + '-wysiwyg-iframe';
1291
+ if (! document.getElementById(proposedId)) {
1292
+ this.editor.attr('id', proposedId);
1293
+ }
1294
+ }
1295
+
1296
+ /**
1297
+ * http://code.google.com/p/jwysiwyg/issues/detail?id=96
1298
+ */
1299
+ this.editor.attr("tabindex", $(element).attr("tabindex"));
1300
+
1301
+ this.element = $("<div/>").addClass("wysiwyg");
1302
+
1303
+ if (!this.options.iFrameClass) {
1304
+ this.element.css({
1305
+ width: "100%"
1306
+ });
1307
+ }
1308
+
1309
+ $(element).hide().before(this.element);
1310
+
1311
+ this.viewHTML = false;
1312
+
1313
+ /**
1314
+ * @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
1315
+ */
1316
+ this.initialContent = $(element).val();
1317
+ this.ui.initFrame();
1318
+
1319
+ if (this.options.resizeOptions && $.fn.resizable) {
1320
+ this.element.resizable($.extend(true, {
1321
+ alsoResize: this.editor
1322
+ }, this.options.resizeOptions));
1323
+ }
1324
+
1325
+ if (this.options.autoSave) {
1326
+ $form.bind("submit.wysiwyg", function () { self.autoSaveFunction(); });
1327
+ }
1328
+
1329
+ $form.bind("reset.wysiwyg", function () { self.resetFunction(); });
1330
+ };
1331
+
1332
+ this.ui.initFrame = function () {
1333
+ var self = this.self,
1334
+ stylesheet,
1335
+ growHandler,
1336
+ saveHandler;
1337
+
1338
+ self.ui.appendControls();
1339
+ self.element.append(self.ui.toolbar)
1340
+ .append($("<div><!-- --></div>")
1341
+ .css({
1342
+ clear: "both"
1343
+ }))
1344
+ .append(self.editor);
1345
+
1346
+ self.editorDoc = self.innerDocument();
1347
+
1348
+ if (self.isDestroyed) {
1349
+ return null;
1350
+ }
1351
+
1352
+ self.ui.designMode();
1353
+ self.editorDoc.open();
1354
+ self.editorDoc.write(
1355
+ self.options.html
1356
+ /**
1357
+ * @link http://code.google.com/p/jwysiwyg/issues/detail?id=144
1358
+ */
1359
+ .replace(/INITIAL_CONTENT/, function () { return self.wrapInitialContent(); })
1360
+ );
1361
+ self.editorDoc.close();
1362
+
1363
+ $.wysiwyg.plugin.bind(self);
1364
+
1365
+ $(self.editorDoc).trigger("initFrame.wysiwyg");
1366
+
1367
+ $(self.editorDoc).bind("click.wysiwyg", function (event) {
1368
+ self.ui.checkTargets(event.target ? event.target : event.srcElement);
1369
+ });
1370
+
1371
+ /**
1372
+ * @link https://github.com/akzhan/jwysiwyg/issues/251
1373
+ */
1374
+ setInterval(function () {
1375
+ var offset = null;
1376
+
1377
+ try {
1378
+ var range = self.getInternalRange();
1379
+ if (range) {
1380
+ offset = {
1381
+ range: range,
1382
+ parent: $.browser.msie ? range.parentElement() : range.endContainer.parentNode,
1383
+ width: ($.browser.msie ? range.boundingWidth : range.startOffset - range.endOffset) || 0
1384
+ };
1385
+ }
1386
+ }
1387
+ catch (e) { console.error(e); }
1388
+
1389
+ if (offset && offset.width == 0 && !self.editorDoc.rememberCommand) {
1390
+ self.ui.checkTargets(offset.parent);
1391
+ }
1392
+ }, 400);
1393
+
1394
+ /**
1395
+ * @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
1396
+ */
1397
+ $(self.original).focus(function () {
1398
+ if ($(this).filter(":visible").length === 0) {
1399
+ return;
1400
+ }
1401
+ self.ui.focus();
1402
+ });
1403
+
1404
+ $(self.editorDoc).keydown(function (event) {
1405
+ var emptyContentRegex;
1406
+ if (event.keyCode === 8) { // backspace
1407
+ emptyContentRegex = /^<([\w]+)[^>]*>(<br\/?>)?<\/\1>$/;
1408
+ if (emptyContentRegex.test(self.getContent())) { // if content is empty
1409
+ event.stopPropagation(); // prevent remove single empty tag
1410
+ return false;
1411
+ }
1412
+ }
1413
+
1414
+ self.editorDoc.rememberCommand = false;
1415
+ return true;
1416
+ });
1417
+
1418
+ if (!$.browser.msie) {
1419
+ $(self.editorDoc).keydown(function (event) {
1420
+ var controlName;
1421
+
1422
+ /* Meta for Macs. tom@punkave.com */
1423
+ if (event.ctrlKey || event.metaKey) {
1424
+ for (controlName in self.controls) {
1425
+ if (self.controls[controlName].hotkey && self.controls[controlName].hotkey.ctrl) {
1426
+ if (event.keyCode === self.controls[controlName].hotkey.key) {
1427
+ self.triggerControl.apply(self, [controlName, self.controls[controlName]]);
1428
+
1429
+ return false;
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1434
+
1435
+ return true;
1436
+ });
1437
+ } else if (self.options.brIE) {
1438
+ $(self.editorDoc).keydown(function (event) {
1439
+ if (event.keyCode === 13) {
1440
+ var rng = self.getRange();
1441
+ rng.pasteHTML("<br/>");
1442
+ rng.collapse(false);
1443
+ rng.select();
1444
+
1445
+ return false;
1446
+ }
1447
+
1448
+ return true;
1449
+ });
1450
+ }
1451
+
1452
+ if (self.options.plugins.rmFormat.rmMsWordMarkup) {
1453
+ $(self.editorDoc).bind("keyup.wysiwyg", function (event) {
1454
+ if (event.ctrlKey || event.metaKey) {
1455
+ // CTRL + V (paste)
1456
+ if (86 === event.keyCode) {
1457
+ if ($.wysiwyg.rmFormat) {
1458
+ if ("object" === typeof (self.options.plugins.rmFormat.rmMsWordMarkup)) {
1459
+ $.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: self.options.plugins.rmFormat.rmMsWordMarkup }});
1460
+ } else {
1461
+ $.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: { enabled: true }}});
1462
+ }
1463
+ }
1464
+ }
1465
+ }
1466
+ });
1467
+ }
1468
+
1469
+ if (self.options.autoSave) {
1470
+ $(self.editorDoc).keydown(function () { self.autoSaveFunction(); })
1471
+ .keyup(function () { self.autoSaveFunction(); })
1472
+ .mousedown(function () { self.autoSaveFunction(); })
1473
+ .bind($.support.noCloneEvent ? "input.wysiwyg" : "paste.wysiwyg", function () { self.autoSaveFunction(); });
1474
+ }
1475
+
1476
+ if (self.options.autoGrow) {
1477
+ if (self.options.initialMinHeight !== null) {
1478
+ self.ui.initialHeight = self.options.initialMinHeight;
1479
+ } else {
1480
+ self.ui.initialHeight = $(self.editorDoc).height();
1481
+ }
1482
+ $(self.editorDoc.body).css("border", "1px solid white"); // cancel margin collapsing
1483
+
1484
+ growHandler = function () {
1485
+ self.ui.grow();
1486
+ };
1487
+
1488
+ $(self.editorDoc).keyup(growHandler);
1489
+ $(self.editorDoc).bind("editorRefresh.wysiwyg", growHandler);
1490
+
1491
+ // fix when content height > textarea height
1492
+ self.ui.grow();
1493
+ }
1494
+
1495
+ if (self.options.css) {
1496
+ if (String === self.options.css.constructor) {
1497
+ if ($.browser.msie) {
1498
+ stylesheet = self.editorDoc.createStyleSheet(self.options.css);
1499
+ $(stylesheet).attr({
1500
+ "media": "all"
1501
+ });
1502
+ } else {
1503
+ stylesheet = $("<link/>").attr({
1504
+ "href": self.options.css,
1505
+ "media": "all",
1506
+ "rel": "stylesheet",
1507
+ "type": "text/css"
1508
+ });
1509
+
1510
+ $(self.editorDoc).find("head").append(stylesheet);
1511
+ }
1512
+ } else {
1513
+ self.timers.initFrame_Css = window.setTimeout(function () {
1514
+ $(self.editorDoc.body).css(self.options.css);
1515
+ }, 0);
1516
+ }
1517
+ }
1518
+
1519
+ if (self.initialContent.length === 0) {
1520
+ if ("function" === typeof (self.options.initialContent)) {
1521
+ self.setContent(self.options.initialContent());
1522
+ } else {
1523
+ self.setContent(self.options.initialContent);
1524
+ }
1525
+ }
1526
+
1527
+ if (self.options.maxLength > 0) {
1528
+ $(self.editorDoc).keydown(function (event) {
1529
+ if ($(self.editorDoc).text().length >= self.options.maxLength && $.inArray(event.which, self.validKeyCodes) === -1) {
1530
+ event.preventDefault();
1531
+ }
1532
+ });
1533
+ }
1534
+
1535
+ // Support event callbacks
1536
+ $.each(self.options.events, function (key, handler) {
1537
+ $(self.editorDoc).bind(key + ".wysiwyg", function (event) {
1538
+ // Trigger event handler, providing the event and api to
1539
+ // support additional functionality.
1540
+ handler.apply(self.editorDoc, [event, self]);
1541
+ });
1542
+ });
1543
+
1544
+ // restores selection properly on focus
1545
+ if ($.browser.msie) {
1546
+ // Event chain: beforedeactivate => focusout => blur.
1547
+ // Focusout & blur fired too late to handle internalRange() in dialogs.
1548
+ // When clicked on input boxes both got range = null
1549
+ $(self.editorDoc).bind("beforedeactivate.wysiwyg", function () {
1550
+ self.savedRange = self.getInternalRange();
1551
+ });
1552
+ } else {
1553
+ $(self.editorDoc).bind("blur.wysiwyg", function () {
1554
+ self.savedRange = self.getInternalRange();
1555
+ });
1556
+ }
1557
+
1558
+ $(self.editorDoc.body).addClass("wysiwyg");
1559
+ if (self.options.events && self.options.events.save) {
1560
+ saveHandler = self.options.events.save;
1561
+
1562
+ $(self.editorDoc).bind("keyup.wysiwyg", saveHandler);
1563
+ $(self.editorDoc).bind("change.wysiwyg", saveHandler);
1564
+
1565
+ if ($.support.noCloneEvent) {
1566
+ $(self.editorDoc).bind("input.wysiwyg", saveHandler);
1567
+ } else {
1568
+ $(self.editorDoc).bind("paste.wysiwyg", saveHandler);
1569
+ $(self.editorDoc).bind("cut.wysiwyg", saveHandler);
1570
+ }
1571
+ }
1572
+
1573
+ /**
1574
+ * XHTML5 {@link https://github.com/akzhan/jwysiwyg/issues/152}
1575
+ */
1576
+ if (self.options.xhtml5 && self.options.unicode) {
1577
+ var replacements = {ne:8800,le:8804,para:182,xi:958,darr:8595,nu:957,oacute:243,Uacute:218,omega:969,prime:8242,pound:163,igrave:236,thorn:254,forall:8704,emsp:8195,lowast:8727,brvbar:166,alefsym:8501,nbsp:160,delta:948,clubs:9827,lArr:8656,Omega:937,Auml:196,cedil:184,and:8743,plusmn:177,ge:8805,raquo:187,uml:168,equiv:8801,laquo:171,rdquo:8221,Epsilon:917,divide:247,fnof:402,chi:967,Dagger:8225,iacute:237,rceil:8969,sigma:963,Oslash:216,acute:180,frac34:190,lrm:8206,upsih:978,Scaron:352,part:8706,exist:8707,nabla:8711,image:8465,prop:8733,zwj:8205,omicron:959,aacute:225,Yuml:376,Yacute:221,weierp:8472,rsquo:8217,otimes:8855,kappa:954,thetasym:977,harr:8596,Ouml:214,Iota:921,ograve:242,sdot:8901,copy:169,oplus:8853,acirc:226,sup:8835,zeta:950,Iacute:205,Oacute:211,crarr:8629,Nu:925,bdquo:8222,lsquo:8216,apos:39,Beta:914,eacute:233,egrave:232,lceil:8968,Kappa:922,piv:982,Ccedil:199,ldquo:8220,Xi:926,cent:162,uarr:8593,hellip:8230,Aacute:193,ensp:8194,sect:167,Ugrave:217,aelig:230,ordf:170,curren:164,sbquo:8218,macr:175,Phi:934,Eta:919,rho:961,Omicron:927,sup2:178,euro:8364,aring:229,Theta:920,mdash:8212,uuml:252,otilde:245,eta:951,uacute:250,rArr:8658,nsub:8836,agrave:224,notin:8713,ndash:8211,Psi:936,Ocirc:212,sube:8838,szlig:223,micro:181,not:172,sup1:185,middot:183,iota:953,ecirc:234,lsaquo:8249,thinsp:8201,sum:8721,ntilde:241,scaron:353,cap:8745,atilde:227,lang:10216,__replacement:65533,isin:8712,gamma:947,Euml:203,ang:8736,upsilon:965,Ntilde:209,hearts:9829,Alpha:913,Tau:932,spades:9824,dagger:8224,THORN:222,"int":8747,lambda:955,Eacute:201,Uuml:220,infin:8734,rlm:8207,Aring:197,ugrave:249,Egrave:200,Acirc:194,rsaquo:8250,ETH:208,oslash:248,alpha:945,Ograve:210,Prime:8243,mu:956,ni:8715,real:8476,bull:8226,beta:946,icirc:238,eth:240,prod:8719,larr:8592,ordm:186,perp:8869,Gamma:915,reg:174,ucirc:251,Pi:928,psi:968,tilde:732,asymp:8776,zwnj:8204,Agrave:192,deg:176,AElig:198,times:215,Delta:916,sim:8764,Otilde:213,Mu:924,uArr:8657,circ:710,theta:952,Rho:929,sup3:179,diams:9830,tau:964,Chi:935,frac14:188,oelig:339,shy:173,or:8744,dArr:8659,phi:966,iuml:239,Lambda:923,rfloor:8971,iexcl:161,cong:8773,ccedil:231,Icirc:206,frac12:189,loz:9674,rarr:8594,cup:8746,radic:8730,frasl:8260,euml:235,OElig:338,hArr:8660,Atilde:195,Upsilon:933,there4:8756,ouml:246,oline:8254,Ecirc:202,yacute:253,auml:228,permil:8240,sigmaf:962,iquest:191,empty:8709,pi:960,Ucirc:219,supe:8839,Igrave:204,yen:165,rang:10217,trade:8482,lfloor:8970,minus:8722,Zeta:918,sub:8834,epsilon:949,yuml:255,Sigma:931,Iuml:207,ocirc:244};
1578
+ self.events.bind("getContent", function (text) {
1579
+ return text.replace(/&(?:amp;)?(?!amp|lt|gt|quot)([a-z][a-z0-9]*);/gi, function (str, p1) {
1580
+ if (!replacements[p1]) {
1581
+ p1 = p1.toLowerCase();
1582
+ if (!replacements[p1]) {
1583
+ p1 = "__replacement";
1584
+ }
1585
+ }
1586
+
1587
+ var num = replacements[p1];
1588
+ /* Numeric return if ever wanted: return replacements[p1] ? "&#"+num+";" : ""; */
1589
+ return String.fromCharCode(num);
1590
+ });
1591
+ });
1592
+ }
1593
+ $(self.original).trigger('ready.jwysiwyg', [self.editorDoc, self]);
1594
+ };
1595
+
1596
+ this.innerDocument = function () {
1597
+ var element = this.editor.get(0);
1598
+
1599
+ if (element.nodeName.toLowerCase() === "iframe") {
1600
+ if (element.contentDocument) { // Gecko
1601
+ return element.contentDocument;
1602
+ } else if (element.contentWindow) { // IE
1603
+ return element.contentWindow.document;
1604
+ }
1605
+
1606
+ if (this.isDestroyed) {
1607
+ return null;
1608
+ }
1609
+
1610
+ console.error("Unexpected error in innerDocument");
1611
+
1612
+ /*
1613
+ return ( $.browser.msie )
1614
+ ? document.frames[element.id].document
1615
+ : element.contentWindow.document // contentDocument;
1616
+ */
1617
+ }
1618
+
1619
+ return element;
1620
+ };
1621
+
1622
+ this.insertHtml = function (szHTML) {
1623
+ var img, range;
1624
+
1625
+ if (!szHTML || szHTML.length === 0) {
1626
+ return this;
1627
+ }
1628
+
1629
+ if ($.browser.msie) {
1630
+ this.ui.focus();
1631
+ this.editorDoc.execCommand("insertImage", false, "#jwysiwyg#");
1632
+ img = this.getElementByAttributeValue("img", "src", "#jwysiwyg#");
1633
+ if (img) {
1634
+ $(img).replaceWith(szHTML);
1635
+ }
1636
+ } else {
1637
+ if ($.browser.mozilla) { // @link https://github.com/akzhan/jwysiwyg/issues/50
1638
+ if (1 === $(szHTML).length) {
1639
+ range = this.getInternalRange();
1640
+ range.deleteContents();
1641
+ range.insertNode($(szHTML).get(0));
1642
+ } else {
1643
+ this.editorDoc.execCommand("insertHTML", false, szHTML);
1644
+ }
1645
+ } else {
1646
+ if (!this.editorDoc.execCommand("insertHTML", false, szHTML)) {
1647
+ this.editor.focus();
1648
+ /* :TODO: place caret at the end
1649
+ if (window.getSelection) {
1650
+ } else {
1651
+ }
1652
+ this.editor.focus();
1653
+ */
1654
+ this.editorDoc.execCommand("insertHTML", false, szHTML);
1655
+ }
1656
+ }
1657
+ }
1658
+
1659
+ this.saveContent();
1660
+
1661
+ return this;
1662
+ };
1663
+
1664
+ //check allowed properties
1665
+ this.parseControls = function () {
1666
+ var self = this;
1667
+
1668
+ $.each(this.options.controls, function (controlName, control) {
1669
+ $.each(control, function (propertyName) {
1670
+ if (-1 === $.inArray(propertyName, self.availableControlProperties)) {
1671
+ throw controlName + '["' + propertyName + '"]: property "' + propertyName + '" not exists in Wysiwyg.availableControlProperties';
1672
+ }
1673
+ });
1674
+ });
1675
+
1676
+ if (this.options.parseControls) { //user callback
1677
+ return this.options.parseControls.call(this);
1678
+ }
1679
+
1680
+ return this.options.controls;
1681
+ };
1682
+
1683
+ this.removeFormat = function () {
1684
+ if ($.browser.msie) {
1685
+ this.ui.focus();
1686
+ }
1687
+
1688
+ if (this.options.removeHeadings) {
1689
+ this.editorDoc.execCommand("formatBlock", false, "<p>"); // remove headings
1690
+ }
1691
+
1692
+ this.editorDoc.execCommand("removeFormat", false, null);
1693
+ this.editorDoc.execCommand("unlink", false, null);
1694
+
1695
+ if ($.wysiwyg.rmFormat && $.wysiwyg.rmFormat.enabled) {
1696
+ if ("object" === typeof (this.options.plugins.rmFormat.rmMsWordMarkup)) {
1697
+ $.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: this.options.plugins.rmFormat.rmMsWordMarkup }});
1698
+ } else {
1699
+ $.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: { enabled: true }}});
1700
+ }
1701
+ }
1702
+
1703
+ return this;
1704
+ };
1705
+
1706
+ this.ui.removeHoverClass = function () {
1707
+ $(this).removeClass("wysiwyg-button-hover");
1708
+ };
1709
+
1710
+ this.resetFunction = function () {
1711
+ this.setContent(this.initialContent);
1712
+ };
1713
+
1714
+ this.saveContent = function () {
1715
+ if (this.viewHTML)
1716
+ {
1717
+ return; // no need
1718
+ }
1719
+ if (this.original) {
1720
+ var content, newContent;
1721
+
1722
+ content = this.getContent();
1723
+
1724
+ if (this.options.rmUnwantedBr) {
1725
+ content = content.replace(/<br\/?>$/, "");
1726
+ }
1727
+
1728
+ if (this.options.replaceDivWithP) {
1729
+ newContent = $("<div/>").addClass("temp").append(content);
1730
+
1731
+ newContent.children("div").each(function () {
1732
+ var element = $(this), p = element.find("p"), i;
1733
+
1734
+ if (0 === p.length) {
1735
+ p = $("<p></p>");
1736
+
1737
+ if (this.attributes.length > 0) {
1738
+ for (i = 0; i < this.attributes.length; i += 1) {
1739
+ p.attr(this.attributes[i].name, element.attr(this.attributes[i].name));
1740
+ }
1741
+ }
1742
+
1743
+ p.append(element.html());
1744
+
1745
+ element.replaceWith(p);
1746
+ }
1747
+ });
1748
+
1749
+ content = newContent.html();
1750
+ }
1751
+
1752
+ $(this.original).val(content);
1753
+
1754
+ if (this.options.events && this.options.events.save) {
1755
+ this.options.events.save.call(this);
1756
+ }
1757
+ }
1758
+
1759
+ return this;
1760
+ };
1761
+
1762
+ this.setContent = function (newContent) {
1763
+ this.editorDoc.body.innerHTML = newContent;
1764
+ this.saveContent();
1765
+
1766
+ return this;
1767
+ };
1768
+
1769
+ this.triggerControl = function (name, control) {
1770
+ var cmd = control.command || name, //command directly for designMode=on iframe (this.editorDoc)
1771
+ args = control["arguments"] || [];
1772
+
1773
+ if (control.exec) {
1774
+ control.exec.apply(this); //custom exec function in control, allows DOM changing
1775
+ } else {
1776
+ this.ui.focus();
1777
+ this.ui.withoutCss(); //disable style="" attr inserting in mozzila's designMode
1778
+ // when click <Cut>, <Copy> or <Paste> got "Access to XPConnect service denied" code: "1011"
1779
+ // in Firefox untrusted JavaScript is not allowed to access the clipboard
1780
+ try {
1781
+ this.editorDoc.execCommand(cmd, false, args);
1782
+ } catch (e) {
1783
+ console.error(e);
1784
+ }
1785
+ }
1786
+
1787
+ if (this.options.autoSave) {
1788
+ this.autoSaveFunction();
1789
+ }
1790
+ };
1791
+
1792
+ this.triggerControlCallback = function (name) {
1793
+ $(window).trigger("trigger-" + name + ".wysiwyg", [this]);
1794
+ };
1795
+
1796
+ this.ui.withoutCss = function () {
1797
+ var self = this.self;
1798
+
1799
+ if ($.browser.mozilla) {
1800
+ try {
1801
+ self.editorDoc.execCommand("styleWithCSS", false, false);
1802
+ } catch (e) {
1803
+ try {
1804
+ self.editorDoc.execCommand("useCSS", false, true);
1805
+ } catch (e2) {
1806
+ }
1807
+ }
1808
+ }
1809
+
1810
+ return self;
1811
+ };
1812
+
1813
+ this.wrapInitialContent = function () {
1814
+ var content = this.initialContent,
1815
+ found = content.match(/<\/?p>/gi);
1816
+
1817
+ if (!found) {
1818
+ return "<p>" + content + "</p>";
1819
+ } else {
1820
+ // :TODO: checking/replacing
1821
+ }
1822
+
1823
+ return content;
1824
+ };
1825
+ }
1826
+
1827
+ /*
1828
+ * Wysiwyg namespace: public properties and methods
1829
+ */
1830
+ $.wysiwyg = {
1831
+ messages: {
1832
+ noObject: "Something goes wrong, check object"
1833
+ },
1834
+
1835
+ /**
1836
+ * Custom control support by Alec Gorge ( http://github.com/alecgorge )
1837
+ */
1838
+ addControl: function (object, name, settings) {
1839
+ return object.each(function () {
1840
+ var oWysiwyg = $(this).data("wysiwyg"),
1841
+ customControl = {},
1842
+ toolbar;
1843
+
1844
+ if (!oWysiwyg) {
1845
+ return this;
1846
+ }
1847
+
1848
+ customControl[name] = $.extend(true, {visible: true, custom: true}, settings);
1849
+ $.extend(true, oWysiwyg.options.controls, customControl);
1850
+
1851
+ // render new toolbar
1852
+ toolbar = $(oWysiwyg.options.toolbarHtml);
1853
+ oWysiwyg.ui.toolbar.replaceWith(toolbar);
1854
+ oWysiwyg.ui.toolbar = toolbar;
1855
+ oWysiwyg.ui.appendControls();
1856
+ });
1857
+ },
1858
+
1859
+ clear: function (object) {
1860
+ return object.each(function () {
1861
+ var oWysiwyg = $(this).data("wysiwyg");
1862
+
1863
+ if (!oWysiwyg) {
1864
+ return this;
1865
+ }
1866
+
1867
+ oWysiwyg.setContent("");
1868
+ });
1869
+ },
1870
+
1871
+ console: console, // let our console be available for extensions
1872
+
1873
+ destroy: function (object) {
1874
+ return object.each(function () {
1875
+ var oWysiwyg = $(this).data("wysiwyg");
1876
+
1877
+ if (!oWysiwyg) {
1878
+ return this;
1879
+ }
1880
+
1881
+ oWysiwyg.destroy();
1882
+ });
1883
+ },
1884
+
1885
+ "document": function (object) {
1886
+ // no chains because of return
1887
+ var oWysiwyg = object.data("wysiwyg");
1888
+
1889
+ if (!oWysiwyg) {
1890
+ return undefined;
1891
+ }
1892
+
1893
+ return $(oWysiwyg.editorDoc);
1894
+ },
1895
+
1896
+ getContent: function (object) {
1897
+ // no chains because of return
1898
+ var oWysiwyg = object.data("wysiwyg");
1899
+
1900
+ if (!oWysiwyg) {
1901
+ return undefined;
1902
+ }
1903
+
1904
+ return oWysiwyg.getContent();
1905
+ },
1906
+
1907
+ init: function (object, options) {
1908
+ return object.each(function () {
1909
+ var opts = $.extend(true, {}, options),
1910
+ obj;
1911
+
1912
+ // :4fun:
1913
+ // remove this textarea validation and change line in this.saveContent function
1914
+ // $(this.original).val(content); to $(this.original).html(content);
1915
+ // now you can make WYSIWYG editor on h1, p, and many more tags
1916
+ if (("textarea" !== this.nodeName.toLowerCase()) || $(this).data("wysiwyg")) {
1917
+ return;
1918
+ }
1919
+
1920
+ obj = new Wysiwyg();
1921
+ obj.init(this, opts);
1922
+ $.data(this, "wysiwyg", obj);
1923
+
1924
+ $(obj.editorDoc).trigger("afterInit.wysiwyg");
1925
+ });
1926
+ },
1927
+
1928
+ insertHtml: function (object, szHTML) {
1929
+ return object.each(function () {
1930
+ var oWysiwyg = $(this).data("wysiwyg");
1931
+
1932
+ if (!oWysiwyg) {
1933
+ return this;
1934
+ }
1935
+
1936
+ oWysiwyg.insertHtml(szHTML);
1937
+ });
1938
+ },
1939
+
1940
+ plugin: {
1941
+ listeners: {},
1942
+
1943
+ bind: function (Wysiwyg) {
1944
+ var self = this;
1945
+
1946
+ $.each(this.listeners, function (action, handlers) {
1947
+ var i, plugin;
1948
+
1949
+ for (i = 0; i < handlers.length; i += 1) {
1950
+ plugin = self.parseName(handlers[i]);
1951
+
1952
+ $(Wysiwyg.editorDoc).bind(action + ".wysiwyg", {plugin: plugin}, function (event) {
1953
+ $.wysiwyg[event.data.plugin.name][event.data.plugin.method].apply($.wysiwyg[event.data.plugin.name], [Wysiwyg]);
1954
+ });
1955
+ }
1956
+ });
1957
+ },
1958
+
1959
+ exists: function (name) {
1960
+ var plugin;
1961
+
1962
+ if ("string" !== typeof (name)) {
1963
+ return false;
1964
+ }
1965
+
1966
+ plugin = this.parseName(name);
1967
+
1968
+ if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
1969
+ return false;
1970
+ }
1971
+
1972
+ return true;
1973
+ },
1974
+
1975
+ listen: function (action, handler) {
1976
+ var plugin;
1977
+
1978
+ plugin = this.parseName(handler);
1979
+
1980
+ if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
1981
+ return false;
1982
+ }
1983
+
1984
+ if (!this.listeners[action]) {
1985
+ this.listeners[action] = [];
1986
+ }
1987
+
1988
+ this.listeners[action].push(handler);
1989
+
1990
+ return true;
1991
+ },
1992
+
1993
+ parseName: function (name) {
1994
+ var elements;
1995
+
1996
+ if ("string" !== typeof (name)) {
1997
+ return false;
1998
+ }
1999
+
2000
+ elements = name.split(".");
2001
+
2002
+ if (2 > elements.length) {
2003
+ return false;
2004
+ }
2005
+
2006
+ return {name: elements[0], method: elements[1]};
2007
+ },
2008
+
2009
+ register: function (data) {
2010
+ if (!data.name) {
2011
+ console.error("Plugin name missing");
2012
+ }
2013
+
2014
+ $.each($.wysiwyg, function (pluginName) {
2015
+ if (pluginName === data.name) {
2016
+ console.error("Plugin with name '" + data.name + "' was already registered");
2017
+ }
2018
+ });
2019
+
2020
+ $.wysiwyg[data.name] = data;
2021
+
2022
+ return true;
2023
+ }
2024
+ },
2025
+
2026
+ removeFormat: function (object) {
2027
+ return object.each(function () {
2028
+ var oWysiwyg = $(this).data("wysiwyg");
2029
+
2030
+ if (!oWysiwyg) {
2031
+ return this;
2032
+ }
2033
+
2034
+ oWysiwyg.removeFormat();
2035
+ });
2036
+ },
2037
+
2038
+ save: function (object) {
2039
+ return object.each(function () {
2040
+ var oWysiwyg = $(this).data("wysiwyg");
2041
+
2042
+ if (!oWysiwyg) {
2043
+ return this;
2044
+ }
2045
+
2046
+ oWysiwyg.saveContent();
2047
+ });
2048
+ },
2049
+
2050
+ selectAll: function (object) {
2051
+ var oWysiwyg = object.data("wysiwyg"), oBody, oRange, selection;
2052
+
2053
+ if (!oWysiwyg) {
2054
+ return this;
2055
+ }
2056
+
2057
+ oBody = oWysiwyg.editorDoc.body;
2058
+ if (window.getSelection) {
2059
+ selection = oWysiwyg.getInternalSelection();
2060
+ selection.selectAllChildren(oBody);
2061
+ } else {
2062
+ oRange = oBody.createTextRange();
2063
+ oRange.moveToElementText(oBody);
2064
+ oRange.select();
2065
+ }
2066
+ },
2067
+
2068
+ setContent: function (object, newContent) {
2069
+ return object.each(function () {
2070
+ var oWysiwyg = $(this).data("wysiwyg");
2071
+
2072
+ if (!oWysiwyg) {
2073
+ return this;
2074
+ }
2075
+
2076
+ oWysiwyg.setContent(newContent);
2077
+ });
2078
+ },
2079
+
2080
+ triggerControl: function (object, controlName) {
2081
+ return object.each(function () {
2082
+ var oWysiwyg = $(this).data("wysiwyg");
2083
+
2084
+ if (!oWysiwyg) {
2085
+ return this;
2086
+ }
2087
+
2088
+ if (!oWysiwyg.controls[controlName]) {
2089
+ console.error("Control '" + controlName + "' not exists");
2090
+ }
2091
+
2092
+ oWysiwyg.triggerControl.apply(oWysiwyg, [controlName, oWysiwyg.controls[controlName]]);
2093
+ });
2094
+ },
2095
+
2096
+ support: {
2097
+ prop: supportsProp
2098
+ },
2099
+
2100
+ utils: {
2101
+ extraSafeEntities: [["<", ">", "'", '"', " "], [32]],
2102
+
2103
+ encodeEntities: function (str) {
2104
+ var self = this, aStr, aRet = [];
2105
+
2106
+ if (this.extraSafeEntities[1].length === 0) {
2107
+ $.each(this.extraSafeEntities[0], function (i, ch) {
2108
+ self.extraSafeEntities[1].push(ch.charCodeAt(0));
2109
+ });
2110
+ }
2111
+ aStr = str.split("");
2112
+ $.each(aStr, function (i) {
2113
+ var iC = aStr[i].charCodeAt(0);
2114
+ if ($.inArray(iC, self.extraSafeEntities[1]) && (iC < 65 || iC > 127 || (iC > 90 && iC < 97))) {
2115
+ aRet.push('&#' + iC + ';');
2116
+ } else {
2117
+ aRet.push(aStr[i]);
2118
+ }
2119
+ });
2120
+
2121
+ return aRet.join('');
2122
+ }
2123
+ }
2124
+ };
2125
+
2126
+ /**
2127
+ * Unifies dialog methods to allow custom implementations
2128
+ *
2129
+ * Events:
2130
+ * * afterOpen
2131
+ * * beforeShow
2132
+ * * afterShow
2133
+ * * beforeHide
2134
+ * * afterHide
2135
+ * * beforeClose
2136
+ * * afterClose
2137
+ *
2138
+ * Example:
2139
+ * var dialog = new ($.wysiwyg.dialog)($('#idToTextArea').data('wysiwyg'), {"title": "Test", "content": "form data, etc."});
2140
+ *
2141
+ * dialog.bind("afterOpen", function () { alert('you should see a dialog behind this one!'); });
2142
+ *
2143
+ * dialog.open();
2144
+ *
2145
+ *
2146
+ */
2147
+ $.wysiwyg.dialog = function (jWysiwyg, opts) {
2148
+
2149
+ var theme = (jWysiwyg && jWysiwyg.options && jWysiwyg.options.dialog) ? jWysiwyg.options.dialog : (opts.theme ? opts.theme : "default"),
2150
+ obj = new $.wysiwyg.dialog.createDialog(theme),
2151
+ that = this,
2152
+ $that = $(that);
2153
+
2154
+ this.options = {
2155
+ "modal": true,
2156
+ "draggable": true,
2157
+ "title": "Title",
2158
+ "content": "Content",
2159
+ "width": "auto",
2160
+ "height": "auto",
2161
+ "zIndex": 2000,
2162
+ "open": false,
2163
+ "close": false
2164
+ };
2165
+
2166
+ this.isOpen = false;
2167
+
2168
+ $.extend(this.options, opts);
2169
+
2170
+ this.object = obj;
2171
+
2172
+ // Opens a dialog with the specified content
2173
+ this.open = function () {
2174
+ this.isOpen = true;
2175
+
2176
+ obj.init.apply(that, []);
2177
+ var $dialog = obj.show.apply(that, []);
2178
+
2179
+ $that.trigger("afterOpen", [$dialog]);
2180
+
2181
+ };
2182
+
2183
+ this.show = function () {
2184
+ this.isOpen = true;
2185
+
2186
+ $that.trigger("beforeShow");
2187
+
2188
+ var $dialog = obj.show.apply(that, []);
2189
+
2190
+ $that.trigger("afterShow");
2191
+ };
2192
+
2193
+ this.hide = function () {
2194
+ this.isOpen = false;
2195
+
2196
+ $that.trigger("beforeHide");
2197
+
2198
+ var $dialog = obj.hide.apply(that, []);
2199
+
2200
+ $that.trigger("afterHide", [$dialog]);
2201
+ };
2202
+
2203
+ // Closes the dialog window.
2204
+ this.close = function () {
2205
+ this.isOpen = false;
2206
+
2207
+ var $dialog = obj.hide.apply(that, []);
2208
+
2209
+ $that.trigger("beforeClose", [$dialog]);
2210
+
2211
+ obj.destroy.apply(that, []);
2212
+
2213
+ $that.trigger("afterClose", [$dialog]);
2214
+
2215
+ };
2216
+
2217
+ if (this.options.open) {
2218
+ $that.bind("afterOpen", this.options.open);
2219
+ }
2220
+ if (this.options.close) {
2221
+ $that.bind("afterClose", this.options.close);
2222
+ }
2223
+
2224
+ return this;
2225
+ };
2226
+
2227
+ // "Static" Dialog methods.
2228
+ $.extend(true, $.wysiwyg.dialog, {
2229
+ _themes : {}, // sample {"Theme Name": object}
2230
+ _theme : "", // the current theme
2231
+
2232
+ register : function(name, obj) {
2233
+ $.wysiwyg.dialog._themes[name] = obj;
2234
+ },
2235
+
2236
+ deregister : function (name) {
2237
+ delete $.wysiwyg.dialog._themes[name];
2238
+ },
2239
+
2240
+ createDialog : function (name) {
2241
+ return new ($.wysiwyg.dialog._themes[name]);
2242
+ },
2243
+
2244
+ getDimensions : function () {
2245
+ var width = document.body.scrollWidth,
2246
+ height = document.body.scrollHeight;
2247
+
2248
+ if ($.browser.opera) {
2249
+ height = Math.max(
2250
+ $(document).height(),
2251
+ $(window).height(),
2252
+ document.documentElement.clientHeight);
2253
+ }
2254
+
2255
+ return [width, height];
2256
+ }
2257
+ });
2258
+
2259
+ $(function () { // need access to jQuery UI stuff.
2260
+ if (jQuery.ui) {
2261
+ $.wysiwyg.dialog.register("jqueryui", function () {
2262
+ var that = this;
2263
+
2264
+ this._$dialog = null;
2265
+
2266
+ this.init = function() {
2267
+ var abstractDialog = this,
2268
+ content = this.options.content;
2269
+
2270
+ if (typeof content === 'object') {
2271
+ if (typeof content.html === 'function') {
2272
+ content = content.html();
2273
+ } else if(typeof content.toString === 'function') {
2274
+ content = content.toString();
2275
+ }
2276
+ }
2277
+
2278
+ that._$dialog = $('<div></div>').attr('title', this.options.title).html(content);
2279
+
2280
+ var dialogHeight = this.options.height == 'auto' ? 300 : this.options.height,
2281
+ dialogWidth = this.options.width == 'auto' ? 450 : this.options.width;
2282
+
2283
+ // console.log(that._$dialog);
2284
+
2285
+ that._$dialog.dialog({
2286
+ modal: this.options.modal,
2287
+ draggable: this.options.draggable,
2288
+ height: dialogHeight,
2289
+ width: dialogWidth
2290
+ });
2291
+
2292
+ return that._$dialog;
2293
+ };
2294
+
2295
+ this.show = function () {
2296
+ that._$dialog.dialog("open");
2297
+ return that._$dialog;
2298
+ };
2299
+
2300
+ this.hide = function () {
2301
+ that._$dialog.dialog("close");
2302
+ return that._$dialog;
2303
+ };
2304
+
2305
+ this.destroy = function() {
2306
+ that._$dialog.dialog("destroy");
2307
+ return that._$dialog;
2308
+ };
2309
+ });
2310
+ }
2311
+
2312
+ $.wysiwyg.dialog.register("default", function () {
2313
+ var that = this;
2314
+
2315
+ this._$dialog = null;
2316
+
2317
+ this.init = function() {
2318
+ var abstractDialog = this,
2319
+ content = this.options.content;
2320
+
2321
+ if (typeof content === 'object') {
2322
+ if(typeof content.html === 'function') {
2323
+ content = content.html();
2324
+ }
2325
+ else if(typeof content.toString === 'function') {
2326
+ content = content.toString();
2327
+ }
2328
+ }
2329
+
2330
+ that._$dialog = $('<div class="wysiwyg-dialog"></div>').css({"z-index": this.options.zIndex});
2331
+
2332
+ var $topbar = $('<div class="wysiwyg-dialog-topbar"><div class="wysiwyg-dialog-close-wrapper"></div><div class="wysiwyg-dialog-title">'+this.options.title+'</div></div>');
2333
+ var $link = $('<a href="#" class="wysiwyg-dialog-close-button">X</a>');
2334
+
2335
+ $link.click(function () {
2336
+ abstractDialog.close(); // this is important it makes sure that is close from the abstract $.wysiwyg.dialog instace, not just locally
2337
+ });
2338
+
2339
+ $topbar.find('.wysiwyg-dialog-close-wrapper').prepend($link);
2340
+
2341
+ var $dcontent = $('<div class="wysiwyg-dialog-content">'+content+'</div>');
2342
+
2343
+ that._$dialog.append($topbar).append($dcontent);
2344
+
2345
+ // Set dialog's height & width, and position it correctly:
2346
+ var dialogHeight = this.options.height == 'auto' ? 300 : this.options.height,
2347
+ dialogWidth = this.options.width == 'auto' ? 450 : this.options.width;
2348
+ that._$dialog.hide().css({
2349
+ "width": dialogWidth,
2350
+ "height": dialogHeight,
2351
+ "left": (($(window).width() - dialogWidth) / 2),
2352
+ "top": (($(window).height() - dialogHeight) / 3)
2353
+ });
2354
+
2355
+ $("body").append(that._$dialog);
2356
+
2357
+ return that._$dialog;
2358
+ };
2359
+
2360
+ this.show = function () {
2361
+
2362
+ // Modal feature:
2363
+ if (this.options.modal) {
2364
+ var dimensions = $.wysiwyg.dialog.getDimensions(),
2365
+ wrapper = $('<div class="wysiwyg-dialog-modal-div"></div>')
2366
+ .css({"width": dimensions[0]});
2367
+ that._$dialog.wrap(wrapper);
2368
+ }
2369
+
2370
+ // Draggable feature:
2371
+ if (this.options.draggable) {
2372
+
2373
+ var mouseDown = false;
2374
+
2375
+ that._$dialog.find("div.wysiwyg-dialog-topbar").bind("mousedown", function (e) {
2376
+ e.preventDefault();
2377
+ $(this).css({ "cursor": "move" });
2378
+ var $topbar = $(this),
2379
+ _dialog = $(this).parents(".wysiwyg-dialog"),
2380
+ offsetX = (e.pageX - parseInt(_dialog.css("left"), 10)),
2381
+ offsetY = (e.pageY - parseInt(_dialog.css("top"), 10));
2382
+ mouseDown = true;
2383
+ $(this).css({ "cursor": "move" });
2384
+
2385
+ $(document).bind("mousemove", function (e) {
2386
+ e.preventDefault();
2387
+ if (mouseDown) {
2388
+ _dialog.css({
2389
+ "top": (e.pageY - offsetY),
2390
+ "left": (e.pageX - offsetX)
2391
+ });
2392
+ }
2393
+ }).bind("mouseup", function (e) {
2394
+ e.preventDefault();
2395
+ mouseDown = false;
2396
+ $topbar.css({ "cursor": "auto" });
2397
+ $(document).unbind("mousemove").unbind("mouseup");
2398
+ });
2399
+
2400
+ });
2401
+ }
2402
+
2403
+ that._$dialog.show();
2404
+ return that._$dialog;
2405
+
2406
+ };
2407
+
2408
+ this.hide = function () {
2409
+ that._$dialog.hide();
2410
+ return that._$dialog;
2411
+ };
2412
+
2413
+ this.destroy = function() {
2414
+
2415
+ // Modal feature:
2416
+ if (this.options.modal) {
2417
+ that._$dialog.unwrap();
2418
+ }
2419
+
2420
+ // Draggable feature:
2421
+ if (this.options.draggable) {
2422
+ that._$dialog.find("div.wysiwyg-dialog-topbar").unbind("mousedown");
2423
+ }
2424
+
2425
+ that._$dialog.remove();
2426
+ return that._$dialog;
2427
+ };
2428
+ });
2429
+ });
2430
+ // end Dialog
2431
+
2432
+ $.fn.wysiwyg = function (method) {
2433
+ var args = arguments, plugin;
2434
+
2435
+ if ("undefined" !== typeof $.wysiwyg[method]) {
2436
+ // set argument object to undefined
2437
+ args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
2438
+ return $.wysiwyg[method].apply($.wysiwyg, Array.prototype.slice.call(args, 1));
2439
+ } else if ("object" === typeof method || !method) {
2440
+ Array.prototype.unshift.call(args, this);
2441
+ return $.wysiwyg.init.apply($.wysiwyg, args);
2442
+ } else if ($.wysiwyg.plugin.exists(method)) {
2443
+ plugin = $.wysiwyg.plugin.parseName(method);
2444
+ args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
2445
+ return $.wysiwyg[plugin.name][plugin.method].apply($.wysiwyg[plugin.name], Array.prototype.slice.call(args, 1));
2446
+ } else {
2447
+ console.error("Method '" + method + "' does not exist on jQuery.wysiwyg.\nTry to include some extra controls or plugins");
2448
+ }
2449
+ };
2450
+
2451
+ $.fn.getWysiwyg = function () {
2452
+ return this.data("wysiwyg");
2453
+ };
2454
+ })(jQuery);