From 5bed81563e5bd457c5760c66cfa8135faac0e3e0 Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Mon, 17 Mar 2014 22:01:29 +0000 Subject: [PATCH] Add shim for codemirror on touchscreens fixes #2385 - stolen the CM shim from js-bin - if we're on a touchscreen device, don't use CM - if we're on a touchscreen device, show a coming soon message for uploads --- .gitignore | 1 - Gruntfile.js | 6 +- .../assets/lib/editor/mobileCodeMirror.js | 112 ++++++++++++++++++ .../lib/showdown/extensions/ghostdown.js | 20 +++- core/server/views/editor.hbs | 11 ++ core/test/functional/base.js | 14 ++- 6 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 core/client/assets/lib/editor/mobileCodeMirror.js diff --git a/.gitignore b/.gitignore index b0e08e1b99..aaaf2630e0 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ projectFilesBackup /core/client/tpl/hbs-tpl.js /core/client/assets/css /core/client/assets/fonts -/core/client/assets/vendor /core/server/data/export/exported* /docs /_site diff --git a/Gruntfile.js b/Gruntfile.js index 54327be17c..38657b01e0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -485,8 +485,8 @@ var path = require('path'), 'core/client/assets/lib/editor/uploadManager.js', 'core/client/assets/lib/editor/markdownEditor.js', 'core/client/assets/lib/editor/htmlPreview.js', - 'core/client/assets/lib/editor/scrollHandler.js' - + 'core/client/assets/lib/editor/scrollHandler.js', + 'core/client/assets/lib/editor/mobileCodeMirror.js' ], 'core/built/scripts/templates.js': [ @@ -548,7 +548,7 @@ var path = require('path'), 'core/client/assets/lib/editor/markdownEditor.js', 'core/client/assets/lib/editor/htmlPreview.js', 'core/client/assets/lib/editor/scrollHandler.js', - + 'core/client/assets/lib/editor/mobileCodeMirror.js', 'core/client/tpl/hbs-tpl.js', diff --git a/core/client/assets/lib/editor/mobileCodeMirror.js b/core/client/assets/lib/editor/mobileCodeMirror.js new file mode 100644 index 0000000000..7e2d3faad5 --- /dev/null +++ b/core/client/assets/lib/editor/mobileCodeMirror.js @@ -0,0 +1,112 @@ +// Taken from js-bin with thanks to Remy Sharp +// yeah, nasty, but it allows me to switch from a RTF to plain text if we're running a iOS + +/*global Ghost, $, _, DocumentTouch, CodeMirror*/ +(function () { + Ghost.touchEditor = false; + + var noop = function () {}, + hasTouchScreen, + smallScreen, + TouchEditor, + _oldCM, + key; + + // Taken from "Responsive design & the Guardian" with thanks to Matt Andrews + // Added !window._phantom so that the functional tests run as though this is not a touch screen. + // In future we can do something more advanced here for testing both touch and non touch + hasTouchScreen = function () { + return !window._phantom && + ( + ('ontouchstart' in window) || + (window.DocumentTouch && document instanceof DocumentTouch) + ); + }; + + smallScreen = function () { + if (window.matchMedia('(max-width: 1000px)').matches) { + return true; + } + + return false; + }; + + if (hasTouchScreen()) { + $('body').addClass('touch-editor'); + Ghost.touchEditor = true; + + TouchEditor = function (el, options) { + /*jshint unused:false*/ + this.textarea = el; + this.win = { document : this.textarea }; + this.ready = true; + this.wrapping = document.createElement('div'); + + var textareaParent = this.textarea.parentNode; + this.wrapping.appendChild(this.textarea); + textareaParent.appendChild(this.wrapping); + + this.textarea.style.opacity = 1; + + $(this.textarea).blur(_.throttle(function () { + $(document).trigger('markdownEditorChange', { panelId: el.id }); + }, 200)); + + if (!smallScreen()) { + $(this.textarea).on('change', _.throttle(function () { + $(document).trigger('markdownEditorChange', { panelId: el.id }); + }, 200)); + } + }; + + TouchEditor.prototype = { + setOption: function (type, handler) { + if (type === 'onChange') { + $(this.textarea).change(handler); + } + }, + eachLine: function () { + return []; + }, + getValue: function () { + return this.textarea.value; + }, + setValue: function (code) { + this.textarea.value = code; + }, + focus: noop, + getCursor: function () { + return { line: 0, ch: 0 }; + }, + setCursor: noop, + currentLine: function () { + return 0; + }, + cursorPosition: function () { + return { character: 0 }; + }, + addMarkdown: noop, + nthLine: noop, + refresh: noop, + selectLines: noop, + on: noop + }; + + _oldCM = CodeMirror; + + // CodeMirror = noop; + + for (key in _oldCM) { + if (_oldCM.hasOwnProperty(key)) { + CodeMirror[key] = noop; + } + } + + CodeMirror.fromTextArea = function (el, options) { + return new TouchEditor(el, options); + }; + + CodeMirror.keyMap = { basic: {} }; + + } +}()); \ No newline at end of file diff --git a/core/client/assets/lib/showdown/extensions/ghostdown.js b/core/client/assets/lib/showdown/extensions/ghostdown.js index c684dd2d6f..9c6fb9cebf 100644 --- a/core/client/assets/lib/showdown/extensions/ghostdown.js +++ b/core/client/assets/lib/showdown/extensions/ghostdown.js @@ -1,4 +1,5 @@ /* jshint node:true, browser:true */ +var Ghost = Ghost || {}; (function () { var ghostdown = function () { return [ @@ -12,15 +13,24 @@ pathRegex = /^(\/)?([^\/\0]+(\/)?)+$/i; return text.replace(imageMarkdownRegex, function (match, key, alt, src) { - var result = ""; + var result = '', + output; if (src && (src.match(uriRegex) || src.match(pathRegex))) { result = ''; } - return '
' + result + - '
Add image of ' + alt + '
' + - '' + - '
'; + + if (Ghost && Ghost.touchEditor) { + output = '
' + + result + '
Mobile uploads coming soon
'; + } else { + output = '
' + + result + '
Add image of ' + alt + '
' + + '' + + '
'; + } + + return output; }); } }, diff --git a/core/server/views/editor.hbs b/core/server/views/editor.hbs index e3a0f0ec3d..0d5e614034 100644 --- a/core/server/views/editor.hbs +++ b/core/server/views/editor.hbs @@ -1,4 +1,15 @@ {{!< default}} +
diff --git a/core/test/functional/base.js b/core/test/functional/base.js index 0d73b8c9aa..619fe2893a 100644 --- a/core/test/functional/base.js +++ b/core/test/functional/base.js @@ -49,12 +49,16 @@ var DEBUG = false, // TOGGLE THIS TO GET MORE SCREENSHOTS casper.writeContentToCodeMirror = function (content) { var lines = content.split("\n"); - casper.each(lines, function (self, line) { - self.sendKeys('.CodeMirror-wrap textarea', line, {keepFocus: true}); - self.sendKeys('.CodeMirror-wrap textarea', casper.page.event.key.Enter, {keepFocus: true}); - }); + casper.waitForSelector('.CodeMirror-wrap textarea', function onSuccess() { + casper.each(lines, function (self, line) { + self.sendKeys('.CodeMirror-wrap textarea', line, {keepFocus: true}); + self.sendKeys('.CodeMirror-wrap textarea', casper.page.event.key.Enter, {keepFocus: true}); + }); - return this; + return this; + }, function onTimeout() { + casper.test.fail('CodeMirror was not found.'); + }, 2000); }; casper.waitForOpaque = function (classname, then, timeout) {