<commit_before>var inBrowser = false;
var NativeStorageError = require('./NativeStorageError');

function isInBrowser() {
    inBrowser = (window.cordova && window.cordova.platformId === 'browser') || !(window.phonegap || window.cordova);
    return inBrowser;
}

function storageSupportAnalyse() {
    if (!isInBrowser()) {
        return 0;
        //storageHandlerDelegate = exec;
    } else {
        if (window.localStorage) {
            return 1;
            //storageHandlerDelegate = localStorageHandle;
        } else {
            return 2;
            //console.log("ALERT! localstorage isn't supported");
        }
    }
}

//if storage not available gracefully fails, no error message for now
function StorageHandle() {
    this.storageSupport = storageSupportAnalyse();
    switch (this.storageSupport) {
        case 0:
            var exec = require('cordova/exec');
            this.storageHandlerDelegate = exec;
            break;
        case 1:
            var localStorageHandle = require('./LocalStorageHandle');
            this.storageHandlerDelegate = localStorageHandle;
            break;
        case 2:
            console.log("ALERT! localstorage isn't supported");
            break;
        default:
            console.log("StorageSupport Error");
            break;
    }
}

StorageHandle.prototype.set = function(reference, value, success, error) {
    if (reference === null) {
        error("The reference can't be null");
        return;
    }
    if (value === null) {
        error("a Null value isn't supported");
        return;
    }
    switch (typeof value) {
        case 'undefined':
            error("an undefined type isn't supported");
            break;
        case 'boolean':
            {
                this.putBoolean(reference, value, success, error);
                break;
            }
        case 'number':
            {
                // Good now check if it's a float or an int
                if (value === +value) {
                    if (value === (value | 0)) {
                        // it's an int
                        this.putInt(reference, value, success, error);
                    } else if (value !== (value | 0)) {
                        this.putDouble(reference, value, success, error);
                    }
                } else {
                    error("The value doesn't seem to be a number");
                }
                break;
            }
        case 'string':
            {
                this.putString(reference, value, success, error);
                break;
            }
        case 'object':
            {
                this.putObject(reference, value, success, error);
                break;
            }
        default:
            error("The type isn't supported or isn't been recognized");
            break;
    }
};

/* removing */
StorageHandle.prototype.remove = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }

    if (inBrowser) {
        try {
            localStorage.removeItem(reference);
            success();
        } catch (e) {
            error(e);
        }
    } else {
        this.storageHandlerDelegate(success, error, "NativeStorage", "remove", [reference]);
    }
};

/* boolean storage */
StorageHandle.prototype.putBoolean = function(reference, aBoolean, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }

    if (aBoolean === null) {
        error("a Null value isn't supported");
        return;
    }

    if (typeof aBoolean === 'boolean') {
        this.storageHandlerDelegate(function(returnedBool) {
            if ('string' === typeof returnedBool) {
                if ((returnedBool === 'true')) {
                    success(true);
                } else if ((returnedBool === 'false')) {
                    success(false);
                } else {
                    error("The returned boolean from SharedPreferences was not recognized: " + returnedBool);
                }
            } else {
                success(returnedBool);
            }
        }, error, "NativeStorage", "putBoolean", [reference, aBoolean]);
    } else {
        error("Only boolean types are supported");
    }
};

StorageHandle.prototype.getBoolean = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(function(returnedBool) {
        if ('string' === typeof returnedBool) {
            if ((returnedBool === 'true')) {
                success(true);
            } else if ((returnedBool === 'false')) {
                success(false);
            } else {
                error("The returned boolean from SharedPreferences was not recognized: " + returnedBool);
            }
        } else {
            success(returnedBool);
        }
    }, error, "NativeStorage", "getBoolean", [reference]);
};

/* int storage */
StorageHandle.prototype.putInt = function(reference, anInt, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putInt", [reference, anInt]);
};

StorageHandle.prototype.getInt = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "getInt", [reference]);
};

/* float storage */
StorageHandle.prototype.putDouble = function(reference, aFloat, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putDouble", [reference, aFloat]);
};

StorageHandle.prototype.getDouble = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(function(data) {
        if (isNaN(data)) {
            error('Expected double but got non-number');
        } else {
            success(parseFloat(data));
        }
    }, error, "NativeStorage", "getDouble", [reference]);
};

/* string storage */
StorageHandle.prototype.putString = function(reference, s, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putString", [reference, s]);
};

StorageHandle.prototype.getString = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "getString", [reference]);
};

/* object storage  COMPOSITE AND DOESNT CARE FOR BROWSER*/
StorageHandle.prototype.putObject = function(reference, obj, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(err);
    }
    this.putString(reference, objAsString, success, error);
};

StorageHandle.prototype.getObject = function(reference, success, error) {
    this.getString(reference, function(data) {
        var obj = {};
        try {
            obj = JSON.parse(data);
            success(obj);
        } catch (err) {
            error(err);
        }
    }, error);
};

/* API >= 2 */
StorageHandle.prototype.setItem = function(reference, obj, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
        return;
    }
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    this.storageHandlerDelegate(success, function(code) {
        error(new NativeStorageError(code, "Native", ""));
    }, "NativeStorage", "setItem", [reference, objAsString]);
};

StorageHandle.prototype.getItem = function(reference, success, error) {
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    var obj = {};

    this.storageHandlerDelegate(
        function(data) {
            try {
                obj = JSON.parse(data);
                success(obj);
            } catch (err) {
                error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
            }
        },
        function(code) {
            error(new NativeStorageError(code, "Native", ""));
        }, "NativeStorage", "getItem", [reference]);
};

/* API >= 2 */
StorageHandle.prototype.setItem = function(reference, obj, encryptConfig, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
        return;
    }
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }

    var action = "setItem";
    var params = [reference, objAsString];
    switch (encryptConfig.mode) {
        case "password":
            method = "setItemWithPassword";
            params = [reference, objAsString, encryptConfig.value];
            break;
        case "key":
            method = "setItemWithKey";
            break;
        case "none":
            break;
        default:
            {
                error(new NativeStorageError(NativeStorageError.WRONG_PARAMETER, "JS", ""));
                return;
            }
    }
    this.storageHandlerDelegate(success, function(code) {
        error(new NativeStorageError(code, "Native", ""));
    }, "NativeStorage", action, params);
};

StorageHandle.prototype.getItem = function(reference, encryptConfig, success, error) {
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    var obj = {};

    var action = "getItem";
    var params = [reference];
    switch (encryptConfig.mode) {
        case "password":
            method = "getItemWithPassword";
            params = [reference, encryptConfig.value];
            break;
        case "key":
            method = "getItemWithKey";
            break;
        case "none":
            break;
        default:
            {
                error(new NativeStorageError(NativeStorageError.WRONG_PARAMETER, "JS", ""));
                return;
            }
    }

    this.storageHandlerDelegate(
        function(data) {
            try {
                obj = JSON.parse(data);
                success(obj);
            } catch (err) {
                error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
            }
        },
        function(code) {
            error(new NativeStorageError(code, "Native", ""));
        }, "NativeStorage", action, params);
};

var storageHandle = new StorageHandle();
module.exports = storageHandle;
<commit_msg>Simple fix<commit_after>var inBrowser = false;
var NativeStorageError = require('./NativeStorageError');

function isInBrowser() {
    inBrowser = (window.cordova && window.cordova.platformId === 'browser') || !(window.phonegap || window.cordova);
    return inBrowser;
}

function storageSupportAnalyse() {
    if (!isInBrowser()) {
        return 0;
        //storageHandlerDelegate = exec;
    } else {
        if (window.localStorage) {
            return 1;
            //storageHandlerDelegate = localStorageHandle;
        } else {
            return 2;
            //console.log("ALERT! localstorage isn't supported");
        }
    }
}

//if storage not available gracefully fails, no error message for now
function StorageHandle() {
    this.storageSupport = storageSupportAnalyse();
    switch (this.storageSupport) {
        case 0:
            var exec = require('cordova/exec');
            this.storageHandlerDelegate = exec;
            break;
        case 1:
            var localStorageHandle = require('./LocalStorageHandle');
            this.storageHandlerDelegate = localStorageHandle;
            break;
        case 2:
            console.log("ALERT! localstorage isn't supported");
            break;
        default:
            console.log("StorageSupport Error");
            break;
    }
}

StorageHandle.prototype.set = function(reference, value, success, error) {
    if (reference === null) {
        error("The reference can't be null");
        return;
    }
    if (value === null) {
        error("a Null value isn't supported");
        return;
    }
    switch (typeof value) {
        case 'undefined':
            error("an undefined type isn't supported");
            break;
        case 'boolean':
            {
                this.putBoolean(reference, value, success, error);
                break;
            }
        case 'number':
            {
                // Good now check if it's a float or an int
                if (value === +value) {
                    if (value === (value | 0)) {
                        // it's an int
                        this.putInt(reference, value, success, error);
                    } else if (value !== (value | 0)) {
                        this.putDouble(reference, value, success, error);
                    }
                } else {
                    error("The value doesn't seem to be a number");
                }
                break;
            }
        case 'string':
            {
                this.putString(reference, value, success, error);
                break;
            }
        case 'object':
            {
                this.putObject(reference, value, success, error);
                break;
            }
        default:
            error("The type isn't supported or isn't been recognized");
            break;
    }
};

/* removing */
StorageHandle.prototype.remove = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }

    if (inBrowser) {
        try {
            localStorage.removeItem(reference);
            success();
        } catch (e) {
            error(e);
        }
    } else {
        this.storageHandlerDelegate(success, error, "NativeStorage", "remove", [reference]);
    }
};

/* boolean storage */
StorageHandle.prototype.putBoolean = function(reference, aBoolean, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }

    if (aBoolean === null) {
        error("a Null value isn't supported");
        return;
    }

    if (typeof aBoolean === 'boolean') {
        this.storageHandlerDelegate(function(returnedBool) {
            if ('string' === typeof returnedBool) {
                if ((returnedBool === 'true')) {
                    success(true);
                } else if ((returnedBool === 'false')) {
                    success(false);
                } else {
                    error("The returned boolean from SharedPreferences was not recognized: " + returnedBool);
                }
            } else {
                success(returnedBool);
            }
        }, error, "NativeStorage", "putBoolean", [reference, aBoolean]);
    } else {
        error("Only boolean types are supported");
    }
};

StorageHandle.prototype.getBoolean = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(function(returnedBool) {
        if ('string' === typeof returnedBool) {
            if ((returnedBool === 'true')) {
                success(true);
            } else if ((returnedBool === 'false')) {
                success(false);
            } else {
                error("The returned boolean from SharedPreferences was not recognized: " + returnedBool);
            }
        } else {
            success(returnedBool);
        }
    }, error, "NativeStorage", "getBoolean", [reference]);
};

/* int storage */
StorageHandle.prototype.putInt = function(reference, anInt, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putInt", [reference, anInt]);
};

StorageHandle.prototype.getInt = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "getInt", [reference]);
};

/* float storage */
StorageHandle.prototype.putDouble = function(reference, aFloat, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putDouble", [reference, aFloat]);
};

StorageHandle.prototype.getDouble = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(function(data) {
        if (isNaN(data)) {
            error('Expected double but got non-number');
        } else {
            success(parseFloat(data));
        }
    }, error, "NativeStorage", "getDouble", [reference]);
};

/* string storage */
StorageHandle.prototype.putString = function(reference, s, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "putString", [reference, s]);
};

StorageHandle.prototype.getString = function(reference, success, error) {
    if (reference === null) {
        error("Null reference isn't supported");
        return;
    }
    this.storageHandlerDelegate(success, error, "NativeStorage", "getString", [reference]);
};

/* object storage  COMPOSITE AND DOESNT CARE FOR BROWSER*/
StorageHandle.prototype.putObject = function(reference, obj, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(err);
    }
    this.putString(reference, objAsString, success, error);
};

StorageHandle.prototype.getObject = function(reference, success, error) {
    this.getString(reference, function(data) {
        var obj = {};
        try {
            obj = JSON.parse(data);
            success(obj);
        } catch (err) {
            error(err);
        }
    }, error);
};

/* API >= 2 */
StorageHandle.prototype.setItem = function(reference, obj, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
        return;
    }
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    this.storageHandlerDelegate(success, function(code) {
        error(new NativeStorageError(code, "Native", ""));
    }, "NativeStorage", "setItem", [reference, objAsString]);
};

StorageHandle.prototype.getItem = function(reference, success, error) {
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    var obj = {};

    this.storageHandlerDelegate(
        function(data) {
            try {
                obj = JSON.parse(data);
                success(obj);
            } catch (err) {
                error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
            }
        },
        function(code) {
            error(new NativeStorageError(code, "Native", ""));
        }, "NativeStorage", "getItem", [reference]);
};

/* API >= 2 */
StorageHandle.prototype.setItem = function(reference, obj, encryptConfig, success, error) {
    var objAsString = "";
    try {
        objAsString = JSON.stringify(obj);
    } catch (err) {
        error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
        return;
    }
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }

    var action = "setItem";
    var params = [reference, objAsString];
    switch (encryptConfig.mode) {
        case "password":
            action = "setItemWithPassword";
            action = [reference, objAsString, encryptConfig.value];
            break;
        case "key":
            action = "setItemWithKey";
            break;
        case "none":
            break;
        default:
            {
                error(new NativeStorageError(NativeStorageError.WRONG_PARAMETER, "JS", ""));
                return;
            }
    }
    this.storageHandlerDelegate(success, function(code) {
        error(new NativeStorageError(code, "Native", ""));
    }, "NativeStorage", action, params);
};

StorageHandle.prototype.getItem = function(reference, encryptConfig, success, error) {
    if (reference === null) {
        error(new NativeStorageError(NativeStorageError.NULL_REFERENCE, "JS", ""));
        return;
    }
    var obj = {};

    var action = "getItem";
    var params = [reference];
    switch (encryptConfig.mode) {
        case "password":
            action = "getItemWithPassword";
            params = [reference, encryptConfig.value];
            break;
        case "key":
            action = "getItemWithKey";
            break;
        case "none":
            break;
        default:
            {
                error(new NativeStorageError(NativeStorageError.WRONG_PARAMETER, "JS", ""));
                return;
            }
    }

    this.storageHandlerDelegate(
        function(data) {
            try {
                obj = JSON.parse(data);
                success(obj);
            } catch (err) {
                error(new NativeStorageError(NativeStorageError.JSON_ERROR, "JS", err));
            }
        },
        function(code) {
            error(new NativeStorageError(code, "Native", ""));
        }, "NativeStorage", action, params);
};

var storageHandle = new StorageHandle();
module.exports = storageHandle;
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------------
//- framework
//---------------------------------------------------------------------------------
'use strict;'
var fn = require('./fn.js');
var db = require('./db.js');
var hal = require('./hal.js');
var log = console.log;

//- api/apples || api/apples/abc3b4=1
function getIdFromPath(path) {
  var tokens = path.split('/');
  var id = fn.btoa(tokens[tokens.length - 1]);
  if (isNaN(id)) return 0;
  return id;
}

//- api/apples/123456/create
function getIdAndRelFromPath(path) {
  var idAndRel = { id: 0, rel: ''};

  var tokens = path.split('/')
  tokens = fn.btoa(tokens[tokens.length - 1]).split('/');
  if (tokens.length === 2) {
    idAndRel.id = tokens[0];
    idAndRel.rel = tokens[1];
  }
  else {
    idAndRel.rel = tokens[0];
  }
  return idAndRel;
}

var filterEmpty = fn.filter(function(e) { return Object.getOwnPropertyNames(e).length > 0; });

function validateApiCall(reqRel, entity) {
  var links = hal.getLinksForCurrentState(entity);
  if ( ! fn.some(function(link) { return link.rel === reqRel; }, links)) {
    throw {
      statusCode: 409,
      message: 'Conflict',
      log: 'Error: API call not allowed, rel: ' + reqRel + ' entity: ' + JSON.stringify(entity)
    }
  }
  return true;
}

function validatePropsExist(body, entity) {
  var diff = fn.diff(Object.keys(body), Object.keys(entity));
  if (diff.length > 0) {
    throw { statusCode: 400, message: 'Bad Request', log: 'Properties: ' + diff + ' do not exist ! ' + JSON.stringify(entity) }
  }
}

function validatePropsMatch(body, entity) {
  var diff = fn.diff(Object.keys(body), Object.keys(entity));
  if (diff.length > 0) {
    throw { statusCode: 400, message: 'Bad Request', log: 'Properties: ' + diff + ' failed to match ! ' + JSON.stringify(entity) }
  }
}

//-- exports ------------------------------------------------------------
//-----------------------------------------------------------------------
exports.clearDb = function() {
  db.clear();
}

exports.Resource = function(entityCtor) {
  var entityCtor = entityCtor;
  var typeName = entityCtor.toString().match(/function ([^\(]+)/)[1].toLowerCase();

  function createAndStore(body) {
    var entity = new entityCtor();
    validatePropsMatch(body, entity);
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    db.save(entity);
    return { name: typeName, data: entity }; //todo: - return 201 (Created) -
  }

  function processAndStore(rel, body, entity) {
    var result = entity[rel](body);
    if (result) {
      db.save(entity);
      return { name: typeName, data: entity };
    }
    throw {
      statusCode: 422,
      message: 'Unprocessable Entity',
      log: from + ": Unprocessable, Rel: " + idAndRel.rel + " Entity: " + JSON.stringify(entity)
    };
  }

  function getById(id) {
    var entity = db.get(id);
    if (typeof entity === 'undefined') {
      throw { statusCode: 404, message: 'Not Found', log: "GET: entity === undefined" };
    }
    return { name: typeName, data: entity };
  };

  function getAll() {
    var entities = db.getAll();
    if (entities.length >= 1) {
      entities = filterEmpty(entities);
    }
    var result = { name: typeName, data: entities };
    return result;
  };

  //- public api -----
  //-
  this.get = function(path) {
    var id = getIdFromPath(path);

    if (id === 0) return getAll();
    return getById(id);
  };

  this.put = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    var entity = db.get(idAndRel.id);
    validateApiCall(idAndRel.rel, entity);
    validatePropsMatch(body, entity);
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.post = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    if(idAndRel.id === 0) {
      return createAndStore(body);
    }
    //- else: process post message id !== 0 and body.props don't have to exist on entity
    var entity = db.get(idAndRel.id);
    validateApiCall(idAndRel.rel, entity);
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.patch = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    var entity = db.get(idAndRel.id);

    validatePropsExist(body, entity);
    validateApiCall(idAndRel.rel, entity);

    //- update entity
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.delete = function(path) {
    var id = getIdFromPath(path);
    db.remove(id);
  };
};

//- api/apples/123456/create
function getTypeFromPath(path) {
  var tokens = path.split('/');
  if (tokens.length > 1) {
    return tokens[1].slice(0, -1);
  }
  throw { statusCode: 500, message: 'Internal Server Error', log: 'Not an API call: ' + path };
}

exports.handle = function(app, req) {
  try {
    var path = req.url.substring(req.url.indexOf('api'), req.url.length);
    path = fn.trimLeftAndRight(path, '/');

    var requestedType = getTypeFromPath(path);
    var resource = app[requestedType + 'Resource'];
    var handler = resource[req.method.toLowerCase()];
    var result = handler(path, req.body);
    var halResult = hal.convert(result.name, result.data);
    return halResult;
  }
  catch (e) {
    log('Fx Exception: ' + JSON.stringify(e));
    if (e.hasOwnProperty('statusCode')) {
      return {
        statusCode: e.statusCode,
        message: e.message
      };
    }
    return {
      statusCode: 500,
      message: e
    };
  }
};
<commit_msg>fixed 'delete'<commit_after>//---------------------------------------------------------------------------------
//- framework
//---------------------------------------------------------------------------------
'use strict;'
var fn = require('./fn.js');
var db = require('./db.js');
var hal = require('./hal.js');
var log = console.log;

//- api/apples || api/apples/abc3b4=1
function getIdFromPath(path) {
  var tokens = path.split('/');
  var id = fn.btoa(tokens[tokens.length - 1]);
  if (isNaN(id)) return 0;
  return id;
}

//- api/apples/123456/create
function getIdAndRelFromPath(path) {
  var idAndRel = { id: 0, rel: ''};

  var tokens = path.split('/')
  tokens = fn.btoa(tokens[tokens.length - 1]).split('/');
  if (tokens.length === 2) {
    idAndRel.id = tokens[0];
    idAndRel.rel = tokens[1];
  }
  else {
    idAndRel.rel = tokens[0];
  }
  return idAndRel;
}

var filterEmpty = fn.filter(function(e) { return Object.getOwnPropertyNames(e).length > 0; });

function validateApiCall(reqRel, entity) {
  var links = hal.getLinksForCurrentState(entity);
  if ( ! fn.some(function(link) { return link.rel === reqRel; }, links)) {
    throw {
      statusCode: 409,
      message: 'Conflict',
      log: 'Error: API call not allowed, rel: ' + reqRel + ' entity: ' + JSON.stringify(entity)
    }
  }
  return true;
}

function validatePropsExist(body, entity) {
  var diff = fn.diff(Object.keys(body), Object.keys(entity));
  if (diff.length > 0) {
    throw { statusCode: 400, message: 'Bad Request', log: 'Properties: ' + diff + ' do not exist ! ' + JSON.stringify(entity) }
  }
}

function validatePropsMatch(body, entity) {
  var diff = fn.diff(Object.keys(body), Object.keys(entity));
  if (diff.length > 0) {
    throw { statusCode: 400, message: 'Bad Request', log: 'Properties: ' + diff + ' failed to match ! ' + JSON.stringify(entity) }
  }
}

//-- exports ------------------------------------------------------------
//-----------------------------------------------------------------------
exports.clearDb = function() {
  db.clear();
}

exports.Resource = function(entityCtor) {
  var entityCtor = entityCtor;
  var typeName = entityCtor.toString().match(/function ([^\(]+)/)[1].toLowerCase();

  function createAndStore(body) {
    var entity = new entityCtor();
    validatePropsMatch(body, entity);
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    db.save(entity);
    return { name: typeName, data: entity }; //todo: - return 201 (Created) -
  }

  function processAndStore(rel, body, entity) {
    var result = entity[rel](body);
    if (result) {
      db.save(entity);
      return { name: typeName, data: entity };
    }
    throw {
      statusCode: 422,
      message: 'Unprocessable Entity',
      log: from + ": Unprocessable, Rel: " + idAndRel.rel + " Entity: " + JSON.stringify(entity)
    };
  }

  function getById(id) {
    var entity = db.get(id);
    if (typeof entity === 'undefined') {
      throw { statusCode: 404, message: 'Not Found', log: "GET: entity === undefined" };
    }
    return { name: typeName, data: entity };
  };

  function getAll() {
    var entities = db.getAll();
    if (entities.length >= 1) {
      entities = filterEmpty(entities);
    }
    var result = { name: typeName, data: entities };
    return result;
  };

  //- public api -----
  //-
  this.get = function(path) {
    var id = getIdFromPath(path);

    if (id === 0) return getAll();
    return getById(id);
  };

  this.put = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    var entity = db.get(idAndRel.id);
    validateApiCall(idAndRel.rel, entity);
    validatePropsMatch(body, entity);
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.post = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    if(idAndRel.id === 0) {
      return createAndStore(body);
    }
    //- else: process post message id !== 0 and body.props don't have to exist on entity
    var entity = db.get(idAndRel.id);
    validateApiCall(idAndRel.rel, entity);
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.patch = function(path, body) {
    var idAndRel = getIdAndRelFromPath(path);
    var entity = db.get(idAndRel.id);
    validateApiCall(idAndRel.rel, entity);
    validatePropsExist(body, entity);

    //- update entity
    fn.each(function(key) { entity[key] = body[key]; }, Object.keys(body));
    return processAndStore(idAndRel.rel, body, entity);
  };

  this.delete = function(path) {
    var idAndRel = getIdAndRelFromPath(path);
    db.remove(idAndRel.id);
  };
};

//- api/apples/123456/create
function getTypeFromPath(path) {
  var tokens = path.split('/');
  if (tokens.length > 1) {
    return tokens[1].slice(0, -1);
  }
  throw { statusCode: 500, message: 'Internal Server Error', log: 'Not an API call: ' + path };
}

exports.handle = function(app, req) {
  try {
    var path = req.url.substring(req.url.indexOf('api'), req.url.length);
    path = fn.trimLeftAndRight(path, '/');

    var requestedType = getTypeFromPath(path);
    var resource = app[requestedType + 'Resource'];
    var handler = resource[req.method.toLowerCase()];
    var result = handler(path, req.body);
    var halResult = hal.convert(result.name, result.data);
    return halResult;
  }
  catch (e) {
    log('Fx Exception: ' + JSON.stringify(e));
    if (e.hasOwnProperty('statusCode')) {
      return {
        statusCode: e.statusCode,
        message: e.message
      };
    }
    return {
      statusCode: 500,
      message: e
    };
  }
};
<|endoftext|>


<commit_before>function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}
function getSavedPadding(){
	paddingstart = getCookie("paddingstart");
	paddingend = getCookie("paddingend");
	if(paddingstart != ""){
		document.getElementById("paddingstart").value = paddingstart;
	}
	if(paddingend != ""){
		document.getElementById("paddingend").value = paddingend;
	}
}

function reveal(evt, modal) {
	document.getElementById(modal).style.display = "block";
}

function hideReveal(evt, modal) {
	document.getElementById(modal).style.display = 'none';
}

function revealRuleForm(evt, modal,seriesid,seriesname){
	reveal(evt, modal);
	document.getElementById("seriesid").value = seriesid;
	document.getElementById("seriesname").value = seriesname;
	getSavedPadding();
}
function handleRecordingType(myRadio){
	if(myRadio.value == "all"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "recent"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "time"){
		document.getElementById("recordtime").style = "display: relative;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "aftertime"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: relative;";
	}
}
function submitCreateRule(){
	var url = "api.php?api=rules&cmd=create";
	var seriesid = document.getElementById("seriesid").value;
	var paddingstart = document.getElementById("paddingstart").value;
	var paddingend = document.getElementById("paddingend").value;
	var channel = document.getElementById("channel").value;
	var recordtype = document.getElementById("recordtype").value;
	var recordtime = document.getElementById("recordtime").value;
	var recordafter = document.getElementById("recordafter").value;
	var radios = document.getElementsByName('recordtype');

	setCookie("paddingstart",paddingstart,3000);
	setCookie("paddingend",paddingend,3000);
	for (var i = 0, length = radios.length; i < length; i++) {
	    if (radios[i].checked) {
		recordtype = radios[i].value;
		break;
	    }
	}
	url += "&seriesid=" + seriesid;
	url += "&start=" + paddingstart;
	url += "&end=" + paddingend;
	if(channel){
		url += "&channel=" + channel;
	}
	if(recordtype == "all"){
		url += "&recentonly=0";
		createRecording(url);
	}else if(recordtype == "recent"){
		url += "&recentonly=1";
		createRecording(url);
	}else if(recordtype == "time"){
		url += "&recentonly=0";
		if(recordtime){
			recordtime = moment(recordtime).unix();
			url += "&recordtime=" + recordtime;
			createRecording(url);
		}else{
			alert("specify a valid date/time for this type of recording");
		}
	}else if(recordtype == "aftertime"){
		url += "&recentonly=0";
		if(recordafter){
			recordafter = moment(recordafter).unix();
			url += "&recordafter=" + recordafter;
			createRecording(url);
		}else{
			alert("specify a valid date for this type of recording");

		}
	}
	return false;
}

function createRecording(url){
	var result = confirm("Are you sure you want to create this rule?");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully created.");
				openSearchPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule creation failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRecording(url){
	var result = confirm("Are you sure you want to delete this recording?\nThis action cannot be undone!");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Recording deleted successfully.");
				openRecordingsPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRule(url){
	var result = confirm("Are you sure you want to delete this rule? ");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully deleted.");
				openSearchPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRule2(url){
	var result = confirm("Are you sure you want to delete this rule? ");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully deleted.");
				openRulesPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function openTab(evt, tabname) {
	var i, tabcontent, tablinks;
	// get elements with class="tabcontent" and hide
	tabcontent = document.getElementsByClassName("tabcontent");
	for (i=-0; i < tabcontent.length; i++) {
		tabcontent[i].style.display = "none";
	}

	// get elements with class="tablink" and remove the active
	tablinks = document.getElementsByClassName("tablink");
	for (i=0; i < tablinks.length; i++) {
		tablinks[i].className = tablinks[i].className.replace(" active", "");
	}

	// load the page
	if (tabname == 'recordings_page') {
		openRecordingsPage();
	}
	if (tabname == 'rules_page') {
		openRulesPage();
	}
	if (tabname == 'search_page') {
		openSearchPage();
	}
	if (tabname == 'hdhr_page') {
		openHDHRPage();
	}
	if (tabname == 'upcoming_page') {
		openUpcomingPage();
	}

	//show the tablinks
	document.getElementById(tabname).style.display = "block";
	evt.currentTarget.className += " active";
}

/* Set the status message */
function setStatus(msg)
{
	isStatusIdle = 0;
	if(msg == '' || msg == null || msg == undefined)
	{
		isStatusIdle = 1;
		msg = "Idle.";
	}
	document.getElementById('statusMessage').innerHTML = msg;
}

function goSearch()
{
	var str = document.getElementById('searchString').value;
	openSearchPage(str);
}
<commit_msg>delete rule function<commit_after>function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}
function getSavedPadding(){
	paddingstart = getCookie("paddingstart");
	paddingend = getCookie("paddingend");
	if(paddingstart != ""){
		document.getElementById("paddingstart").value = paddingstart;
	}
	if(paddingend != ""){
		document.getElementById("paddingend").value = paddingend;
	}
}

function deleteRule(evt, rule_id, reveal) {
	deleteRuleByID(rule_id,false);
	hideReveal(evt, reveal);
}

function reveal(evt, modal) {
	document.getElementById(modal).style.display = "block";
}

function hideReveal(evt, modal) {
	document.getElementById(modal).style.display = 'none';
}

function revealRuleForm(evt, modal,seriesid,seriesname){
	reveal(evt, modal);
	document.getElementById("seriesid").value = seriesid;
	document.getElementById("seriesname").value = seriesname;
	getSavedPadding();
}
function handleRecordingType(myRadio){
	if(myRadio.value == "all"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "recent"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "time"){
		document.getElementById("recordtime").style = "display: relative;";
		document.getElementById("recordafter").style = "display: none;";
	}else if(myRadio.value == "aftertime"){
		document.getElementById("recordtime").style = "display: none;";
		document.getElementById("recordafter").style = "display: relative;";
	}
}
function submitCreateRule(){
	var url = "api.php?api=rules&cmd=create";
	var seriesid = document.getElementById("seriesid").value;
	var paddingstart = document.getElementById("paddingstart").value;
	var paddingend = document.getElementById("paddingend").value;
	var channel = document.getElementById("channel").value;
	var recordtype = document.getElementById("recordtype").value;
	var recordtime = document.getElementById("recordtime").value;
	var recordafter = document.getElementById("recordafter").value;
	var radios = document.getElementsByName('recordtype');

	setCookie("paddingstart",paddingstart,3000);
	setCookie("paddingend",paddingend,3000);
	for (var i = 0, length = radios.length; i < length; i++) {
	    if (radios[i].checked) {
		recordtype = radios[i].value;
		break;
	    }
	}
	url += "&seriesid=" + seriesid;
	url += "&start=" + paddingstart;
	url += "&end=" + paddingend;
	if(channel){
		url += "&channel=" + channel;
	}
	if(recordtype == "all"){
		url += "&recentonly=0";
		createRecording(url);
	}else if(recordtype == "recent"){
		url += "&recentonly=1";
		createRecording(url);
	}else if(recordtype == "time"){
		url += "&recentonly=0";
		if(recordtime){
			recordtime = moment(recordtime).unix();
			url += "&recordtime=" + recordtime;
			createRecording(url);
		}else{
			alert("specify a valid date/time for this type of recording");
		}
	}else if(recordtype == "aftertime"){
		url += "&recentonly=0";
		if(recordafter){
			recordafter = moment(recordafter).unix();
			url += "&recordafter=" + recordafter;
			createRecording(url);
		}else{
			alert("specify a valid date for this type of recording");

		}
	}
	return false;
}

function createRecording(url){
	var result = confirm("Are you sure you want to create this rule?");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully created.");
				openSearchPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule creation failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRecording(url){
	var result = confirm("Are you sure you want to delete this recording?\nThis action cannot be undone!");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Recording deleted successfully.");
				openRecordingsPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRule(url){
	var result = confirm("Are you sure you want to delete this rule? ");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully deleted.");
				openSearchPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function confirmDeleteRule2(url){
	var result = confirm("Are you sure you want to delete this rule? ");
	if (result){
		var request = new XMLHttpRequest();
		request.onreadystatechange = function() {
			if (request.readyState == 4 && request.status == 200) {
			   alert("Rule has been successfully deleted.");
				openRulesPage();
			}
			if (request.readyState == 4 && request.status == 400) {
			   alert("Rule deletion failed.  The url was " + url);
			}
		};
		request.open("GET", url, true);
		request.send(null);
	}
}

function openTab(evt, tabname) {
	var i, tabcontent, tablinks;
	// get elements with class="tabcontent" and hide
	tabcontent = document.getElementsByClassName("tabcontent");
	for (i=-0; i < tabcontent.length; i++) {
		tabcontent[i].style.display = "none";
	}

	// get elements with class="tablink" and remove the active
	tablinks = document.getElementsByClassName("tablink");
	for (i=0; i < tablinks.length; i++) {
		tablinks[i].className = tablinks[i].className.replace(" active", "");
	}

	// load the page
	if (tabname == 'recordings_page') {
		openRecordingsPage();
	}
	if (tabname == 'rules_page') {
		openRulesPage();
	}
	if (tabname == 'search_page') {
		openSearchPage();
	}
	if (tabname == 'hdhr_page') {
		openHDHRPage();
	}
	if (tabname == 'upcoming_page') {
		openUpcomingPage();
	}

	//show the tablinks
	document.getElementById(tabname).style.display = "block";
	evt.currentTarget.className += " active";
}

/* Set the status message */
function setStatus(msg)
{
	isStatusIdle = 0;
	if(msg == '' || msg == null || msg == undefined)
	{
		isStatusIdle = 1;
		msg = "Idle.";
	}
	document.getElementById('statusMessage').innerHTML = msg;
}

function goSearch()
{
	var str = document.getElementById('searchString').value;
	openSearchPage(str);
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc service
 * @name discussionToolApp.config
 * @description
 * # config
 * Constant in the discussionToolApp.
 */
angular.module('discussionToolApp')
  .constant('config', {
    authCookieName: 'DiscussionToolAuth',
    oidcAuthorizationUrl: 'https://api.learning-layers.eu/o/oauth2/authorize',
    oidcClientId: '03d7dd09-e99e-43ea-bd93-d06661426c95',
    sssRestUrl: 'http://test-ll.know-center.tugraz.at/layers.test/',
    ldRestUrl: 'http://178.62.62.23:9000/',
    ldClientUrl: 'http://178.62.62.23/',
    bnpUrl: 'http://localhost/BitsAndPieces/'
  });
<commit_msg>Cleared config before tagging a release.<commit_after>'use strict';

/**
 * @ngdoc service
 * @name discussionToolApp.config
 * @description
 * # config
 * Constant in the discussionToolApp.
 */
angular.module('discussionToolApp')
  .constant('config', {
    authCookieName: 'DiscussionToolAuth',
    oidcAuthorizationUrl: '',
    oidcClientId: '',
    sssRestUrl: '',
    ldRestUrl: '',
    ldClientUrl: '',
    bnpUrl: ''
  });
<|endoftext|>


<commit_before>var serverUsage = false;
if (typeof exports !== "undefined")
{
	serverUsage = true;
	var worldConsts = require("../GameWorld");
    exports.getAstar = function()
	{
		return astar;
    }

    var GAME_WORLD_CELL_BLOCK = worldConsts.GAME_WORLD_CELL_BLOCK;

    var heapFactory = require("./BinaryHeap");
}

var astar = {
    init: function (grid, start, end) {

        //currentId is an important optimization on the client
        //a given browser has a single instance of astar, so instead of
        //marking a node as closed, we give an init id and use this value
        //as close. This way we only init once.
        //could we collide, yea, but for now this is okay
        this.currentId = Math.floor(Math.random() * 2147483647);
        if (astar.grid == undefined) {
            astar.grid = [];
            for (var x = 0; x < grid.length; x++) {
                if (serverUsage) {
                    astar.grid.push([]);
                }
                for (var y = 0; y < grid[x].length; y++) {
                    var intVal = 0;
                    if (serverUsage) { //if we're using this on the server, we don't have a object array, just ints. turn our ints into objs.
                        intVal = grid[x][y];
                        astar.grid[x][y] = {};
                        astar.grid[x][y].x = x;
                        astar.grid[x][y].y = y;
                        astar.grid[x][y].BlockType = intVal;
                    }

                    var node = grid[x][y];
                    node.f
                    node.f = 0;
                    node.g = 0;
                    node.h = 0;
                    node.visited = 0;
                    node.parent = null;
                }
            }

            if (!serverUsage) //if we're not on the server, set our grid to the passed grid.
            {
            	astar.grid = grid;
            }
        }

        start.f
        start.f = 0;
        start.g = 0;
        start.h = 0;
        start.visited = 0;
        start.closed = -1;
        start.parent = null;

        end.f
        end.f = 0;
        end.g = 0;
        end.h = 0;
        end.visited = 0;
        end.closed = -1;

    },
    equals: function (node1, node2) {
        return node1.x == node2.x && node1.y == node2.y;
    },
    nextPoint: function (startPoint, endPoint, distance) {
        //**called: var nextPoint = astar.nextPoint(myPos, seekPos, zom);
        var fullDistance = Math.round(Math.sqrt((endPoint.x - startPoint.x) * (endPoint.x - startPoint.x) + (endPoint.y - startPoint.y) * (endPoint.y - startPoint.y)));

        //if we're there
        if (fullDistance <= 0 || isNaN(fullDistance)) {
            return undefined;
        }

        //if we're almost there
        if (fullDistance < distance) {
            distance = fullDistance
        }

        var midX = startPoint.x + distance / fullDistance * (endPoint.x - startPoint.x);
        var midY = startPoint.y + distance / fullDistance * (endPoint.y - startPoint.y);
        return { x: Math.round(midX), y: Math.round(midY) };
    },
    pathToPoints: function (paths, model, rate, startPoint) {

        if (rate <= 0) { rate = 1; }
        var points = [];
        if (startPoint == undefined) {
            throw "not having a start point causing skipping.";
        }
        points = points.concat(this.derivePoints(undefined, paths[0], model, rate, startPoint));
        for (var i = 0; i < paths.length; i++) {
            if (i + 1 >= paths.length) { //we're done
                return points;
            }
            else {
                points = points.concat(this.derivePoints(paths[i], paths[i + 1], model, rate));
            }
        }
        return points;
    },
    smoothPath: function (path, width, gameWorldModel) {

        if (path == undefined || path.length == 0) {
            throw "smoothPath requires a path";
        }

        if (astar.grid == undefined) {
            throw "smoothPath requires a grid - try astar.search or astar.init first";
        }

        if (gameWorldModel == undefined) {
            throw "smoothPath requires a world model";
        }

        var newPath = [];
        newPath.push(path[0]);

        var current = 0;

        for (var i = 0; i < path.length; i++) {
            var pos = this.getFurthestBline(i, path, path[i], gameWorldModel, width);
            if (pos != undefined) {
                newPath.push(path[pos]);

                if (pos == path.length - 1) {
                    return newPath;
                }
            }
        }

        return path; //no smoothing..?

    },
    getFurthestBline: function (current, path, startCell, gameWorldModel, width) {
        //check this cell for a bline to the furthest path
        for (var i = path.length - 1; i > current; i--) {
            if (this.blineAvail(startCell, path[i], gameWorldModel, width)) {
                return i;
            }
        }

        return undefined;
    },
    derivePoints: function (startCell, endCell, model, rate, spritePoint) {
        //todo: add sprite to this equation
        var startPoint;
        if (spritePoint == undefined) {
            startPoint = model.getAbsoluteCenterOfWorldCell(startCell.x, startCell.y);
        }
        else {
            startPoint = spritePoint;
        }
        var endPoint = model.getAbsoluteCenterOfWorldCell(endCell.x, endCell.y);
        var points = [];
        points.push(startPoint);
        while (startPoint != undefined) {
            startPoint = this.nextPoint(startPoint, endPoint, rate);
            if (startPoint != undefined) {
                points.push(startPoint);
            }
            /*   startPoint = this.nextPoint(startPoint, endPoint, rate);
            if (startPoint != undefined) { //if not undefined
            if (startPoint.x != points[points.length - 1].x && startPoint.y != points[points.length - 1].y) { //if not the same as the last point
            points.push(startPoint);
            }
            else {
            startPoint = undefined;
            }
            }*/
        }
        return points;

    },
    blineAvail: function (start, end, gameWorldModel, width) {
        //draw a line from start to end
        //move down the line, note the cells that are in there
        //if no blocks, return all the cells.
        var path = [];
        startPoint = gameWorldModel.getWorldCellCenter(start.x, start.y);
        startPointLeft = { "x": startPoint.x - width / 2, "y": startPoint.y };
        startPointRight = { "x": startPoint.x + width / 2, "y": startPoint.y };

        endPoint = gameWorldModel.getWorldCellCenter(end.x, end.y);
        endPointLeft = { "x": endPoint.x - width / 2, "y": endPoint.y };
        endPointRight = { "x": endPoint.x + width / 2, "y": endPoint.y };

        //calculate the slope
        //var m = endPoint.y - startPoint.y/endPoint.x - startPoint.x;

        //break the line in speed segments -
        //we make a wild assumption that all speed is the same :(
        var speed = gameWorldModel.cellWidth / 2;

        //find the length of the line
        var distance = Math.sqrt((endPoint.x - startPoint.x) * (endPoint.x - startPoint.x) + (endPoint.y - startPoint.y) * (endPoint.y - startPoint.y));

        var points = distance / speed;

        //given the number of points we chunk our line and using the
        //midpoint function and find cells. We can check if the cells are blockers.
        for (var i = 1; i <= points; i++) {

            if (!this.checkBlock(startPoint, endPoint, speed, i, gameWorldModel, distance)) {
                return false;
            }

            if (!this.checkBlock(startPointLeft, endPointLeft, speed, i, gameWorldModel, distance)) {
                return false;
            }

            if (!this.checkBlock(startPointRight, endPointRight, speed, i, gameWorldModel, distance)) {
                return false;
            }
        }

        return true;
    },
    checkBlock: function (startPoint, endPoint, speed, point, gameWorldModel, distance) {
        var midX = startPoint.x + (speed * point) / distance * (endPoint.x - startPoint.x);
        var midY = startPoint.y + (speed * point) / distance * (endPoint.y - startPoint.y);

        var cell = gameWorldModel.getWorldCellFromScreenCoord(midX, midY);
        if (astar.grid[cell.x] == undefined || astar.grid[cell.x][cell.y] == undefined) {
            return false;
        }
        else if (astar.grid[cell.x][cell.y].BlockType == GAME_WORLD_CELL_BLOCK) {
            return false;
        }

        return true;

    },
    search: function (grid, start, end, gameWorldModel, sprite) {

        astar.init(grid, start, end);

        heuristic = astar.manhattan;

        var openList = [];
        openList.push(start);

        var openHeap;
        if (serverUsage) {
            openHeap = heapFactory.getHeap(function (node) { return node.f; });
        }
        else {
            openHeap = new BinaryHeap(function (node) { return node.f; });
        }

        openHeap.push(start);

        while (openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path
            if (this.equals(currentNode, end)) {
                var curr = currentNode;
                var ret = [];
                while (curr.parent) {
                    ret.push(curr);
                    curr = curr.parent;
                }
                ret[ret.length] = start; //always start with our current spot.
                return ret.reverse();
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors
            currentNode.closed = this.currentId;

            var neighbors = astar.neighbors(currentNode);
            for (var i = 0; i < neighbors.length; i++) {
                var neighbor = neighbors[i];

                if (neighbor.closed == this.currentId ||
                	neighbor.BlockType == GAME_WORLD_CELL_BLOCK
                	) {
                    // not a valid node to process, skip to next neighbor
                    continue;
                }

                // g score is the shortest distance from start to current node, we need to check if
                //   the path we have arrived at this neighbor is the shortest one we have seen yet
                // 1 is the distance from a node to it's neighbor.  This could be variable for weighted paths.
                var gScore = currentNode.g + 1;
                var gScoreIsBest = false;
                var beenVisited = neighbor.visited;

                if (beenVisited != this.currentId || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = this.currentId;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor, end);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;

                    if (beenVisited != this.currentId) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        // No result was found -- empty array signifies failure to find path
        return [];
    },
    manhattan: function (pos0, pos1) {
        var d1 = Math.abs(pos1.x - pos0.x);
        var d2 = Math.abs(pos1.y - pos0.y);
        return d1 + d2;
    },
    neighbors: function (node) { //manhattan is modded to do corners
        var ret = [];
        var x = node.x;
        var y = node.y;

        if (astar.grid[x - 1] != undefined && astar.grid[x - 1][y] != undefined) {
            ret.push(astar.grid[x - 1][y]);
        }

        if (astar.grid[x + 1] != undefined && astar.grid[x + 1][y] != undefined) {
            ret.push(astar.grid[x + 1][y]);
        }

        if (astar.grid[x] != undefined && astar.grid[x][y - 1] != undefined) {
            ret.push(astar.grid[x][y - 1]);
        }

        if (astar.grid[x] != undefined && astar.grid[x][y + 1] != undefined) {
            ret.push(astar.grid[x][y + 1]);
        }

        return ret;
    }
};
<commit_msg>credit where due<commit_after>//initial implementation reviewed from: https://github.com/bgrins/javascript-astar

var serverUsage = false;
if (typeof exports !== "undefined")
{
	serverUsage = true;
	var worldConsts = require("../GameWorld");
    exports.getAstar = function()
	{
		return astar;
    }

    var GAME_WORLD_CELL_BLOCK = worldConsts.GAME_WORLD_CELL_BLOCK;

    var heapFactory = require("./BinaryHeap");
}

var astar = {
    init: function (grid, start, end) {

        //currentId is an important optimization on the client
        //a given browser has a single instance of astar, so instead of
        //marking a node as closed, we give an init id and use this value
        //as close. This way we only init once.
        //could we collide, yea, but for now this is okay
        this.currentId = Math.floor(Math.random() * 2147483647);
        if (astar.grid == undefined) {
            astar.grid = [];
            for (var x = 0; x < grid.length; x++) {
                if (serverUsage) {
                    astar.grid.push([]);
                }
                for (var y = 0; y < grid[x].length; y++) {
                    var intVal = 0;
                    if (serverUsage) { //if we're using this on the server, we don't have a object array, just ints. turn our ints into objs.
                        intVal = grid[x][y];
                        astar.grid[x][y] = {};
                        astar.grid[x][y].x = x;
                        astar.grid[x][y].y = y;
                        astar.grid[x][y].BlockType = intVal;
                    }

                    var node = grid[x][y];
                    node.f
                    node.f = 0;
                    node.g = 0;
                    node.h = 0;
                    node.visited = 0;
                    node.parent = null;
                }
            }

            if (!serverUsage) //if we're not on the server, set our grid to the passed grid.
            {
            	astar.grid = grid;
            }
        }

        start.f
        start.f = 0;
        start.g = 0;
        start.h = 0;
        start.visited = 0;
        start.closed = -1;
        start.parent = null;

        end.f
        end.f = 0;
        end.g = 0;
        end.h = 0;
        end.visited = 0;
        end.closed = -1;

    },
    equals: function (node1, node2) {
        return node1.x == node2.x && node1.y == node2.y;
    },
    nextPoint: function (startPoint, endPoint, distance) {
        //**called: var nextPoint = astar.nextPoint(myPos, seekPos, zom);
        var fullDistance = Math.round(Math.sqrt((endPoint.x - startPoint.x) * (endPoint.x - startPoint.x) + (endPoint.y - startPoint.y) * (endPoint.y - startPoint.y)));

        //if we're there
        if (fullDistance <= 0 || isNaN(fullDistance)) {
            return undefined;
        }

        //if we're almost there
        if (fullDistance < distance) {
            distance = fullDistance
        }

        var midX = startPoint.x + distance / fullDistance * (endPoint.x - startPoint.x);
        var midY = startPoint.y + distance / fullDistance * (endPoint.y - startPoint.y);
        return { x: Math.round(midX), y: Math.round(midY) };
    },
    pathToPoints: function (paths, model, rate, startPoint) {

        if (rate <= 0) { rate = 1; }
        var points = [];
        if (startPoint == undefined) {
            throw "not having a start point causing skipping.";
        }
        points = points.concat(this.derivePoints(undefined, paths[0], model, rate, startPoint));
        for (var i = 0; i < paths.length; i++) {
            if (i + 1 >= paths.length) { //we're done
                return points;
            }
            else {
                points = points.concat(this.derivePoints(paths[i], paths[i + 1], model, rate));
            }
        }
        return points;
    },
    smoothPath: function (path, width, gameWorldModel) {

        if (path == undefined || path.length == 0) {
            throw "smoothPath requires a path";
        }

        if (astar.grid == undefined) {
            throw "smoothPath requires a grid - try astar.search or astar.init first";
        }

        if (gameWorldModel == undefined) {
            throw "smoothPath requires a world model";
        }

        var newPath = [];
        newPath.push(path[0]);

        var current = 0;

        for (var i = 0; i < path.length; i++) {
            var pos = this.getFurthestBline(i, path, path[i], gameWorldModel, width);
            if (pos != undefined) {
                newPath.push(path[pos]);

                if (pos == path.length - 1) {
                    return newPath;
                }
            }
        }

        return path; //no smoothing..?

    },
    getFurthestBline: function (current, path, startCell, gameWorldModel, width) {
        //check this cell for a bline to the furthest path
        for (var i = path.length - 1; i > current; i--) {
            if (this.blineAvail(startCell, path[i], gameWorldModel, width)) {
                return i;
            }
        }

        return undefined;
    },
    derivePoints: function (startCell, endCell, model, rate, spritePoint) {
        //todo: add sprite to this equation
        var startPoint;
        if (spritePoint == undefined) {
            startPoint = model.getAbsoluteCenterOfWorldCell(startCell.x, startCell.y);
        }
        else {
            startPoint = spritePoint;
        }
        var endPoint = model.getAbsoluteCenterOfWorldCell(endCell.x, endCell.y);
        var points = [];
        points.push(startPoint);
        while (startPoint != undefined) {
            startPoint = this.nextPoint(startPoint, endPoint, rate);
            if (startPoint != undefined) {
                points.push(startPoint);
            }
            /*   startPoint = this.nextPoint(startPoint, endPoint, rate);
            if (startPoint != undefined) { //if not undefined
            if (startPoint.x != points[points.length - 1].x && startPoint.y != points[points.length - 1].y) { //if not the same as the last point
            points.push(startPoint);
            }
            else {
            startPoint = undefined;
            }
            }*/
        }
        return points;

    },
    blineAvail: function (start, end, gameWorldModel, width) {
        //draw a line from start to end
        //move down the line, note the cells that are in there
        //if no blocks, return all the cells.
        var path = [];
        startPoint = gameWorldModel.getWorldCellCenter(start.x, start.y);
        startPointLeft = { "x": startPoint.x - width / 2, "y": startPoint.y };
        startPointRight = { "x": startPoint.x + width / 2, "y": startPoint.y };

        endPoint = gameWorldModel.getWorldCellCenter(end.x, end.y);
        endPointLeft = { "x": endPoint.x - width / 2, "y": endPoint.y };
        endPointRight = { "x": endPoint.x + width / 2, "y": endPoint.y };

        //calculate the slope
        //var m = endPoint.y - startPoint.y/endPoint.x - startPoint.x;

        //break the line in speed segments -
        //we make a wild assumption that all speed is the same :(
        var speed = gameWorldModel.cellWidth / 2;

        //find the length of the line
        var distance = Math.sqrt((endPoint.x - startPoint.x) * (endPoint.x - startPoint.x) + (endPoint.y - startPoint.y) * (endPoint.y - startPoint.y));

        var points = distance / speed;

        //given the number of points we chunk our line and using the
        //midpoint function and find cells. We can check if the cells are blockers.
        for (var i = 1; i <= points; i++) {

            if (!this.checkBlock(startPoint, endPoint, speed, i, gameWorldModel, distance)) {
                return false;
            }

            if (!this.checkBlock(startPointLeft, endPointLeft, speed, i, gameWorldModel, distance)) {
                return false;
            }

            if (!this.checkBlock(startPointRight, endPointRight, speed, i, gameWorldModel, distance)) {
                return false;
            }
        }

        return true;
    },
    checkBlock: function (startPoint, endPoint, speed, point, gameWorldModel, distance) {
        var midX = startPoint.x + (speed * point) / distance * (endPoint.x - startPoint.x);
        var midY = startPoint.y + (speed * point) / distance * (endPoint.y - startPoint.y);

        var cell = gameWorldModel.getWorldCellFromScreenCoord(midX, midY);
        if (astar.grid[cell.x] == undefined || astar.grid[cell.x][cell.y] == undefined) {
            return false;
        }
        else if (astar.grid[cell.x][cell.y].BlockType == GAME_WORLD_CELL_BLOCK) {
            return false;
        }

        return true;

    },
    search: function (grid, start, end, gameWorldModel, sprite) {

        astar.init(grid, start, end);

        heuristic = astar.manhattan;

        var openList = [];
        openList.push(start);

        var openHeap;
        if (serverUsage) {
            openHeap = heapFactory.getHeap(function (node) { return node.f; });
        }
        else {
            openHeap = new BinaryHeap(function (node) { return node.f; });
        }

        openHeap.push(start);

        while (openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path
            if (this.equals(currentNode, end)) {
                var curr = currentNode;
                var ret = [];
                while (curr.parent) {
                    ret.push(curr);
                    curr = curr.parent;
                }
                ret[ret.length] = start; //always start with our current spot.
                return ret.reverse();
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors
            currentNode.closed = this.currentId;

            var neighbors = astar.neighbors(currentNode);
            for (var i = 0; i < neighbors.length; i++) {
                var neighbor = neighbors[i];

                if (neighbor.closed == this.currentId ||
                	neighbor.BlockType == GAME_WORLD_CELL_BLOCK
                	) {
                    // not a valid node to process, skip to next neighbor
                    continue;
                }

                // g score is the shortest distance from start to current node, we need to check if
                //   the path we have arrived at this neighbor is the shortest one we have seen yet
                // 1 is the distance from a node to it's neighbor.  This could be variable for weighted paths.
                var gScore = currentNode.g + 1;
                var gScoreIsBest = false;
                var beenVisited = neighbor.visited;

                if (beenVisited != this.currentId || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = this.currentId;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor, end);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;

                    if (beenVisited != this.currentId) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        // No result was found -- empty array signifies failure to find path
        return [];
    },
    manhattan: function (pos0, pos1) {
        var d1 = Math.abs(pos1.x - pos0.x);
        var d2 = Math.abs(pos1.y - pos0.y);
        return d1 + d2;
    },
    neighbors: function (node) { //manhattan is modded to do corners
        var ret = [];
        var x = node.x;
        var y = node.y;

        if (astar.grid[x - 1] != undefined && astar.grid[x - 1][y] != undefined) {
            ret.push(astar.grid[x - 1][y]);
        }

        if (astar.grid[x + 1] != undefined && astar.grid[x + 1][y] != undefined) {
            ret.push(astar.grid[x + 1][y]);
        }

        if (astar.grid[x] != undefined && astar.grid[x][y - 1] != undefined) {
            ret.push(astar.grid[x][y - 1]);
        }

        if (astar.grid[x] != undefined && astar.grid[x][y + 1] != undefined) {
            ret.push(astar.grid[x][y + 1]);
        }

        return ret;
    }
};
<|endoftext|>


<commit_before>
var requiredMethods = [
	'set',
	'init',
	'capture',
	'liveviewMode',
	'liveviewImage',
	'_event',
	'_error'
]

var optionalMethods = [
	'af',
	'setFocusPoint',
	'captureHDR',
	'moveFocus',
]

var requiredObjects = [
	'supportedCameras',
	'properties',
]

var requiredProperties = [
	'name',
]

var requiredExposureSettings = [
	{ name: 'shutter', values: [
	    { name: "30s",     ev: -11,         required: true },
	    { name: "25s",     ev: -10 - 2 / 3, required: true },
	    { name: "20s",     ev: -10 - 1 / 3, required: true },
	    { name: "15s",     ev: -10,         required: true },
	    { name: "13s",     ev: -9 - 2 / 3,  required: true },
	    { name: "10s",     ev: -9 - 1 / 3,  required: true },
	    { name: "8s",      ev: -9,          required: true },
	    { name: "6s",      ev: -8 - 2 / 3,  required: true },
	    { name: "5s",      ev: -8 - 1 / 3,  required: true },
	    { name: "4s",      ev: -8,          required: true },
	    { name: "3s",      ev: -7 - 2 / 3,  required: true },
	    { name: "2.5s",    ev: -7 - 1 / 3,  required: true },
	    { name: "2s",      ev: -7,          required: true },
	    { name: "1.6s",    ev: -6 - 2 / 3,  required: true },
	    { name: "1.3s",    ev: -6 - 1 / 3,  required: true },
	    { name: "1s",      ev: -6,          required: true },
	    { name: "0.8s",    ev: -5 - 2 / 3,  required: true },
	    { name: "0.6s",    ev: -5 - 1 / 3,  required: true },
	    { name: "1/2",     ev: -5,          required: true },
	    { name: "0.4s",    ev: -4 - 2 / 3,  required: true },
	    { name: "1/3",     ev: -4 - 1 / 3,  required: true },
	    { name: "1/4",     ev: -4,          required: true },
	    { name: "1/5",     ev: -3 - 2 / 3,  required: true },
	    { name: "1/6",     ev: -3 - 1 / 3,  required: true },
	    { name: "1/8",     ev: -3,          required: true },
	    { name: "1/10",    ev: -2 - 2 / 3,  required: true },
	    { name: "1/13",    ev: -2 - 1 / 3,  required: true },
	    { name: "1/15",    ev: -2,          required: true },
	    { name: "1/20",    ev: -1 - 2 / 3,  required: true },
	    { name: "1/25",    ev: -1 - 1 / 3,  required: true },
	    { name: "1/30",    ev: -1,          required: true },
	    { name: "1/40",    ev: 0 - 2 / 3,   required: true },
	    { name: "1/50",    ev: 0 - 1 / 3,   required: true },
	    { name: "1/60",    ev: 0,           required: true },
	    { name: "1/80",    ev: 0 + 1 / 3,   required: true },
	    { name: "1/100",   ev: 0 + 2 / 3,   required: true },
	    { name: "1/125",   ev: 1,           required: true },
	    { name: "1/160",   ev: 1 + 1 / 3,   required: true },
	    { name: "1/200",   ev: 1 + 2 / 3,   required: true },
	    { name: "1/250",   ev: 2,           required: true },
	    { name: "1/320",   ev: 2 + 1 / 3,   required: true },
	    { name: "1/400",   ev: 2 + 2 / 3,   required: true },
	    { name: "1/500",   ev: 3,           required: true },
	    { name: "1/640",   ev: 3 + 1 / 3,   required: true },
	    { name: "1/800",   ev: 3 + 2 / 3,   required: true },
	    { name: "1/1000",  ev: 4,           required: true },
	    { name: "1/1250",  ev: 4 + 1 / 3,   required: true },
	    { name: "1/1600",  ev: 4 + 2 / 3,   required: true },
	    { name: "1/2000",  ev: 5,           required: true },
	]},
	{ name: 'iso', values: [
	    { name: "200",      ev: -1,         required: true },
	    { name: "250",      ev: -1 - 1 / 3, required: true },
	    { name: "320",      ev: -1 - 2 / 3, required: true },
	    { name: "400",      ev: -2,         required: true },
	    { name: "500",      ev: -2 - 1 / 3, required: true },
	    { name: "640",      ev: -2 - 2 / 3, required: true },
	    { name: "800",      ev: -3,         required: true },
	    { name: "1000",     ev: -3 - 1 / 3, required: true },
	    { name: "1250",     ev: -3 - 2 / 3, required: true },
	    { name: "1600",     ev: -4,         required: true },
	    { name: "2000",     ev: -4 - 1 / 3, required: true },
	    { name: "2500",     ev: -4 - 2 / 3, required: true },
	    { name: "3200",     ev: -5,         required: true },
	]},
	{ name: 'aperture', values: [
	    { name: "1.8",      ev: -6 - 1 / 3, required: true },
	    { name: "2.0",      ev: -6,         required: true },
	    { name: "2.2",      ev: -5 - 2 / 3, required: true },
	    { name: "2.5",      ev: -5 - 1 / 3, required: true },
	    { name: "2.8",      ev: -5,         required: true },
	    { name: "3.2",      ev: -4 - 2 / 3, required: true },
	    { name: "3.5",      ev: -4 - 1 / 3, required: true },
	    { name: "3.6",      ev: -4 - 1 / 3, required: true },
	    { name: "4.0",      ev: -4,         required: true },
	    { name: "4.5",      ev: -3 - 2 / 3, required: true },
	    { name: "5.0",      ev: -3 - 1 / 3, required: true },
	    { name: "5.6",      ev: -3,         required: true },
	    { name: "6.3",      ev: -2 - 2 / 3, required: true },
	    { name: "6.3",      ev: -2 - 2 / 3, required: true },
	    { name: "7.1",      ev: -2 - 1 / 3, required: true },
	    { name: "8",        ev: -2,         required: true },
	    { name: "9",        ev: -1 - 2 / 3, required: true },
	    { name: "10",       ev: -1 - 1 / 3, required: true },
	    { name: "11",       ev: -1,         required: true },
	    { name: "13",       ev: -0 - 2 / 3, required: true },
	    { name: "14",       ev: -0 - 1 / 3, required: true },
	    { name: "16",       ev:  0,         required: true },
	    { name: "18",       ev:  0 + 1 / 3, required: true },
	    { name: "20",       ev:  0 + 2 / 3, required: true },
	    { name: "22",       ev:  1,         required: true },
	]}
]

exports.driver = function(driver) {
	var name = driver.name || "(missing name)";
	for(var i = 0; i < requiredMethods.length; i++) {
		if(typeof driver[requiredMethods[i]] != 'function') console.log("DRIVER TEST: missing required '" + requiredMethods[i] + "' method in driver", name);
	}

	for(var i = 0; i < optionalMethods.length; i++) {
		if(typeof driver[optionalMethods[i]] != 'function') console.log("DRIVER TEST: missing optional '" + optionalMethods[i] + "' method in driver", name);
	}

	for(var i = 0; i < requiredObjects.length; i++) {
		if(typeof driver[requiredObjects[i]] != 'object') console.log("DRIVER TEST: missing required '" + requiredObjects[i] + "' object in driver", name);
	}

	for(var i = 0; i < requiredProperties.length; i++) {
		if(driver[requiredProperties[i]] == null) console.log("DRIVER TEST: missing required '" + requiredProperties[i] + "' property in driver", name);
	}

	if(!driver.properties) return;

	for(var i = 0; i < requiredExposureSettings.length; i++) {
		if(typeof driver.properties[requiredExposureSettings[i].name] == 'object') {
			var setting = driver.properties[requiredExposureSettings[i].name];
			if(!setting.ev) console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' should have ev flag set in driver", name);
			if(setting.category != "exposure") console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' category must be 'exposure' in driver", name);
			if(Array.isArray(setting.values) && setting.values.length > 1) {
				for(j = 0; j < requiredExposureSettings[i].values.length; j++) {
					var testVal = requiredExposureSettings[i].values[j];
					var foundVal = null;
					for(var k = 0; k < settings.values; k++) {
						if(testVal.name == setting.values[k].name) {
							foundVal = setting.values[k];
							break;
						}
					}
					if(foundVal) {
						if(foundVal.ev != testVal.ev) {
							console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' ev should be '" + testVal.ev + "' in driver", name);
						}
					} else if(testVal.required) {
						console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' missing value for '" + testVal.name + "' in driver", name);
					}
				}
			} else {
				console.log("DRIVER TEST: setting '" + requiredExposureSettings[i] + "' missing 'values' array in driver", name);
			}
		} else {
			console.log("DRIVER TEST: missing required '" + requiredExposureSettings[i] + "' exposure setting in driver", name);
		}
	}
}
<commit_msg>added more driver tests<commit_after>
var requiredMethods = [
	'set',
	'init',
	'capture',
	'liveviewMode',
	'liveviewImage',
	'_event',
	'_error'
]

var optionalMethods = [
	'af',
	'setFocusPoint',
	'captureHDR',
	'moveFocus',
]

var requiredObjects = [
	'supportedCameras',
	'properties',
]

var requiredProperties = [
	'name',
]

var requiredExposureSettings = [
	{ name: 'shutter', values: [
	    { name: "30s",     ev: -11,         required: true },
	    { name: "25s",     ev: -10 - 2 / 3, required: true },
	    { name: "20s",     ev: -10 - 1 / 3, required: true },
	    { name: "15s",     ev: -10,         required: true },
	    { name: "13s",     ev: -9 - 2 / 3,  required: true },
	    { name: "10s",     ev: -9 - 1 / 3,  required: true },
	    { name: "8s",      ev: -9,          required: true },
	    { name: "6s",      ev: -8 - 2 / 3,  required: true },
	    { name: "5s",      ev: -8 - 1 / 3,  required: true },
	    { name: "4s",      ev: -8,          required: true },
	    { name: "3s",      ev: -7 - 2 / 3,  required: true },
	    { name: "2.5s",    ev: -7 - 1 / 3,  required: true },
	    { name: "2s",      ev: -7,          required: true },
	    { name: "1.6s",    ev: -6 - 2 / 3,  required: true },
	    { name: "1.3s",    ev: -6 - 1 / 3,  required: true },
	    { name: "1s",      ev: -6,          required: true },
	    { name: "0.8s",    ev: -5 - 2 / 3,  required: true },
	    { name: "0.6s",    ev: -5 - 1 / 3,  required: true },
	    { name: "1/2",     ev: -5,          required: true },
	    { name: "0.4s",    ev: -4 - 2 / 3,  required: true },
	    { name: "1/3",     ev: -4 - 1 / 3,  required: true },
	    { name: "1/4",     ev: -4,          required: true },
	    { name: "1/5",     ev: -3 - 2 / 3,  required: true },
	    { name: "1/6",     ev: -3 - 1 / 3,  required: true },
	    { name: "1/8",     ev: -3,          required: true },
	    { name: "1/10",    ev: -2 - 2 / 3,  required: true },
	    { name: "1/13",    ev: -2 - 1 / 3,  required: true },
	    { name: "1/15",    ev: -2,          required: true },
	    { name: "1/20",    ev: -1 - 2 / 3,  required: true },
	    { name: "1/25",    ev: -1 - 1 / 3,  required: true },
	    { name: "1/30",    ev: -1,          required: true },
	    { name: "1/40",    ev: 0 - 2 / 3,   required: true },
	    { name: "1/50",    ev: 0 - 1 / 3,   required: true },
	    { name: "1/60",    ev: 0,           required: true },
	    { name: "1/80",    ev: 0 + 1 / 3,   required: true },
	    { name: "1/100",   ev: 0 + 2 / 3,   required: true },
	    { name: "1/125",   ev: 1,           required: true },
	    { name: "1/160",   ev: 1 + 1 / 3,   required: true },
	    { name: "1/200",   ev: 1 + 2 / 3,   required: true },
	    { name: "1/250",   ev: 2,           required: true },
	    { name: "1/320",   ev: 2 + 1 / 3,   required: true },
	    { name: "1/400",   ev: 2 + 2 / 3,   required: true },
	    { name: "1/500",   ev: 3,           required: true },
	    { name: "1/640",   ev: 3 + 1 / 3,   required: true },
	    { name: "1/800",   ev: 3 + 2 / 3,   required: true },
	    { name: "1/1000",  ev: 4,           required: true },
	    { name: "1/1250",  ev: 4 + 1 / 3,   required: true },
	    { name: "1/1600",  ev: 4 + 2 / 3,   required: true },
	    { name: "1/2000",  ev: 5,           required: true },
	]},
	{ name: 'iso', values: [
	    { name: "200",      ev: -1,         required: true },
	    { name: "250",      ev: -1 - 1 / 3, required: true },
	    { name: "320",      ev: -1 - 2 / 3, required: true },
	    { name: "400",      ev: -2,         required: true },
	    { name: "500",      ev: -2 - 1 / 3, required: true },
	    { name: "640",      ev: -2 - 2 / 3, required: true },
	    { name: "800",      ev: -3,         required: true },
	    { name: "1000",     ev: -3 - 1 / 3, required: true },
	    { name: "1250",     ev: -3 - 2 / 3, required: true },
	    { name: "1600",     ev: -4,         required: true },
	    { name: "2000",     ev: -4 - 1 / 3, required: true },
	    { name: "2500",     ev: -4 - 2 / 3, required: true },
	    { name: "3200",     ev: -5,         required: true },
	]},
	{ name: 'aperture', values: [
	    { name: "1.8",      ev: -6 - 1 / 3, required: true },
	    { name: "2.0",      ev: -6,         required: true },
	    { name: "2.2",      ev: -5 - 2 / 3, required: true },
	    { name: "2.5",      ev: -5 - 1 / 3, required: true },
	    { name: "2.8",      ev: -5,         required: true },
	    { name: "3.2",      ev: -4 - 2 / 3, required: true },
	    { name: "3.5",      ev: -4 - 1 / 3, required: true },
	    { name: "3.6",      ev: -4 - 1 / 3, required: true },
	    { name: "4.0",      ev: -4,         required: true },
	    { name: "4.5",      ev: -3 - 2 / 3, required: true },
	    { name: "5.0",      ev: -3 - 1 / 3, required: true },
	    { name: "5.6",      ev: -3,         required: true },
	    { name: "6.3",      ev: -2 - 2 / 3, required: true },
	    { name: "6.3",      ev: -2 - 2 / 3, required: true },
	    { name: "7.1",      ev: -2 - 1 / 3, required: true },
	    { name: "8",        ev: -2,         required: true },
	    { name: "9",        ev: -1 - 2 / 3, required: true },
	    { name: "10",       ev: -1 - 1 / 3, required: true },
	    { name: "11",       ev: -1,         required: true },
	    { name: "13",       ev: -0 - 2 / 3, required: true },
	    { name: "14",       ev: -0 - 1 / 3, required: true },
	    { name: "16",       ev:  0,         required: true },
	    { name: "18",       ev:  0 + 1 / 3, required: true },
	    { name: "20",       ev:  0 + 2 / 3, required: true },
	    { name: "22",       ev:  1,         required: true },
	]}
]

exports.driver = function(driver) {
	var name = driver.name || "(missing name)";
	for(var i = 0; i < requiredMethods.length; i++) {
		if(typeof driver[requiredMethods[i]] != 'function') console.log("DRIVER TEST: missing required '" + requiredMethods[i] + "' method in driver", name);
	}

	for(var i = 0; i < optionalMethods.length; i++) {
		if(typeof driver[optionalMethods[i]] != 'function') console.log("DRIVER TEST: missing optional '" + optionalMethods[i] + "' method in driver", name);
	}

	for(var i = 0; i < requiredObjects.length; i++) {
		if(typeof driver[requiredObjects[i]] != 'object') console.log("DRIVER TEST: missing required '" + requiredObjects[i] + "' object in driver", name);
	}

	for(var i = 0; i < requiredProperties.length; i++) {
		if(driver[requiredProperties[i]] == null) console.log("DRIVER TEST: missing required '" + requiredProperties[i] + "' property in driver", name);
	}

	if(!driver.properties) return;

	for(var i = 0; i < requiredExposureSettings.length; i++) {
		if(typeof driver.properties[requiredExposureSettings[i].name] == 'object') {
			var setting = driver.properties[requiredExposureSettings[i].name];
			if(!setting.ev) console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' should have ev flag set in driver", name);
			if(setting.category != "exposure") console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' category must be 'exposure' in driver", name);
			if(Array.isArray(setting.values) && setting.values.length > 1) {
				for(j = 0; j < requiredExposureSettings[i].values.length; j++) {
					var testVal = requiredExposureSettings[i].values[j];
					var foundVal = null;
					for(var k = 0; k < settings.values; k++) {
						if(testVal.name == setting.values[k].name) {
							foundVal = setting.values[k];
							break;
						}
					}
					if(foundVal) {
						if(foundVal.ev != testVal.ev) {
							console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' ev should be '" + testVal.ev + "' in driver", name);
						}
					} else if(testVal.required) {
						console.log("DRIVER TEST: setting '" + requiredExposureSettings[i].name + "' missing value for '" + testVal.name + "' in driver", name);
					}
				}
			} else {
				console.log("DRIVER TEST: setting '" + requiredExposureSettings[i] + "' missing 'values' array in driver", name);
			}
		} else {
			console.log("DRIVER TEST: missing required '" + requiredExposureSettings[i] + "' exposure setting in driver", name);
		}
	}
}

<|endoftext|>