Files
Pinry/pinry/static/js/vue/main.js

523 lines
13 KiB
JavaScript
Raw Normal View History

var events = new Vue({});
var eventsName = {
pinReflowDone: "single-pin-reflow-done",
allPinReflowDone: "all-pin-reflow-done",
pinView: {
open: "view-single-pin",
close: "close-pin-view",
},
2019-11-22 14:37:06 +08:00
pinForm: {
close: "pin_form.close",
open: "pin_form.open",
}
};
function fetchPins(offset) {
2019-11-22 14:37:06 +08:00
var apiUrl = API_BASE + 'pins/?format=json&ordering=-id&limit=50&offset=' + String(offset);
if (tagFilter) apiUrl = apiUrl + '&tags__name=' + tagFilter;
if (userFilter) apiUrl = apiUrl + '&submitter__username=' + userFilter;
return axios.get(apiUrl)
}
var utils = {
getDocumentHeight: function () {
var body = document.body,
2019-11-22 14:37:06 +08:00
html = document.documentElement;
return Math.max(
body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight,
html.offsetHeight,
);
},
getWindowHeight: function () {
return window.innerHeight
}
};
function EventCounter(countEvent, triggerEvent, triggerTimes) {
var self = {
id: new Date().getTime(),
count: 0,
targetCount: triggerTimes,
triggerEvent: countEvent,
countEvent: countEvent,
};
events.$on(
countEvent,
2019-11-22 14:37:06 +08:00
function () {
self.count += 1;
if (self.count >= self.targetCount) {
events.$emit(triggerEvent)
}
}
);
self.resetAfterReflow = function (targetCount) {
self.count = 0;
self.targetCount = targetCount;
};
self.reset = function (targetCount) {
self.targetCount = targetCount;
};
return self;
}
function HeightTable(blockMargin) {
var self = {
data: {}
};
2019-11-22 14:37:06 +08:00
function get(obj, index) {
if (!obj.data.hasOwnProperty(index)) {
return null
}
return obj.data[index];
}
function set(obj, index, value) {
if (!obj.data.hasOwnProperty(index)) {
obj.data[index] = value
}
return obj.data[index];
}
function getHeightOffset(obj, indexOfElement, rowSize) {
var height = 0;
for (var index = rowSize - 1; index < indexOfElement; index += rowSize) {
var value = obj.get(index);
if (value === null) {
console.log("Error occurs while loading elements's height offset");
return null
}
height += (value + blockMargin);
}
return height
}
2019-11-22 14:37:06 +08:00
self.get = function (index) {
return get(self, index);
};
self.set = function (index, value) {
return set(self, index, value)
};
self.getHeightOffset = function (index, rowSize) {
return getHeightOffset(self, index, rowSize);
};
2019-11-22 14:37:06 +08:00
self.getMaxHeight = function (rowSize, blockMargin) {
var size = Object.keys(self.data).length;
var heightArray = [];
2019-11-22 14:37:06 +08:00
for (var column = 0; column < rowSize; column++) {
heightArray.push(0);
}
for (var index = 0; index < size; index++) {
var column = index % rowSize;
heightArray[column] = heightArray[column] + self.get(index) + blockMargin;
}
return Math.max(...heightArray);
};
return self;
}
Vue.component(
'pin-form',
{
data: function () {
return {};
},
2019-11-22 14:37:06 +08:00
props: ['url', 'show'],
template: "#pin-form-template",
2019-11-22 14:37:06 +08:00
mounted: function () {
this.initilize();
events.$on(
eventsName.pinForm.open,
this.showModal,
)
},
methods: {
getModal: function () {
return $('#pin-form');
},
initilize: function () {
$('#pin-form-image-upload').dropzone(
{
url: API_BASE + "images/",
paramName: 'image',
parallelUploads: 1,
uploadMultiple: false,
maxFiles: 1,
acceptedFiles: 'image/*',
headers: {
'X-CSRFToken': getCSRFToken(),
},
success: function (file, resp) {
var image_url = $('#pin-form-image-url');
image_url.parent().fadeOut(300);
uploadedImage = resp.id;
image_url.val(resp.thumbnail.image);
// createPinPreviewFromForm();
},
error: function (error) {
message('Problem uploading image.', 'alert alert-error');
},
},
);
},
showModal: function () {
var modal = this.getModal();
modal.show();
},
hideModal: function () {
var modal = this.getModal();
modal.hide();
},
}
}
);
Vue.component(
'light-box',
{
data: function () {
return {
backgroundStyle: null,
lightBoxWrapperStyle: null,
lightBoxImageWrapperStyle: null,
}
},
props: ['pin'],
template: "#lightbox-template",
mounted: function () {
var documentHeight = utils.getDocumentHeight();
var imageWidth = this.pin.image.standard.width;
var imageHeight = this.pin.image.standard.height;
var windowHeight = utils.getWindowHeight();
var backgroundHeight = documentHeight;
var lightBoxWrapperStyle = {
'width': imageWidth + "px",
'marginTop': '80px',
'marginBottom': '80px',
2019-11-22 14:37:06 +08:00
'margin-left': -imageWidth / 2 + "px",
};
var wrapper = this.$el.querySelector(".lightbox-wrapper");
if (wrapper.getBoundingClientRect().height + 140 > windowHeight) {
var wrapperHeight = wrapper.getBoundingClientRect().height;
backgroundHeight = wrapperHeight + 160;
}
this.backgroundStyle = {
height: backgroundHeight + 'px',
};
this.lightBoxImageWrapperStyle = {
height: imageHeight + 'px',
};
this.lightBoxWrapperStyle = lightBoxWrapperStyle;
},
methods: {
2019-11-22 14:37:06 +08:00
onCloseView: function () {
events.$emit(eventsName.pinView.close);
}
}
}
);
Vue.component('pin', {
data: function () {
return {
'loaded': false,
'editable': true,
'active': false,
2019-02-28 18:22:08 +08:00
'imageStyle': null,
'pinStyle': null,
'height': null,
'heightOffset': null,
}
},
props: ['pin', 'args', 'index', 'heightTable'],
template: '#pin-template',
2019-11-22 14:37:06 +08:00
created: function () {
var self = this;
events.$on("reflow", function () {
self.reflow();
});
},
2019-11-22 14:37:06 +08:00
mounted: function () {
this.reflow();
this.$emit("rendered", {index: this.index, height: this.height});
2019-02-28 18:22:08 +08:00
},
2019-11-22 14:37:06 +08:00
updated: function () {
events.$emit(eventsName.pinReflowDone);
},
methods: {
2019-11-22 14:37:06 +08:00
showImageDetail: function (event) {
events.$emit(eventsName.pinView.open, this.pin);
if (event) event.preventDefault();
},
2019-11-22 14:37:06 +08:00
reflow: function () {
this.heightOffset = this.heightTable.getHeightOffset(this.index, this.args.rowSize);
this.imageStyle = this.getImageStyle();
this.pinStyle = this.getPinStyle();
this.height = this.getTextHeight() + this.pin.image.thumbnail.height;
},
2019-11-22 14:37:06 +08:00
getImageStyle: function () {
return {
width: this.pin.image.thumbnail.width + 'px',
height: this.pin.image.thumbnail.height + 'px',
}
},
2019-11-22 14:37:06 +08:00
getPinStyle: function () {
var self = this;
var marginTop = self.heightOffset;
var marginLeft = 0;
function isFirstOne(rowSize, index) {
index = index + 1;
2019-11-22 14:37:06 +08:00
if ((index % rowSize) === 1) {
return true;
}
}
function getRowNumber(rowSize, index) {
index = index + 1;
var rowNumber = Math.floor(index % rowSize);
if (rowNumber === 0) {
return rowSize;
}
return rowNumber;
}
if (isFirstOne(self.args.rowSize, self.index)) {
marginLeft = self.args.rowStartMargin;
} else {
var marginPerBlock = self.args.blockWidth + self.args.blockMargin;
var rowNumber = getRowNumber(self.args.rowSize, self.index);
marginLeft = self.args.rowStartMargin + marginPerBlock * (rowNumber - 1);
}
return {
'margin-left': marginLeft + 'px',
'margin-top': marginTop + 'px',
};
},
onImageLoad: function () {
this.loaded = true;
},
getAvatar: function () {
return "//gravatar.com/avatar/" + this.pin.submitter.gravatar;
},
getUserLink: function () {
return "/pins/users/" + this.pin.submitter.username + "/"
},
getTagLink: function (tag) {
return "/pins/tags/" + tag + "/"
},
2019-11-22 14:37:06 +08:00
getTextHeight: function () {
var element = this.$el.querySelector(".pin-description");
return element.getBoundingClientRect().height;
},
}
});
Vue.component('pin-container', {
data: function () {
return {
args: {
"containerWidth": 0,
"blockWidth": 240,
"blockMargin": 15,
"rowSize": 0,
"rowStartMargin": 0,
"rowEndMargin": 0,
},
"pins": [],
"heightTable": [],
"counter": null,
"containerStyle": null,
2019-03-07 19:28:23 +08:00
status: {
loading: true,
hasNext: true,
offset: 0,
},
};
},
template: "#pin-container-template",
2019-11-22 14:37:06 +08:00
created: function () {
this.heightTable = HeightTable(this.args.blockMargin);
2019-03-07 19:28:23 +08:00
this.loadMore();
var self = this;
2019-11-22 14:37:06 +08:00
window.addEventListener("optimizedResize", function () {
2019-03-01 17:21:59 +08:00
self.reflow();
});
2019-03-07 19:28:23 +08:00
self.bindScrollHandler();
self.counter = EventCounter(
eventsName.pinReflowDone,
eventsName.allPinReflowDone,
self.pins.length,
);
events.$on(eventsName.allPinReflowDone, this.updateContainerStyle);
},
2019-11-22 14:37:06 +08:00
mounted: function () {
this.reflow();
},
methods: {
2019-11-22 14:37:06 +08:00
updateContainerStyle: function () {
var height = this.heightTable.getMaxHeight(
this.args.rowSize,
this.args.blockMargin,
);
this.containerStyle = {
height: height + "px",
};
},
2019-03-07 19:28:23 +08:00
loadMore() {
var self = this;
self.markAsLoading();
fetchPins(self.status.offset).then(
function (res) {
var newPins = self.pins.concat(res.data.results);
self.counter.reset(newPins.length);
self.pins = newPins;
2019-03-07 19:28:23 +08:00
self.status.offset += res.data.results.length;
if (res.data.next === null) {
self.markAsLoaded(false);
} else {
self.markAsLoaded(true);
}
},
);
},
2019-11-22 14:37:06 +08:00
markAsLoaded: function (hasNext) {
2019-03-07 19:28:23 +08:00
this.status.hasNext = hasNext;
this.status.loading = false;
2019-03-07 18:57:53 +08:00
this.$emit(
2019-11-22 14:37:06 +08:00
"loaded",
2019-03-07 18:57:53 +08:00
);
if (!hasNext) {
this.$emit("no-more-pins")
}
2019-03-07 18:57:53 +08:00
},
2019-11-22 14:37:06 +08:00
markAsLoading: function () {
2019-03-07 19:28:23 +08:00
this.status.loading = true;
2019-03-07 18:57:53 +08:00
this.$emit(
2019-11-22 14:37:06 +08:00
"loading",
2019-03-07 18:57:53 +08:00
);
},
2019-03-07 19:28:23 +08:00
bindScrollHandler: function () {
2019-03-07 18:57:53 +08:00
var self = this;
2019-11-22 14:37:06 +08:00
scrollHandler = function () {
2019-03-07 19:28:23 +08:00
if (self.status.loading || !self.status.hasNext) {
2019-03-07 18:57:53 +08:00
return
}
var windowPosition = getDocumentScrollTop() + window.innerHeight;
var bottom = utils.getDocumentHeight() - 100;
2019-11-22 14:37:06 +08:00
if (windowPosition > bottom) {
2019-03-07 19:28:23 +08:00
self.loadMore();
2019-03-07 18:57:53 +08:00
}
};
2019-11-22 14:37:06 +08:00
window.addEventListener('scroll', function (e) {
2019-03-07 18:57:53 +08:00
scrollHandler();
}
);
},
2019-11-22 14:37:06 +08:00
updateChildHeight: function (childArg) {
this.heightTable.set(childArg.index, childArg.height);
},
2019-11-22 14:37:06 +08:00
reflow: function () {
this.counter.resetAfterReflow(self.pins.length);
this.updateArguments();
events.$emit("reflow");
},
2019-11-22 14:37:06 +08:00
updateArguments: function () {
var blockContainer = this.$el;
var containerWidth = blockContainer.clientWidth;
var blockMargin = this.args.blockMargin,
2019-11-22 14:37:06 +08:00
blockWidth = this.args.blockWidth,
rowSize = Math.floor(containerWidth / (blockWidth + blockMargin));
var rowStartMargin = (containerWidth - rowSize * (blockWidth + blockMargin)) / 2;
var rowEndMargin = rowStartMargin - blockMargin;
this.args.containerWidth = containerWidth;
this.args.blockWidth = blockWidth;
this.args.blockMargin = blockMargin;
this.args.rowSize = rowSize;
this.args.rowStartMargin = rowStartMargin;
this.args.rowEndMargin = rowEndMargin;
},
},
});
2019-11-22 14:37:06 +08:00
(function () {
var previousResize = 0;
2019-11-22 14:37:06 +08:00
var throttle = function (type, name, obj) {
obj = obj || window;
var running = false;
var func = function () {
if (running) {
return;
}
var now = new Date().getTime();
if ((now - previousResize) < 200) {
return
}
previousResize = now;
running = true;
requestAnimationFrame(function () {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
2019-03-01 17:21:59 +08:00
throttle("resize", "optimizedResize");
2019-03-01 17:21:59 +08:00
})();
2019-03-07 18:57:53 +08:00
var app = new Vue({
el: '#app',
data() {
return {
loading: true,
noMore: false,
currentPin: null,
}
},
2019-11-22 14:37:06 +08:00
created: function () {
events.$on(
eventsName.pinView.open,
this.onViewPin,
);
events.$on(
eventsName.pinView.close,
this.onClosePin,
);
},
methods: {
2019-11-22 14:37:06 +08:00
showPinForm: function () {
events.$emit(
eventsName.pinForm.open,
)
},
2019-11-22 14:37:06 +08:00
onViewPin: function (pin) {
this.currentPin = pin;
},
2019-11-22 14:37:06 +08:00
onClosePin: function (pin) {
this.currentPin = null;
},
2019-11-22 14:37:06 +08:00
onLoaded: function () {
this.loading = false;
},
2019-11-22 14:37:06 +08:00
onLoading: function () {
this.loading = true;
},
onNoMore: function () {
this.noMore = true;
}
},
});