From ee13047e263257b9778fd2f4e441e6e40ab73649 Mon Sep 17 00:00:00 2001 From: Will Kwon Date: Sat, 2 Apr 2016 12:37:20 -0700 Subject: [PATCH 1/6] Updating the rest of the modules to buildToolsVersion 23.0.3 --- WordPressUtils/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressUtils/build.gradle b/WordPressUtils/build.gradle index 7dfafeaf4632..09dbb62b48c8 100644 --- a/WordPressUtils/build.gradle +++ b/WordPressUtils/build.gradle @@ -29,7 +29,7 @@ android { publishNonDefault true compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName "1.9.0" From c5b98a88c01551e8dbf44366072607d364ec01dd Mon Sep 17 00:00:00 2001 From: Maxime Biais Date: Wed, 6 Apr 2016 18:58:13 +0200 Subject: [PATCH 2/6] Squashed 'libs/editor/' changes from a35d654..0f6fba8 0f6fba8 Merge pull request #334 from wordpress-mobile/issue/292-backspace-bug 041f285 Changed ZSSEditor.getYCaretInfo to find parent contenteditable divs correctly 92cb325 Fixed an issue with losing cursor focus on API<19 0c2b5fd Made blockquote parsing handle DIVs tags as paragraphs c62ce8e Convert divs to paragraph tags when extracting the HTML from the editor f5e5d26 Changed getFocusedField to use this.focusedField, in order to avoid div targeting issues now that paragraphs are also divs c925ebd Add line height and margin styling to div tags (duplicated from p tags, and excluding the contenteditable and separator divs eb1677c Drop defaultParagraphSeparator assignment and use divs instead of ps for paragraphs (WebView default) 5e90578 Merge pull request #333 from wordpress-mobile/issue/258-remove-failed-uploads-when-switching-to-html-mode 9a0ef46 Merge commit '4c9324cf1eee00b66c76e0d5a917c86e1293a845' into develop ac96e74 fix #258: prompt the user to delete failed uploads before switching to HTML mode b856f7b update to android-gradle 2.0.0-rc1 e6f4e6b Add missing classes for images inserted from media library - also fix a bug with undefined alt text 31d2970 Add missing attributes for uploaded images ce6ab9d update to android-gradle-2.0.0-rc1 git-subtree-dir: libs/editor git-subtree-split: 0f6fba8fc434c4007c7cdd300c4dd9fa2df5a218 --- WordPressEditor/build.gradle | 2 +- .../android/editor/EditorFragment.java | 102 +++++++++++------- .../src/main/res/values/strings.xml | 3 + example/build.gradle | 2 +- .../editor-common/assets/ZSSRichTextEditor.js | 69 ++++++++---- libs/editor-common/assets/editor-android.css | 9 ++ 6 files changed, 128 insertions(+), 59 deletions(-) diff --git a/WordPressEditor/build.gradle b/WordPressEditor/build.gradle index 63cd3a5c681b..5bbf8d5b439b 100644 --- a/WordPressEditor/build.gradle +++ b/WordPressEditor/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-beta7' + classpath 'com.android.tools.build:gradle:2.0.0-rc1' } } diff --git a/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java b/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java index ccef283f6791..d149212e7b1d 100755 --- a/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java +++ b/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java @@ -5,6 +5,7 @@ import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; @@ -407,55 +408,82 @@ protected void initJsEditor() { } } - @Override - public void onClick(View v) { - int id = v.getId(); - if (id == R.id.format_bar_button_html) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.HTML_BUTTON_TAPPED); + public void checkForFailedUploadAndSwitchToHtmlMode(final ToggleButton toggleButton) { + // Show an Alert Dialog asking the user if he wants to remove all failed media before upload + if (hasFailedMediaUploads()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.editor_failed_uploads_switch_html) + .setPositiveButton(R.string.editor_remove_failed_uploads, new OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // Clear failed uploads and switch to HTML mode + removeAllFailedMediaUploads(); + toggleHtmlMode(toggleButton); + } + }).setNegativeButton(android.R.string.cancel, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + toggleButton.setChecked(false); + } + }); + builder.create().show(); + } else { + toggleHtmlMode(toggleButton); + } + } - // Don't switch to HTML mode if currently uploading media - if (!mUploadingMedia.isEmpty()) { - ((ToggleButton) v).setChecked(false); + private void toggleHtmlMode(final ToggleButton toggleButton) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.HTML_BUTTON_TAPPED); - if (isAdded()) { - ToastUtils.showToast(getActivity(), R.string.alert_html_toggle_uploading, ToastUtils.Duration.LONG); - } - return; + // Don't switch to HTML mode if currently uploading media + if (!mUploadingMedia.isEmpty()) { + toggleButton.setChecked(false); + + if (isAdded()) { + ToastUtils.showToast(getActivity(), R.string.alert_html_toggle_uploading, ToastUtils.Duration.LONG); } + return; + } - clearFormatBarButtons(); - updateFormatBarEnabledState(true); + clearFormatBarButtons(); + updateFormatBarEnabledState(true); - if (((ToggleButton) v).isChecked()) { - mSourceViewTitle.setText(getTitle()); + if (toggleButton.isChecked()) { + mSourceViewTitle.setText(getTitle()); - SpannableString spannableContent = new SpannableString(getContent()); - HtmlStyleUtils.styleHtmlForDisplay(spannableContent); - mSourceViewContent.setText(spannableContent); + SpannableString spannableContent = new SpannableString(getContent()); + HtmlStyleUtils.styleHtmlForDisplay(spannableContent); + mSourceViewContent.setText(spannableContent); - mWebView.setVisibility(View.GONE); - mSourceView.setVisibility(View.VISIBLE); + mWebView.setVisibility(View.GONE); + mSourceView.setVisibility(View.VISIBLE); - mSourceViewContent.requestFocus(); - mSourceViewContent.setSelection(0); + mSourceViewContent.requestFocus(); + mSourceViewContent.setSelection(0); - InputMethodManager imm = ((InputMethodManager) getActivity() - .getSystemService(Context.INPUT_METHOD_SERVICE)); - imm.showSoftInput(mSourceViewContent, InputMethodManager.SHOW_IMPLICIT); - } else { - mWebView.setVisibility(View.VISIBLE); - mSourceView.setVisibility(View.GONE); + InputMethodManager imm = ((InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE)); + imm.showSoftInput(mSourceViewContent, InputMethodManager.SHOW_IMPLICIT); + } else { + mWebView.setVisibility(View.VISIBLE); + mSourceView.setVisibility(View.GONE); - mTitle = mSourceViewTitle.getText().toString(); - mContentHtml = mSourceViewContent.getText().toString(); - updateVisualEditorFields(); + mTitle = mSourceViewTitle.getText().toString(); + mContentHtml = mSourceViewContent.getText().toString(); + updateVisualEditorFields(); - // Update the list of failed media uploads - mWebView.execJavaScriptFromString("ZSSEditor.getFailedMedia();"); + // Update the list of failed media uploads + mWebView.execJavaScriptFromString("ZSSEditor.getFailedMedia();"); - // Reset selection to avoid buggy cursor behavior - mWebView.execJavaScriptFromString("ZSSEditor.resetSelectionOnField('zss_field_content');"); - } + // Reset selection to avoid buggy cursor behavior + mWebView.execJavaScriptFromString("ZSSEditor.resetSelectionOnField('zss_field_content');"); + } + } + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.format_bar_button_html) { + checkForFailedUploadAndSwitchToHtmlMode((ToggleButton) v); } else if (id == R.id.format_bar_button_media) { mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); ((ToggleButton) v).setChecked(false); diff --git a/WordPressEditor/src/main/res/values/strings.xml b/WordPressEditor/src/main/res/values/strings.xml index 5da604328659..f81a0ae87e52 100644 --- a/WordPressEditor/src/main/res/values/strings.xml +++ b/WordPressEditor/src/main/res/values/strings.xml @@ -81,4 +81,7 @@ Image thumbnail + Some media uploads have failed. You can\'t switch to HTML mode + in this state. Remove all failed uploads and continue? + Remove failed uploads diff --git a/example/build.gradle b/example/build.gradle index e91d4b7beda4..ff876849c1c0 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-beta7' + classpath 'com.android.tools.build:gradle:2.0.0-rc1' } } diff --git a/libs/editor-common/assets/ZSSRichTextEditor.js b/libs/editor-common/assets/ZSSRichTextEditor.js index 5cde6455bf63..e4e136bc3b09 100755 --- a/libs/editor-common/assets/ZSSRichTextEditor.js +++ b/libs/editor-common/assets/ZSSRichTextEditor.js @@ -26,7 +26,9 @@ const NodeName = { LI: "LI", CODE: "CODE", SPAN: "SPAN", - BR: "BR" + BR: "BR", + DIV: "DIV", + BODY: "BODY" }; // The editor object @@ -63,7 +65,7 @@ ZSSEditor.editableFields = {}; ZSSEditor.lastTappedNode = null; // The default paragraph separator -ZSSEditor.defaultParagraphSeparator = 'p'; +ZSSEditor.defaultParagraphSeparator = 'div'; // Video format tags supported by the [video] shortcode: https://codex.wordpress.org/Video_Shortcode // mp4, m4v and webm prioritized since they're supported by the stock player as of Android API 23 @@ -92,7 +94,6 @@ ZSSEditor.init = function() { } document.execCommand('insertBrOnReturn', false, false); - document.execCommand('defaultParagraphSeparator', false, this.defaultParagraphSeparator); var editor = $('div.field').each(function() { var editableField = new ZSSField($(this)); @@ -172,7 +173,7 @@ ZSSEditor.formatNewLine = function(e) { this.formatNewLineInsideBlockquote(e); } else if (!ZSSEditor.isCommandEnabled('insertOrderedList') && !ZSSEditor.isCommandEnabled('insertUnorderedList')) { - document.execCommand('formatBlock', false, 'p'); + document.execCommand('formatBlock', false, 'div'); } } else { e.preventDefault(); @@ -192,20 +193,13 @@ ZSSEditor.getField = function(fieldId) { }; ZSSEditor.getFocusedField = function() { - var currentField = $(this.closerParentNodeWithName('div')); + var currentField = $(this.findParentContenteditableDiv()); var currentFieldId; if (currentField) { currentFieldId = currentField.attr('id'); } - while (currentField && (!currentFieldId || this.editableFields[currentFieldId] == null)) { - currentField = this.closerParentNodeStartingAtNode('div', currentField); - if (currentField) { - currentFieldId = currentField.attr('id'); - } - } - if (!currentFieldId) { ZSSEditor.resetSelectionOnField('zss_field_content'); currentFieldId = 'zss_field_content'; @@ -480,7 +474,7 @@ ZSSEditor.getYCaretInfo = function() { // if (needsToWorkAroundNewlineBug) { var closerParentNode = ZSSEditor.closerParentNode(); - var closerDiv = ZSSEditor.closerParentNodeWithName('div'); + var closerDiv = ZSSEditor.findParentContenteditableDiv(); var fontSize = $(closerParentNode).css('font-size'); var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5); @@ -654,7 +648,7 @@ ZSSEditor.setHeading = function(heading) { }; ZSSEditor.setParagraph = function() { - var formatTag = "p"; + var formatTag = "div"; var formatBlock = document.queryCommandValue('formatBlock'); if (formatBlock.length > 0 && formatBlock.toLowerCase() == formatTag) { @@ -1028,8 +1022,11 @@ ZSSEditor.updateImage = function(url, alt) { }; ZSSEditor.insertImage = function(url, remoteId, alt) { - var html = '' + alt + ''; - + var html = '' + alt
+    }
+    html += '' this.insertHTML(this.wrapInParagraphTags(html)); this.sendEnabledStyles(); }; @@ -1138,6 +1135,9 @@ ZSSEditor.finishLocalImageSwap = function(image, imageNode, imageNodeIdentifier, imageNode.attr('remoteurl', image.getAttribute("remoteurl")); } imageNode.attr('src', image.src); + // Set extra attributes and classes used by WordPress + imageNode.attr({'width': image.width, 'height': image.height}); + imageNode.addClass("alignnone size-full"); ZSSEditor.markImageUploadDone(imageNodeIdentifier); var joinedArguments = ZSSEditor.getJoinedFocusedFieldIdAndCaretArguments(); ZSSEditor.callback("callback-input", joinedArguments); @@ -2289,7 +2289,7 @@ ZSSEditor.removeVisualFormatting = function( html ) { str = ZSSEditor.replaceVideoPressVideosForShortcode( str ); str = ZSSEditor.replaceVideosForShortcode( str ); return str; -} +}; ZSSEditor.insertHTML = function(html) { document.execCommand('insertHTML', false, html); @@ -2618,7 +2618,9 @@ ZSSEditor.getAncestorElementForSettingBlockquote = function(range) { || parentElement.nodeName == NodeName.OL || parentElement.nodeName == NodeName.LI || parentElement.nodeName == NodeName.CODE - || parentElement.nodeName == NodeName.SPAN)) { + || parentElement.nodeName == NodeName.SPAN + // Include nested divs, but ignore the parent contenteditable field div + || (parentElement.nodeName == NodeName.DIV && parentElement.parentElement.nodeName != NodeName.BODY))) { parentElement = parentElement.parentNode; } @@ -2740,6 +2742,23 @@ ZSSEditor.hasPreviousSiblingWithName = function(node, siblingNodeName) { // MARK: - Parent nodes & tags +ZSSEditor.findParentContenteditableDiv = function() { + var parentNode = null; + var selection = window.getSelection(); + if (selection.rangeCount < 1) { + return null; + } + var range = selection.getRangeAt(0).cloneRange(); + + var referenceNode = this.closerParentNodeWithNameRelativeToNode('div', range.commonAncestorContainer); + + while (referenceNode.parentNode.nodeName != NodeName.BODY) { + referenceNode = this.closerParentNodeWithNameRelativeToNode('div', referenceNode.parentNode); + } + + return referenceNode; +}; + ZSSEditor.closerParentNode = function() { var parentNode = null; @@ -3246,7 +3265,7 @@ ZSSField.prototype.wrapCaretInParagraphIfNecessary = function() var range = selection.getRangeAt(0); if (range.startContainer == range.endContainer) { - var paragraph = document.createElement("p"); + var paragraph = document.createElement("div"); var textNode = document.createTextNode("​"); paragraph.appendChild(textNode); @@ -3285,7 +3304,11 @@ ZSSField.prototype.isEmpty = function() { }; ZSSField.prototype.getHTML = function() { - var html = wp.saveText(this.wrappedObject.html()); + var html = this.wrappedObject.html(); + if (ZSSEditor.defaultParagraphSeparator == 'div') { + html = html.replace(/(/igm, '

'); + } + html = wp.saveText(html); html = ZSSEditor.removeVisualFormatting( html ); return html; }; @@ -3312,6 +3335,12 @@ ZSSField.prototype.setHTML = function(html) { ZSSEditor.currentEditingImage = null; var mutatedHTML = wp.loadText(html); mutatedHTML = ZSSEditor.applyVisualFormatting(mutatedHTML); + + if (ZSSEditor.defaultParagraphSeparator == 'div') { + // Replace the paragraph tags we get from wpload with divs + mutatedHTML = mutatedHTML.replace(/(/igm, ''); + } + this.wrappedObject.html(mutatedHTML); }; diff --git a/libs/editor-common/assets/editor-android.css b/libs/editor-common/assets/editor-android.css index c1ae09e730ca..cd57c242452a 100644 --- a/libs/editor-common/assets/editor-android.css +++ b/libs/editor-common/assets/editor-android.css @@ -16,6 +16,15 @@ video::-webkit-media-controls-fullscreen-button { display: none; } +/* Duplicates paragraph tag formatting for div tags, which are needed on Android API 19+ due to autocorrect issues: +https://bugs.chromium.org/p/chromium/issues/detail?id=599890 +*/ +div:not(.field):not(#separatorDiv) { + line-height: 24px; + margin-top: 0px; + margin-bottom: 24px; +} + /* --- API<19 workarounds --- */ /* Used only on older APIs (API<19), which don't support CSS filter effects (specifically, blur). */ From 003b5739b257e7ace6e73f03cce650f52187847e Mon Sep 17 00:00:00 2001 From: Will Kwon Date: Thu, 7 Apr 2016 08:48:11 -0700 Subject: [PATCH 3/6] Updating to rc3 --- WordPressUtils/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressUtils/build.gradle b/WordPressUtils/build.gradle index 09dbb62b48c8..3b5b1335d1c2 100644 --- a/WordPressUtils/build.gradle +++ b/WordPressUtils/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-rc1' + classpath 'com.android.tools.build:gradle:2.0.0-rc3' } } From 084c5aaf8667dc64f2694c52ed0f2b30941df598 Mon Sep 17 00:00:00 2001 From: Will Kwon Date: Thu, 7 Apr 2016 13:20:10 -0700 Subject: [PATCH 4/6] Updating to gradle 2.0 --- WordPressUtils/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressUtils/build.gradle b/WordPressUtils/build.gradle index 3b5b1335d1c2..23c91ea51919 100644 --- a/WordPressUtils/build.gradle +++ b/WordPressUtils/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-rc3' + classpath 'com.android.tools.build:gradle:2.0.0' } } From d2f7ed4e05fa13e80fbe573f043fac43baf5e0b1 Mon Sep 17 00:00:00 2001 From: Maxime Biais Date: Fri, 8 Apr 2016 14:34:32 +0200 Subject: [PATCH 5/6] Squashed 'libs/editor/' changes from 0f6fba8..41a1010 41a1010 Merge pull request #337 from wordpress-mobile/issue/336-media-insert-paragraph-fix 767f36f Added comment for API<19 media insertion newline hack 1678bd1 Updating to gradle 2.0 c7a583d Fixed adding newline after media insertion for API<19 2e12295 Corrected some variable names in media insertion methods 200c8d5 Modified paragraph tag wrapping for media insertion to handle div tags correctly 94aa080 Updating to rc3 19cfc2e Merge commit 'c5b98a88c01551e8dbf44366072607d364ec01dd' into develop 64a065f Updating the rest of the modules to buildToolsVersion 23.0.3 f2c8351 Merge commit '4c9324cf1eee00b66c76e0d5a917c86e1293a845' into develop 3878f51 Add missing classes for images inserted from media library - also fix a bug with undefined alt text 3a4fe83 Add missing attributes for uploaded images 61f219e update to android-gradle-2.0.0-rc1 29e1264 Merge commit 'c6efe0a9190244d40e64300efc9cca56ae5acd5c' into develop a061001 Merge branch 'develop' into issue/120editor-initial-focus 0aa44f3 Merge commit '9b0b5fab24db9f435b278ad0b9e77d5895135700' into develop 0e8a35f Merge pull request #3897 from wordpress-mobile/issue/260editor-clear-failed-images-on-upload 85e19fb Null check getParentRangeOfFocusedNode/setRange in onMutationObserved - in case editor is not in focus 778bc7a Show the software keyboard once the DOM has loaded e0b6c72 Focus on the title field when opening posts 58c156b New EditorFragment method to removeAllFailedMediaUploads() 533abee New JS function ZSSEditor.removeAllFailedMediaUploads 29db13e Merge pull request #3896 from wordpress-mobile/issue/300editor-broken-images-after-upload-2 c53cb9d remove debug logs a29070e Use remoteurl in the link wrapper 9abfb9d Merge branch 'develop' into issue/300editor-broken-images-after-upload-2 964938e Merge branch 'develop' into issue/297editor-backspace-media f6acb17 update to com.android.tools.build:gradle:2.0.0-beta7 e71320e Merge branch 'develop' into issue/297editor-backspace-media 2ff37a5 Merge commit '8db246f15ce6f4d2c7f7f7ec51c68b87e9a66c2f' into develop 7c57f6f Changed MutationObserver handling to check if the WebView supports it, rather than rely on API levels 256916c Refactor: grouped mutation observation methods together c129a3b Refactored DOM element mutation listening, delegating everything to one trackNodeForMutation method 3df3bbd Changed MutationObserver behavior to track individual media nodes instead of each contenteditable div f096312 Moved failed media methods to the generic media method group b13515d Parse for failed media when returning from HTML to visual mode f193d88 Track DOMNodeRemoved events when parsing for failed media 44b4e92 Fixed a variable name error in ZSSEditor.removeImage b2342da On API<19, use DOMNodeRemoved events to track media deletions (instead of the unsupported MutationObserver used for newer APIs) e5e7693 Merge branch 'develop' into issue/297editor-backspace-media 06b252d Merge pull request #3804 from wordpress-mobile/issue/enable-editor-debug-mode 2daf87e Consume KEYCODE_VOLUME_UP event when debug print is called 8519f0d broken retries 8bd6476 Merge branch 'issue/enable-editor-debug-mode' into issue/300editor-broken-images-after-upload-2 06d70f9 use a remoteUrl attribute to avoid seeing broken image if download failed e5c9d8a remove debug action bar button and log raw html when volume up button is pressed 19f7043 fix function call errors dae02b2 Add back image swapping onError 515e508 fix wordpress-mobile/WordPress-Editor-Android#300: Retry download onError after an upload 05936de add missing comment 316f566 Updated gradle to 2.0.0-beta6 d6871e8 Fixes an issue where manually deleting uploading/failed media will cause the caret to disappear eb05458 Notify native through a callback whenever uploading/failed media are manually deleted 4fe64ad catch a common JS exception 3d45aff Merge branch 'develop' into issue/288editor-log-js-errors-in-crashlytics 8db4d8c fix wordpress-mobile/WordPress-Editor-Android#288: new EditorWebViewAbstract.ErrorListener used to forward JS errors to Crashlytics f2ba93d Keep the format bar disabled on rotation when the title field is in focus f2f6c0f Rely on ZSSEditor to flag uploads as completed in native-side checks 1fc3ed1 Added null checking for MediaType onMediaUploadFailed 223c7f0 Wait until the ZSSEditor has replaced the local media with remote before marking it as completed 4ff61f7 Strip any trailing   when returning the title git-subtree-dir: libs/editor git-subtree-split: 41a1010939cb0af0cc43672558265000623aefd2 --- WordPressEditor/build.gradle | 4 +-- example/build.gradle | 4 +-- .../editor-common/assets/ZSSRichTextEditor.js | 36 ++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/WordPressEditor/build.gradle b/WordPressEditor/build.gradle index 5bbf8d5b439b..2d47277c4fa6 100644 --- a/WordPressEditor/build.gradle +++ b/WordPressEditor/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-rc1' + classpath 'com.android.tools.build:gradle:2.0.0' } } @@ -20,7 +20,7 @@ android { publishNonDefault true compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionCode 8 diff --git a/example/build.gradle b/example/build.gradle index ff876849c1c0..6fdfd5842919 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-rc1' + classpath 'com.android.tools.build:gradle:2.0.0' } } @@ -15,7 +15,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { applicationId "org.wordpress.editorexample" diff --git a/libs/editor-common/assets/ZSSRichTextEditor.js b/libs/editor-common/assets/ZSSRichTextEditor.js index e4e136bc3b09..e05190dd8537 100755 --- a/libs/editor-common/assets/ZSSRichTextEditor.js +++ b/libs/editor-common/assets/ZSSRichTextEditor.js @@ -740,12 +740,12 @@ ZSSEditor.setBackgroundColor = function(color) { }; /** - * @brief Wraps given HTML in paragraph tags and appends a new line + * @brief Wraps given HTML in paragraph tags, appends a new line, and inserts it into the field * @details This method makes sure that passed HTML is wrapped in a separate paragraph. * It also appends a new opening paragraph tag and a space. This step is necessary to keep any spans or * divs in the HTML from being read by the WebView as a style and applied to all future paragraphs. */ -ZSSEditor.wrapInParagraphTags = function(html) { +ZSSEditor.insertHTMLWrappedInParagraphTags = function(html) { var space = '
'; var paragraphOpenTag = '<' + this.defaultParagraphSeparator + '>'; var paragraphCloseTag = ''; @@ -753,9 +753,18 @@ ZSSEditor.wrapInParagraphTags = function(html) { if (this.getFocusedField().getHTML().length == 0) { html = paragraphOpenTag + html; } - html = html + paragraphCloseTag + paragraphOpenTag + space; - return html; + // Without this line, API<19 WebView will reset the caret to the start of the document, inserting the new line + // there instead of under the newly added media item + if (nativeState.androidApiLevel < 19) { + html = html + '​'; + } + + // Due to the way the WebView handles divs, we need to add a new paragraph in a separate insertion - otherwise, + // the new paragraph will be nested within the existing paragraph. + this.insertHTML(html); + + this.insertHTML(paragraphOpenTag + space + paragraphCloseTag); }; // Needs addClass method @@ -1024,10 +1033,12 @@ ZSSEditor.updateImage = function(url, alt) { ZSSEditor.insertImage = function(url, remoteId, alt) { var html = '' + alt
+        html += '' - this.insertHTML(this.wrapInParagraphTags(html)); + html += '"/>'; + + this.insertHTMLWrappedInParagraphTags(html); + this.sendEnabledStyles(); }; @@ -1064,7 +1075,7 @@ ZSSEditor.insertLocalImage = function(imageNodeIdentifier, localImageUrl) { var image = ''; var html = imgContainerStart + progressElement + image + imgContainerEnd; - this.insertHTML(this.wrapInParagraphTags(html)); + this.insertHTMLWrappedInParagraphTags(html); ZSSEditor.trackNodeForMutation(this.getImageContainerNodeWithIdentifier(imageNodeIdentifier)); @@ -1352,7 +1363,8 @@ ZSSEditor.insertVideo = function(videoURL, posterURL, videopressID) { html += '>'; - this.insertHTML(this.wrapInParagraphTags(html)); + this.insertHTMLWrappedInParagraphTags(html); + this.sendEnabledStyles(); }; @@ -1394,7 +1406,7 @@ ZSSEditor.insertLocalVideo = function(videoNodeIdentifier, posterURL) { var image = ''; var html = videoContainerStart + progressElement + image + videoContainerEnd; - this.insertHTML(this.wrapInParagraphTags(html)); + this.insertHTMLWrappedInParagraphTags(html); ZSSEditor.trackNodeForMutation(this.getVideoContainerNodeWithIdentifier(videoNodeIdentifier)); @@ -2235,13 +2247,13 @@ ZSSEditor.insertGallery = function( imageIds, type, columns ) { shortcode = '[gallery columns="' + columns + '" ids="' + imageIds + '"]'; } - this.insertHTML(this.wrapInParagraphTags(shortcode)); + this.insertHTMLWrappedInParagraphTags(shortcode); } ZSSEditor.insertLocalGallery = function( placeholderId ) { var container = '[' + nativeState.localizedStringUploadingGallery + ']'; - this.insertHTML(this.wrapInParagraphTags(container)); + this.insertHTMLWrappedInParagraphTags(container); } ZSSEditor.replacePlaceholderGallery = function( placeholderId, imageIds, type, columns ) { From 52910dd0664cf2031a6a1c78f3cb1b170a4eeff9 Mon Sep 17 00:00:00 2001 From: Maxime Biais Date: Mon, 11 Apr 2016 09:11:28 +0200 Subject: [PATCH 6/6] Squashed 'libs/editor/' changes from 41a1010..7be0b57 7be0b57 Merge pull request #342 from wordpress-mobile/issue/340-wrap-new-post-links-in-paragraphs 6decc08 Wrap links in a new paragraph when the post is empty 04e0570 Merge pull request #338 from wordpress-mobile/issue/fix-travis-and-kill-jacoco 0372a90 Merge pull request #332 from wordpress-mobile/issue/307-expand-selection-to-word-when-creating-a-link 76883ef Update travis config to use build tools 23.0.3 e0b6a7d remove jacoco code coverage 5670205 homemade selection expansion 5749a16 Merge branch 'develop' into issue/307-expand-selection-to-word-when-creating-a-link 1b4a632 Expand selection to the word when creating a link and nothing is selected git-subtree-dir: libs/editor git-subtree-split: 7be0b577ed7c57301cda9fd99f1d1ca9f2a49dd3 --- .travis.yml | 2 +- README.md | 2 +- WordPressEditor/build.gradle | 36 +------------ .../android/editor/EditorFragment.java | 4 +- .../android/editor/JsCallbackReceiver.java | 2 +- .../editor-common/assets/ZSSRichTextEditor.js | 53 ++++++++++++++++++- 6 files changed, 57 insertions(+), 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index c065b2d6d317..b47d5ac194e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ android: - extra-android-support - platform-tools - tools - - build-tools-23.0.2 + - build-tools-23.0.3 - android-23 env: diff --git a/README.md b/README.md index a3781d021ea1..35c05f08a273 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Finally, update `[PROJECT_ROOT]\.git\info\exclude` to ignore the symlink locally This project has both unit testing and integration testing, maintained and run separately. -Unit testing is done with the [Robolectric framework](http://robolectric.org/). To run unit tests simply run `gradlew testDebug`. Code coverage reports can be generated via [JaCoCo.](http://www.eclemma.org/jacoco/) To generate them locally run `gradlew jacocoTestReport`. +Unit testing is done with the [Robolectric framework](http://robolectric.org/). To run unit tests simply run `gradlew testDebug`. Integration testing is done with the [Android testing framework](http://developer.android.com/tools/testing/testing_android.html). To run integration tests run `gradlew connectedAndroidTest`. diff --git a/WordPressEditor/build.gradle b/WordPressEditor/build.gradle index 2d47277c4fa6..7f0b2f4e2904 100644 --- a/WordPressEditor/build.gradle +++ b/WordPressEditor/build.gradle @@ -8,7 +8,6 @@ buildscript { } apply plugin: 'com.android.library' -apply plugin: 'jacoco' apply plugin: 'maven' apply plugin: 'signing' @@ -119,43 +118,10 @@ uploadArchives { } // -// Testing and code coverage +// Testing // android.testOptions.unitTests.all { include '**/*Test.class' exclude '**/ApplicationTest.class' } - -jacoco { - toolVersion = "0.7.1.201405082137" -} - -// Use these to define which classes to include and exclude from code coverage analysis -def coverageSourceDirs = [ 'src/main/java' ] -def coverageExclusions = [ '**/R.class', - '**/R$*.class', - '**/*$ViewInjector*.*', - '**/BuildConfig.*', - '**/Manifest*.*', - '**/Legacy**.class', - '**/legacy/**/*.class' ] - -task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") { - group = "Reporting" - description = "Generate Jacoco coverage reports" - - classDirectories = fileTree( - dir: 'build/intermediates/classes/debug', - excludes: coverageExclusions - ) - - additionalSourceDirs = files(coverageSourceDirs) - sourceDirectories = files(coverageSourceDirs) - executionData = files('build/jacoco/testDebug.exec') - - reports { - xml.enabled = true - html.enabled = true - } -} diff --git a/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java b/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java index d149212e7b1d..266e2ed4e066 100755 --- a/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java +++ b/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java @@ -529,7 +529,7 @@ public void onClick(View v) { } else { // Visual mode mGetSelectedTextCountDownLatch = new CountDownLatch(1); - mWebView.execJavaScriptFromString("ZSSEditor.execFunctionForResult('getSelectedText');"); + mWebView.execJavaScriptFromString("ZSSEditor.execFunctionForResult('getSelectedTextToLinkify');"); try { if (mGetSelectedTextCountDownLatch.await(1, TimeUnit.SECONDS)) { dialogBundle.putString(LinkDialogFragment.LINK_DIALOG_ARG_TEXT, mJavaScriptResult); @@ -1251,7 +1251,7 @@ public void onGetHtmlResponse(Map inputArgs) { } } break; - case "getSelectedText": + case "getSelectedTextToLinkify": mJavaScriptResult = inputArgs.get("result"); mGetSelectedTextCountDownLatch.countDown(); break; diff --git a/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java b/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java index f7ca4c0914de..f8e9989891fb 100755 --- a/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java +++ b/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java @@ -211,7 +211,7 @@ public void executeCallback(String callbackId, String params) { responseIds.add("id"); responseIds.add("contents"); break; - case "getSelectedText": + case "getSelectedTextToLinkify": responseIds.add("result"); break; case "getFailedMedia": diff --git a/libs/editor-common/assets/ZSSRichTextEditor.js b/libs/editor-common/assets/ZSSRichTextEditor.js index e05190dd8537..22da90e05350 100755 --- a/libs/editor-common/assets/ZSSRichTextEditor.js +++ b/libs/editor-common/assets/ZSSRichTextEditor.js @@ -400,10 +400,53 @@ ZSSEditor.resetSelectionOnField = function(fieldId) { ZSSEditor.getSelectedText = function() { var selection = window.getSelection(); - return selection.toString(); }; +ZSSEditor.canExpandBackward = function(range) { + var caretRange = range.cloneRange(); + if (range.startOffset == 0) { + return false; + } + caretRange.setStart(range.startContainer, range.startOffset - 1); + caretRange.setEnd(range.startContainer, range.startOffset); + if (!caretRange.toString().match(/\w/)) { + return false; + } + return true; +}; + +ZSSEditor.canExpandForward = function(range) { + var caretRange = range.cloneRange(); + if (range.endOffset == range.endContainer.length) { + return false; + } + caretRange.setStart(range.endContainer, range.endOffset); + caretRange.setEnd(range.endContainer, range.endOffset + 1); + if (!caretRange.toString().match(/\w/)) { + return false; + } + return true; +}; + +ZSSEditor.getSelectedTextToLinkify = function() { + var selection = window.getSelection(); + var element = ZSSEditor.getField("zss_field_content"); + // If there is no text selected, try to expand it to the word under the cursor + if (selection.rangeCount == 1) { + var range = selection.getRangeAt(0); + while (ZSSEditor.canExpandBackward(range)) { + range.setStart(range.startContainer, range.startOffset - 1); + } + while (ZSSEditor.canExpandForward(range)) { + range.setEnd(range.endContainer, range.endOffset + 1); + } + selection.removeAllRanges(); + selection.addRange(range); + } + return selection.toString(); +}; + ZSSEditor.getCaretArguments = function() { var caretInfo = this.getYCaretInfo(); @@ -770,7 +813,13 @@ ZSSEditor.insertHTMLWrappedInParagraphTags = function(html) { // Needs addClass method ZSSEditor.insertLink = function(url, title) { - this.insertHTML('' + title + ""); + var html = '' + title + ""; + + if (this.getFocusedField().getHTML().length == 0) { + html = '<' + this.defaultParagraphSeparator + '>' + html; + } + + this.insertHTML(html); }; ZSSEditor.updateLink = function(url, title) {