提交 70d780d3 编写于 作者: P Phil Hughes

Merge branch '38869-templates' into 'master'

Export template selector as ES6 modules

See merge request gitlab-org/gitlab-ce!15524
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
import 'vendor/jquery.waitforimages'; import 'vendor/jquery.waitforimages';
(function() { // Width where images must fits in, for 2-up this gets divided by 2
gl.ImageFile = (function() { const availWidth = 900;
var prepareFrames; const viewModes = ['two-up', 'swipe'];
// Width where images must fits in, for 2-up this gets divided by 2 export default class ImageFile {
ImageFile.availWidth = 900; constructor(file) {
this.file = file;
ImageFile.viewModes = ['two-up', 'swipe']; this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
return function(deletedWidth, deletedHeight) {
function ImageFile(file) { return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
this.file = file; _this.initViewModes();
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
return function(deletedWidth, deletedHeight) { // Load two-up view after images are loaded
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { // so that we can display the correct width and height information
_this.initViewModes(); const $images = $('.two-up.view img', _this.file);
// Load two-up view after images are loaded $images.waitForImages(function() {
// so that we can display the correct width and height information _this.initView('two-up');
const $images = $('.two-up.view img', _this.file); });
});
$images.waitForImages(function() { };
_this.initView('two-up'); })(this));
}); }
initViewModes() {
const viewMode = viewModes[0];
$('.view-modes', this.file).removeClass('hide');
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
return function(event) {
if (!$(event.currentTarget).hasClass('active')) {
return _this.activateViewMode(event.currentTarget.className);
}
};
})(this));
return this.activateViewMode(viewMode);
}
activateViewMode(viewMode) {
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
return function() {
$(".view." + viewMode, _this.file).fadeIn(200);
return _this.initView(viewMode);
};
})(this));
}
initView(viewMode) {
return this.views[viewMode].call(this);
}
// eslint-disable-next-line class-methods-use-this
initDraggable($el, padding, callback) {
var dragging = false;
var $body = $('body');
var $offsetEl = $el.parent();
$el.off('mousedown').on('mousedown', function() {
dragging = true;
$body.css('user-select', 'none');
});
$body.off('mouseup').off('mousemove').on('mouseup', function() {
dragging = false;
$body.css('user-select', '');
})
.on('mousemove', function(e) {
var left;
if (!dragging) return;
left = e.pageX - ($offsetEl.offset().left + padding);
callback(e, left);
});
}
prepareFrames(view) {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
$('.frame', view).each((function(_this) {
return function(index, frame) {
var height, width;
width = $(frame).width();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return maxHeight = height > maxHeight ? height : maxHeight;
};
})(this)).css({
width: maxWidth,
height: maxHeight
});
return [maxWidth, maxHeight];
}
views = {
'two-up': function() {
return $('.two-up.view .wrap', this.file).each((function(_this) {
return function(index, wrap) {
$('img', wrap).each(function() {
var currentWidth;
currentWidth = $(this).width();
if (currentWidth > availWidth / 2) {
return $(this).width(availWidth / 2);
}
});
return _this.requestImageInfo($('img', wrap), function(width, height) {
$('.image-info .meta-width', wrap).text(width + "px");
$('.image-info .meta-height', wrap).text(height + "px");
return $('.image-info', wrap).removeClass('hide');
}); });
}; };
})(this)); })(this));
} },
'swipe': function() {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
height: maxHeight + 28
});
$swipeWrap.css({
width: maxWidth + 1,
height: maxHeight + 2
});
// Set swipeBar left position to match image frame
$swipeBar.css({
left: 1
});
ImageFile.prototype.initViewModes = function() { wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
var viewMode;
viewMode = ImageFile.viewModes[0]; _this.initDraggable($swipeBar, wrapPadding, function(e, left) {
$('.view-modes', this.file).removeClass('hide'); if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) { $swipeWrap.width((maxWidth + 1) - left);
return function(event) { $swipeBar.css('left', left);
if (!$(event.currentTarget).hasClass('active')) { }
return _this.activateViewMode(event.currentTarget.className); });
}
};
})(this));
return this.activateViewMode(viewMode);
};
ImageFile.prototype.activateViewMode = function(viewMode) {
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
return function() {
$(".view." + viewMode, _this.file).fadeIn(200);
return _this.initView(viewMode);
}; };
})(this)); })(this));
}; },
'onion-skin': function() {
ImageFile.prototype.initView = function(viewMode) { var dragTrackWidth, maxHeight, maxWidth;
return this.views[viewMode].call(this); maxWidth = 0;
}; maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
ImageFile.prototype.initDraggable = function($el, padding, callback) { return $('.onion-skin.view', this.file).each((function(_this) {
var dragging = false; return function(index, view) {
var $body = $('body'); var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
var $offsetEl = $el.parent(); ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$frame = $('.onion-skin-frame', view);
$el.off('mousedown').on('mousedown', function() { $frameAdded = $('.frame.added', view);
dragging = true; $track = $('.drag-track', view);
$body.css('user-select', 'none'); $dragger = $('.dragger', $track);
});
$frame.css({
$body.off('mouseup').off('mousemove').on('mouseup', function() { width: maxWidth + 16,
dragging = false; height: maxHeight + 28
$body.css('user-select', ''); });
}) $('.swipe-wrap', view).css({
.on('mousemove', function(e) { width: maxWidth + 1,
var left; height: maxHeight + 2
if (!dragging) return; });
$dragger.css({
left: dragTrackWidth
});
left = e.pageX - ($offsetEl.offset().left + padding); framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
callback(e, left); _this.initDraggable($dragger, framePadding, function(e, left) {
}); var opacity = left / dragTrackWidth;
};
prepareFrames = function(view) { if (opacity >= 0 && opacity <= 1) {
var maxHeight, maxWidth; $dragger.css('left', left);
maxWidth = 0; $frameAdded.css('opacity', opacity);
maxHeight = 0; }
$('.frame', view).each((function(_this) { });
return function(index, frame) {
var height, width;
width = $(frame).width();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return maxHeight = height > maxHeight ? height : maxHeight;
}; };
})(this)).css({ })(this));
width: maxWidth, }
height: maxHeight }
});
return [maxWidth, maxHeight]; requestImageInfo(img, callback) {
}; const domImg = img.get(0);
if (domImg) {
ImageFile.prototype.views = { if (domImg.complete) {
'two-up': function() { return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
return $('.two-up.view .wrap', this.file).each((function(_this) { } else {
return function(index, wrap) { return img.on('load', (function(_this) {
$('img', wrap).each(function() { return function() {
var currentWidth; return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
currentWidth = $(this).width();
if (currentWidth > ImageFile.availWidth / 2) {
return $(this).width(ImageFile.availWidth / 2);
}
});
return _this.requestImageInfo($('img', wrap), function(width, height) {
$('.image-info .meta-width', wrap).text(width + "px");
$('.image-info .meta-height', wrap).text(height + "px");
return $('.image-info', wrap).removeClass('hide');
});
};
})(this));
},
'swipe': function() {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
height: maxHeight + 28
});
$swipeWrap.css({
width: maxWidth + 1,
height: maxHeight + 2
});
// Set swipeBar left position to match image frame
$swipeBar.css({
left: 1
});
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
$swipeWrap.width((maxWidth + 1) - left);
$swipeBar.css('left', left);
}
});
};
})(this));
},
'onion-skin': function() {
var dragTrackWidth, maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) {
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
$dragger = $('.dragger', $track);
$frame.css({
width: maxWidth + 16,
height: maxHeight + 28
});
$('.swipe-wrap', view).css({
width: maxWidth + 1,
height: maxHeight + 2
});
$dragger.css({
left: dragTrackWidth
});
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
_this.initDraggable($dragger, framePadding, function(e, left) {
var opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left);
$frameAdded.css('opacity', opacity);
}
});
}; };
})(this)); })(this));
} }
}; }
}
ImageFile.prototype.requestImageInfo = function(img, callback) { }
var domImg;
domImg = img.get(0);
if (domImg) {
if (domImg.complete) {
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
} else {
return img.on('load', (function(_this) {
return function() {
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
};
})(this));
}
}
};
return ImageFile;
})();
}).call(window);
...@@ -30,7 +30,7 @@ import projectImport from './project_import'; ...@@ -30,7 +30,7 @@ import projectImport from './project_import';
import Labels from './labels'; import Labels from './labels';
import LabelManager from './label_manager'; import LabelManager from './label_manager';
/* global Sidebar */ /* global Sidebar */
import IssuableTemplateSelectors from './templates/issuable_template_selectors';
import Flash from './flash'; import Flash from './flash';
import CommitsList from './commits'; import CommitsList from './commits';
import Issue from './issue'; import Issue from './issue';
...@@ -264,7 +264,7 @@ import ProjectVariables from './project_variables'; ...@@ -264,7 +264,7 @@ import ProjectVariables from './project_variables';
new IssuableForm($('.issue-form')); new IssuableForm($('.issue-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
new gl.IssuableTemplateSelectors(); new IssuableTemplateSelectors();
break; break;
case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:new':
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
...@@ -288,7 +288,7 @@ import ProjectVariables from './project_variables'; ...@@ -288,7 +288,7 @@ import ProjectVariables from './project_variables';
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
new gl.IssuableTemplateSelectors(); new IssuableTemplateSelectors();
new AutoWidthDropdownSelect($('.js-target-branch-select')).init(); new AutoWidthDropdownSelect($('.js-target-branch-select')).init();
break; break;
case 'projects:tags:new': case 'projects:tags:new':
......
import ImageBadge from '../image_badge'; import ImageBadge from '../image_badge';
import ImageDiff from '../image_diff'; import ImageDiff from '../image_diff';
import ReplacedImageDiff from '../replaced_image_diff'; import ReplacedImageDiff from '../replaced_image_diff';
import '../../commit/image_file'; import ImageFile from '../../commit/image_file';
export function resizeCoordinatesToImageElement(imageEl, meta) { export function resizeCoordinatesToImageElement(imageEl, meta) {
const { x, y, width, height } = meta; const { x, y, width, height } = meta;
...@@ -81,7 +81,7 @@ export function initImageDiff(fileEl, canCreateNote, renderCommentBadge) { ...@@ -81,7 +81,7 @@ export function initImageDiff(fileEl, canCreateNote, renderCommentBadge) {
// ImageFile needs to be invoked before initImageDiff so that badges // ImageFile needs to be invoked before initImageDiff so that badges
// can mount to the correct location // can mount to the correct location
new gl.ImageFile(fileEl); // eslint-disable-line no-new new ImageFile(fileEl); // eslint-disable-line no-new
if (fileEl.querySelector('.diff-file .js-single-image')) { if (fileEl.querySelector('.diff-file .js-single-image')) {
diff = new ImageDiff(fileEl, options); diff = new ImageDiff(fileEl, options);
......
<script> <script>
import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors';
export default { export default {
props: { props: {
formState: { formState: {
...@@ -32,7 +34,7 @@ ...@@ -32,7 +34,7 @@
}; };
editor.getValue = () => this.formState.description; editor.getValue = () => this.formState.description;
this.issuableTemplate = new gl.IssuableTemplateSelectors({ this.issuableTemplate = new IssuableTemplateSelectors({
$dropdowns: $(this.$refs.toggle), $dropdowns: $(this.$refs.toggle),
editor, editor,
}); });
......
/* eslint-disable comma-dangle, max-len, no-useless-return, no-param-reassign, max-len */ /* eslint-disable no-useless-return, max-len */
import Api from '../api';
import Api from '../api';
import TemplateSelector from '../blob/template_selector'; import TemplateSelector from '../blob/template_selector';
((global) => { export default class IssuableTemplateSelector extends TemplateSelector {
class IssuableTemplateSelector extends TemplateSelector { constructor(...args) {
constructor(...args) { super(...args);
super(...args); this.projectPath = this.dropdown.data('project-path');
this.projectPath = this.dropdown.data('project-path'); this.namespacePath = this.dropdown.data('namespace-path');
this.namespacePath = this.dropdown.data('namespace-path'); this.issuableType = this.$dropdownContainer.data('issuable-type');
this.issuableType = this.$dropdownContainer.data('issuable-type'); this.titleInput = $(`#${this.issuableType}_title`);
this.titleInput = $(`#${this.issuableType}_title`);
const initialQuery = {
const initialQuery = { name: this.dropdown.data('selected'),
name: this.dropdown.data('selected') };
};
if (initialQuery.name) this.requestFile(initialQuery);
if (initialQuery.name) this.requestFile(initialQuery);
$('.reset-template', this.dropdown.parent()).on('click', () => {
$('.reset-template', this.dropdown.parent()).on('click', () => { this.setInputValueToTemplateContent();
this.setInputValueToTemplateContent(); });
});
$('.no-template', this.dropdown.parent()).on('click', () => {
$('.no-template', this.dropdown.parent()).on('click', () => { this.currentTemplate.content = '';
this.currentTemplate.content = ''; this.setInputValueToTemplateContent();
this.setInputValueToTemplateContent(); $('.dropdown-toggle-text', this.dropdown).text('Choose a template');
$('.dropdown-toggle-text', this.dropdown).text('Choose a template'); });
}); }
}
requestFile(query) { requestFile(query) {
this.startLoadingSpinner(); this.startLoadingSpinner();
Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => { Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => {
this.currentTemplate = currentTemplate; this.currentTemplate = currentTemplate;
if (err) return; // Error handled by global AJAX error handler if (err) return; // Error handled by global AJAX error handler
this.stopLoadingSpinner(); this.stopLoadingSpinner();
this.setInputValueToTemplateContent(); this.setInputValueToTemplateContent();
}); });
return; return;
} }
setInputValueToTemplateContent() { setInputValueToTemplateContent() {
// `this.setEditorContent` sets the value of the description input field // `this.setEditorContent` sets the value of the description input field
// to the content of the template selected. // to the content of the template selected.
if (this.titleInput.val() === '') { if (this.titleInput.val() === '') {
// If the title has not yet been set, focus the title input and // If the title has not yet been set, focus the title input and
// skip focusing the description input by setting `true` as the // skip focusing the description input by setting `true` as the
// `skipFocus` option to `setEditorContent`. // `skipFocus` option to `setEditorContent`.
this.setEditorContent(this.currentTemplate, { skipFocus: true }); this.setEditorContent(this.currentTemplate, { skipFocus: true });
this.titleInput.focus(); this.titleInput.focus();
} else { } else {
this.setEditorContent(this.currentTemplate, { skipFocus: false }); this.setEditorContent(this.currentTemplate, { skipFocus: false });
}
return;
} }
return;
} }
}
global.IssuableTemplateSelector = IssuableTemplateSelector;
})(window.gl || (window.gl = {}));
/* eslint-disable no-new, comma-dangle, class-methods-use-this, no-param-reassign */ /* eslint-disable no-new, class-methods-use-this */
import IssuableTemplateSelector from './issuable_template_selector';
((global) => { export default class IssuableTemplateSelectors {
class IssuableTemplateSelectors { constructor({ $dropdowns, editor } = {}) {
constructor({ $dropdowns, editor } = {}) { this.$dropdowns = $dropdowns || $('.js-issuable-selector');
this.$dropdowns = $dropdowns || $('.js-issuable-selector'); this.editor = editor || this.initEditor();
this.editor = editor || this.initEditor();
this.$dropdowns.each((i, dropdown) => { this.$dropdowns.each((i, dropdown) => {
const $dropdown = $(dropdown); const $dropdown = $(dropdown);
new gl.IssuableTemplateSelector({ new IssuableTemplateSelector({
pattern: /(\.md)/, pattern: /(\.md)/,
data: $dropdown.data('data'), data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-issuable-selector-wrap'), wrapper: $dropdown.closest('.js-issuable-selector-wrap'),
dropdown: $dropdown, dropdown: $dropdown,
editor: this.editor editor: this.editor,
});
}); });
} });
initEditor() {
const editor = $('.markdown-area');
// Proxy ace-editor's .setValue to jQuery's .val
editor.setValue = editor.val;
editor.getValue = editor.val;
return editor;
}
} }
global.IssuableTemplateSelectors = IssuableTemplateSelectors; initEditor() {
})(window.gl || (window.gl = {})); const editor = $('.markdown-area');
// Proxy ace-editor's .setValue to jQuery's .val
editor.setValue = editor.val;
editor.getValue = editor.val;
return editor;
}
}
---
title: Remove template selector from global namespace
merge_request:
author:
type: performance
...@@ -157,27 +157,19 @@ describe('utilsHelper', () => { ...@@ -157,27 +157,19 @@ describe('utilsHelper', () => {
beforeEach(() => { beforeEach(() => {
window.gl = window.gl || (window.gl = {}); window.gl = window.gl || (window.gl = {});
glCache = window.gl; glCache = window.gl;
window.gl.ImageFile = () => {};
fileEl = document.createElement('div'); fileEl = document.createElement('div');
fileEl.innerHTML = ` fileEl.innerHTML = `
<div class="diff-file"></div> <div class="diff-file"></div>
`; `;
spyOn(ImageDiff.prototype, 'init').and.callFake(() => {});
spyOn(ReplacedImageDiff.prototype, 'init').and.callFake(() => {}); spyOn(ReplacedImageDiff.prototype, 'init').and.callFake(() => {});
spyOn(ImageDiff.prototype, 'init').and.callFake(() => {});
}); });
afterEach(() => { afterEach(() => {
window.gl = glCache; window.gl = glCache;
}); });
it('should initialize gl.ImageFile', () => {
spyOn(window.gl, 'ImageFile');
utilsHelper.initImageDiff(fileEl, false, false);
expect(gl.ImageFile).toHaveBeenCalled();
});
it('should initialize ImageDiff if js-single-image', () => { it('should initialize ImageDiff if js-single-image', () => {
const diffFileEl = fileEl.querySelector('.diff-file'); const diffFileEl = fileEl.querySelector('.diff-file');
diffFileEl.innerHTML = ` diffFileEl.innerHTML = `
......
...@@ -34,7 +34,6 @@ describe('Inline edit form component', () => { ...@@ -34,7 +34,6 @@ describe('Inline edit form component', () => {
}); });
it('renders template selector when templates exists', (done) => { it('renders template selector when templates exists', (done) => {
spyOn(gl, 'IssuableTemplateSelectors');
vm.issuableTemplates = ['test']; vm.issuableTemplates = ['test'];
Vue.nextTick(() => { Vue.nextTick(() => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册