mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
split remaining utilities from shared
This commit is contained in:
@@ -39,45 +39,8 @@ api.planGemLimits = importedLibs.planGemLimits;
|
|||||||
|
|
||||||
preenHistory = importedLibs.preenHistory;
|
preenHistory = importedLibs.preenHistory;
|
||||||
|
|
||||||
/*
|
|
||||||
Preen 3-day past-completed To-Dos from Angular & mobile app
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.preenTodos = importedLibs.preenTodos;
|
api.preenTodos = importedLibs.preenTodos;
|
||||||
|
api.updateStore = importedLibs.updateStore;
|
||||||
/*
|
|
||||||
Update the in-browser store with new gear. FIXME this was in user.fns, but it was causing strange issues there
|
|
||||||
*/
|
|
||||||
|
|
||||||
sortOrder = _.reduce(content.gearTypes, (function(m, v, k) {
|
|
||||||
m[v] = k;
|
|
||||||
return m;
|
|
||||||
}), {});
|
|
||||||
|
|
||||||
api.updateStore = function(user) {
|
|
||||||
var changes;
|
|
||||||
if (!user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
changes = [];
|
|
||||||
_.each(content.gearTypes, function(type) {
|
|
||||||
var found;
|
|
||||||
found = _.find(content.gear.tree[type][user.stats["class"]], function(item) {
|
|
||||||
return !user.items.gear.owned[item.key];
|
|
||||||
});
|
|
||||||
if (found) {
|
|
||||||
changes.push(found);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
changes = changes.concat(_.filter(content.gear.flat, function(v) {
|
|
||||||
var ref;
|
|
||||||
return ((ref = v.klass) === 'special' || ref === 'mystery' || ref === 'armoire') && !user.items.gear.owned[v.key] && (typeof v.canOwn === "function" ? v.canOwn(user) : void 0);
|
|
||||||
}));
|
|
||||||
return _.sortBy(changes, function(c) {
|
|
||||||
return sortOrder[c.type];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -96,215 +59,18 @@ Misc Helpers
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
api.uuid = importedLibs.uuid;
|
api.uuid = importedLibs.uuid;
|
||||||
|
api.countExists = importedLibs.countExists;
|
||||||
api.countExists = function(items) {
|
|
||||||
return _.reduce(items, (function(m, v) {
|
|
||||||
return m + (v ? 1 : 0);
|
|
||||||
}), 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
api.taskDefaults = importedLibs.taskDefaults;
|
api.taskDefaults = importedLibs.taskDefaults;
|
||||||
|
api.percent = importedLibs.percent;
|
||||||
api.percent = function(x, y, dir) {
|
api.removeWhitespace = importedLibs.removeWhitespace;
|
||||||
var roundFn;
|
api.encodeiCalLink = importedLibs.encodeiCalLink;
|
||||||
switch (dir) {
|
api.gold = importedLibs.gold;
|
||||||
case "up":
|
api.silver = importedLibs.silver;
|
||||||
roundFn = Math.ceil;
|
api.taskClasses = importedLibs.taskClasses;
|
||||||
break;
|
api.friendlyTimestamp = importedLibs.friendlyTimestamp;
|
||||||
case "down":
|
api.newChatMessages = importedLibs.newChatMessages;
|
||||||
roundFn = Math.floor;
|
api.noTags = importedLibs.appliedTags;
|
||||||
break;
|
api.appliedTags = importedLibs.appliedTags;
|
||||||
default:
|
|
||||||
roundFn = Math.round;
|
|
||||||
}
|
|
||||||
if (x === 0) {
|
|
||||||
x = 1;
|
|
||||||
}
|
|
||||||
return Math.max(0, roundFn(x / y * 100));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Remove whitespace #FIXME are we using this anywwhere? Should we be?
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.removeWhitespace = function(str) {
|
|
||||||
if (!str) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return str.replace(/\s/g, '');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Encode the download link for .ics iCal file
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.encodeiCalLink = function(uid, apiToken) {
|
|
||||||
var loc, ref;
|
|
||||||
loc = (typeof window !== "undefined" && window !== null ? window.location.host : void 0) || (typeof process !== "undefined" && process !== null ? (ref = process.env) != null ? ref.BASE_URL : void 0 : void 0) || '';
|
|
||||||
return encodeURIComponent("http://" + loc + "/v1/users/" + uid + "/calendar.ics?apiToken=" + apiToken);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gold amount from their money
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.gold = function(num) {
|
|
||||||
if (num) {
|
|
||||||
return Math.floor(num);
|
|
||||||
} else {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Silver amount from their money
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.silver = function(num) {
|
|
||||||
if (num) {
|
|
||||||
return ("0" + Math.floor((num - Math.floor(num)) * 100)).slice(-2);
|
|
||||||
} else {
|
|
||||||
return "00";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Task classes given everything about the class
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.taskClasses = function(task, filters, dayStart, lastCron, showCompleted, main) {
|
|
||||||
var classes, completed, enabled, filter, priority, ref, repeat, type, value;
|
|
||||||
if (filters == null) {
|
|
||||||
filters = [];
|
|
||||||
}
|
|
||||||
if (dayStart == null) {
|
|
||||||
dayStart = 0;
|
|
||||||
}
|
|
||||||
if (lastCron == null) {
|
|
||||||
lastCron = +(new Date);
|
|
||||||
}
|
|
||||||
if (showCompleted == null) {
|
|
||||||
showCompleted = false;
|
|
||||||
}
|
|
||||||
if (main == null) {
|
|
||||||
main = false;
|
|
||||||
}
|
|
||||||
if (!task) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
type = task.type, completed = task.completed, value = task.value, repeat = task.repeat, priority = task.priority;
|
|
||||||
if (main) {
|
|
||||||
if (!task._editing) {
|
|
||||||
for (filter in filters) {
|
|
||||||
enabled = filters[filter];
|
|
||||||
if (enabled && !((ref = task.tags) != null ? ref[filter] : void 0)) {
|
|
||||||
return 'hidden';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
classes = type;
|
|
||||||
if (task._editing) {
|
|
||||||
classes += " beingEdited";
|
|
||||||
}
|
|
||||||
if (type === 'todo' || type === 'daily') {
|
|
||||||
if (completed || (type === 'daily' && !shouldDo(+(new Date), task, {
|
|
||||||
dayStart: dayStart
|
|
||||||
}))) {
|
|
||||||
classes += " completed";
|
|
||||||
} else {
|
|
||||||
classes += " uncompleted";
|
|
||||||
}
|
|
||||||
} else if (type === 'habit') {
|
|
||||||
if (task.down && task.up) {
|
|
||||||
classes += ' habit-wide';
|
|
||||||
}
|
|
||||||
if (!task.down && !task.up) {
|
|
||||||
classes += ' habit-narrow';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (priority === 0.1) {
|
|
||||||
classes += ' difficulty-trivial';
|
|
||||||
} else if (priority === 1) {
|
|
||||||
classes += ' difficulty-easy';
|
|
||||||
} else if (priority === 1.5) {
|
|
||||||
classes += ' difficulty-medium';
|
|
||||||
} else if (priority === 2) {
|
|
||||||
classes += ' difficulty-hard';
|
|
||||||
}
|
|
||||||
if (value < -20) {
|
|
||||||
classes += ' color-worst';
|
|
||||||
} else if (value < -10) {
|
|
||||||
classes += ' color-worse';
|
|
||||||
} else if (value < -1) {
|
|
||||||
classes += ' color-bad';
|
|
||||||
} else if (value < 1) {
|
|
||||||
classes += ' color-neutral';
|
|
||||||
} else if (value < 5) {
|
|
||||||
classes += ' color-good';
|
|
||||||
} else if (value < 10) {
|
|
||||||
classes += ' color-better';
|
|
||||||
} else {
|
|
||||||
classes += ' color-best';
|
|
||||||
}
|
|
||||||
return classes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Friendly timestamp
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.friendlyTimestamp = function(timestamp) {
|
|
||||||
return moment(timestamp).format('MM/DD h:mm:ss a');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Does user have new chat messages?
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.newChatMessages = function(messages, lastMessageSeen) {
|
|
||||||
if (!((messages != null ? messages.length : void 0) > 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (messages != null ? messages[0] : void 0) && (messages[0].id !== lastMessageSeen);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
are any tags active?
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.noTags = function(tags) {
|
|
||||||
return _.isEmpty(tags) || _.isEmpty(_.filter(tags, function(t) {
|
|
||||||
return t;
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Are there tags applied?
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.appliedTags = function(userTags, taskTags) {
|
|
||||||
var arr;
|
|
||||||
arr = [];
|
|
||||||
_.each(userTags, function(t) {
|
|
||||||
if (t == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (taskTags != null ? taskTags[t.id] : void 0) {
|
|
||||||
return arr.push(t.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return arr.join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
19
common/script/libs/appliedTags.js
Normal file
19
common/script/libs/appliedTags.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Are there tags applied?
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(userTags, taskTags) {
|
||||||
|
var arr;
|
||||||
|
arr = [];
|
||||||
|
_.each(userTags, function(t) {
|
||||||
|
if (t == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (taskTags != null ? taskTags[t.id] : void 0) {
|
||||||
|
return arr.push(t.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arr.join(', ');
|
||||||
|
};
|
||||||
7
common/script/libs/countExists.js
Normal file
7
common/script/libs/countExists.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
module.exports = function(items) {
|
||||||
|
return _.reduce(items, (function(m, v) {
|
||||||
|
return m + (v ? 1 : 0);
|
||||||
|
}), 0);
|
||||||
|
};
|
||||||
9
common/script/libs/encodeiCalLink.js
Normal file
9
common/script/libs/encodeiCalLink.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
Encode the download link for .ics iCal file
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(uid, apiToken) {
|
||||||
|
var loc, ref;
|
||||||
|
loc = (typeof window !== "undefined" && window !== null ? window.location.host : void 0) || (typeof process !== "undefined" && process !== null ? (ref = process.env) != null ? ref.BASE_URL : void 0 : void 0) || '';
|
||||||
|
return encodeURIComponent("http://" + loc + "/v1/users/" + uid + "/calendar.ics?apiToken=" + apiToken);
|
||||||
|
};
|
||||||
9
common/script/libs/friendlyTimestamp.js
Normal file
9
common/script/libs/friendlyTimestamp.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Friendly timestamp
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(timestamp) {
|
||||||
|
return moment(timestamp).format('MM/DD h:mm:ss a');
|
||||||
|
};
|
||||||
7
common/script/libs/gold.js
Normal file
7
common/script/libs/gold.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = function(num) {
|
||||||
|
if (num) {
|
||||||
|
return Math.floor(num);
|
||||||
|
} else {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -7,6 +7,19 @@ import preenTodos from './preenTodos';
|
|||||||
import dotSet from './dotSet';
|
import dotSet from './dotSet';
|
||||||
import dotGet from './dotGet';
|
import dotGet from './dotGet';
|
||||||
import preenHistory from './preenHistory';
|
import preenHistory from './preenHistory';
|
||||||
|
import countExists from './countExists';
|
||||||
|
import updateStore from './updateStore';
|
||||||
|
|
||||||
|
import appliedTags from './appliedTags';
|
||||||
|
import encodeiCalLink from './encodeiCalLink';
|
||||||
|
import friendlyTimestamp from './friendlyTimestamp';
|
||||||
|
import gold from './gold';
|
||||||
|
import newChatMessages from './newChatMessages';
|
||||||
|
import noTags from './noTags';
|
||||||
|
import percent from './percent';
|
||||||
|
import removeWhitespace from './removeWhitespace';
|
||||||
|
import silver from './silver';
|
||||||
|
import taskClasses from './taskClasses';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
uuid,
|
uuid,
|
||||||
@@ -18,4 +31,16 @@ module.exports = {
|
|||||||
dotSet,
|
dotSet,
|
||||||
dotGet,
|
dotGet,
|
||||||
preenHistory,
|
preenHistory,
|
||||||
|
countExists,
|
||||||
|
updateStore,
|
||||||
|
appliedTags,
|
||||||
|
encodeiCalLink,
|
||||||
|
friendlyTimestamp,
|
||||||
|
gold,
|
||||||
|
newChatMessages,
|
||||||
|
noTags,
|
||||||
|
percent,
|
||||||
|
removeWhitespace,
|
||||||
|
silver,
|
||||||
|
taskClasses,
|
||||||
};
|
};
|
||||||
|
|||||||
10
common/script/libs/newChatMessages.js
Normal file
10
common/script/libs/newChatMessages.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Does user have new chat messages?
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(messages, lastMessageSeen) {
|
||||||
|
if (!((messages != null ? messages.length : void 0) > 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (messages != null ? messages[0] : void 0) && (messages[0].id !== lastMessageSeen);
|
||||||
|
};
|
||||||
11
common/script/libs/noTags.js
Normal file
11
common/script/libs/noTags.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
/*
|
||||||
|
are any tags active?
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(tags) {
|
||||||
|
return _.isEmpty(tags) || _.isEmpty(_.filter(tags, function(t) {
|
||||||
|
return t;
|
||||||
|
}));
|
||||||
|
};
|
||||||
17
common/script/libs/percent.js
Normal file
17
common/script/libs/percent.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = function(x, y, dir) {
|
||||||
|
var roundFn;
|
||||||
|
switch (dir) {
|
||||||
|
case "up":
|
||||||
|
roundFn = Math.ceil;
|
||||||
|
break;
|
||||||
|
case "down":
|
||||||
|
roundFn = Math.floor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
roundFn = Math.round;
|
||||||
|
}
|
||||||
|
if (x === 0) {
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
return Math.max(0, roundFn(x / y * 100));
|
||||||
|
};
|
||||||
10
common/script/libs/removeWhitespace.js
Normal file
10
common/script/libs/removeWhitespace.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Remove whitespace #FIXME are we using this anywwhere? Should we be?
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(str) {
|
||||||
|
if (!str) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return str.replace(/\s/g, '');
|
||||||
|
};
|
||||||
11
common/script/libs/silver.js
Normal file
11
common/script/libs/silver.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Silver amount from their money
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(num) {
|
||||||
|
if (num) {
|
||||||
|
return ("0" + Math.floor((num - Math.floor(num)) * 100)).slice(-2);
|
||||||
|
} else {
|
||||||
|
return "00";
|
||||||
|
}
|
||||||
|
};
|
||||||
83
common/script/libs/taskClasses.js
Normal file
83
common/script/libs/taskClasses.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
shouldDo
|
||||||
|
} from '../cron';
|
||||||
|
/*
|
||||||
|
Task classes given everything about the class
|
||||||
|
*/
|
||||||
|
module.exports = function(task, filters, dayStart, lastCron, showCompleted, main) {
|
||||||
|
var classes, completed, enabled, filter, priority, ref, repeat, type, value;
|
||||||
|
if (filters == null) {
|
||||||
|
filters = [];
|
||||||
|
}
|
||||||
|
if (dayStart == null) {
|
||||||
|
dayStart = 0;
|
||||||
|
}
|
||||||
|
if (lastCron == null) {
|
||||||
|
lastCron = +(new Date);
|
||||||
|
}
|
||||||
|
if (showCompleted == null) {
|
||||||
|
showCompleted = false;
|
||||||
|
}
|
||||||
|
if (main == null) {
|
||||||
|
main = false;
|
||||||
|
}
|
||||||
|
if (!task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
type = task.type, completed = task.completed, value = task.value, repeat = task.repeat, priority = task.priority;
|
||||||
|
if (main) {
|
||||||
|
if (!task._editing) {
|
||||||
|
for (filter in filters) {
|
||||||
|
enabled = filters[filter];
|
||||||
|
if (enabled && !((ref = task.tags) != null ? ref[filter] : void 0)) {
|
||||||
|
return 'hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classes = type;
|
||||||
|
if (task._editing) {
|
||||||
|
classes += " beingEdited";
|
||||||
|
}
|
||||||
|
if (type === 'todo' || type === 'daily') {
|
||||||
|
if (completed || (type === 'daily' && !shouldDo(+(new Date), task, {
|
||||||
|
dayStart: dayStart
|
||||||
|
}))) {
|
||||||
|
classes += " completed";
|
||||||
|
} else {
|
||||||
|
classes += " uncompleted";
|
||||||
|
}
|
||||||
|
} else if (type === 'habit') {
|
||||||
|
if (task.down && task.up) {
|
||||||
|
classes += ' habit-wide';
|
||||||
|
}
|
||||||
|
if (!task.down && !task.up) {
|
||||||
|
classes += ' habit-narrow';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (priority === 0.1) {
|
||||||
|
classes += ' difficulty-trivial';
|
||||||
|
} else if (priority === 1) {
|
||||||
|
classes += ' difficulty-easy';
|
||||||
|
} else if (priority === 1.5) {
|
||||||
|
classes += ' difficulty-medium';
|
||||||
|
} else if (priority === 2) {
|
||||||
|
classes += ' difficulty-hard';
|
||||||
|
}
|
||||||
|
if (value < -20) {
|
||||||
|
classes += ' color-worst';
|
||||||
|
} else if (value < -10) {
|
||||||
|
classes += ' color-worse';
|
||||||
|
} else if (value < -1) {
|
||||||
|
classes += ' color-bad';
|
||||||
|
} else if (value < 1) {
|
||||||
|
classes += ' color-neutral';
|
||||||
|
} else if (value < 5) {
|
||||||
|
classes += ' color-good';
|
||||||
|
} else if (value < 10) {
|
||||||
|
classes += ' color-better';
|
||||||
|
} else {
|
||||||
|
classes += ' color-best';
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
};
|
||||||
36
common/script/libs/updateStore.js
Normal file
36
common/script/libs/updateStore.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import content from '../content/index';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Update the in-browser store with new gear. FIXME this was in user.fns, but it was causing strange issues there
|
||||||
|
*/
|
||||||
|
|
||||||
|
var sortOrder = _.reduce(content.gearTypes, (function(m, v, k) {
|
||||||
|
m[v] = k;
|
||||||
|
return m;
|
||||||
|
}), {});
|
||||||
|
|
||||||
|
module.exports = function(user) {
|
||||||
|
var changes;
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changes = [];
|
||||||
|
_.each(content.gearTypes, function(type) {
|
||||||
|
var found;
|
||||||
|
found = _.find(content.gear.tree[type][user.stats["class"]], function(item) {
|
||||||
|
return !user.items.gear.owned[item.key];
|
||||||
|
});
|
||||||
|
if (found) {
|
||||||
|
changes.push(found);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
changes = changes.concat(_.filter(content.gear.flat, function(v) {
|
||||||
|
var ref;
|
||||||
|
return ((ref = v.klass) === 'special' || ref === 'mystery' || ref === 'armoire') && !user.items.gear.owned[v.key] && (typeof v.canOwn === "function" ? v.canOwn(user) : void 0);
|
||||||
|
}));
|
||||||
|
return _.sortBy(changes, function(c) {
|
||||||
|
return sortOrder[c.type];
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user