<commit_before>import MemberImportError from 'ghost-admin/errors/member-import-error';
import Service, {inject as service} from '@ember/service';
import validator from 'validator';

export default Service.extend({
    ajax: service(),
    membersUtils: service(),
    ghostPaths: service(),

    async check(data) {
        if (!data || !data.length) {
            return [new MemberImportError('File is empty, nothing to import. Please select a different file.')];
        }

        let validatedSet = [];
        let validationSampleSize = 15;
        let validationResults = [];

        if (data && data.length > validationSampleSize) {
            // validated data size is larger than sample size take 3
            // equal parts from head, tail and middle of the data set
            const partitionSize = validationSampleSize / 3;

            const head = data.slice(0, partitionSize);
            const tail = data.slice((data.length - partitionSize), data.length);

            const middleIndex = Math.floor(data.length / 2);
            const middleStartIndex = middleIndex - 2;
            const middleEndIndex = middleIndex + 3;
            const middle = data.slice(middleStartIndex, middleEndIndex);

            validatedSet.push(...head);
            validatedSet.push(...middle);
            validatedSet.push(...tail);
        } else {
            validatedSet = data;
        }

        let emailValidation = this._checkEmails(validatedSet);
        if (emailValidation !== true) {
            validationResults.push(new MemberImportError('Emails in provided data don\'t appear to be valid email addresses.'));
        }

        const hasStripeId = this._containsRecordsWithStripeId(validatedSet);

        if (hasStripeId) {
            let stripeLocalValidation = this._checkStripeLocal(validatedSet);
            if (stripeLocalValidation !== true) {
                validationResults.push(new MemberImportError('Stripe customer IDs exist in the data, but no stripe account is connected.'));
            }

            if (stripeLocalValidation === true && this.membersUtils.isStripeEnabled) {
                let stripeSeverValidation = await this._checkStripeServer(validatedSet);
                if (stripeSeverValidation !== true) {
                    validationResults.push(new MemberImportError('Stripe customer IDs exist in the data, but we could not find such customer in connected Stripe account'));
                }
            }
        }

        if (validationResults.length) {
            return validationResults;
        } else {
            return true;
        }
    },

    _containsRecordsWithStripeId(validatedSet) {
        let memberWithStripeId = validatedSet.find(m => !!(m.stripe_customer_id));
        return !!memberWithStripeId;
    },

    _checkEmails(validatedSet) {
        let result = true;

        validatedSet.forEach((member) => {
            if (!member.email) {
                result = false;
            }

            if (member.email && !validator.isEmail(member.email)) {
                result = false;
            }
        });

        return result;
    },

    _checkStripeLocal(validatedSet) {
        let result = true;

        if (!this.membersUtils.isStripeEnabled) {
            validatedSet.forEach((member) => {
                if (member.stripe_customer_id) {
                    result = false;
                }
            });
        }

        return result;
    },

    async _checkStripeServer(validatedSet) {
        const url = this.ghostPaths.get('url').api('members/upload/validate');

        let response;
        try {
            response = await this.ajax.post(url, {
                data: {
                    members: validatedSet
                }
            });
        } catch (e) {
            return false;
        }

        if (response.errors) {
            return false;
        }

        return true;
    }
});
<commit_msg>Added Stripe customer id duplication detection for member import<commit_after>import MemberImportError from 'ghost-admin/errors/member-import-error';
import Service, {inject as service} from '@ember/service';
import validator from 'validator';

export default Service.extend({
    ajax: service(),
    membersUtils: service(),
    ghostPaths: service(),

    async check(data) {
        if (!data || !data.length) {
            return [new MemberImportError('File is empty, nothing to import. Please select a different file.')];
        }

        let validatedSet = [];
        let validationSampleSize = 15;
        let validationResults = [];

        if (data && data.length > validationSampleSize) {
            // validated data size is larger than sample size take 3
            // equal parts from head, tail and middle of the data set
            const partitionSize = validationSampleSize / 3;

            const head = data.slice(0, partitionSize);
            const tail = data.slice((data.length - partitionSize), data.length);

            const middleIndex = Math.floor(data.length / 2);
            const middleStartIndex = middleIndex - 2;
            const middleEndIndex = middleIndex + 3;
            const middle = data.slice(middleStartIndex, middleEndIndex);

            validatedSet.push(...head);
            validatedSet.push(...middle);
            validatedSet.push(...tail);
        } else {
            validatedSet = data;
        }

        // check can be done on whole set as it won't be too slow
        let emailValidation = this._checkEmails(data);
        if (emailValidation !== true) {
            validationResults.push(new MemberImportError('Emails in provided data don\'t appear to be valid email addresses.'));
        }

        const hasStripeId = this._containsRecordsWithStripeId(validatedSet);

        if (hasStripeId) {
            // check can be done on whole set as it won't be too slow
            let hasDuplicateStripeIds = this._hasDuplicateStripeIds(data);
            if (hasDuplicateStripeIds === true) {
                validationResults.push(new MemberImportError('Some members will not be imported. Members with duplicate Stripe customer ids are not allowed.'));
            }

            if (!this.membersUtils.isStripeEnabled) {
                validationResults.push(new MemberImportError('Stripe customer IDs exist in the data, but no stripe account is connected.'));
            } else {
                let stripeSeverValidation = await this._checkStripeServer(validatedSet);
                if (stripeSeverValidation !== true) {
                    validationResults.push(new MemberImportError('Stripe customer IDs exist in the data, but we could not find such customer in connected Stripe account'));
                }
            }
        }

        if (validationResults.length) {
            return validationResults;
        } else {
            return true;
        }
    },

    _containsRecordsWithStripeId(validatedSet) {
        let memberWithStripeId = validatedSet.find(m => !!(m.stripe_customer_id));
        return !!memberWithStripeId;
    },

    _checkEmails(validatedSet) {
        let result = true;

        validatedSet.forEach((member) => {
            if (!member.email) {
                result = false;
            }

            if (member.email && !validator.isEmail(member.email)) {
                result = false;
            }
        });

        return result;
    },

    _hasDuplicateStripeIds(validatedSet) {
        const customersMap = validatedSet.reduce((acc, member) => {
            if (member.stripe_customer_id && member.stripe_customer_id !== 'undefined') {
                if (acc[member.stripe_customer_id]) {
                    acc[member.stripe_customer_id] += 1;
                } else {
                    acc[member.stripe_customer_id] = 1;
                }
            }

            return acc;
        }, {});

        for (const key in customersMap) {
            if (customersMap[key] > 1) {
                return true;
            }
        }
    },

    _checkContainsStripeIDs(validatedSet) {
        let result = true;

        if (!this.membersUtils.isStripeEnabled) {
            validatedSet.forEach((member) => {
                if (member.stripe_customer_id) {
                    result = false;
                }
            });
        }

        return result;
    },

    async _checkStripeServer(validatedSet) {
        const url = this.ghostPaths.get('url').api('members/upload/validate');

        let response;
        try {
            response = await this.ajax.post(url, {
                data: {
                    members: validatedSet
                }
            });
        } catch (e) {
            return false;
        }

        if (response.errors) {
            return false;
        }

        return true;
    }
});
<|endoftext|>


<commit_before>"use strict";

var pgMap = require('./pgMap.js');
var debug = require('debug')('OSMBC:model:user');
var should = require('should');
var async = require('async');
var messageCenter = require('../notification/messageCenter.js');
var mailReceiver = require('../notification/mailReceiver.js');
var random = require('randomstring');
var emailValidator = require('email-validator');

function User (proto)
{
	debug("User");
  debug("Prototype %s",JSON.stringify(proto));
  this.id = 0;
  for (var k in proto) {
    this[k] = proto[k];
  }
}

function create (proto) {
	debug("create");
	return new User(proto);
}

function createNewUser (proto,callback) {
  debug("createNewUser");
  if (typeof(proto)=='function') {
    callback = proto;
    proto = null;
  }
  if (proto) should.not.exist(proto.id);
  var user = create(proto);
  find({OSMUser:user.OSMUser},function (err,result){
    if (err) return callback(err);
    if (result && result.length>0) {
      return callback(new Error("User >"+user.OSMUser+"< already exists."));
    }
    // set some defaults for the user
    if (!proto) user.mailNewCollection="false";
    if (!proto) user.mailAllComment = "false";
    if (!proto) user.mailComment=[];
    if (!proto) user.mailBlogLanguageStatusChange=[];
    // save data
    user.save(function updateUser(err,result){
      if (err) return callback(err,result);
      mailReceiver.updateUser(result);
      callback(null,result);
    });
  });
}

User.prototype.calculateChanges = function calculateChanges(callback) {
  debug('User.prototype.calculateChanges');
  var self=this;
  if (self._countChanges) return;
  pgMap.count("select count(*) as count from changes where data->>'user'='"+this.OSMUser+"' and data->>'table'='article'",function(err,result){
    if (err) return callback(err);
    self._countChanges = result.count;
    return callback();
  });
};

User.prototype.remove = pgMap.remove;

function find(obj,ord,callback) {
	debug("find");
  pgMap.find({table:"usert",create:create},obj,ord,callback);
}
function findById(id,callback) {
	debug("findById %s",id);
  pgMap.findById(id,{table:"usert",create:create},callback);
}

function findOne(obj1,obj2,callback) {
  debug("findOne");
  pgMap.findOne({table:"usert",create:create},obj1,obj2,callback);
}

var pgObject= {};
pgObject.createString = 'CREATE TABLE usert (  id bigserial NOT NULL,  data json,  \
                  CONSTRAINT user_pkey PRIMARY KEY (id) ) WITH (  OIDS=FALSE);';
pgObject.indexDefinition={};
pgObject.viewDefinition = {};
pgObject.table="usert";
module.exports.pg = pgObject;

User.prototype.validateEmail = function validateEmail(user,validationCode,callback) {
  debug('validateEmail');
  should(typeof(user)).eql("object");
  should(typeof(validationCode)).eql("string");
  should(typeof(callback)).eql("function");
  var self = this;
  var err;
  if (self.OSMUser !== user.OSMUser) {
    debug("User is wrong");
    err = new Error("Wrong User: expected >"+self.OSMUser+"< given >"+user.OSMUser+"<");
    return callback(err);
  }
  if (!self.emailInvalidation) {
    debug("nothing in validation");
    err = new Error("No Validation pending for user >"+self.OSMUser+"<");
    return callback(err);
  }
  if (validationCode !== self.emailValidationKey) {
    debug("Validation Code is wrong");
    err = new Error("Wrong Validation Code for EMail for user >"+self.OSMUser+"<");
    messageCenter.global.sendInfo({oid:self.id,user:user.OSMUser,table:"usert",property:"email",from:null,to:"Validation Failed"},function(){
      return callback(err);
    });
    return;
  }
  debug("Email Validation OK saving User");
  var oldmail = self.email;
  self.email = self.emailInvalidation;
  delete self.emailInvalidation;
  delete self.emailValidationKey;
  self.save(function logit(err){
    mailReceiver.updateUser(self);
    if (err) return callback(err);
    messageCenter.global.sendInfo({oid:self.id,user:user.OSMUser,table:"usert",property:"email",from:oldmail,to:self.email},function(){
      return callback(err);
    });

  });
};

User.prototype.setAndSave = function setAndSave(user,data,callback) {
  debug("setAndSave");
  should(typeof(user)).equal('string');
  should(typeof(data)).equal('object');
  should(typeof(callback)).equal('function');
  var self = this;
  delete self.lock;
  var sendWelcomeEmail = false;
  // remove spaces from front and and of email adress
  if (data.email) data.email = data.email.trim();

  // check and react on Mail Change
  if (data.email && data.email.trim()!=="" && data.email !== self.email) {
    if (self.OSMUser !== user && self.hasLoggedIn()) return callback(new Error("EMail address can only be changed by the user himself, after he has logged in."));
    if (data.email !=="resend") {
      if (!emailValidator.validate(data.email)) {
        var error = new Error("Invalid Email Address: "+data.email);
        return callback(error);
      }
      if (data.email !== "") {
        // put email to validation email, and generate a key.
        data.emailInvalidation = data.email;
        data.emailValidationKey = random.generate();
        delete data.email;
      }
    } else {
      // resend case.
      delete data.email;
    }

    sendWelcomeEmail = true;

  }

  // Check Change of OSMUser Name.
  if (data.OSMUser !== self.OSMUser) {
    if (self.hasLoggedIn()) return callback(new Error(">"+self.OSMUser+"< already has logged in, change in name not possible."));
  }
  async.series([
    function checkUserName(cb){
      if (data.OSMUser && data.OSMUser !== self.OSMUser) {
        find({OSMUser:data.OSMUser},function(err,result) {
          if (err) return callback(err);
          if (result && result.length) {
            return cb(new Error("User >" + data.OSMUser + "< already exists."));
          } else return cb();
        });
      }
      else return cb();
    }
  ],function finalFunction(err) {
      if (err) return callback(err);
      async.forEachOf(data,function setAndSaveEachOf(value,key,cb_eachOf){
        // There is no Value for the key, so do nothing
        if (typeof(value)=='undefined') return cb_eachOf();

        // The Value to be set, is the same then in the object itself
        // so do nothing
        if (value === self[key]) return cb_eachOf();
        if (JSON.stringify(value)===JSON.stringify(self[key])) return cb_eachOf();
        if (typeof(self[key])==='undefined' && value === '') return cb_eachOf();

        debug("Set Key %s to value >>%s<<",key,value);
        debug("Old Value Was >>%s<<",self[key]);

        async.series ( [
          function(cb) {
            // do not log validation key in logfile
            var toValue = value;
            // Hide Validation Key not to show to all users
            if (key === "emailValidationKey") return cb();

            messageCenter.global.sendInfo({oid:self.id,user:user,table:"usert",property:key,from:self[key],to:toValue},cb);
          },
          function(cb) {
            self[key] = value;
            cb();
          }
        ],function(err){
          cb_eachOf(err);
        });

      },function setAndSaveFinalCB(err) {
        debug('setAndSaveFinalCB');
        if (err) return callback(err);
        self.save(function (err) {
          // Inform Mail Receiver Module, that there could be a change
          if (err) return callback(err);
          // tell mail receiver to update the information about the users
          mailReceiver.updateUser(self);
          if (sendWelcomeEmail) {
            var m = new mailReceiver.MailReceiver(self);
            // do not wait for mail to go out.
            // mail is logged in outgoing mail list
            m.sendWelcomeMail(user,function (){});
          }
          return callback();
        });
      });
    });
};

User.prototype.hasLoggedIn = function hasLoggedIn() {
  debug('User.prototype.hasLoggedIn');
  if (this.lastAccess) return true;
  return false;
};

User.prototype.getNotificationStatus = function getNotificationStatus(channel, type) {
  debug("User.prototype.getNotificationStatus");
  if (!this.notificationStatus) return null;
  if (!this.notificationStatus[channel]) return null;
  return this.notification[channel][type];
};

User.prototype.getMainLang = function getMainLang() {
  debug("User.prototype.getMainLang");
  if (this.mainLang) return this.mainLang;
  if (this.language) return this.language;
  return "EN";
};

User.prototype.getSecondLang = function getSecondLang() {
  debug("User.prototype.getMainLang");
  if (this.secondLang) return this.secondLang;
  return null;
};

User.prototype.setOption = function setOption(view,option,value) {
  debug('User.prototype.setOption');
  if (!this.option) this.option = {};
  if (!this.option[view]) this.option[view] = {};
  this.option[view][option] = value;
};

var defaultOption = {};

User.prototype.getOption = function getOption(view,option) {
  debug("User.protoype.getOption");
  if (this.option && this.option[view] && this.option[view][option]) return this.option[view][option];
  if (defaultOption && defaultOption[view] && defaultOption[view][option]) return defaultOption[view][option];
  return null;
};
// Creates an User object and stores it to database
// can use a prototype to initialise data
// Parameter: Prototype (optional)
//            callback
// Prototype is not allowed to have an id
module.exports.createNewUser = createNewUser;

// save stores the current object to database
User.prototype.save = pgMap.save; // Create Tables and Views

module.exports.create= create;
module.exports.find = find;
module.exports.findById = findById;
module.exports.findOne = findOne;

User.prototype.getTable = function getTable() {
  return "usert";
};
<commit_msg>Index for usert on OSMUser<commit_after>"use strict";

var pgMap = require('./pgMap.js');
var debug = require('debug')('OSMBC:model:user');
var should = require('should');
var async = require('async');
var messageCenter = require('../notification/messageCenter.js');
var mailReceiver = require('../notification/mailReceiver.js');
var random = require('randomstring');
var emailValidator = require('email-validator');

function User (proto)
{
	debug("User");
  debug("Prototype %s",JSON.stringify(proto));
  this.id = 0;
  for (var k in proto) {
    this[k] = proto[k];
  }
}

function create (proto) {
	debug("create");
	return new User(proto);
}

function createNewUser (proto,callback) {
  debug("createNewUser");
  if (typeof(proto)=='function') {
    callback = proto;
    proto = null;
  }
  if (proto) should.not.exist(proto.id);
  var user = create(proto);
  find({OSMUser:user.OSMUser},function (err,result){
    if (err) return callback(err);
    if (result && result.length>0) {
      return callback(new Error("User >"+user.OSMUser+"< already exists."));
    }
    // set some defaults for the user
    if (!proto) user.mailNewCollection="false";
    if (!proto) user.mailAllComment = "false";
    if (!proto) user.mailComment=[];
    if (!proto) user.mailBlogLanguageStatusChange=[];
    // save data
    user.save(function updateUser(err,result){
      if (err) return callback(err,result);
      mailReceiver.updateUser(result);
      callback(null,result);
    });
  });
}

User.prototype.calculateChanges = function calculateChanges(callback) {
  debug('User.prototype.calculateChanges');
  var self=this;
  if (self._countChanges) return;
  pgMap.count("select count(*) as count from changes where data->>'user'='"+this.OSMUser+"' and data->>'table'='article'",function(err,result){
    if (err) return callback(err);
    self._countChanges = result.count;
    return callback();
  });
};

User.prototype.remove = pgMap.remove;

function find(obj,ord,callback) {
	debug("find");
  pgMap.find({table:"usert",create:create},obj,ord,callback);
}
function findById(id,callback) {
	debug("findById %s",id);
  pgMap.findById(id,{table:"usert",create:create},callback);
}

function findOne(obj1,obj2,callback) {
  debug("findOne");
  pgMap.findOne({table:"usert",create:create},obj1,obj2,callback);
}

var pgObject= {};
pgObject.createString = 'CREATE TABLE usert (  id bigserial NOT NULL,  data json,  \
                  CONSTRAINT user_pkey PRIMARY KEY (id) ) WITH (  OIDS=FALSE);';
pgObject.indexDefinition={
  "user_id_idx":"CREATE INDEX user_id_idx ON usert USING btree (((data ->> 'OSMUser'::text)))"

};
pgObject.viewDefinition = {};
pgObject.table="usert";
module.exports.pg = pgObject;

User.prototype.validateEmail = function validateEmail(user,validationCode,callback) {
  debug('validateEmail');
  should(typeof(user)).eql("object");
  should(typeof(validationCode)).eql("string");
  should(typeof(callback)).eql("function");
  var self = this;
  var err;
  if (self.OSMUser !== user.OSMUser) {
    debug("User is wrong");
    err = new Error("Wrong User: expected >"+self.OSMUser+"< given >"+user.OSMUser+"<");
    return callback(err);
  }
  if (!self.emailInvalidation) {
    debug("nothing in validation");
    err = new Error("No Validation pending for user >"+self.OSMUser+"<");
    return callback(err);
  }
  if (validationCode !== self.emailValidationKey) {
    debug("Validation Code is wrong");
    err = new Error("Wrong Validation Code for EMail for user >"+self.OSMUser+"<");
    messageCenter.global.sendInfo({oid:self.id,user:user.OSMUser,table:"usert",property:"email",from:null,to:"Validation Failed"},function(){
      return callback(err);
    });
    return;
  }
  debug("Email Validation OK saving User");
  var oldmail = self.email;
  self.email = self.emailInvalidation;
  delete self.emailInvalidation;
  delete self.emailValidationKey;
  self.save(function logit(err){
    mailReceiver.updateUser(self);
    if (err) return callback(err);
    messageCenter.global.sendInfo({oid:self.id,user:user.OSMUser,table:"usert",property:"email",from:oldmail,to:self.email},function(){
      return callback(err);
    });

  });
};

User.prototype.setAndSave = function setAndSave(user,data,callback) {
  debug("setAndSave");
  should(typeof(user)).equal('string');
  should(typeof(data)).equal('object');
  should(typeof(callback)).equal('function');
  var self = this;
  delete self.lock;
  var sendWelcomeEmail = false;
  // remove spaces from front and and of email adress
  if (data.email) data.email = data.email.trim();

  // check and react on Mail Change
  if (data.email && data.email.trim()!=="" && data.email !== self.email) {
    if (self.OSMUser !== user && self.hasLoggedIn()) return callback(new Error("EMail address can only be changed by the user himself, after he has logged in."));
    if (data.email !=="resend") {
      if (!emailValidator.validate(data.email)) {
        var error = new Error("Invalid Email Address: "+data.email);
        return callback(error);
      }
      if (data.email !== "") {
        // put email to validation email, and generate a key.
        data.emailInvalidation = data.email;
        data.emailValidationKey = random.generate();
        delete data.email;
      }
    } else {
      // resend case.
      delete data.email;
    }

    sendWelcomeEmail = true;

  }

  // Check Change of OSMUser Name.
  if (data.OSMUser !== self.OSMUser) {
    if (self.hasLoggedIn()) return callback(new Error(">"+self.OSMUser+"< already has logged in, change in name not possible."));
  }
  async.series([
    function checkUserName(cb){
      if (data.OSMUser && data.OSMUser !== self.OSMUser) {
        find({OSMUser:data.OSMUser},function(err,result) {
          if (err) return callback(err);
          if (result && result.length) {
            return cb(new Error("User >" + data.OSMUser + "< already exists."));
          } else return cb();
        });
      }
      else return cb();
    }
  ],function finalFunction(err) {
      if (err) return callback(err);
      async.forEachOf(data,function setAndSaveEachOf(value,key,cb_eachOf){
        // There is no Value for the key, so do nothing
        if (typeof(value)=='undefined') return cb_eachOf();

        // The Value to be set, is the same then in the object itself
        // so do nothing
        if (value === self[key]) return cb_eachOf();
        if (JSON.stringify(value)===JSON.stringify(self[key])) return cb_eachOf();
        if (typeof(self[key])==='undefined' && value === '') return cb_eachOf();

        debug("Set Key %s to value >>%s<<",key,value);
        debug("Old Value Was >>%s<<",self[key]);

        async.series ( [
          function(cb) {
            // do not log validation key in logfile
            var toValue = value;
            // Hide Validation Key not to show to all users
            if (key === "emailValidationKey") return cb();

            messageCenter.global.sendInfo({oid:self.id,user:user,table:"usert",property:key,from:self[key],to:toValue},cb);
          },
          function(cb) {
            self[key] = value;
            cb();
          }
        ],function(err){
          cb_eachOf(err);
        });

      },function setAndSaveFinalCB(err) {
        debug('setAndSaveFinalCB');
        if (err) return callback(err);
        self.save(function (err) {
          // Inform Mail Receiver Module, that there could be a change
          if (err) return callback(err);
          // tell mail receiver to update the information about the users
          mailReceiver.updateUser(self);
          if (sendWelcomeEmail) {
            var m = new mailReceiver.MailReceiver(self);
            // do not wait for mail to go out.
            // mail is logged in outgoing mail list
            m.sendWelcomeMail(user,function (){});
          }
          return callback();
        });
      });
    });
};

User.prototype.hasLoggedIn = function hasLoggedIn() {
  debug('User.prototype.hasLoggedIn');
  if (this.lastAccess) return true;
  return false;
};

User.prototype.getNotificationStatus = function getNotificationStatus(channel, type) {
  debug("User.prototype.getNotificationStatus");
  if (!this.notificationStatus) return null;
  if (!this.notificationStatus[channel]) return null;
  return this.notification[channel][type];
};

User.prototype.getMainLang = function getMainLang() {
  debug("User.prototype.getMainLang");
  if (this.mainLang) return this.mainLang;
  if (this.language) return this.language;
  return "EN";
};

User.prototype.getSecondLang = function getSecondLang() {
  debug("User.prototype.getMainLang");
  if (this.secondLang) return this.secondLang;
  return null;
};

User.prototype.setOption = function setOption(view,option,value) {
  debug('User.prototype.setOption');
  if (!this.option) this.option = {};
  if (!this.option[view]) this.option[view] = {};
  this.option[view][option] = value;
};

var defaultOption = {};

User.prototype.getOption = function getOption(view,option) {
  debug("User.protoype.getOption");
  if (this.option && this.option[view] && this.option[view][option]) return this.option[view][option];
  if (defaultOption && defaultOption[view] && defaultOption[view][option]) return defaultOption[view][option];
  return null;
};
// Creates an User object and stores it to database
// can use a prototype to initialise data
// Parameter: Prototype (optional)
//            callback
// Prototype is not allowed to have an id
module.exports.createNewUser = createNewUser;

// save stores the current object to database
User.prototype.save = pgMap.save; // Create Tables and Views

module.exports.create= create;
module.exports.find = find;
module.exports.findById = findById;
module.exports.findOne = findOne;

User.prototype.getTable = function getTable() {
  return "usert";
};
<|endoftext|>


<commit_before>"use strict";

module.exports = function(sequelize, DataTypes) {
    var ref = sequelize.define('ref', {
        doi: {
            type: DataTypes.STRING,
            defaultValue: "NA",
            comment: "DOI of the attached publication"
        },
        author: {
            type: DataTypes.STRING,
            allowNull: false,
            comment: "firt author name"
        },
        year: {
            type: DataTypes.STRING,
            comment: "year of publication",
            defaultValue: "NA"
        },
        jstor: {
          type: DataTypes.STRING,
          defaultValue: "NA",
          comment: "JSTOR of the attached publication"
        },
        pmid: {
            type: DataTypes.STRING,
            defaultValue: "NA",
            comment: "PMID of the attached publication"
        },
        bibtex: {
            type: DataTypes.TEXT,
            defaultValue: "NA",
            comment: "BibTex of the attached publication"
        },
        paper_url: {
            type: DataTypes.TEXT,
            allowNull: true,
            isUrl: true,
            comment: "URL of the attached publication"
        },
        data_url: {
            type: DataTypes.TEXT,
            isUrl: true,
            allowNull: false,
            comment: "URL of the attached data"
        }
    }, {
        underscored: true,
        freezeTableName: true,
        classMethods: {
            associate: function(models) {
                ref.hasMany(models.dataset, {
                    onDelete: 'cascade'
                })
            }
        }
    });

    return ref
};
<commit_msg>Removing default value as NA to NULL<commit_after>"use strict";

module.exports = function(sequelize, DataTypes) {
    var ref = sequelize.define('ref', {
        doi: {
            type: DataTypes.STRING,
            comment: "DOI of the attached publication"
        },
        author: {
            type: DataTypes.STRING,
            allowNull: false,
            comment: "firt author name"
        },
        year: {
            type: DataTypes.STRING,
            comment: "year of publication"
        },
        jstor: {
          type: DataTypes.STRING,
          comment: "JSTOR of the attached publication"
        },
        pmid: {
            type: DataTypes.STRING,
            comment: "PMID of the attached publication"
        },
        bibtex: {
            type: DataTypes.TEXT,
            comment: "BibTex of the attached publication"
        },
        paper_url: {
            type: DataTypes.TEXT,
            allowNull: true,
            isUrl: true,
            comment: "URL of the attached publication"
        },
        data_url: {
            type: DataTypes.TEXT,
            isUrl: true,
            allowNull: false,
            comment: "URL of the attached data"
        }
    }, {
        underscored: true,
        freezeTableName: true,
        classMethods: {
            associate: function(models) {
                ref.hasMany(models.dataset, {
                    onDelete: 'cascade'
                })
            }
        }
    });

    return ref
};
<|endoftext|>


<commit_before>var Sequelize = require('sequelize');

module.exports = function(sequelize, DataTypes) {
	var Rom = sequelize.define('Rom', {
			timestamp: {
				type: Sequelize.DATE,
				notNull: true,
			},
			md5sum: {
				type: Sequelize.STRING(32),
				notEmpty: true,
			},
			filename: {
				type: Sequelize.STRING(255),
				notEmpty: true,
			},
			updateChannel: {
				type: Sequelize.ENUM,
				values: [ 'stable', 'snapshot', 'RC', 'nightly' ],
				notNull: true,
			},
			changelog: {
				type: Sequelize.TEXT,
				notEmpty: false,
			},
			apiLevel: {
				type: Sequelize.INTEGER.UNSIGNED,
				notNull: true,
				min: 1,
			},
			subdirectory: {
				type: Sequelize.STRING(255),
				notEmpty: false,
			},
			isActive: {
				type: Sequelize.BOOLEAN,
				notNull: true,
			},
			sourceCodeTimestamp: {
				type: Sequelize.DATE,
				isNull: true,
			},
	}, {
		validate: {
			deviceIdValid : function() {
				if (!this.DeviceId || this.DeviceId < 0) {
					throw new Error('DeviceId is a mandatory field!');
				}
			},
			uniqueEnabledFilenamePerSubdirectory : function() {
				var filterParameters = {
					id: {
						ne: this.id,
					},
					DeviceId: this.DeviceId,
					filename: this.filename,
					subdirectory: this.subdirectory,
					isActive: true
				};

				// Find all existing active roms for this filename in the given subdirectory.
				Rom.count({
					where: filterParameters,
				}).success(function(totalExisting) {
					if (totalExisting > 0) {
						throw new Error('There are already ' + totalExisting + ' existing ROM matching ' + JSON.stringify(filterParameters));
					}
				});
			},
		}
	});

	return Rom;
}
<commit_msg>Make "experimental" a valid updateChannel.<commit_after>var Sequelize = require('sequelize');

module.exports = function(sequelize, DataTypes) {
	var Rom = sequelize.define('Rom', {
			timestamp: {
				type: Sequelize.DATE,
				notNull: true,
			},
			md5sum: {
				type: Sequelize.STRING(32),
				notEmpty: true,
			},
			filename: {
				type: Sequelize.STRING(255),
				notEmpty: true,
			},
			updateChannel: {
				type: Sequelize.ENUM,
				values: [ 'stable', 'snapshot', 'RC', 'nightly', 'experimental' ],
				notNull: true,
			},
			changelog: {
				type: Sequelize.TEXT,
				notEmpty: false,
			},
			apiLevel: {
				type: Sequelize.INTEGER.UNSIGNED,
				notNull: true,
				min: 1,
			},
			subdirectory: {
				type: Sequelize.STRING(255),
				notEmpty: false,
			},
			isActive: {
				type: Sequelize.BOOLEAN,
				notNull: true,
			},
			sourceCodeTimestamp: {
				type: Sequelize.DATE,
				isNull: true,
			},
	}, {
		validate: {
			deviceIdValid : function() {
				if (!this.DeviceId || this.DeviceId < 0) {
					throw new Error('DeviceId is a mandatory field!');
				}
			},
			uniqueEnabledFilenamePerSubdirectory : function() {
				var filterParameters = {
					id: {
						ne: this.id,
					},
					DeviceId: this.DeviceId,
					filename: this.filename,
					subdirectory: this.subdirectory,
					isActive: true
				};

				// Find all existing active roms for this filename in the given subdirectory.
				Rom.count({
					where: filterParameters,
				}).success(function(totalExisting) {
					if (totalExisting > 0) {
						throw new Error('There are already ' + totalExisting + ' existing ROM matching ' + JSON.stringify(filterParameters));
					}
				});
			},
		}
	});

	return Rom;
}
<|endoftext|>


<commit_before>var Tuple = require('../lib/tuple')

var gametree = require('../module/gametree')
var fs = require('fs')

var alpha = 'abcdefghijklmnopqrstuvwxyz'

exports.meta = {
    name: 'Smart Game Format',
    extensions: ['sgf']
}

exports.tokenize = function(input) {
    var tokens = []
    var rules = {
        ignore: /^\s+/,
        parenthesis: /^(\(|\))/,
        semicolon: /^;/,
        prop_ident: /^[A-Za-z]+/,
        c_value_type: /^\[(\]|[^]*?[^\\]\])/
    }

    while (input.length > 0) {
        var token = null
        var length = 0

        for (var type in rules) {
            var matches = rules[type].exec(input)
            if (!matches) continue

            var value = matches[0]
            length = value.length

            if (type == 'c_value_type')
                value = exports.unescapeString(value.substr(1, length - 2))
            token = new Tuple(type, value)

            break
        }

        if (token && token[0] != 'ignore') tokens.push(token)
        input = input.substr(length)
    }

    return tokens
}

exports.parse = function(tokens, callback, start, depth) {
    if (!callback) callback = function(progress) {}
    if (!start) start = [0]
    if (isNaN(depth)) depth = 0

    var i = start[0]
    var node, property, tree = gametree.new()
    tree.collapsed = tokens.length >= setting.get('graph.collapse_tokens_count')
        && depth >= setting.get('graph.collapse_min_depth')

    while (i < tokens.length) {
        if (new Tuple('parenthesis', '(').equals(tokens[i])) break
        if (new Tuple('parenthesis', ')').equals(tokens[i])) return tree

        tokens[i].unpack(function(type, value) {
            if (type == 'semicolon') {
                node = {}
                tree.nodes.push(node)
            } else if (type == 'prop_ident') {
                node[value] = []
                property = node[value]
            } else if (type == 'c_value_type') {
                property.push(value)
            }
        })

        start[0] = ++i
    }

    while (i < tokens.length) {
        if (new Tuple('parenthesis', '(').equals(tokens[i])) {
            var newdepth = depth + Math.min(tree.subtrees.length, 1)
            start[0] = i + 1

            t = exports.parse(tokens, callback, start, newdepth)
            t.parent = tree
            tree.subtrees.push(t)
            tree.current = 0

            i = start[0]
        } else if (new Tuple('parenthesis', ')').equals(tokens[i])) {
            start[0] = i
            callback(i / tokens.length)
            break
        }

        i++
    }

    return tree
}

exports.parseFile = function(filename, callback) {
    var input = fs.readFileSync(filename, { encoding: 'utf8' })
    var tokens = exports.tokenize(input)

    return exports.parse(tokens, callback)
}

exports.point2vertex = function(point) {
    if (point.length != 2) return new Tuple(-1, -1)

    point = point.toLowerCase()
    return new Tuple(alpha.indexOf(point[0]), alpha.indexOf(point[1]))
}

exports.vertex2point = function(tuple) {
    return tuple.unpack(function(x, y) {
        if (x < 0 || y < 0) return ''
        return alpha[x] + alpha[y]
    })
}

exports.compressed2list = function(compressed) {
    var colon = compressed.indexOf(':')
    if (colon < 0) return [exports.point2vertex(compressed)]

    var v1 = exports.point2vertex(compressed.slice(0, colon))
    var v2 = exports.point2vertex(compressed.slice(colon + 1))
    var list = []

    for (var i = v1[0]; i <= v2[0]; i++) {
        for (var j = v1[1]; j <= v2[1]; j++) {
            list.push(new Tuple(i, j))
        }
    }

    return list
}

exports.addBoard = function(tree, index, baseboard) {
    if (isNaN(index)) index = 0
    if (index >= tree.nodes.length) return tree

    var node = tree.nodes[index]
    var vertex = null
    var board = null

    if (!baseboard) {
        var prev = gametree.navigate(tree, index, -1)

        if (!prev[0]) {
            var size = 'SZ' in node ? node.SZ[0].toInt() : 19
            baseboard = new Board(size)
        } else {
            var prevNode = prev[0].nodes[prev[1]]

            if (!prevNode.board) exports.addBoard(prev[0], prev[1])
            baseboard = prevNode.board
        }
    }

    if ('B' in node) {
        vertex = exports.point2vertex(node.B[0])
        board = baseboard.makeMove(1, vertex)
    } else if ('W' in node) {
        vertex = exports.point2vertex(node.W[0])
        board = baseboard.makeMove(-1, vertex)
    } else {
        board = baseboard.makeMove(0)
    }

    var ids = ['AW', 'AE', 'AB']

    for (var i = 0; i < ids.length; i++) {
        if (!(ids[i] in node)) continue

        node[ids[i]].each(function(value) {
            if (value.indexOf(':') < 0) {
                // Single point
                board.arrangement[exports.point2vertex(value)] = i - 1
            } else {
                // Compressed point list
                exports.compressed2list(value).each(function(vertex) {
                    board.arrangement[vertex] = i - 1
                })
            }
        })
    }

    if (vertex != null) {
        board.overlays[vertex] = new Tuple('point', 0, '')
    }

    var ids = ['CR', 'MA', 'SQ', 'TR']
    var classes = ['circle', 'cross', 'square', 'triangle']

    for (var i = 0; i < ids.length; i++) {
        if (!(ids[i] in node)) continue

        node[ids[i]].each(function(value) {
            if (value.indexOf(':') < 0) {
                // Single point
                board.overlays[exports.point2vertex(value)] = new Tuple(classes[i], 0, '')
            } else {
                // Compressed point list
                exports.compressed2list(value).each(function(vertex) {
                    board.overlays[vertex] = new Tuple(classes[i], 0, '')
                })
            }
        })
    }

    if ('LB' in node) {
        node.LB.each(function(composed) {
            var sep = composed.indexOf(':')
            var point = composed.slice(0, sep)
            var label = composed.slice(sep + 1).replace(/\s+/, ' ')
            board.overlays[exports.point2vertex(point)] = new Tuple('label', 0, label)
        })
    }

    node.board = board

    if (index == tree.nodes.length - 1 && tree.subtrees.length > 0) {
        // Add variations

        tree.subtrees.each(function(subtree) {
            if (subtree.nodes.length == 0) return

            var v, sign

            if ('B' in subtree.nodes[0]) {
                v = sgf.point2vertex(subtree.nodes[0].B[0])
                sign = 1
            } else if ('W' in subtree.nodes[0]) {
                v = sgf.point2vertex(subtree.nodes[0].W[0])
                sign = -1
            } else {
                return
            }

            if (v in board.overlays)
                board.overlays[v] = board.overlays[v].unpack(function(a, b, c) {
                     return new Tuple(a, sign, c)
                })
            else board.overlays[v] = new Tuple('', sign, '')
        })
    }

    return tree
}

exports.tree2string = function(tree) {
    var output = ''

    tree.nodes.each(function(node) {
        output += ';'

        for (var id in node) {
            if (id.toUpperCase() != id) continue
            output += id

            node[id].each(function(value) {
                output += '[' + exports.escapeString(value.toString()) + ']'
            })
        }

        output += '\n'
    })

    if (tree.current != null)
        output += '(' + exports.tree2string(tree.subtrees[tree.current]) + ')'

    for (var i = 0; i < tree.subtrees.length; i++) {
        if (i == tree.current) continue
        output += '(' + exports.tree2string(tree.subtrees[i]) + ')'
    }

    return output
}

exports.escapeString = function(input) {
    return input.replace(/\\/g, '\\\\').replace(/\]/g, '\\]')
}

exports.unescapeString = function(input) {
    return input.replace(/\\(\r\n|\n\r|\n|\r)/g, '').replace(/\\(.)/g, function(m, p) { return p })
}
<commit_msg>Ensure there is no infinite loop<commit_after>var Tuple = require('../lib/tuple')

var gametree = require('../module/gametree')
var fs = require('fs')

var alpha = 'abcdefghijklmnopqrstuvwxyz'

exports.meta = {
    name: 'Smart Game Format',
    extensions: ['sgf']
}

exports.tokenize = function(input) {
    var tokens = []
    var rules = {
        ignore: /^\s+/,
        parenthesis: /^(\(|\))/,
        semicolon: /^;/,
        prop_ident: /^[A-Za-z]+/,
        c_value_type: /^\[(\]|[^]*?[^\\]\])/
    }

    while (input.length > 0) {
        var token = null
        var length = 1

        for (var type in rules) {
            var matches = rules[type].exec(input)
            if (!matches) continue

            var value = matches[0]
            length = value.length

            if (type == 'c_value_type')
                value = exports.unescapeString(value.substr(1, length - 2))
            token = new Tuple(type, value)

            break
        }

        if (token && token[0] != 'ignore') tokens.push(token)
        input = input.substr(length)
    }

    return tokens
}

exports.parse = function(tokens, callback, start, depth) {
    if (!callback) callback = function(progress) {}
    if (!start) start = [0]
    if (isNaN(depth)) depth = 0

    var i = start[0]
    var node, property, tree = gametree.new()
    tree.collapsed = tokens.length >= setting.get('graph.collapse_tokens_count')
        && depth >= setting.get('graph.collapse_min_depth')

    while (i < tokens.length) {
        if (new Tuple('parenthesis', '(').equals(tokens[i])) break
        if (new Tuple('parenthesis', ')').equals(tokens[i])) return tree

        tokens[i].unpack(function(type, value) {
            if (type == 'semicolon') {
                node = {}
                tree.nodes.push(node)
            } else if (type == 'prop_ident') {
                node[value] = []
                property = node[value]
            } else if (type == 'c_value_type') {
                property.push(value)
            }
        })

        start[0] = ++i
    }

    while (i < tokens.length) {
        if (new Tuple('parenthesis', '(').equals(tokens[i])) {
            var newdepth = depth + Math.min(tree.subtrees.length, 1)
            start[0] = i + 1

            t = exports.parse(tokens, callback, start, newdepth)
            t.parent = tree
            tree.subtrees.push(t)
            tree.current = 0

            i = start[0]
        } else if (new Tuple('parenthesis', ')').equals(tokens[i])) {
            start[0] = i
            callback(i / tokens.length)
            break
        }

        i++
    }

    return tree
}

exports.parseFile = function(filename, callback) {
    var input = fs.readFileSync(filename, { encoding: 'utf8' })
    var tokens = exports.tokenize(input)

    return exports.parse(tokens, callback)
}

exports.point2vertex = function(point) {
    if (point.length != 2) return new Tuple(-1, -1)

    point = point.toLowerCase()
    return new Tuple(alpha.indexOf(point[0]), alpha.indexOf(point[1]))
}

exports.vertex2point = function(tuple) {
    return tuple.unpack(function(x, y) {
        if (x < 0 || y < 0) return ''
        return alpha[x] + alpha[y]
    })
}

exports.compressed2list = function(compressed) {
    var colon = compressed.indexOf(':')
    if (colon < 0) return [exports.point2vertex(compressed)]

    var v1 = exports.point2vertex(compressed.slice(0, colon))
    var v2 = exports.point2vertex(compressed.slice(colon + 1))
    var list = []

    for (var i = v1[0]; i <= v2[0]; i++) {
        for (var j = v1[1]; j <= v2[1]; j++) {
            list.push(new Tuple(i, j))
        }
    }

    return list
}

exports.addBoard = function(tree, index, baseboard) {
    if (isNaN(index)) index = 0
    if (index >= tree.nodes.length) return tree

    var node = tree.nodes[index]
    var vertex = null
    var board = null

    if (!baseboard) {
        var prev = gametree.navigate(tree, index, -1)

        if (!prev[0]) {
            var size = 'SZ' in node ? node.SZ[0].toInt() : 19
            baseboard = new Board(size)
        } else {
            var prevNode = prev[0].nodes[prev[1]]

            if (!prevNode.board) exports.addBoard(prev[0], prev[1])
            baseboard = prevNode.board
        }
    }

    if ('B' in node) {
        vertex = exports.point2vertex(node.B[0])
        board = baseboard.makeMove(1, vertex)
    } else if ('W' in node) {
        vertex = exports.point2vertex(node.W[0])
        board = baseboard.makeMove(-1, vertex)
    } else {
        board = baseboard.makeMove(0)
    }

    var ids = ['AW', 'AE', 'AB']

    for (var i = 0; i < ids.length; i++) {
        if (!(ids[i] in node)) continue

        node[ids[i]].each(function(value) {
            if (value.indexOf(':') < 0) {
                // Single point
                board.arrangement[exports.point2vertex(value)] = i - 1
            } else {
                // Compressed point list
                exports.compressed2list(value).each(function(vertex) {
                    board.arrangement[vertex] = i - 1
                })
            }
        })
    }

    if (vertex != null) {
        board.overlays[vertex] = new Tuple('point', 0, '')
    }

    var ids = ['CR', 'MA', 'SQ', 'TR']
    var classes = ['circle', 'cross', 'square', 'triangle']

    for (var i = 0; i < ids.length; i++) {
        if (!(ids[i] in node)) continue

        node[ids[i]].each(function(value) {
            if (value.indexOf(':') < 0) {
                // Single point
                board.overlays[exports.point2vertex(value)] = new Tuple(classes[i], 0, '')
            } else {
                // Compressed point list
                exports.compressed2list(value).each(function(vertex) {
                    board.overlays[vertex] = new Tuple(classes[i], 0, '')
                })
            }
        })
    }

    if ('LB' in node) {
        node.LB.each(function(composed) {
            var sep = composed.indexOf(':')
            var point = composed.slice(0, sep)
            var label = composed.slice(sep + 1).replace(/\s+/, ' ')
            board.overlays[exports.point2vertex(point)] = new Tuple('label', 0, label)
        })
    }

    node.board = board

    if (index == tree.nodes.length - 1 && tree.subtrees.length > 0) {
        // Add variations

        tree.subtrees.each(function(subtree) {
            if (subtree.nodes.length == 0) return

            var v, sign

            if ('B' in subtree.nodes[0]) {
                v = sgf.point2vertex(subtree.nodes[0].B[0])
                sign = 1
            } else if ('W' in subtree.nodes[0]) {
                v = sgf.point2vertex(subtree.nodes[0].W[0])
                sign = -1
            } else {
                return
            }

            if (v in board.overlays)
                board.overlays[v] = board.overlays[v].unpack(function(a, b, c) {
                     return new Tuple(a, sign, c)
                })
            else board.overlays[v] = new Tuple('', sign, '')
        })
    }

    return tree
}

exports.tree2string = function(tree) {
    var output = ''

    tree.nodes.each(function(node) {
        output += ';'

        for (var id in node) {
            if (id.toUpperCase() != id) continue
            output += id

            node[id].each(function(value) {
                output += '[' + exports.escapeString(value.toString()) + ']'
            })
        }

        output += '\n'
    })

    if (tree.current != null)
        output += '(' + exports.tree2string(tree.subtrees[tree.current]) + ')'

    for (var i = 0; i < tree.subtrees.length; i++) {
        if (i == tree.current) continue
        output += '(' + exports.tree2string(tree.subtrees[i]) + ')'
    }

    return output
}

exports.escapeString = function(input) {
    return input.replace(/\\/g, '\\\\').replace(/\]/g, '\\]')
}

exports.unescapeString = function(input) {
    return input.replace(/\\(\r\n|\n\r|\n|\r)/g, '').replace(/\\(.)/g, function(m, p) { return p })
}
<|endoftext|>


<commit_before>/**
 * XadillaX created at 2016-10-18 17:25:18 With ♥
 *
 * Copyright (c) 2016 Souche.com, all rights
 * reserved.
 */
"use strict";

const hack = require("./util/hack");
const Query = require("../lib/query");
const Toshihiko = require("../lib/toshihiko");

describe("query promise", function() {
    const toshihiko = new Toshihiko("base");
    const model = toshihiko.define("model", [ { name: "key1", primaryKey: true } ]);

    describe("find", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "find", [ undefined, [ "ok" ], "again" ]);
            return query.find(true).should.eventually.deepEqual([{}]);
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "find");
            return query.find().should.be.rejectedWith("find predefinition 1");
        });
    });

    describe("update", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "updateByQuery", [ undefined, {}, "again" ]);
            return query.update({ key1: 1 }).should.eventually.deepEqual({});
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "updateByQuery");
            return query.update({ key1: 1 }).should.be.rejectedWith("updateByQuery predefinition 1");
        });
    });

    describe("delete", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "deleteByQuery", [ undefined, "ok", "again" ]);
            return query.delete().should.eventually.equal("ok");
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "deleteByQuery");
            return query.delete().should.be.rejectedWith("deleteByQuery predefinition 1");
        });
    });

    describe("execute", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "execute", [ undefined, "eeeee", "again" ]);
            return query.execute().should.eventually.equal("eeeee");
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "execute");
            return query.execute(function(err) {
                err.message.should.equal("execute predefinition 1");
            }).should.be.rejectedWith("execute predefinition 1");
        });
    });
});
<commit_msg>test: add test cases for query count<commit_after>/**
 * XadillaX created at 2016-10-18 17:25:18 With ♥
 *
 * Copyright (c) 2016 Souche.com, all rights
 * reserved.
 */
"use strict";

const hack = require("./util/hack");
const Query = require("../lib/query");
const Toshihiko = require("../lib/toshihiko");

describe("query promise", function() {
    const toshihiko = new Toshihiko("base");
    const model = toshihiko.define("model", [ { name: "key1", primaryKey: true } ]);

    describe("count", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "count", [ undefined, 10, "again" ]);
            return query.count().should.eventually.deepEqual(10);
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "count");
            return query.count().should.be.rejectedWith("count predefinition 1");
        });
    });

    describe("find", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "find", [ undefined, [ "ok" ], "again" ]);
            return query.find(true).should.eventually.deepEqual([{}]);
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "find");
            return query.find().should.be.rejectedWith("find predefinition 1");
        });
    });

    describe("update", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "updateByQuery", [ undefined, {}, "again" ]);
            return query.update({ key1: 1 }).should.eventually.deepEqual({});
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "updateByQuery");
            return query.update({ key1: 1 }).should.be.rejectedWith("updateByQuery predefinition 1");
        });
    });

    describe("delete", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "deleteByQuery", [ undefined, "ok", "again" ]);
            return query.delete().should.eventually.equal("ok");
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "deleteByQuery");
            return query.delete().should.be.rejectedWith("deleteByQuery predefinition 1");
        });
    });

    describe("execute", function() {
        it("should resolve", function() {
            const query = new Query(model);
            hack.hackAsyncReturn(query.adapter, "execute", [ undefined, "eeeee", "again" ]);
            return query.execute().should.eventually.equal("eeeee");
        });

        it("should reject", function() {
            const query = new Query(model);
            hack.hackAsyncErr(query.adapter, "execute");
            return query.execute(function(err) {
                err.message.should.equal("execute predefinition 1");
            }).should.be.rejectedWith("execute predefinition 1");
        });
    });
});
<|endoftext|>


<commit_before>var nodes = null;
var edges = null;
var network = null;

var url = null;
var token = null;
var roomId = null;
var streaming = null;
var stepSize = 5;
var collapseEvents = true;

var streamFrom = null;
var scrollbackFrom = null;

function init() {
    initGraph();

    $("#connectButton").on("click", function() {
        url = $("#inputUrl").val();
        token = $("#inputToken").val();
        roomId = $("#inputRoomId").val();
        streaming = $("#inputStreaming").is(":checked");
        stepSize = $("#stepSize").val();
        collapseEvents = $("#collapseEvents").is(":checked");
        initialSync();
    });

    $("#scrollbackButton").on("click", function() {
        scrollback();
    });
};

function initialSync() {
    var endpoint = url + "/_matrix/client/api/v1/initialSync?access_token=$token&limit=$stepSize&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$stepSize", stepSize);
    $.getJSON(endpoint, function(data) {
        streamFrom = data.end;
        for (var i=0; i<data.rooms.length; ++i) {
            var room = data.rooms[i];
            if (room.room_id === roomId) {
                // add new events to graph
                console.log("Adding "+room.messages.chunk.length+" new events to the graph");
                for (var j=0; j<room.messages.chunk.length; ++j) {
                    addEvent(room.messages.chunk[j]);
                }
                scrollbackFrom = room.messages.start;
                break;
            }
        }
        if (collapseEvents) {
            collapseNodes();
        }
        if (streaming) {
            console.log("Starting event stream");
            longpollEventStream();
        }
    }).fail(function(err) {
        console.error("Failed to do initial sync: "+JSON.stringify(err));
    });
};

function longpollEventStream() {
    var endpoint = url + "/_matrix/client/api/v1/events?access_token=$token&from=$from&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$from", streamFrom);
    $.getJSON(endpoint, function(data) {
        streamFrom = data.end;
        if (data.chunk.length > 0) {
            console.log("Got "+data.chunk.length+" new events.");
        }
        for (var i=0; i<data.chunk.length; ++i) {
            if (data.chunk[i].room_id === roomId) {
                // add new event to graph
                console.log("Adding new event to graph");
                addEvent(data.chunk[i]);
            }
        }
        longpollEventStream();
    }).fail(function(err) {
        setTimeout(longpollEventStream, 5000);
    });
};

function initGraph() {
    nodes = new vis.DataSet();
    edges = new vis.DataSet();

    // create a network
    var container = document.getElementById('eventGraph');
    var data = {
        nodes: nodes,
        edges: edges
    };
    var options = {
        stabilize: false,
        nodes: {
            shape: "box"
        }
    };
    network = new vis.Network(container, data, options);
    // add event listeners
    network.on('select', function(params) {
        var text = "";
        if (params.nodes.length === 1) {
            text = JSON.stringify(nodes.get(params.nodes[0]).blob, undefined, 2);
        }

        document.getElementById('eventInfo').innerHTML = text;
    });
    network.on("resize", function(params) {
        console.log(params.width,params.height);
    });
};

function collapseNodes() {
    console.log("Collapsing nodes...");
};

function addEvent(event) {
    try {
        // extract the origin event domain for colouring based on group... bit cheeky.
        var segments = event.event_id.split(":");
        var domain = segments[segments.length-1];
        nodes.add({
            id: event.event_id,
            label: event.event_id,
            group: domain,
            blob: event
        });
        // Add edges from prev_events (NB: requires hack on synapse currently)
        // on events/utils.py:126 to not del d["prev_events"]
        for (var i=0; i<event.prev_events.length; ++i) {
            var prev_event_id = event.prev_events[i][0];
            edges.add({
                from: prev_event_id,
                to: event.event_id,
                style: "arrow"
            });
        }
    }
    catch (err) {
        console.error("Failed to addEvent: "+err);
    }
};

function scrollback() {
    var endpoint = url + "/_matrix/client/api/v1/rooms/$roomid/messages?access_token=$token&from=$from&dir=b&limit=$stepSize&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$from", scrollbackFrom);
    endpoint = endpoint.replace("$roomid", roomId);
    endpoint = endpoint.replace("$stepSize", stepSize);
    $.getJSON(endpoint, function(data) {
        scrollbackFrom = data.end;
        if (data.chunk.length > 0) {
            console.log("Got "+data.chunk.length+" old scrollback events.");
        }
        for (var i=0; i<data.chunk.length; ++i) {
            if (data.chunk[i].room_id === roomId) {
                // add new event to graph
                console.log("Adding new event to graph");
                addEvent(data.chunk[i]);
            }
        }
        if (collapseEvents) {
            collapseNodes();
        }
    }).fail(function(err) {
        console.error("Failed to perform scrollback: "+JSON.stringify(err));
    });
};
<commit_msg>Add notes on vis.js format for nodes<commit_after>var nodes = null;
var edges = null;
var network = null;
var hiddenNodes = {};

var url = null;
var token = null;
var roomId = null;
var streaming = null;
var stepSize = 5;
var collapseEvents = true;

var streamFrom = null;
var scrollbackFrom = null;

function init() {
    initGraph();

    $("#connectButton").on("click", function() {
        url = $("#inputUrl").val();
        token = $("#inputToken").val();
        roomId = $("#inputRoomId").val();
        streaming = $("#inputStreaming").is(":checked");
        stepSize = $("#stepSize").val();
        collapseEvents = $("#collapseEvents").is(":checked");
        initialSync();
    });

    $("#scrollbackButton").on("click", function() {
        scrollback();
    });
};

function initialSync() {
    var endpoint = url + "/_matrix/client/api/v1/initialSync?access_token=$token&limit=$stepSize&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$stepSize", stepSize);
    $.getJSON(endpoint, function(data) {
        streamFrom = data.end;
        for (var i=0; i<data.rooms.length; ++i) {
            var room = data.rooms[i];
            if (room.room_id === roomId) {
                // add new events to graph
                console.log("Adding "+room.messages.chunk.length+" new events to the graph");
                for (var j=0; j<room.messages.chunk.length; ++j) {
                    addEvent(room.messages.chunk[j]);
                }
                scrollbackFrom = room.messages.start;
                break;
            }
        }
        if (collapseEvents) {
            collapseNodes();
        }
        if (streaming) {
            console.log("Starting event stream");
            longpollEventStream();
        }
    }).fail(function(err) {
        console.error("Failed to do initial sync: "+JSON.stringify(err));
    });
};

function longpollEventStream() {
    var endpoint = url + "/_matrix/client/api/v1/events?access_token=$token&from=$from&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$from", streamFrom);
    $.getJSON(endpoint, function(data) {
        streamFrom = data.end;
        if (data.chunk.length > 0) {
            console.log("Got "+data.chunk.length+" new events.");
        }
        for (var i=0; i<data.chunk.length; ++i) {
            if (data.chunk[i].room_id === roomId) {
                // add new event to graph
                console.log("Adding new event to graph");
                addEvent(data.chunk[i]);
            }
        }
        longpollEventStream();
    }).fail(function(err) {
        setTimeout(longpollEventStream, 5000);
    });
};

function initGraph() {
    nodes = new vis.DataSet();
    edges = new vis.DataSet();

    // create a network
    var container = document.getElementById('eventGraph');
    var data = {
        nodes: nodes,
        edges: edges
    };
    var options = {
        stabilize: false,
        nodes: {
            shape: "box"
        }
    };
    network = new vis.Network(container, data, options);
    // add event listeners
    network.on('select', function(params) {
        var text = "";
        if (params.nodes.length === 1) {
            text = JSON.stringify(nodes.get(params.nodes[0]).blob, undefined, 2);
        }

        document.getElementById('eventInfo').innerHTML = text;
    });
    network.on("resize", function(params) {
        console.log(params.width,params.height);
    });
};

function collapseNodes() {
    console.log("Collapsing nodes...");
    // network.nodes => { "eventid": Node, ... }
    // Node.edges => [Edge, Edge] (for the middle node in NODE--->NODE--->NODE)
    // Edge => from=Node, to=Node, fromId=event_id, toId=event_id
};

function addEvent(event) {
    try {
        // extract the origin event domain for colouring based on group... bit cheeky.
        var segments = event.event_id.split(":");
        var domain = segments[segments.length-1];
        nodes.add({
            id: event.event_id,
            label: event.event_id,
            group: domain,
            blob: event
        });
        // Add edges from prev_events (NB: requires hack on synapse currently)
        // on events/utils.py:126 to not del d["prev_events"]
        for (var i=0; i<event.prev_events.length; ++i) {
            var prev_event_id = event.prev_events[i][0];
            edges.add({
                from: prev_event_id,
                to: event.event_id,
                style: "arrow"
            });
        }
    }
    catch (err) {
        console.error("Failed to addEvent: "+err);
    }
};

function scrollback() {
    var endpoint = url + "/_matrix/client/api/v1/rooms/$roomid/messages?access_token=$token&from=$from&dir=b&limit=$stepSize&raw=yep";
    endpoint = endpoint.replace("$token", token);
    endpoint = endpoint.replace("$from", scrollbackFrom);
    endpoint = endpoint.replace("$roomid", roomId);
    endpoint = endpoint.replace("$stepSize", stepSize);
    $.getJSON(endpoint, function(data) {
        scrollbackFrom = data.end;
        if (data.chunk.length > 0) {
            console.log("Got "+data.chunk.length+" old scrollback events.");
        }
        for (var i=0; i<data.chunk.length; ++i) {
            if (data.chunk[i].room_id === roomId) {
                // add new event to graph
                console.log("Adding new event to graph");
                addEvent(data.chunk[i]);
            }
        }
        if (collapseEvents) {
            collapseNodes();
        }
    }).fail(function(err) {
        console.error("Failed to perform scrollback: "+JSON.stringify(err));
    });
};
<|endoftext|>


<commit_before>/*
	2D Transformation Matrix v2.6.5
	(c) Epistemex.com 2014-2016
	License: MIT, header required.
*/
function Matrix(a){var b=this;b._t=b.transform;b.a=b.d=1;b.b=b.c=b.e=b.f=0;if(a){(b.context=a).setTransform(1,0,0,1,0,0)}}Matrix.fromTriangles=function(j,k,a){var b=new Matrix(),c=new Matrix(a),d,e,f,h,g,i;if(Array.isArray(j)){if(typeof j[0]==="number"){f=j[4];h=j[5];g=k[4];i=k[5];d=[j[0]-f,j[1]-h,j[2]-f,j[3]-h,f,h];e=[k[0]-g,k[1]-i,k[2]-g,k[3]-i,g,i]}else{f=j[2].x;h=j[2].y;g=k[2].x;i=k[2].y;d=[j[0].x-f,j[0].y-h,j[1].x-f,j[1].y-h,f,h];e=[k[0].x-g,k[0].y-i,k[1].x-g,j[1].y-i,g,i]}}else{d=[j.px-j.rx,j.py-j.ry,j.qx-j.rx,j.qy-j.ry,j.rx,j.ry];e=[k.px-k.rx,k.py-k.ry,k.qx-k.rx,k.qy-k.ry,k.rx,k.ry]}b.setTransform.apply(b,d);c.setTransform.apply(c,e);return c.multiply(b.inverse())};Matrix.fromSVGMatrix=function(b,a){console.warn("Obsolete. Use Matrix.from()");return new Matrix(a).multiply(b)};Matrix.fromDOMMatrix=function(b,a){console.warn("Obsolete. Use Matrix.from()");if(!b.is2D){throw"Cannot use 3D matrix."}return new Matrix(a).multiply(b)};Matrix.fromSVGTransformList=function(d,a){var c=new Matrix(a),b=0;while(b<d.length){c.multiply(d[b++].matrix)}return c};Matrix.from=function(g,h,i,k,l,n,j){var o=new Matrix(j);if(typeof g==="number"){o.setTransform(g,h,i,k,l,n)}else{if(typeof g.is2D==="boolean"&&!g.is2D){throw"Cannot use 3D DOMMatrix."}if(h){o.context=h}o.multiply(g)}return o};Matrix.prototype={concat:function(a){return this.clone().multiply(a)},flipX:function(){return this._t(-1,0,0,1,0,0)},flipY:function(){return this._t(1,0,0,-1,0,0)},reflectVector:function(c,e){var b=this.applyToPoint(0,1),a=(b.x*c+b.y*e)*2;c-=a*b.x;e-=a*b.y;return{x:c,y:e}},reset:function(){return this.setTransform(1,0,0,1,0,0)},rotate:function(a){var b=Math.cos(a),c=Math.sin(a);return this._t(b,c,-c,b,0,0)},rotateFromVector:function(a,b){return this.rotate(Math.atan2(b,a))},rotateDeg:function(a){return this.rotate(a*Math.PI/180)},scaleU:function(a){return this._t(a,0,0,a,0,0)},scale:function(a,b){return this._t(a,0,0,b,0,0)},scaleX:function(a){return this._t(a,0,0,1,0,0)},scaleY:function(a){return this._t(1,0,0,a,0,0)},shear:function(a,b){return this._t(1,b,a,1,0,0)},shearX:function(a){return this._t(1,0,a,1,0,0)},shearY:function(a){return this._t(1,a,0,1,0,0)},skew:function(a,b){return this.shear(Math.tan(a),Math.tan(b))},skewDeg:function(a,b){return this.shear(Math.tan(a/180*Math.PI),Math.tan(b/180*Math.PI))},skewX:function(a){return this.shearX(Math.tan(a))},skewY:function(a){return this.shearY(Math.tan(a))},setTransform:function(g,h,i,j,k,l){var m=this;m.a=g;m.b=h;m.c=i;m.d=j;m.e=k;m.f=l;return m._x()},translate:function(a,b){return this._t(1,0,0,1,a,b)},translateX:function(a){return this._t(1,0,0,1,a,0)},translateY:function(a){return this._t(1,0,0,1,0,a)},transform:function(b,d,f,h,j,l){var m=this,a=m.a,c=m.b,e=m.c,g=m.d,i=m.e,k=m.f;m.a=a*b+e*d;m.b=c*b+g*d;m.c=a*f+e*h;m.d=c*f+g*h;m.e=a*j+e*l+i;m.f=c*j+g*l+k;return m._x()},multiply:function(a){return this._t(a.a,a.b,a.c,a.d,a.e,a.f)},divide:function(a){if(!a.isInvertible()){throw"Matrix not invertible"}return this.multiply(a.inverse())},divideScalar:function(a){var b=this;b.a/=a;b.b/=a;b.c/=a;b.d/=a;b.e/=a;b.f/=a;return b._x()},inverse:function(a){var d=this,c=new Matrix(a?d.context:null),b=d.determinant();if(d._q(b,0)){throw"Matrix not invertible."}c.a=d.d/b;c.b=-d.b/b;c.c=-d.c/b;c.d=d.a/b;c.e=(d.c*d.f-d.d*d.e)/b;c.f=-(d.a*d.f-d.b*d.e)/b;return c},interpolate:function(c,e,a){var d=this,b=a?new Matrix(a):new Matrix();b.a=d.a+(c.a-d.a)*e;b.b=d.b+(c.b-d.b)*e;b.c=d.c+(c.c-d.c)*e;b.d=d.d+(c.d-d.d)*e;b.e=d.e+(c.e-d.e)*e;b.f=d.f+(c.f-d.f)*e;return b._x()},interpolateAnim:function(e,j,a){var d=new Matrix(a?a:null),b=this.decompose(),c=e.decompose(),k=b.translate,l=c.translate,g=b.scale,f=b.rotation+(c.rotation-b.rotation)*j,n=k.x+(l.x-k.x)*j,o=k.y+(l.y-k.y)*j,h=g.x+(c.scale.x-g.x)*j,i=g.y+(c.scale.y-g.y)*j;d.translate(n,o);d.rotate(f);d.scale(h,i);return d._x()},decompose:function(w){var l=this,e=l.a,h=l.b,i=l.c,j=l.d,f=Math.acos,g=Math.atan,u=Math.sqrt,m=Math.PI,v={x:l.e,y:l.f},o=0,q={x:1,y:1},t={x:0,y:0},k=e*j-h*i;if(w){if(e){t={x:g(i/e),y:g(h/e)};q={x:e,y:k/e}}else{if(h){o=m*0.5;q={x:h,y:k/h};t.x=g(j/h)}else{q={x:i,y:j};t.x=m*0.25}}}else{if(e||h){var n=u(e*e+h*h);o=h>0?f(e/n):-f(e/n);q={x:n,y:k/n};t.x=g((e*i+h*j)/(n*n))}else{if(i||j){var p=u(i*i+j*j);o=m*0.5-(j>0?f(-i/p):-f(i/p));q={x:k/p,y:p};t.y=g((e*i+h*j)/(p*p))}else{q={x:0,y:0}}}}return{translate:v,rotation:o,scale:q,skew:t}},determinant:function(){return this.a*this.d-this.b*this.c},applyToPoint:function(b,c){var a=this;return{x:b*a.a+c*a.c+a.e,y:b*a.b+c*a.d+a.f}},applyToArray:function(e){var a=0,d,b,c=[];if(typeof e[0]==="number"){b=e.length;while(a<b){d=this.applyToPoint(e[a++],e[a++]);c.push(d.x,d.y)}}else{while(d=e[a++]){c.push(this.applyToPoint(d.x,d.y))}}return c},applyToTypedArray:function(e,f){var a=0,d,b=e.length,c=f?new Float64Array(b):new Float32Array(b);while(a<b){d=this.applyToPoint(e[a],e[a+1]);c[a++]=d.x;c[a++]=d.y}return c},applyToContext:function(a){var b=this;a.setTransform(b.a,b.b,b.c,b.d,b.e,b.f);return b},isIdentity:function(){var a=this;return a._q(a.a,1)&&a._q(a.b,0)&&a._q(a.c,0)&&a._q(a.d,1)&&a._q(a.e,0)&&a._q(a.f,0)},isInvertible:function(){return !this._q(this.determinant(),0)},isValid:function(){return !(this.a*this.d)},isEqual:function(a){var b=this,c=b._q;return c(b.a,a.a)&&c(b.b,a.b)&&c(b.c,a.c)&&c(b.d,a.d)&&c(b.e,a.e)&&c(b.f,a.f)},clone:function(a){return new Matrix(a?null:this.context).multiply(this)},toArray:function(){var a=this;return[a.a,a.b,a.c,a.d,a.e,a.f]},toTypedArray:function(d){var b=d?new Float64Array(6):new Float32Array(6),c=this;b[0]=c.a;b[1]=c.b;b[2]=c.c;b[3]=c.d;b[4]=c.e;b[5]=c.f;return b},toCSS:function(){return"matrix("+this.toArray()+")"},toCSS3D:function(){var a=this;return"matrix3d("+a.a+","+a.b+",0,0,"+a.c+","+a.d+",0,0,0,0,1,0,"+a.e+","+a.f+",0,1)"},toJSON:function(){var a=this;return'{"a":'+a.a+',"b":'+a.b+',"c":'+a.c+',"d":'+a.d+',"e":'+a.e+',"f":'+a.f+"}"},toString:function(a){var b=this;a=a||4;return"a="+b.a.toFixed(a)+" b="+b.b.toFixed(a)+" c="+b.c.toFixed(a)+" d="+b.d.toFixed(a)+" e="+b.e.toFixed(a)+" f="+b.f.toFixed(a)},toCSV:function(){return this.toArray().join()+"\r\n"},toDOMMatrix:function(){var a=null;if("DOMMatrix" in window){a=new DOMMatrix();a.a=this.a;a.b=this.b;a.c=this.c;a.d=this.d;a.e=this.e;a.f=this.f}return a},toSVGMatrix:function(){var a=this.decompose(),f=a.translate,c=a.scale,d=a.skew,b=this._q,e=document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGMatrix();if(!e){return null}e=e.translate(f.x,f.y);e=e.rotate(a.rotation/Math.PI*180);e=e.scaleNonUniform(c.x,c.y);if(!b(0,d.x)){e=e.skewX(d.x)}if(!b(0,d.y)){e=e.skewY(d.y)}return e},_q:function(a,b){return Math.abs(a-b)<1e-14},_x:function(){var a=this;if(a.context){a.context.setTransform(a.a,a.b,a.c,a.d,a.e,a.f)}return a}};if(typeof exports!=="undefined"){exports.Matrix=Matrix};<commit_msg>Update matrix.js<commit_after>/*
	2D Transformation Matrix v2.7.2
	(c) Epistemex.com 2014-2017
	License: MIT, header required.
*/
function Matrix(b,c){var d=this,a;d._t=d.transform;d.a=d.d=1;d.b=d.c=d.e=d.f=0;if(b){(d.context=b).setTransform(1,0,0,1,0,0)}Object.defineProperty(d,"element",{get:function(){return a},set:function(e){if(!a){d._px=d._getPX();d.useCSS3D=!1}a=e;(d._st=a.style)[d._px]=d.toCSS()}});if(c){d.element=c}}Matrix.fromTriangles=function(j,k,a){var b=new Matrix(),c=new Matrix(a),d,e,f,h,g,i;if(Array.isArray(j)){if(typeof j[0]==="number"){f=j[4];h=j[5];g=k[4];i=k[5];d=[j[0]-f,j[1]-h,j[2]-f,j[3]-h,f,h];e=[k[0]-g,k[1]-i,k[2]-g,k[3]-i,g,i]}else{f=j[2].x;h=j[2].y;g=k[2].x;i=k[2].y;d=[j[0].x-f,j[0].y-h,j[1].x-f,j[1].y-h,f,h];e=[k[0].x-g,k[0].y-i,k[1].x-g,k[1].y-i,g,i]}}else{d=[j.px-j.rx,j.py-j.ry,j.qx-j.rx,j.qy-j.ry,j.rx,j.ry];e=[k.px-k.rx,k.py-k.ry,k.qx-k.rx,k.qy-k.ry,k.rx,k.ry]}b.setTransform.apply(b,d);c.setTransform.apply(c,e);return c.multiply(b.inverse())};Matrix.fromSVGTransformList=function(e,a,b){var d=new Matrix(a,b),c=0;while(c<e.length){d.multiply(e[c++].matrix)}return d};Matrix.from=function(g,h,i,k,o,p,j,n){var r=new Matrix(j,n),t,l,s;if(typeof g==="number"){r.setTransform(g,h,i,k,o,p)}else{if(typeof g.x==="number"){s=Math.sqrt(g.x*g.x+g.y*g.y);t=l=1;if(k){t=s}else{l=s}r.translate(h||0,i||0).rotateFromVector(g).scaleU(t).translate(l,0)}else{if(typeof g.is2D==="boolean"&&!g.is2D){throw"Cannot use 3D DOMMatrix."}if(h){r.context=h}if(i){r.element=i}r.multiply(g)}}return r};Matrix.prototype={_getPX:function(){var b=["t","oT","msT","mozT","webkitT","khtmlT"],a=0,c,d=document.createElement("div").style;while(c=b[a++]){if(typeof d[c+"ransform"]!=="undefined"){return c+"ransform"}}},concat:function(a){return this.clone().multiply(a)},flipX:function(){return this._t(-1,0,0,1,0,0)},flipY:function(){return this._t(1,0,0,-1,0,0)},reflectVector:function(c,e){var b=this.applyToPoint(0,1),a=(b.x*c+b.y*e)*2;c-=a*b.x;e-=a*b.y;return{x:c,y:e}},reset:function(){return this.setTransform(1,0,0,1,0,0)},rotate:function(a){var b=Math.cos(a),c=Math.sin(a);return this._t(b,c,-c,b,0,0)},rotateFromVector:function(a,b){return this.rotate(typeof a==="number"?Math.atan2(b,a):Math.atan2(a.y,a.x))},rotateDeg:function(a){return this.rotate(a*Math.PI/180)},scaleU:function(a){return this._t(a,0,0,a,0,0)},scale:function(a,b){return this._t(a,0,0,b,0,0)},scaleX:function(a){return this._t(a,0,0,1,0,0)},scaleY:function(a){return this._t(1,0,0,a,0,0)},scaleFromVector:function(a,b){return this.scaleU(Math.sqrt(a*a+b*b))},shear:function(a,b){return this._t(1,b,a,1,0,0)},shearX:function(a){return this._t(1,0,a,1,0,0)},shearY:function(a){return this._t(1,a,0,1,0,0)},skew:function(a,b){return this.shear(Math.tan(a),Math.tan(b))},skewDeg:function(a,b){return this.shear(Math.tan(a/180*Math.PI),Math.tan(b/180*Math.PI))},skewX:function(a){return this.shearX(Math.tan(a))},skewY:function(a){return this.shearY(Math.tan(a))},setTransform:function(g,h,i,j,k,l){var m=this;m.a=g;m.b=h;m.c=i;m.d=j;m.e=k;m.f=l;return m._x()},translate:function(a,b){return this._t(1,0,0,1,a,b)},translateX:function(a){return this._t(1,0,0,1,a,0)},translateY:function(a){return this._t(1,0,0,1,0,a)},transform:function(b,d,f,h,j,l){var m=this,a=m.a,c=m.b,e=m.c,g=m.d,i=m.e,k=m.f;m.a=a*b+e*d;m.b=c*b+g*d;m.c=a*f+e*h;m.d=c*f+g*h;m.e=a*j+e*l+i;m.f=c*j+g*l+k;return m._x()},multiply:function(a){return this._t(a.a,a.b,a.c,a.d,a.e,a.f)},divide:function(a){return this.multiply(a.inverse())},divideScalar:function(a){var b=this;if(!a){throw"Division on zero"}b.a/=a;b.b/=a;b.c/=a;b.d/=a;b.e/=a;b.f/=a;return b._x()},inverse:function(a,b){var e=this,d=new Matrix(a?e.context:null,b?e.element:null),c=e.determinant();if(e._q(c,0)){throw"Matrix not invertible."}d.a=e.d/c;d.b=-e.b/c;d.c=-e.c/c;d.d=e.a/c;d.e=(e.c*e.f-e.d*e.e)/c;d.f=-(e.a*e.f-e.b*e.e)/c;return d},interpolate:function(d,f,a,b){var e=this,c=new Matrix(a,b);c.a=e.a+(d.a-e.a)*f;c.b=e.b+(d.b-e.b)*f;c.c=e.c+(d.c-e.c)*f;c.d=e.d+(d.d-e.d)*f;c.e=e.e+(d.e-e.e)*f;c.f=e.f+(d.f-e.f)*f;return c._x()},interpolateAnim:function(f,h,a,d){var e=new Matrix(a,d),b=this.decompose(),c=f.decompose(),i=b.translate,j=c.translate,g=b.scale;e.translate(i.x+(j.x-i.x)*h,i.y+(j.y-i.y)*h);e.rotate(b.rotation+(c.rotation-b.rotation)*h);e.scale(g.x+(c.scale.x-g.x)*h,g.y+(c.scale.y-g.y)*h);return e._x()},decompose:function(w){var l=this,e=l.a,h=l.b,i=l.c,j=l.d,f=Math.acos,g=Math.atan,u=Math.sqrt,m=Math.PI,v={x:l.e,y:l.f},o=0,q={x:1,y:1},t={x:0,y:0},k=e*j-h*i;if(w){if(e){t={x:g(i/e),y:g(h/e)};q={x:e,y:k/e}}else{if(h){o=m*0.5;q={x:h,y:k/h};t.x=g(j/h)}else{q={x:i,y:j};t.x=m*0.25}}}else{if(e||h){var n=u(e*e+h*h);o=h>0?f(e/n):-f(e/n);q={x:n,y:k/n};t.x=g((e*i+h*j)/(n*n))}else{if(i||j){var p=u(i*i+j*j);o=m*0.5-(j>0?f(-i/p):-f(i/p));q={x:k/p,y:p};t.y=g((e*i+h*j)/(p*p))}else{q={x:0,y:0}}}}return{translate:v,rotation:o,scale:q,skew:t}},determinant:function(){return this.a*this.d-this.b*this.c},applyToPoint:function(b,c){var a=this;return{x:b*a.a+c*a.c+a.e,y:b*a.b+c*a.d+a.f}},applyToArray:function(e){var a=0,d,b,c=[];if(typeof e[0]==="number"){b=e.length;while(a<b){d=this.applyToPoint(e[a++],e[a++]);c.push(d.x,d.y)}}else{while(d=e[a++]){c.push(this.applyToPoint(d.x,d.y))}}return c},applyToTypedArray:function(e,f){var a=0,d,b=e.length,c=f?new Float64Array(b):new Float32Array(b);while(a<b){d=this.applyToPoint(e[a],e[a+1]);c[a++]=d.x;c[a++]=d.y}return c},applyToContext:function(a){var b=this;a.setTransform(b.a,b.b,b.c,b.d,b.e,b.f);return b},applyToElement:function(a,c){var b=this;if(!b._px){b._px=b._getPX()}a.style[b._px]=c?b.toCSS3D():b.toCSS();return b},applyToObject:function(b){var a=this;b.a=a.a;b.b=a.b;b.c=a.c;b.d=a.d;b.e=a.e;b.f=a.f;return a},isIdentity:function(){var a=this;return a._q(a.a,1)&&a._q(a.b,0)&&a._q(a.c,0)&&a._q(a.d,1)&&a._q(a.e,0)&&a._q(a.f,0)},isInvertible:function(){return !this._q(this.determinant(),0)},isValid:function(){return !(this.a*this.d)},isEqual:function(a){var b=this,c=b._q;return c(b.a,a.a)&&c(b.b,a.b)&&c(b.c,a.c)&&c(b.d,a.d)&&c(b.e,a.e)&&c(b.f,a.f)},clone:function(a){return new Matrix(a?null:this.context).multiply(this)},toArray:function(){var a=this;return[a.a,a.b,a.c,a.d,a.e,a.f]},toTypedArray:function(d){var b=d?new Float64Array(6):new Float32Array(6),c=this;b[0]=c.a;b[1]=c.b;b[2]=c.c;b[3]=c.d;b[4]=c.e;b[5]=c.f;return b},toCSS:function(){return"matrix("+this.toArray()+")"},toCSS3D:function(){var a=this;return"matrix3d("+a.a+","+a.b+",0,0,"+a.c+","+a.d+",0,0,0,0,1,0,"+a.e+","+a.f+",0,1)"},toJSON:function(){var a=this;return'{"a":'+a.a+',"b":'+a.b+',"c":'+a.c+',"d":'+a.d+',"e":'+a.e+',"f":'+a.f+"}"},toString:function(a){var b=this;a=a||4;return"a="+b.a.toFixed(a)+" b="+b.b.toFixed(a)+" c="+b.c.toFixed(a)+" d="+b.d.toFixed(a)+" e="+b.e.toFixed(a)+" f="+b.f.toFixed(a)},toCSV:function(){return this.toArray().join()+"\r\n"},toDOMMatrix:function(){var a=null;if("DOMMatrix" in window){a=new DOMMatrix();a.a=this.a;a.b=this.b;a.c=this.c;a.d=this.d;a.e=this.e;a.f=this.f}return a},toSVGMatrix:function(){var a=this,b=document.createElementNS("http://www.w3.org/2000/svg","svg"),c=null;if(b){c=b.createSVGMatrix();c.a=a.a;c.b=a.b;c.c=a.c;c.d=a.d;c.e=a.e;c.f=a.f}return c},_q:function(a,b){return Math.abs(a-b)<1e-14},_x:function(){var a=this;if(a.context){a.context.setTransform(a.a,a.b,a.c,a.d,a.e,a.f)}if(a._st){a._st[a._px]=a.useCSS3D?a.toCSS3D():a.toCSS()}return a}};if(typeof exports!=="undefined"){exports.Matrix=Matrix};<|endoftext|>


<commit_before>import expect from 'expect';
import reducer from '../../reducers/form';
import * as constants from '../../constants/form';

describe('form reducer', () => {

  describe('transaction', () => {

    it('default state should have tags', () => {
      expect(reducer().transaction.defaults.tags).toExist();
    });

    it('should have default attributes', () => {
      const attributes = reducer().transaction.attributes;
      expect(attributes.amount).toEqual(0);
      expect(attributes.tags).toExist();
      expect(attributes.description).toEqual('');
    });

    it('should reset the form', () => {
      const state = {
        transaction: {
          attributes: {
            amount: 9999
          }
        }
      };

      const newState = reducer(state, {
        type: constants.RESET_TRANSACTION_FORM
      });
      expect(newState.transaction.attributes.amount).toEqual(0);
    });

    it('should append new attributes to the form', () => {
      const description = 'Receipt';
      const state = reducer({}, {
        type: constants.APPEND_TRANSACTION_FORM,
        attributes: { description }
      });

      expect(state.transaction.attributes.description).toEqual(description);
    });

  });

  describe('login', () => {

    it('should have a default state', () => {
      expect(reducer().transaction.attributes).toExist();
    });

    it('should reset the form', () => {
      const state = {
        login: {
          attributes: {
            email: 'test@gmail.com'
          }
        }
      };

      const newState = reducer(state, {
        type: constants.RESET_LOGIN_FORM
      });
      expect(newState.login.attributes.email).toNotExist();
    });

    it('should append new attributes to the form', () => {
      const email = 'test@gmail.com';
      const state = reducer({}, {
        type: constants.APPEND_LOGIN_FORM,
        attributes: { email }
      });

      expect(state.login.attributes.email).toEqual(email);
    });

  });

});
<commit_msg>Adapting the tests<commit_after>import expect from 'expect';
import reducer from '../../reducers/form';
import * as constants from '../../constants/form';

describe('form reducer', () => {

  describe('transaction', () => {

    it('should have default attributes', () => {
      const attributes = reducer().transaction.attributes;
      expect(attributes.amount).toEqual(0);
      expect(attributes.tags).toExist();
      expect(attributes.description).toEqual('');
    });

    it('should reset the form', () => {
      const state = {
        transaction: {
          attributes: {
            amount: 9999
          }
        }
      };

      const newState = reducer(state, {
        type: constants.RESET_TRANSACTION_FORM
      });
      expect(newState.transaction.attributes.amount).toEqual(0);
    });

    it('should append new attributes to the form', () => {
      const description = 'Receipt';
      const state = reducer({}, {
        type: constants.APPEND_TRANSACTION_FORM,
        attributes: { description }
      });

      expect(state.transaction.attributes.description).toEqual(description);
    });

  });

  describe('login', () => {

    it('should have a default state', () => {
      expect(reducer().transaction.attributes).toExist();
    });

    it('should reset the form', () => {
      const state = {
        login: {
          attributes: {
            email: 'test@gmail.com'
          }
        }
      };

      const newState = reducer(state, {
        type: constants.RESET_LOGIN_FORM
      });
      expect(newState.login.attributes.email).toNotExist();
    });

    it('should append new attributes to the form', () => {
      const email = 'test@gmail.com';
      const state = reducer({}, {
        type: constants.APPEND_LOGIN_FORM,
        attributes: { email }
      });

      expect(state.login.attributes.email).toEqual(email);
    });

  });

});
<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */

const { createInterface: readline } = require('readline');
const { inspect } = require('util');
const { createReadStream, writeFileSync, readFileSync } = require('fs');

const { get } = require('lodash');

const values = require('../lib/helpers/defaults');

function capitalizeSentences(copy) {
  return copy.replace(/\. [a-z]/g, match => `. ${match.slice(-1).toUpperCase()}`);
}

class Block {
  write(buffer) {
    if (!this[this.active]) {
      this[this.active] = buffer;
    } else {
      while (buffer.indexOf('*') === 0 || buffer.indexOf(' ') === 0) {
        buffer = buffer.slice(1);
      }
      if (buffer.indexOf('-') === 0) {
        buffer = Buffer.concat([Buffer.from('\n'), buffer]);
      }
      this[this.active] = Buffer.concat([this[this.active], Buffer.from(' '), buffer]);
    }
  }
}

const props = [
  'description',
  'affects',
  'recommendation',
  '@nodefault',
];

(async () => {
  const blocks = {};
  await new Promise((resolve, reject) => {
    const read = readline({ input: createReadStream('./lib/helpers/defaults.js') });
    let nextIsOption;
    let inBlock;
    let option;

    read.on('line', (line) => {
      const strLine = line.trim();
      line = Buffer.from(strLine);

      if (strLine.startsWith('/*')) {
        inBlock = true;
        nextIsOption = true;
        return;
      }

      if (!inBlock) return;

      if (nextIsOption) {
        nextIsOption = false;
        option = blocks[strLine.substring(2)] = new Block(); // eslint-disable-line no-multi-assign
        return;
      }

      const next = props.find((prop) => {
        if (strLine.substring(2, 2 + prop.length) === prop) {
          option.active = prop;
          option.write(line.slice(prop.length + 4));
          return true;
        }
        return false;
      });

      if (next) return;

      if (strLine.startsWith('*/')) {
        inBlock = false;
        option = false;
        return;
      }

      if (option && option.active) {
        option.write(line);
      }
    });

    read.on('close', () => {
      resolve();
    });

    read.on('error', reject);
  });

  let mid = Buffer.from('');

  function append(what) {
    mid = Buffer.concat([mid, Buffer.from(what)]);
  }

  Object.keys(blocks).sort().forEach((block) => {
    append(`\n### ${block}\n\n`);
    if (blocks[block].description) {
      append(`${capitalizeSentences(blocks[block].description.toString())}  \n\n`);
    }
    ['affects', 'recommendation'].forEach((section) => {
      if (blocks[block][section]) {
        append(`${section}: ${blocks[block][section].toString()}  \n`);
      }
    });
    if (!('@nodefault' in blocks[block])) {
      append('\ndefault value:\n');
      append('```js\n');
      const value = get(values, block);
      switch (typeof value) {
        case 'boolean':
        case 'number':
          append(`${String(value)}`);
          break;
        case 'string':
        case 'object':
          append(inspect(value));
          break;
        case 'function': {
          let fixIndent;
          let mute = false;
          append(String(value).split('\n').map((line, index) => {
            if (index === 1) {
              line.match(/^(\s+)\S+/);
              fixIndent = RegExp.$1.length - 2;
            }
            if (line.includes('changeme')) return undefined;
            if (line.startsWith(' ')) line = line.slice(fixIndent);
            line = line.replace(/ \/\/ eslint-disable.+/, '');
            line = line.replace(/ \/\/ TODO.+/, '');
            line = line.replace(/ class="[ \-\w]+ ?"/, '');
            if (line.includes('<style>')) {
              mute = true;
              return '<style>/* css and html classes omitted for brevity, see lib/helpers/defaults.js */</style>';
            } else if (line.includes('</style>')) {
              mute = false;
              return undefined;
            }
            if (mute) return undefined;
            return line;
          }).filter(Boolean)
            .join('\n'));
          break;
        }
        default:
          throw new Error(`unexpected value type ${typeof value}`);
      }
      append('\n```\n');
    }
  });

  const conf = readFileSync('./docs/configuration.md');

  const comStart = '<!-- START CONF OPTIONS -->';
  const comEnd = '<!-- END CONF OPTIONS -->';

  const pre = conf.slice(0, conf.indexOf(comStart) + comStart.length);
  const post = conf.slice(conf.indexOf(comEnd));

  writeFileSync('./docs/configuration.md', Buffer.concat([pre, mid, post]));
})();
<commit_msg>style: lint docs/update-configuration.js<commit_after>/* eslint-disable no-param-reassign */

const { createInterface: readline } = require('readline');
const { inspect } = require('util');
const { createReadStream, writeFileSync, readFileSync } = require('fs');

const { get } = require('lodash');

const values = require('../lib/helpers/defaults');

function capitalizeSentences(copy) {
  return copy.replace(/\. [a-z]/g, match => `. ${match.slice(-1).toUpperCase()}`);
}

class Block {
  write(buffer) {
    if (!this[this.active]) {
      this[this.active] = buffer;
    } else {
      while (buffer.indexOf('*') === 0 || buffer.indexOf(' ') === 0) {
        buffer = buffer.slice(1);
      }
      if (buffer.indexOf('-') === 0) {
        buffer = Buffer.concat([Buffer.from('\n'), buffer]);
      }
      this[this.active] = Buffer.concat([this[this.active], Buffer.from(' '), buffer]);
    }
  }
}

const props = [
  'description',
  'affects',
  'recommendation',
  '@nodefault',
];

(async () => {
  const blocks = {};
  await new Promise((resolve, reject) => {
    const read = readline({ input: createReadStream('./lib/helpers/defaults.js') });
    let nextIsOption;
    let inBlock;
    let option;

    read.on('line', (line) => {
      const strLine = line.trim();
      line = Buffer.from(strLine);

      if (strLine.startsWith('/*')) {
        inBlock = true;
        nextIsOption = true;
        return;
      }

      if (!inBlock) return;

      if (nextIsOption) {
        nextIsOption = false;
        option = blocks[strLine.substring(2)] = new Block(); // eslint-disable-line no-multi-assign
        return;
      }

      const next = props.find((prop) => {
        if (strLine.substring(2, 2 + prop.length) === prop) {
          option.active = prop;
          option.write(line.slice(prop.length + 4));
          return true;
        }
        return false;
      });

      if (next) return;

      if (strLine.startsWith('*/')) {
        inBlock = false;
        option = false;
        return;
      }

      if (option && option.active) {
        option.write(line);
      }
    });

    read.on('close', () => {
      resolve();
    });

    read.on('error', reject);
  });

  let mid = Buffer.from('');

  function append(what) {
    mid = Buffer.concat([mid, Buffer.from(what)]);
  }

  Object.keys(blocks).sort().forEach((block) => {
    append(`\n### ${block}\n\n`);
    if (blocks[block].description) {
      append(`${capitalizeSentences(blocks[block].description.toString())}  \n\n`);
    }
    ['affects', 'recommendation'].forEach((section) => {
      if (blocks[block][section]) {
        append(`${section}: ${blocks[block][section].toString()}  \n`);
      }
    });
    if (!('@nodefault' in blocks[block])) {
      append('\ndefault value:\n');
      append('```js\n');
      const value = get(values, block);
      switch (typeof value) {
        case 'boolean':
        case 'number':
          append(`${String(value)}`);
          break;
        case 'string':
        case 'object':
          append(inspect(value));
          break;
        case 'function': {
          let fixIndent;
          let mute = false;
          append(String(value).split('\n').map((line, index) => {
            if (index === 1) {
              line.match(/^(\s+)\S+/);
              fixIndent = RegExp.$1.length - 2;
            }
            if (line.includes('changeme')) return undefined;
            if (line.startsWith(' ')) line = line.slice(fixIndent);
            line = line.replace(/ \/\/ eslint-disable.+/, '');
            line = line.replace(/ \/\/ TODO.+/, '');
            line = line.replace(/ class="[ \-\w]+ ?"/, '');
            if (line.includes('<style>')) {
              mute = true;
              return '<style>/* css and html classes omitted for brevity, see lib/helpers/defaults.js */</style>';
            }
            if (line.includes('</style>')) {
              mute = false;
              return undefined;
            }
            if (mute) return undefined;
            return line;
          }).filter(Boolean)
            .join('\n'));
          break;
        }
        default:
          throw new Error(`unexpected value type ${typeof value}`);
      }
      append('\n```\n');
    }
  });

  const conf = readFileSync('./docs/configuration.md');

  const comStart = '<!-- START CONF OPTIONS -->';
  const comEnd = '<!-- END CONF OPTIONS -->';

  const pre = conf.slice(0, conf.indexOf(comStart) + comStart.length);
  const post = conf.slice(conf.indexOf(comEnd));

  writeFileSync('./docs/configuration.md', Buffer.concat([pre, mid, post]));
})();
<|endoftext|>


<commit_before>function FindProxyForURL(url, host) {
	// LIMA RENHOLD PROXY AUTOCONFIG - AV OMMUNDSEN SERVICES
	// Prv proxy for cache, hvis ikke den er tilgjengelig, g direkte til nettet.
	if (dnsDomainIs(host, "google.com") || dnsDomainIs(host, "www.google.com"))
        return "DIRECT";
	if (isInNet(myIpAddress(), "192.168.1.0", "255.255.255.0"))
		return "PROXY 192.168.1.2:3128; DIRECT";
	else
		return "DIRECT";
}<commit_msg>Delete limarenhold.pac<commit_after><|endoftext|>


<commit_before>var run = function(args){
	// depatch
	var data = args.recorderdata;
    var xindex = args.choosecolumn[0];
    var yindex = args.choosecolumn[1];
    var xfactor = args.ampfactor[0];
    var yfactor = args.ampfactor[1];
    var datalines = data.split("\n");
    var series = [];
    for (var index in datalines) {
        var dataitems = datalines[index].split(" ");
        var x = parseFloat(dataitems[xindex]) * xfactor;
        var y = parseFloat(dataitems[yindex]) * yfactor;
        series.push([x, y]);
    }
    var highchartjson = {
                chart: {
                    type: "scatter",
                    height: 600
                },
                title: {
                    text: "文件:"+filename
                },
                legend: {
                    enabled: false
                },
                plotOptions: {
                    series: {
                        lineWidth: 1
                    }
                },
                yAxis: {
                    title: {
                        enabled: false
                    },
                    gridLineWidth: 0.8
                },
                xAxis: {
                    title: {
                        enabled: false
                    },
                    gridLineWidth: 0.8
                },
                series: [{
                    data: series,
                    marker: {
                        enabled: false
                    }
                }]
            }
    var result = [];
    result.push({"type": "highchart", "value": highchartjson});
    return result;
}<commit_msg>change aspect ratio to square<commit_after>var run = function(args){
	// depatch
	var data = args.recorderdata;
    var xindex = args.choosecolumn[0];
    var yindex = args.choosecolumn[1];
    var xfactor = args.ampfactor[0];
    var yfactor = args.ampfactor[1];
    var datalines = data.split("\n");
    var series = [];
    for (var index in datalines) {
        var dataitems = datalines[index].split(" ");
        var x = parseFloat(dataitems[xindex]) * xfactor;
        var y = parseFloat(dataitems[yindex]) * yfactor;
        series.push([x, y]);
    }
    var highchartjson = {
                chart: {
                    type: "scatter",
                    height: 700
                },
                title: {
                    text: "文件:"+filename
                },
                legend: {
                    enabled: false
                },
                plotOptions: {
                    series: {
                        lineWidth: 1
                    }
                },
                yAxis: {
                    title: {
                        enabled: false
                    },
                    gridLineWidth: 0.8
                },
                xAxis: {
                    title: {
                        enabled: false
                    },
                    gridLineWidth: 0.8
                },
                series: [{
                    data: series,
                    marker: {
                        enabled: false
                    }
                }]
            }
    var result = [];
    result.push({"type": "highchart", "value": highchartjson});
    return result;
}<|endoftext|>