Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 97 additions & 62 deletions src/extensions/default/QuickView/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ define(function (require, exports, module) {
FileUtils = brackets.getModule("file/FileUtils"),
Menus = brackets.getModule("command/Menus"),
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
LanguageManager = brackets.getModule("language/LanguageManager"),
Strings = brackets.getModule("strings"),
ViewUtils = brackets.getModule("utils/ViewUtils"),
TokenUtils = brackets.getModule("utils/TokenUtils");
Expand All @@ -48,7 +49,8 @@ define(function (require, exports, module) {
$previewContainer, // Preview container
$previewContent, // Preview content holder
lastMousePos, // Last mouse position
animationRequest; // Request for animation frame
animationRequest, // Request for animation frame
extensionlessImagePreview; // Whether to try and preview extensionless URLs

// Constants
var CMD_ENABLE_QUICK_VIEW = "view.enableQuickView",
Expand All @@ -60,6 +62,9 @@ define(function (require, exports, module) {

prefs = PreferencesManager.getExtensionPrefs("quickview");
prefs.definePreference("enabled", "boolean", true);
// Whether or not to try and show image previews for URLs missing extensions
// (e.g., https://avatars2.githubusercontent.com/u/476009?v=3&s=200)
prefs.definePreference("extensionlessImagePreview", "boolean", true);

/**
* There are three states for this var:
Expand Down Expand Up @@ -450,67 +455,83 @@ define(function (require, exports, module) {
}
}

if (tokenString) {
// Strip leading/trailing quotes, if present
tokenString = tokenString.replace(/(^['"])|(['"]$)/g, "");

if (/^(data\:image)|(\.gif|\.png|\.jpg|\.jpeg|\.webp|\.svg)$/i.test(tokenString)) {
var sPos, ePos;
var docPath = editor.document.file.fullPath;
var imgPath;

if (PathUtils.isAbsoluteUrl(tokenString)) {
imgPath = tokenString;
} else {
imgPath = "file:///" + FileUtils.getDirectoryPath(docPath) + tokenString;
}

if (urlMatch) {
sPos = {line: pos.line, ch: urlMatch.index};
ePos = {line: pos.line, ch: urlMatch.index + urlMatch[0].length};
} else {
sPos = {line: pos.line, ch: token.start};
ePos = {line: pos.line, ch: token.end};
}

if (imgPath) {
var imgPreview = "<div class='image-preview'>" +
" <img src=\"" + imgPath + "\">" +
"</div>";
var coord = cm.charCoords(sPos);
var xpos = (cm.charCoords(ePos).left - coord.left) / 2 + coord.left;

var showHandler = function () {
// Hide the preview container until the image is loaded.
$previewContainer.hide();


$previewContainer.find(".image-preview > img").on("load", function () {
$previewContent
.append("<div class='img-size'>" +
this.naturalWidth + " &times; " + this.naturalHeight + " " + Strings.UNIT_PIXELS +
"</div>"
);
$previewContainer.show();
positionPreview(editor, popoverState.xpos, popoverState.ytop, popoverState.ybot);
});
};

return {
start: sPos,
end: ePos,
content: imgPreview,
onShow: showHandler,
xpos: xpos,
ytop: coord.top,
ybot: coord.bottom,
_imgPath: imgPath
};
}
}
if (!tokenString) {
return null;
}

return null;

// Strip leading/trailing quotes, if present
tokenString = tokenString.replace(/(^['"])|(['"]$)/g, "");

var sPos, ePos;
var docPath = editor.document.file.fullPath;
var imgPath;

// Determine whether or not this URL/path is likely to be an image.
var parsed = PathUtils.parseUrl(tokenString);
var hasProtocol = parsed.protocol !== "";
var ext = parsed.filenameExtension.replace(/^\./, '');
var language = LanguageManager.getLanguageForExtension(ext);
var id = language && language.getId();
var isImage = id === "image" || id === "svg";

// Use this URL if this is an absolute URL and either points to a
// filename with a known image extension, or lacks an extension (e.g.,
// a web service that returns an image). Honour the extensionlessImagePreview
// preference as well in the latter case.
if (hasProtocol && (isImage || (!ext && extensionlessImagePreview))) {
imgPath = tokenString;
}
// Use this filename if this is a path with a known image extension.
else if (!hasProtocol && isImage) {
imgPath = "file:///" + FileUtils.getDirectoryPath(docPath) + tokenString;
}

if (!imgPath) {
return null;
}

if (urlMatch) {
sPos = {line: pos.line, ch: urlMatch.index};
ePos = {line: pos.line, ch: urlMatch.index + urlMatch[0].length};
} else {
sPos = {line: pos.line, ch: token.start};
ePos = {line: pos.line, ch: token.end};
}

var imgPreview = "<div class='image-preview'>" +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you put in the imgPath check here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already been dealt with in the if-block above from lines 475-483, returning early if we have no value for imgPath, and therefore not needing to do the check and indent all this code.

" <img src=\"" + imgPath + "\">" +
"</div>";
var coord = cm.charCoords(sPos);
var xpos = (cm.charCoords(ePos).left - coord.left) / 2 + coord.left;

var showHandler = function () {
// Hide the preview container until the image is loaded.
$previewContainer.hide();

$previewContainer.find(".image-preview > img").on("load", function () {
$previewContent
.append("<div class='img-size'>" +
this.naturalWidth + " &times; " + this.naturalHeight + " " + Strings.UNIT_PIXELS +
"</div>"
);
$previewContainer.show();
positionPreview(editor, popoverState.xpos, popoverState.ytop, popoverState.ybot);
}).on("error", function (e) {
e.preventDefault();
hidePreview();
});
};

return {
start: sPos,
end: ePos,
content: imgPreview,
onShow: showHandler,
xpos: xpos,
ytop: coord.top,
ybot: coord.bottom,
_imgPath: imgPath
};
}


Expand All @@ -522,7 +543,6 @@ define(function (require, exports, module) {
* Lacks only hoverTimer (supplied by handleMouseMove()) and marker (supplied by showPreview()).
*/
function queryPreviewProviders(editor, pos, token) {

var line = editor.document.getLine(pos.line);

// FUTURE: Support plugin providers. For now we just hard-code...
Expand Down Expand Up @@ -719,6 +739,16 @@ define(function (require, exports, module) {
CommandManager.get(CMD_ENABLE_QUICK_VIEW).setChecked(enabled);
}

function setExtensionlessImagePreview(_extensionlessImagePreview, doNotSave) {
if(extensionlessImagePreview !== _extensionlessImagePreview) {
extensionlessImagePreview = _extensionlessImagePreview;
if (!doNotSave) {
prefs.set("extensionlessImagePreview", enabled);
prefs.save();
}
}
}

function setEnabled(_enabled, doNotSave) {
if (enabled !== _enabled) {
enabled = _enabled;
Expand Down Expand Up @@ -786,10 +816,15 @@ define(function (require, exports, module) {

// Setup initial UI state
setEnabled(prefs.get("enabled"), true);
setExtensionlessImagePreview(prefs.get("extensionlessImagePreview"), true);

prefs.on("change", "enabled", function () {
setEnabled(prefs.get("enabled"), true);
});

prefs.on("change", "extensionlessImagePreview", function () {
setExtensionlessImagePreview(prefs.get("extensionlessImagePreview"));
});

// For unit testing
exports._queryPreviewProviders = queryPreviewProviders;
Expand Down
31 changes: 31 additions & 0 deletions src/extensions/default/QuickView/unittest-files/test.css
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,34 @@ background: -ms-linear-gradient(top, #d2dfed 0%,#c8d7eb 26%,#bed0ea 51%,#a6c0e3
background-image: linear-gradient(to bottom, #333, #CCC;
background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(51,51,51)), to(rgb(204,204,204));
}

.good-preview-image-urls {
background: "http://example.com/image.gif";
background: "http://example.com/image.png";
background: "http://example.com/image.jpe";
background: "http://example.com/image.jpeg";
background: "http://example.com/image.jpg";
background: "http://example.com/image.ico";
background: "http://example.com/image.bmp";
background: "http://example.com/image.svg";

background: "https://image.service.com/id/1234513";
background: "http://image.service.com/id/1234513";
background: "https://image.service.com/id/1234513?w=300&h=400";
}

.ignored-preview-image-urls {
background: "https://website.com/index.html";
background: "https://website.com/style.css";
background: "https://website.com/script.js";
background: "https://website.com/package.json";
background: "https://website.com/readme.md";
background: "https://website.com/data.xml";
background: "https://website.com/music.mp3";
background: "https://website.com/video.ogv";
background: "https://website.com/video.mp4";
background: "https://website.com/video.mpeg";
background: "https://website.com/video.webm";
background: "https://website.com/archive.zip";
background: "https://website.com/archive.tgz";
}
57 changes: 55 additions & 2 deletions src/extensions/default/QuickView/unittests.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
define(function (require, exports, module) {
"use strict";

var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
FileUtils = brackets.getModule("file/FileUtils");
var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
FileUtils = brackets.getModule("file/FileUtils"),
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
prefs = PreferencesManager.getExtensionPrefs("quickview");

describe("Quick View", function () {
var testFolder = FileUtils.getNativeModuleDirectoryPath(module) + "/unittest-files/";
Expand Down Expand Up @@ -472,6 +474,57 @@ define(function (require, exports, module) {
checkImagePathAtPos("img/don't.png", 184, 26); // url("") containing '
checkImageDataAtPos("data:image/svg+xml;utf8, <svg version='1.1' xmlns='http://www.w3.org/2000/svg'></svg>", 185, 26); // data url("") containing '
});

it("Should show image preview for URLs with known image extensions", function() {
checkImageDataAtPos("http://example.com/image.gif", 194, 20);
checkImageDataAtPos("http://example.com/image.png", 195, 20);
checkImageDataAtPos("http://example.com/image.jpe", 196, 20);
checkImageDataAtPos("http://example.com/image.jpeg", 197, 20);
checkImageDataAtPos("http://example.com/image.jpg", 198, 20);
checkImageDataAtPos("http://example.com/image.ico", 199, 20);
checkImageDataAtPos("http://example.com/image.bmp", 200, 20);
checkImageDataAtPos("http://example.com/image.svg", 201, 20);
});

it("Should show image preview for extensionless URLs (with protocol) with pref set", function() {
// Flip the pref on and restore when done
var original = prefs.get("extensionlessImagePreview");
prefs.set("extensionlessImagePreview", true);

checkImageDataAtPos("https://image.service.com/id/1234513", 203, 20); // https
checkImageDataAtPos("http://image.service.com/id/1234513", 204, 20); // http
checkImageDataAtPos("https://image.service.com/id/1234513?w=300&h=400", 205, 20); // qs params

prefs.set("extensionlessImagePreview", original);
});

it("Should not show image preview for extensionless URLs (with protocol) without pref set", function() {
// Flip the pref off and restore when done
var original = prefs.get("extensionlessImagePreview");
prefs.set("extensionlessImagePreview", false);

checkImageDataAtPos("https://image.service.com/id/1234513", 203, 20); // https
checkImageDataAtPos("http://image.service.com/id/1234513", 204, 20); // http
checkImageDataAtPos("https://image.service.com/id/1234513?w=300&h=400", 205, 20); // qs params

prefs.set("extensionlessImagePreview", original);
});

it("Should ignore URLs for common non-image extensions", function() {
expectNoPreviewAtPos(209, 20); // .html
expectNoPreviewAtPos(210, 20); // .css
expectNoPreviewAtPos(211, 20); // .js
expectNoPreviewAtPos(212, 20); // .json
expectNoPreviewAtPos(213, 20); // .md
expectNoPreviewAtPos(214, 20); // .xml
expectNoPreviewAtPos(215, 20); // .mp3
expectNoPreviewAtPos(216, 20); // .ogv
expectNoPreviewAtPos(217, 20); // .mp4
expectNoPreviewAtPos(218, 20); // .mpeg
expectNoPreviewAtPos(219, 20); // .webm
expectNoPreviewAtPos(220, 20); // .zip
expectNoPreviewAtPos(221, 20); // .tgz
});

it("Should show image preview for a data URI inside url()", function () {
runs(function () {
Expand Down