diff --git a/src/plugins/oer/copy/lib/copy-plugin.coffee b/src/plugins/oer/copy/lib/copy-plugin.coffee index 8f65faeb0b..a06e818ced 100644 --- a/src/plugins/oer/copy/lib/copy-plugin.coffee +++ b/src/plugins/oer/copy/lib/copy-plugin.coffee @@ -2,6 +2,23 @@ define ['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './pa buffer = '' srcpath = null + content_type = null + + getSection = ($el) -> + headings = ['h1', 'h2', 'h3'] + level = headings.indexOf $el[0].nodeName.toLowerCase() + # Pick up all elements until the next heading of the same level or higher + selector = headings.slice(0, level+1).join(',') + + if $el.addBack + # Jquery >= 1.8 + $el = $el.nextUntil(selector).addBack() + else + # Jquery < 1.8 + $el = $el.nextUntil(selector).andSelf() + html = '' + html += jQuery(e).outerHtml() for e in $el + return html Plugin.create 'copy', getCurrentPath: -> @@ -26,13 +43,21 @@ define ['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './pa else return srcpath - buffer: (content, path) -> + getContentType: -> + if localStorage + return localStorage.alohaOerCopyContentType + else + return content_type + + buffer: (content, type, path) -> buffer = content buffer = buffer.replace /id="[^"]+"/, '' - srcpath = path + content_type = type or 'text/html' + srcpath = path or @getCurrentPath() localStorage.alohaOerCopyBuffer = buffer if localStorage localStorage.alohaOerCopySrcPath = srcpath if localStorage + localStorage.alohaOerCopyContentType = content_type if localStorage # Disable copy button, it will re-enable when you move the cursor. This # gives visual feedback and prevents you from copying the same thing @@ -42,25 +67,16 @@ define ['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './pa @pastebutton.flash?() copySection: ($el) -> - headings = ['h1', 'h2', 'h3'] - level = headings.indexOf $el[0].nodeName.toLowerCase() - # Pick up all elements until the next heading of the same level or higher - selector = headings.slice(0, level+1).join(',') - - if $el.addBack - # Jquery >= 1.8 - $el = $el.nextUntil(selector).addBack() - else - # Jquery < 1.8 - $el = $el.nextUntil(selector).andSelf() - html = '' - html += jQuery(e).outerHtml() for e in $el - - path = @getCurrentPath() - if path != null - @buffer html, path - else - @buffer html + content = getSection($el) + + # Fire a copy event, allow something more suitable to handle this. + evt = $.Event('copy') + evt.oerContent = content + evt.clipboardData = + setData: (t, c) => @buffer c, t + Aloha.activeEditable.obj.trigger(evt) + if not evt.isDefaultPrevented() + @buffer content init: -> plugin = @ @@ -89,7 +105,19 @@ define ['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './pa tooltip: 'Paste', click: (e) -> e.preventDefault() - range = Aloha.Selection.getRangeObject() + + # Fire a paste event, allow something else to handle this, if that + # something deems itself more suitable. + evt = $.Event('paste') + evt.clipboardData = + getData: (t) -> + if t == plugin.getContentType() + return plugin.getBuffer() + return null + Aloha.activeEditable.obj.trigger(evt) + return if evt.isDefaultPrevented() + + # Default paste behaviour follows $elements = jQuery plugin.getBuffer() dstpath = plugin.getCurrentPath() @@ -111,6 +139,7 @@ define ['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './pa else console.log "Image path already absolute: #{imgpath}" + range = Aloha.Selection.getRangeObject() GENTICS.Utils.Dom.insertIntoDOM $elements, range, Aloha.activeEditable.obj @copybutton = UI.adopt "copy", Button, diff --git a/src/plugins/oer/copy/lib/copy-plugin.js b/src/plugins/oer/copy/lib/copy-plugin.js index dbac070859..9b44348c27 100644 --- a/src/plugins/oer/copy/lib/copy-plugin.js +++ b/src/plugins/oer/copy/lib/copy-plugin.js @@ -1,10 +1,27 @@ -// Generated by CoffeeScript 1.5.0 +// Generated by CoffeeScript 1.6.3 (function() { - define(['aloha', 'aloha/plugin', 'jquery', 'ui/ui', 'ui/button', 'PubSub', './path', 'css!copy/css/copy.css'], function(Aloha, Plugin, jQuery, UI, Button, PubSub, Path) { - var buffer, srcpath; + var buffer, content_type, getSection, srcpath; buffer = ''; srcpath = null; + content_type = null; + getSection = function($el) { + var e, headings, html, level, selector, _i, _len; + headings = ['h1', 'h2', 'h3']; + level = headings.indexOf($el[0].nodeName.toLowerCase()); + selector = headings.slice(0, level + 1).join(','); + if ($el.addBack) { + $el = $el.nextUntil(selector).addBack(); + } else { + $el = $el.nextUntil(selector).andSelf(); + } + html = ''; + for (_i = 0, _len = $el.length; _i < _len; _i++) { + e = $el[_i]; + html += jQuery(e).outerHtml(); + } + return html; + }; return Plugin.create('copy', { getCurrentPath: function() { if (this.settings.path) { @@ -26,41 +43,46 @@ return srcpath; } }, - buffer: function(content, path) { + getContentType: function() { + if (localStorage) { + return localStorage.alohaOerCopyContentType; + } else { + return content_type; + } + }, + buffer: function(content, type, path) { var _base; buffer = content; buffer = buffer.replace(/id="[^"]+"/, ''); - srcpath = path; + content_type = type || 'text/html'; + srcpath = path || this.getCurrentPath(); if (localStorage) { localStorage.alohaOerCopyBuffer = buffer; } if (localStorage) { localStorage.alohaOerCopySrcPath = srcpath; } + if (localStorage) { + localStorage.alohaOerCopyContentType = content_type; + } this.copybutton.disable(); this.pastebutton.enable(); return typeof (_base = this.pastebutton).flash === "function" ? _base.flash() : void 0; }, copySection: function($el) { - var e, headings, html, level, path, selector, _i, _len; - headings = ['h1', 'h2', 'h3']; - level = headings.indexOf($el[0].nodeName.toLowerCase()); - selector = headings.slice(0, level + 1).join(','); - if ($el.addBack) { - $el = $el.nextUntil(selector).addBack(); - } else { - $el = $el.nextUntil(selector).andSelf(); - } - html = ''; - for (_i = 0, _len = $el.length; _i < _len; _i++) { - e = $el[_i]; - html += jQuery(e).outerHtml(); - } - path = this.getCurrentPath(); - if (path !== null) { - return this.buffer(html, path); - } else { - return this.buffer(html); + var content, evt, + _this = this; + content = getSection($el); + evt = $.Event('copy'); + evt.oerContent = content; + evt.clipboardData = { + setData: function(t, c) { + return _this.buffer(c, t); + } + }; + Aloha.activeEditable.obj.trigger(evt); + if (!evt.isDefaultPrevented()) { + return this.buffer(content); } }, init: function() { @@ -86,9 +108,21 @@ this.pastebutton = UI.adopt('paste', Button, { tooltip: 'Paste', click: function(e) { - var $elements, dstpath, range; + var $elements, dstpath, evt, range; e.preventDefault(); - range = Aloha.Selection.getRangeObject(); + evt = $.Event('paste'); + evt.clipboardData = { + getData: function(t) { + if (t === plugin.getContentType()) { + return plugin.getBuffer(); + } + return null; + } + }; + Aloha.activeEditable.obj.trigger(evt); + if (evt.isDefaultPrevented()) { + return; + } $elements = jQuery(plugin.getBuffer()); dstpath = plugin.getCurrentPath(); if (dstpath !== null) { @@ -112,6 +146,7 @@ }); } } + range = Aloha.Selection.getRangeObject(); return GENTICS.Utils.Dom.insertIntoDOM($elements, range, Aloha.activeEditable.obj); } }); diff --git a/src/plugins/oer/math/css/math.css b/src/plugins/oer/math/css/math.css index c6be96dc91..19ccd64258 100644 --- a/src/plugins/oer/math/css/math.css +++ b/src/plugins/oer/math/css/math.css @@ -102,6 +102,6 @@ math, background-image: url(../img/remove-02.png); } -button.done { +button.done, button.copy { float: right; } diff --git a/src/plugins/oer/math/lib/math-plugin.coffee b/src/plugins/oer/math/lib/math-plugin.coffee index 3abb693ed8..606bb7dedc 100644 --- a/src/plugins/oer/math/lib/math-plugin.coffee +++ b/src/plugins/oer/math/lib/math-plugin.coffee @@ -37,7 +37,7 @@ # -define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', 'css!../../../oer/math/css/math.css' ], (Aloha, Plugin, jQuery, Popover, UI) -> +define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', 'copy/copy-plugin', 'css!../../../oer/math/css/math.css' ], (Aloha, Plugin, jQuery, Popover, UI, Copy) -> EDITOR_HTML = '''
@@ -61,6 +61,7 @@ define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', ' Plain text +
''' @@ -138,6 +139,55 @@ define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', ' if editable.obj.is(':not(.aloha-root-editable)') return + # Bind copy and paste handlers. When a user copies content with math, place + # it on the clipboard with a different content type. This will prevent the + # cleanup that the browser does on namespaces. Use this alternative content + # type again when pasting. Prevent the browser default. This will only work + # in browsers that support event.clipboardData, chrome and safari to date. + editable.obj.on 'copy', (e) -> + content = e.oerContent or Aloha.getSelection().getRangeAt(0).cloneContents() + $content = $('
').append(content) + # If there is math among the content we're copying, treat it specially. + # Check that we also have a script tag in our selection, that occurs + # towards the end of the math and ensures we have the whole of it. + # The idea is to only do custom copy/paste if we need it, and let the + # browser handle other content. Also buffer it in our local copy buffer. + if $content.has('span.math-element').length and $content.has('script').length + e.preventDefault() + clipboard = e.clipboardData or e.originalEvent.clipboardData + clipboard.setData 'text/oerpub-content', $content.html() + else + Copy.buffer $content.html() + + editable.obj.on 'paste', (e) -> + clipboard = e.clipboardData or e.originalEvent.clipboardData + content = clipboard.getData('text/oerpub-content') + if content + e.preventDefault() + $content = jQuery( + '
') + .append(content).hide() + + # Remove ids, new ones will be assigned + $content.find('*[id]').removeAttr('id') + + # Paste content into editor + range = Aloha.getSelection().getRangeAt(0) + range.insertNode($content.get(0)) + + # Re-typeset math, because we need our context menu back + math = [] + $content.find('.math-element').each (idx, el) -> + deferred = $.Deferred() + math.push(deferred) + triggerMathJax jQuery(el), () -> deferred.resolve() + + # When we're done typesetting, show the content and unwrap it. + $.when.apply($content, math).done -> + $content.each () -> + $$$ = jQuery(@) + $$$.replaceWith $$$.contents() + # Bind ctrl+m to math insert/mathify editable.obj.bind 'keydown', 'ctrl+m', (evt) -> insertMath() @@ -280,6 +330,8 @@ define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', ' $editor.find('.remove').on 'click', => $span.trigger 'hide-popover' cleanupFormula($editor, $span, true) + $editor.find('.copy').on 'click', => + Copy.buffer $span.outerHtml(), 'text/oerpub-content' $formula = $editor.find('.formula') @@ -464,6 +516,13 @@ define [ 'aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', ' UI.adopt 'insertMath', null, click: () -> insertMath() + # Add a copy option to the mathjax menu + MathJax.Callback.Queue MathJax.Hub.Register.StartupHook "MathMenu Ready", () -> + copyCommand = MathJax.Menu.ITEM.COMMAND "Copy Math", (e,f,g) -> + $script = jQuery(document.getElementById(MathJax.Menu.jax.inputID)) + Copy.buffer $script.parent().parent().outerHtml(), 'text/oerpub-content' + MathJax.Menu.menu.items.unshift copyCommand + ob = selector: '.math-element' populator: buildEditor diff --git a/src/plugins/oer/math/lib/math-plugin.js b/src/plugins/oer/math/lib/math-plugin.js index 75547ad930..328db08ed3 100644 --- a/src/plugins/oer/math/lib/math-plugin.js +++ b/src/plugins/oer/math/lib/math-plugin.js @@ -2,9 +2,9 @@ (function() { var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; - define(['aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', 'css!../../../oer/math/css/math.css'], function(Aloha, Plugin, jQuery, Popover, UI) { + define(['aloha', 'aloha/plugin', 'jquery', 'overlay/overlay-plugin', 'ui/ui', 'copy/copy-plugin', 'css!../../../oer/math/css/math.css'], function(Aloha, Plugin, jQuery, Popover, UI, Copy) { var $_editor, EDITOR_HTML, LANGUAGES, MATHML_ANNOTATION_MIME_ENCODINGS, MATHML_ANNOTATION_NONMIME_ENCODINGS, TOOLTIP_TEMPLATE, addAnnotation, buildEditor, cleanupFormula, findFormula, getEncoding, getMathFor, insertMath, insertMathInto, makeCloseIcon, ob, placeCursorAfter, squirrelMath, triggerMathJax; - EDITOR_HTML = '
\n
\n

\n \n
\n \n
'; + EDITOR_HTML = '
\n
\n

\n \n
\n \n
'; $_editor = jQuery(EDITOR_HTML); LANGUAGES = { 'math/asciimath': { @@ -77,6 +77,46 @@ if (editable.obj.is(':not(.aloha-root-editable)')) { return; } + editable.obj.on('copy', function(e) { + var $content, clipboard, content; + content = e.oerContent || Aloha.getSelection().getRangeAt(0).cloneContents(); + $content = $('
').append(content); + if ($content.has('span.math-element').length && $content.has('script').length) { + e.preventDefault(); + clipboard = e.clipboardData || e.originalEvent.clipboardData; + return clipboard.setData('text/oerpub-content', $content.html()); + } else { + return Copy.buffer($content.html()); + } + }); + editable.obj.on('paste', function(e) { + var $content, clipboard, content, math, range; + clipboard = e.clipboardData || e.originalEvent.clipboardData; + content = clipboard.getData('text/oerpub-content'); + if (content) { + e.preventDefault(); + $content = jQuery('
').append(content).hide(); + $content.find('*[id]').removeAttr('id'); + range = Aloha.getSelection().getRangeAt(0); + range.insertNode($content.get(0)); + math = []; + $content.find('.math-element').each(function(idx, el) { + var deferred; + deferred = $.Deferred(); + math.push(deferred); + return triggerMathJax(jQuery(el), function() { + return deferred.resolve(); + }); + }); + return $.when.apply($content, math).done(function() { + return $content.each(function() { + var $$$; + $$$ = jQuery(this); + return $$$.replaceWith($$$.contents()); + }); + }); + } + }); editable.obj.bind('keydown', 'ctrl+m', function(evt) { insertMath(); return evt.preventDefault(); @@ -209,6 +249,9 @@ $span.trigger('hide-popover'); return cleanupFormula($editor, $span, true); }); + $editor.find('.copy').on('click', function() { + return Copy.buffer($span.outerHtml(), 'text/oerpub-content'); + }); $formula = $editor.find('.formula'); mimeType = $span.find('script[type]').attr('type') || 'math/tex'; mimeType = mimeType.split(';')[0]; @@ -387,6 +430,15 @@ return insertMath(); } }); + MathJax.Callback.Queue(MathJax.Hub.Register.StartupHook("MathMenu Ready", function() { + var copyCommand; + copyCommand = MathJax.Menu.ITEM.COMMAND("Copy Math", function(e, f, g) { + var $script; + $script = jQuery(document.getElementById(MathJax.Menu.jax.inputID)); + return Copy.buffer($script.parent().parent().outerHtml(), 'text/oerpub-content'); + }); + return MathJax.Menu.menu.items.unshift(copyCommand); + })); ob = { selector: '.math-element', populator: buildEditor, diff --git a/src/plugins/oer/semanticblock/lib/semanticblock-plugin.coffee b/src/plugins/oer/semanticblock/lib/semanticblock-plugin.coffee index 1fa085b343..8bd907e89c 100644 --- a/src/plugins/oer/semanticblock/lib/semanticblock-plugin.coffee +++ b/src/plugins/oer/semanticblock/lib/semanticblock-plugin.coffee @@ -86,7 +86,7 @@ define ['aloha', 'block/blockmanager', 'aloha/plugin', 'aloha/pluginmanager', 'j callback: (e) -> # grab the content of the block that was just clicked $element = jQuery(this).parents('.semantic-container').first() - Copy.buffer $element.outerHtml(), Copy.getCurrentPath() + Copy.buffer $element.outerHtml() , name: 'mouseover' selector: '.semantic-container .semantic-controls-top .copy' diff --git a/src/plugins/oer/semanticblock/lib/semanticblock-plugin.js b/src/plugins/oer/semanticblock/lib/semanticblock-plugin.js index 6c7c70c68f..3021e2eda9 100644 --- a/src/plugins/oer/semanticblock/lib/semanticblock-plugin.js +++ b/src/plugins/oer/semanticblock/lib/semanticblock-plugin.js @@ -68,7 +68,7 @@ callback: function(e) { var $element; $element = jQuery(this).parents('.semantic-container').first(); - return Copy.buffer($element.outerHtml(), Copy.getCurrentPath()); + return Copy.buffer($element.outerHtml()); } }, { name: 'mouseover',