2017-09-10 19:41:34 +08:00
|
|
|
import moment from 'moment'
|
|
|
|
import { logger } from '../../logger'
|
2017-04-11 22:14:25 +08:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
let dateFormat = ''
|
2018-03-13 14:01:44 +08:00
|
|
|
let axisFormat = ''
|
2019-02-06 16:54:09 -02:00
|
|
|
let excludes = []
|
2017-09-14 20:59:58 +08:00
|
|
|
let title = ''
|
|
|
|
let sections = []
|
|
|
|
let tasks = []
|
|
|
|
let currentSection = ''
|
2017-04-11 22:14:25 +08:00
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const clear = function () {
|
2017-04-11 22:14:25 +08:00
|
|
|
sections = []
|
|
|
|
tasks = []
|
|
|
|
currentSection = ''
|
|
|
|
title = ''
|
|
|
|
taskCnt = 0
|
|
|
|
lastTask = undefined
|
|
|
|
lastTaskID = undefined
|
|
|
|
rawTasks = []
|
|
|
|
}
|
|
|
|
|
2018-03-13 14:01:44 +08:00
|
|
|
export const setAxisFormat = function (txt) {
|
|
|
|
axisFormat = txt
|
|
|
|
}
|
|
|
|
|
|
|
|
export const getAxisFormat = function () {
|
|
|
|
return axisFormat
|
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const setDateFormat = function (txt) {
|
2017-04-11 22:14:25 +08:00
|
|
|
dateFormat = txt
|
|
|
|
}
|
|
|
|
|
2019-02-06 16:54:09 -02:00
|
|
|
export const setExcludes = function (txt) {
|
2019-02-07 11:21:00 -02:00
|
|
|
excludes = txt.toLowerCase().split(/[\s,]+/)
|
2019-02-06 16:54:09 -02:00
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const setTitle = function (txt) {
|
2017-04-11 22:14:25 +08:00
|
|
|
title = txt
|
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const getTitle = function () {
|
2017-04-11 22:14:25 +08:00
|
|
|
return title
|
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const addSection = function (txt) {
|
2017-04-11 22:14:25 +08:00
|
|
|
currentSection = txt
|
|
|
|
sections.push(txt)
|
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const getTasks = function () {
|
2017-09-14 20:59:58 +08:00
|
|
|
let allItemsPricessed = compileTasks()
|
|
|
|
const maxDepth = 10
|
|
|
|
let iterationCount = 0
|
2017-04-11 22:14:25 +08:00
|
|
|
while (!allItemsPricessed && (iterationCount < maxDepth)) {
|
|
|
|
allItemsPricessed = compileTasks()
|
|
|
|
iterationCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
tasks = rawTasks
|
|
|
|
|
|
|
|
return tasks
|
|
|
|
}
|
2015-02-08 20:07:15 +01:00
|
|
|
|
2019-02-06 17:24:20 -02:00
|
|
|
const isInvalidDate = function (date, dateFormat, excludes) {
|
2019-02-06 17:25:39 -02:00
|
|
|
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
|
|
|
return true
|
|
|
|
}
|
2019-02-07 11:21:00 -02:00
|
|
|
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
|
|
|
return true
|
|
|
|
}
|
2019-02-06 17:25:39 -02:00
|
|
|
return excludes.indexOf(date.format(dateFormat.trim())) >= 0
|
2019-02-06 17:24:20 -02:00
|
|
|
}
|
2019-02-06 17:09:59 -02:00
|
|
|
|
2019-02-07 11:21:00 -02:00
|
|
|
const fixTaskDates = function (task, dateFormat, excludes) {
|
2019-02-07 11:22:46 -02:00
|
|
|
if (excludes.length && !task.manualEndTime) {
|
2019-02-07 11:21:00 -02:00
|
|
|
let startTime = moment(task.startTime).add(1, 'd')
|
|
|
|
let endTime = moment(task.endTime)
|
|
|
|
while (startTime.date() <= endTime.date()) {
|
|
|
|
if (isInvalidDate(startTime, dateFormat, excludes)) {
|
|
|
|
endTime.add(1, 'd')
|
|
|
|
}
|
|
|
|
startTime.add(1, 'd')
|
|
|
|
}
|
|
|
|
task.endTime = endTime.toDate()
|
2019-02-06 16:54:09 -02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 11:21:00 -02:00
|
|
|
const getStartDate = function (prevTime, dateFormat, str) {
|
2017-04-11 22:14:25 +08:00
|
|
|
str = str.trim()
|
2015-02-20 19:06:15 +01:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Test for after
|
2017-09-14 20:59:58 +08:00
|
|
|
const re = /^after\s+([\d\w-]+)/
|
|
|
|
const afterStatement = re.exec(str.trim())
|
2015-10-21 21:14:41 +02:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
if (afterStatement !== null) {
|
2017-09-14 20:59:58 +08:00
|
|
|
const task = findTaskById(afterStatement[1])
|
2015-10-24 12:44:47 +02:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
if (typeof task === 'undefined') {
|
2017-09-14 20:59:58 +08:00
|
|
|
const dt = new Date()
|
2017-04-11 22:14:25 +08:00
|
|
|
dt.setHours(0, 0, 0, 0)
|
|
|
|
return dt
|
2015-02-08 20:07:15 +01:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
return task.endTime
|
|
|
|
}
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Check for actual date set
|
2019-02-06 17:09:59 -02:00
|
|
|
let mDate = moment(str, dateFormat.trim(), true)
|
2019-02-06 16:54:09 -02:00
|
|
|
if (mDate.isValid()) {
|
2019-02-07 11:21:00 -02:00
|
|
|
return mDate.toDate()
|
2017-04-11 22:14:25 +08:00
|
|
|
} else {
|
2017-09-10 19:41:34 +08:00
|
|
|
logger.debug('Invalid date:' + str)
|
|
|
|
logger.debug('With date format:' + dateFormat.trim())
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Default date - now
|
2019-02-07 11:21:00 -02:00
|
|
|
return new Date()
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2015-02-08 20:07:15 +01:00
|
|
|
|
2019-02-07 11:21:00 -02:00
|
|
|
const getEndDate = function (prevTime, dateFormat, str) {
|
2017-04-11 22:14:25 +08:00
|
|
|
str = str.trim()
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Check for actual date
|
2019-02-06 17:09:59 -02:00
|
|
|
let mDate = moment(str, dateFormat.trim(), true)
|
2019-02-06 16:54:09 -02:00
|
|
|
if (mDate.isValid()) {
|
|
|
|
return mDate.toDate()
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const d = moment(prevTime)
|
2017-04-16 23:48:36 +08:00
|
|
|
// Check for length
|
2017-09-14 20:59:58 +08:00
|
|
|
const re = /^([\d]+)([wdhms])/
|
|
|
|
const durationStatement = re.exec(str.trim())
|
2017-04-11 22:14:25 +08:00
|
|
|
|
|
|
|
if (durationStatement !== null) {
|
|
|
|
switch (durationStatement[2]) {
|
|
|
|
case 's':
|
|
|
|
d.add(durationStatement[1], 'seconds')
|
|
|
|
break
|
|
|
|
case 'm':
|
|
|
|
d.add(durationStatement[1], 'minutes')
|
|
|
|
break
|
|
|
|
case 'h':
|
|
|
|
d.add(durationStatement[1], 'hours')
|
|
|
|
break
|
|
|
|
case 'd':
|
|
|
|
d.add(durationStatement[1], 'days')
|
|
|
|
break
|
|
|
|
case 'w':
|
|
|
|
d.add(durationStatement[1], 'weeks')
|
|
|
|
break
|
2015-02-08 20:07:15 +01:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2017-04-16 23:48:36 +08:00
|
|
|
// Default date - now
|
2019-02-07 11:21:00 -02:00
|
|
|
return d.toDate()
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
let taskCnt = 0
|
|
|
|
const parseId = function (idStr) {
|
2017-04-11 22:14:25 +08:00
|
|
|
if (typeof idStr === 'undefined') {
|
|
|
|
taskCnt = taskCnt + 1
|
|
|
|
return 'task' + taskCnt
|
|
|
|
}
|
|
|
|
return idStr
|
|
|
|
}
|
2015-02-08 20:07:15 +01:00
|
|
|
// id, startDate, endDate
|
|
|
|
// id, startDate, length
|
|
|
|
// id, after x, endDate
|
|
|
|
// id, after x, length
|
|
|
|
// startDate, endDate
|
|
|
|
// startDate, length
|
|
|
|
// after x, endDate
|
|
|
|
// after x, length
|
|
|
|
// endDate
|
|
|
|
// length
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const compileData = function (prevTask, dataStr) {
|
|
|
|
let ds
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
if (dataStr.substr(0, 1) === ':') {
|
|
|
|
ds = dataStr.substr(1, dataStr.length)
|
|
|
|
} else {
|
|
|
|
ds = dataStr
|
|
|
|
}
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const data = ds.split(',')
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const task = {}
|
2015-08-25 14:49:56 -07:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Get tags like active, done cand crit
|
2017-09-14 20:59:58 +08:00
|
|
|
let matchFound = true
|
2017-04-11 22:14:25 +08:00
|
|
|
while (matchFound) {
|
|
|
|
matchFound = false
|
|
|
|
if (data[0].match(/^\s*active\s*$/)) {
|
|
|
|
task.active = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-02-08 20:07:15 +01:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
if (data[0].match(/^\s*done\s*$/)) {
|
|
|
|
task.done = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
if (data[0].match(/^\s*crit\s*$/)) {
|
|
|
|
task.crit = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2017-09-14 20:59:58 +08:00
|
|
|
for (let i = 0; i < data.length; i++) {
|
2017-04-11 22:14:25 +08:00
|
|
|
data[i] = data[i].trim()
|
|
|
|
}
|
|
|
|
|
2019-02-07 11:21:00 -02:00
|
|
|
let endTimeData = ''
|
2017-04-11 22:14:25 +08:00
|
|
|
switch (data.length) {
|
|
|
|
case 1:
|
|
|
|
task.id = parseId()
|
|
|
|
task.startTime = prevTask.endTime
|
2019-02-07 11:21:00 -02:00
|
|
|
endTimeData = data[0]
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
case 2:
|
|
|
|
task.id = parseId()
|
2019-02-07 11:21:00 -02:00
|
|
|
task.startTime = getStartDate(undefined, dateFormat, data[0])
|
|
|
|
endTimeData = data[1]
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
case 3:
|
|
|
|
task.id = parseId(data[0])
|
2019-02-07 11:21:00 -02:00
|
|
|
task.startTime = getStartDate(undefined, dateFormat, data[1])
|
|
|
|
endTimeData = data[2]
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
2019-02-07 11:21:00 -02:00
|
|
|
if (endTimeData) {
|
|
|
|
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData)
|
2019-02-07 11:22:46 -02:00
|
|
|
task.manualEndTime = endTimeData === moment(task.endTime).format(dateFormat.trim())
|
2019-02-07 11:21:00 -02:00
|
|
|
fixTaskDates(task, dateFormat, excludes)
|
|
|
|
}
|
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
return task
|
|
|
|
}
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const parseData = function (prevTaskId, dataStr) {
|
|
|
|
let ds
|
2017-04-11 22:14:25 +08:00
|
|
|
if (dataStr.substr(0, 1) === ':') {
|
|
|
|
ds = dataStr.substr(1, dataStr.length)
|
|
|
|
} else {
|
|
|
|
ds = dataStr
|
|
|
|
}
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const data = ds.split(',')
|
2017-04-11 22:14:25 +08:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const task = {}
|
2015-10-21 21:14:41 +02:00
|
|
|
|
2017-04-16 23:48:36 +08:00
|
|
|
// Get tags like active, done cand crit
|
2017-09-14 20:59:58 +08:00
|
|
|
let matchFound = true
|
2017-04-11 22:14:25 +08:00
|
|
|
while (matchFound) {
|
|
|
|
matchFound = false
|
|
|
|
if (data[0].match(/^\s*active\s*$/)) {
|
|
|
|
task.active = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
if (data[0].match(/^\s*done\s*$/)) {
|
|
|
|
task.done = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
if (data[0].match(/^\s*crit\s*$/)) {
|
|
|
|
task.crit = true
|
|
|
|
data.shift(1)
|
|
|
|
matchFound = true
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
2017-09-14 20:59:58 +08:00
|
|
|
for (let i = 0; i < data.length; i++) {
|
2017-04-11 22:14:25 +08:00
|
|
|
data[i] = data[i].trim()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (data.length) {
|
|
|
|
case 1:
|
|
|
|
task.id = parseId()
|
2017-04-16 23:48:36 +08:00
|
|
|
task.startTime = { type: 'prevTaskEnd', id: prevTaskId }
|
|
|
|
task.endTime = { data: data[0] }
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
case 2:
|
|
|
|
task.id = parseId()
|
2017-04-16 23:48:36 +08:00
|
|
|
task.startTime = { type: 'getStartDate', startData: data[0] }
|
|
|
|
task.endTime = { data: data[1] }
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
case 3:
|
|
|
|
task.id = parseId(data[0])
|
2017-04-16 23:48:36 +08:00
|
|
|
task.startTime = { type: 'getStartDate', startData: data[1] }
|
|
|
|
task.endTime = { data: data[2] }
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
return task
|
|
|
|
}
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
let lastTask
|
|
|
|
let lastTaskID
|
|
|
|
let rawTasks = []
|
|
|
|
const taskDb = {}
|
2017-09-10 21:23:04 +08:00
|
|
|
export const addTask = function (descr, data) {
|
2017-09-14 20:59:58 +08:00
|
|
|
const rawTask = {
|
2017-04-11 22:14:25 +08:00
|
|
|
section: currentSection,
|
|
|
|
type: currentSection,
|
|
|
|
processed: false,
|
2019-02-07 11:21:00 -02:00
|
|
|
manualEndTime: false,
|
2017-04-16 23:48:36 +08:00
|
|
|
raw: { data: data },
|
2017-04-11 22:14:25 +08:00
|
|
|
task: descr
|
|
|
|
}
|
2017-09-14 20:59:58 +08:00
|
|
|
const taskInfo = parseData(lastTaskID, data)
|
2017-04-11 22:14:25 +08:00
|
|
|
rawTask.raw.startTime = taskInfo.startTime
|
|
|
|
rawTask.raw.endTime = taskInfo.endTime
|
|
|
|
rawTask.id = taskInfo.id
|
|
|
|
rawTask.prevTaskId = lastTaskID
|
|
|
|
rawTask.active = taskInfo.active
|
|
|
|
rawTask.done = taskInfo.done
|
|
|
|
rawTask.crit = taskInfo.crit
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const pos = rawTasks.push(rawTask)
|
2017-04-11 22:14:25 +08:00
|
|
|
|
|
|
|
lastTaskID = rawTask.id
|
2017-04-16 23:48:36 +08:00
|
|
|
// Store cross ref
|
2017-04-11 22:14:25 +08:00
|
|
|
taskDb[rawTask.id] = pos - 1
|
|
|
|
}
|
2015-10-24 12:44:47 +02:00
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const findTaskById = function (id) {
|
2017-09-14 20:59:58 +08:00
|
|
|
const pos = taskDb[id]
|
2017-04-11 22:14:25 +08:00
|
|
|
return rawTasks[pos]
|
|
|
|
}
|
|
|
|
|
2017-09-10 21:23:04 +08:00
|
|
|
export const addTaskOrg = function (descr, data) {
|
2017-09-14 20:59:58 +08:00
|
|
|
const newTask = {
|
2017-04-11 22:14:25 +08:00
|
|
|
section: currentSection,
|
|
|
|
type: currentSection,
|
|
|
|
description: descr,
|
|
|
|
task: descr
|
|
|
|
}
|
2017-09-14 20:59:58 +08:00
|
|
|
const taskInfo = compileData(lastTask, data)
|
2017-04-11 22:14:25 +08:00
|
|
|
newTask.startTime = taskInfo.startTime
|
|
|
|
newTask.endTime = taskInfo.endTime
|
|
|
|
newTask.id = taskInfo.id
|
|
|
|
newTask.active = taskInfo.active
|
|
|
|
newTask.done = taskInfo.done
|
|
|
|
newTask.crit = taskInfo.crit
|
|
|
|
lastTask = newTask
|
|
|
|
tasks.push(newTask)
|
|
|
|
}
|
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
const compileTasks = function () {
|
|
|
|
const compileTask = function (pos) {
|
|
|
|
const task = rawTasks[pos]
|
|
|
|
let startTime = ''
|
2017-04-11 22:14:25 +08:00
|
|
|
switch (rawTasks[pos].raw.startTime.type) {
|
|
|
|
case 'prevTaskEnd':
|
2017-09-14 20:59:58 +08:00
|
|
|
const prevTask = findTaskById(task.prevTaskId)
|
2017-04-11 22:14:25 +08:00
|
|
|
task.startTime = prevTask.endTime
|
|
|
|
break
|
|
|
|
case 'getStartDate':
|
2019-02-07 11:21:00 -02:00
|
|
|
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData)
|
2017-04-11 22:14:25 +08:00
|
|
|
if (startTime) {
|
|
|
|
rawTasks[pos].startTime = startTime
|
2015-10-21 21:14:41 +02:00
|
|
|
}
|
2017-04-11 22:14:25 +08:00
|
|
|
break
|
|
|
|
}
|
2015-10-21 21:14:41 +02:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
if (rawTasks[pos].startTime) {
|
2019-02-07 11:21:00 -02:00
|
|
|
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data)
|
2017-04-11 22:14:25 +08:00
|
|
|
if (rawTasks[pos].endTime) {
|
|
|
|
rawTasks[pos].processed = true
|
2019-02-07 11:22:46 -02:00
|
|
|
rawTasks[pos].manualEndTime = rawTasks[pos].raw.endTime.data === moment(rawTasks[pos].endTime).format(dateFormat.trim())
|
|
|
|
fixTaskDates(rawTasks[pos], dateFormat, excludes)
|
2017-04-11 22:14:25 +08:00
|
|
|
}
|
|
|
|
}
|
2015-10-24 12:44:47 +02:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
return rawTasks[pos].processed
|
|
|
|
}
|
2015-10-21 21:14:41 +02:00
|
|
|
|
2017-09-14 20:59:58 +08:00
|
|
|
let allProcessed = true
|
|
|
|
for (let i = 0; i < rawTasks.length; i++) {
|
2017-04-11 22:14:25 +08:00
|
|
|
compileTask(i)
|
2015-10-24 12:44:47 +02:00
|
|
|
|
2017-04-11 22:14:25 +08:00
|
|
|
allProcessed = allProcessed && rawTasks[i].processed
|
|
|
|
}
|
|
|
|
return allProcessed
|
|
|
|
}
|
2017-09-10 21:51:48 +08:00
|
|
|
|
|
|
|
export default {
|
|
|
|
clear,
|
|
|
|
setDateFormat,
|
2018-03-13 14:01:44 +08:00
|
|
|
setAxisFormat,
|
|
|
|
getAxisFormat,
|
2017-09-10 21:51:48 +08:00
|
|
|
setTitle,
|
|
|
|
getTitle,
|
|
|
|
addSection,
|
|
|
|
getTasks,
|
|
|
|
addTask,
|
|
|
|
findTaskById,
|
2019-02-06 16:54:09 -02:00
|
|
|
addTaskOrg,
|
|
|
|
setExcludes
|
2017-09-10 21:51:48 +08:00
|
|
|
}
|