mirror of
https://github.com/wekan/wekan.git
synced 2025-04-22 21:17:18 -04:00
commit
7fa9603f9d
273 changed files with 9843 additions and 19980 deletions
|
@ -23,7 +23,7 @@ dburles:collection-helpers
|
|||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.8.0
|
||||
mongo@1.9.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
|
@ -38,7 +38,7 @@ wekan-accounts-oidc
|
|||
# Utilities
|
||||
check@1.3.1
|
||||
jquery@1.11.10
|
||||
random@1.1.0
|
||||
random@1.2.0
|
||||
reactive-dict@1.3.0
|
||||
session@1.2.0
|
||||
tracker@1.2.0
|
||||
|
@ -67,7 +67,7 @@ templates:tabs
|
|||
verron:autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.4.0
|
||||
shell-server@0.5.0
|
||||
simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@1.2.3
|
||||
|
@ -75,7 +75,7 @@ horka:swipebox
|
|||
dynamic-import@0.5.1
|
||||
staringatlights:fast-render
|
||||
|
||||
accounts-password@1.5.2
|
||||
accounts-password@1.6.0
|
||||
cfs:gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
|
@ -85,7 +85,8 @@ msavin:usercache
|
|||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent@3.2.0-rc.0!
|
||||
coagmano:stylus
|
||||
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
|
||||
coagmano:stylus@1.1.0!
|
||||
lucasantoniassi:accounts-lockout
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
||||
|
|
|
@ -1 +1 @@
|
|||
METEOR@1.9.2
|
||||
METEOR@1.10.1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
3stack:presence@1.1.2
|
||||
accounts-base@1.5.0
|
||||
accounts-oauth@1.1.16
|
||||
accounts-password@1.5.3
|
||||
accounts-base@1.6.0
|
||||
accounts-oauth@1.2.0
|
||||
accounts-password@1.6.0
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
|
@ -17,7 +17,7 @@ base64@1.0.12
|
|||
binary-heap@1.0.11
|
||||
blaze@2.3.4
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.6.0
|
||||
boilerplate-generator@1.7.0
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.1
|
||||
|
@ -43,7 +43,7 @@ cfs:upload-http@0.0.20
|
|||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@2.0.0
|
||||
coagmano:stylus@1.1.0
|
||||
coffeescript@2.4.1
|
||||
coffeescript-compiler@2.4.1
|
||||
cottz:publish-relations@2.0.8
|
||||
|
@ -75,7 +75,7 @@ htmljs@1.0.11
|
|||
http@1.4.2
|
||||
id-map@1.1.0
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.0
|
||||
inter-process-messaging@0.1.1
|
||||
jquery@1.11.11
|
||||
kadira:blaze-layout@2.3.0
|
||||
kadira:dochead@1.5.0
|
||||
|
@ -84,7 +84,7 @@ kenton:accounts-sandstorm@0.7.0
|
|||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.1.1
|
||||
launch-screen@1.2.0
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
|
@ -104,13 +104,13 @@ meteorspark:util@0.2.0
|
|||
minifier-css@1.5.0
|
||||
minifier-js@2.6.0
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.4.5
|
||||
mobile-status-bar@1.0.14
|
||||
minimongo@1.5.0
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.5
|
||||
modules@0.15.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.8.1
|
||||
mongo@1.9.0
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
|
@ -127,13 +127,13 @@ mquandalle:mousetrap-bindglobal@0.0.1
|
|||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.3.0
|
||||
oauth@1.2.8
|
||||
oauth2@1.2.1
|
||||
npm-mongo@3.7.0
|
||||
oauth@1.3.0
|
||||
oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.1.0
|
||||
ostrio:cookies@2.5.0
|
||||
ostrio:cookies@2.6.0
|
||||
peerlibrary:assert@0.3.0
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
|
@ -144,7 +144,7 @@ promise@0.11.2
|
|||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
rajit:bootstrap3-datepicker@1.7.1_1
|
||||
random@1.1.0
|
||||
random@1.2.0
|
||||
rate-limit@1.0.9
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
|
@ -156,7 +156,7 @@ server-render@0.3.1
|
|||
service-configuration@1.0.11
|
||||
session@1.2.0
|
||||
sha@1.0.9
|
||||
shell-server@0.4.0
|
||||
shell-server@0.5.0
|
||||
simple:authenticate-user-by-token@1.0.1
|
||||
simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
|
@ -186,7 +186,7 @@ useraccounts:core@1.14.2
|
|||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.8.2
|
||||
webapp@1.9.0
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
|
|
|
@ -85,7 +85,8 @@ msavin:usercache
|
|||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent@3.2.0-rc.0!
|
||||
coagmano:stylus
|
||||
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
|
||||
coagmano:stylus@1.1.0!
|
||||
lucasantoniassi:accounts-lockout
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
|
@ -44,7 +44,7 @@ cfs:upload-http@0.0.20
|
|||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@2.0.0
|
||||
coagmano:stylus@1.1.0
|
||||
coffeescript@1.0.17
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
|
@ -1,8 +1,7 @@
|
|||
import ldapjs from 'ldapjs';
|
||||
import util from 'util';
|
||||
import Bunyan from 'bunyan';
|
||||
import {log_debug, log_info, log_warn, log_error} from './logger';
|
||||
|
||||
import { log_debug, log_info, log_warn, log_error } from './logger';
|
||||
|
||||
export default class LDAP {
|
||||
constructor() {
|
||||
|
@ -11,35 +10,66 @@ export default class LDAP {
|
|||
this.connected = false;
|
||||
|
||||
this.options = {
|
||||
host : this.constructor.settings_get('LDAP_HOST'),
|
||||
port : this.constructor.settings_get('LDAP_PORT'),
|
||||
Reconnect : this.constructor.settings_get('LDAP_RECONNECT'),
|
||||
timeout : this.constructor.settings_get('LDAP_TIMEOUT'),
|
||||
connect_timeout : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
|
||||
idle_timeout : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
|
||||
encryption : this.constructor.settings_get('LDAP_ENCRYPTION'),
|
||||
ca_cert : this.constructor.settings_get('LDAP_CA_CERT'),
|
||||
reject_unauthorized : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
|
||||
Authentication : this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
|
||||
Authentication_UserDN : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'),
|
||||
Authentication_Password : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'),
|
||||
Authentication_Fallback : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'),
|
||||
BaseDN : this.constructor.settings_get('LDAP_BASEDN'),
|
||||
Internal_Log_Level : this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
|
||||
User_Authentication : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'),
|
||||
User_Authentication_Field : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'),
|
||||
User_Attributes : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
|
||||
User_Search_Filter : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'),
|
||||
User_Search_Scope : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'),
|
||||
User_Search_Field : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'),
|
||||
Search_Page_Size : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
|
||||
Search_Size_Limit : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'),
|
||||
group_filter_enabled : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'),
|
||||
group_filter_object_class : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'),
|
||||
group_filter_group_id_attribute : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'),
|
||||
group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'),
|
||||
group_filter_group_member_format : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'),
|
||||
group_filter_group_name : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'),
|
||||
host: this.constructor.settings_get('LDAP_HOST'),
|
||||
port: this.constructor.settings_get('LDAP_PORT'),
|
||||
Reconnect: this.constructor.settings_get('LDAP_RECONNECT'),
|
||||
timeout: this.constructor.settings_get('LDAP_TIMEOUT'),
|
||||
connect_timeout: this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
|
||||
idle_timeout: this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
|
||||
encryption: this.constructor.settings_get('LDAP_ENCRYPTION'),
|
||||
ca_cert: this.constructor.settings_get('LDAP_CA_CERT'),
|
||||
reject_unauthorized:
|
||||
this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
|
||||
Authentication: this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
|
||||
Authentication_UserDN: this.constructor.settings_get(
|
||||
'LDAP_AUTHENTIFICATION_USERDN',
|
||||
),
|
||||
Authentication_Password: this.constructor.settings_get(
|
||||
'LDAP_AUTHENTIFICATION_PASSWORD',
|
||||
),
|
||||
Authentication_Fallback: this.constructor.settings_get(
|
||||
'LDAP_LOGIN_FALLBACK',
|
||||
),
|
||||
BaseDN: this.constructor.settings_get('LDAP_BASEDN'),
|
||||
Internal_Log_Level: this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
|
||||
User_Authentication: this.constructor.settings_get(
|
||||
'LDAP_USER_AUTHENTICATION',
|
||||
),
|
||||
User_Authentication_Field: this.constructor.settings_get(
|
||||
'LDAP_USER_AUTHENTICATION_FIELD',
|
||||
),
|
||||
User_Attributes: this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
|
||||
User_Search_Filter: this.constructor.settings_get(
|
||||
'LDAP_USER_SEARCH_FILTER',
|
||||
),
|
||||
User_Search_Scope: this.constructor.settings_get(
|
||||
'LDAP_USER_SEARCH_SCOPE',
|
||||
),
|
||||
User_Search_Field: this.constructor.settings_get(
|
||||
'LDAP_USER_SEARCH_FIELD',
|
||||
),
|
||||
Search_Page_Size: this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
|
||||
Search_Size_Limit: this.constructor.settings_get(
|
||||
'LDAP_SEARCH_SIZE_LIMIT',
|
||||
),
|
||||
group_filter_enabled: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_ENABLE',
|
||||
),
|
||||
group_filter_object_class: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_OBJECTCLASS',
|
||||
),
|
||||
group_filter_group_id_attribute: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE',
|
||||
),
|
||||
group_filter_group_member_attribute: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE',
|
||||
),
|
||||
group_filter_group_member_format: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT',
|
||||
),
|
||||
group_filter_group_name: this.constructor.settings_get(
|
||||
'LDAP_GROUP_FILTER_GROUP_NAME',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -58,14 +88,13 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
connectSync(...args) {
|
||||
if (!this._connectSync) {
|
||||
if (!this._connectSync) {
|
||||
this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
|
||||
}
|
||||
return this._connectSync(...args);
|
||||
}
|
||||
|
||||
searchAllSync(...args) {
|
||||
|
||||
if (!this._searchAllSync) {
|
||||
this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
|
||||
}
|
||||
|
@ -78,19 +107,19 @@ export default class LDAP {
|
|||
let replied = false;
|
||||
|
||||
const connectionOptions = {
|
||||
url : `${this.options.host}:${this.options.port}`,
|
||||
timeout : this.options.timeout,
|
||||
url: `${this.options.host}:${this.options.port}`,
|
||||
timeout: this.options.timeout,
|
||||
connectTimeout: this.options.connect_timeout,
|
||||
idleTimeout : this.options.idle_timeout,
|
||||
reconnect : this.options.Reconnect,
|
||||
idleTimeout: this.options.idle_timeout,
|
||||
reconnect: this.options.Reconnect,
|
||||
};
|
||||
|
||||
if (this.options.Internal_Log_Level !== 'disabled') {
|
||||
connectionOptions.log = new Bunyan({
|
||||
name : 'ldapjs',
|
||||
name: 'ldapjs',
|
||||
component: 'client',
|
||||
stream : process.stderr,
|
||||
level : this.options.Internal_Log_Level,
|
||||
stream: process.stderr,
|
||||
level: this.options.Internal_Log_Level,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -100,10 +129,12 @@ export default class LDAP {
|
|||
|
||||
if (this.options.ca_cert && this.options.ca_cert !== '') {
|
||||
// Split CA cert into array of strings
|
||||
const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n');
|
||||
let cert = [];
|
||||
const ca = [];
|
||||
chainLines.forEach((line) => {
|
||||
const chainLines = this.constructor
|
||||
.settings_get('LDAP_CA_CERT')
|
||||
.split('\n');
|
||||
let cert = [];
|
||||
const ca = [];
|
||||
chainLines.forEach(line => {
|
||||
cert.push(line);
|
||||
if (line.match(/-END CERTIFICATE-/)) {
|
||||
ca.push(cert.join('\n'));
|
||||
|
@ -114,7 +145,7 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
if (this.options.encryption === 'ssl') {
|
||||
connectionOptions.url = `ldaps://${connectionOptions.url}`;
|
||||
connectionOptions.url = `ldaps://${connectionOptions.url}`;
|
||||
connectionOptions.tlsOptions = tlsOptions;
|
||||
} else {
|
||||
connectionOptions.url = `ldap://${connectionOptions.url}`;
|
||||
|
@ -127,7 +158,7 @@ export default class LDAP {
|
|||
|
||||
this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
|
||||
|
||||
this.client.on('error', (error) => {
|
||||
this.client.on('error', error => {
|
||||
log_error('connection', error);
|
||||
if (replied === false) {
|
||||
replied = true;
|
||||
|
@ -171,7 +202,7 @@ export default class LDAP {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
this.client.on('connect', (response) => {
|
||||
this.client.on('connect', response => {
|
||||
log_info('LDAP connected');
|
||||
this.connected = true;
|
||||
if (replied === false) {
|
||||
|
@ -201,7 +232,9 @@ export default class LDAP {
|
|||
}
|
||||
}
|
||||
|
||||
const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`);
|
||||
const usernameFilter = this.options.User_Search_Field.split(',').map(
|
||||
item => `(${item}=${username})`,
|
||||
);
|
||||
|
||||
if (usernameFilter.length === 0) {
|
||||
log_error('LDAP_LDAP_User_Search_Field not defined');
|
||||
|
@ -215,7 +248,6 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
bindUserIfNecessary(username, password) {
|
||||
|
||||
if (this.domainBinded === true) {
|
||||
return;
|
||||
}
|
||||
|
@ -224,7 +256,6 @@ export default class LDAP {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
|
||||
|
||||
const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
|
||||
|
@ -244,23 +275,27 @@ export default class LDAP {
|
|||
|
||||
log_info('Binding UserDN', this.options.Authentication_UserDN);
|
||||
|
||||
this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
|
||||
this.bindSync(
|
||||
this.options.Authentication_UserDN,
|
||||
this.options.Authentication_Password,
|
||||
);
|
||||
this.domainBinded = true;
|
||||
}
|
||||
|
||||
searchUsersSync(username, page) {
|
||||
this.bindIfNecessary();
|
||||
const searchOptions = {
|
||||
filter : this.getUserFilter(username),
|
||||
scope : this.options.User_Search_Scope || 'sub',
|
||||
filter: this.getUserFilter(username),
|
||||
scope: this.options.User_Search_Scope || 'sub',
|
||||
sizeLimit: this.options.Search_Size_Limit,
|
||||
};
|
||||
|
||||
if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(',');
|
||||
if (!!this.options.User_Attributes)
|
||||
searchOptions.attributes = this.options.User_Attributes.split(',');
|
||||
|
||||
if (this.options.Search_Page_Size > 0) {
|
||||
searchOptions.paged = {
|
||||
pageSize : this.options.Search_Page_Size,
|
||||
pageSize: this.options.Search_Page_Size,
|
||||
pagePause: !!page,
|
||||
};
|
||||
}
|
||||
|
@ -279,7 +314,9 @@ export default class LDAP {
|
|||
getUserByIdSync(id, attribute) {
|
||||
this.bindIfNecessary();
|
||||
|
||||
const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(',');
|
||||
const Unique_Identifier_Field = this.constructor
|
||||
.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD')
|
||||
.split(',');
|
||||
|
||||
let filter;
|
||||
|
||||
|
@ -290,11 +327,13 @@ export default class LDAP {
|
|||
});
|
||||
} else {
|
||||
const filters = [];
|
||||
Unique_Identifier_Field.forEach((item) => {
|
||||
filters.push(new this.ldapjs.filters.EqualityFilter({
|
||||
attribute: item,
|
||||
value : new Buffer(id, 'hex'),
|
||||
}));
|
||||
Unique_Identifier_Field.forEach(item => {
|
||||
filters.push(
|
||||
new this.ldapjs.filters.EqualityFilter({
|
||||
attribute: item,
|
||||
value: new Buffer(id, 'hex'),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
filter = new this.ldapjs.filters.OrFilter({ filters });
|
||||
|
@ -327,7 +366,7 @@ export default class LDAP {
|
|||
|
||||
const searchOptions = {
|
||||
filter: this.getUserFilter(username),
|
||||
scope : this.options.User_Search_Scope || 'sub',
|
||||
scope: this.options.User_Search_Scope || 'sub',
|
||||
};
|
||||
|
||||
log_info('Searching user', username);
|
||||
|
@ -341,7 +380,13 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
if (result.length > 1) {
|
||||
log_error('Search by username', username, 'returned', result.length, 'records');
|
||||
log_error(
|
||||
'Search by username',
|
||||
username,
|
||||
'returned',
|
||||
result.length,
|
||||
'records',
|
||||
);
|
||||
}
|
||||
|
||||
return result[0];
|
||||
|
@ -359,9 +404,12 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
if (this.options.group_filter_group_member_attribute !== '') {
|
||||
const format_value = ldapUser[this.options.group_filter_group_member_format];
|
||||
const format_value =
|
||||
ldapUser[this.options.group_filter_group_member_format];
|
||||
if (format_value) {
|
||||
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
|
||||
filter.push(
|
||||
`(${this.options.group_filter_group_member_attribute}=${format_value})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +417,7 @@ export default class LDAP {
|
|||
|
||||
const searchOptions = {
|
||||
filter: filter.join('').replace(/#{username}/g, username),
|
||||
scope : 'sub',
|
||||
scope: 'sub',
|
||||
};
|
||||
|
||||
log_debug('Group list filter LDAP:', searchOptions.filter);
|
||||
|
@ -381,13 +429,12 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
|
||||
const groups = [];
|
||||
result.map((item) => {
|
||||
const groups = [];
|
||||
result.map(item => {
|
||||
groups.push(item[grp_identifier]);
|
||||
});
|
||||
log_debug(`Groups: ${groups.join(', ')}`);
|
||||
return groups;
|
||||
|
||||
}
|
||||
|
||||
isUserInGroup(username, ldapUser) {
|
||||
|
@ -404,20 +451,25 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
if (this.options.group_filter_group_member_attribute !== '') {
|
||||
const format_value = ldapUser[this.options.group_filter_group_member_format];
|
||||
const format_value =
|
||||
ldapUser[this.options.group_filter_group_member_format];
|
||||
if (format_value) {
|
||||
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
|
||||
filter.push(
|
||||
`(${this.options.group_filter_group_member_attribute}=${format_value})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.group_filter_group_id_attribute !== '') {
|
||||
filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`);
|
||||
filter.push(
|
||||
`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`,
|
||||
);
|
||||
}
|
||||
filter.push(')');
|
||||
|
||||
const searchOptions = {
|
||||
filter: filter.join('').replace(/#{username}/g, username),
|
||||
scope : 'sub',
|
||||
scope: 'sub',
|
||||
};
|
||||
|
||||
log_debug('Group filter LDAP:', searchOptions.filter);
|
||||
|
@ -435,7 +487,7 @@ export default class LDAP {
|
|||
_raw: entry.raw,
|
||||
};
|
||||
|
||||
Object.keys(values._raw).forEach((key) => {
|
||||
Object.keys(values._raw).forEach(key => {
|
||||
const value = values._raw[key];
|
||||
|
||||
if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
|
||||
|
@ -458,11 +510,12 @@ export default class LDAP {
|
|||
// Force LDAP idle to wait the record processing
|
||||
this.client._updateIdle(true);
|
||||
page(null, entries, {
|
||||
end, next: () => {
|
||||
end,
|
||||
next: () => {
|
||||
// Reset idle timer
|
||||
this.client._updateIdle();
|
||||
next && next();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -473,7 +526,7 @@ export default class LDAP {
|
|||
return;
|
||||
}
|
||||
|
||||
res.on('error', (error) => {
|
||||
res.on('error', error => {
|
||||
log_error(error);
|
||||
page(error);
|
||||
return;
|
||||
|
@ -481,16 +534,19 @@ export default class LDAP {
|
|||
|
||||
let entries = [];
|
||||
|
||||
const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500;
|
||||
const internalPageSize =
|
||||
options.paged && options.paged.pageSize > 0
|
||||
? options.paged.pageSize * 2
|
||||
: 500;
|
||||
|
||||
res.on('searchEntry', (entry) => {
|
||||
res.on('searchEntry', entry => {
|
||||
entries.push(this.extractLdapEntryData(entry));
|
||||
|
||||
if (entries.length >= internalPageSize) {
|
||||
processPage({
|
||||
entries,
|
||||
title: 'Internal Page',
|
||||
end : false,
|
||||
end: false,
|
||||
});
|
||||
entries = [];
|
||||
}
|
||||
|
@ -502,14 +558,14 @@ export default class LDAP {
|
|||
processPage({
|
||||
entries,
|
||||
title: 'Final Page',
|
||||
end : true,
|
||||
end: true,
|
||||
});
|
||||
} else if (entries.length) {
|
||||
log_info('Page');
|
||||
processPage({
|
||||
entries,
|
||||
title: 'Page',
|
||||
end : false,
|
||||
end: false,
|
||||
next,
|
||||
});
|
||||
entries = [];
|
||||
|
@ -521,7 +577,7 @@ export default class LDAP {
|
|||
processPage({
|
||||
entries,
|
||||
title: 'Final Page',
|
||||
end : true,
|
||||
end: true,
|
||||
});
|
||||
entries = [];
|
||||
}
|
||||
|
@ -539,7 +595,7 @@ export default class LDAP {
|
|||
return;
|
||||
}
|
||||
|
||||
res.on('error', (error) => {
|
||||
res.on('error', error => {
|
||||
log_error(error);
|
||||
callback(error);
|
||||
return;
|
||||
|
@ -547,7 +603,7 @@ export default class LDAP {
|
|||
|
||||
const entries = [];
|
||||
|
||||
res.on('searchEntry', (entry) => {
|
||||
res.on('searchEntry', entry => {
|
||||
entries.push(this.extractLdapEntryData(entry));
|
||||
});
|
||||
|
||||
|
@ -576,7 +632,7 @@ export default class LDAP {
|
|||
}
|
||||
|
||||
disconnect() {
|
||||
this.connected = false;
|
||||
this.connected = false;
|
||||
this.domainBinded = false;
|
||||
log_info('Disconecting');
|
||||
this.client.unbind();
|
|
@ -1,13 +1,12 @@
|
|||
Oidc = {};
|
||||
|
||||
OAuth.registerService('oidc', 2, null, function (query) {
|
||||
|
||||
OAuth.registerService('oidc', 2, null, function(query) {
|
||||
var debug = process.env.DEBUG || false;
|
||||
var token = getToken(query);
|
||||
if (debug) console.log('XXX: register token:', token);
|
||||
|
||||
var accessToken = token.access_token || token.id_token;
|
||||
var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
|
||||
var expiresAt = +new Date() + 1000 * parseInt(token.expires_in, 10);
|
||||
|
||||
var userinfo = getUserInfo(accessToken);
|
||||
if (debug) console.log('XXX: userinfo:', userinfo);
|
||||
|
@ -22,12 +21,14 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
|||
|
||||
if (accessToken) {
|
||||
var tokenContent = getTokenContent(accessToken);
|
||||
var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
|
||||
var fields = _.pick(
|
||||
tokenContent,
|
||||
getConfiguration().idTokenWhitelistFields,
|
||||
);
|
||||
_.extend(serviceData, fields);
|
||||
}
|
||||
|
||||
if (token.refresh_token)
|
||||
serviceData.refreshToken = token.refresh_token;
|
||||
if (token.refresh_token) serviceData.refreshToken = token.refresh_token;
|
||||
if (debug) console.log('XXX: serviceData:', serviceData);
|
||||
|
||||
var profile = {};
|
||||
|
@ -37,88 +38,101 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
|||
|
||||
return {
|
||||
serviceData: serviceData,
|
||||
options: { profile: profile }
|
||||
options: { profile: profile },
|
||||
};
|
||||
});
|
||||
|
||||
var userAgent = "Meteor";
|
||||
var userAgent = 'Meteor';
|
||||
if (Meteor.release) {
|
||||
userAgent += "/" + Meteor.release;
|
||||
userAgent += '/' + Meteor.release;
|
||||
}
|
||||
|
||||
var getToken = function (query) {
|
||||
var getToken = function(query) {
|
||||
var debug = process.env.DEBUG || false;
|
||||
var config = getConfiguration();
|
||||
if(config.tokenEndpoint.includes('https://')){
|
||||
if (config.tokenEndpoint.includes('https://')) {
|
||||
var serverTokenEndpoint = config.tokenEndpoint;
|
||||
}else{
|
||||
} else {
|
||||
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
|
||||
}
|
||||
var requestPermissions = config.requestPermissions;
|
||||
var response;
|
||||
|
||||
try {
|
||||
response = HTTP.post(
|
||||
serverTokenEndpoint,
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
"User-Agent": userAgent
|
||||
},
|
||||
params: {
|
||||
code: query.code,
|
||||
client_id: config.clientId,
|
||||
client_secret: OAuth.openSecret(config.secret),
|
||||
redirect_uri: OAuth._redirectUri('oidc', config),
|
||||
grant_type: 'authorization_code',
|
||||
scope: requestPermissions,
|
||||
state: query.state
|
||||
}
|
||||
}
|
||||
);
|
||||
response = HTTP.post(serverTokenEndpoint, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'User-Agent': userAgent,
|
||||
},
|
||||
params: {
|
||||
code: query.code,
|
||||
client_id: config.clientId,
|
||||
client_secret: OAuth.openSecret(config.secret),
|
||||
redirect_uri: OAuth._redirectUri('oidc', config),
|
||||
grant_type: 'authorization_code',
|
||||
scope: requestPermissions,
|
||||
state: query.state,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
|
||||
{ response: err.response });
|
||||
throw _.extend(
|
||||
new Error(
|
||||
'Failed to get token from OIDC ' +
|
||||
serverTokenEndpoint +
|
||||
': ' +
|
||||
err.message,
|
||||
),
|
||||
{ response: err.response },
|
||||
);
|
||||
}
|
||||
if (response.data.error) {
|
||||
// if the http response was a json object with an error attribute
|
||||
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
|
||||
throw new Error(
|
||||
'Failed to complete handshake with OIDC ' +
|
||||
serverTokenEndpoint +
|
||||
': ' +
|
||||
response.data.error,
|
||||
);
|
||||
} else {
|
||||
if (debug) console.log('XXX: getToken response: ', response.data);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
var getUserInfo = function (accessToken) {
|
||||
var getUserInfo = function(accessToken) {
|
||||
var debug = process.env.DEBUG || false;
|
||||
var config = getConfiguration();
|
||||
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
|
||||
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
|
||||
if (config.userinfoEndpoint.includes("https://")) {
|
||||
if (config.userinfoEndpoint.includes('https://')) {
|
||||
var serverUserinfoEndpoint = config.userinfoEndpoint;
|
||||
} else {
|
||||
var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
|
||||
}
|
||||
var response;
|
||||
try {
|
||||
response = HTTP.get(
|
||||
serverUserinfoEndpoint,
|
||||
{
|
||||
headers: {
|
||||
"User-Agent": userAgent,
|
||||
"Authorization": "Bearer " + accessToken
|
||||
}
|
||||
}
|
||||
);
|
||||
response = HTTP.get(serverUserinfoEndpoint, {
|
||||
headers: {
|
||||
'User-Agent': userAgent,
|
||||
Authorization: 'Bearer ' + accessToken,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
|
||||
{response: err.response});
|
||||
throw _.extend(
|
||||
new Error(
|
||||
'Failed to fetch userinfo from OIDC ' +
|
||||
serverUserinfoEndpoint +
|
||||
': ' +
|
||||
err.message,
|
||||
),
|
||||
{ response: err.response },
|
||||
);
|
||||
}
|
||||
if (debug) console.log('XXX: getUserInfo response: ', response.data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
var getConfiguration = function () {
|
||||
var getConfiguration = function() {
|
||||
var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
|
||||
if (!config) {
|
||||
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
|
||||
|
@ -126,7 +140,7 @@ var getConfiguration = function () {
|
|||
return config;
|
||||
};
|
||||
|
||||
var getTokenContent = function (token) {
|
||||
var getTokenContent = function(token) {
|
||||
var content = null;
|
||||
if (token) {
|
||||
try {
|
||||
|
@ -137,13 +151,13 @@ var getTokenContent = function (token) {
|
|||
var signed = parts[0] + '.' + parts[1];
|
||||
} catch (err) {
|
||||
this.content = {
|
||||
exp: 0
|
||||
exp: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
|
||||
Oidc.retrieveCredential = function(credentialToken, credentialSecret) {
|
||||
return OAuth.retrieveCredential(credentialToken, credentialSecret);
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.80.0",
|
||||
"version": "v3.86.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -40,34 +40,34 @@
|
|||
},
|
||||
"homepage": "https://wekan.github.io",
|
||||
"devDependencies": {
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-meteor": "0.0.9",
|
||||
"eslint-config-prettier": "^3.6.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-meteor": "^0.1.1",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-import-resolver-meteor": "^0.4.0",
|
||||
"eslint-plugin-import": "^2.18.0",
|
||||
"eslint-plugin-meteor": "^5.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"lint-staged": "^7.3.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-meteor": "^6.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"lint-staged": "^10.0.8",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.18.2",
|
||||
"prettier-eslint": "^8.8.2"
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-eslint": "^9.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"ajv": "^5.0.0",
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"ajv": "^6.12.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bcrypt": "^3.0.2",
|
||||
"bson": "^4.0.0",
|
||||
"bcrypt": "^4.0.1",
|
||||
"bson": "^4.0.3",
|
||||
"bunyan": "^1.8.12",
|
||||
"es6-promise": "^4.2.4",
|
||||
"gridfs-stream": "^0.5.3",
|
||||
"es6-promise": "^4.2.8",
|
||||
"gridfs-stream": "^1.1.1",
|
||||
"ldapjs": "^1.0.2",
|
||||
"meteor-node-stubs": "^0.4.1",
|
||||
"mongodb": "^3.3.3",
|
||||
"meteor-node-stubs": "^1.0.0",
|
||||
"mongodb": "^3.5.5",
|
||||
"os": "^0.1.1",
|
||||
"page": "^1.8.6",
|
||||
"qs": "^6.8.0",
|
||||
"source-map-support": "^0.5.12",
|
||||
"page": "^1.11.5",
|
||||
"qs": "^6.9.1",
|
||||
"source-map-support": "^0.5.16",
|
||||
"xss": "^1.0.6"
|
||||
}
|
||||
}
|
132
CHANGELOG.md
132
CHANGELOG.md
|
@ -1,10 +1,140 @@
|
|||
# Upcoming Wekan release
|
||||
|
||||
This release tries to fix the following bugs:
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Hide duplicate "Hide system messages" at Change Settings/Member Settings, because it's also on card
|
||||
slider](https://github.com/wekan/wekan/issues/2837).
|
||||
Thanks to notohiro and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.86 2020-03-24 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Rich editor can not be disabled, regression from changes yesterday at Wekan v3.85](https://github.com/wekan/wekan/commit/12ab8fac5db9c5ac8069d0ca2bca340d6004a25b).
|
||||
Thanks to uusijani, vjrj and xet7.
|
||||
- [1) Fix Pasting text into a card is adding a line before and after
|
||||
(and multiplies by pasting more) by changing paste "p" to "br".
|
||||
2) Fixes to summernote and markdown comment editors, related
|
||||
to keeping them open when adding comments, having
|
||||
@member mention not close card, and disabling clicking of
|
||||
@member mention](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).
|
||||
Thanks to xet7 !
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.85 2020-03-23 Wekan release
|
||||
|
||||
This release fixes the following CRITICAL SECURITY VULNERABILITIES:
|
||||
|
||||
- [Fix XSS bug reported today 4 hours ago by Cyb3rjunky](https://github.com/wekan/wekan/commit/482682e50079d70c5113169020d6834013b57c11).
|
||||
Logged in users could run javascript in input fields.
|
||||
This affects Wekan versions v3.12-v3.84.
|
||||
In [Wekan v3.12](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v312-2019-08-09-wekan-release)
|
||||
there was [changes for XSS filter to allow inserting images, videos etc
|
||||
on comment WYSIWYG editor](https://github.com/wekan/wekan/pull/2593)
|
||||
so features related to that are now removed.
|
||||
After this fix, Javascript in input fields is not executed.
|
||||
Thanks to Cyb3rjunky and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.84 2020-03-16 Wekan release
|
||||
|
||||
This release adds the following features:
|
||||
|
||||
- Add settings for mouse wheel scroll inertia and scroll
|
||||
amount [Part1](https://github.com/wekan/wekan/commit/9d13001b903f9ec50f5fa3a4bdbacae32b27ac65)
|
||||
and [Part2](https://github.com/wekan/wekan/commit/aaecac091209e90c0c2123830728f5e7a835ccb4).
|
||||
For example: sudo snap set wekan scrollinertia='200' , sudo snap set wekan scrollamount='200' .
|
||||
Thanks to danger89 and xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Upgrade to Meteor 1.10.1](https://github.com/wekan/wekan/commit/e16c65babc1f021c35a3d46bc61e649ec94d1e82).
|
||||
Thanks to xet7.
|
||||
- [Update markdown](https://github.com/wekan/wekan/commit/6e0fa78022ea487176eb0a32ec5a4a441f8e0c3c).
|
||||
Thanks to xet7.
|
||||
- [Update minimist](https://github.com/wekan/wekan/commit/ea6baa5c2b956ee28b0a7e63f988e2fc1998201a).
|
||||
Thanks to xet7.
|
||||
- [Update acorn](https://github.com/wekan/wekan/commit/369a29707bbec3bf89717c16e8b698fb4666087a).
|
||||
Thanks to xet7.
|
||||
- [Update prettier-eslint](https://github.com/wekan/wekan/commit/8183b7bdaa01d2ce53ac7215beafd5efe21373e8).
|
||||
Thanks to xet7.
|
||||
- [Update ostrio:cookies](https://github.com/wekan/wekan/commit/14b8610837117616d436e2bac6a9dc653e315662).
|
||||
Thanks to xet7.
|
||||
- [Add build time profiling to build script](https://github.com/wekan/wekan/commit/f968109e7390139e50375ee29bc7bc3cf1e1ab41).
|
||||
Thanks to zodern.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Downgrade stylus to v1.1.0 to speed up building Wekan](https://github.com/wekan/wekan/commit/fca4cdcebf1cc6642aefeb78b911cb5b95ebe473).
|
||||
This is because building newer stylus v2 takes 52 minutes. After this change, building Wekan takes 3 minutes.
|
||||
Thanks to zodern.
|
||||
- [Fix: Error when retrieve token from some OIDC due to not necessary scope
|
||||
parameter](https://github.com/wekan/wekan/pull/2955).
|
||||
Thanks to benoitm76.
|
||||
- [Fix: img tag did not allow width and height. Removed swipebox from markdown editor
|
||||
img tag and updated marked markdown to newest version](https://github.com/wekan/wekan/commit/2b26bbe78a1a2b8b427963a6c44c3853efdb737e).
|
||||
Thanks to hradec and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.83 2020-03-01 Wekan release
|
||||
|
||||
This release tries to revert remaining the following changes:
|
||||
|
||||
- [Revert](https://github.com/wekan/wekan/88573ad2cdb8596b795a82ef40a0662180e8a7d7) change made at Wekan v3.81,
|
||||
because building did not work: [Try to make Meteor build time shorter
|
||||
by excluding legacy and cordova. This was made possible by
|
||||
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.82 2020-03-01 Wekan release
|
||||
|
||||
This release reverts the following changes:
|
||||
|
||||
- Revert change made at Wekan v3.81, because building did not work: [Try to make Meteor build time shorter
|
||||
by excluding legacy and cordova. This was made possible by
|
||||
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.81 2020-03-01 Wekan release
|
||||
|
||||
This release [fixes](https://github.com/wekan/wekan/commit/aac7c380c8c389b0683b2bd64e2cc856993f0e30) the following CRITICAL SECURITY VULNERABILITIES and other bugs:
|
||||
|
||||
- Fix critical and moderate security vulnerabilities reported at 2020-02-26 with
|
||||
responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff),
|
||||
Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining),
|
||||
that follow standard 90 days before public disclosure.
|
||||
Thanks to xet7.
|
||||
- Fix webhook error that prevented some card etc deleting from web UI of board.
|
||||
Thanks to xet7.
|
||||
- Add missing Font Awesome icon to Board Settings Menu.
|
||||
Thanks to xet7.
|
||||
- Remove autofocus from many form input boxes so that they would not cause warnings.
|
||||
Thanks to xet7.
|
||||
|
||||
and does the following upgrades:
|
||||
|
||||
- [Upgrade Meteor to 1.10-rc.2](https://github.com/wekan/wekan/commit/26b521e86e6ac40b7ba25bbe8dac7bf4d48d43ce).
|
||||
Thanks to xet7.
|
||||
- [Try to make Meteor build time shorter by excluding legacy and cordova. This was made possible by
|
||||
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Try to fix afterwards loading of cards by adding fallback when requestIdleCallback is not
|
||||
available](https://github.com/wekan/wekan/commit/2b9540ce02de604bf84ea082f2dcb1d01673708c).
|
||||
Thanks to xet7.
|
||||
- [Make profile.initials available in publications](https://github.com/wekan/wekan/pull/2948).
|
||||
Thanks to NicoP-S.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ LABEL maintainer="wekan"
|
|||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.16.1 \
|
||||
METEOR_RELEASE=1.9.0 \
|
||||
METEOR_RELEASE=1.10-rc.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
|
@ -110,7 +110,9 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
|||
CORS="" \
|
||||
CORS_ALLOW_HEADERS="" \
|
||||
CORS_EXPOSE_HEADERS="" \
|
||||
DEFAULT_AUTHENTICATION_METHOD=""
|
||||
DEFAULT_AUTHENTICATION_METHOD="" \
|
||||
SCROLLINERTIA="0" \
|
||||
SCROLLAMOUNT="auto"
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v3.80.0"
|
||||
appVersion: "v3.86.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
|
|
@ -33,6 +33,13 @@ BlazeComponent.extendComponent({
|
|||
cardId,
|
||||
});
|
||||
resetCommentInput(input);
|
||||
// With Richer editor is in use, and comment is submitted,
|
||||
// clear comment form with JQuery. Id #summernote is defined
|
||||
// at client/components/main/editor.jade where it previously was
|
||||
// id=id, now it is id="summernote".
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') {
|
||||
$('#summernote').summernote('code', '');
|
||||
}
|
||||
Tracker.flush();
|
||||
autosize.update(input);
|
||||
input.trigger('submitted');
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
template(name="editor")
|
||||
// With Richer editor is in use, and comment is submitted,
|
||||
// clear comment form with JQuery Comment at
|
||||
// client/components/activities/comments.js . Id #summernote is defined
|
||||
// here at client/components/main/editor.jade where it previously was
|
||||
// id=id, now it is id="summernote".
|
||||
textarea.editor(
|
||||
dir="auto"
|
||||
class="{{class}}"
|
||||
id=id
|
||||
id="summernote"
|
||||
autofocus=autofocus
|
||||
placeholder="{{_ 'comment-placeholder'}}")
|
||||
+Template.contentBlock
|
||||
|
|
|
@ -1,87 +1,3 @@
|
|||
import _sanitizeXss from 'xss';
|
||||
const ASIS = 'asis';
|
||||
const sanitizeXss = (input, options) => {
|
||||
const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i;
|
||||
const allowedIframeSrcRegex = (function() {
|
||||
let reg = defaultAllowedIframeSrc;
|
||||
const SAFE_IFRAME_SRC_PATTERN =
|
||||
Meteor.settings.public.SAFE_IFRAME_SRC_PATTERN;
|
||||
try {
|
||||
if (SAFE_IFRAME_SRC_PATTERN !== undefined) {
|
||||
reg = new RegExp(SAFE_IFRAME_SRC_PATTERN, 'i');
|
||||
}
|
||||
} catch (e) {
|
||||
/*eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
||||
|
||||
console.error('Wrong pattern specified', SAFE_IFRAM_SRC_PATTERN, e);
|
||||
}
|
||||
return reg;
|
||||
})();
|
||||
const targetWindow = '_blank';
|
||||
const getHtmlDOM = html => {
|
||||
const i = document.createElement('i');
|
||||
i.innerHTML = html;
|
||||
return i.firstChild;
|
||||
};
|
||||
options = {
|
||||
onTag(tag, html, options) {
|
||||
const htmlDOM = getHtmlDOM(html);
|
||||
const getAttr = attr => {
|
||||
return htmlDOM && attr && htmlDOM.getAttribute(attr);
|
||||
};
|
||||
if (tag === 'iframe') {
|
||||
const clipCls = 'note-vide-clip';
|
||||
if (!options.isClosing) {
|
||||
const iframeCls = getAttr('class');
|
||||
let safe = iframeCls.indexOf(clipCls) > -1;
|
||||
const src = getAttr('src');
|
||||
if (allowedIframeSrcRegex.exec(src)) {
|
||||
safe = true;
|
||||
}
|
||||
if (safe)
|
||||
return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`;
|
||||
} else {
|
||||
// remove </iframe> tag
|
||||
return '';
|
||||
}
|
||||
} else if (tag === 'a') {
|
||||
if (!options.isClosing) {
|
||||
if (getAttr(ASIS) === 'true') {
|
||||
// if has a ASIS attribute, don't do anything, it's a member id
|
||||
return html;
|
||||
} else {
|
||||
const href = getAttr('href');
|
||||
if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) {
|
||||
// a valid url
|
||||
return `<a href=${href} target=${targetWindow}>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (tag === 'img') {
|
||||
if (!options.isClosing) {
|
||||
const src = getAttr('src');
|
||||
if (src) {
|
||||
return `<a href='${src}' class='swipebox'><img src='${src}' class="attachment-image-preview mCS_img_loaded"></a>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onTagAttr(tag, name, value) {
|
||||
if (tag === 'img' && name === 'src') {
|
||||
if (value && value.substr(0, 5) === 'data:') {
|
||||
// allow image with dataURI src
|
||||
return `${name}='${value}'`;
|
||||
}
|
||||
} else if (tag === 'a' && name === 'target') {
|
||||
return `${name}='${targetWindow}'`; // always change a href target to a new window
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
...options,
|
||||
};
|
||||
return _sanitizeXss(input, options);
|
||||
};
|
||||
Template.editor.onRendered(() => {
|
||||
const textareaSelector = 'textarea';
|
||||
const mentions = [
|
||||
|
@ -94,13 +10,7 @@ Template.editor.onRendered(() => {
|
|||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const user = Users.findOne(member.userId);
|
||||
if (user._id === Meteor.userId()) {
|
||||
return null;
|
||||
}
|
||||
const value = user.username;
|
||||
const username =
|
||||
value && value.match(/\s+/) ? `"${value}"` : value;
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
})
|
||||
.filter(Boolean),
|
||||
|
@ -120,16 +30,15 @@ Template.editor.onRendered(() => {
|
|||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) {
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
['view', ['fullscreen']],
|
||||
['table', ['table']],
|
||||
['font', ['bold']],
|
||||
['color', ['color']],
|
||||
['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
['font', ['bold', 'underline']],
|
||||
//['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
]
|
||||
: [
|
||||
['style', ['style']],
|
||||
|
@ -139,11 +48,47 @@ Template.editor.onRendered(() => {
|
|||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['insert', ['link', 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = sanitizeXss;
|
||||
const cleanPastedHTML = function(input) {
|
||||
const badTags = [
|
||||
'style',
|
||||
'script',
|
||||
'applet',
|
||||
'embed',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'meta',
|
||||
'link',
|
||||
'button',
|
||||
'form',
|
||||
].join('|');
|
||||
const badPatterns = new RegExp(
|
||||
`(?:${[
|
||||
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
|
||||
`<(${badTags})[^>]*?\\/>`,
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
let output = input;
|
||||
// remove bad Tags
|
||||
output = output.replace(badPatterns, '');
|
||||
// remove attributes ' style="..."'
|
||||
const badAttributes = new RegExp(
|
||||
`(?:${[
|
||||
'on\\S+=([\'"]?).*?\\1',
|
||||
'href=([\'"]?)javascript:.*?\\2',
|
||||
'style=([\'"]?).*?\\3',
|
||||
'target=\\S+',
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
output = output.replace(badAttributes, '');
|
||||
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
|
||||
return output;
|
||||
};
|
||||
const editor = '.editor';
|
||||
const selectors = [
|
||||
`.js-new-comment-form ${editor}`,
|
||||
|
@ -163,11 +108,27 @@ Template.editor.onRendered(() => {
|
|||
}
|
||||
return undefined;
|
||||
};
|
||||
// Prevent @member mentions on Add Comment input field
|
||||
// from closing card, part 1.
|
||||
let popupShown = false;
|
||||
inputs.each(function(idx, input) {
|
||||
mSummernotes[idx] = $(input).summernote({
|
||||
placeholder,
|
||||
// Prevent @member mentions on Add Comment input field
|
||||
// from closing card, part 2.
|
||||
onKeydown(e) {
|
||||
if (popupShown) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onKeyup(e) {
|
||||
if (popupShown) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
// Prevent @member mentions on Add Comment input field
|
||||
// from closing card, part 3.
|
||||
onKeydown(e) {
|
||||
if (popupShown) {
|
||||
e.preventDefault();
|
||||
|
@ -180,28 +141,19 @@ Template.editor.onRendered(() => {
|
|||
},
|
||||
onInit(object) {
|
||||
const originalInput = this;
|
||||
const setAutocomplete = function(jEditor) {
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions).on({
|
||||
'textComplete:show'() {
|
||||
popupShown = true;
|
||||
},
|
||||
'textComplete:hide'() {
|
||||
popupShown = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
$(originalInput).on('submitted', function() {
|
||||
// resetCommentInput has been called
|
||||
$(originalInput).on('input', function() {
|
||||
// when comment is submitted, the original textarea will be set to '', so shall we
|
||||
if (!this.value) {
|
||||
const sn = getSummernote(this);
|
||||
sn && sn.summernote('code', '');
|
||||
sn && sn.summernote('reset');
|
||||
object && object.editingArea.find('.note-placeholder').show();
|
||||
}
|
||||
});
|
||||
const jEditor = object && object.editable;
|
||||
const toolbar = object && object.toolbar;
|
||||
setAutocomplete(jEditor);
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions);
|
||||
}
|
||||
if (toolbar !== undefined) {
|
||||
const fBtn = toolbar.find('.btn-fullscreen');
|
||||
fBtn.on('click', function() {
|
||||
|
@ -289,9 +241,15 @@ Template.editor.onRendered(() => {
|
|||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
const someNote = getSummernote(object);
|
||||
// Fix Pasting text into a card is adding a line before and after
|
||||
// (and multiplies by pasting more) by changing paste "p" to "br".
|
||||
// Fixes https://github.com/wekan/wekan/2890 .
|
||||
// == Fix Start ==
|
||||
someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
// == Fix End ==
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
someNote.summernote('code', ''); //clear original
|
||||
someNote.summernote('reset'); //clear original
|
||||
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
|
||||
};
|
||||
setTimeout(function() {
|
||||
|
@ -331,6 +289,8 @@ Template.editor.onRendered(() => {
|
|||
}
|
||||
});
|
||||
|
||||
import sanitizeXss from 'xss';
|
||||
|
||||
// XXX I believe we should compute a HTML rendered field on the server that
|
||||
// would handle markdown and user mentions. We can simply have two
|
||||
// fields, one source, and one compiled version (in HTML) and send only the
|
||||
|
@ -352,23 +312,28 @@ Blaze.Template.registerHelper(
|
|||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
const [fullMention, quoteduser, simple] = currentMention;
|
||||
const username = quoteduser || simple;
|
||||
const [fullMention, username] = currentMention;
|
||||
const knowedUser = _.findWhere(knowedUsers, { username });
|
||||
if (!knowedUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const linkValue = [' ', at, knowedUser.username];
|
||||
let linkClass = 'atMention js-open-member';
|
||||
//let linkClass = 'atMention js-open-member';
|
||||
let linkClass = 'atMention';
|
||||
if (knowedUser.userId === Meteor.userId()) {
|
||||
linkClass += ' me';
|
||||
}
|
||||
const link = HTML.A(
|
||||
// This @user mention link generation did open same Wekan
|
||||
// window in new tab, so now A is changed to U so it's
|
||||
// underlined and there is no link popup. This way also
|
||||
// text can be selected more easily.
|
||||
//const link = HTML.A(
|
||||
const link = HTML.U(
|
||||
{
|
||||
class: linkClass,
|
||||
// XXX Hack. Since we stringify this render function result below with
|
||||
|
@ -376,42 +341,41 @@ Blaze.Template.registerHelper(
|
|||
// `userId` to the popup as usual, and we need to store it in the DOM
|
||||
// using a data attribute.
|
||||
'data-userId': knowedUser.userId,
|
||||
[ASIS]: 'true',
|
||||
},
|
||||
linkValue,
|
||||
);
|
||||
|
||||
content = content.replace(fullMention, Blaze.toHTML(link));
|
||||
}
|
||||
|
||||
return HTML.Raw(sanitizeXss(content));
|
||||
}),
|
||||
);
|
||||
|
||||
Template.viewer.events({
|
||||
// Viewer sometimes have click-able wrapper around them (for instance to edit
|
||||
// the corresponding text). Clicking a link shouldn't fire these actions, stop
|
||||
// we stop these event at the viewer component level.
|
||||
'click a'(event, templateInstance) {
|
||||
let prevent = true;
|
||||
event.stopPropagation();
|
||||
|
||||
// XXX We hijack the build-in browser action because we currently don't have
|
||||
// `_blank` attributes in viewer links, and the transformer function is
|
||||
// handled by a third party package that we can't configure easily. Fix that
|
||||
// by using directly `_blank` attribute in the rendered HTML.
|
||||
event.preventDefault();
|
||||
|
||||
const userId = event.currentTarget.dataset.userid;
|
||||
if (userId) {
|
||||
Popup.open('member').call({ userId }, event, templateInstance);
|
||||
// Prevent @member mentions on Add Comment input field
|
||||
// from closing card, part 4.
|
||||
PopupNoClose.open('member').call({ userId }, event, templateInstance);
|
||||
event.preventDefault();
|
||||
} else {
|
||||
const href = event.currentTarget.href;
|
||||
const child = event.currentTarget.firstElementChild;
|
||||
if (child && child.tagName === 'IMG') {
|
||||
prevent = false;
|
||||
} else if (href) {
|
||||
if (href) {
|
||||
window.open(href, '_blank');
|
||||
}
|
||||
}
|
||||
if (prevent) {
|
||||
event.stopPropagation();
|
||||
|
||||
// XXX We hijack the build-in browser action because we currently don't have
|
||||
// `_blank` attributes in viewer links, and the transformer function is
|
||||
// handled by a third party package that we can't configure easily. Fix that
|
||||
// by using directly `_blank` attribute in the rendered HTML.
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -110,7 +110,7 @@ template(name="editUserPopup")
|
|||
label.hide.userId(type="text" value=user._id)
|
||||
label
|
||||
| {{_ 'fullname'}}
|
||||
input.js-profile-fullname(type="text" value=user.profile.fullname autofocus)
|
||||
input.js-profile-fullname(type="text" value=user.profile.fullname)
|
||||
label
|
||||
| {{_ 'username'}}
|
||||
span.error.hide.username-taken
|
||||
|
@ -159,7 +159,7 @@ template(name="newUserPopup")
|
|||
//label.hide.userId(type="text" value=user._id)
|
||||
label
|
||||
| {{_ 'fullname'}}
|
||||
input.js-profile-fullname(type="text" value="" autofocus)
|
||||
input.js-profile-fullname(type="text" value="")
|
||||
label
|
||||
| {{_ 'username'}}
|
||||
span.error.hide.username-taken
|
||||
|
|
|
@ -245,7 +245,7 @@ template(name="outgoingWebhooksPopup")
|
|||
b
|
||||
.materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
|
||||
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
|
||||
input.js-outgoing-webhooks-url(type="text" name="url" value=url autofocus)
|
||||
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
|
||||
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
|
||||
select.js-outgoing-webhooks-type(name="type")
|
||||
each _type in types
|
||||
|
@ -257,7 +257,7 @@ template(name="outgoingWebhooksPopup")
|
|||
input(type="hidden" value=_id name="id")
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
form.integration-form
|
||||
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus)
|
||||
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title")
|
||||
input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
|
||||
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
|
||||
select.js-outgoing-webhooks-type(name="type")
|
||||
|
@ -267,7 +267,10 @@ template(name="outgoingWebhooksPopup")
|
|||
|
||||
template(name="boardMenuPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-custom-fields {{_ 'custom-fields'}}
|
||||
li
|
||||
a.js-custom-fields
|
||||
i.fa.fa-list-alt
|
||||
| {{_ 'custom-fields'}}
|
||||
li
|
||||
a.js-open-archives
|
||||
i.fa.fa-archive
|
||||
|
|
|
@ -98,12 +98,12 @@ template(name="changeLanguagePopup")
|
|||
|
||||
template(name="changeSettingsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-toggle-system-messages
|
||||
i.fa.fa-comments-o
|
||||
| {{_ 'hide-system-messages'}}
|
||||
if hiddenSystemMessages
|
||||
i.fa.fa-check
|
||||
//li
|
||||
// a.js-toggle-system-messages
|
||||
// i.fa.fa-comments-o
|
||||
// | {{_ 'hide-system-messages'}}
|
||||
// if hiddenSystemMessages
|
||||
// i.fa.fa-check
|
||||
li
|
||||
a.js-toggle-desktop-drag-handles
|
||||
i.fa.fa-arrows
|
||||
|
|
|
@ -206,3 +206,207 @@ escapeActions.forEach(actionName => {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Prevent @member mentions on Add Comment input field
|
||||
// from closing card, part 5.
|
||||
// This duplicate below of above popup function is needed, because at
|
||||
// wekan/components/main/editor.js at bottom is popping up visible
|
||||
// @member mention, and it seems to trigger closing also card popup,
|
||||
// so in below closing popup is disabled.
|
||||
window.PopupNoClose = new (class {
|
||||
constructor() {
|
||||
// The template we use to render popups
|
||||
this.template = Template.popup;
|
||||
|
||||
// We only want to display one popup at a time and we keep the view object
|
||||
// in this `Popup.current` variable. If there is no popup currently opened
|
||||
// the value is `null`.
|
||||
this.current = null;
|
||||
|
||||
// It's possible to open a sub-popup B from a popup A. In that case we keep
|
||||
// the data of popup A so we can return back to it. Every time we open a new
|
||||
// popup the stack grows, every time we go back the stack decrease, and if
|
||||
// we close the popup the stack is reseted to the empty stack [].
|
||||
this._stack = [];
|
||||
|
||||
// We invalidate this internal dependency every time the top of the stack
|
||||
// has changed and we want to re-render a popup with the new top-stack data.
|
||||
this._dep = new Tracker.Dependency();
|
||||
}
|
||||
|
||||
/// This function returns a callback that can be used in an event map:
|
||||
/// Template.tplName.events({
|
||||
/// 'click .elementClass': Popup.open("popupName"),
|
||||
/// });
|
||||
/// The popup inherit the data context of its parent.
|
||||
open(name) {
|
||||
const self = this;
|
||||
const popupName = `${name}Popup`;
|
||||
function clickFromPopup(evt) {
|
||||
return $(evt.target).closest('.js-pop-over').length !== 0;
|
||||
}
|
||||
return function(evt) {
|
||||
// If a popup is already opened, clicking again on the opener element
|
||||
// should close it -- and interrupt the current `open` function.
|
||||
/*
|
||||
if (self.isOpen()) {
|
||||
const previousOpenerElement = self._getTopStack().openerElement;
|
||||
if (previousOpenerElement === evt.currentTarget) {
|
||||
self.close();
|
||||
return;
|
||||
} else {
|
||||
$(previousOpenerElement).removeClass('is-active');
|
||||
}
|
||||
}
|
||||
*/
|
||||
// We determine the `openerElement` (the DOM element that is being clicked
|
||||
// and the one we take in reference to position the popup) from the event
|
||||
// if the popup has no parent, or from the parent `openerElement` if it
|
||||
// has one. This allows us to position a sub-popup exactly at the same
|
||||
// position than its parent.
|
||||
let openerElement;
|
||||
if (clickFromPopup(evt)) {
|
||||
openerElement = self._getTopStack().openerElement;
|
||||
} else {
|
||||
self._stack = [];
|
||||
openerElement = evt.currentTarget;
|
||||
}
|
||||
$(openerElement).addClass('is-active');
|
||||
evt.preventDefault();
|
||||
|
||||
// We push our popup data to the stack. The top of the stack is always
|
||||
// used as the data source for our current popup.
|
||||
self._stack.push({
|
||||
popupName,
|
||||
openerElement,
|
||||
hasPopupParent: clickFromPopup(evt),
|
||||
title: self._getTitle(popupName),
|
||||
depth: self._stack.length,
|
||||
offset: self._getOffset(openerElement),
|
||||
dataContext: (this && this.currentData && this.currentData()) || this,
|
||||
});
|
||||
|
||||
// If there are no popup currently opened we use the Blaze API to render
|
||||
// one into the DOM. We use a reactive function as the data parameter that
|
||||
// return the complete along with its top element and depends on our
|
||||
// internal dependency that is being invalidated every time the top
|
||||
// element of the stack has changed and we want to update the popup.
|
||||
//
|
||||
// Otherwise if there is already a popup open we just need to invalidate
|
||||
// our internal dependency, and since we just changed the top element of
|
||||
// our internal stack, the popup will be updated with the new data.
|
||||
if (!self.isOpen()) {
|
||||
self.current = Blaze.renderWithData(
|
||||
self.template,
|
||||
() => {
|
||||
self._dep.depend();
|
||||
return { ...self._getTopStack(), stack: self._stack };
|
||||
},
|
||||
document.body,
|
||||
);
|
||||
} else {
|
||||
self._dep.changed();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This function returns a callback that can be used in an event map:
|
||||
/// Template.tplName.events({
|
||||
/// 'click .elementClass': Popup.afterConfirm("popupName", function() {
|
||||
/// // What to do after the user has confirmed the action
|
||||
/// }),
|
||||
/// });
|
||||
afterConfirm(name, action) {
|
||||
const self = this;
|
||||
|
||||
return function(evt, tpl) {
|
||||
const context = (this.currentData && this.currentData()) || this;
|
||||
context.__afterConfirmAction = action;
|
||||
self.open(name).call(context, evt, tpl);
|
||||
};
|
||||
}
|
||||
|
||||
/// The public reactive state of the popup.
|
||||
isOpen() {
|
||||
this._dep.changed();
|
||||
return Boolean(this.current);
|
||||
}
|
||||
|
||||
/// In case the popup was opened from a parent popup we can get back to it
|
||||
/// with this `Popup.back()` function. You can go back several steps at once
|
||||
/// by providing a number to this function, e.g. `Popup.back(2)`. In this case
|
||||
/// intermediate popup won't even be rendered on the DOM. If the number of
|
||||
/// steps back is greater than the popup stack size, the popup will be closed.
|
||||
back(n = 1) {
|
||||
if (this._stack.length > n) {
|
||||
_.times(n, () => this._stack.pop());
|
||||
this._dep.changed();
|
||||
}
|
||||
// else {
|
||||
// this.close();
|
||||
//}
|
||||
}
|
||||
|
||||
/// Close the current opened popup.
|
||||
/*
|
||||
close() {
|
||||
if (this.isOpen()) {
|
||||
Blaze.remove(this.current);
|
||||
this.current = null;
|
||||
|
||||
const openerElement = this._getTopStack().openerElement;
|
||||
$(openerElement).removeClass('is-active');
|
||||
|
||||
this._stack = [];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
getOpenerComponent() {
|
||||
const { openerElement } = Template.parentData(4);
|
||||
return BlazeComponent.getComponentForElement(openerElement);
|
||||
}
|
||||
|
||||
// An utility fonction that returns the top element of the internal stack
|
||||
_getTopStack() {
|
||||
return this._stack[this._stack.length - 1];
|
||||
}
|
||||
|
||||
// We automatically calculate the popup offset from the reference element
|
||||
// position and dimensions. We also reactively use the window dimensions to
|
||||
// ensure that the popup is always visible on the screen.
|
||||
_getOffset(element) {
|
||||
const $element = $(element);
|
||||
return () => {
|
||||
Utils.windowResizeDep.depend();
|
||||
|
||||
if (Utils.isMiniScreen()) return { left: 0, top: 0 };
|
||||
|
||||
const offset = $element.offset();
|
||||
const popupWidth = 300 + 15;
|
||||
return {
|
||||
left: Math.min(offset.left, $(window).width() - popupWidth),
|
||||
top: offset.top + $element.outerHeight(),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// We get the title from the translation files. Instead of returning the
|
||||
// result, we return a function that compute the result and since `TAPi18n.__`
|
||||
// is a reactive data source, the title will be changed reactively.
|
||||
_getTitle(popupName) {
|
||||
return () => {
|
||||
const translationKey = `${popupName}-title`;
|
||||
|
||||
// XXX There is no public API to check if there is an available
|
||||
// translation for a given key. So we try to translate the key and if the
|
||||
// translation output equals the key input we deduce that no translation
|
||||
// was available and returns `false`. There is a (small) risk a false
|
||||
// positives.
|
||||
const title = TAPi18n.__(translationKey);
|
||||
// when popup showed as full of small screen, we need a default header to clearly see [X] button
|
||||
const defaultTitle = Utils.isMiniScreen() ? '' : false;
|
||||
return title !== translationKey ? title : defaultTitle;
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -240,6 +240,11 @@ services:
|
|||
# https://github.com/wekan/wekan/pull/2560
|
||||
- RICHER_CARD_COMMENT_EDITOR=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== MOUSE SCROLL ====
|
||||
# https://github.com/wekan/wekan/issues/2949
|
||||
- SCROLLINERTIA=0
|
||||
- SCROLLAMOUNT=auto
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
|
||||
# https://github.com/wekan/wekan/issues/2518
|
||||
- CARD_OPENED_WEBHOOK_ENABLED=false
|
||||
|
@ -504,18 +509,22 @@ services:
|
|||
# The limit number of entries (0=unlimited)
|
||||
#- LDAP_SEARCH_SIZE_LIMIT=0
|
||||
#
|
||||
# Enable group filtering
|
||||
# Enable group filtering. Note the authenticated ldap user must be able to query all relevant group data with own login data from ldap.
|
||||
#- LDAP_GROUP_FILTER_ENABLE=false
|
||||
#
|
||||
# The object class for filtering. Example: group
|
||||
#- LDAP_GROUP_FILTER_OBJECTCLASS=
|
||||
#
|
||||
# The attribute of a group identifying it. Example: cn
|
||||
#- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
|
||||
#
|
||||
# The attribute inside a group object listing its members. Example: member
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
|
||||
#
|
||||
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn ist saved as value into the attribute.
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
|
||||
#
|
||||
# The group name (id) that matches all users.
|
||||
#- LDAP_GROUP_FILTER_GROUP_NAME=
|
||||
#
|
||||
# LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
|
||||
|
|
|
@ -757,7 +757,7 @@
|
|||
"cardAssigneesPopup-title": "Zugewiesen",
|
||||
"addmore-detail": "Eine detailliertere Beschreibung hinzufügen",
|
||||
"show-on-card": "Zeige auf Karte",
|
||||
"new": "New",
|
||||
"editUserPopup-title": "Edit User",
|
||||
"newUserPopup-title": "New User"
|
||||
"new": "Neu",
|
||||
"editUserPopup-title": "Benutzer ändern",
|
||||
"newUserPopup-title": "Neuer Benutzer"
|
||||
}
|
||||
|
|
|
@ -731,8 +731,8 @@
|
|||
"restore-all": "Restore all",
|
||||
"delete-all": "Cancella tutto",
|
||||
"loading": "Loading, please wait.",
|
||||
"previous_as": "last time was",
|
||||
"act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__",
|
||||
"previous_as": "l'ultima volta è stata",
|
||||
"act-a-dueAt": "Scadenza modificata in __timeValue__\nData precedente: __timeOldValue__",
|
||||
"act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)",
|
||||
"act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)",
|
||||
"act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)",
|
||||
|
@ -740,15 +740,15 @@
|
|||
"a-endAt": "modified ending time to be",
|
||||
"a-startAt": "modified starting time to be",
|
||||
"a-receivedAt": "modified received time to be",
|
||||
"almostdue": "current due time %s is approaching",
|
||||
"pastdue": "current due time %s is past",
|
||||
"duenow": "current due time %s is today",
|
||||
"almostdue": "la data di scadenza attuale %s si sta avvicinando",
|
||||
"pastdue": "la data di scadenza attuale %s è scaduta",
|
||||
"duenow": "la data di scadenza attuale %s è oggi",
|
||||
"act-newDue": "__list__/__card__ has 1st due reminder [__board__]",
|
||||
"act-withDue": "__list__/__card__ due reminders [__board__]",
|
||||
"act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching",
|
||||
"act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
|
||||
"act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
|
||||
"act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
|
||||
"act-atUserComment": "Sei stato menzionato in [__board__] __list__/__card__",
|
||||
"delete-user-confirm-popup": "Sei sicuro di voler cancellare questo profilo? Non sarà possibile ripristinarlo.",
|
||||
"accounts-allowUserDelete": "Permetti agli utenti di cancellare il loro profilo",
|
||||
"hide-minicard-label-text": "Nascondi etichetta minicard",
|
||||
|
|
|
@ -757,7 +757,7 @@
|
|||
"cardAssigneesPopup-title": "担当者",
|
||||
"addmore-detail": "詳細説明の追加",
|
||||
"show-on-card": "カードに表示する項目",
|
||||
"new": "New",
|
||||
"editUserPopup-title": "Edit User",
|
||||
"newUserPopup-title": "New User"
|
||||
"new": "新規作成",
|
||||
"editUserPopup-title": "ユーザーを編集",
|
||||
"newUserPopup-title": "新規ユーザー"
|
||||
}
|
||||
|
|
|
@ -583,9 +583,9 @@
|
|||
"default": "Standaard",
|
||||
"queue": "Rij",
|
||||
"subtask-settings": "Subtaak Instellingen",
|
||||
"card-settings": "Card Settings",
|
||||
"card-settings": "Kaart Instellingen",
|
||||
"boardSubtaskSettingsPopup-title": "Bord Subtaak Instellingen",
|
||||
"boardCardSettingsPopup-title": "Card Settings",
|
||||
"boardCardSettingsPopup-title": "Kaart Instellingen",
|
||||
"deposit-subtasks-board": "Plaats subtaken op dit bord:",
|
||||
"deposit-subtasks-list": "Plaats subtaken in deze lijst:",
|
||||
"show-parent-in-minicard": "Toon bron in minikaart:",
|
||||
|
@ -755,9 +755,9 @@
|
|||
"show-desktop-drag-handles": "Toon sleep gereedschap op werkblad",
|
||||
"assignee": "Toegewezen aan",
|
||||
"cardAssigneesPopup-title": "Toegewezen aan",
|
||||
"addmore-detail": "Add a more detailed description",
|
||||
"show-on-card": "Show on Card",
|
||||
"new": "New",
|
||||
"editUserPopup-title": "Edit User",
|
||||
"newUserPopup-title": "New User"
|
||||
"addmore-detail": "Voeg een meer gedetailleerde beschrijving toe",
|
||||
"show-on-card": "Toon op kaart",
|
||||
"new": "Nieuw",
|
||||
"editUserPopup-title": "Wijzig gebruiker",
|
||||
"newUserPopup-title": "Nieuwe gebruiker"
|
||||
}
|
||||
|
|
|
@ -757,7 +757,7 @@
|
|||
"cardAssigneesPopup-title": "Przypisujący",
|
||||
"addmore-detail": "Dodaj bardziej szczegółowy opis",
|
||||
"show-on-card": "Pokaż na karcie",
|
||||
"new": "New",
|
||||
"editUserPopup-title": "Edit User",
|
||||
"newUserPopup-title": "New User"
|
||||
"new": "Nowy",
|
||||
"editUserPopup-title": "Edytuj użytkownika",
|
||||
"newUserPopup-title": "Nowy użytkownik"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"accept": "Accept",
|
||||
"accept": "Aceptă",
|
||||
"act-activity-notify": "Activity Notification",
|
||||
"act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
|
@ -42,23 +42,23 @@
|
|||
"act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-withBoardTitle": "__board__",
|
||||
"act-withCardTitle": "[__board__] __card__",
|
||||
"actions": "Actions",
|
||||
"activities": "Activities",
|
||||
"activity": "Activity",
|
||||
"activity-added": "added %s to %s",
|
||||
"actions": "Acțiuni",
|
||||
"activities": "Activități",
|
||||
"activity": "Activitate",
|
||||
"activity-added": "s-a adăugat %s la %s",
|
||||
"activity-archived": "%s moved to Archive",
|
||||
"activity-attached": "attached %s to %s",
|
||||
"activity-created": "created %s",
|
||||
"activity-attached": "s-a atașat %s la %s",
|
||||
"activity-created": "s-a creat %s",
|
||||
"activity-customfield-created": "created custom field %s",
|
||||
"activity-excluded": "excluded %s from %s",
|
||||
"activity-imported": "imported %s into %s from %s",
|
||||
"activity-imported-board": "imported %s from %s",
|
||||
"activity-joined": "joined %s",
|
||||
"activity-moved": "moved %s from %s to %s",
|
||||
"activity-excluded": "s-a exclus %s din %s",
|
||||
"activity-imported": "s-a importat %s în %s din %s",
|
||||
"activity-imported-board": "s-a importat %s din %s",
|
||||
"activity-joined": "ai devenit membru %s",
|
||||
"activity-moved": "s-a mutat %s din %s în%s",
|
||||
"activity-on": "on %s",
|
||||
"activity-removed": "removed %s from %s",
|
||||
"activity-sent": "sent %s to %s",
|
||||
"activity-unjoined": "unjoined %s",
|
||||
"activity-removed": "s-a șters %s din %s",
|
||||
"activity-sent": "s-a trimis %s către%s",
|
||||
"activity-unjoined": "nu mai ești membru al %s",
|
||||
"activity-subtask-added": "added subtask to %s",
|
||||
"activity-checked-item": "checked %s in checklist %s of %s",
|
||||
"activity-unchecked-item": "unchecked %s in checklist %s of %s",
|
||||
|
@ -68,7 +68,7 @@
|
|||
"activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
|
||||
"activity-checklist-item-added": "added checklist item to '%s' in %s",
|
||||
"activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
|
||||
"add": "Add",
|
||||
"add": "Adaugă",
|
||||
"activity-checked-item-card": "checked %s in checklist %s",
|
||||
"activity-unchecked-item-card": "unchecked %s in checklist %s",
|
||||
"activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
|
@ -82,20 +82,20 @@
|
|||
"add-subtask": "Add Subtask",
|
||||
"add-checklist": "Add Checklist",
|
||||
"add-checklist-item": "Add an item to checklist",
|
||||
"add-cover": "Add Cover",
|
||||
"add-cover": "Adaugă Decor",
|
||||
"add-label": "Add Label",
|
||||
"add-list": "Add List",
|
||||
"add-members": "Add Members",
|
||||
"added": "Added",
|
||||
"addMemberPopup-title": "Members",
|
||||
"add-members": "Adaugă Membrii",
|
||||
"added": "S-a adăugat",
|
||||
"addMemberPopup-title": "Membrii",
|
||||
"admin": "Admin",
|
||||
"admin-desc": "Can view and edit cards, remove members, and change settings for the board.",
|
||||
"admin-desc": "Poate vedea și edita carduri, șterge membrii, și schimba setările tablei.",
|
||||
"admin-announcement": "Announcement",
|
||||
"admin-announcement-active": "Active System-Wide Announcement",
|
||||
"admin-announcement-title": "Announcement from Administrator",
|
||||
"all-boards": "All boards",
|
||||
"and-n-other-card": "And __count__ other card",
|
||||
"and-n-other-card_plural": "And __count__ other cards",
|
||||
"all-boards": "Toate tablele",
|
||||
"and-n-other-card": "Și __count__ alt card",
|
||||
"and-n-other-card_plural": "Și __count__ alte carduri",
|
||||
"apply": "Apply",
|
||||
"app-is-offline": "Loading, please wait. Refreshing the page will cause data loss. If loading does not work, please check that server has not stopped.",
|
||||
"archive": "Move to Archive",
|
||||
|
@ -106,54 +106,54 @@
|
|||
"archive-swimlane": "Move Swimlane to Archive",
|
||||
"archive-selection": "Move selection to Archive",
|
||||
"archiveBoardPopup-title": "Move Board to Archive?",
|
||||
"archived-items": "Archive",
|
||||
"archived-items": "Arhivă",
|
||||
"archived-boards": "Boards in Archive",
|
||||
"restore-board": "Restore Board",
|
||||
"no-archived-boards": "No Boards in Archive.",
|
||||
"archives": "Archive",
|
||||
"template": "Template",
|
||||
"templates": "Templates",
|
||||
"assign-member": "Assign member",
|
||||
"attached": "attached",
|
||||
"assign-member": "Atribuie unui membru",
|
||||
"attached": "s-a atașat",
|
||||
"attachment": "Ataşament",
|
||||
"attachment-delete-pop": "Deleting an attachment is permanent. There is no undo.",
|
||||
"attachmentDeletePopup-title": "Delete Attachment?",
|
||||
"attachment-delete-pop": "Ștergerea unui atașament este permanentă. Nu există recuperare.",
|
||||
"attachmentDeletePopup-title": "Șterge Atașament?",
|
||||
"attachments": "Ataşamente",
|
||||
"auto-watch": "Automatically watch boards when they are created",
|
||||
"avatar-too-big": "The avatar is too large (70KB max)",
|
||||
"back": "Înapoi",
|
||||
"board-change-color": "Change color",
|
||||
"board-nb-stars": "%s stars",
|
||||
"board-not-found": "Board not found",
|
||||
"board-private-info": "This board will be <strong>private</strong>.",
|
||||
"board-public-info": "This board will be <strong>public</strong>.",
|
||||
"boardChangeColorPopup-title": "Change Board Background",
|
||||
"boardChangeTitlePopup-title": "Rename Board",
|
||||
"boardChangeVisibilityPopup-title": "Change Visibility",
|
||||
"board-change-color": "Schimbă culoare",
|
||||
"board-nb-stars": "%s stele",
|
||||
"board-not-found": "Tabla nu a fost găsită",
|
||||
"board-private-info": "Această tabla va fi <strong>privată</strong>.",
|
||||
"board-public-info": "Această tabla va fi <strong>publică</strong>.",
|
||||
"boardChangeColorPopup-title": "Schimbă Fundalul Tablei",
|
||||
"boardChangeTitlePopup-title": "Redenumește Tabla",
|
||||
"boardChangeVisibilityPopup-title": "Schimbă Vizibilitatea",
|
||||
"boardChangeWatchPopup-title": "Change Watch",
|
||||
"boardMenuPopup-title": "Board Settings",
|
||||
"boardChangeViewPopup-title": "Board View",
|
||||
"boards": "Boards",
|
||||
"boards": "Table",
|
||||
"board-view": "Board View",
|
||||
"board-view-cal": "Calendar",
|
||||
"board-view-swimlanes": "Swimlanes",
|
||||
"board-view-collapse": "Collapse",
|
||||
"board-view-lists": "Liste",
|
||||
"bucket-example": "Like “Bucket List” for example",
|
||||
"cancel": "Cancel",
|
||||
"cancel": "Anulează",
|
||||
"card-archived": "This card is moved to Archive.",
|
||||
"board-archived": "This board is moved to Archive.",
|
||||
"card-comments-title": "This card has %s comment.",
|
||||
"card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
|
||||
"card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
|
||||
"card-comments-title": "Acest card are %s commentariu.",
|
||||
"card-delete-notice": "Ștergerea este permanentă. Se vor pierde toate acțiunile asociate acestui card.",
|
||||
"card-delete-pop": "Toate acțiunile vor fi șterse din jurnalul de activități si nu vei mai putea redeschide cardul. Nu există recuperare.",
|
||||
"card-delete-suggest-archive": "You can move a card to Archive to remove it from the board and preserve the activity.",
|
||||
"card-due": "Due",
|
||||
"card-due-on": "Due on",
|
||||
"card-spent": "Spent Time",
|
||||
"card-edit-attachments": "Edit attachments",
|
||||
"card-edit-attachments": "Editează atașamente",
|
||||
"card-edit-custom-fields": "Edit custom fields",
|
||||
"card-edit-labels": "Edit labels",
|
||||
"card-edit-members": "Edit members",
|
||||
"card-edit-labels": "Editează etichete",
|
||||
"card-edit-members": "Editează membrii",
|
||||
"card-labels-title": "Change the labels for the card.",
|
||||
"card-members-title": "Add or remove members of the board from the card.",
|
||||
"card-start": "Start",
|
||||
|
|
|
@ -757,7 +757,7 @@
|
|||
"cardAssigneesPopup-title": "Dodeljen član",
|
||||
"addmore-detail": "Dodaj podrobnejši opis",
|
||||
"show-on-card": "Prikaži na kartici",
|
||||
"new": "New",
|
||||
"editUserPopup-title": "Edit User",
|
||||
"newUserPopup-title": "New User"
|
||||
"new": "Novo",
|
||||
"editUserPopup-title": "Uredi uporabnika",
|
||||
"newUserPopup-title": "Nov uporabnik"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"accept": "Chấp nhận",
|
||||
"act-activity-notify": "Activity Notification",
|
||||
"act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-activity-notify": "Thông báo hoạt động",
|
||||
"act-addAttachment": "thêm tập tin đính kèm __attachment__ vào thẻ __list__ tại danh sách __list__ tại đường bơi __swimlane__ tại bảng __board__",
|
||||
"act-deleteAttachment": "xóa tập tin đính kèm __attachment__ tại thẻ __card__ tại danh sách __list__ tại đường bơi __swimlane__ tại bảng __board__ ",
|
||||
"act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
"act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
|
||||
|
|
|
@ -108,7 +108,7 @@ if (Meteor.isServer) {
|
|||
let participants = [];
|
||||
let watchers = [];
|
||||
let title = 'act-activity-notify';
|
||||
let board = null;
|
||||
const board = Boards.findOne(activity.boardId);
|
||||
const description = `act-${activity.activityType}`;
|
||||
const params = {
|
||||
activityId: activity._id,
|
||||
|
@ -122,8 +122,11 @@ if (Meteor.isServer) {
|
|||
params.userId = activity.userId;
|
||||
}
|
||||
if (activity.boardId) {
|
||||
board = activity.board();
|
||||
params.board = board.title;
|
||||
if (board.title.length > 0) {
|
||||
params.board = board.title;
|
||||
} else {
|
||||
params.board = '';
|
||||
}
|
||||
title = 'act-withBoardTitle';
|
||||
params.url = board.absoluteUrl();
|
||||
params.boardId = activity.boardId;
|
||||
|
|
169
models/users.js
169
models/users.js
|
@ -620,44 +620,6 @@ Users.mutations({
|
|||
});
|
||||
|
||||
Meteor.methods({
|
||||
setCreateUser(fullname, username, password, isAdmin, isActive, email) {
|
||||
if (Meteor.user().isAdmin) {
|
||||
check(fullname, String);
|
||||
check(username, String);
|
||||
check(password, String);
|
||||
check(isAdmin, String);
|
||||
check(isActive, String);
|
||||
check(email, String);
|
||||
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
const nUsersWithEmail = Users.find({ email }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else if (nUsersWithEmail > 0) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Accounts.createUser({
|
||||
fullname,
|
||||
username,
|
||||
password,
|
||||
isAdmin,
|
||||
isActive,
|
||||
email: email.toLowerCase(),
|
||||
from: 'admin',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setUsername(username, userId) {
|
||||
check(username, String);
|
||||
check(userId, String);
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else {
|
||||
Users.update(userId, { $set: { username } });
|
||||
}
|
||||
},
|
||||
setListSortBy(value) {
|
||||
check(value, String);
|
||||
Meteor.user().setListSortBy(value);
|
||||
|
@ -678,51 +640,97 @@ Meteor.methods({
|
|||
check(limit, Number);
|
||||
Meteor.user().setShowCardsCountAt(limit);
|
||||
},
|
||||
setEmail(email, userId) {
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
const existingUser = Users.findOne(
|
||||
{ 'emails.address': email },
|
||||
{ fields: { _id: 1 } },
|
||||
);
|
||||
if (existingUser) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Users.update(userId, {
|
||||
$set: {
|
||||
emails: [
|
||||
{
|
||||
address: email,
|
||||
verified: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
setUsernameAndEmail(username, email, userId) {
|
||||
check(username, String);
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
check(userId, String);
|
||||
Meteor.call('setUsername', username, userId);
|
||||
Meteor.call('setEmail', email, userId);
|
||||
},
|
||||
setPassword(newPassword, userId) {
|
||||
check(userId, String);
|
||||
check(newPassword, String);
|
||||
if (Meteor.user().isAdmin) {
|
||||
Accounts.setPassword(userId, newPassword);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.methods({
|
||||
setCreateUser(fullname, username, password, isAdmin, isActive, email) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(fullname, String);
|
||||
check(username, String);
|
||||
check(password, String);
|
||||
check(isAdmin, String);
|
||||
check(isActive, String);
|
||||
check(email, String);
|
||||
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
const nUsersWithEmail = Users.find({ email }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else if (nUsersWithEmail > 0) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Accounts.createUser({
|
||||
fullname,
|
||||
username,
|
||||
password,
|
||||
isAdmin,
|
||||
isActive,
|
||||
email: email.toLowerCase(),
|
||||
from: 'admin',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setUsername(username, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(username, String);
|
||||
check(userId, String);
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else {
|
||||
Users.update(userId, { $set: { username } });
|
||||
}
|
||||
}
|
||||
},
|
||||
setEmail(email, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
const existingUser = Users.findOne(
|
||||
{ 'emails.address': email },
|
||||
{ fields: { _id: 1 } },
|
||||
);
|
||||
if (existingUser) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Users.update(userId, {
|
||||
$set: {
|
||||
emails: [
|
||||
{
|
||||
address: email,
|
||||
verified: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setUsernameAndEmail(username, email, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(username, String);
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
check(userId, String);
|
||||
Meteor.call('setUsername', username, userId);
|
||||
Meteor.call('setEmail', email, userId);
|
||||
}
|
||||
},
|
||||
setPassword(newPassword, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(userId, String);
|
||||
check(newPassword, String);
|
||||
if (Meteor.user().isAdmin) {
|
||||
Accounts.setPassword(userId, newPassword);
|
||||
}
|
||||
}
|
||||
},
|
||||
// we accept userId, username, email
|
||||
inviteUserToBoard(username, boardId) {
|
||||
check(username, String);
|
||||
|
@ -754,8 +762,9 @@ if (Meteor.isServer) {
|
|||
throw new Meteor.Error('error-user-notAllowSelf');
|
||||
} else {
|
||||
if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist');
|
||||
if (Settings.findOne().disableRegistration)
|
||||
if (Settings.findOne({ disableRegistration: true })) {
|
||||
throw new Meteor.Error('error-user-notCreated');
|
||||
}
|
||||
// Set in lowercase email before creating account
|
||||
const email = username.toLowerCase();
|
||||
username = email.substring(0, posAt);
|
||||
|
|
558
package-lock.json
generated
558
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.80.0",
|
||||
"version": "v3.86.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -41,15 +41,68 @@
|
|||
"any-observable": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"@types/eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
|
||||
"integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
|
||||
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.3",
|
||||
"@typescript-eslint/typescript-estree": "1.13.0",
|
||||
"eslint-scope": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz",
|
||||
"integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-visitor-keys": "^1.0.0",
|
||||
"@typescript-eslint/experimental-utils": "1.13.0",
|
||||
"@typescript-eslint/typescript-estree": "1.13.0",
|
||||
"eslint-visitor-keys": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
|
||||
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.unescape": "4.0.1",
|
||||
"semver": "5.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
|
||||
"integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
|
@ -69,12 +122,6 @@
|
|||
"json-schema-traverse": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
|
||||
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz",
|
||||
|
@ -200,50 +247,6 @@
|
|||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"dev": true
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.1.3",
|
||||
"esutils": "^2.0.2",
|
||||
"js-tokens": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
|
@ -501,12 +504,6 @@
|
|||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
|
||||
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
|
||||
},
|
||||
"circular-json": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
|
||||
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
|
||||
"dev": true
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
|
@ -1504,8 +1501,7 @@
|
|||
"flatted": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
|
||||
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg=="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
|
@ -2012,12 +2008,6 @@
|
|||
"integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
|
||||
"dev": true
|
||||
},
|
||||
"is-resolvable": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
|
||||
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
|
||||
"dev": true
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
||||
|
@ -2114,9 +2104,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"dev": true
|
||||
},
|
||||
"ldap-filter": {
|
||||
|
@ -2458,9 +2448,9 @@
|
|||
}
|
||||
},
|
||||
"loglevel": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz",
|
||||
"integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==",
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz",
|
||||
"integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==",
|
||||
"dev": true
|
||||
},
|
||||
"loglevel-colored-level-prefix": {
|
||||
|
@ -3116,9 +3106,9 @@
|
|||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
|
@ -3163,7 +3153,7 @@
|
|||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
"minimist": "1.2.5"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
|
@ -3692,12 +3682,6 @@
|
|||
"semver-compare": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"pluralize": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
|
||||
"integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
|
||||
"dev": true
|
||||
},
|
||||
"posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||
|
@ -3755,325 +3739,37 @@
|
|||
"dev": true
|
||||
},
|
||||
"prettier-eslint": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.8.2.tgz",
|
||||
"integrity": "sha512-2UzApPuxi2yRoyMlXMazgR6UcH9DKJhNgCviIwY3ixZ9THWSSrUww5vkiZ3C48WvpFl1M1y/oU63deSy1puWEA==",
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-9.0.1.tgz",
|
||||
"integrity": "sha512-KZT65QTosSAqBBqmrC+RpXbsMRe7Os2YSR9cAfFbDlyPAopzA/S5bioiZ3rpziNQNSJaOxmtXSx07EQ+o2Dlug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"@typescript-eslint/parser": "^1.10.2",
|
||||
"common-tags": "^1.4.0",
|
||||
"core-js": "^3.1.4",
|
||||
"dlv": "^1.1.0",
|
||||
"eslint": "^4.0.0",
|
||||
"indent-string": "^3.2.0",
|
||||
"eslint": "^5.0.0",
|
||||
"indent-string": "^4.0.0",
|
||||
"lodash.merge": "^4.6.0",
|
||||
"loglevel-colored-level-prefix": "^1.0.0",
|
||||
"prettier": "^1.7.0",
|
||||
"pretty-format": "^23.0.1",
|
||||
"require-relative": "^0.8.7",
|
||||
"typescript": "^2.5.1",
|
||||
"typescript-eslint-parser": "^16.0.0",
|
||||
"typescript": "^3.2.1",
|
||||
"vue-eslint-parser": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
||||
"core-js": {
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
|
||||
"integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
|
||||
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
|
||||
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"dev": true
|
||||
},
|
||||
"chardet": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
|
||||
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
|
||||
"dev": true
|
||||
},
|
||||
"cli-cursor": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
|
||||
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"restore-cursor": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^4.0.1",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"eslint": {
|
||||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
|
||||
"integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^5.3.0",
|
||||
"babel-code-frame": "^6.22.0",
|
||||
"chalk": "^2.1.0",
|
||||
"concat-stream": "^1.6.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"debug": "^3.1.0",
|
||||
"doctrine": "^2.1.0",
|
||||
"eslint-scope": "^3.7.1",
|
||||
"eslint-visitor-keys": "^1.0.0",
|
||||
"espree": "^3.5.4",
|
||||
"esquery": "^1.0.0",
|
||||
"esutils": "^2.0.2",
|
||||
"file-entry-cache": "^2.0.0",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"globals": "^11.0.1",
|
||||
"ignore": "^3.3.3",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"inquirer": "^3.0.6",
|
||||
"is-resolvable": "^1.0.0",
|
||||
"js-yaml": "^3.9.1",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.3.0",
|
||||
"lodash": "^4.17.4",
|
||||
"minimatch": "^3.0.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.8.2",
|
||||
"path-is-inside": "^1.0.2",
|
||||
"pluralize": "^7.0.0",
|
||||
"progress": "^2.0.0",
|
||||
"regexpp": "^1.0.1",
|
||||
"require-uncached": "^1.0.3",
|
||||
"semver": "^5.3.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"strip-json-comments": "~2.0.1",
|
||||
"table": "4.0.2",
|
||||
"text-table": "~0.2.0"
|
||||
}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "3.7.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
|
||||
"integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esrecurse": "^4.1.0",
|
||||
"estraverse": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"espree": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
|
||||
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^5.5.0",
|
||||
"acorn-jsx": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"external-editor": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
|
||||
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chardet": "^0.4.0",
|
||||
"iconv-lite": "^0.4.17",
|
||||
"tmp": "^0.0.33"
|
||||
}
|
||||
},
|
||||
"figures": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
|
||||
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"file-entry-cache": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
|
||||
"integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"flat-cache": "^1.2.1",
|
||||
"object-assign": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"flat-cache": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
|
||||
"integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"circular-json": "^0.3.1",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"rimraf": "~2.6.2",
|
||||
"write": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"ignore": {
|
||||
"version": "3.3.10",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
|
||||
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
|
||||
"dev": true
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
|
||||
"integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^3.0.0",
|
||||
"chalk": "^2.0.0",
|
||||
"cli-cursor": "^2.1.0",
|
||||
"cli-width": "^2.0.0",
|
||||
"external-editor": "^2.0.4",
|
||||
"figures": "^2.0.0",
|
||||
"lodash": "^4.3.0",
|
||||
"mute-stream": "0.0.7",
|
||||
"run-async": "^2.2.0",
|
||||
"rx-lite": "^4.0.8",
|
||||
"rx-lite-aggregates": "^4.0.8",
|
||||
"string-width": "^2.1.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"through": "^2.3.6"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
|
||||
"dev": true
|
||||
},
|
||||
"onetime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
|
||||
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mimic-fn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
|
||||
"integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
|
||||
"dev": true
|
||||
},
|
||||
"restore-cursor": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
|
||||
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"onetime": "^2.0.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"slice-ansi": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
|
||||
"integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
|
||||
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^5.2.3",
|
||||
"ajv-keywords": "^2.1.0",
|
||||
"chalk": "^2.1.0",
|
||||
"lodash": "^4.17.4",
|
||||
"slice-ansi": "1.0.0",
|
||||
"string-width": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
|
||||
"integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mkdirp": "^0.5.1"
|
||||
}
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4139,14 +3835,14 @@
|
|||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"minimist": "^1.2.5",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4224,39 +3920,6 @@
|
|||
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
|
||||
"dev": true
|
||||
},
|
||||
"require-uncached": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
|
||||
"integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caller-path": "^0.1.0",
|
||||
"resolve-from": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"caller-path": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
|
||||
"integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"callsites": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
|
||||
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
|
||||
"integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"require_optional": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||
|
@ -4319,21 +3982,6 @@
|
|||
"is-promise": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"rx-lite": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
|
||||
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
|
||||
"dev": true
|
||||
},
|
||||
"rx-lite-aggregates": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
|
||||
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"rx-lite": "*"
|
||||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
|
||||
|
@ -4990,29 +4638,11 @@
|
|||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
|
||||
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
|
||||
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript-eslint-parser": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-16.0.1.tgz",
|
||||
"integrity": "sha512-IKawLTu4A2xN3aN/cPLxvZ0bhxZHILGDKTZWvWNJ3sLNhJ3PjfMEDQmR2VMpdRPrmWOadgWXRwjLBzSA8AGsaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.unescape": "4.0.1",
|
||||
"semver": "5.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||
|
@ -5144,9 +4774,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
||||
"version": "5.7.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.80.0",
|
||||
"version": "v3.86.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -50,7 +50,7 @@
|
|||
"lint-staged": "^7.3.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-eslint": "^8.8.2"
|
||||
"prettier-eslint": "^9.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.4",
|
||||
|
@ -60,6 +60,7 @@
|
|||
"bson": "^4.0.3",
|
||||
"bunyan": "^1.8.12",
|
||||
"es6-promise": "^4.2.4",
|
||||
"flatted": "^2.0.1",
|
||||
"gridfs-stream": "^0.5.3",
|
||||
"ldapjs": "^1.0.2",
|
||||
"meteor-node-stubs": "^0.4.1",
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*.{json,js}]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md, !test/*.md]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
|
@ -1 +0,0 @@
|
|||
*.min.js
|
2
packages/markdown/marked/.gitattributes
vendored
2
packages/markdown/marked/.gitattributes
vendored
|
@ -1,2 +0,0 @@
|
|||
test/* linguist-vendored
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
**Marked version:**
|
||||
|
||||
**Markdown flavor:** Markdown.pl|CommonMark|GitHub Flavored Markdown|n/a
|
||||
|
||||
<!-- The NPM version or commit hash having the issue -->
|
||||
|
||||
<!--
|
||||
|
||||
If submitting something other than a defect with Marked itself, please use the following:
|
||||
|
||||
**Proposal type:** new feature | project operations | other
|
||||
|
||||
## What pain point are you perceiving?
|
||||
|
||||
## What solution are you suggesting?
|
||||
|
||||
-->
|
||||
|
||||
## Expectation
|
||||
|
||||
**CommonMark Demo:** [demo](https://spec.commonmark.org/dingus/)
|
||||
<!-- You can replace the link above with a permalink from: https://spec.commonmark.org/dingus/ -->
|
||||
|
||||
<!-- Describe the output you are expecting from marked -->
|
||||
|
||||
## Result
|
||||
|
||||
**Marked Demo:** [demo](https://marked.js.org/demo/)
|
||||
<!-- You can replace the link above with a permalink from: https://marked.js.org/demo/ -->
|
||||
|
||||
<!-- Describe the output you received from marked -->
|
||||
|
||||
## What was attempted
|
||||
|
||||
<!-- Describe what code combination got you there -->
|
||||
|
||||
<!--
|
||||
If error is thrown add the following:
|
||||
|
||||
## Call stack & console log
|
||||
|
||||
-->
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Marked says it does this thing but does not
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
<!-- If possible, use the Marked Demo permalink and compare to the CommonMark Dingus permalink to demonstrate the bug -->
|
||||
<!--
|
||||
1. [Marked Demo](https://marked.js.org/demo/)
|
||||
2. [CommonMark Demo](https://spec.commonmark.org/dingus/)
|
||||
-->
|
||||
|
||||
<!-- If you need a specific version and options to reproduce the bug, use the following template -->
|
||||
<!--
|
||||
1. Install marked `npm install --save marked@0.3.19` with the version you are using
|
||||
2. Run marked with input string and options such as `marked('hello *world*', {gfm: true})`
|
||||
3. Actual output (or error) is...
|
||||
-->
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Marked doesn't do this thing and I think it should
|
||||
|
||||
---
|
||||
|
||||
**Describe the feature**
|
||||
A clear and concise description of what you would like.
|
||||
|
||||
**Why is this feature necessary?**
|
||||
A clear and concise description of why.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
name: Proposal
|
||||
about: Marked doesn't do this thing and I think it should
|
||||
|
||||
---
|
||||
|
||||
**What pain point are you perceiving?.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
|
@ -1,53 +0,0 @@
|
|||
|
||||
<!--
|
||||
|
||||
If release PR, add ?template=release.md to the PR url to use the release PR template.
|
||||
|
||||
If badging PR, add ?template=badges.md to the PR url to use the badges PR template.
|
||||
|
||||
Otherwise, you are stating this PR fixes an issue that has been submitted; or,
|
||||
describes the issue or proposal under consideration and contains the project-related code to implement.
|
||||
|
||||
-->
|
||||
|
||||
**Marked version:**
|
||||
|
||||
<!-- The NPM version or commit hash having the issue -->
|
||||
|
||||
**Markdown flavor:** Markdown.pl|CommonMark|GitHub Flavored Markdown|n/a
|
||||
|
||||
## Description
|
||||
|
||||
- Fixes #### (if fixing a known issue; otherwise, describe issue using the following format)
|
||||
|
||||
<!--
|
||||
|
||||
If no issue exists that you're aware of. The maintainers should be able to figure out if it's a duplicate.
|
||||
|
||||
## Expectation
|
||||
|
||||
Describe the output you are expecting from marked
|
||||
|
||||
## Result
|
||||
|
||||
Describe the output you received from marked
|
||||
|
||||
## What was attempted
|
||||
|
||||
Describe what code combination got you there
|
||||
|
||||
-->
|
||||
|
||||
## Contributor
|
||||
|
||||
- [ ] Test(s) exist to ensure functionality and minimize regression (if no tests added, list tests covering this PR); or,
|
||||
- [ ] no tests required for this PR.
|
||||
- [ ] If submitting new feature, it has been documented in the appropriate places.
|
||||
|
||||
## Committer
|
||||
|
||||
In most cases, this should be a different person than the contributor.
|
||||
|
||||
- [ ] Draft GitHub release notes have been updated.
|
||||
- [ ] CI is green (no forced merge required).
|
||||
- [ ] Merge PR
|
|
@ -1,50 +0,0 @@
|
|||
**@mention the contributor:**
|
||||
|
||||
## Recommendation to:
|
||||
|
||||
- [ ] Change user group
|
||||
- [ ] Add a badge
|
||||
- [ ] Remove a badge
|
||||
|
||||
<!--
|
||||
|
||||
Explain your reasoning behind tagging that person.
|
||||
|
||||
Preferably by citing objective examples, like PRs, Issues, and so on.
|
||||
|
||||
-->
|
||||
|
||||
## As the one mentioned, I would like to:
|
||||
|
||||
- [ ] accept the recommendation; or,
|
||||
- [ ] graciously decline; or,
|
||||
- [ ] dispute the recommendation
|
||||
|
||||
within 30 days, if you have not indicated which option you are taking one of the following will happen:
|
||||
|
||||
1. If adding a badge, we will assume you are graciously declining.
|
||||
2. If removing a badge, we will assume you do not want to dispute the recommendation; therefore, the badge will be removed.
|
||||
|
||||
<!--
|
||||
|
||||
Why would someone not accept a badge? Loads of reasons depending on the circumstances.
|
||||
|
||||
1. If you're a committer and someone puts a badge for you on having decision making authority in an area, do you really a) think you earned it and b) think you can do that *and* all the other stuff you got going as a committer, admin, or publisher (not to even mention your outside life)? Maybe not. And that's okay. Thank them for the recognition, explain you aren't able to take more on at the moment. It's cool to get recognized though.
|
||||
2. Maybe you don't feel you actually earned it yet. I remember being in an interview once. The interviewer asked me to give an example of going above and beyond the call of duty. I said, "That's hard. Because what you consider going above and beyond may be what I consider to be 'just rising to'. If we're in battle and you get wounded and I pull you out of the frey before heading back into it, I don't consider that going above and beyond; I consider that rising to."
|
||||
|
||||
Why would someone remove their own badge? Loads of reasons...
|
||||
|
||||
1. Maybe you got a lot going on right now and want to broadcast to the Marked community that, "Hey, I don't want to say I'm going to do this unless I can really commit to it right now in a way that serves the project well." That's awesome! That takes courage! Because a) saying "no" is hard for most humans ("people pleasers") and b) the alternative, well, for those of us here since about October of 2017 (and prior), we know what the alternative can look like.
|
||||
2. Maybe you just think you've done all you can to help and learned all you can from the experience. Again, very awesome and courageous. It takes courage to know when to walk away on your own accord.
|
||||
|
||||
Why would you want to remove someone's badge? Loads of reasons...
|
||||
|
||||
1. Maybe they have decision making authority on something. You asked for their advice. And, you ended up waiting almost a month before receiving a response.
|
||||
2. Maybe it was relevant at the time (Master of Marked, for example) but you think they've lost their former level of skill (fell out of practice, for example). They could always get it back.
|
||||
3. Maybe to signal to them that, "Hey, you seem to have forgotten about us. Are you still around (or alive)?"
|
||||
|
||||
Anyway, you get the idea. This isn't about good or bad...it's just about giving the community a simple game mechanic by which to publicly say, "Thank you" or "Here's what my status is" in the community or "Hey, I think something's wrong here" in a civil manner.
|
||||
|
||||
-->
|
||||
|
||||
Note: All committers must approve via review before merging, the disapproving committer can simply close the PR.
|
|
@ -1,25 +0,0 @@
|
|||
## Publisher
|
||||
|
||||
- [ ] `$ npm version` has been run.
|
||||
- [ ] Release notes in [draft GitHub release](https://github.com/markedjs/marked/releases) are up to date
|
||||
- [ ] Release notes include which flavors and versions of Markdown are supported by this release
|
||||
- [ ] Committer checklist is complete.
|
||||
- [ ] Merge PR.
|
||||
- [ ] Publish GitHub release using `master` with correct version number.
|
||||
- [ ] `$ npm publish` has been run.
|
||||
- [ ] Create draft GitHub release to prepare next release.
|
||||
|
||||
Note: If merges to `master` occur after submitting this PR and before running `$ npm pubish` you should be able to
|
||||
|
||||
1. pull from `upstream/master` (`git pull upstream master`) into the branch holding this version,
|
||||
2. run `$ npm run build` to regenerate the `min` file, and
|
||||
3. commit and push the updated changes.
|
||||
|
||||
## Committer
|
||||
|
||||
In most cases, this should be someone different than the publisher.
|
||||
|
||||
- [ ] Version in `package.json` has been updated (see [PUBLISHING.md](https://github.com/markedjs/marked/blob/master/docs/PUBLISHING.md)).
|
||||
- [ ] The `marked.min.js` has been updated; or,
|
||||
- [ ] release does not change library.
|
||||
- [ ] CI is green (no forced merge required).
|
3
packages/markdown/marked/.gitignore
vendored
3
packages/markdown/marked/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
test/compiled_tests
|
|
@ -1,46 +0,0 @@
|
|||
language: node_js
|
||||
|
||||
jobs:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- stage: security scan 🔐
|
||||
|
||||
include:
|
||||
- stage: unit tests 👩🏽💻
|
||||
script: npm run test:unit
|
||||
node_js: lts/*
|
||||
|
||||
- stage: spec tests 👩🏽💻
|
||||
script: npm run test:specs
|
||||
node_js: v4
|
||||
- node_js: lts/*
|
||||
- node_js: node
|
||||
|
||||
- stage: lint ✨
|
||||
script: npm run test:lint
|
||||
node_js: lts/*
|
||||
|
||||
- stage: minify 🗜️
|
||||
script: |
|
||||
npm run build
|
||||
if ! git diff --quiet; then
|
||||
git config --global user.email "travis@travis-ci.org"
|
||||
git config --global user.name "Travis-CI"
|
||||
git config credential.helper "store --file=.git/credentials"
|
||||
echo "https://${GITHUB_TOKEN}:@github.com" > .git/credentials
|
||||
git commit -am '🗜️ minify [skip ci]'
|
||||
git push origin HEAD:${TRAVIS_BRANCH}
|
||||
fi
|
||||
node_js: lts/*
|
||||
if: branch = master AND type = push
|
||||
|
||||
- stage: security scan 🔐
|
||||
script: npm run test:redos
|
||||
node_js: lts/*
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
git:
|
||||
depth: 3
|
|
@ -1,15 +0,0 @@
|
|||
all:
|
||||
@cp lib/marked.js marked.js
|
||||
@uglifyjs --comments '/\*[^\0]+?Copyright[^\0]+?\*/' -o marked.min.js lib/marked.js
|
||||
|
||||
clean:
|
||||
@rm marked.js
|
||||
@rm marked.min.js
|
||||
|
||||
bench:
|
||||
@node test --bench
|
||||
|
||||
man/marked.1.txt:
|
||||
groff -man -Tascii man/marked.1 | col -b > man/marked.1.txt
|
||||
|
||||
.PHONY: clean all
|
|
@ -39,7 +39,7 @@ Also read about:
|
|||
|
||||
## Usage
|
||||
|
||||
### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML by default 🚨
|
||||
### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! 🚨
|
||||
|
||||
**CLI**
|
||||
|
||||
|
|
10
packages/markdown/marked/SECURITY.md
Normal file
10
packages/markdown/marked/SECURITY.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Security Policy
|
||||
|
||||
The only completely secure system is the one that doesn't exist in the first place.
|
||||
Having said that, we take the security of Marked very seriously.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please disclose potential security issues by email to the project [committers](https://marked.js.org/#/AUTHORS.md) as well as the [listed owners within NPM](https://docs.npmjs.com/cli/owner).
|
||||
We will provide an initial assessment of security reports within 48 hours and should apply patches within 2 weeks
|
||||
(also, feel free to contribute a fix for the issue).
|
|
@ -1,215 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Marked CLI
|
||||
* Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
marked = require('../');
|
||||
|
||||
/**
|
||||
* Man Page
|
||||
*/
|
||||
|
||||
function help() {
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var options = {
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
setsid: false,
|
||||
stdio: 'inherit'
|
||||
};
|
||||
|
||||
spawn('man', [path.resolve(__dirname, '/../man/marked.1')], options)
|
||||
.on('error', function() {
|
||||
fs.readFile(path.resolve(__dirname, '/../man/marked.1.txt'), 'utf8', function(err, data) {
|
||||
if (err) throw err;
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function version() {
|
||||
var pkg = require('../package.json');
|
||||
console.log(pkg.version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
|
||||
function main(argv, callback) {
|
||||
var files = [],
|
||||
options = {},
|
||||
input,
|
||||
output,
|
||||
string,
|
||||
arg,
|
||||
tokens,
|
||||
opt;
|
||||
|
||||
function getarg() {
|
||||
var arg = argv.shift();
|
||||
|
||||
if (arg.indexOf('--') === 0) {
|
||||
// e.g. --opt
|
||||
arg = arg.split('=');
|
||||
if (arg.length > 1) {
|
||||
// e.g. --opt=val
|
||||
argv.unshift(arg.slice(1).join('='));
|
||||
}
|
||||
arg = arg[0];
|
||||
} else if (arg[0] === '-') {
|
||||
if (arg.length > 2) {
|
||||
// e.g. -abc
|
||||
argv = arg.substring(1).split('').map(function(ch) {
|
||||
return '-' + ch;
|
||||
}).concat(argv);
|
||||
arg = argv.shift();
|
||||
} else {
|
||||
// e.g. -a
|
||||
}
|
||||
} else {
|
||||
// e.g. foo
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
while (argv.length) {
|
||||
arg = getarg();
|
||||
switch (arg) {
|
||||
case '--test':
|
||||
return require('../test').main(process.argv.slice());
|
||||
case '-o':
|
||||
case '--output':
|
||||
output = argv.shift();
|
||||
break;
|
||||
case '-i':
|
||||
case '--input':
|
||||
input = argv.shift();
|
||||
break;
|
||||
case '-s':
|
||||
case '--string':
|
||||
string = argv.shift();
|
||||
break;
|
||||
case '-t':
|
||||
case '--tokens':
|
||||
tokens = true;
|
||||
break;
|
||||
case '-h':
|
||||
case '--help':
|
||||
return help();
|
||||
case '-v':
|
||||
case '--version':
|
||||
return version();
|
||||
default:
|
||||
if (arg.indexOf('--') === 0) {
|
||||
opt = camelize(arg.replace(/^--(no-)?/, ''));
|
||||
if (!marked.defaults.hasOwnProperty(opt)) {
|
||||
continue;
|
||||
}
|
||||
if (arg.indexOf('--no-') === 0) {
|
||||
options[opt] = typeof marked.defaults[opt] !== 'boolean'
|
||||
? null
|
||||
: false;
|
||||
} else {
|
||||
options[opt] = typeof marked.defaults[opt] !== 'boolean'
|
||||
? argv.shift()
|
||||
: true;
|
||||
}
|
||||
} else {
|
||||
files.push(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getData(callback) {
|
||||
if (!input) {
|
||||
if (files.length <= 2) {
|
||||
if (string) {
|
||||
return callback(null, string);
|
||||
}
|
||||
return getStdin(callback);
|
||||
}
|
||||
input = files.pop();
|
||||
}
|
||||
return fs.readFile(input, 'utf8', callback);
|
||||
}
|
||||
|
||||
return getData(function(err, data) {
|
||||
if (err) return callback(err);
|
||||
|
||||
data = tokens
|
||||
? JSON.stringify(marked.lexer(data, options), null, 2)
|
||||
: marked(data, options);
|
||||
|
||||
if (!output) {
|
||||
process.stdout.write(data + '\n');
|
||||
return callback();
|
||||
}
|
||||
|
||||
return fs.writeFile(output, data, callback);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function getStdin(callback) {
|
||||
var stdin = process.stdin,
|
||||
buff = '';
|
||||
|
||||
stdin.setEncoding('utf8');
|
||||
|
||||
stdin.on('data', function(data) {
|
||||
buff += data;
|
||||
});
|
||||
|
||||
stdin.on('error', function(err) {
|
||||
return callback(err);
|
||||
});
|
||||
|
||||
stdin.on('end', function() {
|
||||
return callback(null, buff);
|
||||
});
|
||||
|
||||
try {
|
||||
stdin.resume();
|
||||
} catch (e) {
|
||||
callback(e);
|
||||
}
|
||||
}
|
||||
|
||||
function camelize(text) {
|
||||
return text.replace(/(\w)-(\w)/g, function(_, a, b) {
|
||||
return a + b.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.error(`marked: output to ${err.path}: No such directory`);
|
||||
return process.exit(1);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose / Entry Point
|
||||
*/
|
||||
|
||||
if (!module.parent) {
|
||||
process.title = 'marked';
|
||||
main(process.argv.slice(), function(err, code) {
|
||||
if (err) return handleError(err);
|
||||
return process.exit(code || 0);
|
||||
});
|
||||
} else {
|
||||
module.exports = main;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "marked",
|
||||
"homepage": "https://github.com/markedjs/marked",
|
||||
"authors": [
|
||||
"Christopher Jeffrey <chjjeffrey@gmail.com>"
|
||||
],
|
||||
"description": "A markdown parser built for speed",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"markup",
|
||||
"html"
|
||||
],
|
||||
"main": "lib/marked.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"app/bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "marked",
|
||||
"version": "0.3.4",
|
||||
"repo": "markedjs/marked",
|
||||
"description": "A markdown parser built for speed",
|
||||
"keywords": ["markdown", "markup", "html"],
|
||||
"scripts": ["lib/marked.js"],
|
||||
"main": "lib/marked.js",
|
||||
"license": "MIT"
|
||||
}
|
|
@ -3,27 +3,31 @@
|
|||
"plugins": [
|
||||
"standard"
|
||||
],
|
||||
"parserOptions": { "ecmaVersion": 5 },
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
"sourceType": "script"
|
||||
},
|
||||
"rules": {
|
||||
"semi": ["error", "always"],
|
||||
"indent": ["warn", 2, {
|
||||
"VariableDeclarator": { "var": 2 },
|
||||
"indent": ["error", 2, {
|
||||
"SwitchCase": 1,
|
||||
"VariableDeclarator": { "var": 2 },
|
||||
"outerIIFEBody": 0
|
||||
}],
|
||||
"space-before-function-paren": "off",
|
||||
"object-curly-spacing": "off",
|
||||
"operator-linebreak": ["error", "before", { "overrides": { "=": "after" } }],
|
||||
"space-before-function-paren": ["error", "never"],
|
||||
"no-cond-assign": "off",
|
||||
"no-useless-escape": "off",
|
||||
"no-return-assign": "off",
|
||||
"one-var": "off",
|
||||
"no-control-regex": "off"
|
||||
"no-control-regex": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
|
||||
"prefer-const": "off",
|
||||
"no-var": "off"
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"amd": true,
|
||||
"jasmine": true
|
||||
"amd": true
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
# Authors
|
||||
|
||||
Marked takes an encompassing approach to its community. As such, you can think of these as [concentric circles](https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951), where each group encompases the following groups.
|
||||
Marked takes an encompassing approach to its community. As such, you can think of these as [concentric circles](https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951), where each group encompasses the following groups.
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
|
@ -170,7 +170,7 @@ To be removed: You can remove yourself through the [GitHub UI](https://help.gith
|
|||
A note on volunteering:
|
||||
|
||||
1. Please do not volunteer unless you believe you can demonstrate to your peers you can do the work required.
|
||||
2. Please do not overcommit yourself; we count on those committed to the project to be responsive. Really consider, with all you have going on, wehther you able to really commit to it.
|
||||
2. Please do not overcommit yourself; we count on those committed to the project to be responsive. Really consider, with all you have going on, whether you able to really commit to it.
|
||||
3. Don't let the previous frighten you away, it can always be changed later by you or your peers.
|
||||
|
||||
[Details on badges](#badges)
|
||||
|
@ -227,7 +227,7 @@ Badges? If you *want* 'em, we got 'em, and here's how you get 'em (and…dr
|
|||
</blockquote>
|
||||
</dd>
|
||||
<dt>Dr. Docs</dt>
|
||||
<dd>Someone who has contributed a great deal to the creation and maintainance of the non-code areas of marked.</dd>
|
||||
<dd>Someone who has contributed a great deal to the creation and maintenance of the non-code areas of marked.</dd>
|
||||
<dt>Eye for the CLI</dt>
|
||||
<dd>At this point? Pretty much anyone who can update that `man` file to the current Marked version without regression in the CLI tool itself.</dd>
|
||||
<dt>GitHub Guru</dt>
|
||||
|
@ -259,9 +259,9 @@ Badges? If you *want* 'em, we got 'em, and here's how you get 'em (and…dr
|
|||
|
||||
<dl>
|
||||
<dt>Defibrillator</dt>
|
||||
<dd>A contributor who stepped up to help bring Marked back to life by contriuting solutions to help Marked pass when compared against the CommonMark and GitHub Flavored Markdown specifications.</dd>
|
||||
<dd>A contributor who stepped up to help bring Marked back to life by contributing solutions to help Marked pass when compared against the CommonMark and GitHub Flavored Markdown specifications.</dd>
|
||||
<dt>Maker of the Marked mark</dt>
|
||||
<dd>This badge is given to the person or oganization credited with creating the logo (or logotype) used in Marked communications for a given period of time. **Maker of the Marked mark from 2017 to present**, for example.</dd>
|
||||
<dd>This badge is given to the person or organization credited with creating the logo (or logotype) used in Marked communications for a given period of time. **Maker of the Marked mark from 2017 to present**, for example.</dd>
|
||||
<dt>Release Wrangler</dt>
|
||||
<dd>This is a badge given to all Publishers.</dd>
|
||||
<dt>Snyk's Security Saint</dt>
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
- [ ] Make sure you are on the `master` branch.
|
||||
- [ ] Be sure to run `npm install` or `npm update`.
|
||||
- [ ] Create a branch.
|
||||
- [ ] Make as small a change as possible.
|
||||
- [ ] Run `npm test`, fix any broken things (for linting, you can run `npm run lint` to have the linter fix them for you).
|
||||
- [ ] Submit a PR.
|
||||
- [ ] Update code in `src` folder. (`lib` folder is for auto compiled code)
|
||||
- [ ] Run `npm run test:all`, fix any broken things (for linting, you can run `npm run lint` to have the linter fix them for you).
|
||||
- [ ] Run `npm run build:reset` to remove changes to compiled files.
|
||||
- [ ] Submit a Pull Request.
|
||||
|
||||
## Design principles
|
||||
|
||||
|
@ -30,16 +31,19 @@ The following table lists the ticket type labels we use when there is work to be
|
|||
|RR - refactor and re-engineer |Results in an improvement to developers using Marked (improved readability) or end-users (faster performance) or both. |
|
||||
|NFS - new feature (spec related) |A capability Marked does not currently provide but is in one of the [supported specifications](#/README.md#specifications) |
|
||||
|NFU - new feature (user requested) |A capability Marked does not currently provide but has been requested by users of Marked. |
|
||||
|NFE - new feature (should be an extension) |A capability Marked does not currently provide and is not part of a spec. |
|
||||
|
||||
## Test early, often, and everything
|
||||
|
||||
We try to write test cases to validate output (writing tests based on the [supported specifications](#/README.md#specifications)) and minimize regression (writing tests for issues fixed). Therefore, if you would like to contribute, some things you should know regarding the test harness.
|
||||
|
||||
|Location |Description |
|
||||
|:-------------|:---------------------------------------------------|
|
||||
|/test/browser |For testing Marked in a client-side implementation. |
|
||||
|/test/new |Tests not related to the original `markdown.pl`. |
|
||||
|/test/original|Tests validating against the original `markdown.pl`.|
|
||||
|Location |Description |
|
||||
|:---------------------|:--------------------------------------------------------------------------------------------------------------|
|
||||
|/test/specs/commonmark|Tests for [CommonMark](https://spec.commonmark.org/current/) compliance |
|
||||
|/test/specs/gfm |Tests for [GFM](https://github.github.com/gfm/) compliance |
|
||||
|/test/specs/new |Tests not related to the original `markdown.pl`. |
|
||||
|/test/specs/original |Tests validating against the original `markdown.pl`. |
|
||||
|/test/specs/redos |Tests for [ReDOS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS) vulnerabilities|
|
||||
|
||||
If your test uses features or options, assuming `gfm` is set to `false`, for example, you can add [front-matter](https://www.npmjs.com/package/front-matter) to the top of
|
||||
your `.md` file
|
||||
|
@ -84,9 +88,8 @@ To check for (and fix) standardized syntax (lint):
|
|||
npm run lint
|
||||
```
|
||||
|
||||
To build your own minified version of Marked:
|
||||
To build your own es5, esm, and minified versions of Marked:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## Overall strategy
|
||||
|
||||
**Master is always shippable:** We try to merge PRs in such a way that `master` is the only branch to really be concerned about *and* `master` can always be released. This allows smoother flow between new fetures, bug fixes, and so on. (Almost a continuous deployment setup, without automation.)
|
||||
**Master is always shippable:** We try to merge PRs in such a way that `master` is the only branch to really be concerned about *and* `master` can always be released. This allows smoother flow between new features, bug fixes, and so on. (Almost a continuous deployment setup, without automation.)
|
||||
|
||||
## Versioning
|
||||
|
||||
|
@ -20,5 +20,5 @@ We follow [semantic versioning](https://semver.org) where the following sequence
|
|||
What to expect while Marked is a zero-major (0.x.y):
|
||||
|
||||
1. The major will remain at zero; thereby, alerting consumers to the potentially volatile nature of the package.
|
||||
2. The minor will tend to be more analagous to a `major` release.
|
||||
3. The patch will tend to be more analagous to a `minor` release or a collection of bug fixes (patches).
|
||||
2. The minor will tend to be more analogous to a `major` release.
|
||||
3. The patch will tend to be more analogous to a `minor` release or a collection of bug fixes (patches).
|
||||
|
|
|
@ -25,7 +25,7 @@ These documentation pages are also rendered using marked 💯
|
|||
|
||||
<h2 id="usage">Usage</h2>
|
||||
|
||||
### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML by default 🚨
|
||||
### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! 🚨
|
||||
|
||||
**CLI**
|
||||
|
||||
|
@ -72,8 +72,8 @@ We actively support the features of the following [Markdown flavors](https://git
|
|||
|Flavor |Version |
|
||||
|:----------------------------------------------------------|:----------|
|
||||
|The original markdown.pl |-- |
|
||||
|[CommonMark](http://spec.commonmark.org/0.28/) |0.28 |
|
||||
|[GitHub Flavored Markdown](https://github.github.com/gfm/) |0.28 |
|
||||
|[CommonMark](http://spec.commonmark.org/0.29/) |0.29 |
|
||||
|[GitHub Flavored Markdown](https://github.github.com/gfm/) |0.29 |
|
||||
|
||||
By supporting the above Markdown flavors, it's possible that Marked can help you use other flavors as well; however, these are not actively supported by the community.
|
||||
|
||||
|
|
|
@ -14,18 +14,19 @@ marked(markdownString [,options] [,callback])
|
|||
|
||||
```js
|
||||
// Create reference instance
|
||||
var myMarked = require('marked');
|
||||
const marked = require('marked');
|
||||
|
||||
// Set options
|
||||
// `highlight` example uses `highlight.js`
|
||||
myMarked.setOptions({
|
||||
renderer: new myMarked.Renderer(),
|
||||
highlight: function(code) {
|
||||
return require('highlight.js').highlightAuto(code).value;
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
highlight: function(code, language) {
|
||||
const hljs = require('highlight.js');
|
||||
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext';
|
||||
return hljs.highlight(validLanguage, code).value;
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
|
@ -34,14 +35,14 @@ myMarked.setOptions({
|
|||
});
|
||||
|
||||
// Compile
|
||||
console.log(myMarked('I am using __markdown__.'));
|
||||
console.log(marked(markdownString));
|
||||
```
|
||||
|
||||
<h2 id="options">Options</h2>
|
||||
|
||||
|Member |Type |Default |Since |Notes |
|
||||
|:-----------|:---------|:--------|:--------|:-------------|
|
||||
|baseUrl |`string` |`null` |0.3.9 |A prefix url for any relative link. |
|
||||
|baseUrl |`string` |`null` |0.3.9 |A prefix url for any relative link. |
|
||||
|breaks |`boolean` |`false` |v0.2.7 |If true, add `<br>` on a single line break (copies GitHub). Requires `gfm` be `true`.|
|
||||
|gfm |`boolean` |`true` |v0.2.1 |If true, use approved [GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/).|
|
||||
|headerIds |`boolean` |`true` |v0.4.0 |If true, include an `id` attribute when emitting headings (h1, h2, h3, etc).|
|
||||
|
@ -50,13 +51,12 @@ console.log(myMarked('I am using __markdown__.'));
|
|||
|langPrefix |`string` |`'language-'`|v0.3.0|A string to prefix the className in a `<code>` block. Useful for syntax highlighting.|
|
||||
|mangle |`boolean` |`true` |v0.3.4 |If true, autolinked email address is escaped with HTML character references.|
|
||||
|pedantic |`boolean` |`false` |v0.2.1 |If true, conform to the original `markdown.pl` as much as possible. Don't fix original markdown bugs or behavior. Turns off and overrides `gfm`.|
|
||||
|renderer |`object` |`new Renderer()`|v0.3.0|An object containing functions to render tokens to HTML. See [extensibility](USING_PRO.md) for more details.|
|
||||
|sanitize |`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.|
|
||||
|renderer |`object` |`new Renderer()`|v0.3.0|An object containing functions to render tokens to HTML. See [extensibility](/#/USING_PRO.md) for more details.|
|
||||
|sanitize |`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.<br>**Warning**: This feature is deprecated and it should NOT be used as it cannot be considered secure.<br>Instead use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! |
|
||||
|sanitizer |`function`|`null` |v0.3.4 |A function to sanitize the HTML passed into `markdownString`.|
|
||||
|silent |`boolean` |`false` |v0.2.7 |If true, the parser does not throw any exception.|
|
||||
|smartLists |`boolean` |`false` |v0.2.8 |If true, use smarter list behavior than those found in `markdown.pl`.|
|
||||
|smartypants |`boolean` |`false` |v0.2.9 |If true, use "smart" typographic punctuation for things like quotes and dashes.|
|
||||
|tables |`boolean` |`true` |v0.2.7 |If true and `gfm` is true, use [GFM Tables extension](https://github.github.com/gfm/#tables-extension-).|
|
||||
|xhtml |`boolean` |`false` |v0.3.2 |If true, emit self-closing HTML tags for void elements (<br/>, <img/>, etc.) with a "/" as required by XHTML.|
|
||||
|
||||
<h2 id="highlight">Asynchronous highlighting</h2>
|
||||
|
@ -64,7 +64,7 @@ console.log(myMarked('I am using __markdown__.'));
|
|||
Unlike `highlight.js` the `pygmentize.js` library uses asynchronous highlighting. This example demonstrates that marked is agnostic when it comes to the highlighter you use.
|
||||
|
||||
```js
|
||||
myMarked.setOptions({
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang, callback) {
|
||||
require('pygmentize-bundled') ({ lang: lang, format: 'html' }, code, function (err, result) {
|
||||
callback(err, result.toString());
|
||||
|
@ -72,7 +72,81 @@ myMarked.setOptions({
|
|||
}
|
||||
});
|
||||
|
||||
console.log(myMarked(markdownString));
|
||||
console.log(marked(markdownString));
|
||||
```
|
||||
|
||||
In both examples, `code` is a `string` representing the section of code to pass to the highlighter. In this example, `lang` is a `string` informing the highlighter what programming lnaguage to use for the `code` and `callback` is the `function` the asynchronous highlighter will call once complete.
|
||||
In both examples, `code` is a `string` representing the section of code to pass to the highlighter. In this example, `lang` is a `string` informing the highlighter what programming language to use for the `code` and `callback` is the `function` the asynchronous highlighter will call once complete.
|
||||
|
||||
<h2 id="workers">Workers</h2>
|
||||
|
||||
To prevent ReDoS attacks you can run marked on a worker and terminate it when parsing takes longer than usual.
|
||||
|
||||
Marked can be run in a [worker thread](https://nodejs.org/api/worker_threads.html) on a node server, or a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) in a browser.
|
||||
|
||||
### Node Worker Thread
|
||||
|
||||
```js
|
||||
// markedWorker.js
|
||||
|
||||
const marked = require('marked');
|
||||
const { parentPort } = require('worker_threads');
|
||||
|
||||
parentPort.on('message', (markdownString) => {
|
||||
parentPort.postMessage(marked(markdownString));
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
// index.js
|
||||
|
||||
const { Worker } = require('worker_threads');
|
||||
const markedWorker = new Worker('./markedWorker.js');
|
||||
|
||||
const markedTimeout = setTimeout(() => {
|
||||
markedWorker.terminate();
|
||||
throw new Error('Marked took too long!');
|
||||
}, timeoutLimit);
|
||||
|
||||
markedWorker.on('message', (html) => {
|
||||
clearTimeout(markedTimeout);
|
||||
console.log(html);
|
||||
markedWorker.terminate();
|
||||
});
|
||||
|
||||
markedWorker.postMessage(markdownString);
|
||||
```
|
||||
|
||||
### Web Worker
|
||||
|
||||
> **NOTE**: Web Workers send the payload from `postMessage` in an object with the payload in a `.data` property
|
||||
|
||||
```js
|
||||
// markedWorker.js
|
||||
|
||||
importScripts('path/to/marked.min.js');
|
||||
|
||||
onmessage = (e) => {
|
||||
const markdownString = e.data
|
||||
postMessage(marked(markdownString));
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
// script.js
|
||||
|
||||
const markedWorker = new Worker('./markedWorker.js');
|
||||
|
||||
const markedTimeout = setTimeout(() => {
|
||||
markedWorker.terminate();
|
||||
throw new Error('Marked took too long!');
|
||||
}, timeoutLimit);
|
||||
|
||||
markedWorker.onmessage = (e) => {
|
||||
clearTimeout(markedTimeout);
|
||||
const html = e.data;
|
||||
console.log(html);
|
||||
markedWorker.terminate();
|
||||
};
|
||||
|
||||
markedWorker.postMessage(markdownString);
|
||||
```
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## Extending Marked
|
||||
|
||||
To champion the single-responsibility and open/closed prinicples, we have tried to make it relatively painless to extend marked. If you are looking to add custom functionality, this is the place to start.
|
||||
To champion the single-responsibility and open/closed principles, we have tried to make it relatively painless to extend marked. If you are looking to add custom functionality, this is the place to start.
|
||||
|
||||
<h2 id="renderer">The renderer</h2>
|
||||
|
||||
|
@ -10,14 +10,14 @@ The renderer is...
|
|||
|
||||
```js
|
||||
// Create reference instance
|
||||
var myMarked = require('marked');
|
||||
const marked = require('marked');
|
||||
|
||||
// Get reference
|
||||
var renderer = new myMarked.Renderer();
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
// Override function
|
||||
renderer.heading = function (text, level) {
|
||||
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
|
||||
const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
|
||||
|
||||
return `
|
||||
<h${level}>
|
||||
|
@ -29,7 +29,7 @@ renderer.heading = function (text, level) {
|
|||
};
|
||||
|
||||
// Run marked
|
||||
console.log(myMarked('# heading+', { renderer: renderer }));
|
||||
console.log(marked('# heading+', { renderer: renderer }));
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
@ -105,13 +105,13 @@ The parser is...
|
|||
You also have direct access to the lexer and parser if you so desire.
|
||||
|
||||
``` js
|
||||
var tokens = marked.lexer(text, options);
|
||||
console.log(marked.parser(tokens));
|
||||
const tokens = marked.lexer(text, options);
|
||||
console.log(marked.parser(tokens, options));
|
||||
```
|
||||
|
||||
``` js
|
||||
var lexer = new marked.Lexer(options);
|
||||
var tokens = lexer.lex(text);
|
||||
const lexer = new marked.Lexer(options);
|
||||
const tokens = lexer.lex(text);
|
||||
console.log(tokens);
|
||||
console.log(lexer.rules);
|
||||
```
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* globals marked, unfetch, ES6Promise */
|
||||
/* globals marked, unfetch, ES6Promise, Promise */ // eslint-disable-line no-redeclare
|
||||
|
||||
if (!window.Promise) {
|
||||
window.Promise = ES6Promise;
|
||||
|
@ -7,7 +7,7 @@ if (!window.fetch) {
|
|||
window.fetch = unfetch;
|
||||
}
|
||||
|
||||
onunhandledrejection = function (e) {
|
||||
onunhandledrejection = function(e) {
|
||||
throw e.reason;
|
||||
};
|
||||
|
||||
|
@ -70,7 +70,7 @@ Promise.all([
|
|||
setInitialText(),
|
||||
setInitialVersion()
|
||||
.then(setInitialOptions)
|
||||
]).then(function () {
|
||||
]).then(function() {
|
||||
handleInputChange();
|
||||
handleOutputChange();
|
||||
checkForChanges();
|
||||
|
@ -84,8 +84,8 @@ function setInitialText() {
|
|||
$markdownElem.value = search.text;
|
||||
} else {
|
||||
return fetch('./initial.md')
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (text) {
|
||||
.then(function(res) { return res.text(); })
|
||||
.then(function(text) {
|
||||
if ($markdownElem.value === '') {
|
||||
$markdownElem.value = text;
|
||||
}
|
||||
|
@ -95,18 +95,18 @@ function setInitialText() {
|
|||
|
||||
function setInitialQuickref() {
|
||||
return fetch('./quickref.md')
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (text) {
|
||||
.then(function(res) { return res.text(); })
|
||||
.then(function(text) {
|
||||
document.querySelector('#quickref').value = text;
|
||||
});
|
||||
}
|
||||
|
||||
function setInitialVersion() {
|
||||
return fetch('https://data.jsdelivr.com/v1/package/npm/marked')
|
||||
.then(function (res) {
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (json) {
|
||||
.then(function(json) {
|
||||
for (var i = 0; i < json.versions.length; i++) {
|
||||
var ver = json.versions[i];
|
||||
markedVersions[ver] = 'https://cdn.jsdelivr.net/npm/marked@' + ver + '/lib/marked.js';
|
||||
|
@ -116,20 +116,20 @@ function setInitialVersion() {
|
|||
$markedVerElem.appendChild(opt);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
.then(function() {
|
||||
return fetch('https://api.github.com/repos/markedjs/marked/commits')
|
||||
.then(function (res) {
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (json) {
|
||||
markedVersions['master'] = 'https://cdn.jsdelivr.net/gh/markedjs/marked@' + json[0].sha + '/lib/marked.js';
|
||||
.then(function(json) {
|
||||
markedVersions.master = 'https://cdn.jsdelivr.net/gh/markedjs/marked@' + json[0].sha + '/lib/marked.js';
|
||||
})
|
||||
.catch(function () {
|
||||
.catch(function() {
|
||||
// do nothing
|
||||
// uses url without commit
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
.then(function() {
|
||||
if (search.version) {
|
||||
if (markedVersions[search.version]) {
|
||||
return search.version;
|
||||
|
@ -142,7 +142,7 @@ function setInitialVersion() {
|
|||
return search.version;
|
||||
case 'pr':
|
||||
return getPrCommit(match[2])
|
||||
.then(function (commit) {
|
||||
.then(function(commit) {
|
||||
if (!commit) {
|
||||
return 'master';
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ function setInitialVersion() {
|
|||
|
||||
return 'master';
|
||||
})
|
||||
.then(function (version) {
|
||||
.then(function(version) {
|
||||
$markedVerElem.value = version;
|
||||
})
|
||||
.then(updateVersion);
|
||||
|
@ -220,7 +220,7 @@ function handleAddVersion(e) {
|
|||
$commitVerElem.disabled = true;
|
||||
var pr = $commitVerElem.value.replace(/\D/g, '');
|
||||
getPrCommit(pr)
|
||||
.then(function (commit) {
|
||||
.then(function(commit) {
|
||||
$commitVerElem.disabled = false;
|
||||
if (!commit) {
|
||||
alert('That is not a valid PR');
|
||||
|
@ -271,12 +271,12 @@ function addCommitVersion(value, text, commit) {
|
|||
|
||||
function getPrCommit(pr) {
|
||||
return fetch('https://api.github.com/repos/markedjs/marked/pulls/' + pr + '/commits')
|
||||
.then(function (res) {
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (json) {
|
||||
.then(function(json) {
|
||||
return json[json.length - 1].sha;
|
||||
}).catch(function () {
|
||||
}).catch(function() {
|
||||
// return undefined
|
||||
});
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ function setDefaultOptions() {
|
|||
function setOptions(opts) {
|
||||
$optionsElem.value = JSON.stringify(
|
||||
opts,
|
||||
function (key, value) {
|
||||
function(key, value) {
|
||||
if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -375,13 +375,13 @@ function updateVersion() {
|
|||
promise = Promise.resolve(markedVersionCache[$markedVerElem.value]);
|
||||
} else {
|
||||
promise = fetch(markedVersions[$markedVerElem.value])
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (text) {
|
||||
.then(function(res) { return res.text(); })
|
||||
.then(function(text) {
|
||||
markedVersionCache[$markedVerElem.value] = text;
|
||||
return text;
|
||||
});
|
||||
}
|
||||
return promise.then(function (text) {
|
||||
return promise.then(function(text) {
|
||||
var script = document.createElement('script');
|
||||
script.textContent = text;
|
||||
|
||||
|
@ -479,7 +479,7 @@ function messageWorker(message) {
|
|||
markedWorker.terminate();
|
||||
}
|
||||
markedWorker = new Worker('worker.js');
|
||||
markedWorker.onmessage = function (e) {
|
||||
markedWorker.onmessage = function(e) {
|
||||
clearTimeout(markedWorker.timeout);
|
||||
markedWorker.working = false;
|
||||
switch (e.data.task) {
|
||||
|
@ -500,7 +500,7 @@ function messageWorker(message) {
|
|||
delayTime = 10;
|
||||
checkForChanges();
|
||||
};
|
||||
markedWorker.onerror = markedWorker.onmessageerror = function (err) {
|
||||
markedWorker.onerror = markedWorker.onmessageerror = function(err) {
|
||||
clearTimeout(markedWorker.timeout);
|
||||
var error = 'There was an error in the Worker';
|
||||
if (err) {
|
||||
|
@ -526,7 +526,7 @@ function messageWorker(message) {
|
|||
}
|
||||
|
||||
function workerTimeout(seconds) {
|
||||
markedWorker.timeout = setTimeout(function () {
|
||||
markedWorker.timeout = setTimeout(function() {
|
||||
seconds++;
|
||||
markedWorker.onerror('Marked has taken longer than ' + seconds + ' second' + (seconds > 1 ? 's' : '') + ' to respond...');
|
||||
workerTimeout(seconds);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* globals marked, unfetch, ES6Promise */
|
||||
/* globals marked, unfetch, ES6Promise, Promise */ // eslint-disable-line no-redeclare
|
||||
if (!self.Promise) {
|
||||
self.importScripts('https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.js');
|
||||
self.Promise = ES6Promise;
|
||||
|
@ -11,15 +11,15 @@ if (!self.fetch) {
|
|||
var versionCache = {};
|
||||
var currentVersion;
|
||||
|
||||
onunhandledrejection = function (e) {
|
||||
onunhandledrejection = function(e) {
|
||||
throw e.reason;
|
||||
};
|
||||
|
||||
onmessage = function (e) {
|
||||
onmessage = function(e) {
|
||||
if (e.data.version === currentVersion) {
|
||||
parse(e);
|
||||
} else {
|
||||
loadVersion(e.data.version).then(function () {
|
||||
loadVersion(e.data.version).then(function() {
|
||||
parse(e);
|
||||
});
|
||||
}
|
||||
|
@ -87,13 +87,13 @@ function loadVersion(ver) {
|
|||
promise = Promise.resolve(versionCache[ver]);
|
||||
} else {
|
||||
promise = fetch(ver)
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (text) {
|
||||
.then(function(res) { return res.text(); })
|
||||
.then(function(text) {
|
||||
versionCache[ver] = text;
|
||||
return text;
|
||||
});
|
||||
}
|
||||
return promise.then(function (text) {
|
||||
return promise.then(function(text) {
|
||||
try {
|
||||
// eslint-disable-next-line no-new-func
|
||||
Function(text)();
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
<ul>
|
||||
<li><a href="#/USING_ADVANCED.md#options">Options</a></li>
|
||||
<li><a href="#/USING_ADVANCED.md#highlight">Highlighting</a></li>
|
||||
<li><a href="#/USING_ADVANCED.md#workers">Workers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
module.exports = require('./lib/marked');
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"spec_dir": "test",
|
||||
"spec_files": [
|
||||
"**/*-spec.js"
|
||||
],
|
||||
"helpers": [
|
||||
"helpers/helpers.js"
|
||||
],
|
||||
"stopSpecOnExpectationFailure": false,
|
||||
"random": true
|
||||
}
|
1830
packages/markdown/marked/lib/marked.esm.js
Normal file
1830
packages/markdown/marked/lib/marked.esm.js
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ marked \- a javascript markdown parser
|
|||
.B marked
|
||||
[\-o \fI<output>\fP] [\-i \fI<input>\fP] [\-\-help]
|
||||
[\-\-tokens] [\-\-pedantic] [\-\-gfm]
|
||||
[\-\-breaks] [\-\-tables] [\-\-sanitize]
|
||||
[\-\-breaks] [\-\-sanitize]
|
||||
[\-\-smart\-lists] [\-\-lang\-prefix \fI<prefix>\fP]
|
||||
[\-\-no\-etc...] [\-\-silent] [\fIfilename\fP]
|
||||
|
||||
|
@ -72,9 +72,6 @@ Enable github flavored markdown.
|
|||
.BI \-\-breaks
|
||||
Enable GFM line breaks. Only works with the gfm option.
|
||||
.TP
|
||||
.BI \-\-tables
|
||||
Enable GFM tables. Only works with the gfm option.
|
||||
.TP
|
||||
.BI \-\-sanitize
|
||||
Sanitize output. Ignore any HTML input.
|
||||
.TP
|
||||
|
|
|
@ -4,10 +4,10 @@ NAME
|
|||
marked - a javascript markdown parser
|
||||
|
||||
SYNOPSIS
|
||||
marked [-o <output>] [-i <input>] [--help] [--tokens]
|
||||
[--pedantic] [--gfm] [--breaks] [--tables] [--sanitize]
|
||||
[--smart-lists] [--lang-prefix <prefix>] [--no-etc...] [--silent]
|
||||
[filename]
|
||||
marked [-o <output>] [-i <input>] [--help] [--tokens] [--pedantic]
|
||||
[--gfm] [--breaks] [--sanitize] [--smart-lists] [--lang-prefix <pre-
|
||||
fix>] [--no-etc...] [--silent] [filename]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
marked is a full-featured javascript markdown parser, built for speed.
|
||||
|
@ -56,9 +56,6 @@ OPTIONS
|
|||
--breaks
|
||||
Enable GFM line breaks. Only works with the gfm option.
|
||||
|
||||
--tables
|
||||
Enable GFM tables. Only works with the gfm option.
|
||||
|
||||
--sanitize
|
||||
Sanitize output. Ignore any HTML input.
|
||||
|
||||
|
|
6
packages/markdown/marked/marked.min.js
vendored
6
packages/markdown/marked/marked.min.js
vendored
File diff suppressed because one or more lines are too long
3626
packages/markdown/marked/package-lock.json
generated
Normal file
3626
packages/markdown/marked/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,13 +2,14 @@
|
|||
"name": "marked",
|
||||
"description": "A markdown parser built for speed",
|
||||
"author": "Christopher Jeffrey",
|
||||
"version": "0.6.2",
|
||||
"main": "./lib/marked.js",
|
||||
"version": "0.8.0",
|
||||
"main": "./src/marked.js",
|
||||
"bin": "./bin/marked",
|
||||
"man": "./man/marked.1",
|
||||
"files": [
|
||||
"bin/",
|
||||
"lib/",
|
||||
"src/",
|
||||
"man/",
|
||||
"marked.min.js"
|
||||
],
|
||||
|
@ -29,41 +30,48 @@
|
|||
"html"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@markedjs/html-differ": "^2.0.1",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"commonmark": "0.x",
|
||||
"eslint": "^5.15.1",
|
||||
"eslint-config-standard": "^12.0.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-node": "^8.0.1",
|
||||
"eslint-plugin-promise": "^4.0.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"eslint-plugin-vuln-regex-detector": "^1.0.4",
|
||||
"front-matter": "^3.0.1",
|
||||
"glob-to-regexp": "^0.4.0",
|
||||
"jasmine": "^3.3.1",
|
||||
"markdown": "0.x",
|
||||
"markdown-it": "8.x",
|
||||
"node-fetch": "^2.3.0",
|
||||
"uglify-js": "^3.4.9"
|
||||
"@babel/core": "^7.8.7",
|
||||
"@babel/preset-env": "^7.8.7",
|
||||
"@markedjs/html-differ": "^3.0.0",
|
||||
"cheerio": "^0.22.0",
|
||||
"commonmark": "^0.29.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-standard": "^14.1.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"front-matter": "^3.1.0",
|
||||
"jasmine": "^3.5.0",
|
||||
"markdown": "^0.5.0",
|
||||
"markdown-it": "^10.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"rollup": "^2.0.6",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-license": "^0.13.0",
|
||||
"uglify-js": "^3.8.0",
|
||||
"vuln-regex-detector": "^1.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jasmine --config=jasmine.json",
|
||||
"test:all": "npm test && npm run test:lint",
|
||||
"test:unit": "npm test -- test/unit/**/*-spec.js",
|
||||
"test:specs": "npm test -- test/specs/**/*-spec.js",
|
||||
"test:cm": "npm test -- test/specs/commonmark/**/*-spec.js",
|
||||
"test:gfm": "npm test -- test/specs/gfm/**/*-spec.js",
|
||||
"test:marked": "npm test -- test/specs/marked/**/*-spec.js",
|
||||
"test:old": "node test",
|
||||
"test:lint": "eslint bin/marked .",
|
||||
"test:redos": "eslint --plugin vuln-regex-detector --rule '\"vuln-regex-detector/no-vuln-regex\": 2' lib/marked.js",
|
||||
"test:node4": "npx node@4 ./node_modules/jasmine/bin/jasmine.js --config=jasmine.json",
|
||||
"bench": "node test --bench",
|
||||
"test:redos": "node test/vuln-regex.js",
|
||||
"test:update": "node test/update-specs.js",
|
||||
"bench": "npm run rollup && node test/bench.js",
|
||||
"lint": "eslint --fix bin/marked .",
|
||||
"build": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js",
|
||||
"preversion": "npm run build && (git diff --quiet || git commit -am 'minify')"
|
||||
"build:reset": "git checkout upstream/master lib/marked.js lib/marked.esm.js marked.min.js",
|
||||
"build": "npm run rollup && npm run minify",
|
||||
"rollup": "npm run rollup:umd && npm run rollup:esm",
|
||||
"rollup:umd": "rollup -c rollup.config.js",
|
||||
"rollup:esm": "rollup -c rollup.config.esm.js",
|
||||
"minify": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js",
|
||||
"preversion": "npm run build && (git diff --quiet || git commit -am 'build')"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">= 8.16.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
In this directory:
|
||||
|
||||
#
|
||||
# MarkdownTester -- Run tests for Markdown implementations
|
||||
#
|
||||
# Copyright (c) 2004-2005 John Gruber
|
||||
# <http://daringfireball.net/projects/markdown/>
|
||||
#
|
||||
|
||||
Partially modified for testing purposes.
|
|
@ -1,5 +0,0 @@
|
|||
<!doctype html>
|
||||
<title>marked tests</title>
|
||||
<p>testing...</p>
|
||||
<script src="marked.js"></script>
|
||||
<script src="test.js"></script>
|
|
@ -1,39 +0,0 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
var testMod = require('../'),
|
||||
load = testMod.load;
|
||||
|
||||
var express = require('express'),
|
||||
app = express();
|
||||
|
||||
var files = load();
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
var setHeader = res.setHeader;
|
||||
res.setHeader = function(name) {
|
||||
switch (name) {
|
||||
case 'Cache-Control':
|
||||
case 'Last-Modified':
|
||||
case 'ETag':
|
||||
return;
|
||||
}
|
||||
return setHeader.apply(res, arguments);
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/test.js', function(req, res, next) {
|
||||
var test = fs.readFileSync(path.join(__dirname, 'test.js'), 'utf8');
|
||||
var testScript = test.replace('__TESTS__', JSON.stringify(files))
|
||||
.replace('__MAIN__', testMod.runTests + '')
|
||||
.replace('__LIBS__', testMod.testFile + '');
|
||||
|
||||
res.contentType('.js');
|
||||
res.send(testScript);
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, '/../../lib')));
|
||||
app.use(express.static(__dirname));
|
||||
|
||||
app.listen(8080);
|
|
@ -1,66 +0,0 @@
|
|||
|
||||
;(function() {
|
||||
var console = {},
|
||||
files = __TESTS__; // eslint-disable-line no-undef
|
||||
|
||||
console.log = function(text) {
|
||||
var args = Array.prototype.slice.call(arguments, 1),
|
||||
i = 0;
|
||||
|
||||
text = text.replace(/%\w/g, function() {
|
||||
return args[i++] || '';
|
||||
});
|
||||
|
||||
if (window.console) window.console.log(text);
|
||||
document.body.innerHTML += '<pre>' + escape(text) + '</pre>';
|
||||
};
|
||||
|
||||
if (!Object.keys) {
|
||||
Object.keys = function(obj) {
|
||||
var out = [],
|
||||
key;
|
||||
|
||||
for (key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
out.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
}
|
||||
|
||||
if (!Array.prototype.forEach) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.forEach = function(callback, context) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
callback.call(context || null, this[i], i, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.trim) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
String.prototype.trim = function() {
|
||||
return this.replace(/^\s+|\s+$/g, '');
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function load() {
|
||||
return files;
|
||||
}
|
||||
|
||||
function escape(html, encode) {
|
||||
return html
|
||||
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
__LIBS__; // eslint-disable-line no-undef, no-unused-expressions
|
||||
|
||||
(__MAIN__)(); // eslint-disable-line no-undef
|
||||
}).call(this);
|
|
@ -1,26 +0,0 @@
|
|||
const marked = require('../../');
|
||||
const htmlDiffer = require('./html-differ.js');
|
||||
|
||||
beforeEach(() => {
|
||||
marked.setOptions(marked.getDefaults());
|
||||
|
||||
jasmine.addMatchers({
|
||||
toRender: () => {
|
||||
return {
|
||||
compare: (spec, expected) => {
|
||||
const result = {};
|
||||
const actual = marked(spec.markdown, spec.options);
|
||||
result.pass = htmlDiffer.isEqual(expected, actual);
|
||||
|
||||
if (result.pass) {
|
||||
result.message = `${spec.markdown}\n------\n\nExpected: Should Fail`;
|
||||
} else {
|
||||
const diff = htmlDiffer.firstDiff(actual, expected);
|
||||
result.message = `Expected: ${diff.expected}\n Actual: ${diff.actual}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
const HtmlDiffer = require('@markedjs/html-differ').HtmlDiffer;
|
||||
const htmlDiffer = new HtmlDiffer({ignoreSelfClosingSlash: true});
|
||||
|
||||
module.exports = {
|
||||
isEqual: htmlDiffer.isEqual.bind(htmlDiffer),
|
||||
firstDiff: (actual, expected, padding) => {
|
||||
padding = padding || 30;
|
||||
const result = htmlDiffer
|
||||
.diffHtml(actual, expected)
|
||||
.reduce((obj, diff) => {
|
||||
if (diff.added) {
|
||||
if (obj.firstIndex === null) {
|
||||
obj.firstIndex = obj.expected.length;
|
||||
}
|
||||
obj.expected += diff.value;
|
||||
} else if (diff.removed) {
|
||||
if (obj.firstIndex === null) {
|
||||
obj.firstIndex = obj.actual.length;
|
||||
}
|
||||
obj.actual += diff.value;
|
||||
} else {
|
||||
obj.actual += diff.value;
|
||||
obj.expected += diff.value;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, {
|
||||
firstIndex: null,
|
||||
actual: '',
|
||||
expected: ''
|
||||
});
|
||||
|
||||
return {
|
||||
actual: result.actual.substring(result.firstIndex - padding, result.firstIndex + padding),
|
||||
expected: result.expected.substring(result.firstIndex - padding, result.firstIndex + padding)
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,551 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
// 'use strict' is here so we can use let and const in node 4
|
||||
|
||||
/**
|
||||
* marked tests
|
||||
* Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
|
||||
* https://github.com/markedjs/marked
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modules
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fm = require('front-matter');
|
||||
const g2r = require('glob-to-regexp');
|
||||
let marked = require('../');
|
||||
const htmlDiffer = require('./helpers/html-differ.js');
|
||||
|
||||
/**
|
||||
* Load Tests
|
||||
*/
|
||||
|
||||
function load(options) {
|
||||
options = options || {};
|
||||
const dir = path.join(__dirname, 'compiled_tests');
|
||||
const glob = g2r(options.glob || '*', { extended: true });
|
||||
|
||||
const list = fs
|
||||
.readdirSync(dir)
|
||||
.filter(file => {
|
||||
return path.extname(file) === '.md';
|
||||
})
|
||||
.sort();
|
||||
|
||||
const files = list.reduce((obj, item) => {
|
||||
const name = path.basename(item, '.md');
|
||||
if (glob.test(name)) {
|
||||
const file = path.join(dir, item);
|
||||
const content = fm(fs.readFileSync(file, 'utf8'));
|
||||
|
||||
obj[name] = {
|
||||
options: content.attributes,
|
||||
text: content.body,
|
||||
html: fs.readFileSync(file.replace(/[^.]+$/, 'html'), 'utf8')
|
||||
};
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
if (options.bench || options.time) {
|
||||
if (!options.glob) {
|
||||
// Change certain tests to allow
|
||||
// comparison to older benchmark times.
|
||||
fs.readdirSync(path.join(__dirname, 'new')).forEach(name => {
|
||||
if (path.extname(name) === '.html') return;
|
||||
if (name === 'main.md') return;
|
||||
delete files[name];
|
||||
});
|
||||
}
|
||||
|
||||
if (files['backslash_escapes.md']) {
|
||||
files['backslash_escapes.md'] = {
|
||||
text: 'hello world \\[how](are you) today'
|
||||
};
|
||||
}
|
||||
|
||||
if (files['main.md']) {
|
||||
files['main.md'].text = files['main.md'].text.replace('* * *\n\n', '');
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Runner
|
||||
*/
|
||||
|
||||
function runTests(engine, options) {
|
||||
if (typeof engine !== 'function') {
|
||||
options = engine;
|
||||
engine = null;
|
||||
}
|
||||
|
||||
engine = engine || marked;
|
||||
options = options || {};
|
||||
|
||||
let succeeded = 0;
|
||||
let failed = 0;
|
||||
const files = options.files || load(options);
|
||||
const filenames = Object.keys(files);
|
||||
|
||||
if (options.marked) {
|
||||
marked.setOptions(options.marked);
|
||||
}
|
||||
|
||||
for (let i = 0; i < filenames.length; i++) {
|
||||
const filename = filenames[i];
|
||||
const file = files[filename];
|
||||
|
||||
const success = testFile(engine, file, filename, i + 1);
|
||||
|
||||
if (success) {
|
||||
succeeded++;
|
||||
} else {
|
||||
failed++;
|
||||
if (options.stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n%d/%d tests completed successfully.', succeeded, filenames.length);
|
||||
if (failed) console.log('%d/%d tests failed.', failed, filenames.length);
|
||||
|
||||
return !failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a file
|
||||
*/
|
||||
|
||||
function testFile(engine, file, filename, index) {
|
||||
const opts = Object.keys(file.options);
|
||||
|
||||
if (marked._original) {
|
||||
marked.defaults = marked._original;
|
||||
delete marked._original;
|
||||
}
|
||||
|
||||
console.log('#%d. Test %s', index, filename);
|
||||
|
||||
if (opts.length) {
|
||||
marked._original = marked.defaults;
|
||||
marked.defaults = {};
|
||||
Object.keys(marked._original).forEach(key => {
|
||||
marked.defaults[key] = marked._original[key];
|
||||
});
|
||||
opts.forEach(key => {
|
||||
if (marked.defaults.hasOwnProperty(key)) {
|
||||
marked.defaults[key] = file.options[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const before = process.hrtime();
|
||||
|
||||
let text, html, elapsed;
|
||||
try {
|
||||
text = engine(file.text);
|
||||
html = file.html;
|
||||
} catch (e) {
|
||||
elapsed = process.hrtime(before);
|
||||
console.log('\n failed in %dms\n', prettyElapsedTime(elapsed));
|
||||
throw e;
|
||||
}
|
||||
|
||||
elapsed = process.hrtime(before);
|
||||
|
||||
if (htmlDiffer.isEqual(text, html)) {
|
||||
if (elapsed[0] > 0) {
|
||||
console.log('\n failed because it took too long.\n\n passed in %dms\n', prettyElapsedTime(elapsed));
|
||||
return false;
|
||||
}
|
||||
console.log(' passed in %dms', prettyElapsedTime(elapsed));
|
||||
return true;
|
||||
}
|
||||
|
||||
const diff = htmlDiffer.firstDiff(text, html);
|
||||
|
||||
console.log('\n failed in %dms', prettyElapsedTime(elapsed));
|
||||
console.log(' Expected: %s', diff.expected);
|
||||
console.log(' Actual: %s\n', diff.actual);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark a function
|
||||
*/
|
||||
|
||||
function bench(name, files, engine) {
|
||||
const start = Date.now();
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
for (const filename in files) {
|
||||
engine(files[filename].text);
|
||||
}
|
||||
}
|
||||
|
||||
const end = Date.now();
|
||||
|
||||
console.log('%s completed in %dms.', name, end - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark all engines
|
||||
*/
|
||||
|
||||
function runBench(options) {
|
||||
options = options || {};
|
||||
const files = load(options);
|
||||
|
||||
// Non-GFM, Non-pedantic
|
||||
marked.setOptions({
|
||||
gfm: false,
|
||||
tables: false,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: false
|
||||
});
|
||||
if (options.marked) {
|
||||
marked.setOptions(options.marked);
|
||||
}
|
||||
bench('marked', files, marked);
|
||||
|
||||
// GFM
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
tables: false,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: false
|
||||
});
|
||||
if (options.marked) {
|
||||
marked.setOptions(options.marked);
|
||||
}
|
||||
bench('marked (gfm)', files, marked);
|
||||
|
||||
// Pedantic
|
||||
marked.setOptions({
|
||||
gfm: false,
|
||||
tables: false,
|
||||
breaks: false,
|
||||
pedantic: true,
|
||||
sanitize: false,
|
||||
smartLists: false
|
||||
});
|
||||
if (options.marked) {
|
||||
marked.setOptions(options.marked);
|
||||
}
|
||||
bench('marked (pedantic)', files, marked);
|
||||
|
||||
try {
|
||||
bench('commonmark', files, (() => {
|
||||
const commonmark = require('commonmark');
|
||||
const parser = new commonmark.Parser();
|
||||
const writer = new commonmark.HtmlRenderer();
|
||||
return function (text) {
|
||||
return writer.render(parser.parse(text));
|
||||
};
|
||||
})());
|
||||
} catch (e) {
|
||||
console.log('Could not bench commonmark. (Error: %s)', e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
bench('markdown-it', files, (() => {
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const md = new MarkdownIt();
|
||||
return md.render.bind(md);
|
||||
})());
|
||||
} catch (e) {
|
||||
console.log('Could not bench markdown-it. (Error: %s)', e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
bench('markdown.js', files, (() => {
|
||||
const markdown = require('markdown').markdown;
|
||||
return markdown.toHTML.bind(markdown);
|
||||
})());
|
||||
} catch (e) {
|
||||
console.log('Could not bench markdown.js. (Error: %s)', e.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple one-time benchmark
|
||||
*/
|
||||
|
||||
function time(options) {
|
||||
options = options || {};
|
||||
const files = load(options);
|
||||
if (options.marked) {
|
||||
marked.setOptions(options.marked);
|
||||
}
|
||||
bench('marked', files, marked);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown Test Suite Fixer
|
||||
* This function is responsible for "fixing"
|
||||
* the markdown test suite. There are
|
||||
* certain aspects of the suite that
|
||||
* are strange or might make tests
|
||||
* fail for reasons unrelated to
|
||||
* conformance.
|
||||
*/
|
||||
|
||||
function fix() {
|
||||
['compiled_tests', 'original', 'new', 'redos'].forEach(dir => {
|
||||
try {
|
||||
fs.mkdirSync(path.resolve(__dirname, dir));
|
||||
} catch (e) {
|
||||
// directory already exists
|
||||
}
|
||||
});
|
||||
|
||||
// rm -rf tests
|
||||
fs.readdirSync(path.resolve(__dirname, 'compiled_tests')).forEach(file => {
|
||||
fs.unlinkSync(path.resolve(__dirname, 'compiled_tests', file));
|
||||
});
|
||||
|
||||
// cp -r original tests
|
||||
fs.readdirSync(path.resolve(__dirname, 'original')).forEach(file => {
|
||||
let text = fs.readFileSync(path.resolve(__dirname, 'original', file), 'utf8');
|
||||
|
||||
if (path.extname(file) === '.md') {
|
||||
if (fm.test(text)) {
|
||||
text = fm(text);
|
||||
text = `---\n${text.frontmatter}\ngfm: false\n---\n${text.body}`;
|
||||
} else {
|
||||
text = `---\ngfm: false\n---\n${text}`;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), text);
|
||||
});
|
||||
|
||||
// node fix.js
|
||||
const dir = path.join(__dirname, 'compiled_tests');
|
||||
|
||||
fs.readdirSync(dir).filter(file => {
|
||||
return path.extname(file) === '.html';
|
||||
}).forEach(file => {
|
||||
file = path.join(dir, file);
|
||||
let html = fs.readFileSync(file, 'utf8');
|
||||
|
||||
// fix unencoded quotes
|
||||
html = html
|
||||
.replace(/='([^\n']*)'(?=[^<>\n]*>)/g, '=&__APOS__;$1&__APOS__;')
|
||||
.replace(/="([^\n"]*)"(?=[^<>\n]*>)/g, '=&__QUOT__;$1&__QUOT__;')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/&__QUOT__;/g, '"')
|
||||
.replace(/&__APOS__;/g, '\'');
|
||||
|
||||
fs.writeFileSync(file, html);
|
||||
});
|
||||
|
||||
// turn <hr /> into <hr>
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
file = path.join(dir, file);
|
||||
let text = fs.readFileSync(file, 'utf8');
|
||||
|
||||
text = text.replace(/(<|<)hr\s*\/(>|>)/g, '$1hr$2');
|
||||
|
||||
fs.writeFileSync(file, text);
|
||||
});
|
||||
|
||||
// markdown does some strange things.
|
||||
// it does not encode naked `>`, marked does.
|
||||
{
|
||||
const file = `${dir}/amps_and_angles_encoding.html`;
|
||||
const html = fs.readFileSync(file, 'utf8')
|
||||
.replace('6 > 5.', '6 > 5.');
|
||||
|
||||
fs.writeFileSync(file, html);
|
||||
}
|
||||
|
||||
// cp new/* tests/
|
||||
fs.readdirSync(path.resolve(__dirname, 'new')).forEach(file => {
|
||||
fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file),
|
||||
fs.readFileSync(path.resolve(__dirname, 'new', file)));
|
||||
});
|
||||
|
||||
// cp redos/* tests/
|
||||
fs.readdirSync(path.resolve(__dirname, 'redos')).forEach(file => {
|
||||
fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file),
|
||||
fs.readFileSync(path.resolve(__dirname, 'redos', file)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument Parsing
|
||||
*/
|
||||
|
||||
function parseArg(argv) {
|
||||
argv = argv.slice(2);
|
||||
|
||||
const options = {};
|
||||
const orphans = [];
|
||||
|
||||
function getarg() {
|
||||
let arg = argv.shift();
|
||||
|
||||
if (arg.indexOf('--') === 0) {
|
||||
// e.g. --opt
|
||||
arg = arg.split('=');
|
||||
if (arg.length > 1) {
|
||||
// e.g. --opt=val
|
||||
argv.unshift(arg.slice(1).join('='));
|
||||
}
|
||||
arg = arg[0];
|
||||
} else if (arg[0] === '-') {
|
||||
if (arg.length > 2) {
|
||||
// e.g. -abc
|
||||
argv = arg.substring(1).split('').map(ch => {
|
||||
return `-${ch}`;
|
||||
}).concat(argv);
|
||||
arg = argv.shift();
|
||||
} else {
|
||||
// e.g. -a
|
||||
}
|
||||
} else {
|
||||
// e.g. foo
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
while (argv.length) {
|
||||
let arg = getarg();
|
||||
switch (arg) {
|
||||
case '-f':
|
||||
case '--fix':
|
||||
case 'fix':
|
||||
if (options.fix !== false) {
|
||||
options.fix = true;
|
||||
}
|
||||
break;
|
||||
case '--no-fix':
|
||||
case 'no-fix':
|
||||
options.fix = false;
|
||||
break;
|
||||
case '-b':
|
||||
case '--bench':
|
||||
options.bench = true;
|
||||
break;
|
||||
case '-s':
|
||||
case '--stop':
|
||||
options.stop = true;
|
||||
break;
|
||||
case '-t':
|
||||
case '--time':
|
||||
options.time = true;
|
||||
break;
|
||||
case '-m':
|
||||
case '--minified':
|
||||
options.minified = true;
|
||||
break;
|
||||
case '--glob':
|
||||
arg = argv.shift();
|
||||
options.glob = arg.replace(/^=/, '');
|
||||
break;
|
||||
default:
|
||||
if (arg.indexOf('--') === 0) {
|
||||
const opt = camelize(arg.replace(/^--(no-)?/, ''));
|
||||
if (!marked.defaults.hasOwnProperty(opt)) {
|
||||
continue;
|
||||
}
|
||||
options.marked = options.marked || {};
|
||||
if (arg.indexOf('--no-') === 0) {
|
||||
options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
|
||||
? null
|
||||
: false;
|
||||
} else {
|
||||
options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
|
||||
? argv.shift()
|
||||
: true;
|
||||
}
|
||||
} else {
|
||||
orphans.push(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function camelize(text) {
|
||||
return text.replace(/(\w)-(\w)/g, (_, a, b) => a + b.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
|
||||
function main(argv) {
|
||||
const opt = parseArg(argv);
|
||||
|
||||
if (opt.fix !== false) {
|
||||
fix();
|
||||
}
|
||||
|
||||
if (opt.fix) {
|
||||
// only run fix
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt.bench) {
|
||||
return runBench(opt);
|
||||
}
|
||||
|
||||
if (opt.time) {
|
||||
return time(opt);
|
||||
}
|
||||
|
||||
if (opt.minified) {
|
||||
marked = require('../marked.min.js');
|
||||
}
|
||||
return runTests(opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*/
|
||||
|
||||
if (!module.parent) {
|
||||
process.title = 'marked';
|
||||
process.exit(main(process.argv.slice()) ? 0 : 1);
|
||||
} else {
|
||||
exports = main;
|
||||
exports.main = main;
|
||||
exports.runTests = runTests;
|
||||
exports.testFile = testFile;
|
||||
exports.runBench = runBench;
|
||||
exports.load = load;
|
||||
exports.bench = bench;
|
||||
module.exports = exports;
|
||||
}
|
||||
|
||||
// returns time to millisecond granularity
|
||||
function prettyElapsedTime(hrtimeElapsed) {
|
||||
const seconds = hrtimeElapsed[0];
|
||||
const frac = Math.round(hrtimeElapsed[1] / 1e3) / 1e3;
|
||||
return seconds * 1e3 + frac;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const folder = process.argv[2];
|
||||
const jsonFile = process.argv[3];
|
||||
|
||||
if (!folder || !jsonFile) {
|
||||
console.log('node ./json-to-files.js {path to folder} {path to json file}');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const specs = require(jsonFile);
|
||||
|
||||
const files = specs.reduce((obj, spec) => {
|
||||
if (!obj[spec.section]) {
|
||||
obj[spec.section] = {
|
||||
md: [],
|
||||
html: [],
|
||||
options: {}
|
||||
};
|
||||
}
|
||||
|
||||
obj[spec.section].md.push(spec.markdown);
|
||||
obj[spec.section].html.push(spec.html);
|
||||
Object.assign(obj[spec.section].options, spec.options);
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
try {
|
||||
fs.mkdirSync(folder, {recursive: true});
|
||||
} catch (ex) {
|
||||
// already exists
|
||||
}
|
||||
|
||||
for (const section in files) {
|
||||
const file = files[section];
|
||||
const name = section.toLowerCase().replace(' ', '_');
|
||||
const frontMatter = Object.keys(file.options).map(opt => {
|
||||
let value = file.options[opt];
|
||||
if (typeof value !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
return `${opt}: ${value}`;
|
||||
}).join('\n');
|
||||
|
||||
let markdown = file.md.join('\n\n');
|
||||
if (frontMatter) {
|
||||
markdown = `---\n${frontMatter}\n---\n\n${markdown}`;
|
||||
}
|
||||
const html = file.html.join('\n\n');
|
||||
|
||||
const mdFile = path.resolve(folder, `${name}.md`);
|
||||
const htmlFile = path.resolve(folder, `${name}.html`);
|
||||
|
||||
if (fs.existsSync(mdFile) || fs.existsSync(htmlFile)) {
|
||||
throw new Error(`${name} already exists.`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(mdFile, markdown);
|
||||
fs.writeFileSync(htmlFile, html);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<ul>
|
||||
<li>This should be</li>
|
||||
<li>An unordered list</li>
|
||||
</ul>
|
||||
|
||||
<ol>
|
||||
<li>This should be</li>
|
||||
<li>An unordered list</li>
|
||||
</ol>
|
|
@ -1,5 +0,0 @@
|
|||
* This should be
|
||||
* An unordered list
|
||||
|
||||
1. This should be
|
||||
2. An unordered list
|
|
@ -1,3 +0,0 @@
|
|||
<p>hello world
|
||||
<a href="http://example.com">http://example.com</a>
|
||||
</p>
|
|
@ -1,2 +0,0 @@
|
|||
hello world
|
||||
<http://example.com>
|
|
@ -1,15 +0,0 @@
|
|||
<p>(See <a href="https://www.example.com/fhqwhgads">https://www.example.com/fhqwhgads</a>.)</p>
|
||||
|
||||
<p>((<a href="http://foo.com">http://foo.com</a>))</p>
|
||||
|
||||
<p>((<a href="http://foo.com">http://foo.com</a>.))</p>
|
||||
|
||||
<p><a href="HTTP://FOO.COM">HTTP://FOO.COM</a></p>
|
||||
|
||||
<p><a href="hTtP://fOo.CoM">hTtP://fOo.CoM</a></p>
|
||||
|
||||
<p><del><a href="mailto:hello@email.com">hello@email.com</a></del></p>
|
||||
|
||||
<p><strong><a href="mailto:me@example.com">me@example.com</a></strong></p>
|
||||
|
||||
<p><strong><a href="mailto:test@test.com">test@test.com</a></strong></p>
|
|
@ -1,15 +0,0 @@
|
|||
(See https://www.example.com/fhqwhgads.)
|
||||
|
||||
((http://foo.com))
|
||||
|
||||
((http://foo.com.))
|
||||
|
||||
HTTP://FOO.COM
|
||||
|
||||
hTtP://fOo.CoM
|
||||
|
||||
~~hello@email.com~~
|
||||
|
||||
**me@example.com**
|
||||
|
||||
__test@test.com__
|
|
@ -1,3 +0,0 @@
|
|||
<p>This fails in markdown.pl and upskirt:</p>
|
||||
|
||||
<ul><li>hello<blockquote><p>world</p></blockquote></li></ul>
|
|
@ -1,4 +0,0 @@
|
|||
This fails in markdown.pl and upskirt:
|
||||
|
||||
* hello
|
||||
> world
|
|
@ -1 +0,0 @@
|
|||
<p><a href="/url">hi</a></p>
|
|
@ -1,3 +0,0 @@
|
|||
[hi]
|
||||
|
||||
[HI]: /url
|
|
@ -1,91 +0,0 @@
|
|||
<p>Here are some valid autolinks:</p>
|
||||
|
||||
<h3 id="example-565">Example 565</h3>
|
||||
|
||||
<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
|
||||
|
||||
<h3 id="example-566">Example 566</h3>
|
||||
|
||||
<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p>
|
||||
|
||||
<h3 id="example-567">Example 567</h3>
|
||||
|
||||
<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
|
||||
|
||||
<h3 id="example-568">Example 568</h3>
|
||||
|
||||
<p>Uppercase is also fine:</p>
|
||||
|
||||
<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
|
||||
|
||||
<p>Note that many strings that count as absolute URIs for purposes of this spec are not valid URIs, because their schemes are not registered or because of other problems with their syntax:</p>
|
||||
|
||||
<h3 id="example-569">Example 569</h3>
|
||||
|
||||
<p><a href="a+b+c:d">a+b+c:d</a></p>
|
||||
|
||||
<h3 id="example-570">Example 570</h3>
|
||||
|
||||
<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
|
||||
|
||||
<h3 id="example-571">Example 571</h3>
|
||||
|
||||
<p><a href="http://../">http://../</a></p>
|
||||
|
||||
<h3 id="example-572">Example 572</h3>
|
||||
|
||||
<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>
|
||||
|
||||
<h3 id="example-573">Example 573</h3>
|
||||
|
||||
<p>Spaces are not allowed in autolinks:</p>
|
||||
|
||||
<p><http://foo.bar/baz bim></p>
|
||||
|
||||
<h3 id="example-574">Example 574</h3>
|
||||
|
||||
<p>Backslash-escapes do not work inside autolinks:</p>
|
||||
|
||||
<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
|
||||
|
||||
<p>Examples of email autolinks:</p>
|
||||
|
||||
<h3 id="example-575">Example 575</h3>
|
||||
|
||||
<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
|
||||
|
||||
<h3 id="example-576">Example 576</h3>
|
||||
|
||||
<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
|
||||
|
||||
<h3 id="example-577">Example 577</h3>
|
||||
|
||||
<p>Backslash-escapes do not work inside email autolinks:</p>
|
||||
|
||||
<p><foo+@bar.example.com></p>
|
||||
|
||||
<p>These are not autolinks:</p>
|
||||
|
||||
<h3 id="example-578">Example 578</h3>
|
||||
|
||||
<p><></p>
|
||||
|
||||
<h3 id="example-579">Example 579</h3>
|
||||
|
||||
<p>< http://foo.bar ></p>
|
||||
|
||||
<h3 id="example-580">Example 580</h3>
|
||||
|
||||
<p><m:abc></p>
|
||||
|
||||
<h3 id="example-581">Example 581</h3>
|
||||
|
||||
<p><foo.bar.baz></p>
|
||||
|
||||
<h3 id="example-582">Example 582</h3>
|
||||
|
||||
<p>http://example.com</p>
|
||||
|
||||
<h3 id="example-583">Example 583</h3>
|
||||
|
||||
<p>foo@bar.example.com</p>
|
|
@ -1,96 +0,0 @@
|
|||
---
|
||||
gfm: false
|
||||
mangle: false
|
||||
---
|
||||
|
||||
Here are some valid autolinks:
|
||||
|
||||
### Example 565
|
||||
|
||||
<http://foo.bar.baz>
|
||||
|
||||
### Example 566
|
||||
|
||||
<http://foo.bar.baz/test?q=hello&id=22&boolean>
|
||||
|
||||
### Example 567
|
||||
|
||||
<irc://foo.bar:2233/baz>
|
||||
|
||||
### Example 568
|
||||
|
||||
Uppercase is also fine:
|
||||
|
||||
<MAILTO:FOO@BAR.BAZ>
|
||||
|
||||
Note that many strings that count as absolute URIs for purposes of this spec are not valid URIs, because their schemes are not registered or because of other problems with their syntax:
|
||||
|
||||
### Example 569
|
||||
|
||||
<a+b+c:d>
|
||||
|
||||
### Example 570
|
||||
|
||||
<made-up-scheme://foo,bar>
|
||||
|
||||
### Example 571
|
||||
|
||||
<http://../>
|
||||
|
||||
### Example 572
|
||||
|
||||
<localhost:5001/foo>
|
||||
|
||||
### Example 573
|
||||
|
||||
Spaces are not allowed in autolinks:
|
||||
|
||||
<http://foo.bar/baz bim>
|
||||
|
||||
### Example 574
|
||||
|
||||
Backslash-escapes do not work inside autolinks:
|
||||
|
||||
<http://example.com/\[\>
|
||||
|
||||
Examples of email autolinks:
|
||||
|
||||
### Example 575
|
||||
|
||||
<foo@bar.example.com>
|
||||
|
||||
### Example 576
|
||||
|
||||
<foo+special@Bar.baz-bar0.com>
|
||||
|
||||
### Example 577
|
||||
|
||||
Backslash-escapes do not work inside email autolinks:
|
||||
|
||||
<foo\+@bar.example.com>
|
||||
|
||||
These are not autolinks:
|
||||
|
||||
### Example 578
|
||||
|
||||
<>
|
||||
|
||||
### Example 579
|
||||
|
||||
< http://foo.bar >
|
||||
|
||||
### Example 580
|
||||
|
||||
<m:abc>
|
||||
|
||||
### Example 581
|
||||
|
||||
<foo.bar.baz>
|
||||
|
||||
### Example 582
|
||||
|
||||
http://example.com
|
||||
|
||||
### Example 583
|
||||
|
||||
foo@bar.example.com
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue