This commit is contained in:
masnn committed 2020-04-08 21:10:24 +08:00
1 parent 777a4bc927
commit 9c45527522
396 files changed
+13772

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
export default function (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
+19
View File
@@ -0,0 +1,19 @@
/**
*
* @param {Event} ev
* @param {String} targetUrl
* @param {Boolean} alwaysOpenInNewWindow
*/
export default function emulateAnchorClick(ev, targetUrl, alwaysOpenInNewWindow = false) {
let openInNewWindow;
if (alwaysOpenInNewWindow) {
openInNewWindow = true;
} else {
openInNewWindow = (ev.ctrlKey || ev.shiftKey || ev.metaKey);
}
if (openInNewWindow) {
window.open(targetUrl);
} else {
window.location.href = targetUrl;
}
}
+8
View File
@@ -0,0 +1,8 @@
import substitute from './substitute';
export default function i18n(str, ...params) {
if (window.LOCALES && window.LOCALES[str]) {
return substitute(window.LOCALES[str], params);
}
return substitute(str, params);
}
+30
View File
@@ -0,0 +1,30 @@
export default async function loadReactRedux(storeReducer) {
const React = await import('react');
const { render, unmountComponentAtNode } = await import('react-dom');
const { Provider } = await import('react-redux');
const { createStore, applyMiddleware } = await import('redux');
const { default: reduxThunk } = await import('redux-thunk');
const { default: reduxPromise } = await import('redux-promise-middleware');
const reduxMiddlewares = [];
reduxMiddlewares.push(reduxThunk);
reduxMiddlewares.push(reduxPromise());
if (process.env.NODE_ENV !== 'production') {
const { createLogger } = await import('redux-logger');
reduxMiddlewares.push(createLogger({
collapsed: true,
duration: true,
}));
}
const store = createStore(storeReducer, applyMiddleware(...reduxMiddlewares));
return {
React,
render,
unmountComponentAtNode,
Provider,
store,
};
}
+13
View File
@@ -0,0 +1,13 @@
export function isAbove(width) {
if (window.matchMedia) {
return window.matchMedia(`(min-width: ${width}px)`).matches;
}
return window.innerWidth >= width;
}
export function isBelow(width) {
if (window.matchMedia) {
return window.matchMedia(`(max-width: ${width}px)`).matches;
}
return window.innerWidth <= width;
}
+15
View File
@@ -0,0 +1,15 @@
/* eslint-disable */
// https://github.com/andrasq/node-mongoid-js/blob/master/mongoid.js
export function parse(idstring) {
if (typeof idstring !== 'string') {
idstring = String(idstring);
}
return {
timestamp: parseInt(idstring.slice( 0, 0+8), 16),
machineid: parseInt(idstring.slice( 8, 8+6), 16),
pid: parseInt(idstring.slice(14, 14+4), 16),
sequence: parseInt(idstring.slice(18, 18+6), 16),
};
}
+11
View File
@@ -0,0 +1,11 @@
export default function parseQueryString(str) {
const obj = {};
(str || document.location.search)
.replace(/(^\?)/, '')
.split('&')
.forEach((n) => {
const [key, value] = n.split('=').map(v => decodeURIComponent(v));
obj[key] = value;
});
return obj;
}
+150
View File
@@ -0,0 +1,150 @@
// Modified from jquery-pjax to support multiple fragments and jQuery 3.0
// https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js
import uuid from 'uuid/v4';
import Notification from 'vj/components/notification';
import NProgress from 'vj/components/nprogress';
import request from './request';
const pjax = {};
let currentState = null;
let currentXHR = null;
let inProgress = 0;
function cancelLastXHR() {
if (currentXHR && currentXHR.readyState < 4) {
currentXHR.abort();
currentXHR = null;
}
}
function canonicalizeUrl(url) {
const a = document.createElement('a');
a.href = url;
return a.href;
}
function extractMetaData(requestOption, response) {
const metaData = {
title: response.title ? response.title : document.title,
url: response.url ? response.url : requestOption.url,
};
metaData.url = canonicalizeUrl(metaData.url);
return metaData;
}
function incProgress() {
inProgress++;
NProgress.done();
NProgress.start();
}
function decProgress() {
inProgress--;
if (inProgress === 0) {
NProgress.done();
}
}
pjax.request = async (opt) => {
const options = {
method: 'get',
push: true,
...opt,
};
if (!currentState) {
// create initial state
currentState = {
id: uuid(),
options: {
url: window.location.href,
},
};
window.history.replaceState(currentState, null);
}
cancelLastXHR();
if (options.push) {
// in popstate, options.push === false
window.history.pushState(null, null, options.url);
}
incProgress();
try {
const data = await request.ajax({
beforeSend(xhr) {
currentXHR = xhr;
},
...options,
});
const meta = extractMetaData(options, data);
currentState = {
id: uuid(),
options: opt,
};
if (options.push) {
window.history.replaceState(currentState, null, meta.url);
}
document.title = meta.title;
data.fragments.forEach((fragment) => {
if (process.env.NODE_ENV !== 'production') {
if (fragment.html === undefined) {
// eslint-disable-next-line quotes
throw new Error(`Fragement should contain 'html'`);
}
}
const $el = $(fragment.html.trim());
if (process.env.NODE_ENV !== 'production') {
if ($el.length === 0) {
// eslint-disable-next-line quotes
throw new Error(`Unable to build elements from fragment 'html'`);
}
}
const fragmentId = $el.attr('data-fragment-id');
if (process.env.NODE_ENV !== 'production') {
if (!fragmentId) {
// eslint-disable-next-line quotes
throw new Error(`Unable to extract fragment id from fragment 'html'`);
}
}
const $target = $(`[data-fragment-id="${fragmentId}"]`);
if (process.env.NODE_ENV !== 'production') {
if ($target.length === 0) {
// eslint-disable-next-line quotes
throw new Error(`Unable to get target fragment from fragment id`);
}
}
$target.trigger('vjContentRemove');
$target.replaceWith($el);
$el.trigger('vjContentNew');
});
} catch (err) {
if (!err.aborted) {
Notification.error(err.message);
console.log(err);
}
} finally {
decProgress();
}
};
function handlePopState(ev) {
if (!ev.originalEvent) {
return;
}
const { state } = ev.originalEvent;
if (!state) {
return;
}
if (!state.id || (currentState && state.id === currentState.id)) {
return;
}
pjax.request({
...state.options,
push: false,
});
}
$(window).on('popstate', handlePopState);
export default pjax;
+79
View File
@@ -0,0 +1,79 @@
import i18n from './i18n';
const request = {
/**
* @param {object} options
*/
async ajax(options) {
return new Promise((resolve, reject) => {
$
.ajax({
dataType: 'json',
...options,
})
.fail((jqXHR, textStatus, errorThrown) => {
if (textStatus === 'abort') {
const err = new Error(i18n('Aborted'));
err.aborted = true;
reject(err);
} else if (jqXHR.readyState === 0) {
reject(new Error(i18n('Network error')));
} else if (errorThrown instanceof Error) {
reject(errorThrown);
} else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) {
reject(new Error(jqXHR.responseJSON.error.message));
} else {
reject(new Error(textStatus));
}
})
.done(resolve);
});
},
/**
* @param {string} url
* @param {JQueryStatic | Node | string | object} dataOrForm
* @param {object} options
*/
post(url, dataOrForm = {}, options = {}) {
let postData;
if (dataOrForm instanceof jQuery && dataOrForm.is('form')) {
// $form
postData = dataOrForm.serialize();
} else if (dataOrForm instanceof Node && $(dataOrForm).is('form')) {
// form
postData = $(dataOrForm).serialize();
} else if (typeof dataOrForm === 'string') {
// foo=bar&box=boz
postData = dataOrForm;
} else {
// {foo: 'bar'}
postData = $.param({
csrf_token: UiContext.csrf_token,
...dataOrForm,
}, true);
}
return request.ajax({
url,
method: 'post',
data: postData,
...options,
});
},
/**
* @param {string} url
* @param {object} qs
* @param {object} options
*/
get(url, qs = {}, options = {}) {
return request.ajax({
url,
data: qs,
method: 'get',
...options,
});
},
};
export default request;
+57
View File
@@ -0,0 +1,57 @@
import 'jquery.easing';
export async function slideDown($element, duration, fromCss = {}, toCss = {}) {
const originalStyl = $element.attr('style') || '';
$element.css({
position: 'absolute',
visibility: 'none',
display: 'block',
});
const height = $element.outerHeight();
$element.attr('style', originalStyl);
$element.css({
height: 0,
overflow: 'hidden',
display: 'block',
...fromCss,
});
$element.height();
await $element
.transition({
height,
...toCss,
}, {
duration,
easing: 'easeOutCubic',
})
.promise();
$element.attr('style', originalStyl);
$element.css({
display: 'block',
});
}
export async function slideUp($element, duration, fromCss = {}, toCss = {}) {
const originalStyl = $element.attr('style') || '';
const height = $element.outerHeight();
$element.css({
height,
overflow: 'hidden',
display: 'block',
...fromCss,
});
$element.height();
await $element
.transition({
height: 0,
...toCss,
}, {
duration,
easing: 'easeOutCubic',
})
.promise();
$element.attr('style', originalStyl);
$element.css({
display: 'none',
});
}
+8
View File
@@ -0,0 +1,8 @@
export default function substitute(str, obj) {
return str.replace(/\{([^{}]+)\}/g, (match, key) => {
if (obj[key] !== undefined) {
return String(obj[key]);
}
return `{${key}}`;
});
}
+23
View File
@@ -0,0 +1,23 @@
import _ from 'lodash';
export default function tpl(pieces, ...substitutions) {
let result = pieces[0];
for (let i = 0; i < substitutions.length; ++i) {
const subst = substitutions[i];
let substHtml;
if (typeof subst === 'object' && subst.templateRaw) {
substHtml = subst.html;
} else {
substHtml = _.escape(String(subst));
}
result += substHtml + pieces[i + 1];
}
return result;
}
export function rawHtml(html) {
return {
templateRaw: true,
html,
};
}
+12
View File
@@ -0,0 +1,12 @@
let zIndexCurrent = 1000;
const manager = {
getCurrent() {
return zIndexCurrent;
},
getNext() {
return ++zIndexCurrent;
},
};
export default manager;