Last active
April 10, 2017 05:17
-
-
Save sharapeco/aca28dad498188d818b8f421f751ffc8 to your computer and use it in GitHub Desktop.
wysiwygetize (by Spharian) readable code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jQuery.fn.wysxiwygetize = function(aOptions) { | |
var selection; | |
var $self = this, | |
$lastFocusedTextarea, | |
$lastFocusedEditor, | |
isFocused = false, | |
scrollTop, | |
fullScreenEditorId, | |
tabIndex = 0, | |
hasTabInput = false, | |
isMouseUp = true, | |
isSourceMode = false, | |
$document = $(document), | |
$window = $(window), | |
$btnsList = $(".buttons-list"); | |
var $fullscreenContainer = $("#fullscreen-container"); | |
var copyStyleList = ["border-top-width", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "box-shadow", "color", "font-family", "font-size", "background-color"], | |
editorStyles = $self.css(copyStyleList), | |
textareaStyles = $(".styled-output").eq(0).prev().focus().css(copyStyleList); | |
function getSelection() { | |
if (window.getSelection) { | |
return window.getSelection(); | |
} else if (document.selection.createRange) { | |
return document.selection.createRange(); | |
} | |
return null; | |
} | |
function updateSelectionHandler() { | |
if (!isFocused) { | |
return; | |
} | |
setTimeout(function() { | |
var rect; | |
selection = getSelection(); | |
if (!selection) { | |
return; | |
} | |
$btnsList.show(); | |
if (selection.getRangeAt) { | |
rect = selection.getRangeAt(0).getClientRects()[0]; | |
} else { | |
rect = selection.getClientRects()[0]; | |
} | |
if (rect) { | |
$btnsList.css({ | |
top: rect.bottom + 10, | |
left: rect.left | |
}) | |
} | |
if (!selection.getRangeAt(0).collapsed || selection.text) { | |
$btnsList.show(); | |
} else { | |
$btnsList.hide(); | |
} | |
}) | |
} | |
function resizeHandler() { | |
$(".styled-output").each(function() { | |
var $editor = $(this), | |
$textarea = $editor.prev(); | |
$editor.css({ | |
width: $textarea.outerWidth(), | |
height: $textarea.outerHeight(), | |
top: 0, | |
left: 0 | |
}) | |
}) | |
} | |
function exitFullscreen() { | |
var $wrapper = $("[data-wysigetize-item=" + fullScreenEditorId + "]"), | |
val = $fullscreenContainer.find("textarea").val(), | |
html = $fullscreenContainer.find(".styled-output").html(); | |
$wrapper.find("textarea").val(val); | |
$wrapper.find(".styled-output").html(html); | |
isSourceMode ? $(".viewsource-btn").addClass(options.viewsource.activeClass) : $(".viewsource-btn").removeClass(options.viewsource.activeClass); | |
$fullscreenContainer.stop(false, true).fadeOut(function() { | |
$fullscreenContainer.empty(); | |
$(".wysigetize-textarea-wrapper").removeClass("fullscreen"); | |
$("body, html").animate({ | |
scrollTop: scrollTop | |
}, 200) | |
}); | |
$("html, body").removeClass("no-wy-scroll") | |
} | |
function toggleBlockElement(tagName) { | |
selection.anchorNode.parentNode.localName === tagName ? document.execCommand("formatBlock", false, "<p>") : document.execCommand("formatBlock", false, "<" + tagName + ">"); | |
$btnsList.hide(); | |
} | |
var options = $.extend({ | |
buttons: { | |
bold: "B", | |
italic: "I", | |
underline: "U", | |
link: "Link", | |
h1: "H1", | |
h2: "H2", | |
h3: "H3" | |
}, | |
viewsource: { | |
display: true, | |
label: "View source", | |
"class": "viewsource-style", | |
activeClass: "active-btn" | |
}, | |
fullscreen: { | |
display: true, | |
label: "Fullscreen", | |
exitLabel: "Quit fullscreen", | |
"class": "fullscreen-style" | |
}, | |
style: "dark" | |
}, aOptions), | |
$body = $("body"), | |
$btnsList = $('<ul class="buttons-list"></ul>'), | |
$btns = $(".buttons-list button"), | |
editorCount = 0; | |
var toolbarBG, toolbarHoverBG, toolbarFG; | |
switch (options.style) { | |
case "dark": | |
toolbarBG = "#383838"; | |
toolbarHoverBG = "#272626"; | |
toolbarFG = "#fff"; | |
break; | |
case "light": | |
toolbarBG = "#F7F7F7"; | |
toolbarHoverBG = "#E1E1E1"; | |
toolbarFG = "#252525"; | |
} | |
$("html").addClass("wysiwygetize"); | |
$body.append($btnsList); | |
$body.append('<div id="fullscreen-container"></div>'); | |
$btnsList.css({ | |
background: toolbarHoverBG, | |
borderRadius: "3px", | |
overflow: "hidden", | |
margin: 0, | |
padding: 0, | |
listStyle: "none", | |
display: "none", | |
position: "fixed", | |
zIndex: 20, | |
border: "1px solid #272626" | |
}); | |
var btnsCount = 0; | |
$.each(options.buttons, function(btnValue, btnLabel) { | |
btnsCount++; | |
var n = 1; | |
btnsCount === 1 && (n = 0); | |
var $btn = $('<button data-tag="' + btnValue + '">' + btnLabel + "</button>"); | |
$btn.css({ | |
background: toolbarBG, | |
border: "none", | |
color: toolbarFG, | |
cursor: "pointer", | |
fontFamily: "Arial, sans-serif", | |
fontSize: "17px", | |
margin: 0, | |
outline: "none", | |
display: "block", | |
padding: "13px 20px" | |
}); | |
switch (btnValue) { | |
case "bold": | |
$btn.css("font-weight", "bold"); | |
break; | |
case "italic": | |
$btn.css("font-style", "italic"); | |
break; | |
case "underline": | |
$btn.css("text-decoration", "underline") | |
} | |
$btnsList.append('<li style="float: left; margin: 0 0 0 ' + n + 'px">' + $btn[0].outerHTML + "</li>") | |
}); | |
$btns.hover(function() { | |
$(this).css("background", toolbarHoverBG); | |
}, function() { | |
$btns.css("background", toolbarBG); | |
}); | |
$self.each(function() { | |
var $textarea = $(this); | |
tabIndex++; | |
$textarea.css({ | |
outline: "none", | |
resize: "none" | |
}); | |
editorCount++; | |
$textarea.wrap('<div class="wysigetize-textarea-wrapper" data-wysigetize-item="' + editorCount + '">'); | |
options.viewsource.display && options.fullscreen.display ? $textarea.after('<ul class="wysigetize-controls" style="top: -' + $textarea.css("margin-bottom") + '"><li class="fullscreen-btn ' + options.fullscreen.class + '">' + options.fullscreen.label + '</li><li class="viewsource-btn ' + options.viewsource.class + '">' + options.viewsource.label + "</li></ul>") : options.viewsource.display ? $textarea.after('<ul class="wysigetize-controls" style="top: -' + $textarea.css("margin-bottom") + '"><li class="viewsource ' + options.viewsource.class + '">' + options.viewsource.label + "</li></ul>") : options.fullscreen.display && $textarea.after('<ul class="wysigetize-controls" style="top: -' + $textarea.css("margin-bottom") + '"><li class="fullscreen ' + options.fullscreen.class + '">' + options.fullscreen.label + "</li></ul>"); | |
$textarea.after($('<div class="styled-output" contenteditable="true" tabindex="' + tabIndex + '">').css({ | |
backgroundColor: $textarea.css("background-color"), | |
borderRadius: $textarea.css("border-radius"), | |
position: "absolute", | |
top: 0, | |
left: 0, | |
border: $textarea.css("border"), | |
width: $textarea.outerWidth(), | |
height: $textarea.outerHeight(), | |
boxShadow: $textarea.css("box-shadow"), | |
marginTop: $textarea.css("margin-top"), | |
marginLeft: $textarea.css("margin-left"), | |
padding: $textarea.css("padding-top"), | |
fontFamily: $textarea.css("font-family"), | |
fontSize: $textarea.css("font-size"), | |
lineHeight: $textarea.css("line-height"), | |
outline: "none", | |
overflowX: "hidden", | |
borderColor: $textarea.css("border-top-color"), | |
borderWidth: $textarea.css("border-top-width"), | |
border: $textarea.css("border-top-width") + " solid " + $textarea.css("border-top-color"), | |
wordWrap: "break-word" | |
})) | |
}); | |
var $textareaWrapper = $(".wysigetize-textarea-wrapper"); | |
$textareaWrapper.width($textareaWrapper.find("textarea").outerWidth()); | |
// もとの textarea に内容を反映する | |
$document.on("keyup", ".styled-output", function() { | |
var $editor = $(this); | |
$editor.prev().val($editor.html()); | |
}); | |
// Tab フォーカスしたときに内容を選択状態にする | |
$document.on("focus", ".styled-output", function() { | |
isFocused = true; | |
var $editor = $(this), selection, range; | |
if (hasTabInput) { | |
if (document.body.createTextRange) { | |
range = document.body.createTextRange(); | |
range.moveToElementText(element); | |
range.select(); | |
} else if (window.getSelection) { | |
selection = window.getSelection(), | |
range = document.createRange(); | |
range.selectNodeContents($editor[0]); | |
selection.removeAllRanges(); | |
selection.addRange(range); | |
} | |
hasTabInput = false; | |
} | |
}); | |
$document.on("blur", ".styled-output", function() { | |
var $editor = $(this); | |
isFocused = false; | |
$lastFocusedEditor = $editor.first(); | |
$lastFocusedTextarea = $editor.first().prev(); | |
if (isMouseUp) { | |
$editor.css(editorStyles); | |
$btnsList.hide(); | |
} | |
}); | |
$(".styled-output").each(function() { | |
var $editor = $(this); | |
$editor.html($editor.prev().text()) | |
}); | |
$(".styled-output").eq(0).prev().blur(); | |
$(".styled-output").focus(function() { | |
$(this).css(textareaStyles) | |
}); | |
$document.mouseup(function() { | |
updateSelectionHandler() | |
}); | |
$document.keyup(function() { | |
updateSelectionHandler(); | |
}); | |
$document.click(function() { | |
setTimeout(function() { | |
resizeHandler(); | |
}); | |
}); | |
$document.keydown(function(e) { | |
if (e.which === 27) { // ESC | |
exitFullscreen(); | |
} | |
}); | |
$window.keydown(function(e) { | |
if (e.which === 9) { // TAB | |
hasTabInput = true; | |
} | |
}); | |
$window.resize(function() { | |
resizeHandler() | |
}); | |
$window.scroll(function() { | |
if (selection && selection.rangeCount !== 0) { | |
var rect = selection.getRangeAt(0).getClientRects()[0]; | |
if (rect) { | |
var left = rect.left, | |
bottom = rect.bottom; | |
$btnsList.css({ | |
top: bottom + 10, | |
left: left | |
}) | |
} | |
} | |
}); | |
$(".styled-output").scroll(function() { | |
$btnsList.hide() | |
}); | |
$document.on("mousedown", ".buttons-list button[data-tag]", function() { | |
isMouseUp = false; | |
}); | |
$document.on("mouseup", ".buttons-list button[data-tag]", function() { | |
isMouseUp = true; | |
}); | |
$document.on("click", ".buttons-list button[data-tag]", function() { | |
$($lastFocusedEditor).focus(); | |
switch ($(this).data("tag")) { | |
case "bold": | |
document.execCommand("bold", false, null); | |
break; | |
case "italic": | |
document.execCommand("italic", false, null); | |
break; | |
case "underline": | |
document.execCommand("underline", false, null); | |
break; | |
case "link": | |
var linkURL = prompt("Specify a link"); | |
linkURL && document.execCommand("createLink", false, linkURL); | |
break; | |
case "h1": | |
toggleBlockElement("h1"); | |
break; | |
case "h2": | |
toggleBlockElement("h2"); | |
break; | |
case "h3": | |
toggleBlockElement("h3") | |
} | |
var html = $($lastFocusedEditor).html(); | |
$($lastFocusedTextarea).val(html); | |
}); | |
$document.on("click", ".viewsource-btn", function() { | |
var $btn = $(this), | |
$editor = $btn.parents(".wysigetize-textarea-wrapper").find(".styled-output"); | |
$btn.toggleClass(options.viewsource.activeClass); | |
$editor.toggle(); | |
isSourceMode = !isSourceMode; | |
$(".styled-output").each(function() { | |
var $editor = $(this); | |
$editor.html($editor.prev().val()) | |
}) | |
}); | |
$document.on("click", ".fullscreen-btn", function() { | |
var $clonedWrapper, $origWrapper, scrollTop, html; | |
if ($fullscreenContainer.children().length === 0) { | |
scrollTop = $document.scrollTop(); | |
$origWrapper = $(this).parents(".wysigetize-textarea-wrapper"); | |
html = $origWrapper.find("textarea").val(); | |
$clonedWrapper = $origWrapper.clone(); | |
fullScreenEditorId = $clonedWrapper.data("wysigetize-item"); | |
$fullscreenContainer.append($clonedWrapper); | |
$fullscreenContainer.fadeIn(function() { | |
$("html, body").addClass("no-wy-scroll") | |
}); | |
$clonedWrapper.addClass("fullscreen").removeAttr("data-wysigetize-item"); | |
$clonedWrapper.find(".fullscreen-btn").text(options.fullscreen.exitLabel); | |
$clonedWrapper.find("textarea").val(html); | |
} else { | |
exitFullscreen(); | |
} | |
}) | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment