Skip to content

Commit 9705f84

Browse files
committed
Adds content state comparison functionality to autosave interval
1 parent 1a644dd commit 9705f84

File tree

1 file changed

+147
-2
lines changed

1 file changed

+147
-2
lines changed

js/h5p.js

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,16 @@ H5P.init = function (target) {
224224
instance.getCurrentState instanceof Function ||
225225
typeof instance.getCurrentState === 'function')) {
226226

227-
var saveTimer, save = function () {
227+
var saveTimer, lastSavedState, save = function () {
228228
var state = instance.getCurrentState();
229-
if (state !== undefined) {
229+
230+
// Compare the last state with current state
231+
var equalStates = H5P.isObjectEqual(state, lastSavedState);
232+
233+
// Save user data when the current state changes
234+
if (state !== undefined && !equalStates) {
230235
H5P.setUserData(contentId, 'state', state, {deleteOnChange: true});
236+
lastSavedState = state;
231237
}
232238
if (H5PIntegration.saveFreq) {
233239
// Continue autosave
@@ -1400,6 +1406,145 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size, instance)
14001406
dialog.open();
14011407
};
14021408

1409+
1410+
/**
1411+
* Internal recursive function that determines if two objects are equivalent
1412+
*
1413+
* Returns true if equal and false otherwise
1414+
*
1415+
* @param {object} aObject First object being compared
1416+
* @param {object} bObject Second object being compared
1417+
* @param {Array} [aStack] a stack reprentation of the first objects' internal object stack
1418+
* @param {Array} [bStack] a stack reprentation of the second objects' internal object stack
1419+
*/
1420+
H5P.isObjectEqual = function (aObject, bObject, aStack, bStack) {
1421+
1422+
// Handle circular object comparison
1423+
const pushStack = function() {
1424+
aStack = aStack || [];
1425+
bStack = bStack || [];
1426+
var aLength = aStack.length;
1427+
for(let i = 0; i < aLength; i++){
1428+
if(aStack[i] === aObject) {
1429+
return bStack[i] === bObject;
1430+
}
1431+
}
1432+
1433+
aStack.push(aObject);
1434+
bStack.push(bObject);
1435+
}
1436+
1437+
if(aObject === null || bObject === null) {
1438+
return false;
1439+
}
1440+
1441+
if(aObject !== aObject) {
1442+
return bObject !== bObject;
1443+
}
1444+
1445+
// Compare objects via their types
1446+
1447+
var className = toString.call(aObject);
1448+
if(className !== toString.call(bObject)){
1449+
return false;
1450+
}
1451+
1452+
switch(className) {
1453+
case '[object String]':
1454+
return '' + aObject === '' + bObject;
1455+
1456+
case '[object Number]':
1457+
if(+aObject !== +aObject) {
1458+
return +bObject !== +bObject;
1459+
}
1460+
1461+
case '[object Boolean]':
1462+
return +aObject === +bObject;
1463+
1464+
case '[object Undefined]':
1465+
return true;
1466+
1467+
case '[object Array]':
1468+
pushStack();
1469+
aLength = aObject.length;
1470+
if(aLength !== bObject.length) {
1471+
return false;
1472+
}
1473+
1474+
// Compare array contents recursively
1475+
for(let i = 0; i < aLength; i++) {
1476+
if(!H5P.isObjectEqual(aObject[i], bObject[i], aStack, bStack)){
1477+
return false;
1478+
}
1479+
}
1480+
aStack.pop();
1481+
bStack.pop();
1482+
1483+
case '[object Object]':
1484+
pushStack();
1485+
if(typeof aObject != 'object' || typeof bObject != 'object'){
1486+
return false;
1487+
}
1488+
var _keys = H5P.keys(aObject);
1489+
var aLength = _keys.length;
1490+
1491+
if(H5P.keys(bObject).length !== aLength){
1492+
return false;
1493+
}
1494+
1495+
// Compare nested objects recursively
1496+
for(let i = 0; i < aLength; i++) {
1497+
key = _keys[i];
1498+
if(!(H5P.hasKey(bObject, key) && H5P.isObjectEqual(aObject[key], bObject[key], aStack, bStack))) {
1499+
return false;
1500+
}
1501+
}
1502+
aStack.pop();
1503+
bStack.pop();
1504+
}
1505+
1506+
return true;
1507+
}
1508+
1509+
/**
1510+
* Helper function to determine if an object has a given key
1511+
*
1512+
* @param {object} obj
1513+
* @param {string} key
1514+
*/
1515+
H5P.hasKey = function(obj, key) {
1516+
return obj != null && typeof obj !== 'undefined' && hasOwnProperty.call(obj, key);
1517+
}
1518+
1519+
/**
1520+
* Helper function to determine if a given variable is an object
1521+
*
1522+
* @param {object} obj
1523+
*/
1524+
H5P.isObject = function(obj) {
1525+
var type = typeof obj;
1526+
return type === 'function' || type === 'object' && !!obj;
1527+
}
1528+
1529+
/**
1530+
* Helper function that creates an array of an objects' respective keys
1531+
*
1532+
* @param {object} obj
1533+
*/
1534+
H5P.keys = function(obj) {
1535+
1536+
var keys = [];
1537+
if(!H5P.isObject(obj)){
1538+
return keys;
1539+
}
1540+
for(var key in obj) {
1541+
if (H5P.hasKey(obj, key)) {
1542+
keys.push(key);
1543+
}
1544+
}
1545+
return keys;
1546+
}
1547+
14031548
/**
14041549
* Show a toast message.
14051550
*

0 commit comments

Comments
 (0)