From db57fd405fa9c66dc0ea4bc063ecaf806a14e875 Mon Sep 17 00:00:00 2001 From: Arsen Melikyan Date: Mon, 11 Jan 2016 04:54:26 +0400 Subject: [PATCH 1/3] Add 'copy to clipboard' buttons for easy challenge link and code copying --- bower.json | 3 +- client/commonFramework/create-editor.js | 105 ++++++++++++++++++++++++ gulpfile.js | 1 + server/views/challenges/showHTML.jade | 2 + server/views/challenges/showJS.jade | 2 + 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index b43360485a..361ecbf7bc 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "lightbox2": "~2.8.1", "rxjs": "~4.0.6", "CodeMirror": "~5.8.0", - "chai": "~3.4.1" + "chai": "~3.4.1", + "clipboard": "~1.5.5" } } diff --git a/client/commonFramework/create-editor.js b/client/commonFramework/create-editor.js index 7990fb9c4f..56685b201b 100644 --- a/client/commonFramework/create-editor.js +++ b/client/commonFramework/create-editor.js @@ -3,6 +3,7 @@ window.common = (function(global) { Rx: { Subject, Observable }, CodeMirror, emmetCodeMirror, + Clipboard, common = { init: [] } } = global; @@ -36,6 +37,102 @@ window.common = (function(global) { } ); + var clipboard = new Clipboard('.demo1', { + text: function(trigger) { + var type; + switch (common.challengeType) { + case common.challengeTypes.HTML: + type = 'html'; + break; + case common.challengeTypes.JS: + case common.challengeTypes.BONFIRE: + type = 'javascript'; + break; + default: + type = ''; + } + + var returnValue = ''; + if (trigger.id === 'markdown') { + returnValue = '```' + type + '\n' + editor.getSelection() + '\n```'; + editor.replaceSelection(editor.getSelection()); + return returnValue; + } else if (trigger.id === 'plain') { + returnValue = editor.getSelection(); + editor.replaceSelection(editor.getSelection()); + return returnValue; + } else if (trigger.id === 'link') { + editor.replaceSelection(editor.getSelection()); + return ('[Challenge - ' + common.challengeName + + (common.username ? ' (' + common.username + '\'s solution)' : '') + + '](' + window.location + ')') + .replace(/\)/g, '%29').replace(/%29\]/g, ')]') + ')'; + } + } + }); + + common.clipboard = clipboard; + + var copyButton = ``; + + var left, top; + $(document).mousemove(function(event) { + left = event.pageX; + top = event.pageY; + }); + + function showPopover() { + setTimeout(function() { + if (editor.somethingSelected()) { + $('#demo').popover('show'); + var editorOffset = $('#mainEditorPanel > form.code').offset(), + editorHeight = $('#mainEditorPanel > form.code').height(), + editorWidth = $('#mainEditorPanel > form.code').width(), + theHeight = $('.popover').height(), + theWidth = $('.popover').width(); + if (left < editorOffset.left + || left > editorOffset.left + editorWidth - theWidth) { + left = (editorOffset.left + editorWidth) / 2; + } + if (top < editorOffset.top + || top > editorOffset.top + editorHeight - theHeight) { + top = (editorOffset.top + editorHeight) / 2; + } + $('.popover').css('left', (left + 10) + 'px'); + $('.popover').css('top', (top - (theHeight / 2)) + 'px'); + } + }, 100); + } + + if ( + challengeType === challengeTypes.HTML || + challengeType === challengeTypes.JS || + challengeType === challengeTypes.BONFIRE + ) { + + $('body').append(copyButton); + $('#demo').popover({html: true}); + + CodeMirror.on(document, 'mousedown', function() { + $('#demo').popover('hide'); + }); + + CodeMirror.on(document, 'mouseup', showPopover); + + $(document).on('click', '.demo1', function(e) { + e.preventDefault(); + $('#demo').popover('hide'); + }); + } + editor.setSize('100%', 'auto'); common.editorExecute$ = new Subject(); @@ -68,6 +165,14 @@ window.common = (function(global) { 'Cmd-Enter': function() { common.editorExecute$.onNext(); return false; + }, + 'Ctrl-A': function(cm) { + cm.execCommand('selectAll'); + showPopover(); + }, + 'Cmd-A': function(cm) { + cm.execCommand('selectAll'); + showPopover(); } }); diff --git a/gulpfile.js b/gulpfile.js index 902f142ce3..45e64095bf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -80,6 +80,7 @@ var paths = { vendorChallenges: [ 'public/bower_components/jshint/dist/jshint.js', 'public/bower_components/chai/chai.js', + 'public/bower_components/clipboard/dist/clipboard.min.js', 'public/bower_components/CodeMirror/lib/codemirror.js', 'public/bower_components/CodeMirror/addon/edit/closebrackets.js', 'public/bower_components/CodeMirror/addon/edit/matchbrackets.js', diff --git a/server/views/challenges/showHTML.jade b/server/views/challenges/showHTML.jade index 7ee05e21a8..8a6b2355d5 100644 --- a/server/views/challenges/showHTML.jade +++ b/server/views/challenges/showHTML.jade @@ -85,6 +85,8 @@ block content common.dashedName = !{JSON.stringify(dashedName)}; common.isCompleted = !{JSON.stringify(isCompleted)}; + common.username = !{JSON.stringify(user && user.username || '')}; + common.init.push(function() { common.editor.setOption('lint', false); common.editor.setOption('mode', 'text/html'); diff --git a/server/views/challenges/showJS.jade b/server/views/challenges/showJS.jade index 7ce81d65b3..444fa4549f 100644 --- a/server/views/challenges/showJS.jade +++ b/server/views/challenges/showJS.jade @@ -89,6 +89,8 @@ block content common.dashedName = !{JSON.stringify(dashedName)}; common.isCompleted = !{JSON.stringify(isCompleted)}; + common.username = !{JSON.stringify(user && user.username || '')}; + include ../partials/challenge-footer script. common.init.push(function() { From e1aec710aaa51de37fb42365d41caafbd28ea26b Mon Sep 17 00:00:00 2001 From: Abhisek Pattnaik Date: Sat, 16 Jan 2016 11:12:57 +0530 Subject: [PATCH 2/3] Fix url encoding --- client/commonFramework/create-editor.js | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/client/commonFramework/create-editor.js b/client/commonFramework/create-editor.js index 56685b201b..76c2aa1e2e 100644 --- a/client/commonFramework/create-editor.js +++ b/client/commonFramework/create-editor.js @@ -46,27 +46,27 @@ window.common = (function(global) { break; case common.challengeTypes.JS: case common.challengeTypes.BONFIRE: - type = 'javascript'; + type = 'js'; break; default: type = ''; } var returnValue = ''; - if (trigger.id === 'markdown') { - returnValue = '```' + type + '\n' + editor.getSelection() + '\n```'; - editor.replaceSelection(editor.getSelection()); - return returnValue; - } else if (trigger.id === 'plain') { - returnValue = editor.getSelection(); - editor.replaceSelection(editor.getSelection()); - return returnValue; - } else if (trigger.id === 'link') { - editor.replaceSelection(editor.getSelection()); - return ('[Challenge - ' + common.challengeName + - (common.username ? ' (' + common.username + '\'s solution)' : '') - + '](' + window.location + ')') - .replace(/\)/g, '%29').replace(/%29\]/g, ')]') + ')'; + switch (trigger.id) { + case 'markdown': + returnValue = '```' + type + '\n' + editor.getSelection() + '\n```'; + editor.replaceSelection(editor.getSelection()); + return returnValue; + case 'plain': + returnValue = editor.getSelection(); + editor.replaceSelection(editor.getSelection()); + return returnValue; + case 'link': + editor.replaceSelection(editor.getSelection()); + return '[Challenge - ' + common.challengeName + + (common.username ? ' (' + common.username + '\'s solution)' : '') + + '](' + String(window.location).replace(/\)/g, '%29') + ')'; } } }); From d380640480a4a4255010dca96b275246cb587e8d Mon Sep 17 00:00:00 2001 From: Arsen Melikyan Date: Sat, 16 Jan 2016 10:49:44 +0400 Subject: [PATCH 3/3] Fix double-click and mousemove issues and do some linting --- client/commonFramework/create-editor.js | 76 +++++++++++++++---------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/client/commonFramework/create-editor.js b/client/commonFramework/create-editor.js index 76c2aa1e2e..6282967149 100644 --- a/client/commonFramework/create-editor.js +++ b/client/commonFramework/create-editor.js @@ -37,7 +37,7 @@ window.common = (function(global) { } ); - var clipboard = new Clipboard('.demo1', { + var clipboard = new Clipboard('.copy-btn', { text: function(trigger) { var type; switch (common.challengeType) { @@ -63,6 +63,7 @@ window.common = (function(global) { editor.replaceSelection(editor.getSelection()); return returnValue; case 'link': + default: editor.replaceSelection(editor.getSelection()); return '[Challenge - ' + common.challengeName + (common.username ? ' (' + common.username + '\'s solution)' : '') @@ -73,45 +74,62 @@ window.common = (function(global) { common.clipboard = clipboard; - var copyButton = ``; - var left, top; + var left = -5, top = -5; $(document).mousemove(function(event) { left = event.pageX; top = event.pageY; }); - function showPopover() { - setTimeout(function() { - if (editor.somethingSelected()) { - $('#demo').popover('show'); - var editorOffset = $('#mainEditorPanel > form.code').offset(), - editorHeight = $('#mainEditorPanel > form.code').height(), - editorWidth = $('#mainEditorPanel > form.code').width(), - theHeight = $('.popover').height(), - theWidth = $('.popover').width(); - if (left < editorOffset.left - || left > editorOffset.left + editorWidth - theWidth) { - left = (editorOffset.left + editorWidth) / 2; + function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) { + func.apply(context, args); } - if (top < editorOffset.top - || top > editorOffset.top + editorHeight - theHeight) { - top = (editorOffset.top + editorHeight) / 2; - } - $('.popover').css('left', (left + 10) + 'px'); - $('.popover').css('top', (top - (theHeight / 2)) + 'px'); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + func.apply(context, args); } - }, 100); + }; } + var showPopover = debounce(function() { + if (editor.somethingSelected()) { + $('#copy-btn-parent').popover('show'); + var editorOffset = $('#mainEditorPanel > form.code').offset(), + editorHeight = $('#mainEditorPanel > form.code').height(), + editorWidth = $('#mainEditorPanel > form.code').width(), + theHeight = $('.popover').height(), + theWidth = $('.popover').width(); + if ((left < editorOffset.left + || left > editorOffset.left + editorWidth - theWidth) || left === -5) { + left = (editorOffset.left + editorWidth) / 2; + } + if ((top < editorOffset.top + || top > editorOffset.top + editorHeight - theHeight) || top === -5) { + top = (editorOffset.top + editorHeight) / 2; + } + $('.popover').css('left', (left + 10) + 'px'); + $('.popover').css('top', (top - (theHeight / 2) - 7) + 'px'); + } + }, 250); + if ( challengeType === challengeTypes.HTML || challengeType === challengeTypes.JS || @@ -119,17 +137,17 @@ contents of code editor'>Copy as Plain Code">`; ) { $('body').append(copyButton); - $('#demo').popover({html: true}); + $('#copy-btn-parent').popover({html: true}); CodeMirror.on(document, 'mousedown', function() { - $('#demo').popover('hide'); + $('#copy-btn-parent').popover('hide'); }); CodeMirror.on(document, 'mouseup', showPopover); - $(document).on('click', '.demo1', function(e) { + $(document).on('click', '.copy-btn', function(e) { e.preventDefault(); - $('#demo').popover('hide'); + $('#copy-btn-parent').popover('hide'); }); }