PageSourceSearch

https://www.socialmotus.com/js/editor.js

js socialmotus.com collected 2026-10-04 21:14:21 UTC 81,053 bytes, 2,932 lines download raw bytes

1$(document).ready(function () {
2
3	/**
4	 * WYSIWYG - jQuery plugin 0.97
5	 * (0.97.2 - From infinity)
6	 *
7	 * Copyright (c) 2008-2009 Juan M Martinez, 2010-2011 Akzhan Abdulin and all contributors
8	 * https://github.com/akzhan/jwysiwyg
9	 *
10	 * Dual licensed under the MIT and GPL licenses:
11	 *   http://www.opensource.org/licenses/mit-license.php
12	 *   http://www.gnu.org/licenses/gpl.html
13	 *
14	 */
15	
16	/*jslint browser: true, forin: true */
17
18	(function ($) {
19		"use strict";
20		/* Wysiwyg namespace: private properties and methods */
21	
22		var console = window.console ? window.console : {
23			log: $.noop,
24			error: function (msg) {
25				$.error(msg);
26			}
27		};
28		var supportsProp = (('prop' in $.fn) && ('removeProp' in $.fn));
29	
30		function Wysiwyg() {
31			this.controls = {
32				bold: {
33					groupIndex: 0,
34					visible: false,
35					tags: ["b", "strong"],
36					css: {
37						fontWeight: "bold"
38					},
39					tooltip: "Bold",
40					hotkey: {"ctrl": 1, "key": 66}
41				},
42	
43				copy: {
44					groupIndex: 8,
45					visible: false,
46					tooltip: "Copy"
47				},
48	
49				createLink: {
50					groupIndex: 6,
51					visible: false,
52					exec: function () {
53						var self = this;
54						if ($.wysiwyg.controls && $.wysiwyg.controls.link) {
55							$.wysiwyg.controls.link.init(this);
56						} else if ($.wysiwyg.autoload) {
57							$.wysiwyg.autoload.control("wysiwyg.link.js", function () {
58								self.controls.createLink.exec.apply(self);
59							});
60						} else {
61							console.error("$.wysiwyg.controls.link not defined. You need to include wysiwyg.link.js file");
62						}
63					},
64					tags: ["a"],
65					tooltip: "Create link"
66				},
67	
68				cut: {
69					groupIndex: 8,
70					visible: false,
71					tooltip: "Cut"
72				},
73	
74				decreaseFontSize: {
75					groupIndex: 9,
76					visible: false,
77					tags: ["small"],
78					tooltip: "Decrease font size",
79					exec: function () {
80						this.decreaseFontSize();
81					}
82				},
83	
84				h1: {
85					groupIndex: 7,
86					visible: false,
87					className: "h1",
88					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
89					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h1>" : "h1",
90					tags: ["h1"],
91					tooltip: "Header 1"
92				},
93	
94				h2: {
95					groupIndex: 7,
96					visible: false,
97					className: "h2",
98					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
99					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h2>" : "h2",
100					tags: ["h2"],
101					tooltip: "Header 2"
102				},
103	
104				h3: {
105					groupIndex: 7,
106					visible: false,
107					className: "h3",
108					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
109					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h3>" : "h3",
110					tags: ["h3"],
111					tooltip: "Header 3"
112				},
113				h4: {
114					groupIndex: 7,
115					visible: false,
116					className: "h4",
117					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
118					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h4>" : "h4",
119					tags: ["h4"],
120					tooltip: "Header 4"
121				},
122	
123				h5: {
124					groupIndex: 7,
125					visible: false,
126					className: "h5",
127					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
128					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h5>" : "h5",
129					tags: ["h5"],
130					tooltip: "Header 5"
131				},
132	
133				h6: {
134					groupIndex: 7,
135					visible: false,
136					className: "h6",
137					command: ($.browser.msie || $.browser.safari || $.browser.opera) ? "FormatBlock" : "heading",
138					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<h6>" : "h6",
139					tags: ["h6"],
140					tooltip: "Header 6"
141				},
142	
143				highlight: {
144					tooltip:     "Highlight",
145					className:   "highlight",
146					groupIndex:  1,
147					visible:     false,
148					css: {
149						backgroundColor: "rgb(255, 255, 102)"
150					},
151					exec: function () {
152						var command, node, selection, args;
153	
154						if ($.browser.msie || $.browser.safari) {
155							command = "backcolor";
156						} else {
157							command = "hilitecolor";
158						}
159	
160						if ($.browser.msie) {
161							node = this.getInternalRange().parentElement();
162						} else {
163							selection = this.getInternalSelection();
164							node = selection.extentNode || selection.focusNode;
165	
166							while (node.style === undefined) {
167								node = node.parentNode;
168								if (node.tagName && node.tagName.toLowerCase() === "body") {
169									return;
170								}
171							}
172						}
173	
174						if (node.style.backgroundColor === "rgb(255, 255, 102)" ||
175								node.style.backgroundColor === "#ffff66") {
176							args = "#ffffff";
177						} else {
178							args = "#ffff66";
179						}
180	
181						this.editorDoc.execCommand(command, false, args);
182					}
183				},
184	
185				html: {
186					groupIndex: 10,
187					visible: false,
188					exec: function () {
189						var elementHeight;
190	
191						if (this.options.resizeOptions && $.fn.resizable) {
192							elementHeight = this.element.height();
193						}
194	
195						if (this.viewHTML) {
196							this.setContent(this.original.value);
197	
198							$(this.original).hide();
199							this.editor.show();
200	
201							if (this.options.resizeOptions && $.fn.resizable) {
202								// if element.height still the same after frame was shown
203								if (elementHeight === this.element.height()) {
204									this.element.height(elementHeight + this.editor.height());
205								}
206	
207								this.element.resizable($.extend(true, {
208									alsoResize: this.editor
209								}, this.options.resizeOptions));
210							}
211							
212							this.ui.toolbar.find("li").each(function () {
213								var li = $(this);
214	
215								if (li.hasClass("html")) {
216									li.removeClass("active");
217								} else {
218									li.removeClass('disabled');
219								}
220							});
221						} else {
222							this.saveContent();
223	
224							$(this.original).css({
225								width:	this.element.outerWidth() - 6,
226								height: this.element.height() - this.ui.toolbar.height() - 6,
227								resize: "none"
228							}).show();
229							this.editor.hide();
230							
231							if (this.options.resizeOptions && $.fn.resizable) {
232								// if element.height still the same after frame was hidden
233								if (elementHeight === this.element.height()) {
234									this.element.height(this.ui.toolbar.height());
235								}
236	
237								this.element.resizable("destroy");
238							}
239	
240							this.ui.toolbar.find("li").each(function () {
241								var li = $(this);
242	
243								if (li.hasClass("html")) {
244									li.addClass("active");
245								} else {
246									if (false === li.hasClass("fullscreen")) {
247										li.removeClass("active").addClass('disabled');
248									}
249								}
250							});
251						}
252	
253						this.viewHTML = !(this.viewHTML);
254					},
255					tooltip: "View source code"
256				},
257	
258				increaseFontSize: {
259					groupIndex: 9,
260					visible: false,
261					tags: ["big"],
262					tooltip: "Increase font size",
263					exec: function () {
264						this.increaseFontSize();
265					}
266				},
267	
268				insertImage: {
269					groupIndex: 6,
270					visible: false,
271					exec: function () {
272						var self = this;
273	
274						if ($.wysiwyg.controls && $.wysiwyg.controls.image) {
275							$.wysiwyg.controls.image.init(this);
276						} else if ($.wysiwyg.autoload) {
277							$.wysiwyg.autoload.control("wysiwyg.image.js", function () {
278								self.controls.insertImage.exec.apply(self);
279							});
280						} else {
281							console.error("$.wysiwyg.controls.image not defined. You need to include wysiwyg.image.js file");
282						}
283					},
284					tags: ["img"],
285					tooltip: "Insert image"
286				},
287	
288				insertOrderedList: {
289					groupIndex: 5,
290					visible: false,
291					tags: ["ol"],
292					tooltip: "Insert Ordered List"
293				},
294	
295				insertTable: {
296					groupIndex: 6,
297					visible: false,
298					exec: function () {
299						var self = this;
300	
301						if ($.wysiwyg.controls && $.wysiwyg.controls.table) {
302							$.wysiwyg.controls.table(this);
303						} else if ($.wysiwyg.autoload) {
304							$.wysiwyg.autoload.control("wysiwyg.table.js", function () {
305								self.controls.insertTable.exec.apply(self);
306							});
307						} else {
308							console.error("$.wysiwyg.controls.table not defined. You need to include wysiwyg.table.js file");
309						}
310					},
311					tags: ["table"],
312					tooltip: "Insert table"
313				},
314	
315				insertUnorderedList: {
316					groupIndex: 5,
317					visible: false,
318					tags: ["ul"],
319					tooltip: "Insert Unordered List"
320				},
321	
322				italic: {
323					groupIndex: 0,
324					visible: false,
325					tags: ["i", "em"],
326					css: {
327						fontStyle: "italic"
328					},
329					tooltip: "Italic",
330					hotkey: {"ctrl": 1, "key": 73}
331				},
332	
333				justifyLeft: {
334					visible: false,
335					groupIndex: 1,
336					css: {
337						textAlign: "left"
338					},
339					tooltip: "Justify Left"
340				},
341	
342				justifyCenter: {
343					groupIndex: 1,
344					visible: false,
345					tags: ["center"],
346					css: {
347						textAlign: "center"
348					},
349					tooltip: "Justify Center"
350				},
351	
352				justifyRight: {
353					groupIndex: 1,
354					visible: false,
355					css: {
356						textAlign: "right"
357					},
358					tooltip: "Justify Right"
359				},
360	
361				justifyFull: {
362					groupIndex: 1,
363					visible: false,
364					css: {
365						textAlign: "justify"
366					},
367					tooltip: "Justify Full"
368				},
369	
370				ltr: {
371					groupIndex: 9,
372					visible: false,
373					exec: function () {
374						var p = this.dom.getElement("p");
375	
376						if (!p) {
377							return false;
378						}
379	
380						$(p).attr("dir", "ltr");
381						return true;
382					},
383					tooltip : "Left to Right"
384				},
385	
386				rtl: {
387					groupIndex: 9,
388					visible: false,
389					exec: function () {
390						var p = this.dom.getElement("p");
391	
392						if (!p) {
393							return false;
394						}
395	
396						$(p).attr("dir", "rtl");
397						return true;
398					},
399					tooltip : "Right to Left"
400				},
401				
402				indent: {
403					groupIndex: 2,
404					visible: false,
405					tooltip: "Indent"
406				},
407	
408				outdent: {
409					groupIndex: 2,
410					visible: false,
411					tooltip: "Outdent"
412				},
413	
414				insertHorizontalRule: {
415					groupIndex: 5,
416					visible: false,
417					tags: ["hr"],
418					tooltip: "Insert Horizontal Rule"
419				},
420	
421				paragraph: {
422					groupIndex: 7,
423					visible: false,
424					className: "paragraph",
425					command: "FormatBlock",
426					"arguments": ($.browser.msie || $.browser.safari || $.browser.opera) ? "<p>" : "p",
427					tags: ["p"],
428					tooltip: "Paragraph"
429				},
430	
431				paste: {
432					groupIndex: 8,
433					visible: false,
434					tooltip: "Paste"
435				},
436	
437				undo: {
438					groupIndex: 4,
439					visible: false,
440					tooltip: "Undo"
441				},
442	
443				redo: {
444					groupIndex: 4,
445					visible: false,
446					tooltip: "Redo"
447				},
448	
449				removeFormat: {
450					groupIndex: 10,
451					visible: false,
452					exec: function () {
453						this.removeFormat();
454					},
455					tooltip: "Remove formatting"
456				},
457	
458	
459				underline: {
460					groupIndex: 0,
461					visible: false,
462					tags: ["u"],
463					css: {
464						textDecoration: "underline"
465					},
466					tooltip: "Underline",
467					hotkey: {"ctrl": 1, "key": 85}
468				},
469	
470				strikeThrough: {
471					groupIndex: 0,
472					visible: false,
473					tags: ["s", "strike"],
474					css: {
475						textDecoration: "line-through"
476					},
477					tooltip: "Strike-through"
478				},
479	
480				subscript: {
481					groupIndex: 3,
482					visible: false,
483					tags: ["sub"],
484					tooltip: "Subscript"
485				},
486	
487				superscript: {
488					groupIndex: 3,
489					visible: false,
490					tags: ["sup"],
491					tooltip: "Superscript"
492				},
493	
494				code: {
495					visible : false,
496					groupIndex: 6,
497					tooltip: "Code snippet",
498					exec: function () {
499						var range	= this.getInternalRange(),
500							common	= $(range.commonAncestorContainer),
501							$nodeName = range.commonAncestorContainer.nodeName.toLowerCase();
502						if (common.parent("code").length) {
503							common.unwrap();
504						} else {
505							if ($nodeName !== "body") {
506								common.wrap("<code/>");
507							}
508						}
509					}
510				},
511				
512				cssWrap: {
513					visible : false,
514					groupIndex: 6,
515					tooltip: "CSS Wrapper",
516					exec: function () { 
517						$.wysiwyg.controls.cssWrap.init(this);
518					}
519				}
520				
521			};
522	
523			this.defaults = {
524				html: '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>INITIAL_CONTENT</body></html>',
525				debug: false,
526				controls: {},
527				css: {},
528				events: {},
529				autoGrow: false,
530				autoSave: true,
531				brIE: true,					// http://code.google.com/p/jwysiwyg/issues/detail?id=15
532				formHeight: 270,
533				formWidth: 440,
534				iFrameClass: null,
535				initialContent: "<p>Initial content</p>",
536				maxHeight: 10000,			// see autoGrow
537				maxLength: 0,
538				messages: {
539					nonSelection: "Select the text you wish to link"
540				},
541				toolbarHtml: '<ul role="menu" class="toolbar"></ul>',
542				removeHeadings: false,
543				replaceDivWithP: false,
544				resizeOptions: false,
545				rmUnusedControls: false,	// https://github.com/akzhan/jwysiwyg/issues/52
546				rmUnwantedBr: true,			// http://code.google.com/p/jwysiwyg/issues/detail?id=11
547				tableFiller: "Lorem ipsum",
548				initialMinHeight: null,
549	
550				controlImage: {
551					forceRelativeUrls: false
552				},
553	
554				controlLink: {
555					forceRelativeUrls: false
556				},
557	
558				plugins: { // placeholder for plugins settings
559					autoload: false,
560					i18n: false,
561					rmFormat: {
562						rmMsWordMarkup: false
563					}
564				}
565			};
566	
567			this.availableControlProperties = [
568				"arguments",
569				"callback",
570				"className",
571				"command",
572				"css",
573				"custom",
574				"exec",
575				"groupIndex",
576				"hotkey",
577				"icon",
578				"tags",
579				"tooltip",
580				"visible"
581			];
582	
583			this.editor			= null;
584			this.editorDoc		= null;
585			this.element		= null;
586			this.options		= {};
587			this.original		= null;
588			this.savedRange		= null;
589			this.timers			= [];
590			this.validKeyCodes	= [8, 9, 13, 16, 17, 18, 19, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46];
591	
592			this.isDestroyed	= false;
593	
594			this.dom		= { // DOM related properties and methods
595				ie:		{
596					parent: null // link to dom
597				},
598				w3c:	{
599					parent: null // link to dom
600				}
601			};
602			this.dom.parent		= this;
603			this.dom.ie.parent	= this.dom;
604			this.dom.w3c.parent	= this.dom;
605	
606			this.ui			= {};	// UI related properties and methods
607			this.ui.self	= this;
608			this.ui.toolbar	= null;
609			this.ui.initialHeight = null; // ui.grow
610	
611			this.dom.getAncestor = function (element, filterTagName) {
612				filterTagName = filterTagName.toLowerCase();
613	
614				while (element && "body" !== element.tagName.toLowerCase()) {
615					if (filterTagName === element.tagName.toLowerCase()) {
616						return element;
617					}
618	
619					element = element.parentNode;
620				}
621	
622				return null;
623			};
624	
625			this.dom.getElement = function (filterTagName) {
626				var dom = this;
627	
628				if (window.getSelection) {
629					return dom.w3c.getElement(filterTagName);
630				} else {
631					return dom.ie.getElement(filterTagName);
632				}
633			};
634	
635			this.dom.ie.getElement = function (filterTagName) {
636				var dom			= this.parent,
637					selection	= dom.parent.getInternalSelection(),
638					range		= selection.createRange(),
639					element;
640	
641				if ("Control" === selection.type) {
642					// control selection
643					if (1 === range.length) {
644						element = range.item(0);
645					} else {
646						// multiple control selection
647						return null;
648					}
649				} else {
650					element = range.parentElement();
651				}
652	
653				return dom.getAncestor(element, filterTagName);
654			};
655	
656			this.dom.w3c.getElement = function (filterTagName) {
657				var dom		= this.parent,
658					range	= dom.parent.getInternalRange(),
659					element;
660	
661				if (!range) {
662					return null;
663				}
664	
665				element	= range.commonAncestorContainer;
666	
667				if (3 === element.nodeType) {
668					element = element.parentNode;
669				}
670	
671				// if startContainer not Text, Comment, or CDATASection element then
672				// startOffset is the number of child nodes between the start of the
673				// startContainer and the boundary point of the Range
674				if (element === range.startContainer) {
675					element = element.childNodes[range.startOffset];
676				}
677	
678				return dom.getAncestor(element, filterTagName);
679			};
680	
681			this.ui.addHoverClass = function () {
682				$(this).addClass("wysiwyg-button-hover");
683			};
684	
685			this.ui.appendControls = function () {
686				var ui = this,
687					self = this.self,
688					controls = self.parseControls(),
689					hasVisibleControls	= true, // to prevent separator before first item
690					groups = [],
691					controlsByGroup = {},
692					i,
693					currentGroupIndex, // jslint wants all vars at top of function
694					iterateGroup = function (controlName, control) {
695						if (control.groupIndex && currentGroupIndex !== control.groupIndex) {
696							currentGroupIndex = control.groupIndex;
697							hasVisibleControls = false;
698						}
699	
700						if (!control.visible) {
701							return;
702						}
703	
704						if (!hasVisibleControls) {
705							ui.appendItemSeparator();
706							hasVisibleControls = true;
707						}
708	
709						if (control.custom) {
710							ui.appendItemCustom(controlName, control);
711						} else {
712							ui.appendItem(controlName, control);
713						}
714					};
715	
716				$.each(controls, function (name, c) {
717					var index = "empty";
718	
719					if (undefined !== c.groupIndex) {
720						if ("" === c.groupIndex) {
721							index = "empty";
722						} else {
723							index = c.groupIndex;
724						}
725					}
726	
727					if (undefined === controlsByGroup[index]) {
728						groups.push(index);
729						controlsByGroup[index] = {};
730					}
731					controlsByGroup[index][name] = c;
732				});
733	
734				groups.sort(function (a, b) {
735					if ("number" === typeof (a) && typeof (a) === typeof (b)) {
736						return (a - b);
737					} else {
738						a = a.toString();
739						b = b.toString();
740	
741						if (a > b) {
742							return 1;
743						}
744	
745						if (a === b) {
746							return 0;
747						}
748	
749						return -1;
750					}
751				});
752	
753				if (0 < groups.length) {
754					// set to first index in groups to proper placement of separator
755					currentGroupIndex = groups[0];
756				}
757	
758				for (i = 0; i < groups.length; i += 1) {
759					$.each(controlsByGroup[groups[i]], iterateGroup);
760				}
761			};
762	
763			this.ui.appendItem = function (name, control) {
764				var self = this.self,
765					className = control.className || control.command || name || "empty",
766					tooltip = control.tooltip || control.command || name || "";
767	
768				return $('<li role="menuitem" unselectable="on">' + (className) + "</li>")
769					.addClass(className)
770					.attr("title", tooltip)
771					.hover(this.addHoverClass, this.removeHoverClass)
772					.click(function () {
773						if ($(this).hasClass("disabled")) {
774							return false;
775						}
776	
777						self.triggerControl.apply(self, [name, control]);
778	
779						this.blur();
780						self.ui.returnRange();
781						self.ui.focus();
782						return true;
783					})
784					.appendTo(self.ui.toolbar);
785			};
786	
787			this.ui.appendItemCustom = function (name, control) {
788				var self = this.self,
789					tooltip = control.tooltip || control.command || name || "";
790	
791				if (control.callback) {
792					$(window).bind("trigger-" + name + ".wysiwyg", control.callback);
793				}
794	
795				return $('<li role="menuitem" unselectable="on" style="background: url(\'' + control.icon + '\') no-repeat;"></li>')
796					.addClass("custom-command-" + name)
797					.addClass("wysiwyg-custom-command")
798					.addClass(name)
799					.attr("title", tooltip)
800					.hover(this.addHoverClass, this.removeHoverClass)
801					.click(function () {
802						if ($(this).hasClass("disabled")) {
803							return false;
804						}
805	
806						self.triggerControl.apply(self, [name, control]);
807	
808						this.blur();
809						self.ui.returnRange();
810						self.ui.focus();
811	
812						self.triggerControlCallback(name);
813						return true;
814					})
815					.appendTo(self.ui.toolbar);
816			};
817	
818			this.ui.appendItemSeparator = function () {
819				var self = this.self;
820				return $('<li role="separator" class="separator"></li>').appendTo(self.ui.toolbar);
821			};
822	
823			this.autoSaveFunction = function () {
824				this.saveContent();
825			};
826	
827			this.ui.checkTargets = function (element) {
828				var self = this.self;
829	
830				$.each(self.options.controls, function (name, control) {
831					var className = control.className || control.command || name || "empty",
832						tags,
833						elm,
834						css,
835						el,
836						checkActiveStatus = function (cssProperty, cssValue) {
837							var handler;
838	
839							if ("function" === typeof (cssValue)) {
840								handler = cssValue;
841								if (handler(el.css(cssProperty).toString().toLowerCase(), self)) {
842									self.ui.toolbar.find("." + className).addClass("active");
843								}
844							} else {
845								if (el.css(cssProperty).toString().toLowerCase() === cssValue) {
846									self.ui.toolbar.find("." + className).addClass("active");
847								}
848							}
849						};
850	
851					if ("fullscreen" !== className) {
852						self.ui.toolbar.find("." + className).removeClass("active");
853					}
854	
855					if (control.tags || (control.options && control.options.tags)) {
856						tags = control.tags || (control.options && control.options.tags);
857	
858						elm = element;
859						while (elm) {
860							if (elm.nodeType !== 1) {
861								break;
862							}
863	
864							if ($.inArray(elm.tagName.toLowerCase(), tags) !== -1) {
865								self.ui.toolbar.find("." + className).addClass("active");
866							}
867	
868							elm = elm.parentNode;
869						}
870					}
871	
872					if (control.css || (control.options && control.options.css)) {
873						css = control.css || (control.options && control.options.css);
874						el = $(element);
875	
876						while (el) {
877							if (el[0].nodeType !== 1) {
878								break;
879							}
880							$.each(css, checkActiveStatus);
881	
882							el = el.parent();
883						}
884					}
885				});
886			};
887	
888			this.ui.designMode = function () {
889				var attempts = 3,
890					self = this.self,
891					runner;
892					runner = function (attempts) {
893						if ("on" === self.editorDoc.designMode) {
894							if (self.timers.designMode) {
895								window.clearTimeout(self.timers.designMode);
896							}
897	
898							// IE needs to reget the document element (this.editorDoc) after designMode was set
899							if (self.innerDocument() !== self.editorDoc) {
900								self.ui.initFrame();
901							}
902	
903							return;
904						}
905	
906						try {
907							self.editorDoc.designMode = "on";
908						} catch (e) {
909						}
910	
911						attempts -= 1;
912						if (attempts > 0) {
913							self.timers.designMode = window.setTimeout(function () { runner(attempts); }, 100);
914						}
915					};
916	
917				runner(attempts);
918			};
919	
920			this.destroy = function () {
921				this.isDestroyed = true;
922	
923				var i, $form = this.element.closest("form");
924	
925				for (i = 0; i < this.timers.length; i += 1) {
926					window.clearTimeout(this.timers[i]);
927				}
928	
929				// Remove bindings
930				$form.unbind(".wysiwyg");
931				this.element.remove();
932				$.removeData(this.original, "wysiwyg");
933				$(this.original).show();
934				return this;
935			};
936	
937			this.getRangeText = function () {
938				var r = this.getInternalRange();
939	
940				if (r.toString) {
941					r = r.toString();
942				} else if (r.text) {	// IE
943					r = r.text;
944				}
945	
946				return r;
947			};
948			//not used?
949			this.execute = function (command, arg) {
950				if (typeof (arg) === "undefined") {
951					arg = null;
952				}
953				this.editorDoc.execCommand(command, false, arg);
954			};
955	
956			this.extendOptions = function (options) {
957				var controls = {};
958	
959				/**
960				 * If the user set custom controls, we catch it, and merge with the
961				 * defaults controls later.
962				 */
963				if ("object" === typeof options.controls) {
964					controls = options.controls;
965					delete options.controls;
966				}
967	
968				options = $.extend(true, {}, this.defaults, options);
969				options.controls = $.extend(true, {}, controls, this.controls, controls);
970	
971				if (options.rmUnusedControls) {
972					$.each(options.controls, function (controlName) {
973						if (!controls[controlName]) {
974							delete options.controls[controlName];
975						}
976					});
977				}
978	
979				return options;
980			};
981	
982			this.ui.focus = function () {
983				var self = this.self;
984	
985				self.editor.get(0).contentWindow.focus();
986				return self;
987			};
988	
989			this.ui.returnRange = function () {
990				var self = this.self, sel;
991	
992				if (self.savedRange !== null) {
993					if (window.getSelection) { //non IE and there is already a selection
994						sel = window.getSelection();
995						if (sel.rangeCount > 0) {
996							sel.removeAllRanges();
997						}
998						try {
999							sel.addRange(self.savedRange);
1000						} catch (e) {
1001							console.error(e);
1002						}
1003					} else if (window.document.createRange) { // non IE and no selection
1004						window.getSelection().addRange(self.savedRange);
1005					} else if (window.document.selection) { //IE
1006						self.savedRange.select();
1007					}
1008	
1009					self.savedRange = null;
1010				}
1011			};
1012	
1013			this.increaseFontSize = function () {
1014				if ($.browser.mozilla || $.browser.opera) {
1015					this.editorDoc.execCommand('increaseFontSize', false, null);
1016				} else if ($.browser.safari) {
1017					var newNode = this.editorDoc.createElement('big');
1018					this.getInternalRange().surroundContents(newNode);
1019				} else {
1020					console.error("Internet Explorer?");
1021				}
1022			};
1023	
1024			this.decreaseFontSize = function () {
1025				if ($.browser.mozilla || $.browser.opera) {
1026					this.editorDoc.execCommand('decreaseFontSize', false, null);
1027				} else if ($.browser.safari) {
1028					var newNode = this.editorDoc.createElement('small');
1029					this.getInternalRange().surroundContents(newNode);
1030				} else {
1031					console.error("Internet Explorer?");
1032				}
1033			};
1034	
1035			this.getContent = function () {
1036				if (this.viewHTML) {
1037					this.setContent(this.original.value);
1038				}
1039				return this.events.filter('getContent', this.editorDoc.body.innerHTML);
1040			};
1041			
1042			/**
1043			 * A jWysiwyg specific event system.
1044			 *
1045			 * Example:
1046			 * 
1047			 * $("#editor").getWysiwyg().events.bind("getContent", function (orig) {
1048			 *     return "<div id='content'>"+orgi+"</div>";
1049			 * });
1050			 * 
1051			 * This makes it so that when ever getContent is called, it is wrapped in a div#content.
1052			 */
1053			this.events = {
1054				_events : {},
1055				
1056				/**
1057				 * Similar to jQuery's bind, but for jWysiwyg only.
1058				 */
1059				bind : function (eventName, callback) {
1060					if (typeof (this._events.eventName) !== "object") {
1061						this._events[eventName] = [];
1062					}
1063					this._events[eventName].push(callback);
1064				},
1065				
1066				/**
1067				 * Similar to jQuery's trigger, but for jWysiwyg only.
1068				 */
1069				trigger : function (eventName, args) {
1070					if (typeof (this._events.eventName) === "object") {
1071						var editor = this.editor;
1072						$.each(this._events[eventName], function (k, v) {
1073							if (typeof (v) === "function") {
1074								v.apply(editor, args);
1075							}
1076						});
1077					}
1078				},
1079				
1080				/**
1081				 * This "filters" `originalText` by passing it as the first argument to every callback
1082				 * with the name `eventName` and taking the return value and passing it to the next function.
1083				 *
1084				 * This function returns the result after all the callbacks have been applied to `originalText`.
1085				 */
1086				filter : function (eventName, originalText) {
1087					if (typeof (this._events[eventName]) === "object") {
1088						var editor = this.editor,
1089							args = Array.prototype.slice.call(arguments, 1);
1090	
1091						$.each(this._events[eventName], function (k, v) {
1092							if (typeof (v) === "function") {
1093								originalText = v.apply(editor, args);
1094							}
1095						});
1096					}
1097					return originalText;
1098				}
1099			};
1100	
1101			this.getElementByAttributeValue = function (tagName, attributeName, attributeValue) {
1102				var i, value, elements = this.editorDoc.getElementsByTagName(tagName);
1103	
1104				for (i = 0; i < elements.length; i += 1) {
1105					value = elements[i].getAttribute(attributeName);
1106	
1107					if ($.browser.msie) {
1108						/** IE add full path, so I check by the last chars. */
1109						value = value.substr(value.length - attributeValue.length);
1110					}
1111	
1112					if (value === attributeValue) {
1113						return elements[i];
1114					}
1115				}
1116	
1117				return false;
1118			};
1119	
1120			this.getInternalRange = function () {
1121				var selection = this.getInternalSelection();
1122	
1123				if (!selection) {
1124					return null;
1125				}
1126	
1127				if (selection.rangeCount && selection.rangeCount > 0) { // w3c
1128					return selection.getRangeAt(0);
1129				} else if (selection.createRange) { // ie
1130					return selection.createRange();
1131				}
1132	
1133				return null;
1134			};
1135	
1136			this.getInternalSelection = function () {
1137				// firefox: document.getSelection is deprecated
1138				if (this.editor.get(0).contentWindow) {
1139					if (this.editor.get(0).contentWindow.getSelection) {
1140						return this.editor.get(0).contentWindow.getSelection();
1141					}
1142					if (this.editor.get(0).contentWindow.selection) {
1143						return this.editor.get(0).contentWindow.selection;
1144					}
1145				}
1146				if (this.editorDoc.getSelection) {
1147					return this.editorDoc.getSelection();
1148				}
1149				if (this.editorDoc.selection) {
1150					return this.editorDoc.selection;
1151				}
1152	
1153				return null;
1154			};
1155	
1156			this.getRange = function () {
1157				var selection = this.getSelection();
1158	
1159				if (!selection) {
1160					return null;
1161				}
1162	
1163				if (selection.rangeCount && selection.rangeCount > 0) { // w3c
1164					selection.getRangeAt(0);
1165				} else if (selection.createRange) { // ie
1166					return selection.createRange();
1167				}
1168	
1169				return null;
1170			};
1171	
1172			this.getSelection = function () {
1173				return (window.getSelection) ? window.getSelection() : window.document.selection;
1174			};
1175	
1176			// :TODO: you can type long string and letters will be hidden because of overflow
1177			this.ui.grow = function () {
1178				var self = this.self,
1179					innerBody = $(self.editorDoc.body),
1180					innerHeight = $.browser.msie ? innerBody[0].scrollHeight : innerBody.height() + 2 + 20, // 2 - borders, 20 - to prevent content jumping on grow
1181					minHeight = self.ui.initialHeight,
1182					height = Math.max(innerHeight, minHeight);
1183	
1184				height = Math.min(height, self.options.maxHeight);
1185	
1186				self.editor.attr("scrolling", height < self.options.maxHeight ? "no" : "auto"); // hide scrollbar firefox
1187				innerBody.css("overflow", height < self.options.maxHeight ? "hidden" : ""); // hide scrollbar chrome
1188	
1189				self.editor.get(0).height = height;
1190	
1191				return self;
1192			};
1193	
1194			this.init = function (element, options) {
1195				var self = this,
1196					$form = $(element).closest("form"),
1197					newX = element.width || element.clientWidth || 0,
1198					newY = element.height || element.clientHeight || 0
1199					;
1200	
1201				this.options	= this.extendOptions(options);
1202				this.original	= element;
1203				this.ui.toolbar	= $(this.options.toolbarHtml);
1204	
1205				if ($.browser.msie && parseInt($.browser.version, 10) < 8) {
1206					this.options.autoGrow = false;
1207				}
1208	
1209				if (newX === 0 && element.cols) {
1210					newX = (element.cols * 8) + 21;
1211				}
1212				if (newY === 0 && element.rows) {
1213					newY = (element.rows * 16) + 16;
1214				}
1215	
1216				this.editor = $(window.location.protocol === "https:" ? '<iframe src="javascript:false;"></iframe>' : "<iframe></iframe>").attr("frameborder", "0");
1217	
1218				if (this.options.iFrameClass) {
1219					this.editor.addClass(this.options.iFrameClass);
1220				} else {
1221					this.editor.css({
1222						minHeight: (newY - 6).toString() + "px",
1223						// fix for issue 12 ( http://github.com/akzhan/jwysiwyg/issues/issue/12 )
1224						width: (newX > 50) ? (newX - 8).toString() + "px" : ""
1225					});
1226					if ($.browser.msie && parseInt($.browser.version, 10) < 7) {
1227						this.editor.css("height", newY.toString() + "px");
1228					}
1229				}
1230				/**
1231				 * http://code.google.com/p/jwysiwyg/issues/detail?id=96
1232				 */
1233				this.editor.attr("tabindex", $(element).attr("tabindex"));
1234	
1235				this.element = $("<div/>").addClass("wysiwyg");
1236	
1237				if (!this.options.iFrameClass) {
1238					this.element.css({
1239						width: (newX > 0) ? newX.toString() + "px" : "100%"
1240					});
1241				}
1242	
1243				$(element).hide().before(this.element);
1244	
1245				this.viewHTML = false;
1246	
1247				/**
1248				 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
1249				 */
1250				this.initialContent = $(element).val();
1251				this.ui.initFrame();
1252	
1253				if (this.options.resizeOptions && $.fn.resizable) {
1254					this.element.resizable($.extend(true, {
1255						alsoResize: this.editor
1256					}, this.options.resizeOptions));
1257				}
1258	
1259				if (this.options.autoSave) {
1260					$form.bind("submit.wysiwyg", function () { self.autoSaveFunction(); });
1261				}
1262	
1263				$form.bind("reset.wysiwyg", function () { self.resetFunction(); });
1264			};
1265	
1266			this.ui.initFrame = function () {
1267				var self = this.self,
1268					stylesheet,
1269					growHandler,
1270					saveHandler;
1271	
1272				self.ui.appendControls();
1273				self.element.append(self.ui.toolbar)
1274					.append($("<div><!-- --></div>")
1275						.css({
1276							clear: "both"
1277						}))
1278					.append(self.editor);
1279	
1280				self.editorDoc = self.innerDocument();
1281	
1282				if (self.isDestroyed) {
1283					return null;
1284				}
1285	
1286				self.ui.designMode();
1287				self.editorDoc.open();
1288				self.editorDoc.write(
1289					self.options.html
1290						/**
1291						 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=144
1292						 */
1293						.replace(/INITIAL_CONTENT/, function () { return self.wrapInitialContent(); })
1294				);
1295				self.editorDoc.close();
1296	
1297				$.wysiwyg.plugin.bind(self);
1298	
1299				$(self.editorDoc).trigger("initFrame.wysiwyg");
1300	
1301				$(self.editorDoc).bind("click.wysiwyg", function (event) {
1302					self.ui.checkTargets(event.target ? event.target : event.srcElement);
1303				});
1304	
1305				/**
1306				 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
1307				 */
1308				$(self.original).focus(function () {
1309					if ($(this).filter(":visible")) {
1310						return;
1311					}
1312					self.ui.focus();
1313				});
1314	
1315				$(self.editorDoc).keydown(function (event) {
1316					var emptyContentRegex;
1317					if (event.keyCode === 8) { // backspace
1318						emptyContentRegex = /^<([\w]+)[^>]*>(<br\/?>)?<\/\1>$/;
1319						if (emptyContentRegex.test(self.getContent())) { // if content is empty
1320							event.stopPropagation(); // prevent remove single empty tag
1321							return false;
1322						}
1323					}
1324					return true;
1325				});
1326	
1327				if (!$.browser.msie) {
1328					$(self.editorDoc).keydown(function (event) {
1329						var controlName;
1330	
1331						/* Meta for Macs. [email protected] */
1332						if (event.ctrlKey || event.metaKey) {
1333							for (controlName in self.controls) {
1334								if (self.controls[controlName].hotkey && self.controls[controlName].hotkey.ctrl) {
1335									if (event.keyCode === self.controls[controlName].hotkey.key) {
1336										self.triggerControl.apply(self, [controlName, self.controls[controlName]]);
1337	
1338										return false;
1339									}
1340								}
1341							}
1342						}
1343	
1344						return true;
1345					});
1346				} else if (self.options.brIE) {
1347					$(self.editorDoc).keydown(function (event) {
1348						if (event.keyCode === 13) {
1349							var rng = self.getRange();
1350							rng.pasteHTML("<br/>");
1351							rng.collapse(false);
1352							rng.select();
1353	
1354							return false;
1355						}
1356	
1357						return true;
1358					});
1359				}
1360	
1361				if (self.options.plugins.rmFormat.rmMsWordMarkup) {
1362					$(self.editorDoc).bind("keyup.wysiwyg", function (event) {
1363						if (event.ctrlKey || event.metaKey) {
1364							// CTRL + V (paste)
1365							if (86 === event.keyCode) {
1366								if ($.wysiwyg.rmFormat) {
1367									if ("object" === typeof (self.options.plugins.rmFormat.rmMsWordMarkup)) {
1368										$.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: self.options.plugins.rmFormat.rmMsWordMarkup }});
1369									} else {
1370										$.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: { enabled: true }}});
1371									}
1372								}
1373							}
1374						}
1375					});
1376				}
1377	
1378				if (self.options.autoSave) {
1379					$(self.editorDoc).keydown(function () { self.autoSaveFunction(); })
1380						.keyup(function () { self.autoSaveFunction(); })
1381						.mousedown(function () { self.autoSaveFunction(); })
1382						.bind($.support.noCloneEvent ? "input.wysiwyg" : "paste.wysiwyg", function () { self.autoSaveFunction(); });
1383				}
1384	
1385				if (self.options.autoGrow) {
1386					if (self.options.initialMinHeight !== null) {
1387						self.ui.initialHeight = self.options.initialMinHeight;
1388					} else {
1389						self.ui.initialHeight = $(self.editorDoc).height();
1390					}
1391					$(self.editorDoc.body).css("border", "1px solid white"); // cancel margin collapsing
1392	
1393					growHandler = function () {
1394						self.ui.grow();
1395					};
1396	
1397					$(self.editorDoc).keyup(growHandler);
1398					$(self.editorDoc).bind("editorRefresh.wysiwyg", growHandler);
1399	
1400					// fix when content height > textarea height
1401					self.ui.grow();
1402				}
1403	
1404				if (self.options.css) {
1405					if (String === self.options.css.constructor) {
1406						if ($.browser.msie) {
1407							stylesheet = self.editorDoc.createStyleSheet(self.options.css);
1408							$(stylesheet).attr({
1409								"media":	"all"
1410							});
1411						} else {
1412							stylesheet = $("<link/>").attr({
1413								"href":		self.options.css,
1414								"media":	"all",
1415								"rel":		"stylesheet",
1416								"type":		"text/css"
1417							});
1418	
1419							$(self.editorDoc).find("head").append(stylesheet);
1420						}
1421					} else {
1422						self.timers.initFrame_Css = window.setTimeout(function () {
1423							$(self.editorDoc.body).css(self.options.css);
1424						}, 0);
1425					}
1426				}
1427	
1428				if (self.initialContent.length === 0) {
1429					if ("function" === typeof (self.options.initialContent)) {
1430						self.setContent(self.options.initialContent());
1431					} else {
1432						self.setContent(self.options.initialContent);
1433					}
1434				}
1435	
1436				if (self.options.maxLength > 0) {
1437					$(self.editorDoc).keydown(function (event) {
1438						if ($(self.editorDoc).text().length >= self.options.maxLength && $.inArray(event.which, self.validKeyCodes) === -1) {
1439							event.preventDefault();
1440						}
1441					});
1442				}
1443				
1444				// Support event callbacks
1445				$.each(self.options.events, function (key, handler) {
1446					$(self.editorDoc).bind(key + ".wysiwyg", function (event) {
1447						// Trigger event handler, providing the event and api to 
1448						// support additional functionality.
1449						handler.apply(self.editorDoc, [event, self]);
1450					});
1451				});
1452	
1453				// restores selection properly on focus
1454				if ($.browser.msie) {
1455					// Event chain: beforedeactivate => focusout => blur.
1456					// Focusout & blur fired too late to handle internalRange() in dialogs.
1457					// When clicked on input boxes both got range = null
1458					$(self.editorDoc).bind("beforedeactivate.wysiwyg", function () {
1459						self.savedRange = self.getInternalRange();
1460					});
1461				} else {
1462					$(self.editorDoc).bind("blur.wysiwyg", function () {
1463						self.savedRange = self.getInternalRange();
1464					});
1465				}
1466	
1467				$(self.editorDoc.body).addClass("wysiwyg");
1468				if (self.options.events && self.options.events.save) {
1469					saveHandler = self.options.events.save;
1470	
1471					$(self.editorDoc).bind("keyup.wysiwyg", saveHandler);
1472					$(self.editorDoc).bind("change.wysiwyg", saveHandler);
1473	
1474					if ($.support.noCloneEvent) {
1475						$(self.editorDoc).bind("input.wysiwyg", saveHandler);
1476					} else {
1477						$(self.editorDoc).bind("paste.wysiwyg", saveHandler);
1478						$(self.editorDoc).bind("cut.wysiwyg", saveHandler);
1479					}
1480				}
1481				
1482				/**
1483				 * XHTML5 {@link https://github.com/akzhan/jwysiwyg/issues/152}
1484				 */
1485				if (self.options.xhtml5 && self.options.unicode) {
1486					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
1486: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};
1487					self.events.bind("getContent", function (text) {
1488						return text.replace(/&(?:amp;)?(?!amp|lt|gt|quot)([a-z][a-z0-9]*);/gi, function (str, p1) {
1489							if (!replacements[p1]) {
1490								p1 = p1.toLowerCase();
1491								if (!replacements[p1]) {
1492									p1 = "__replacement";
1493								}
1494							}
1495							
1496							var num = replacements[p1];
1497							/* Numeric return if ever wanted: return replacements[p1] ? "&#"+num+";" : ""; */
1498							return String.fromCharCode(num);
1499						});
1500					});
1501				}
1502			};
1503	
1504			this.innerDocument = function () {
1505				var element = this.editor.get(0);
1506	
1507				if (element.nodeName.toLowerCase() === "iframe") {
1508					if (element.contentDocument) {				// Gecko
1509						return element.contentDocument;
1510					} else if (element.contentWindow) {			// IE
1511						return element.contentWindow.document;
1512					}
1513	
1514					if (this.isDestroyed) {
1515						return null;
1516					}
1517	
1518					console.error("Unexpected error in innerDocument");
1519	
1520					/*
1521					 return ( $.browser.msie )
1522					 ? document.frames[element.id].document
1523					 : element.contentWindow.document // contentDocument;
1524					 */
1525				}
1526	
1527				return element;
1528			};
1529	
1530			this.insertHtml = function (szHTML) {
1531				var img, range;
1532	
1533				if (!szHTML || szHTML.length === 0) {
1534					return this;
1535				}
1536	
1537				if ($.browser.msie) {
1538					this.ui.focus();
1539					this.editorDoc.execCommand("insertImage", false, "#jwysiwyg#");
1540					img = this.getElementByAttributeValue("img", "src", "#jwysiwyg#");
1541					if (img) {
1542						$(img).replaceWith(szHTML);
1543					}
1544				} else {
1545					if ($.browser.mozilla) { // @link https://github.com/akzhan/jwysiwyg/issues/50
1546						if (1 === $(szHTML).length) {
1547							range = this.getInternalRange();
1548							range.deleteContents();
1549							range.insertNode($(szHTML).get(0));
1550						} else {
1551							this.editorDoc.execCommand("insertHTML", false, szHTML);
1552						}
1553					} else {
1554						if (!this.editorDoc.execCommand("insertHTML", false, szHTML)) {
1555							this.editor.focus();
1556							/* :TODO: place caret at the end
1557							if (window.getSelection) {
1558							} else {
1559							}
1560							this.editor.focus();
1561							*/
1562							this.editorDoc.execCommand("insertHTML", false, szHTML);
1563						}
1564					}
1565				}
1566	
1567				this.saveContent();
1568				
1569				return this;
1570			};
1571	
1572			this.parseControls = function () {
1573				var self = this;
1574	
1575				$.each(this.options.controls, function (controlName, control) {
1576					$.each(control, function (propertyName) {
1577						if (-1 === $.inArray(propertyName, self.availableControlProperties)) {
1578							throw controlName + '["' + propertyName + '"]: property "' + propertyName + '" not exists in Wysiwyg.availableControlProperties';
1579						}
1580					});
1581				});
1582	
1583				if (this.options.parseControls) {
1584					return this.options.parseControls.call(this);
1585				}
1586	
1587				return this.options.controls;
1588			};
1589	
1590			this.removeFormat = function () {
1591				if ($.browser.msie) {
1592					this.ui.focus();
1593				}
1594	
1595				if (this.options.removeHeadings) {
1596					this.editorDoc.execCommand("formatBlock", false, "<p>"); // remove headings
1597				}
1598	
1599				this.editorDoc.execCommand("removeFormat", false, null);
1600				this.editorDoc.execCommand("unlink", false, null);
1601	
1602				if ($.wysiwyg.rmFormat && $.wysiwyg.rmFormat.enabled) {
1603					if ("object" === typeof (this.options.plugins.rmFormat.rmMsWordMarkup)) {
1604						$.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: this.options.plugins.rmFormat.rmMsWordMarkup }});
1605					} else {
1606						$.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: { enabled: true }}});
1607					}
1608				}
1609	
1610				return this;
1611			};
1612	
1613			this.ui.removeHoverClass = function () {
1614				$(this).removeClass("wysiwyg-button-hover");
1615			};
1616	
1617			this.resetFunction = function () {
1618				this.setContent(this.initialContent);
1619			};
1620	
1621			this.saveContent = function () {
1622				if (this.viewHTML)
1623				{
1624					return; // no need
1625				}
1626				if (this.original) {
1627					var content, newContent;
1628	
1629					content = this.getContent();
1630	
1631					if (this.options.rmUnwantedBr) {
1632						content = content.replace(/<br\/?>$/, "");
1633					}
1634	
1635					if (this.options.replaceDivWithP) {
1636						newContent = $("<div/>").addClass("temp").append(content);
1637	
1638						newContent.children("div").each(function () {
1639							var element = $(this), p = element.find("p"), i;
1640	
1641							if (0 === p.length) {
1642								p = $("<p></p>");
1643	
1644								if (this.attributes.length > 0) {
1645									for (i = 0; i < this.attributes.length; i += 1) {
1646										p.attr(this.attributes[i].name, element.attr(this.attributes[i].name));
1647									}
1648								}
1649	
1650								p.append(element.html());
1651	
1652								element.replaceWith(p);
1653							}
1654						});
1655						
1656						content = newContent.html();
1657					}
1658	
1659					$(this.original).val(content);
1660	
1661					if (this.options.events && this.options.events.save) {
1662						this.options.events.save.call(this);
1663					}
1664				}
1665	
1666				return this;
1667			};
1668	
1669			this.setContent = function (newContent) {
1670				this.editorDoc.body.innerHTML = newContent;
1671				this.saveContent();
1672	
1673				return this;
1674			};
1675	
1676			this.triggerControl = function (name, control) {
1677				var cmd = control.command || name,
1678					args = control["arguments"] || [];
1679	
1680				if (control.exec) {
1681					control.exec.apply(this);
1682				} else {
1683					this.ui.focus();
1684					this.ui.withoutCss();
1685					// when click <Cut>, <Copy> or <Paste>
1685 got "Access to XPConnect service denied" code: "1011"
1686					// in Firefox untrusted JavaScript is not allowed to access the clipboard
1687					try {
1688						this.editorDoc.execCommand(cmd, false, args);
1689					} catch (e) {
1690						console.error(e);
1691					}
1692				}
1693	
1694				if (this.options.autoSave) {
1695					this.autoSaveFunction();
1696				}
1697			};
1698	
1699			this.triggerControlCallback = function (name) {
1700				$(window).trigger("trigger-" + name + ".wysiwyg", [this]);
1701			};
1702	
1703			this.ui.withoutCss = function () {
1704				var self = this.self;
1705	
1706				if ($.browser.mozilla) {
1707					try {
1708						self.editorDoc.execCommand("styleWithCSS", false, false);
1709					} catch (e) {
1710						try {
1711							self.editorDoc.execCommand("useCSS", false, true);
1712						} catch (e2) {
1713						}
1714					}
1715				}
1716	
1717				return self;
1718			};
1719	
1720			this.wrapInitialContent = function () {
1721				var content = this.initialContent,
1722					found = content.match(/<\/?p>/gi);
1723	
1724				if (!found) {
1725					return "<p>" + content + "</p>";
1726				} else {
1727					// :TODO: checking/replacing
1728				}
1729	
1730				return content;
1731			};
1732		}
1733	
1734		/*
1735		 * Wysiwyg namespace: public properties and methods
1736		 */
1737		$.wysiwyg = {
1738			messages: {
1739				noObject: "Something goes wrong, check object"
1740			},
1741	
1742			/**
1743			 * Custom control support by Alec Gorge ( http://github.com/alecgorge )
1744			 */
1745			addControl: function (object, name, settings) {
1746				return object.each(function () {
1747					var oWysiwyg = $(this).data("wysiwyg"),
1748						customControl = {},
1749						toolbar;
1750	
1751					if (!oWysiwyg) {
1752						return this;
1753					}
1754	
1755					customControl[name] = $.extend(true, {visible: true, custom: true}, settings);
1756					$.extend(true, oWysiwyg.options.controls, customControl);
1757	
1758					// render new toolbar
1759					toolbar = $(oWysiwyg.options.toolbarHtml);
1760					oWysiwyg.ui.toolbar.replaceWith(toolbar);
1761					oWysiwyg.ui.toolbar = toolbar;
1762					oWysiwyg.ui.appendControls();
1763				});
1764			},
1765	
1766			clear: function (object) {
1767				return object.each(function () {
1768					var oWysiwyg = $(this).data("wysiwyg");
1769	
1770					if (!oWysiwyg) {
1771						return this;
1772					}
1773	
1774					oWysiwyg.setContent("");
1775				});
1776			},
1777	
1778			console: console, // let our console be available for extensions
1779	
1780			destroy: function (object) {
1781				return object.each(function () {
1782					var oWysiwyg = $(this).data("wysiwyg");
1783	
1784					if (!oWysiwyg) {
1785						return this;
1786					}
1787	
1788					oWysiwyg.destroy();
1789				});
1790			},
1791	
1792			"document": function (object) {
1793				// no chains because of return
1794				var oWysiwyg = object.data("wysiwyg");
1795	
1796				if (!oWysiwyg) {
1797					return undefined;
1798				}
1799	
1800				return $(oWysiwyg.editorDoc);
1801			},
1802	
1803			getContent: function (object) {
1804				// no chains because of return
1805				var oWysiwyg = object.data("wysiwyg");
1806	
1807				if (!oWysiwyg) {
1808					return undefined;
1809				}
1810	
1811				return oWysiwyg.getContent();
1812			},
1813	
1814			init: function (object, options) {
1815				return object.each(function () {
1816					var opts = $.extend(true, {}, options),
1817						obj;
1818	
1819					// :4fun:
1820					// remove this textarea validation and change line in this.saveContent function
1821					// $(this.original).val(content); to $(this.original).html(content);
1822					// now you can make WYSIWYG editor on h1, p, and many more tags
1823					if (("textarea" !== this.nodeName.toLowerCase()) || $(this).data("wysiwyg")) {
1824						return;
1825					}
1826	
1827					obj = new Wysiwyg();
1828					obj.init(this, opts);
1829					$.data(this, "wysiwyg", obj);
1830	
1831					$(obj.editorDoc).trigger("afterInit.wysiwyg");
1832				});
1833			},
1834	
1835			insertHtml: function (object, szHTML) {
1836				return object.each(function () {
1837					var oWysiwyg = $(this).data("wysiwyg");
1838	
1839					if (!oWysiwyg) {
1840						return this;
1841					}
1842	
1843					oWysiwyg.insertHtml(szHTML);
1844				});
1845			},
1846	
1847			plugin: {
1848				listeners: {},
1849	
1850				bind: function (Wysiwyg) {
1851					var self = this;
1852	
1853					$.each(this.listeners, function (action, handlers) {
1854						var i, plugin;
1855	
1856						for (i = 0; i < handlers.length; i += 1) {
1857							plugin = self.parseName(handlers[i]);
1858	
1859							$(Wysiwyg.editorDoc).bind(action + ".wysiwyg", {plugin: plugin}, function (event) {
1860								$.wysiwyg[event.data.plugin.name][event.data.plugin.method].apply($.wysiwyg[event.data.plugin.name], [Wysiwyg]);
1861							});
1862						}
1863					});
1864				},
1865	
1866				exists: function (name) {
1867					var plugin;
1868	
1869					if ("string" !== typeof (name)) {
1870						return false;
1871					}
1872	
1873					plugin = this.parseName(name);
1874	
1875					if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
1876						return false;
1877					}
1878	
1879					return true;
1880				},
1881	
1882				listen: function (action, handler) {
1883					var plugin;
1884	
1885					plugin = this.parseName(handler);
1886	
1887					if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
1888						return false;
1889					}
1890	
1891					if (!this.listeners[action]) {
1892						this.listeners[action] = [];
1893					}
1894	
1895					this.listeners[action].push(handler);
1896	
1897					return true;
1898				},
1899	
1900				parseName: function (name) {
1901					var elements;
1902	
1903					if ("string" !== typeof (name)) {
1904						return false;
1905					}
1906	
1907					elements = name.split(".");
1908	
1909					if (2 > elements.length) {
1910						return false;
1911					}
1912	
1913					return {name: elements[0], method: elements[1]};
1914				},
1915	
1916				register: function (data) {
1917					if (!data.name) {
1918						console.error("Plugin name missing");
1919					}
1920	
1921					$.each($.wysiwyg, function (pluginName) {
1922						if (pluginName === data.name) {
1923							console.error("Plugin with name '" + data.name + "' was already registered");
1924						}
1925					});
1926	
1927					$.wysiwyg[data.name] = data;
1928	
1929					return true;
1930				}
1931			},
1932	
1933			removeFormat: function (object) {
1934				return object.each(function () {
1935					var oWysiwyg = $(this).data("wysiwyg");
1936	
1937					if (!oWysiwyg) {
1938						return this;
1939					}
1940	
1941					oWysiwyg.removeFormat();
1942				});
1943			},
1944	
1945			save: function (object) {
1946				return object.each(function () {
1947					var oWysiwyg = $(this).data("wysiwyg");
1948	
1949					if (!oWysiwyg) {
1950						return this;
1951					}
1952	
1953					oWysiwyg.saveContent();
1954				});
1955			},
1956	
1957			selectAll: function (object) {
1958				var oWysiwyg = object.data("wysiwyg"), oBody, oRange, selection;
1959	
1960				if (!oWysiwyg) {
1961					return this;
1962				}
1963	
1964				oBody = oWysiwyg.editorDoc.body;
1965				if (window.getSelection) {
1966					selection = oWysiwyg.getInternalSelection();
1967					selection.selectAllChildren(oBody);
1968				} else {
1969					oRange = oBody.createTextRange();
1970					oRange.moveToElementText(oBody);
1971					oRange.select();
1972				}
1973			},
1974	
1975			setContent: function (object, newContent) {
1976				return object.each(function () {
1977					var oWysiwyg = $(this).data("wysiwyg");
1978	
1979					if (!oWysiwyg) {
1980						return this;
1981					}
1982	
1983					oWysiwyg.setContent(newContent);
1984				});
1985			},
1986	
1987			triggerControl: function (object, controlName) {
1988				return object.each(function () {
1989					var oWysiwyg = $(this).data("wysiwyg");
1990	
1991					if (!oWysiwyg) {
1992						return this;
1993					}
1994	
1995					if (!oWysiwyg.controls[controlName]) {
1996						console.error("Control '" + controlName + "' not exists");
1997					}
1998	
1999					oWysiwyg.triggerControl.apply(oWysiwyg, [controlName, oWysiwyg.controls[controlName]]);
2000				});
2001			},
2002	
2003			support: {
2004				prop: supportsProp
2005			},
2006	
2007			utils: {
2008				extraSafeEntities: [["<", ">", "'", '"', " "], [32]],
2009	
2010				encodeEntities: function (str) {
2011					var self = this, aStr, aRet = [];
2012	
2013					if (this.extraSafeEntities[1].length === 0) {
2014						$.each(this.extraSafeEntities[0], function (i, ch) {
2015							self.extraSafeEntities[1].push(ch.charCodeAt(0));
2016						});
2017					}
2018					aStr = str.split("");
2019					$.each(aStr, function (i) {
2020						var iC = aStr[i].charCodeAt(0);
2021						if ($.inArray(iC, self.extraSafeEntities[1]) && (iC < 65 || iC > 127 || (iC > 90 && iC < 97))) {
2022							aRet.push('&#' + iC + ';');
2023						} else {
2024							aRet.push(aStr[i]);
2025						}
2026					});
2027	
2028					return aRet.join('');
2029				}
2030			}
2031		};
2032	
2033		$.fn.wysiwyg = function (method) {
2034			var args = arguments, plugin;
2035	
2036			if ("undefined" !== typeof $.wysiwyg[method]) {
2037				// set argument object to undefined
2038				args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
2039				return $.wysiwyg[method].apply($.wysiwyg, Array.prototype.slice.call(args, 1));
2040			} else if ("object" === typeof method || !method) {
2041				Array.prototype.unshift.call(args, this);
2042				return $.wysiwyg.init.apply($.wysiwyg, args);
2043			} else if ($.wysiwyg.plugin.exists(method)) {
2044				plugin = $.wysiwyg.plugin.parseName(method);
2045				args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
2046				return $.wysiwyg[plugin.name][plugin.method].apply($.wysiwyg[plugin.name], Array.prototype.slice.call(args, 1));
2047			} else {
2048				console.error("Method '" +  method + "' does not exist on jQuery.wysiwyg.\nTry to include some extra controls or plugins");
2049			}
2050		};
2051		
2052		$.fn.getWysiwyg = function () {
2053			return $.data(this, "wysiwyg");
2054		};
2055	})(jQuery);
2056	
2057	
2058	
2059/**
2060 * Controls: Image plugin
2061 *
2062 * Depends on jWYSIWYG
2063 */
2064(function ($) {
2065	if (undefined === $.wysiwyg) {
2066		throw "wysiwyg.image.js depends on $.wysiwyg";
2067	}
2068
2069	if (!$.wysiwyg.controls) {
2070		$.wysiwyg.controls = {};
2071	}
2072
2073	/*
2074	 * Wysiwyg namespace: public properties and methods
2075	 */
2076	$.wysiwyg.controls.image = {
2077		init: function (Wysiwyg) {
2078			var self = this, elements, dialog, formImageHtml, dialogReplacements, key, translation,
2079				img = {
2080					alt: "",
2081					self: Wysiwyg.dom.getElement("img"), // link to element node
2082					src: "http://",
2083					title: ""
2084				};
2085
2086			dialogReplacements = {
2087				legend	: "Insert Image",
2088				preview : "Preview",
2089				url     : "URL",
2090				title   : "Title",
2091				description : "Description",
2092				width   : "Width",
2093				height  : "Height",
2094				original : "Original W x H",
2095				"float"	: "Float",
2096				floatNone : "None",
2097				floatLeft : "Left",
2098				floatRight : "Right",
2099				submit  : "Insert Image",
2100				loading  : "loading",
2101				reset   : "Cancel"
2102			};
2103
2104			formImageHtml = '<form class="wysiwyg" title="{legend}"><fieldset>' +
2105				'<label><img src="" alt="{preview}" width="100%"></label>' +
2106				'<label>{url}: <input type="text" name="src" value=""></label>' +
2107				'<label>{title}: <input type="text" name="imgtitle" value=""></label>' +
2108				'<label>{description}: <input type="text" name="description" value=""></label>' +
2109				'<label>{width} x {height}: <input type="text" name="width" value="" class="width integer"> x <input type="text" name="height" value="" class="height integer"></label><br>' +
2110				'<label>{float}: <select name="float">' + 
2111				'<option value="">{floatNone}</option>' +
2112				'<option value="left">{floatLeft}</option>' +
2113				'<option value="right">{floatRight}</option></select></label><hr>' +
2114				'<button class="button" id="wysiwyg_submit">{submit}</button> ' +
2115				'<button class="button" id="wysiwyg_reset">{reset}</button></fieldset></form>';
2116
2117			for (key in dialogReplacements) {
2118				if ($.wysiwyg.i18n) {
2119					translation = $.wysiwyg.i18n.t(dialogReplacements[key], "dialogs.image");
2120
2121					if (translation === dialogReplacements[key]) { // if not translated search in dialogs 
2122						translation = $.wysiwyg.i18n.t(dialogReplacements[key], "dialogs");
2123					}
2124
2125					dialogReplacements[key] = translation;
2126				}
2127
2128				formImageHtml = formImageHtml.replace("{" + key + "}", dialogReplacements[key]);
2129			}
2130
2131			if (img.self) {
2132				img.src = img.self.src ? img.self.src : "";
2133				img.alt = img.self.alt ? img.self.alt : "";
2134				img.title = img.self.title ? img.self.title : "";
2135				img.width = img.self.width ? img.self.width : "";
2136				img.height = img.self.height ? img.self.height : "";
2137				img.asp = img.width/img.width;
2138			}
2139
2140			if ($.modal) {
2141				elements = $(formImageHtml);
2142				elements = self.makeForm(elements, img);
2143
2144				$.modal(elements, {
2145					onShow: function (dialog) {
2146						$("#wysiwyg_submit", dialog.data).click(function (e) {
2147							self.processInsert(dialog.container, Wysiwyg, img);
2148
2149							$.modal.close();
2150							return false;
2151						});
2152						$("#wysiwyg_reset", dialog.data).click(function (e) {
2153							$.modal.close();
2154							return false;
2155						});
2156						$("fieldset", dialog.data).click(function (e) {
2157							e.stopPropagation();
2158						});
2159						
2160						$("select, input[type=text]", dialog).uniform();
2161						$('.integer', dialog).wl_Number();
2162						$('input[name="src"]', dialog).wl_URL();
2163						
2164					},
2165					maxWidth: Wysiwyg.defaults.formWidth,
2166					maxHeight: Wysiwyg.defaults.formHeight,
2167					overlayClose: true
2168				});
2169			} else if ($.fn.dialog) {
2170				elements = $(formImageHtml);
2171				elements = self.makeForm(elements, img);
2172
2173				dialog = elements.appendTo("body");
2174				dialog.dialog({
2175					modal: true,
2176					resizable: false,
2177					open: function (ev, ui) {
2178						$("#wysiwyg_submit", dialog).click(function (e) {
2179							self.processInsert(dialog.container, Wysiwyg, img);
2180
2181							$(dialog).dialog("close");
2182							return false;
2183						});
2184						$("#wysiwyg_reset", dialog).click(function (e) {
2185							$(dialog).dialog("close");
2186							return false;
2187						});
2188						$('fieldset', dialog).click(function (e) {
2189							e.stopPropagation();
2190						});
2191						$("select, input[type=text]", dialog).uniform();
2192						$('.width', dialog).wl_Number({
2193							step:10,
2194							onChange:function(value){
2195								$('.height', dialog).val(Math.ceil(value/(img.asp || 1)));
2196							}								
2197						});
2198						$('.height', dialog).wl_Number({
2199							step:10,
2200							onChange:function(value){
2201								$('.width', dialog).val(Math.floor(value*(img.asp || 1)));
2202							}								
2203						});
2204						$('input[name="src"]', dialog).wl_URL();
2205						
2206					},
2207					close: function (ev, ui) {
2208						dialog.dialog("destroy");
2209						dialog.remove();
2210					}
2211				});
2212			} else {
2213				if ($.browser.msie) {
2214					Wysiwyg.ui.focus();
2215					Wysiwyg.editorDoc.execCommand("insertImage", true, null);
2216				} else {
2217					elements = $("<div/>")
2218						.css({"position": "fixed",
2219							"z-index": 2000,
2220							"left": "50%", "top": "50%", "background": "rgb(0, 0, 0)",
2221							"margin-top": -1 * Math.round(Wysiwyg.defaults.formHeight / 2),
2222							"margin-left": -1 * Math.round(Wysiwyg.defaults.formWidth / 2)})
2223						.html(formImageHtml);
2224					elements = self.makeForm(elements, img);
2225
2226					$("#wysiwyg_submit", elements).click(function (event) {
2227						self.processInsert(elements, Wysiwyg, img);
2228
2229						$(elements).remove();
2230						return false;
2231					});
2232					$("#wysiwyg_reset", elements).click(function (event) {
2233						if ($.browser.msie) {
2234							Wysiwyg.ui.returnRange();
2235						}
2236
2237						$(elements).remove();
2238						return false;
2239					});
2240					
2241					$("select, input[type=text]", dialog).uniform();
2242					$('.integer', dialog).wl_Number();
2243					$('input[name="src"]', dialog).wl_URL();
2244					
2245					$("body").append(elements);
2246					elements.click(function(e) {
2247						e.stopPropagation();
2248					});
2249				}
2250			}
2251
2252			$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2253		},
2254
2255		processInsert: function (context, Wysiwyg, img) {
2256			var image,
2257				url = $('input[name="src"]', context).val(),
2258				title = $('input[name="imgtitle"]', context).val(),
2259				description = $('input[name="description"]', context).val(),
2260				width = $('input[name="width"]', context).val(),
2261				height = $('input[name="height"]', context).val(),
2262				styleFloat = $('select[name="float"]', context).val(),
2263				style = [],
2264				found,
2265				baseUrl;
2266
2267			if (Wysiwyg.options.controlImage.forceRelativeUrls) {
2268				baseUrl = window.location.protocol + "//" + window.location.hostname;
2269				if (0 === url.indexOf(baseUrl)) {
2270					url = url.substr(baseUrl.length);
2271				}
2272			}
2273
2274			if (img.self) {
2275				// to preserve all img attributes
2276				$(img.self).attr("src", url)
2277					.attr("title", title)
2278					.attr("alt", description)
2279					.css("float", styleFloat);
2280
2281				if (width.toString().match(/^[0-9]+(px|%)?$/)) {
2282					$(img.self).css("width", width);
2283				} else {
2284					$(img.self).css("width", "");
2285				}
2286
2287				if (height.toString().match(/^[0-9]+(px|%)?$/)) {
2288					$(img.self).css("height", height);
2289				} else {
2290					$(img.self).css("height", "");
2291				}
2292				
2293				Wysiwyg.saveContent();
2294			} else {
2295				found = width.toString().match(/^[0-9]+(px|%)?$/);
2296				if (found) {
2297					if (found[1]) {
2298						style.push("width: " + width + ";");
2299					} else {
2300						style.push("width: " + width + "px;");
2301					}
2302				}
2303
2304				found = height.toString().match(/^[0-9]+(px|%)?$/);
2305				if (found) {
2306					if (found[1]) {
2307						style.push("height: " + height + ";");
2308					} else {
2309						style.push("height: " + height + "px;");
2310					}
2311				}
2312
2313				if (styleFloat.length > 0) {
2314					style.push("float: " + styleFloat + ";");
2315				}
2316
2317				if (style.length > 0) {
2318					style = ' style="' + style.join(" ") + '"';
2319				}
2320
2321				image = "<img src='" + url + "' title='" + title + "' alt='" + description + "'" + style + "/>";
2322				Wysiwyg.insertHtml(image);
2323			}
2324		},
2325
2326		makeForm: function (form, img) {
2327			form.find("input[name=src]").val(img.src);
2328			form.find("input[name=imgtitle]").val(img.title);
2329			form.find("input[name=description]").val(img.alt);
2330			form.find('input[name="width"]').val(img.width);
2331			form.find('input[name="height"]').val(img.height);
2332			form.find('img').attr("src", img.src);
2333			img.asp = img.width/img.height;
2334			
2335			form.find("input[name=src]").bind("change", function () {
2336				var image = new Image();
2337				var text = $('#wysiwyg_submit',form).find('span').text();
2338				form.find('img').removeAttr("src");
2339				
2340				$('#wysiwyg_submit',form).prop('disabled',true).find('span').text('wait...');
2341				
2342				image.onload = function(){
2343					
2344					form.find('img').attr("src", image.src);
2345					img.asp = image.width/image.height;
2346					form.find('input[name="width"]').val(image.width);
2347					form.find('input[name="height"]').val(image.height);
2348					$('#wysiwyg_submit',form).find('span').text(text);
2349					$('#wysiwyg_submit',form).prop('disabled',false);
2350					
2351				};
2352				image.src = this.value; 
2353			});
2354
2355			return form;
2356		}
2357	};
2358
2359	$.wysiwyg.insertImage = function (object, url, attributes) {
2360		return object.each(function () {
2361			var Wysiwyg = $(this).data("wysiwyg"),
2362				image,
2363				attribute;
2364
2365			if (!Wysiwyg) {
2366				return this;
2367			}
2368
2369			if (!url || url.length === 0) {
2370				return this;
2371			}
2372
2373			if ($.browser.msie) {
2374				Wysiwyg.ui.focus();
2375			}
2376
2377			if (attributes) {
2378				Wysiwyg.editorDoc.execCommand("insertImage", false, "#jwysiwyg#");
2379				image = Wysiwyg.getElementByAttributeValue("img", "src", "#jwysiwyg#");
2380
2381				if (image) {
2382					image.src = url;
2383
2384					for (attribute in attributes) {
2385						if (attributes.hasOwnProperty(attribute)) {
2386							image.setAttribute(attribute, attributes[attribute]);
2387						}
2388					}
2389				}
2390			} else {
2391				Wysiwyg.editorDoc.execCommand("insertImage", false, url);
2392			}
2393
2394			Wysiwyg.saveContent();
2395
2396			$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2397
2398			return this;
2399		});
2400	};
2401})(jQuery);
2402
2403
2404/**
2405 * Controls: Table plugin
2406 * 
2407 * Depends on jWYSIWYG
2408 */
2409(function ($) {
2410	if (undefined === $.wysiwyg) {
2411		throw "wysiwyg.table.js depends on $.wysiwyg";
2412	}
2413
2414	if (!$.wysiwyg.controls) {
2415		$.wysiwyg.controls = {};
2416	}
2417
2418	var insertTable = function (colCount, rowCount, filler) {
2419		if (isNaN(rowCount) || isNaN(colCount) || rowCount === null || colCount === null) {
2420			return;
2421		}
2422
2423		var i, j, html = ['<table border="1" style="width: 100%;"><tbody>'];
2424
2425		colCount = parseInt(colCount, 10);
2426		rowCount = parseInt(rowCount, 10);
2427
2428		if (filler === null) {
2429			filler = "&nbsp;";
2430		}
2431		filler = "<td>" + filler + "</td>";
2432
2433		for (i = rowCount; i > 0; i -= 1) {
2434			html.push("<tr>");
2435			for (j = colCount; j > 0; j -= 1) {
2436				html.push(filler);
2437			}
2438			html.push("</tr>");
2439		}
2440		html.push("</tbody></table>");
2441
2442		return this.insertHtml(html.join(""));
2443	};
2444
2445	/*
2446	 * Wysiwyg namespace: public properties and methods
2447	 */
2448	$.wysiwyg.controls.table = function (Wysiwyg) {
2449		var dialog, colCount, rowCount, formTableHtml,
2450			formTextLegend = "Insert table",
2451			formTextCols   = "Count of columns",
2452			formTextRows   = "Count of rows",
2453			formTextSubmit = "Insert table",
2454			formTextReset  = "Cancel";
2455
2456		if ($.wysiwyg.i18n) {
2457			formTextLegend = $.wysiwyg.i18n.t(formTextLegend, "dialogs.table");
2458			formTextCols = $.wysiwyg.i18n.t(formTextCols, "dialogs.table");
2459			formTextRows = $.wysiwyg.i18n.t(formTextRows, "dialogs.table");
2460			formTextSubmit = $.wysiwyg.i18n.t(formTextSubmit, "dialogs.table");
2461			formTextReset = $.wysiwyg.i18n.t(formTextReset, "dialogs");
2462		}
2463
2464		formTableHtml = '<form class="wysiwyg" title="' + formTextLegend + '"><fieldset>' +
2465			'<label>' + formTextCols + ': <input type="text" name="colCount" value="3" class="integer" /></label><br>' +
2466			'<label>' + formTextRows + ': <input type="text" name="rowCount" value="3" class="integer" /></label><hr>' +
2467			'<button class="button" id="wysiwyg_submit">' + formTextSubmit + '</button> ' +
2468			'<button class="button" id="wysiwyg_reset">' + formTextReset + '</button></fieldset></form>';
2469
2470		if (!Wysiwyg.insertTable) {
2471			Wysiwyg.insertTable = insertTable;
2472		}
2473
2474		if ($.fn.modal) {
2475			$.modal(formTableHtml, {
2476				onShow: function (dialog) {
2477					$("#wysiwyg_submit", dialog.data).click(function (e) {
2478						e.preventDefault();
2479						rowCount = $('input[name="rowCount"]', dialog.data).val();
2480						colCount = $('input[name="colCount"]', dialog.data).val();
2481
2482						Wysiwyg.insertTable(colCount, rowCount, Wysiwyg.defaults.tableFiller);
2483						$.modal.close();
2484					});
2485					$("#wysiwyg_reset", dialog.data).click(function (e) {
2486						e.preventDefault();
2487						$.modal.close();
2488					});
2489					
2490					$("select, input[type=text]", dialog).uniform();
2491					$('.integer', dialog).wl_Number();
2492					
2493				},
2494				maxWidth: Wysiwyg.defaults.formWidth,
2495				maxHeight: Wysiwyg.defaults.formHeight,
2496				overlayClose: true
2497			});
2498		} else if ($.fn.dialog) {
2499			dialog = $(formTableHtml).appendTo("body");
2500			dialog.dialog({
2501				modal: true,
2502				resizable: false,
2503				open: function (event, ui) {
2504					$("#wysiwyg_submit", dialog).click(function (e) {
2505						e.preventDefault();
2506						rowCount = $('input[name="rowCount"]', dialog).val();
2507						colCount = $('input[name="colCount"]', dialog).val();
2508
2509						Wysiwyg.insertTable(colCount, rowCount, Wysiwyg.defaults.tableFiller);
2510						$(dialog).dialog("close");
2511					});
2512					$("#wysiwyg_reset", dialog).click(function (e) {
2513						e.preventDefault();
2514						$(dialog).dialog("close");
2515					});
2516					
2517					$("select, input[type=text]", dialog).uniform();
2518					$('.integer', dialog).wl_Number();
2519					
2520				},
2521				close: function (event, ui) {
2522					dialog.dialog("destroy");
2523					dialog.remove();
2524				}
2525			});
2526		} else {
2527			colCount = prompt(formTextCols, "3");
2528			rowCount = prompt(formTextRows, "3");
2529
2530			Wysiwyg.insertTable(colCount, rowCount, Wysiwyg.defaults.tableFiller);
2531		}
2532
2533		$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2534	};
2535
2536	$.wysiwyg.insertTable = function (object, colCount, rowCount, filler) {
2537		return object.each(function () {
2538			var Wysiwyg = $(this).data("wysiwyg");
2539
2540			if (!Wysiwyg.insertTable) {
2541				Wysiwyg.insertTable = insertTable;
2542			}
2543
2544			if (!Wysiwyg) {
2545				return this;
2546			}
2547
2548			Wysiwyg.insertTable(colCount, rowCount, filler);
2549			$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2550
2551			return this;
2552		});
2553	};
2554})(jQuery);
2555	
2556/**
2557 * Controls: Link plugin
2558 *
2559 * Depends on jWYSIWYG
2560 *
2561 * By: Esteban Beltran (academo) <[email protected]>
2562 */
2563(function ($) {
2564	if (undefined === $.wysiwyg) {
2565		throw "wysiwyg.link.js depends on $.wysiwyg";
2566	}
2567
2568	if (!$.wysiwyg.controls) {
2569		$.wysiwyg.controls = {};
2570	}
2571
2572	/*
2573	* Wysiwyg namespace: public properties and methods
2574	*/
2575	$.wysiwyg.controls.link = {
2576		init: function (Wysiwyg) {
2577			var self = this, elements, dialog, url, a, selection,
2578				formLinkHtml, formTextLegend, formTextUrl, formTextTitle, formTextTarget,
2579				formTextSubmit, formTextReset,
2580				baseUrl;
2581
2582			formTextLegend  = "Insert Link";
2583			formTextUrl     = "Link URL";
2584			formTextTitle   = "Link Title";
2585			formTextTarget  = "Link Target";
2586			formTextSubmit  = "Insert Link";
2587			formTextReset   = "Cancel";
2588
2589			if ($.wysiwyg.i18n) {
2590				formTextLegend = $.wysiwyg.i18n.t(formTextLegend, "dialogs.link");
2591				formTextUrl    = $.wysiwyg.i18n.t(formTextUrl, "dialogs.link");
2592				formTextTitle  = $.wysiwyg.i18n.t(formTextTitle, "dialogs.link");
2593				formTextTarget = $.wysiwyg.i18n.t(formTextTarget, "dialogs.link");
2594				formTextSubmit = $.wysiwyg.i18n.t(formTextSubmit, "dialogs.link");
2595				formTextReset  = $.wysiwyg.i18n.t(formTextReset, "dialogs");
2596			}
2597
2598			formLinkHtml = '<form class="wysiwyg" title="' + formTextLegend + '"><fieldset>' +
2599				'<label>' + formTextUrl + ': <input type="text" name="linkhref" value=""/></label>' +
2600				'<label>' + formTextTitle + ': <input type="text" name="linktitle" value=""/></label>' +
2601				'<label>' + formTextTarget + ': <input type="text" name="linktarget" value=""/></label><hr>' +
2602				'<button class="button" id="wysiwyg_submit">' + formTextSubmit + '</button> ' +
2603				'<button class="button" id="wysiwyg_reset">' + formTextReset + '</button></fieldset></form>';
2604
2605			a = {
2606				self: Wysiwyg.dom.getElement("a"), // link to element node
2607				href: "http://",
2608				title: "",
2609				target: ""
2610			};
2611
2612			if (a.self) {
2613				a.href = a.self.href ? a.self.href : a.href;
2614				a.title = a.self.title ? a.self.title : "";
2615				a.target = a.self.target ? a.self.target : "";
2616			}
2617
2618			if ($.fn.dialog) {
2619				elements = $(formLinkHtml);
2620				elements.find("input[name=linkhref]").val(a.href);
2621				elements.find("input[name=linktitle]").val(a.title);
2622				elements.find("input[name=linktarget]").val(a.target);
2623
2624				if ($.browser.msie) {
2625					dialog = elements.appendTo(Wysiwyg.editorDoc.body);
2626				} else {
2627					dialog = elements.appendTo("body");
2628				}
2629
2630				dialog.dialog({
2631					modal: true,
2632					resizable: false,
2633					open: function (ev, ui) {
2634						$("#wysiwyg_submit", dialog).click(function (e) {
2635							e.preventDefault();
2636
2637							var url = $('input[name="linkhref"]', dialog).val(),
2638								title = $('input[name="linktitle"]', dialog).val(),
2639								target = $('input[name="linktarget"]', dialog).val(),
2640								baseUrl;
2641
2642							if (Wysiwyg.options.controlLink.forceRelativeUrls) {
2643								baseUrl = window.location.protocol + "//" + window.location.hostname;
2644								if (0 === url.indexOf(baseUrl)) {
2645									url = url.substr(baseUrl.length);
2646								}
2647							}
2648
2649							if (a.self) {
2650								if ("string" === typeof (url)) {
2651									if (url.length > 0) {
2652										// to preserve all link attributes
2653										$(a.self).attr("href", url).attr("title", title).attr("target", target);
2654									} else {
2655										$(a.self).replaceWith(a.self.innerHTML);
2656									}
2657								}
2658							} else {
2659								if ($.browser.msie) {
2660									Wysiwyg.ui.returnRange();
2661								}
2662
2663								//Do new link element
2664								selection = Wysiwyg.getRangeText();
2665								img = Wysiwyg.dom.getElement("img");
2666
2667								if ((selection && selection.length > 0) || img) {
2668									if ($.browser.msie) {
2669										Wysiwyg.ui.focus();
2670									}
2671
2672									if ("string" === typeof (url)) {
2673										if (url.length > 0) {
2674											Wysiwyg.editorDoc.execCommand("createLink", false, url);
2675										} else {
2676											Wysiwyg.editorDoc.execCommand("unlink", false, null);
2677										}
2678									}
2679
2680									a.self = Wysiwyg.dom.getElement("a");
2681
2682									$(a.self).attr("href", url).attr("title", title);
2683
2684									/**
2685									 * @url https://github.com/akzhan/jwysiwyg/issues/16
2686									 */
2687									$(a.self).attr("target", target);
2688								} else if (Wysiwyg.options.messages.nonSelection) {
2689									$.dialog(Wysiwyg.options.messages.nonSelection);
2690								}
2691							}
2692
2693							Wysiwyg.saveContent();
2694
2695							$(dialog).dialog("close");
2696						});
2697						$("#wysiwyg_reset", dialog).click(function (e) {
2698							e.preventDefault();
2699							$(dialog).dialog("close");
2700						});
2701					
2702						$("select, input", dialog).uniform();
2703						$('input[name="linkhref"]', dialog).wl_URL();
2704						
2705					},
2706					close: function (ev, ui) {
2707						dialog.dialog("destroy");
2708						dialog.remove();
2709					}
2710				});
2711			} else {
2712				if (a.self) {
2713					url = window.prompt("URL", a.href);
2714
2715					if (Wysiwyg.options.controlLink.forceRelativeUrls) {
2716						baseUrl = window.location.protocol + "//" + window.location.hostname;
2717						if (0 === url.indexOf(baseUrl)) {
2718							url = url.substr(baseUrl.length);
2719						}
2720					}
2721
2722					if ("string" === typeof (url)) {
2723						if (url.length > 0) {
2724							$(a.self).attr("href", url);
2725						} else {
2726							$(a.self).replaceWith(a.self.innerHTML);
2727						}
2728					}
2729				} else {
2730					//Do new link element
2731					selection = Wysiwyg.getRangeText();
2732					img = Wysiwyg.dom.getElement("img");
2733
2734					if ((selection && selection.length > 0) || img) {
2735						if ($.browser.msie) {
2736							Wysiwyg.ui.focus();
2737							Wysiwyg.editorDoc.execCommand("createLink", true, null);
2738						} else {
2739							url = window.prompt(formTextUrl, a.href);
2740
2741							if (Wysiwyg.options.controlLink.forceRelativeUrls) {
2742								baseUrl = window.location.protocol + "//" + window.location.hostname;
2743								if (0 === url.indexOf(baseUrl)) {
2744									url = url.substr(baseUrl.length);
2745								}
2746							}
2747
2748							if ("string" === typeof (url)) {
2749								if (url.length > 0) {
2750									Wysiwyg.editorDoc.execCommand("createLink", false, url);
2751								} else {
2752									Wysiwyg.editorDoc.execCommand("unlink", false, null);
2753								}
2754							}
2755						}
2756					} else if (Wysiwyg.options.messages.nonSelection) {
2757						window.alert(Wysiwyg.options.messages.nonSelection);
2758					}
2759				}
2760
2761				Wysiwyg.saveContent();
2762			}
2763
2764			$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2765		}
2766	};
2767
2768	$.wysiwyg.createLink = function (object, url) {
2769		return object.each(function () {
2770			var oWysiwyg = $(this).data("wysiwyg"),
2771				selection;
2772
2773			if (!oWysiwyg) {
2774				return this;
2775			}
2776
2777			if (!url || url.length === 0) {
2778				return this;
2779			}
2780
2781			selection = oWysiwyg.getRangeText();
2782
2783			if (selection && selection.length > 0) {
2784				if ($.browser.msie) {
2785					oWysiwyg.ui.focus();
2786				}
2787				oWysiwyg.editorDoc.execCommand("unlink", false, null);
2788				oWysiwyg.editorDoc.execCommand("createLink", false, url);
2789			} else if (oWysiwyg.options.messages.nonSelection) {
2790				window.alert(oWysiwyg.options.messages.nonSelection);
2791			}
2792		});
2793	};
2794})(jQuery);
2795
2796/**
2797 * Controls: Element CSS Wrapper plugin
2798 *
2799 * Depends on jWYSIWYG
2800 * 
2801 * By Yotam Bar-On (https://github.com/tudmotu)
2802 */
2803(function ($) {		
2804	if (undefined === $.wysiwyg) {
2805		throw "wysiwyg.cssWrap.js depends on $.wysiwyg";
2806	}
2807	/* For core enhancements #143
2808	$.wysiwyg.ui.addControl("cssWrap", {
2809		visible : false,
2810		groupIndex: 6,
2811		tooltip: "CSS Wrapper",
2812		exec: function () { 
2813				$.wysiwyg.controls.cssWrap.init(this);
2814			}
2815	}
2816	*/	
2817	if (!$.wysiwyg.controls) {
2818		$.wysiwyg.controls = {};
2819	}
2820
2821	/*
2822	 * Wysiwyg namespace: public properties and methods
2823	 */
2824	$.wysiwyg.controls.cssWrap = {
2825		init: function (Wysiwyg) {
2826			var self = this, formWrapHtml, key, translation,
2827			dialogReplacements = {
2828				legend	: "Wrap Element",
2829				wrapperType : "Wrapper Type",
2830				ID : "ID",
2831				"class" : "Class",
2832				wrap  : "Wrap",
2833				unwrap: "Unwrap",
2834				cancel   : "Cancel"
2835			};
2836
2837			formWrapHtml = '<form class="wysiwyg" title="{legend}"><fieldset>' +
2838				'<label>{wrapperType}:<select name="type"><option value="span">Span</option><option value="div">Div</option></select></label><br>' +
2839				'<label>{ID}:<input name="id" type="text"></label>' + 
2840				'<label>{class}:<input name="class" type="text" ></label><hr>' +
2841				'<button class="cssWrap-unwrap" style="display:none;">{unwrap}</button> ' +
2842				'<button class="cssWrap-submit">{wrap}</button> ' +
2843				'<button class="cssWrap-cancel">{cancel}</button></fieldset></form>';
2844
2845			for (key in dialogReplacements) {
2846				if ($.wysiwyg.i18n) {
2847					translation = $.wysiwyg.i18n.t(dialogReplacements[key]);
2848					if (translation === dialogReplacements[key]) { // if not translated search in dialogs 
2849						translation = $.wysiwyg.i18n.t(dialogReplacements[key], "dialogs");
2850					}
2851					dialogReplacements[key] = translation;
2852				}
2853				formWrapHtml = formWrapHtml.replace("{" + key + "}", dialogReplacements[key]);
2854			}
2855			if (!$(".wysiwyg-dialog-wrapper").length) {
2856				$(formWrapHtml).appendTo("body");
2857				$("form.wysiwyg").dialog({
2858					modal: true,
2859					resizable: false,
2860					open: function (ev, ui) {
2861						$this = $(this);
2862						var range	= Wysiwyg.getInternalRange(), common;
2863						// We make sure that there is some selection:
2864						if (range) {
2865							if ($.browser.msie) {
2866								Wysiwyg.ui.focus();
2867							}
2868							common	= $(range.commonAncestorContainer);
2869						} else {
2870							alert("You must select some elements before you can wrap them.");
2871							$this.dialog("close");
2872							return 0;
2873						}
2874						var $nodeName = range.commonAncestorContainer.nodeName.toLowerCase();
2875						// If the selection is already a .wysiwygCssWrapper, then we want to change it and not double-wrap it.
2876						if (common.parent(".wysiwygCssWrapper").length) {
2877							alert(common.parent(".wysiwygCssWrapper").get(0).nodeName.toLowerCase());
2878							$this.find("select[name=type]").val(common.parent(".wysiwygCssWrapper").get(0).nodeName.toLowerCase());
2879							$this.find("select[name=type]").attr("disabled", "disabled");
2880							$this.find("input[name=id]").val(common.parent(".wysiwygCssWrapper").attr("id"));
2881							$this.find("input[name=class]").val(common.parent(".wysiwygCssWrapper").attr("class").replace('wysiwygCssWrapper ', ''));
2882							// Add the "unwrap" button:
2883							$("form.wysiwyg").find(".cssWrap-unwrap").show();
2884							$("form.wysiwyg").find(".cssWrap-unwrap").click(function(e) {
2885								e.preventDefault();
2886								if ($nodeName !== "body") {
2887									common.unwrap();
2888								}
2889								$this.dialog("close");
2890								return 1;
2891							});
2892						}
2893						// Submit button.
2894						$("form.wysiwyg").find(".cssWrap-submit").click(function(e) {
2895							e.preventDefault();
2896							var $wrapper = $("form.wysiwyg").find("select[name=type]").val();
2897							var $id = $("form.wysiwyg").find("input[name=id]").val();
2898							var $class = $("form.wysiwyg").find("input[name=class]").val();
2899							if ($nodeName !== "body") {
2900								// If the selection is already a .wysiwygCssWrapper, then we want to change it and not double-wrap it.
2901								if (common.parent(".wysiwygCssWrapper").length) {
2902									common.parent(".wysiwygCssWrapper").attr("id", $class);
2903									common.parent(".wysiwygCssWrapper").attr("class", $class);
2904								} else {
2905									common.wrap('<'+$wrapper+' id="'+$id+'" class="'+"wysiwygCssWrapper "+$class+'"/>');
2906								}
2907							} else {
2908								// Currently no implemntation for if $nodeName == 'body'.
2909							}
2910							$this.dialog("close");
2911						});
2912						// Cancel button.
2913						$("form.wysiwyg").find(".cssWrap-cancel").click(function(e) {
2914							e.preventDefault();
2915							$this.dialog("close");
2916							return 1;
2917						});
2918						$("form.wysiwyg").find("select, input[type=text]").uniform();
2919					},
2920					close: function () {
2921						$(this).dialog("destroy");
2922						$(this).remove();
2923					}
2924				});
2925				Wysiwyg.saveContent();
2926			}
2927			$(Wysiwyg.editorDoc).trigger("editorRefresh.wysiwyg");
2928			return 1;
2929		}
2930	}
2931})(jQuery);
2932});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.