diff --git a/build/jquery.notebook.min.css b/build/jquery.notebook.min.css index a0fb2d4..3d722cc 100644 --- a/build/jquery.notebook.min.css +++ b/build/jquery.notebook.min.css @@ -1 +1 @@ -.jquery-notebook.editor{position:relative;outline:0;min-height:50px}.jquery-notebook.editor p{z-index:2;position:relative;min-height:1px}.jquery-notebook.editor p.placeholder{position:absolute;z-index:1;opacity:.5;pointer-events:none}.jquery-notebook p:first-child,.jquery-notebook p.placeholder+p{margin-top:0}.jquery-notebook.bubble{position:absolute;z-index:3;opacity:1;height:43px;left:0;top:0;opacity:0;border-radius:5px;-webkit-box-shadow:2px 2px 2px -1px rgba(0,0,0,.7),inset 0 0 1px rgba(255,255,255,.07),inset 0 0 2px rgba(255,255,255,.15000000000000002);box-shadow:2px 2px 2px -1px rgba(0,0,0,.7),inset 0 0 1px rgba(255,255,255,.07),inset 0 0 2px rgba(255,255,255,.15000000000000002);background:#323232;-webkit-transition:.5s ease-in-out;transition:.5s ease-in-out}.jquery-notebook.bubble.active{opacity:1}.jquery-notebook.bubble:after{content:'';display:block;position:absolute;left:50%;bottom:-3px;margin-left:-4px;width:8px;height:8px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background:#323232;-webkit-box-shadow:0 0 2px #262625;box-shadow:0 0 2px #262625}.jquery-notebook.bubble ul{padding:0;margin:0;list-style:none}.jquery-notebook.bubble ul li{display:inline}.jquery-notebook.bubble button{outline:0;border:0;background:transparent;width:41px;height:42px;font-family:FontAwesome;color:#fff;font-size:14pt;cursor:pointer;margin-top:0}.jquery-notebook.bubble button.active{color:#1ed228}.jquery-notebook.bubble button.bold:after{content:"\f032"}.jquery-notebook.bubble button.italic:after{content:"\f033"}.jquery-notebook.bubble button.underline:after{content:"\f0cd"}.jquery-notebook.bubble button.anchor:after{content:"\f0c1"}.jquery-notebook.bubble button.paste:after{content:"\f0ea"}.jquery-notebook.bubble button.h1:after{font-family:inherit;content:"h1"}.jquery-notebook.bubble button.h2:after{font-family:inherit;content:"h2"}.jquery-notebook.bubble button.ul:after{font-family:inherit;content:"\f0ca"}.jquery-notebook.bubble button.ol:after{font-family:inherit;content:"\f0cb"}.jquery-notebook .link-area{display:none}.jquery-notebook .link-area button{float:left}.jquery-notebook .link-area button:after{content:"\f00d"}.jquery-notebook input[type=text]{width:220px;height:28px;margin:6px 0 0 10px;background:0;border:0;outline:0;color:#fff;font-size:14px;float:left} \ No newline at end of file +.jquery-notebook-container{position:relative}.jquery-notebook.editor{position:relative;outline:0;min-height:50px}.jquery-notebook.editor p{z-index:2;position:relative;min-height:1px}.jquery-notebook.editor p.placeholder{position:absolute;z-index:1;opacity:.5;pointer-events:none}.jquery-notebook p:first-child,.jquery-notebook p.placeholder+p{margin-top:0}.jquery-notebook.bubble{position:absolute;z-index:3;opacity:1;height:43px;left:0;top:0;opacity:0;border-radius:5px;border:1px solid #1C1C1B;-webkit-box-shadow:0 2px 3px rgba(0,0,0,.2),inset 0 0 2px rgba(255,255,255,.2);box-shadow:0 2px 3px rgba(0,0,0,.2),inset 0 0 2px rgba(255,255,255,.2);background:#373735;background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,rgba(55,55,53,1)),color-stop(100%,rgba(40,40,39,1)));background:-webkit-linear-gradient(top,rgba(55,55,53,1) 0,rgba(40,40,39,1) 100%);background:-webkit-gradient(linear,left top,left bottom,from(rgba(55,55,53,1)),to(rgba(40,40,39,1)));background:-webkit-linear-gradient(top,rgba(55,55,53,1) 0,rgba(40,40,39,1) 100%);background:linear-gradient(to bottom,rgba(55,55,53,1) 0,rgba(40,40,39,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#373735', endColorstr='#282827', GradientType=0);-webkit-transition:.15s ease-in-out;transition:.15s ease-in-out;-webkit-transition:.15s ease-in-out}.jquery-notebook.bubble.active{opacity:1}.jquery-notebook.bubble.jump{-webkit-transition:opacity .15s;transition:opacity .15s;-webkit-transition:opacity .15s}.jquery-notebook.bubble:after{content:'';display:block;position:absolute;left:50%;bottom:-6px;margin-left:-8px;width:16px;height:16px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background:#282827;-webkit-box-shadow:2px 2px 1px rgba(0,0,0,.1);box-shadow:2px 2px 1px rgba(0,0,0,.1)}.jquery-notebook.bubble ul{padding:0;margin:0;list-style:none}.jquery-notebook.bubble ul li{display:inline}.jquery-notebook.bubble button{outline:0;border:0;background:transparent;width:41px;height:42px;font-family:FontAwesome;color:#fff;font-size:14pt;cursor:pointer}.jquery-notebook.bubble button.active{color:#60D778}.jquery-notebook.bubble button.bold:after{content:"\f032"}.jquery-notebook.bubble button.italic:after{content:"\f033"}.jquery-notebook.bubble button.underline:after{content:"\f0cd"}.jquery-notebook.bubble button.anchor:after{content:"\f0c1"}.jquery-notebook.bubble button.paste:after{content:"\f0ea"}.jquery-notebook.bubble button.h1:after{font-family:inherit;content:"h1"}.jquery-notebook.bubble button.h2:after{font-family:inherit;content:"h2"}.jquery-notebook.bubble button.ul:after{font-family:inherit;content:"\f0ca"}.jquery-notebook.bubble button.ol:after{font-family:inherit;content:"\f0cb"}.jquery-notebook .link-area{display:none}.jquery-notebook .link-area button{float:left}.jquery-notebook .link-area button:after{content:"\f00d"}.jquery-notebook input[type=text]{width:220px;height:28px;margin:6px 0 0 10px;background:0;border:0;outline:0;color:#fff;font-size:14px;float:left} \ No newline at end of file diff --git a/build/jquery.notebook.min.js b/build/jquery.notebook.min.js index 62583b2..d999ceb 100644 --- a/build/jquery.notebook.min.js +++ b/build/jquery.notebook.min.js @@ -1,403 +1 @@ -! function(e, t, n) { - var o, i = function() { - var e = function(e) { - return e && "none" != e ? e.match(/(-?[0-9\.]+)/g) : [1, 0, 0, 1, 0, 0] - }, t = function(e) { - return e.css("-webkit-transform") || e.css("transform") || e.css("-moz-transform") || e.css("-o-transform") || e.css("-ms-transform") - }, n = function(n) { - var o = t(n); - return e(o) - }, o = function(e, t) { - e.css("-webkit-transform", t), e.css("-moz-transform", t), e.css("-o-transform", t), e.css("-ms-transform", t), e.css("transform", t) - }, i = function(e) { - return "matrix(" + e[0] + ", " + e[1] + ", " + e[2] + ", " + e[3] + ", " + e[4] + ", " + e[5] + ")" - }, a = function(e) { - var t = n(e); - return { - x: parseInt(t[4]), - y: parseInt(t[5]) - } - }, l = function(e, t) { - var a = n(e); - a[0] = a[3] = t; - var l = i(a); - o(e, l) - }, c = function(e, t, a) { - var l = n(e); - l[4] = t, l[5] = a; - var c = i(l); - o(e, c) - }, r = function(e, t) { - var a = n(e), - l = t * (Math.PI / 180), - c = -1 * l; - a[1] = l, a[2] = c; - var r = i(a); - o(e, r) - }; - return { - scale: l, - translate: c, - rotate: r, - getTranslate: a - } - }(), - a = "MacIntel" == n.navigator.platform, - l = 0, - c = 0, - r = { - command: !1, - shift: !1, - isSelecting: !1 - }, s = { - 66: "bold", - 73: "italic", - 85: "underline", - 112: "h1", - 113: "h2", - 122: "undo" - }, u = { - keyboard: { - isCommand: function(e, t, n) { - a && e.metaKey || !a && e.ctrlKey ? t() : n() - }, - isShift: function(e, t, n) { - e.shiftKey ? t() : n() - }, - isModifier: function(e, t) { - var n = e.which, - o = s[n]; - o && t.call(this, o) - }, - isEnter: function(e, t) { - 13 === e.which && t() - }, - isArrow: function(e, t) { - (e.which >= 37 || e.which <= 40) && t() - } - }, - html: { - addTag: function(n, o, i, a) { - var l = e(t.createElement(o)); - return l.attr("contenteditable", Boolean(a)), l.append(" "), n.append(l), i && (r.focusedElement = n.children().last(), u.cursor.set(n, 0, r.focusedElement)), l - } - }, - cursor: { - set: function(e, o, i) { - var a; - if (t.createRange) { - a = t.createRange(); - var l = n.getSelection(), - c = e.children().last(), - r = c.html().length - 1, - s = i ? i[0] : c[0], - u = "undefined" != typeof o ? o : r; - a.setStart(s, u), a.collapse(!0), l.removeAllRanges(), l.addRange(a) - } else a = t.body.createTextRange(), a.moveToElementText(i), a.collapse(!1), a.select() - } - }, - selection: { - save: function() { - if (n.getSelection) { - var e = n.getSelection(); - if (e.rangeCount > 0) return e.getRangeAt(0) - } else if (t.selection && t.selection.createRange) return t.selection.createRange(); - return null - }, - restore: function(e) { - if (e) - if (n.getSelection) { - var o = n.getSelection(); - o.removeAllRanges(), o.addRange(e) - } else t.selection && e.select && e.select() - }, - getText: function() { - var e = ""; - return n.getSelection ? e = n.getSelection().toString() : t.getSelection ? e = t.getSelection().toString() : t.selection && (e = t.selection.createRange().text), e - }, - clear: function() { - window.getSelection ? window.getSelection().empty ? window.getSelection().empty() : window.getSelection().removeAllRanges && window.getSelection().removeAllRanges() : document.selection && document.selection.empty() - }, - getContainer: function(e) { - return n.getSelection && e && e.commonAncestorContainer ? e.commonAncestorContainer : t.selection && e && e.parentElement ? e.parentElement() : null - } - }, - validation: { - isUrl: function(e) { - return /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test(e) - } - } - }, d = { - updatePos: function(t, o) { - var a = n.getSelection(), - l = a.getRangeAt(0), - c = l.getBoundingClientRect(), - r = o.width(), - s = o.height(), - u = (t.offset().left, { - x: c.left + c.width / 2 - r / 2, - y: c.top - s - 8 + e(document).scrollTop() - }); - i.translate(o, u.x, u.y) - }, - updateState: function(e, t) { - t.find("button").removeClass("active"); - var o = n.getSelection(), - i = []; - d.checkForFormatting(o.focusNode, i); - for (var a = { - b: "bold", - i: "italic", - h1: "h1", - h2: "h2", - a: "anchor", - ul: "ul", - ol: "ol" - }, l = 0; l < i.length; l++) { - var c = i[l]; - t.find("button." + a[c]).addClass("active") - } - }, - checkForFormatting: function(e, t) { - var n = ["b", "i", "u", "h1", "h2", "ol", "ul", "li", "a"]; - ("#text" === e.nodeName || -1 != n.indexOf(e.nodeName.toLowerCase())) && ("#text" != e.nodeName && t.push(e.nodeName.toLowerCase()), d.checkForFormatting(e.parentNode, t)) - }, - buildMenu: function(t, n) { - var i = u.html.addTag(n, "ul", !1, !1); - for (var a in o.modifiers) { - var l = u.html.addTag(i, "li", !1, !1), - c = u.html.addTag(l, "button", !1, !1); - c.attr("editor-command", o.modifiers[a]), c.addClass(o.modifiers[a]) - } - n.find("button").click(function(n) { - n.preventDefault(); - var o = e(this).attr("editor-command"); - m.commands[o].call(t, n) - }); - var r = u.html.addTag(n, "div", !1, !1); - r.addClass("link-area"); - var s = u.html.addTag(r, "input", !1, !1); - s.attr({ - type: "text" - }); - var d = u.html.addTag(r, "button", !1, !1); - d.click(function(t) { - t.preventDefault(); - e(this).closest(".editor"); - e(this).closest(".link-area").hide(), e(this).closest(".bubble").find("ul").show() - }) - }, - show: function() { - var t = e(this).parent().find(".bubble"); - t.length || (t = u.html.addTag(e(this).parent(), "div", !1, !1), t.addClass("jquery-notebook bubble"), d.buildMenu(this, t)), t.show(), d.updateState(this, t), t.addClass("active"), d.updatePos(e(this), t) - }, - update: function() { - var t = e(this).parent().find(".bubble"); - d.updateState(this, t) - }, - clear: function() { - var t = e(this).parent().find(".bubble"); - t.hasClass("active") && (t.removeClass("active"), d.hideLinkInput.call(this), d.showButtons.call(this), setTimeout(function() { - t.hasClass("active") || t.hide() - }, 500)) - }, - hideButtons: function() { - e(this).parent().find(".bubble").find("ul").hide() - }, - showButtons: function() { - e(this).parent().find(".bubble").find("ul").show() - }, - showLinkInput: function(t) { - d.hideButtons.call(this); - var n = this, - o = e(this).parent().find(".bubble").find("input[type=text]"), - i = o.closest(".jquery-notebook").find("button.anchor").hasClass("active"); - o.unbind("keydown"), o.keydown(function(o) { - var a = e(this); - u.keyboard.isEnter(o, function() { - o.preventDefault(); - var e = a.val(); - u.validation.isUrl(e) ? (o.url = e, m.commands.createLink(o, t), d.clear.call(n)) : "" === e && i && (m.commands.removeLink(o, t), d.clear.call(n)) - }) - }), o.bind("paste", function() { - var t = e(this); - setTimeout(function() { - var e = t.val(); - /http:\/\/https?:\/\//.test(e) && (e = e.substring(7), t.val(e)) - }, 1) - }); - var a = "http://"; - if (i) { - var l = e(u.selection.getContainer(t)).closest("a"); - a = l.prop("href") || a - } - e(this).parent().find(".link-area").show(), o.val(a).focus() - }, - hideLinkInput: function() { - e(this).parent().find(".bubble").find(".link-area").hide() - } - }, f = { - bindEvents: function(t) { - t.keydown(h.keydown), t.keyup(h.keyup), t.focus(h.focus), t.bind("paste", m.paste), t.mousedown(h.mouseClick), t.mouseup(h.mouseUp), t.mousemove(h.mouseMove), t.blur(h.blur), e("body").mouseup(function(e) { - e.target == e.currentTarget && r.isSelecting && h.mouseUp.call(t, e) - }) - }, - setPlaceholder: function(t) { - if (/^\s*$/.test(e(this).text())) { - e(this).empty(); - var n = u.html.addTag(e(this), "p").addClass("placeholder"); - n.append(e(this).attr("editor-placeholder")), u.html.addTag(e(this), "p", "undefined" != typeof t.focus ? t.focus : !1, !0) - } else e(this).find(".placeholder").remove() - }, - removePlaceholder: function() { - e(this).find(".placeholder").remove() - }, - preserveElementFocus: function() { - var e = n.getSelection() ? n.getSelection().anchorNode : t.activeElement; - if (e) { - var o = e.parentNode, - i = o !== r.focusedElement, - a = this.children, - l = 0; - o === this && (o = e); - for (var c = 0; c < a.length; c++) - if (o === a[c]) { - l = c; - break - } - i && (r.focusedElement = o, r.focusedElementIndex = l) - } - }, - prepare: function(e, t) { - if (o = t, e.attr("editor-mode", o.mode), e.attr("editor-placeholder", o.placeholder), e.attr("contenteditable", !0), e.css("position", "relative"), e.addClass("jquery-notebook editor"), f.setPlaceholder.call(e, {}), f.preserveElementFocus.call(e), o.autoFocus === !0) { - var n = e.find("p:not(.placeholder)"); - u.cursor.set(e, 0, n) - } - } - }, h = { - keydown: function(e) { - var t = this; - r.command && 65 === e.which && setTimeout(function() { - d.show.call(t) - }, 50), u.keyboard.isCommand(e, function() { - r.command = !0 - }, function() { - r.command = !1 - }), u.keyboard.isShift(e, function() { - r.shift = !0 - }, function() { - r.shift = !1 - }), u.keyboard.isModifier.call(this, e, function(t) { - r.command && m.commands[t].call(this, e) - }), r.shift ? u.keyboard.isArrow.call(this, e, function() { - setTimeout(function() { - var e = u.selection.getText(); - "" !== e ? d.show.call(t) : d.clear.call(t) - }, 100) - }) : u.keyboard.isArrow.call(this, e, function() { - d.clear.call(t) - }), 13 === e.which && m.enterKey.call(this, e), 27 === e.which && d.clear.call(this), 86 === e.which && m.paste.call(this, e) - }, - keyup: function(t) { - u.keyboard.isCommand(t, function() { - r.command = !1 - }, function() { - r.command = !0 - }), f.preserveElementFocus.call(this), f.removePlaceholder.call(this), /^\s*$/.test(e(this).text()) && (e(this).empty(), u.html.addTag(e(this), "p", !0, !0)) - }, - focus: function() { - r.command = !1, r.shift = !1 - }, - mouseClick: function(t) { - var n = this; - if (r.isSelecting = !0, 2 === t.button) return setTimeout(function() { - d.show.call(n) - }, 50), void t.preventDefault(); - if (e(this).find(".bubble:visible").length) { - var o = e(this).find(".bubble:visible"), - i = o.offset().left, - a = o.offset().top, - s = o.width(), - u = o.height(); - if (l > i && i + s > l && c > a && a + u > c) return - } - }, - mouseUp: function(e) { - e.preventDefault(); - var t = this; - r.isSelecting = !1, setTimeout(function() { - var e = u.selection.save(); - e.collapsed ? d.clear.call(t) : d.show.call(t) - }, 50) - }, - mouseMove: function(e) { - l = e.pageX, c = e.pageY - }, - blur: function() { - f.setPlaceholder.call(this, { - focus: !1 - }) - } - }, m = { - commands: { - bold: function(e) { - e.preventDefault(), t.execCommand("bold", !1), d.update.call(this) - }, - italic: function(e) { - e.preventDefault(), t.execCommand("italic", !1), d.update.call(this) - }, - underline: function(e) { - e.preventDefault(), t.execCommand("underline", !1), d.update.call(this) - }, - anchor: function(e) { - e.preventDefault(); - var t = u.selection.save(); - d.showLinkInput.call(this, t) - }, - createLink: function(e, n) { - u.selection.restore(n), t.execCommand("createLink", !1, e.url), d.update.call(this) - }, - removeLink: function(t, n) { - var o = e(u.selection.getContainer(n)).closest("a"); - o.contents().first().unwrap() - }, - h1: function(n) { - n.preventDefault(), e(window.getSelection().anchorNode.parentNode).is("h1") ? t.execCommand("formatBlock", !1, "

") : t.execCommand("formatBlock", !1, "

"), d.update.call(this) - }, - h2: function(n) { - n.preventDefault(), e(window.getSelection().anchorNode.parentNode).is("h2") ? t.execCommand("formatBlock", !1, "

") : t.execCommand("formatBlock", !1, "

"), d.update.call(this) - }, - ul: function(e) { - e.preventDefault(), t.execCommand("insertUnorderedList", !1), d.update.call(this) - }, - ol: function(e) { - e.preventDefault(), t.execCommand("insertOrderedList", !1), d.update.call(this) - }, - undo: function(e) { - e.preventDefault(), t.execCommand("undo", !1) - } - }, - enterKey: function(t) { - return "inline" === e(this).attr("editor-mode") ? void t.preventDefault() : void 0 - }, - paste: function() { - var t = e(this); - setTimeout(function() { - t.find("*").each(function() { - var t = e(this); - e.each(this.attributes, function() { - "class" === this.name && t.hasClass("placeholder") || t.removeAttr(this.name) - }) - }) - }, 100) - } - }; - e.fn.notebook = function(t) { - return t = e.extend({}, e.fn.notebook.defaults, t), f.prepare(this, t), f.bindEvents(this), this - }, e.fn.notebook.defaults = { - autoFocus: !1, - placeholder: "Your text here...", - mode: "multiline", - modifiers: ["bold", "italic", "underline", "h1", "h2", "ol", "ul", "anchor"] - } -}(jQuery, document, window); \ No newline at end of file +!function(e,t,n){var o,i=function(){var e=function(e){return e&&"none"!=e?e.match(/(-?[0-9\.]+)/g):[1,0,0,1,0,0]},t=function(e){return e.css("-webkit-transform")||e.css("transform")||e.css("-moz-transform")||e.css("-o-transform")||e.css("-ms-transform")},n=function(n){var o=t(n);return e(o)},o=function(e,t){e.css("-webkit-transform",t),e.css("-moz-transform",t),e.css("-o-transform",t),e.css("-ms-transform",t),e.css("transform",t)},i=function(e){return"matrix("+e[0]+", "+e[1]+", "+e[2]+", "+e[3]+", "+e[4]+", "+e[5]+")"},a=function(e){var t=n(e);return{x:parseInt(t[4]),y:parseInt(t[5])}},l=function(e,t){var a=n(e);a[0]=a[3]=t;var l=i(a);o(e,l)},s=function(e,t,a){var l=n(e);l[4]=t,l[5]=a;var s=i(l);o(e,s)},r=function(e,t){var a=n(e),l=t*(Math.PI/180),s=-1*l;a[1]=l,a[2]=s;var r=i(a);o(e,r)};return{scale:l,translate:s,rotate:r,getTranslate:a}}(),a="MacIntel"==n.navigator.platform,l=0,s=0,r={command:!1,shift:!1,isSelecting:!1},c={66:"bold",73:"italic",85:"underline",112:"h1",113:"h2",122:"undo"},u={keyboard:{isCommand:function(e,t,n){a&&e.metaKey||!a&&e.ctrlKey?t():n()},isShift:function(e,t,n){e.shiftKey?t():n()},isModifier:function(e,t){var n=e.which,o=c[n];o&&t.call(this,o)},isEnter:function(e,t){13===e.which&&t()},isArrow:function(e,t){(e.which>=37||e.which<=40)&&t()}},html:{addTag:function(n,o,i,a){var l=e(t.createElement(o));return l.attr("contenteditable",Boolean(a)),l.append(" "),n.append(l),i&&(r.focusedElement=n.children().last(),u.cursor.set(n,0,r.focusedElement)),l}},cursor:{set:function(e,o,i){var a;if(t.createRange){a=t.createRange();var l=n.getSelection(),s=e.children().last(),r=s.html().length-1,c=i?i[0]:s[0],u="undefined"!=typeof o?o:r;a.setStart(c,u),a.collapse(!0),l.removeAllRanges(),l.addRange(a)}else a=t.body.createTextRange(),a.moveToElementText(i),a.collapse(!1),a.select()}},selection:{save:function(){if(n.getSelection){var e=n.getSelection();if(e.rangeCount>0)return e.getRangeAt(0)}else if(t.selection&&t.selection.createRange)return t.selection.createRange();return null},restore:function(e){if(e)if(n.getSelection){var o=n.getSelection();o.removeAllRanges(),o.addRange(e)}else t.selection&&e.select&&e.select()},getText:function(){var e="";return n.getSelection?e=n.getSelection().toString():t.getSelection?e=t.getSelection().toString():t.selection&&(e=t.selection.createRange().text),e},clear:function(){window.getSelection?window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges():document.selection&&document.selection.empty()},getContainer:function(e){return n.getSelection&&e&&e.commonAncestorContainer?e.commonAncestorContainer:t.selection&&e&&e.parentElement?e.parentElement():null}},validation:{isUrl:function(e){return/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test(e)}}},d={updatePos:function(t,o){var a=n.getSelection(),l=a.getRangeAt(0),s=l.getBoundingClientRect(),r=o.width(),c=o.height(),u=(t.offset().left,{x:s.left+s.width/2-r/2,y:s.top-c-8+e(document).scrollTop()});i.translate(o,u.x,u.y)},updateState:function(e,t){t.find("button").removeClass("active");var o=n.getSelection(),i=[];d.checkForFormatting(o.focusNode,i);for(var a={b:"bold",i:"italic",u:"underline",h1:"h1",h2:"h2",a:"anchor",ul:"ul",ol:"ol"},l=0;l'),o=n,t.attr("editor-mode",o.mode),t.attr("editor-placeholder",o.placeholder),t.attr("contenteditable",!0),t.css("position","relative"),t.addClass("jquery-notebook editor"),f.setPlaceholder.call(t,{}),f.preserveElementFocus.call(t),o.autoFocus===!0){var i=t.find("p:not(.placeholder)");u.cursor.set(t,0,i)}}},h={keydown:function(e){var t=this;r.command&&65===e.which&&setTimeout(function(){d.show.call(t)},50),u.keyboard.isCommand(e,function(){r.command=!0},function(){r.command=!1}),u.keyboard.isShift(e,function(){r.shift=!0},function(){r.shift=!1}),u.keyboard.isModifier.call(this,e,function(t){r.command&&m.commands[t].call(this,e)}),r.shift?u.keyboard.isArrow.call(this,e,function(){setTimeout(function(){var e=u.selection.getText();""!==e?d.show.call(t):d.clear.call(t)},100)}):u.keyboard.isArrow.call(this,e,function(){d.clear.call(t)}),13===e.which&&m.enterKey.call(this,e),27===e.which&&d.clear.call(this),86===e.which&&m.paste.call(this,e)},keyup:function(t){u.keyboard.isCommand(t,function(){r.command=!1},function(){r.command=!0}),f.preserveElementFocus.call(this),f.removePlaceholder.call(this),/^\s*$/.test(e(this).text())&&(e(this).empty(),u.html.addTag(e(this),"p",!0,!0))},focus:function(){r.command=!1,r.shift=!1},mouseClick:function(t){var n=this;if(r.isSelecting=!0,2===t.button)return setTimeout(function(){d.show.call(n)},50),void t.preventDefault();if(e(this).find(".bubble:visible").length){var o=e(this).find(".bubble:visible"),i=o.offset().left,a=o.offset().top,c=o.width(),u=o.height();if(l>i&&i+c>l&&s>a&&a+u>s)return}},mouseUp:function(e){e.preventDefault();var t=this;r.isSelecting=!1,setTimeout(function(){var e=u.selection.save();e.collapsed?d.clear.call(t):d.show.call(t)},50)},mouseMove:function(e){l=e.pageX,s=e.pageY},blur:function(){f.setPlaceholder.call(this,{focus:!1})}},m={commands:{bold:function(e){e.preventDefault(),t.execCommand("bold",!1),d.update.call(this)},italic:function(e){e.preventDefault(),t.execCommand("italic",!1),d.update.call(this)},underline:function(e){e.preventDefault(),t.execCommand("underline",!1),d.update.call(this)},anchor:function(e){e.preventDefault();var t=u.selection.save();d.showLinkInput.call(this,t)},createLink:function(e,n){u.selection.restore(n),t.execCommand("createLink",!1,e.url),d.update.call(this)},removeLink:function(t,n){var o=e(u.selection.getContainer(n)).closest("a");o.contents().first().unwrap()},h1:function(n){n.preventDefault(),e(window.getSelection().anchorNode.parentNode).is("h1")?t.execCommand("formatBlock",!1,"

"):t.execCommand("formatBlock",!1,"

"),d.update.call(this)},h2:function(n){n.preventDefault(),e(window.getSelection().anchorNode.parentNode).is("h2")?t.execCommand("formatBlock",!1,"

"):t.execCommand("formatBlock",!1,"

"),d.update.call(this)},ul:function(e){e.preventDefault(),t.execCommand("insertUnorderedList",!1),d.update.call(this)},ol:function(e){e.preventDefault(),t.execCommand("insertOrderedList",!1),d.update.call(this)},undo:function(e){e.preventDefault(),t.execCommand("undo",!1)}},enterKey:function(t){return"inline"===e(this).attr("editor-mode")?void t.preventDefault():void 0},paste:function(){var t=e(this);setTimeout(function(){t.find("*").each(function(){var t=e(this);e.each(this.attributes,function(){"class"===this.name&&t.hasClass("placeholder")||t.removeAttr(this.name)})})},100)}};e.fn.notebook=function(t){return t=e.extend({},e.fn.notebook.defaults,t),f.prepare(this,t),f.bindEvents(this),this},e.fn.notebook.defaults={autoFocus:!1,placeholder:"Your text here...",mode:"multiline",modifiers:["bold","italic","underline","h1","h2","ol","ul","anchor"]}}(jQuery,document,window); \ No newline at end of file diff --git a/src/js/jquery.notebook.css b/src/js/jquery.notebook.css index 30753fe..129830e 100644 --- a/src/js/jquery.notebook.css +++ b/src/js/jquery.notebook.css @@ -1,3 +1,7 @@ +.jquery-notebook-container { + position: relative; +} + .jquery-notebook.editor { position: relative; outline: none; diff --git a/src/js/jquery.notebook.js b/src/js/jquery.notebook.js index 3865821..11cb1d0 100644 --- a/src/js/jquery.notebook.js +++ b/src/js/jquery.notebook.js @@ -17,679 +17,681 @@ (function($, d, w) { - /* - * This module deals with the CSS transforms. As it is not possible to easily - * combine the transform functions with JavaScript this module abstract those - * functions and generates a raw transform matrix, combining the new transform - * with the others that were previously applied to the element. - */ - - var transform = (function() { - var matrixToArray = function(str) { - if (!str || str == 'none') { - return [1, 0, 0, 1, 0, 0]; - } - return str.match(/(-?[0-9\.]+)/g); - }; + /* + * This module deals with the CSS transforms. As it is not possible to easily + * combine the transform functions with JavaScript this module abstract those + * functions and generates a raw transform matrix, combining the new transform + * with the others that were previously applied to the element. + */ - var getPreviousTransforms = function(elem) { - return elem.css('-webkit-transform') || elem.css('transform') || elem.css('-moz-transform') || - elem.css('-o-transform') || elem.css('-ms-transform'); - }; + var transform = (function() { + var matrixToArray = function(str) { + if (!str || str == 'none') { + return [1, 0, 0, 1, 0, 0]; + } + return str.match(/(-?[0-9\.]+)/g); + }; - var getMatrix = function(elem) { - var previousTransform = getPreviousTransforms(elem); - return matrixToArray(previousTransform); - }; - - var applyTransform = function(elem, transform) { - elem.css('-webkit-transform', transform); - elem.css('-moz-transform', transform); - elem.css('-o-transform', transform); - elem.css('-ms-transform', transform); - elem.css('transform', transform); - }; - - var buildTransformString = function(matrix) { - return 'matrix(' + matrix[0] + - ', ' + matrix[1] + - ', ' + matrix[2] + - ', ' + matrix[3] + - ', ' + matrix[4] + - ', ' + matrix[5] + ')'; - }; + var getPreviousTransforms = function(elem) { + return elem.css('-webkit-transform') || elem.css('transform') || elem.css('-moz-transform') || + elem.css('-o-transform') || elem.css('-ms-transform'); + }; - var getTranslate = function(elem) { - var matrix = getMatrix(elem); - return { - x: parseInt(matrix[4]), - y: parseInt(matrix[5]) - }; - }; + var getMatrix = function(elem) { + var previousTransform = getPreviousTransforms(elem); + return matrixToArray(previousTransform); + }; - var scale = function(elem, _scale) { - var matrix = getMatrix(elem); - matrix[0] = matrix[3] = _scale; - var transform = buildTransformString(matrix); - applyTransform(elem, transform); - }; + var applyTransform = function(elem, transform) { + elem.css('-webkit-transform', transform); + elem.css('-moz-transform', transform); + elem.css('-o-transform', transform); + elem.css('-ms-transform', transform); + elem.css('transform', transform); + }; - var translate = function(elem, x, y) { - var matrix = getMatrix(elem); - matrix[4] = x; - matrix[5] = y; - var transform = buildTransformString(matrix); - applyTransform(elem, transform); - }; + var buildTransformString = function(matrix) { + return 'matrix(' + matrix[0] + + ', ' + matrix[1] + + ', ' + matrix[2] + + ', ' + matrix[3] + + ', ' + matrix[4] + + ', ' + matrix[5] + ')'; + }; - var rotate = function(elem, deg) { - var matrix = getMatrix(elem); - var rad1 = deg * (Math.PI / 180); - var rad2 = rad1 * -1; - matrix[1] = rad1; - matrix[2] = rad2; - var transform = buildTransformString(matrix); - applyTransform(elem, transform); - }; + var getTranslate = function(elem) { + var matrix = getMatrix(elem); + return { + x: parseInt(matrix[4]), + y: parseInt(matrix[5]) + }; + }; - return { - scale: scale, - translate: translate, - rotate: rotate, - getTranslate: getTranslate - }; - })(); + var scale = function(elem, _scale) { + var matrix = getMatrix(elem); + matrix[0] = matrix[3] = _scale; + var transform = buildTransformString(matrix); + applyTransform(elem, transform); + }; + + var translate = function(elem, x, y) { + var matrix = getMatrix(elem); + matrix[4] = x; + matrix[5] = y; + var transform = buildTransformString(matrix); + applyTransform(elem, transform); + }; + + var rotate = function(elem, deg) { + var matrix = getMatrix(elem); + var rad1 = deg * (Math.PI / 180); + var rad2 = rad1 * -1; + matrix[1] = rad1; + matrix[2] = rad2; + var transform = buildTransformString(matrix); + applyTransform(elem, transform); + }; + + return { + scale: scale, + translate: translate, + rotate: rotate, + getTranslate: getTranslate + }; + })(); - var isMac = w.navigator.platform == 'MacIntel', - mouseX = 0, - mouseY = 0, - cache = { - command: false, - shift: false, - isSelecting: false + var isMac = w.navigator.platform == 'MacIntel', + mouseX = 0, + mouseY = 0, + cache = { + command: false, + shift: false, + isSelecting: false + }, + modifiers = { + 66: 'bold', + 73: 'italic', + 85: 'underline', + 112: 'h1', + 113: 'h2', + 122: 'undo' + }, + options, + utils = { + keyboard: { + isCommand: function(e, callbackTrue, callbackFalse) { + if (isMac && e.metaKey || !isMac && e.ctrlKey) { + callbackTrue(); + } else { + callbackFalse(); + } + }, + isShift: function(e, callbackTrue, callbackFalse) { + if (e.shiftKey) { + callbackTrue(); + } else { + callbackFalse(); + } + }, + isModifier: function(e, callback) { + var key = e.which, + cmd = modifiers[key]; + if (cmd) { + callback.call(this, cmd); + } }, - modifiers = { - 66: 'bold', - 73: 'italic', - 85: 'underline', - 112: 'h1', - 113: 'h2', - 122: 'undo' + isEnter: function(e, callback) { + if (e.which === 13) { + callback(); + } }, - options, - utils = { - keyboard: { - isCommand: function(e, callbackTrue, callbackFalse) { - if (isMac && e.metaKey || !isMac && e.ctrlKey) { - callbackTrue(); - } else { - callbackFalse(); - } - }, - isShift: function(e, callbackTrue, callbackFalse) { - if (e.shiftKey) { - callbackTrue(); - } else { - callbackFalse(); - } - }, - isModifier: function(e, callback) { - var key = e.which, - cmd = modifiers[key]; - if (cmd) { - callback.call(this, cmd); - } - }, - isEnter: function(e, callback) { - if (e.which === 13) { - callback(); - } - }, - isArrow: function(e, callback) { - if (e.which >= 37 || e.which <= 40) { - callback(); - } - } - }, - html: { - addTag: function(elem, tag, focus, editable) { - var newElement = $(d.createElement(tag)); - newElement.attr('contenteditable', Boolean(editable)); - newElement.append(' '); - elem.append(newElement); - if (focus) { - cache.focusedElement = elem.children().last(); - utils.cursor.set(elem, 0, cache.focusedElement); - } - return newElement; - } - }, - cursor: { - set: function(editor, pos, elem) { - var range; - if (d.createRange) { - range = d.createRange(); - var selection = w.getSelection(), - lastChild = editor.children().last(), - length = lastChild.html().length - 1, - toModify = elem ? elem[0] : lastChild[0], - theLength = typeof pos !== 'undefined' ? pos : length; - range.setStart(toModify, theLength); - range.collapse(true); - selection.removeAllRanges(); - selection.addRange(range); - } else { - range = d.body.createTextRange(); - range.moveToElementText(elem); - range.collapse(false); - range.select(); - } - } - }, - selection: { - save: function() { - if (w.getSelection) { - var sel = w.getSelection(); - if (sel.rangeCount > 0) { - return sel.getRangeAt(0); - } - } else if (d.selection && d.selection.createRange) { // IE - return d.selection.createRange(); - } - return null; - }, - restore: function(range) { - if (range) { - if (w.getSelection) { - var sel = w.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } else if (d.selection && range.select) { // IE - range.select(); - } - } - }, - getText: function() { - var txt = ''; - if (w.getSelection) { - txt = w.getSelection().toString(); - } else if (d.getSelection) { - txt = d.getSelection().toString(); - } else if (d.selection) { - txt = d.selection.createRange().text; - } - return txt; - }, - clear: function() { - if (window.getSelection) { - if (window.getSelection().empty) { // Chrome - window.getSelection().empty(); - } else if (window.getSelection().removeAllRanges) { // Firefox - window.getSelection().removeAllRanges(); - } - } else if (document.selection) { // IE? - document.selection.empty(); - } - }, - getContainer: function(sel) { - if (w.getSelection && sel && sel.commonAncestorContainer) { - return sel.commonAncestorContainer; - } else if (d.selection && sel && sel.parentElement) { - return sel.parentElement(); - } - return null; - } - }, - validation: { - isUrl: function(url) { - return (/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/).test(url); - } + isArrow: function(e, callback) { + if (e.which >= 37 || e.which <= 40) { + callback(); + } + } + }, + html: { + addTag: function(elem, tag, focus, editable) { + var newElement = $(d.createElement(tag)); + newElement.attr('contenteditable', Boolean(editable)); + newElement.append(' '); + elem.append(newElement); + if (focus) { + cache.focusedElement = elem.children().last(); + utils.cursor.set(elem, 0, cache.focusedElement); + } + return newElement; + } + }, + cursor: { + set: function(editor, pos, elem) { + var range; + if (d.createRange) { + range = d.createRange(); + var selection = w.getSelection(), + lastChild = editor.children().last(), + length = lastChild.html().length - 1, + toModify = elem ? elem[0] : lastChild[0], + theLength = typeof pos !== 'undefined' ? pos : length; + range.setStart(toModify, theLength); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + } else { + range = d.body.createTextRange(); + range.moveToElementText(elem); + range.collapse(false); + range.select(); + } + } + }, + selection: { + save: function() { + if (w.getSelection) { + var sel = w.getSelection(); + if (sel.rangeCount > 0) { + return sel.getRangeAt(0); } + } else if (d.selection && d.selection.createRange) { // IE + return d.selection.createRange(); + } + return null; }, - bubble = { - /* - * This is called to position the bubble above the selection. - */ - updatePos: function(editor, elem) { - var sel = w.getSelection(), - range = sel.getRangeAt(0), - boundary = range.getBoundingClientRect(), - bubbleWidth = elem.width(), - bubbleHeight = elem.height(), - offset = editor.offset().left, - pos = { - x: (boundary.left + boundary.width / 2) - bubbleWidth / 2, - y: boundary.top - bubbleHeight - 8 + $(document).scrollTop() - }; - transform.translate(elem, pos.x, pos.y); - }, - /* - * Updates the bubble to set the active formats for the current selection. - */ - updateState: function(editor, elem) { - elem.find('button').removeClass('active'); - var sel = w.getSelection(), - formats = []; - bubble.checkForFormatting(sel.focusNode, formats); - var formatDict = { - 'b': 'bold', - 'i': 'italic', - 'u': 'underline', - 'h1': 'h1', - 'h2': 'h2', - 'a': 'anchor', - 'ul': 'ul', - 'ol': 'ol' - }; - for (var i = 0; i < formats.length; i++) { - var format = formats[i]; - elem.find('button.' + formatDict[format]).addClass('active'); - } - }, - /* - * Recursively navigates upwards in the DOM to find all the format - * tags enclosing the selection. - */ - checkForFormatting: function(currentNode, formats) { - var validFormats = ['b', 'i', 'u', 'h1', 'h2', 'ol', 'ul', 'li', 'a']; - if (currentNode.nodeName === '#text' || - validFormats.indexOf(currentNode.nodeName.toLowerCase()) != -1) { - if (currentNode.nodeName != '#text') { - formats.push(currentNode.nodeName.toLowerCase()); - } - bubble.checkForFormatting(currentNode.parentNode, formats); - } - }, - buildMenu: function(editor, elem) { - var ul = utils.html.addTag(elem, 'ul', false, false); - for (var cmd in options.modifiers) { - var li = utils.html.addTag(ul, 'li', false, false); - var btn = utils.html.addTag(li, 'button', false, false); - btn.attr('editor-command', options.modifiers[cmd]); - btn.addClass(options.modifiers[cmd]); - } - elem.find('button').click(function(e) { - e.preventDefault(); - var cmd = $(this).attr('editor-command'); - events.commands[cmd].call(editor, e); - }); - var linkArea = utils.html.addTag(elem, 'div', false, false); - linkArea.addClass('link-area'); - var linkInput = utils.html.addTag(linkArea, 'input', false, false); - linkInput.attr({ - type: 'text' - }); - var closeBtn = utils.html.addTag(linkArea, 'button', false, false); - closeBtn.click(function(e) { - e.preventDefault(); - var editor = $(this).closest('.editor'); - $(this).closest('.link-area').hide(); - $(this).closest('.bubble').find('ul').show(); - }); - }, - show: function() { - var tag = $(this).parent().find('.bubble'); - if (!tag.length) { - tag = utils.html.addTag($(this).parent(), 'div', false, false); - tag.addClass('jquery-notebook bubble'); - bubble.buildMenu(this, tag); - } - tag.show(); - bubble.updateState(this, tag); - if (!tag.hasClass('active')) { - tag.addClass('jump'); - } else { - tag.removeClass('jump'); - } - bubble.updatePos($(this), tag); - tag.addClass('active'); - }, - update: function() { - var tag = $(this).parent().find('.bubble'); - bubble.updateState(this, tag); - }, - clear: function() { - var elem = $(this).parent().find('.bubble'); - if (!elem.hasClass('active')) return; - elem.removeClass('active'); - bubble.hideLinkInput.call(this); - bubble.showButtons.call(this); - setTimeout(function() { - if (elem.hasClass('active')) return; - elem.hide(); - }, 500); - }, - hideButtons: function() { - $(this).parent().find('.bubble').find('ul').hide(); - }, - showButtons: function() { - $(this).parent().find('.bubble').find('ul').show(); - }, - showLinkInput: function(selection) { - bubble.hideButtons.call(this); - var editor = this; - var elem = $(this).parent().find('.bubble').find('input[type=text]'); - var hasLink = elem.closest('.jquery-notebook').find('button.anchor').hasClass('active'); - elem.unbind('keydown'); - elem.keydown(function(e) { - var elem = $(this); - utils.keyboard.isEnter(e, function() { - e.preventDefault(); - var url = elem.val(); - if (utils.validation.isUrl(url)) { - e.url = url; - events.commands.createLink(e, selection); - bubble.clear.call(editor); - } else if (url === '' && hasLink) { - events.commands.removeLink(e, selection); - bubble.clear.call(editor); - } - }); - }); - elem.bind('paste', function(e) { - var elem = $(this); - setTimeout(function() { - var text = elem.val(); - if (/http:\/\/https?:\/\//.test(text)) { - text = text.substring(7); - elem.val(text); - } - }, 1); - }); - var linkText = 'http://'; - if (hasLink) { - var anchor = $(utils.selection.getContainer(selection)).closest('a'); - linkText = anchor.prop('href') || linkText; - } - $(this).parent().find('.link-area').show(); - elem.val(linkText).focus(); - }, - hideLinkInput: function() { - $(this).parent().find('.bubble').find('.link-area').hide(); + restore: function(range) { + if (range) { + if (w.getSelection) { + var sel = w.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } else if (d.selection && range.select) { // IE + range.select(); } + } }, - actions = { - bindEvents: function(elem) { - elem.keydown(rawEvents.keydown); - elem.keyup(rawEvents.keyup); - elem.focus(rawEvents.focus); - elem.bind('paste', events.paste); - elem.mousedown(rawEvents.mouseClick); - elem.mouseup(rawEvents.mouseUp); - elem.mousemove(rawEvents.mouseMove); - elem.blur(rawEvents.blur); - $('body').mouseup(function(e) { - if (e.target == e.currentTarget && cache.isSelecting) { - rawEvents.mouseUp.call(elem, e); - } - }); - }, - setPlaceholder: function(e) { - if (/^\s*$/.test($(this).text())) { - $(this).empty(); - var placeholder = utils.html.addTag($(this), 'p').addClass('placeholder'); - placeholder.append($(this).attr('editor-placeholder')); - utils.html.addTag($(this), 'p', typeof e.focus != 'undefined' ? e.focus : false, true); - } else { - $(this).find('.placeholder').remove(); - } - }, - removePlaceholder: function(e) { - $(this).find('.placeholder').remove(); - }, - preserveElementFocus: function() { - var anchorNode = w.getSelection() ? w.getSelection().anchorNode : d.activeElement; - if (anchorNode) { - var current = anchorNode.parentNode, - diff = current !== cache.focusedElement, - children = this.children, - elementIndex = 0; - if (current === this) { - current = anchorNode; - } - for (var i = 0; i < children.length; i++) { - if (current === children[i]) { - elementIndex = i; - break; - } - } - if (diff) { - cache.focusedElement = current; - cache.focusedElementIndex = elementIndex; - } - } - }, - prepare: function(elem, customOptions) { - options = customOptions; - elem.attr('editor-mode', options.mode); - elem.attr('editor-placeholder', options.placeholder); - elem.attr('contenteditable', true); - elem.css('position', 'relative'); - elem.addClass('jquery-notebook editor'); - actions.setPlaceholder.call(elem, {}); - actions.preserveElementFocus.call(elem); - if (options.autoFocus === true) { - var firstP = elem.find('p:not(.placeholder)'); - utils.cursor.set(elem, 0, firstP); - } + getText: function() { + var txt = ''; + if (w.getSelection) { + txt = w.getSelection().toString(); + } else if (d.getSelection) { + txt = d.getSelection().toString(); + } else if (d.selection) { + txt = d.selection.createRange().text; + } + return txt; + }, + clear: function() { + if (window.getSelection) { + if (window.getSelection().empty) { // Chrome + window.getSelection().empty(); + } else if (window.getSelection().removeAllRanges) { // Firefox + window.getSelection().removeAllRanges(); } + } else if (document.selection) { // IE? + document.selection.empty(); + } }, - rawEvents = { - keydown: function(e) { - var elem = this; - if (cache.command && e.which === 65) { - setTimeout(function() { - bubble.show.call(elem); - }, 50); - } - utils.keyboard.isCommand(e, function() { - cache.command = true; - }, function() { - cache.command = false; - }); - utils.keyboard.isShift(e, function() { - cache.shift = true; - }, function() { - cache.shift = false; - }); - utils.keyboard.isModifier.call(this, e, function(modifier) { - if (cache.command) { - events.commands[modifier].call(this, e); - } - }); + getContainer: function(sel) { + if (w.getSelection && sel && sel.commonAncestorContainer) { + return sel.commonAncestorContainer; + } else if (d.selection && sel && sel.parentElement) { + return sel.parentElement(); + } + return null; + } + }, + validation: { + isUrl: function(url) { + return (/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/).test(url); + } + } + }, + bubble = { + /* + * This is called to position the bubble above the selection. + */ + updatePos: function(editor, elem) { + var sel = w.getSelection(), + range = sel.getRangeAt(0), + boundary = range.getBoundingClientRect(), + bubbleWidth = elem.width(), + bubbleHeight = elem.height(), + offset = editor.offset().left, + container = editor.parent().offset(), + pos = { + x: (boundary.left + boundary.width / 2) - (bubbleWidth / 2) - container.left, + y: boundary.top - bubbleHeight - 8 + $(document).scrollTop() - container.top + }; + transform.translate(elem, pos.x, pos.y); + }, + /* + * Updates the bubble to set the active formats for the current selection. + */ + updateState: function(editor, elem) { + elem.find('button').removeClass('active'); + var sel = w.getSelection(), + formats = []; + bubble.checkForFormatting(sel.focusNode, formats); + var formatDict = { + 'b': 'bold', + 'i': 'italic', + 'u': 'underline', + 'h1': 'h1', + 'h2': 'h2', + 'a': 'anchor', + 'ul': 'ul', + 'ol': 'ol' + }; + for (var i = 0; i < formats.length; i++) { + var format = formats[i]; + elem.find('button.' + formatDict[format]).addClass('active'); + } + }, + /* + * Recursively navigates upwards in the DOM to find all the format + * tags enclosing the selection. + */ + checkForFormatting: function(currentNode, formats) { + var validFormats = ['b', 'i', 'u', 'h1', 'h2', 'ol', 'ul', 'li', 'a']; + if (currentNode.nodeName === '#text' || + validFormats.indexOf(currentNode.nodeName.toLowerCase()) != -1) { + if (currentNode.nodeName != '#text') { + formats.push(currentNode.nodeName.toLowerCase()); + } + bubble.checkForFormatting(currentNode.parentNode, formats); + } + }, + buildMenu: function(editor, elem) { + var ul = utils.html.addTag(elem, 'ul', false, false); + for (var cmd in options.modifiers) { + var li = utils.html.addTag(ul, 'li', false, false); + var btn = utils.html.addTag(li, 'button', false, false); + btn.attr('editor-command', options.modifiers[cmd]); + btn.addClass(options.modifiers[cmd]); + } + elem.find('button').click(function(e) { + e.preventDefault(); + var cmd = $(this).attr('editor-command'); + events.commands[cmd].call(editor, e); + }); + var linkArea = utils.html.addTag(elem, 'div', false, false); + linkArea.addClass('link-area'); + var linkInput = utils.html.addTag(linkArea, 'input', false, false); + linkInput.attr({ + type: 'text' + }); + var closeBtn = utils.html.addTag(linkArea, 'button', false, false); + closeBtn.click(function(e) { + e.preventDefault(); + var editor = $(this).closest('.editor'); + $(this).closest('.link-area').hide(); + $(this).closest('.bubble').find('ul').show(); + }); + }, + show: function() { + var tag = $(this).parent().find('.bubble'); + if (!tag.length) { + tag = utils.html.addTag($(this).parent(), 'div', false, false); + tag.addClass('jquery-notebook bubble'); + bubble.buildMenu(this, tag); + } + tag.show(); + bubble.updateState(this, tag); + if (!tag.hasClass('active')) { + tag.addClass('jump'); + } else { + tag.removeClass('jump'); + } + bubble.updatePos($(this), tag); + tag.addClass('active'); + }, + update: function() { + var tag = $(this).parent().find('.bubble'); + bubble.updateState(this, tag); + }, + clear: function() { + var elem = $(this).parent().find('.bubble'); + if (!elem.hasClass('active')) return; + elem.removeClass('active'); + bubble.hideLinkInput.call(this); + bubble.showButtons.call(this); + setTimeout(function() { + if (elem.hasClass('active')) return; + elem.hide(); + }, 500); + }, + hideButtons: function() { + $(this).parent().find('.bubble').find('ul').hide(); + }, + showButtons: function() { + $(this).parent().find('.bubble').find('ul').show(); + }, + showLinkInput: function(selection) { + bubble.hideButtons.call(this); + var editor = this; + var elem = $(this).parent().find('.bubble').find('input[type=text]'); + var hasLink = elem.closest('.jquery-notebook').find('button.anchor').hasClass('active'); + elem.unbind('keydown'); + elem.keydown(function(e) { + var elem = $(this); + utils.keyboard.isEnter(e, function() { + e.preventDefault(); + var url = elem.val(); + if (utils.validation.isUrl(url)) { + e.url = url; + events.commands.createLink(e, selection); + bubble.clear.call(editor); + } else if (url === '' && hasLink) { + events.commands.removeLink(e, selection); + bubble.clear.call(editor); + } + }); + }); + elem.bind('paste', function(e) { + var elem = $(this); + setTimeout(function() { + var text = elem.val(); + if (/http:\/\/https?:\/\//.test(text)) { + text = text.substring(7); + elem.val(text); + } + }, 1); + }); + var linkText = 'http://'; + if (hasLink) { + var anchor = $(utils.selection.getContainer(selection)).closest('a'); + linkText = anchor.prop('href') || linkText; + } + $(this).parent().find('.link-area').show(); + elem.val(linkText).focus(); + }, + hideLinkInput: function() { + $(this).parent().find('.bubble').find('.link-area').hide(); + } + }, + actions = { + bindEvents: function(elem) { + elem.keydown(rawEvents.keydown); + elem.keyup(rawEvents.keyup); + elem.focus(rawEvents.focus); + elem.bind('paste', events.paste); + elem.mousedown(rawEvents.mouseClick); + elem.mouseup(rawEvents.mouseUp); + elem.mousemove(rawEvents.mouseMove); + elem.blur(rawEvents.blur); + $('body').mouseup(function(e) { + if (e.target == e.currentTarget && cache.isSelecting) { + rawEvents.mouseUp.call(elem, e); + } + }); + }, + setPlaceholder: function(e) { + if (/^\s*$/.test($(this).text())) { + $(this).empty(); + var placeholder = utils.html.addTag($(this), 'p').addClass('placeholder'); + placeholder.append($(this).attr('editor-placeholder')); + utils.html.addTag($(this), 'p', typeof e.focus != 'undefined' ? e.focus : false, true); + } else { + $(this).find('.placeholder').remove(); + } + }, + removePlaceholder: function(e) { + $(this).find('.placeholder').remove(); + }, + preserveElementFocus: function() { + var anchorNode = w.getSelection() ? w.getSelection().anchorNode : d.activeElement; + if (anchorNode) { + var current = anchorNode.parentNode, + diff = current !== cache.focusedElement, + children = this.children, + elementIndex = 0; + if (current === this) { + current = anchorNode; + } + for (var i = 0; i < children.length; i++) { + if (current === children[i]) { + elementIndex = i; + break; + } + } + if (diff) { + cache.focusedElement = current; + cache.focusedElementIndex = elementIndex; + } + } + }, + prepare: function(elem, customOptions) { + elem.wrap('
'); + options = customOptions; + elem.attr('editor-mode', options.mode); + elem.attr('editor-placeholder', options.placeholder); + elem.attr('contenteditable', true); + elem.css('position', 'relative'); + elem.addClass('jquery-notebook editor'); + actions.setPlaceholder.call(elem, {}); + actions.preserveElementFocus.call(elem); + if (options.autoFocus === true) { + var firstP = elem.find('p:not(.placeholder)'); + utils.cursor.set(elem, 0, firstP); + } + } + }, + rawEvents = { + keydown: function(e) { + var elem = this; + if (cache.command && e.which === 65) { + setTimeout(function() { + bubble.show.call(elem); + }, 50); + } + utils.keyboard.isCommand(e, function() { + cache.command = true; + }, function() { + cache.command = false; + }); + utils.keyboard.isShift(e, function() { + cache.shift = true; + }, function() { + cache.shift = false; + }); + utils.keyboard.isModifier.call(this, e, function(modifier) { + if (cache.command) { + events.commands[modifier].call(this, e); + } + }); - if (cache.shift) { - utils.keyboard.isArrow.call(this, e, function() { - setTimeout(function() { - var txt = utils.selection.getText(); - if (txt !== '') { - bubble.show.call(elem); - } else { - bubble.clear.call(elem); - } - }, 100); - }); - } else { - utils.keyboard.isArrow.call(this, e, function() { - bubble.clear.call(elem); - }); - } + if (cache.shift) { + utils.keyboard.isArrow.call(this, e, function() { + setTimeout(function() { + var txt = utils.selection.getText(); + if (txt !== '') { + bubble.show.call(elem); + } else { + bubble.clear.call(elem); + } + }, 100); + }); + } else { + utils.keyboard.isArrow.call(this, e, function() { + bubble.clear.call(elem); + }); + } - if (e.which === 13) { - events.enterKey.call(this, e); - } - if (e.which === 27) { - bubble.clear.call(this); - } - if (e.which === 86) { - events.paste.call(this, e); - } - }, - keyup: function(e) { - utils.keyboard.isCommand(e, function() { - cache.command = false; - }, function() { - cache.command = true; - }); - actions.preserveElementFocus.call(this); - actions.removePlaceholder.call(this); + if (e.which === 13) { + events.enterKey.call(this, e); + } + if (e.which === 27) { + bubble.clear.call(this); + } + if (e.which === 86) { + events.paste.call(this, e); + } + }, + keyup: function(e) { + utils.keyboard.isCommand(e, function() { + cache.command = false; + }, function() { + cache.command = true; + }); + actions.preserveElementFocus.call(this); + actions.removePlaceholder.call(this); - /* - * This breaks the undo when the whole text is deleted but so far - * it is the only way that I fould to solve the more serious bug - * that the editor was losing the p elements after deleting the whole text - */ - if (/^\s*$/.test($(this).text())) { - $(this).empty(); - utils.html.addTag($(this), 'p', true, true); - } - }, - focus: function(e) { - cache.command = false; - cache.shift = false; - }, - mouseClick: function(e) { - var elem = this; - cache.isSelecting = true; - if (e.button === 2) { - setTimeout(function() { - bubble.show.call(elem); - }, 50); - e.preventDefault(); - return; - } - if ($(this).find('.bubble:visible').length) { - var bubbleTag = $(this).find('.bubble:visible'), - bubbleX = bubbleTag.offset().left, - bubbleY = bubbleTag.offset().top, - bubbleWidth = bubbleTag.width(), - bubbleHeight = bubbleTag.height(); - if (mouseX > bubbleX && mouseX < bubbleX + bubbleWidth && - mouseY > bubbleY && mouseY < bubbleY + bubbleHeight) { - return; - } - } - }, - mouseUp: function(e) { - e.preventDefault(); - var elem = this; - cache.isSelecting = false; - setTimeout(function() { - var s = utils.selection.save(); - if (s.collapsed) { - bubble.clear.call(elem); - } else { - bubble.show.call(elem); - } - }, 50); - }, - mouseMove: function(e) { - mouseX = e.pageX; - mouseY = e.pageY; - }, - blur: function(e) { - actions.setPlaceholder.call(this, { - focus: false - }); - } + /* + * This breaks the undo when the whole text is deleted but so far + * it is the only way that I fould to solve the more serious bug + * that the editor was losing the p elements after deleting the whole text + */ + if (/^\s*$/.test($(this).text())) { + $(this).empty(); + utils.html.addTag($(this), 'p', true, true); + } + }, + focus: function(e) { + cache.command = false; + cache.shift = false; + }, + mouseClick: function(e) { + var elem = this; + cache.isSelecting = true; + if (e.button === 2) { + setTimeout(function() { + bubble.show.call(elem); + }, 50); + e.preventDefault(); + return; + } + if ($(this).find('.bubble:visible').length) { + var bubbleTag = $(this).find('.bubble:visible'), + bubbleX = bubbleTag.offset().left, + bubbleY = bubbleTag.offset().top, + bubbleWidth = bubbleTag.width(), + bubbleHeight = bubbleTag.height(); + if (mouseX > bubbleX && mouseX < bubbleX + bubbleWidth && + mouseY > bubbleY && mouseY < bubbleY + bubbleHeight) { + return; + } + } + }, + mouseUp: function(e) { + e.preventDefault(); + var elem = this; + cache.isSelecting = false; + setTimeout(function() { + var s = utils.selection.save(); + if (s.collapsed) { + bubble.clear.call(elem); + } else { + bubble.show.call(elem); + } + }, 50); + }, + mouseMove: function(e) { + mouseX = e.pageX; + mouseY = e.pageY; + }, + blur: function(e) { + actions.setPlaceholder.call(this, { + focus: false + }); + } + }, + events = { + commands: { + bold: function(e) { + e.preventDefault(); + d.execCommand('bold', false); + bubble.update.call(this); }, - events = { - commands: { - bold: function(e) { - e.preventDefault(); - d.execCommand('bold', false); - bubble.update.call(this); - }, - italic: function(e) { - e.preventDefault(); - d.execCommand('italic', false); - bubble.update.call(this); - }, - underline: function(e) { - e.preventDefault(); - d.execCommand('underline', false); - bubble.update.call(this); - }, - anchor: function(e) { - e.preventDefault(); - var s = utils.selection.save(); - bubble.showLinkInput.call(this, s); - }, - createLink: function(e, s) { - utils.selection.restore(s); - d.execCommand('createLink', false, e.url); - bubble.update.call(this); - }, - removeLink: function(e, s) { - var el = $(utils.selection.getContainer(s)).closest('a'); - el.contents().first().unwrap(); - }, - h1: function(e) { - e.preventDefault(); - if ($(window.getSelection().anchorNode.parentNode).is('h1')) { - d.execCommand('formatBlock', false, '

'); - } else { - d.execCommand('formatBlock', false, '

'); - } - bubble.update.call(this); - }, - h2: function(e) { - e.preventDefault(); - if ($(window.getSelection().anchorNode.parentNode).is('h2')) { - d.execCommand('formatBlock', false, '

'); - } else { - d.execCommand('formatBlock', false, '

'); - } - bubble.update.call(this); - }, - ul: function(e) { - e.preventDefault(); - d.execCommand('insertUnorderedList', false); - bubble.update.call(this); - }, - ol: function(e) { - e.preventDefault(); - d.execCommand('insertOrderedList', false); - bubble.update.call(this); - }, - undo: function(e) { - e.preventDefault(); - d.execCommand('undo', false); - } - }, - enterKey: function(e) { - if ($(this).attr('editor-mode') === 'inline') { - e.preventDefault(); - return; - } - }, - paste: function(e) { - var elem = $(this); - setTimeout(function() { - elem.find('*').each(function() { - var current = $(this); - $.each(this.attributes, function() { - if (this.name !== 'class' || !current.hasClass('placeholder')) { - current.removeAttr(this.name); - } - }); - }); - }, 100); - } - }; - - $.fn.notebook = function(options) { - options = $.extend({}, $.fn.notebook.defaults, options); - actions.prepare(this, options); - actions.bindEvents(this); - return this; + italic: function(e) { + e.preventDefault(); + d.execCommand('italic', false); + bubble.update.call(this); + }, + underline: function(e) { + e.preventDefault(); + d.execCommand('underline', false); + bubble.update.call(this); + }, + anchor: function(e) { + e.preventDefault(); + var s = utils.selection.save(); + bubble.showLinkInput.call(this, s); + }, + createLink: function(e, s) { + utils.selection.restore(s); + d.execCommand('createLink', false, e.url); + bubble.update.call(this); + }, + removeLink: function(e, s) { + var el = $(utils.selection.getContainer(s)).closest('a'); + el.contents().first().unwrap(); + }, + h1: function(e) { + e.preventDefault(); + if ($(window.getSelection().anchorNode.parentNode).is('h1')) { + d.execCommand('formatBlock', false, '

'); + } else { + d.execCommand('formatBlock', false, '

'); + } + bubble.update.call(this); + }, + h2: function(e) { + e.preventDefault(); + if ($(window.getSelection().anchorNode.parentNode).is('h2')) { + d.execCommand('formatBlock', false, '

'); + } else { + d.execCommand('formatBlock', false, '

'); + } + bubble.update.call(this); + }, + ul: function(e) { + e.preventDefault(); + d.execCommand('insertUnorderedList', false); + bubble.update.call(this); + }, + ol: function(e) { + e.preventDefault(); + d.execCommand('insertOrderedList', false); + bubble.update.call(this); + }, + undo: function(e) { + e.preventDefault(); + d.execCommand('undo', false); + } + }, + enterKey: function(e) { + if ($(this).attr('editor-mode') === 'inline') { + e.preventDefault(); + return; + } + }, + paste: function(e) { + var elem = $(this); + setTimeout(function() { + elem.find('*').each(function() { + var current = $(this); + $.each(this.attributes, function() { + if (this.name !== 'class' || !current.hasClass('placeholder')) { + current.removeAttr(this.name); + } + }); + }); + }, 100); + } }; - $.fn.notebook.defaults = { - autoFocus: false, - placeholder: 'Your text here...', - mode: 'multiline', - modifiers: ['bold', 'italic', 'underline', 'h1', 'h2', 'ol', 'ul', 'anchor'] - }; + $.fn.notebook = function(options) { + options = $.extend({}, $.fn.notebook.defaults, options); + actions.prepare(this, options); + actions.bindEvents(this); + return this; + }; + + $.fn.notebook.defaults = { + autoFocus: false, + placeholder: 'Your text here...', + mode: 'multiline', + modifiers: ['bold', 'italic', 'underline', 'h1', 'h2', 'ol', 'ul', 'anchor'] + }; -})(jQuery, document, window); +})(jQuery, document, window); \ No newline at end of file