<commit_before>
"use strict";

// deps

	const { join } = require("path");
	const { networkInterfaces } = require("os");

	const express = require("express");
	const cors = require("cors");
	const helmet = require("helmet");
	const compression = require("compression");
	// @TODO : add express-fileupload middleware
	// const fileupload = require("express-fileupload");

	const Conf = require("node-confmanager");

	const apiv1routes = require(join(__dirname, "api", "v1", "routes.js"));

// consts

	const APP = express();
	const CONF = new Conf(join(__dirname, "conf.json"));
	const CODE_ERRORS = require(join(__dirname, "returncodes.json"));

// module

	CONF.load().then(() => {

		APP
			.use(cors())
			.use(helmet())
			.use(compression())
			// @TODO : add fileupload middleware
			// .use(fileupload())
			.use((err, req, res, next) => {

				if (res.headersSent) {
					next(err); return;
				}

				res.status(CODE_ERRORS.INTERNAL).send(
					"Ouups ! Something broke !
" + (err.message ? err.message : err)
				);

			});

		// main page

		APP.get("/", (req, res) => {
			res.sendFile(join(__dirname, "web", "index.html"));
		})
		.get("/public/app.js", (req, res) => {
			res.sendFile(join(__dirname, "web", "app.js"));
		})

		// pictures

		.get("/public/pictures/warcraft3.png", (req, res) => {
			res.sendFile(join(__dirname, "web", "pictures", "warcraft3.png"));
		})
		.get("/public/pictures/warcraft3TFT.png", (req, res) => {
			res.sendFile(join(__dirname, "web", "pictures", "warcraft3TFT.png"));
		})

		// ips
		.get("/ips", (req, res) => {

			const result = [];

				const ifaces = networkInterfaces();

				Object.keys(ifaces).forEach((ifname) => {

					let alias = 0;

					ifaces[ifname].forEach((iface) => {

						if ("IPv4" !== iface.family || false !== iface.internal) {
							return;
						}
						else if (1 <= alias) {

							result.push({
								"address": iface.address,
								"name": ifname + "-" + alias
							});

						}
						else {

							result.push({
								"address": iface.address,
								"name": ifname
							});

						}

						++alias;

					});

				});

			res.status(CODE_ERRORS.OK).json(result);

		});

		// add API V1 routes

		apiv1routes(APP).catch((err) => {
			console.warn("Impossible to initiate the API (V1)", err);
		});

		APP.listen(CONF.get("port"), () => {
			console.info("started on port", CONF.get("port"));
		});

	})
	.catch((err) => {
		console.error("Impossible to initiate the configuration", err);
	});
<commit_msg>activate fileupload<commit_after>
"use strict";

// deps

	const { join } = require("path");
	const { networkInterfaces } = require("os");

	const express = require("express");
	const cors = require("cors");
	const helmet = require("helmet");
	const compression = require("compression");
	const fileupload = require("express-fileupload");

	const Conf = require("node-confmanager");

	const apiv1routes = require(join(__dirname, "api", "v1", "routes.js"));

// consts

	const APP = express();
	const CONF = new Conf(join(__dirname, "conf.json"));
	const CODE_ERRORS = require(join(__dirname, "returncodes.json"));

// module

	CONF.load().then(() => {

		APP
			.use(cors())
			.use(helmet())
			.use(compression())
			.use(fileupload())
			.use((err, req, res, next) => {

				if (res.headersSent) {
					next(err); return;
				}

				res.status(CODE_ERRORS.INTERNAL).send(
					"Ouups ! Something broke !
" + (err.message ? err.message : err)
				);

			});

		// main page

		APP.get("/", (req, res) => {
			res.sendFile(join(__dirname, "web", "index.html"));
		})
		.get("/public/app.js", (req, res) => {
			res.sendFile(join(__dirname, "web", "app.js"));
		})

		// pictures

		.get("/public/pictures/warcraft3.png", (req, res) => {
			res.sendFile(join(__dirname, "web", "pictures", "warcraft3.png"));
		})
		.get("/public/pictures/warcraft3TFT.png", (req, res) => {
			res.sendFile(join(__dirname, "web", "pictures", "warcraft3TFT.png"));
		})

		// ips
		.get("/ips", (req, res) => {

			const result = [];

				const ifaces = networkInterfaces();

				Object.keys(ifaces).forEach((ifname) => {

					let alias = 0;

					ifaces[ifname].forEach((iface) => {

						if ("IPv4" !== iface.family || false !== iface.internal) {
							return;
						}
						else if (1 <= alias) {

							result.push({
								"address": iface.address,
								"name": ifname + "-" + alias
							});

						}
						else {

							result.push({
								"address": iface.address,
								"name": ifname
							});

						}

						++alias;

					});

				});

			res.status(CODE_ERRORS.OK).json(result);

		});

		// add API V1 routes

		apiv1routes(APP).catch((err) => {
			console.warn("Impossible to initiate the API (V1)", err);
		});

		APP.listen(CONF.get("port"), () => {
			console.info("started on port", CONF.get("port"));
		});

	})
	.catch((err) => {
		console.error("Impossible to initiate the configuration", err);
	});
<|endoftext|>


<commit_before>'use strict';

// Utils
const { loadJSON } = require('../../utils/json');
const { link } = require('../../utils/tg');
const { logError } = require('../../utils/log');

// Config
const { masterID } = loadJSON('config.json');

// Bot
const { replyOptions } = require('../../bot/options');

// DB
const {
	isAdmin,
	admin,
	isBanned,
	getWarns,
	nowarns
} = require('../../stores/user');

const adminHandler = async ({ message, reply }) => {
	if (message.from.id !== masterID) {
		return null;
	}

	const userToAdmin = message.reply_to_message
		? message.reply_to_message.from
		: message.commandMention
			? message.commandMention
			: message.from;

	if (await isBanned(userToAdmin)) {
		return reply('ℹ Can\'t admin banned user.', replyOptions);
	}

	if (await isAdmin(userToAdmin)) {
		return reply(`⭐ ${link(userToAdmin)} <b>is already admin.</b>.`,
			replyOptions);
	}

	if (await getWarns(userToAdmin)) {
		try {
			await nowarns(userToAdmin);
		} catch (err) {
			logError(err);
		}
	}

	try {
		await admin(userToAdmin);
	} catch (err) {
		logError(err);
	}

	return reply(`⭐ ${link(userToAdmin)} <b>is now admin.</b>.`, replyOptions);
};

module.exports = adminHandler;
<commit_msg>fixed small typo<commit_after>'use strict';

// Utils
const { loadJSON } = require('../../utils/json');
const { link } = require('../../utils/tg');
const { logError } = require('../../utils/log');

// Config
const { masterID } = loadJSON('config.json');

// Bot
const { replyOptions } = require('../../bot/options');

// DB
const {
	isAdmin,
	admin,
	isBanned,
	getWarns,
	nowarns
} = require('../../stores/user');

const adminHandler = async ({ message, reply }) => {
	if (message.from.id !== masterID) {
		return null;
	}

	const userToAdmin = message.reply_to_message
		? message.reply_to_message.from
		: message.commandMention
			? message.commandMention
			: message.from;

	if (await isBanned(userToAdmin)) {
		return reply('ℹ Can\'t admin banned user.', replyOptions);
	}

	if (await isAdmin(userToAdmin)) {
		return reply(`⭐ ${link(userToAdmin)} <b>is already admin.</b>`,
			replyOptions);
	}

	if (await getWarns(userToAdmin)) {
		try {
			await nowarns(userToAdmin);
		} catch (err) {
			logError(err);
		}
	}

	try {
		await admin(userToAdmin);
	} catch (err) {
		logError(err);
	}

	return reply(`⭐ ${link(userToAdmin)} <b>is now admin.</b>.`, replyOptions);
};

module.exports = adminHandler;
<|endoftext|>


<commit_before>'use strict';

var path = require('path');

function resolvePath(p) {

  var parts = p.split('/', 2);

  return (
    path.join(
      require.resolve(parts[0]),
      parts[1]
    )
  );
}

module.exports = function(grunt) {

  require('load-grunt-tasks')(grunt);

  // project configuration
  grunt.initConfig({
    browserify: {
      options: {
        transform: [
          [ 'babelify', {
            global: true
          } ]
        ]
      },
      watch: {
        options: {
          watch: true
        },
        files: {
          'dist/app.js': [ 'app/**/*.js' ]
        }
      },
      app: {
        files: {
          'dist/app.js': [ 'app/**/*.js' ]
        }
      }
    },
    copy: {
      app: {
        files: [
          {
            expand: true,
            cwd: 'app/',
            src: ['**/*.*', '!**/*.js'],
            dest: 'dist'
          },
          {
            src: resolvePath('diagram-js/assets/diagram-js.css'),
            dest: 'dist/css/diagram-js.css'
          }
        ]
      }
    },
    watch: {
      options: {
        livereload: true
      },
      samples: {
        files: [ 'app/**/*.*' ],
        tasks: [ 'copy:app' ]
      }
    },
    connect: {
      livereload: {
        options: {
          port: 9013,
          livereload: true,
          hostname: 'localhost',
          open: true,
          base: [
            'dist'
          ]
        }
      }
    }
  });

  // tasks

  grunt.registerTask('build', [ 'browserify:app', 'copy:app' ]);

  grunt.registerTask('auto-build', [
    'copy',
    'browserify:watch',
    'connect',
    'watch'
  ]);

  grunt.registerTask('default', [ 'build' ]);
};<commit_msg>fix(example): properly copy CSS file<commit_after>'use strict';

var path = require('path');

function resolvePath(p) {
  return require.resolve(p);
}

module.exports = function(grunt) {

  require('load-grunt-tasks')(grunt);

  // project configuration
  grunt.initConfig({
    browserify: {
      options: {
        transform: [
          [ 'babelify', {
            global: true
          } ]
        ]
      },
      watch: {
        options: {
          watch: true
        },
        files: {
          'dist/app.js': [ 'app/**/*.js' ]
        }
      },
      app: {
        files: {
          'dist/app.js': [ 'app/**/*.js' ]
        }
      }
    },
    copy: {
      app: {
        files: [
          {
            expand: true,
            cwd: 'app/',
            src: ['**/*.*', '!**/*.js'],
            dest: 'dist'
          },
          {
            src: resolvePath('diagram-js/assets/diagram-js.css'),
            dest: 'dist/css/diagram-js.css'
          }
        ]
      }
    },
    watch: {
      options: {
        livereload: true
      },
      samples: {
        files: [ 'app/**/*.*' ],
        tasks: [ 'copy:app' ]
      }
    },
    connect: {
      livereload: {
        options: {
          port: 9013,
          livereload: true,
          hostname: 'localhost',
          open: true,
          base: [
            'dist'
          ]
        }
      }
    }
  });

  // tasks

  grunt.registerTask('build', [ 'browserify:app', 'copy:app' ]);

  grunt.registerTask('auto-build', [
    'copy',
    'browserify:watch',
    'connect',
    'watch'
  ]);

  grunt.registerTask('default', [ 'build' ]);
};<|endoftext|>


<commit_before>/// @file JSRoot.openui5.js
/// Bootstraping of OpenUI5 functionality in JSROOT
/// Openui5 loaded directly in the script

(function( factory ) {
   if ( typeof define === "function" && define.amd ) {
      define( ['jquery', 'jquery-ui', 'JSRootCore' ], factory );
   } else {

      if (typeof jQuery == 'undefined')
         throw new Error('jQuery not defined', 'JSRoot.openui5.js');

      if (typeof jQuery.ui == 'undefined')
         throw new Error('jQuery-ui not defined','JSRoot.openui5.js');

      if (typeof JSROOT == 'undefined')
         throw new Error('JSROOT is not defined', 'JSRoot.openui5.js');

      factory(jQuery, jQuery.ui, JSROOT);
   }
} (function($, myui, JSROOT) {

   "use strict";

   JSROOT.sources.push("openui5");

   var load_callback = JSROOT.complete_script_load;
   delete JSROOT.complete_script_load; // normal callback is intercepted - we need to instantiate openui5

   JSROOT.completeUI5Loading = function() {
      // when running with THttpServer, automatically set "rootui5" folder
      let rootui5sys = undefined;
      if (JSROOT.source_dir.indexOf("jsrootsys") >= 0)
         rootui5sys = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys");

      sap.ui.loader.config({
         paths: {
            jsroot: JSROOT.source_dir,
            rootui5: rootui5sys
         }
      });

      JSROOT.CallBack(load_callback);
      load_callback = null;
   }

   function TryOpenOpenUI(sources) {

      // where to take openui5 sources
      let src = sources.shift();

      if ((src.indexOf("roothandler")==0) && (src.indexOf("://")<0)) src = src.replace(/\:\//g,"://");

      let element = document.createElement("script");
      element.setAttribute('type', "text/javascript");
      element.setAttribute('id', "sap-ui-bootstrap");
      // use nojQuery while we are already load jquery and jquery-ui, later one can use directly sap-ui-core.js

      // this is location of openui5 scripts when working with THttpServer or when scripts are installed inside JSROOT
      element.setAttribute('src', src + "resources/sap-ui-core-nojQuery.js"); // latest openui5 version

      element.setAttribute('data-sap-ui-libs', JSROOT.openui5libs || "sap.m, sap.ui.layout, sap.ui.unified, sap.ui.commons");

      element.setAttribute('data-sap-ui-theme', JSROOT.openui5theme || 'sap_belize');
      element.setAttribute('data-sap-ui-compatVersion', 'edge');
      // element.setAttribute('data-sap-ui-bindingSyntax', 'complex');

      element.setAttribute('data-sap-ui-preload', 'async'); // '' to disable Component-preload.js

      element.setAttribute('data-sap-ui-evt-oninit', "JSROOT.completeUI5Loading()");

      element.onerror = function() {
         // remove failed element
         element.parentNode.removeChild(element);
         // and try next
         TryOpenOpenUI(sources);
      }

      element.onload = function() {
         console.log('Load openui5 from ' + src);
      }

      document.getElementsByTagName("head")[0].appendChild(element);
   }

   var openui5_sources = [],
       openui5_dflt = "https://openui5.hana.ondemand.com/1.72.0/",
       openui5_root = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys/distribution");

   if (openui5_root == JSROOT.source_dir) openui5_root = "";

   if (typeof JSROOT.openui5src == 'string') {
      switch (JSROOT.openui5src) {
         case "nodefault": openui5_dflt = ""; break;
         case "default": openui5_sources.push(openui5_dflt); openui5_dflt = ""; break;
         case "nojsroot": openui5_root = ""; break;
         case "jsroot": openui5_sources.push(openui5_root); openui5_root = ""; break;
         default: openui5_sources.push(JSROOT.openui5src); break;
      }
   }

   if (openui5_root && (openui5_sources.indexOf(openui5_root)<0)) openui5_sources.push(openui5_root);
   if (openui5_dflt && (openui5_sources.indexOf(openui5_dflt)<0)) openui5_sources.push(openui5_dflt);

   TryOpenOpenUI(openui5_sources);

   return JSROOT;

}));

<commit_msg>Adjust openui5 loader<commit_after>/// @file JSRoot.openui5.js
/// Bootstraping of OpenUI5 functionality in JSROOT
/// Openui5 loaded directly in the script

(function( factory ) {
   if ( typeof define === "function" && define.amd ) {
      define( ['JSRootCore', 'jquery', 'jquery-ui' ], factory );
   } else {

      if (typeof JSROOT == 'undefined')
         throw new Error('JSROOT is not defined', 'JSRoot.openui5.js');

      if (typeof jQuery == 'undefined')
         throw new Error('jQuery not defined', 'JSRoot.openui5.js');

      if (typeof jQuery.ui == 'undefined')
         throw new Error('jQuery-ui not defined','JSRoot.openui5.js');

      factory(JSROOT);
   }
} (function(JSROOT) {

   "use strict";

   JSROOT.sources.push("openui5");

   var load_callback = JSROOT.complete_script_load;
   delete JSROOT.complete_script_load; // normal callback is intercepted - we need to instantiate openui5

   JSROOT.completeUI5Loading = function() {
      // when running with THttpServer, automatically set "rootui5" folder
      let rootui5sys = undefined;
      if (JSROOT.source_dir.indexOf("jsrootsys") >= 0)
         rootui5sys = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys");

      sap.ui.loader.config({
         paths: {
            jsroot: JSROOT.source_dir,
            rootui5: rootui5sys
         }
      });

      JSROOT.CallBack(load_callback);
      load_callback = null;
   }

   function TryOpenOpenUI(sources) {

      // where to take openui5 sources
      let src = sources.shift();

      if ((src.indexOf("roothandler")==0) && (src.indexOf("://")<0)) src = src.replace(/\:\//g,"://");

      let element = document.createElement("script");
      element.setAttribute('type', "text/javascript");
      element.setAttribute('id', "sap-ui-bootstrap");
      // use nojQuery while we are already load jquery and jquery-ui, later one can use directly sap-ui-core.js

      // this is location of openui5 scripts when working with THttpServer or when scripts are installed inside JSROOT
      element.setAttribute('src', src + "resources/sap-ui-core-nojQuery.js"); // latest openui5 version

      element.setAttribute('data-sap-ui-libs', JSROOT.openui5libs || "sap.m, sap.ui.layout, sap.ui.unified, sap.ui.commons");

      element.setAttribute('data-sap-ui-theme', JSROOT.openui5theme || 'sap_belize');
      element.setAttribute('data-sap-ui-compatVersion', 'edge');
      // element.setAttribute('data-sap-ui-bindingSyntax', 'complex');

      element.setAttribute('data-sap-ui-preload', 'async'); // '' to disable Component-preload.js

      element.setAttribute('data-sap-ui-evt-oninit', "JSROOT.completeUI5Loading()");

      element.onerror = function() {
         // remove failed element
         element.parentNode.removeChild(element);
         // and try next
         TryOpenOpenUI(sources);
      }

      element.onload = function() {
         console.log('Load openui5 from ' + src);
      }

      document.getElementsByTagName("head")[0].appendChild(element);
   }

   var openui5_sources = [],
       openui5_dflt = "https://openui5.hana.ondemand.com/1.72.0/",
       openui5_root = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys/distribution");

   if (openui5_root == JSROOT.source_dir) openui5_root = "";

   if (typeof JSROOT.openui5src == 'string') {
      switch (JSROOT.openui5src) {
         case "nodefault": openui5_dflt = ""; break;
         case "default": openui5_sources.push(openui5_dflt); openui5_dflt = ""; break;
         case "nojsroot": openui5_root = ""; break;
         case "jsroot": openui5_sources.push(openui5_root); openui5_root = ""; break;
         default: openui5_sources.push(JSROOT.openui5src); break;
      }
   }

   if (openui5_root && (openui5_sources.indexOf(openui5_root)<0)) openui5_sources.push(openui5_root);
   if (openui5_dflt && (openui5_sources.indexOf(openui5_dflt)<0)) openui5_sources.push(openui5_dflt);

   TryOpenOpenUI(openui5_sources);

   return JSROOT;

}));

<|endoftext|>


<commit_before>/**
 * a11yclick - Easily handle keyboard click events on non semantic button elements.
 *
 * @param {Object} event Click/keyboard event object.
 * @returns {Boolean} Returns true or false depending on event type and code.
 */
var a11yclick = function(event) {
    'use strict';

    var code = event.charCode || event.keyCode,
        type = event.type;

    if (type === 'click') {
        return true;
    } else if (type === 'keydown') {
        if (code === 32 || code === 13) {
            return true;
        }
    } else {
        return false;
    }
};
<commit_msg>fix to not scroll the page when space key is hit<commit_after>/**
 * a11yclick - Easily handle keyboard click events on non semantic button elements.
 *
 * @param {Object} event Click/keyboard event object.
 * @returns {Boolean} Returns true or false depending on event type and code.
 */
var a11yclick = function(event) {
    'use strict';

    var code = event.charCode || event.keyCode,
        type = event.type;

    if (type === 'click') {
        return true;
    } else if (type === 'keydown') {
        if(code === 32){
        	event.preventDefault();
        }
        if (code === 32 || code === 13) {
            return true;
        }
    } else {
        return false;
    }
};
<|endoftext|>


<commit_before>/*
 *  MainScene.js
 *  2015/09/08
 *  @auther minimo
 *  This Program is MIT license.
 */

phina.define("pbr.MainScene", {
    superClass: 'phina.display.CanvasScene',

    _member: {
        score: 0,

        //再生中BGM
        bgm: null,

        //自機コントロール可能フラグ
        control: false,

        //ラベル用パラメータ
        labelParam: {
            fill: "white",
            stroke: true,
            strokeColor: 'black',
            strokeWidth: 3,

            fontFamily: "Orbitron",
            align: "center",
            baseline: "middle",
            fontSize: 20
        },
        scorelabelParam: {
            fill: "white",
            stroke: true,
            strokeColor: 'black',
            strokeWidth: 3,

            fontFamily: "Orbitron",
            align: "center",
            baseline: "middle",
            fontSize: 20,
            align: "left",
        },
    },

    init: function() {
        this.superInit();
        this.$extend(this._member);

        //バックグラウンド
        var param = {
            width:SC_W,
            height:SC_H,
            fill: 'black',
            stroke: false,
            backgroundColor: 'transparent',
        };
        this.bg = phina.display.RectangleShape(param)
            .addChildTo(this)
            .setPosition(SC_W*0.5, SC_H*0.5)

        //レイヤー準備
        this.base = phina.display.CanvasElement().addChildTo(this).setPosition(SC_OFFSET_X, 0);
        this.layers = [];
        for (var i = 0; i < LAYER_SYSTEM+1; i++) {
            this.layers[i] = phina.display.CanvasElement().addChildTo(this.base);
        }

        //プレイヤー準備
/*
        this.player = pbr.Player()
            .addChildTo(this.playerLayer)
            .setPosition(PN_OFFX, PN_OFFY);
        this.player.visible = false;
*/
        //スコア表示
        var that = this;
        var lb = this.scoreLabel = phina.display.Label("SCORE:", this.scorelabelParam)
            .addChildTo(this)
            .setPosition(8, 32);
        lb.update = function() {
            this.text = "得点:"+that.score;
        }

        //目隠し
        this.mask = phina.display.RectangleShape(param)
            .addChildTo(this)
            .setPosition(SC_W*0.5, SC_H*0.5);
    },

    update: function(app) {
        if (this.control) {
            var player = this.player;
            //マウス操作
            var p = app.pointing;
            if (p.getPointing()) {
                var pt = this.parentScene.pointer;
                this.x += (pt.x - this.x)/this.touchSpeed;
                this.y += (pt.y - this.y)/this.touchSpeed;

                this.mouseON = true;
                this.shotON = true;
            } else {
                this.mouseON = false;
                this.shotON = false;
            }

            //キーボード操作
            var kb = app.keyboard;
            var angle = kb.getKeyAngle();
            if (angle !== null) {
                var m = KEYBOARD_MOVE[angle];
                this.x += m.x*this.speed;
                this.y += m.y*this.speed;
            }
            if (!this.mouseON) this.shotON = app.keyboard.getKey("Z");

            //ショットタイプ変更（テスト用）
            if (app.keyboard.getKey("X") && this.time > this.changeInterval) {
                this.type = (this.type+1)%3;
                this.openBit(this.type);
                this.changeInterval = this.time+30;
            }

            //移動範囲の制限
            this.x = Math.clamp(this.x, 16, SC_W-16);
            this.y = Math.clamp(this.y, 16, SC_H-16);
        }
    },

    //ステージ初期化
    initStage: function() {
    },

    //ステージ再スタート
    restartStage: function() {
    },

    //タッチorクリック開始処理
    onpointstart: function(e) {
    },

    //タッチorクリック移動処理
    onpointmove: function(e) {
    },

    //タッチorクリック終了処理
    onpointend: function(e) {
    },
});
<commit_msg>update main scene<commit_after>/*
 *  MainScene.js
 *  2015/09/08
 *  @auther minimo
 *  This Program is MIT license.
 */

phina.define("pbr.MainScene", {
    superClass: "phina.display.CanvasScene",

    _member: {
        score: 0,

        //再生中BGM
        bgm: null,

        //自機コントロール可能フラグ
        control: false,

        //ラベル用パラメータ
        labelParam: {
            fill: "white",
            stroke: "blue",
            strokeWidth: 2,

            fontFamily: "Orbitron",
            align: "center",
            baseline: "middle",
            fontSize: 32,
            fontWeight: ''
        },
        scorelabelParam: {
            fill: "white",
            stroke: "black",
            strokeWidth: 1,

            fontFamily: "UbuntuMono",
            align: "left",
            baseline: "middle",
            fontSize: 20,
            fontWeight: ''
        },
    },

    init: function() {
        this.superInit();
        this.$extend(this._member);

        //バックグラウンド
        var param = {
            width:SC_W,
            height:SC_H,
            fill: 'black',
            stroke: false,
            backgroundColor: 'transparent',
        };
        this.bg = phina.display.RectangleShape(param)
            .addChildTo(this)
            .setPosition(SC_W*0.5, SC_H*0.5)

        //レイヤー準備
        this.base = phina.display.Layer().addChildTo(this).setPosition(SC_OFFSET_X, 0);
        this.layers = [];
        for (var i = 0; i < LAYER_SYSTEM+1; i++) {
            this.layers[i] = phina.display.Layer().addChildTo(this.base);
        }

        //プレイヤー準備
/*
        this.player = pbr.Player()
            .addChildTo(this)
            .setPosition(PN_OFFX, PN_OFFY);
*/
        //スコア表示
        var that = this;
        this.scoreLabel = phina.display.Label({text:"SCORE:"}.$safe(this.scorelabelParam))
            .addChildTo(this)
            .setPosition(8, 32);
        this.scoreLabel.update = function() {
            this.text = "SCORE "+that.score;
        }

        //目隠し
        this.mask = phina.display.RectangleShape(param)
            .addChildTo(this)
            .setPosition(SC_W*0.5, SC_H*0.5);
        this.mask.alpha = 0;
    },

    update: function(app) {
        if (this.control) {
            var player = this.player;
            //マウス操作
            var p = app.pointing;
            if (p.getPointing()) {
                var pt = this.parentScene.pointer;
                this.x += (pt.x - this.x)/this.touchSpeed;
                this.y += (pt.y - this.y)/this.touchSpeed;

                this.mouseON = true;
                this.shotON = true;
            } else {
                this.mouseON = false;
                this.shotON = false;
            }

            //キーボード操作
            var kb = app.keyboard;
            var angle = kb.getKeyAngle();
            if (angle !== null) {
                var m = KEYBOARD_MOVE[angle];
                this.x += m.x*this.speed;
                this.y += m.y*this.speed;
            }
            if (!this.mouseON) this.shotON = app.keyboard.getKey("Z");

            //ショットタイプ変更（テスト用）
            if (app.keyboard.getKey("X") && this.time > this.changeInterval) {
                this.type = (this.type+1)%3;
                this.openBit(this.type);
                this.changeInterval = this.time+30;
            }

            //移動範囲の制限
            this.x = Math.clamp(this.x, 16, SC_W-16);
            this.y = Math.clamp(this.y, 16, SC_H-16);
        }
    },

    //ステージ初期化
    initStage: function() {
    },

    //ステージ再スタート
    restartStage: function() {
    },

    //タッチorクリック開始処理
    onpointstart: function(e) {
    },

    //タッチorクリック移動処理
    onpointmove: function(e) {
    },

    //タッチorクリック終了処理
    onpointend: function(e) {
    },
});
<|endoftext|>


<commit_before>'use strict';

var blacklist = require('./node_modules/react-native/packager/blacklist');

var config = {
  getProjectRoots() {
    return [__dirname];
  },

  getAssetRoots() {
    // Speficy where to look for assets that are referenced using `image!<image_name>`.
    // Asset directories for images referenced using `./<image.extension>` don't require
    // any entry in here.
    return [];
  },

  getBlacklistRE(platform) {
    return blacklist(platform);
  }
};

module.exports = config;
<commit_msg>Revert "Update rn-cli.config.js"<commit_after>'use strict';

var blacklist = require('./node_modules/react-native/packager/blacklist');

var config = {
  getProjectRoots() {
    return [__dirname];
  },

  getAssetRoots() {
    // speficy where to look for assets
    return [];
  },

  getBlacklistRE(platform) {
    return blacklist(platform);
  }
};

module.exports = config;
<|endoftext|>


<commit_before>/**
 * @function
 * @name ArrayComprehension

 * @description
 *
 */
function ArrayComprehension(options) {
  var generator, start, startIndex, parts, output;

  // if generator is specified we expect the format to be correct
  if (options && options.generator && (generator = options.generator.trim())) {

    parts = generator.split(ArrayComprehension.rangeSymbol);

    if (parts.length > 1) {
      start = parts[0].split(ArrayComprehension.splitSymbol).map(Number);
      startIndex = start.length - 1;

      output = (startIndex ? [start[0]] : []).concat(ArrayComprehension.generateRangeItems(
          start[startIndex],
          Number(parts[1]),
          startIndex ? start[startIndex] - start[0] : null
      ));

    } else {
      output = generator.split(ArrayComprehension.splitSymbol).map(Number);
    }

  } else {
    output = [];
  }

  return output;
}

ArrayComprehension.rangeSymbol = '..';
ArrayComprehension.splitSymbol = ',';

/**
 * @function
 * @name ArrayComprehension.generateRangeItems
 *
 * @param {number} start value of the range
 * @param {number} end value of the range
 * @param {number} [step=1] step for generator
 *
 * @description
 * Generates numbers from start value to end value with specified step
 *
 * @returns {Array}
 */
ArrayComprehension.generateRangeItems = function(start, end, step) {
  step = step || 1;

  return (end < start && step > 0) || (end > start && step < 0) ?
         [] :
         Array.apply(null, new Array(Math.floor((end - start) / step) + 1)).map(function(value, index) {
           return start + step * index;
         });
}
<commit_msg>Update ArrayComprehension.js<commit_after>/**
 * @function
 * @name ArrayComprehension

 * @description
 *
 */
function ArrayComprehension(options) {
  var generator, start, startIndex, parts, output;

  // if generator is specified we expect the format to be correct
  if (options && options.generator && (generator = options.generator.trim())) {

    parts = generator.split(ArrayComprehension.rangeSymbol);

    // look for range notation
    if (parts.length > 1) {
      start = parts[0].split(ArrayComprehension.splitSymbol).map(Number);
      startIndex = start.length - 1;

      output = (startIndex ? [start[0]] : []).concat(ArrayComprehension.generateRangeItems(
          start[startIndex],
          Number(parts[1]),
          startIndex ? start[startIndex] - start[0] : null
      ));

    } else {

      // simple js-array notation
      output = generator.split(ArrayComprehension.splitSymbol).map(Number);
    }

  } else {
    output = [];
  }

  return output;
}

ArrayComprehension.rangeSymbol = '..';
ArrayComprehension.splitSymbol = ',';

/**
 * @function
 * @name ArrayComprehension.generateRangeItems
 *
 * @param {number} start value of the range
 * @param {number} end value of the range
 * @param {number} [step=1] step for generator
 *
 * @description
 * Generates numbers from start value to end value with specified step
 *
 * @returns {Array}
 */
ArrayComprehension.generateRangeItems = function(start, end, step) {
  step = step || 1;

  return (end < start && step > 0) || (end > start && step < 0) ?
         [] :
         Array.apply(null, new Array(Math.floor((end - start) / step) + 1)).map(function(value, index) {
           return start + step * index;
         });
}
<|endoftext|>


<commit_before>/**
*  # Hooks Module
*
*  ## License
*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*
*  ## Description
*
*  This module contains server-side hooks used by etherpad.
*
*  ## Hooks
*/

module.exports = (function () {
  'use strict';

  // Dependencies
  var configuration = require('./configuration.js');
  var padAndAuthor = require('./perm.js').padAndAuthor;

  var hooks = {};

  /**
  * `init` hook is runned once after plugin installation. At the moment, it
  * only populates database.
  */

  hooks.init = function (name, args, callback) {
    configuration.init(function (err) {
      if (err) { console.log(err); }
      callback();
    });
  };

  /**
  * `expressConfigure` hook profits from the args.app express instance to
  * initialize API before YAJSML and then storage and mail.
  */

  hooks.expressConfigure = function (name, args, callback) {
    var storage = require('./storage.js');
    var api = require('./api.js');
    var mail = require('./mail.js');
    api.init(args.app, function () {
      storage.init(function (err) {
        if (err) { return callback(err); }
        mail.init();
        callback(null);
      });
    });
  };

  /**
  * `clientVars` hook will use user login per default for pad `authorName` and
  * profile selected `userColor`. Authenticated user can overwrite default
  * values.
  */

  hooks.clientVars = function (name, context, callback) {
    var opts = padAndAuthor[context.pad.id];
    return callback(opts);
  };

  return hooks;

}).call(this);
<commit_msg>[WIP]Hooks uninstall function for MyPads data<commit_after>/**
*  # Hooks Module
*
*  ## License
*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*
*  ## Description
*
*  This module contains server-side hooks used by etherpad.
*
*  ## Hooks
*/

module.exports = (function () {
  'use strict';

  // Dependencies
  var configuration = require('./configuration.js');
  var padAndAuthor = require('./perm.js').padAndAuthor;

  var hooks = {};

  /**
  * `init` hook is runned once after plugin installation. At the moment, it
  * only populates database.
  */

  hooks.init = function (name, args, callback) {
    configuration.init(function (err) {
      if (err) { console.log(err); }
      callback();
    });
  };

  /**
  * `expressConfigure` hook profits from the args.app express instance to
  * initialize API before YAJSML and then storage and mail.
  */

  hooks.expressConfigure = function (name, args, callback) {
    var storage = require('./storage.js');
    var api = require('./api.js');
    var mail = require('./mail.js');
    api.init(args.app, function () {
      storage.init(function (err) {
        if (err) { return callback(err); }
        mail.init();
        callback(null);
      });
    });
  };

  /**
  * `clientVars` hook will use user login per default for pad `authorName` and
  * profile selected `userColor`. Authenticated user can overwrite default
  * values.
  */

  hooks.clientVars = function (name, context, callback) {
    var opts = padAndAuthor[context.pad.id];
    return callback(opts);
  };

  /**
  * WIP function to use at uninstall of MyPads : erase all MyPads data
  */

  hooks.removeAllData = function () {
    var storage = require('./storage.js');
    storage.db.findKeys(storage.DBPREFIX.GLOBAL + '*', null,
      function (err, keys) {
        console.log('Keys to be removed : ' + keys.join(', '));
        if (err) { throw err; }
        storage.fn.delKeys(keys, function (err) {
          if (err) { throw err; }
          console.log('data successfully removed');
        });
      }
    );
  };

  return hooks;

}).call(this);
<|endoftext|>


<commit_before>// ~ -:- Fancy - foresight physics engine for futuroids -:- ~ +
/*        Copyright 2017 by Andrew Strain. No warranty        *
 *  This program can be redistributed and modified under the  *
 *  terms of the FSF GNU AGPLv3 - see License.md for details  *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** */

/// spotgrav.js -

function addSpotgrav(fig,vplay) {
  'use strict'

  var jote=fig.jote, spot=fig.spot, dlns=fig.dlns
     ,Tau=fig.Tau, Pi=fig.Pi, hPi=fig.hPi, tPi=fig.tPi
     ,Sqrt=fig.Sqrt ,abs=fig.abs ,floor=fig.floor
     ,Drand=fig.Drand ,Hrand=fig.Hrand
     ,rndu=fig.rndu, rndh=fig.rndh

  var epsila=Math.pow(0.5,52)
  var epsilb=Math.pow(0.5,43)

  var _klev=-1,_kcac=[[],[],[],[],[]] //kid lev cache, kid cache
  _kcac[0]=new Array(200),_kcac[1]=new Array(150),_kcac[2]=new Array(100)

  function nextkid(pr,k){
    return (spot.parent[++k]===pr)?k:0
  }

  function _listkids(sb){

    var sd=spot.fchild[sb]; if(!sd){ return 0 }
    var ki=0

    if(++_klev===_kcac.length){ _kcac[_klev] = [0,0,0,0,0,0,0] }

    while(spot.parent[sd]===sb)
    { _kcac[_klev][ki++]=sd++ }

    return ki
  }

  var mingdis

  function grav_spots(p){  //begin recursive gravitation

    squality=5000000*(vplay.gravqual||1)*(vplay.gravqual||1)
    pace=p||vplay.model_pace

    throt=vplay.max_force/abs(pace)

    startwatch('tendspots')
    fig.tendto_spotmap()
    stopwatch('tendspots')

    //~ mtlogcn=mtlogar=logtell=nullfunc
    //logspot=0 //dont dump the spotmap object
    mingdis=16.0
    _klev=-1;
    //~ conlog("ply")
    //~ spot.fchild[1]=0 //testing

    //~ checkspots("pre interply")
    startwatch('gply')
    fig.nbodygrav(p)
    //~ interplyspot(1)
    stopwatch('gply')
    //~ checkspots("aft interply")
    //logtell()

    startwatch('fitspots')
    //~ fig.postfit_spotmap()
    stopwatch('fitspots')

    //~ cologwatch( ['ALLgrav','ply','load','measure'],5 )
  }

  function stub_spots(p){  //begin recursive gravitation

    squality=3000000*(vplay.gravqual||1)*(vplay.gravqual||1)
    pace=p||vplay.model_pace

    throt=vplay.max_force/abs(pace)

    startwatch('tendspots')
    fig.tendto_spotmap()
    stopwatch('tendspots')

    //~ mtlogcn=mtlogar=logtell=nullfunc
    //~ logspot=0 //dont dump the spotmap object

    mingdis=16.0
    _klev=-1;

    //~ spot.fchild[1]=0 //testing
    startwatch('nbody')
    //~ interplyspot(1)
    fig.nbodygrav(p)
    stopwatch('nbody')

    startwatch('fitspots')
    fig.postfit_spotmap()
    stopwatch('fitspots')

    startwatch('press')
    fig.spotclump()
    //~ interplyspot(1)
    stopwatch('press')

    //logtell()

    //~ for(var j=0, je=jote.top ; j<je ; j++ ){
      //~ jote.vx[j]=jote.vy[j]=jote.vz[j]=jote.qx[j]=jote.qy[j]=jote.qz[j]=0
    //~ }

    //~ conlog(spot.top)
    //~ cologwatch( ['tendspots','nbody','fitspots','press'],5 )
  }

  function interplyspot(par){ //inpit
    //mtlogar('assess','inmate spot'+par)
    var kn=_listkids(par)
    //for(var g=0;g<kn;g++) mtlogar('assess','inmt kid'+_kcac[_klev][g])
    if(!kn){
      //~ mtlogcn('inleafed')
      //mtlogar('assess','inmt leaf'+par);
      intermateleaf(par)
      return
    }

    var kids=_kcac[_klev]
    for(var i=0 ; i<kn; i++ )
    { for(var j=i+1 ;j<kn; j++ )
      {
        //mtlogcn('assess1');
        multiply_spots(kids[i],kids[j])
      }
      interplyspot(kids[i])
    }
    _klev-- //kid list cache
  }

  var squality=-1
  function multiply_spots(sa,sb){

    //mtlogar("assess","assessing spots "+sa+" "+sb)

    //~ var calibgrav=0.5, calibsplita=4, calibsplitb=2, calibratio=2

    var idiag=spot.grd[sa]+spot.grd[sb]+0.1
    var tm=spot.grm[sa]+spot.grm[sb]

    if(tm===0){
      //mtlogcn('matelf_spsp',leafsinspot(sa)*leafsinspot(sb))
      return
    }

    var idist   =
     (spot.grx[sa]-spot.grx[sb])*(spot.grx[sa]-spot.grx[sb])
    +(spot.gry[sa]-spot.gry[sb])*(spot.gry[sa]-spot.gry[sb])
    +(spot.grz[sa]-spot.grz[sb])*(spot.grz[sa]-spot.grz[sb])
     //squality
    if( idist>squality*idiag*tm )
    { //mtlogcn('mate_spsp')
      //mtlogar("assess","a- pairing "+sa+"+"+sb);
      //matespots(sa,sb)
      //mtlogcn('matelf_spsp',leafsinspot(sa)*leafsinspot(sb))
      return } //spot v spot
    else
    { //if has leaf node, or small node, or a bit far only split bigger
      var leaf=0,spt=0
      if(!spot.fchild[sa]){ leaf=sa,spt=sb }
      if(!spot.fchild[sb]){ //sb is leaf
        if(leaf){           //sa was leaf
          multimateleafs(sa,sb);
          //mtlogar("assess","a- leafing "+sa+" "+sb);
          //mtlogcn('matelf_lflf');
          return
        }else leaf=sb,spt=sa //sb is leaf, sa wasnt
      }

      if(leaf) //there is a leaf
      {
        //mtlogcn('split_lfsp');
        //leaf or small spot is sc
        //mtlogar("assess","a- split leaf "+leaf)
        for(var kd=spot.fchild[spt]; kd; kd=nextkid(spt,kd) )
        { //mtlogcn('assess2');
          // mtlogar("assess","a- split with "+kd)
          multiply_spots(kd,leaf) }
        //mtlogar("assess","a- ret aft splt leaf")
        return
      }

      //mtlogcn('split_spsp');
      var kn=_listkids(sa),kids=_kcac[_klev]

      for(var kb=spot.fchild[sb]; kb; kb=nextkid(sb,kb) )
      {
        for( var ka=0 ; ka<kn ; ka++  )
        { //mtlogcn('assess3');
          //mtlogar("assess","a- split both "+kb+"&"+kids[ka]);
          multiply_spots(kb,kids[ka]) }
      }
      _klev--
      //mtlogar("assess","a- ret aft splt bth")
      return
    }
  }

  function matespots(p,q){

    _pnt_accel(
     spot.grx[p],spot.gry[p],spot.grz[p],spot.grm[p]
    ,spot.grx[q],spot.gry[q],spot.grz[q],spot.grm[q]
    )
    //~ if(spot.grm[p]>0.01&&spot.grm[q]>0.01) console.log("ov:",p,q,spot.grm[p],spot.grm[q])
    spot.calcx[p]+=_cpx, spot.calcy[p]+=_cpy,spot.calcz[p]+=_cpz
    spot.calcx[q]+=_cqx, spot.calcy[q]+=_cqy,spot.calcz[q]+=_cqz
  }

  function jote_accel(cp,cq){

    var jp=dlns[cp],jq=dlns[cq]
    _pnt_accel(
     jote.x[jp],jote.y[jp],jote.z[jp],jote.g[jp]
    ,jote.x[jq],jote.y[jq],jote.z[jq],jote.g[jq]
    )

   //~ if(isNaN(_cpx)||isNaN(_cpy)||isNaN(_cpz)
    //~ ||isNaN(_cqx)||isNaN(_cqy)||isNaN(_cqz) ){

    //~ console.log("NaNHere: cp",cp,cq,"jp",jp,jq,"jop"
    //~ ,jote.x[jp],jote.y[jp],jote.z[jp],jote.g[jp],"joq"
    //~ ,jote.x[jq],jote.y[jq],jote.z[jq],jote.g[jq])

   //~ }

    jote.qx[jp]+=_cpx, jote.qy[jp]+=_cpy, jote.qz[jp]+=_cpz
   ,jote.qx[jq]+=_cqx, jote.qy[jq]+=_cqy, jote.qz[jq]+=_cqz

  }

  function multimateleafs(p,q){
    squareloop(
      spot.dln_anchor[p], spot.dln_anchor[p]+spot.dln_span[p]
     ,spot.dln_anchor[q], spot.dln_anchor[q]+spot.dln_span[q]
     ,jote_accel
    )
  }

  function intermateleaf(s){
    triangleloop(
      spot.dln_anchor[s], spot.dln_anchor[s]+spot.dln_span[s],jote_accel
    )
  }

  function triangleloop(p,e,fn){ //checked by making 1 spot leaf
    for( var q=p+1 ;q<e ;q=(++q<e)?q:(++p+1) )  //p(p-1)/2
    { fn(p,q) }
  }

  function squareloop(ap,ep,aq,eq,fn){
    for(
      var cp=ap,cq=aq
     ;cp<ep ;cq=(++cq<eq)?cq :(cp++,aq) //(all q by all p)
    )
    { fn(cp,cq) }
  }

  //dumb copy from forces.js

  var pace,throt,_cpx=-1,_cpy=-1,_cpz=-1,_cqx=-1,_cqy=-1,_cqz=-1

  function _pnt_accel(px,py,pz,pg,qx,qy,qz,qg)
  {
    var dx=px-qx, dy=py-qy, dz=pz-qz
    var cf=(dx*dx + dy*dy + dz*dz)
    var hyp=Sqrt(cf)

    //proximity limit mechanism seems resposible
    //for difference in upscale gravity resolution...
    cf=vplay.gravity/cf
    if(cf>throt){ cf= throt*(2-(0.6*(cf-throt)+cf)/cf)  }
    cf=cf*pace/hyp //note fa is no longer sqrt f

    if(qg){ //move p also
      var pf= -cf*qg, qf=cf*pg
      _cpx = dx*pf, _cpy = dy*pf, _cpz = dz*pf
      _cqx = dx*qf, _cqy = dy*qf, _cqz = dz*qf
    }else //move only q
    { cf*=pg, _cqx = dx*cf, _cqy = dy*cf, _cqz = dz*cf
      _cpx=_cpy=_cpz= -0 }
  }

  var _leafcnt=0
  function leafsinspot(s){
    function leafmine(s){ //mine all leafs inside a spot and kids
      var k
      if(!(k= spot.fchild[s])){ _leafcnt++; return }
      for(; k; k=nextkid(s,k) )
      { leafmine(k) }
    }
    _leafcnt=0; leafmine(s)
    return _leafcnt
  }

  // - - - end logger mess

  fig.grav_spots = grav_spots
  fig.stub_spots = stub_spots

  return fig

}<commit_msg>tweak and cruft<commit_after>// ~ -:- Fancy - foresight physics engine for futuroids -:- ~ +
/*        Copyright 2017 by Andrew Strain. No warranty        *
 *  This program can be redistributed and modified under the  *
 *  terms of the FSF GNU AGPLv3 - see License.md for details  *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** */

/// spotgrav.js -

function addSpotgrav(fig,vplay) {
  'use strict'

  var jote=fig.jote, spot=fig.spot, dlns=fig.dlns
     ,Tau=fig.Tau, Pi=fig.Pi, hPi=fig.hPi, tPi=fig.tPi
     ,Sqrt=fig.Sqrt ,abs=fig.abs ,floor=fig.floor
     ,Drand=fig.Drand ,Hrand=fig.Hrand
     ,rndu=fig.rndu, rndh=fig.rndh

  var epsila=Math.pow(0.5,52)
  var epsilb=Math.pow(0.5,43)

  var _klev=-1,_kcac=[[],[],[],[],[]] //kid lev cache, kid cache
  _kcac[0]=new Array(200),_kcac[1]=new Array(150),_kcac[2]=new Array(100)

  function nextkid(pr,k){
    return (spot.parent[++k]===pr)?k:0
  }

  function _listkids(sb){

    var sd=spot.fchild[sb]; if(!sd){ return 0 }
    var ki=0

    if(++_klev===_kcac.length){ _kcac[_klev] = [0,0,0,0,0,0,0] }

    while(spot.parent[sd]===sb)
    { _kcac[_klev][ki++]=sd++ }

    return ki
  }

  var mingdis

  function grav_spots(p){  //begin recursive gravitation

    squality=5000000*(vplay.gravqual||1)*(vplay.gravqual||1)
    pace=p||vplay.model_pace

    throt=vplay.max_force/abs(pace)

    startwatch('tendspots')
    fig.tendto_spotmap()
    stopwatch('tendspots')

    //~ mtlogcn=mtlogar=logtell=nullfunc
    //logspot=0 //dont dump the spotmap object
    mingdis=16.0
    _klev=-1;
    //~ conlog("ply")
    //~ spot.fchild[1]=0 //testing

    //~ checkspots("pre interply")
    startwatch('gply')
    fig.nbodygrav(p)
    //~ interplyspot(1)
    stopwatch('gply')
    //~ checkspots("aft interply")
    //logtell()

    startwatch('fitspots')
    //~ fig.postfit_spotmap()
    stopwatch('fitspots')

    //~ cologwatch( ['ALLgrav','ply','load','measure'],5 )
  }

  function stub_spots(p){  //begin recursive gravitation

    squality=3000000*(vplay.gravqual||1)*(vplay.gravqual||1)
    pace=p||vplay.model_pace

    throt=vplay.max_force/abs(pace)

    startwatch('tendspots')
    fig.tendto_spotmap()
    stopwatch('tendspots')

    //~ mtlogcn=mtlogar=logtell=nullfunc
    //~ logspot=0 //dont dump the spotmap object

    mingdis=16.0
    _klev=-1;

    //~ spot.fchild[1]=0 //testing
    startwatch('nbody')
    //~ interplyspot(1)
    //~ if(vplay.model_clock>5.7)console.log("pregrav",Fgm.Drand.f48().toFixed(5))
    fig.nbodygrav(p)
    //~ if(vplay.model_clock>5.7)console.log("posgrav",Fgm.Drand.f48().toFixed(5))
    stopwatch('nbody')

    //~ startwatch('fitspots')
    //~ fig.postfit_spotmap()
    //~ if(vplay.model_clock>5.7)console.log("preclum",Fgm.Drand.f48().toFixed(5))
    //~ stopwatch('fitspots')

    startwatch('press')
    //~ if(vplay.model_clock>5.7)console.log("preclum",Fgm.Drand.f48().toFixed(5))
    fig.spotclump()
    //~ if(vplay.model_clock>5.7)console.log("posclum",Fgm.Drand.f48().toFixed(5))
    //~ interplyspot(1)
    stopwatch('press')

    //logtell()

    //~ for(var j=0, je=jote.top ; j<je ; j++ ){
      //~ jote.vx[j]=jote.vy[j]=jote.vz[j]=jote.qx[j]=jote.qy[j]=jote.qz[j]=0
    //~ }

    //~ conlog(spot.top)
    //~ cologwatch( ['tendspots','nbody','fitspots','press'],5 )
  }

  function interplyspot(par){ //inpit
    //mtlogar('assess','inmate spot'+par)
    var kn=_listkids(par)
    //for(var g=0;g<kn;g++) mtlogar('assess','inmt kid'+_kcac[_klev][g])
    if(!kn){
      //~ mtlogcn('inleafed')
      //mtlogar('assess','inmt leaf'+par);
      intermateleaf(par)
      return
    }

    var kids=_kcac[_klev]
    for(var i=0 ; i<kn; i++ )
    { for(var j=i+1 ;j<kn; j++ )
      {
        //mtlogcn('assess1');
        multiply_spots(kids[i],kids[j])
      }
      interplyspot(kids[i])
    }
    _klev-- //kid list cache
  }

  var squality=-1
  function multiply_spots(sa,sb){

    //mtlogar("assess","assessing spots "+sa+" "+sb)

    //~ var calibgrav=0.5, calibsplita=4, calibsplitb=2, calibratio=2

    var idiag=spot.grd[sa]+spot.grd[sb]+0.1
    var tm=spot.grm[sa]+spot.grm[sb]

    if(tm===0){
      //mtlogcn('matelf_spsp',leafsinspot(sa)*leafsinspot(sb))
      return
    }

    var idist   =
     (spot.grx[sa]-spot.grx[sb])*(spot.grx[sa]-spot.grx[sb])
    +(spot.gry[sa]-spot.gry[sb])*(spot.gry[sa]-spot.gry[sb])
    +(spot.grz[sa]-spot.grz[sb])*(spot.grz[sa]-spot.grz[sb])
     //squality
    if( idist>squality*idiag*tm )
    { //mtlogcn('mate_spsp')
      //mtlogar("assess","a- pairing "+sa+"+"+sb);
      //matespots(sa,sb)
      //mtlogcn('matelf_spsp',leafsinspot(sa)*leafsinspot(sb))
      return } //spot v spot
    else
    { //if has leaf node, or small node, or a bit far only split bigger
      var leaf=0,spt=0
      if(!spot.fchild[sa]){ leaf=sa,spt=sb }
      if(!spot.fchild[sb]){ //sb is leaf
        if(leaf){           //sa was leaf
          multimateleafs(sa,sb);
          //mtlogar("assess","a- leafing "+sa+" "+sb);
          //mtlogcn('matelf_lflf');
          return
        }else leaf=sb,spt=sa //sb is leaf, sa wasnt
      }

      if(leaf) //there is a leaf
      {
        //mtlogcn('split_lfsp');
        //leaf or small spot is sc
        //mtlogar("assess","a- split leaf "+leaf)
        for(var kd=spot.fchild[spt]; kd; kd=nextkid(spt,kd) )
        { //mtlogcn('assess2');
          // mtlogar("assess","a- split with "+kd)
          multiply_spots(kd,leaf) }
        //mtlogar("assess","a- ret aft splt leaf")
        return
      }

      //mtlogcn('split_spsp');
      var kn=_listkids(sa),kids=_kcac[_klev]

      for(var kb=spot.fchild[sb]; kb; kb=nextkid(sb,kb) )
      {
        for( var ka=0 ; ka<kn ; ka++  )
        { //mtlogcn('assess3');
          //mtlogar("assess","a- split both "+kb+"&"+kids[ka]);
          multiply_spots(kb,kids[ka]) }
      }
      _klev--
      //mtlogar("assess","a- ret aft splt bth")
      return
    }
  }

  function matespots(p,q){

    _pnt_accel(
     spot.grx[p],spot.gry[p],spot.grz[p],spot.grm[p]
    ,spot.grx[q],spot.gry[q],spot.grz[q],spot.grm[q]
    )
    //~ if(spot.grm[p]>0.01&&spot.grm[q]>0.01) console.log("ov:",p,q,spot.grm[p],spot.grm[q])
    spot.calcx[p]+=_cpx, spot.calcy[p]+=_cpy,spot.calcz[p]+=_cpz
    spot.calcx[q]+=_cqx, spot.calcy[q]+=_cqy,spot.calcz[q]+=_cqz
  }

  function jote_accel(cp,cq){

    var jp=dlns[cp],jq=dlns[cq]
    _pnt_accel(
     jote.x[jp],jote.y[jp],jote.z[jp],jote.g[jp]
    ,jote.x[jq],jote.y[jq],jote.z[jq],jote.g[jq]
    )

   //~ if(isNaN(_cpx)||isNaN(_cpy)||isNaN(_cpz)
    //~ ||isNaN(_cqx)||isNaN(_cqy)||isNaN(_cqz) ){

    //~ console.log("NaNHere: cp",cp,cq,"jp",jp,jq,"jop"
    //~ ,jote.x[jp],jote.y[jp],jote.z[jp],jote.g[jp],"joq"
    //~ ,jote.x[jq],jote.y[jq],jote.z[jq],jote.g[jq])

   //~ }

    jote.qx[jp]+=_cpx, jote.qy[jp]+=_cpy, jote.qz[jp]+=_cpz
   ,jote.qx[jq]+=_cqx, jote.qy[jq]+=_cqy, jote.qz[jq]+=_cqz

  }

  function multimateleafs(p,q){
    squareloop(
      spot.dln_anchor[p], spot.dln_anchor[p]+spot.dln_span[p]
     ,spot.dln_anchor[q], spot.dln_anchor[q]+spot.dln_span[q]
     ,jote_accel
    )
  }

  function intermateleaf(s){
    triangleloop(
      spot.dln_anchor[s], spot.dln_anchor[s]+spot.dln_span[s],jote_accel
    )
  }

  function triangleloop(p,e,fn){ //checked by making 1 spot leaf
    for( var q=p+1 ;q<e ;q=(++q<e)?q:(++p+1) )  //p(p-1)/2
    { fn(p,q) }
  }

  function squareloop(ap,ep,aq,eq,fn){
    for(
      var cp=ap,cq=aq
     ;cp<ep ;cq=(++cq<eq)?cq :(cp++,aq) //(all q by all p)
    )
    { fn(cp,cq) }
  }

  //dumb copy from forces.js

  var pace,throt,_cpx=-1,_cpy=-1,_cpz=-1,_cqx=-1,_cqy=-1,_cqz=-1

  function _pnt_accel(px,py,pz,pg,qx,qy,qz,qg)
  {
    var dx=px-qx, dy=py-qy, dz=pz-qz
    var cf=(dx*dx + dy*dy + dz*dz)
    var hyp=Sqrt(cf)

    //proximity limit mechanism seems resposible
    //for difference in upscale gravity resolution...
    cf=vplay.gravity/cf
    if(cf>throt){ cf= throt*(2-(0.6*(cf-throt)+cf)/cf)  }
    cf=cf*pace/hyp //note fa is no longer sqrt f

    if(qg){ //move p also
      var pf= -cf*qg, qf=cf*pg
      _cpx = dx*pf, _cpy = dy*pf, _cpz = dz*pf
      _cqx = dx*qf, _cqy = dy*qf, _cqz = dz*qf
    }else //move only q
    { cf*=pg, _cqx = dx*cf, _cqy = dy*cf, _cqz = dz*cf
      _cpx=_cpy=_cpz= -0 }
  }

  var _leafcnt=0
  function leafsinspot(s){
    function leafmine(s){ //mine all leafs inside a spot and kids
      var k
      if(!(k= spot.fchild[s])){ _leafcnt++; return }
      for(; k; k=nextkid(s,k) )
      { leafmine(k) }
    }
    _leafcnt=0; leafmine(s)
    return _leafcnt
  }

  // - - - end logger mess

  fig.grav_spots = grav_spots
  fig.stub_spots = stub_spots

  return fig

}<|endoftext|>


<commit_before>var fs = require("fs")
  , path = require("path")
  , crypto = require("crypto")
  , Strata = require("..")
  ;

function check (callback, forward) {
  return function (error, result) {
    if (error) callback(error);
    else forward(result);
  }
}

function objectify (directory, callback) {
  var files, segments = {}, count = 0;

  fs.readdir(directory, check(callback, list));

  function list ($1) {
    (files = $1).forEach(function (file) {
      if (!/^\./.test(file)) readFile(file);
      else read();
    });
  }

  function readFile (file) {
    segments[file] = [];

    fs.readFile(path.resolve(directory, file), "utf8", check(callback, lines));

    function lines (lines) {
      lines = lines.split(/\n/);
      lines.pop();
      lines.forEach(function (json) {
        json = json.replace(/[\da-f]+$/, "");
        segments[file].push(JSON.parse(json));
      });
      read();
    }
  }

  function read () {
    if (++count == files.length) callback(null, segments);
  }
}

function stringify (directory, callback) {
  objectify(directory, check(callback, segments));

  function segments (segments) {
    callback(null, JSON.stringify(segments, null, 2));
  }
}

function load (segments, callback) {
  fs.readFile(segments, "utf8", check(callback, parse));

  function parse (json) { callback(null, JSON.parse(json)) }
}

function insert (step, strata, values) {
  step(function () {
    values.sort();
    strata.mutator(values[0], step());
  }, function (cursor) {
    step(function () {
      cursor.insert(values[0], values[0], ~ cursor.index, step());
    }, function () {
      cursor.unlock();
    });
  });
}

function gather (step, strata) {
  var records = [], page, item;
  step(function () {
    records = []
    strata.iterator(step());
  }, function (cursor) {
    step(function () {
      return true;
    }, page = function (more) {
      if (more) return cursor.offset;
      cursor.unlock();
      step(null, records);
    }, item = function (i) {
      if (i < cursor.length) {
        cursor.get(i, step());
        step()(null, i);
      } else {
        step.jump(page);
        cursor.next(step());
      }
    }, function (record, i) {
      records.push(record);
      step.jump(item);
      step()(null, i + 1);
    });
  });
}

function serialize (segments, directory, callback) {
  if (typeof segments == "string") load(segments, check(callback, write));
  else write (segments);

  function write (segments) {
    var files = Object.keys(segments), count = 0;

    files.forEach(function (segment) {
      var records = [];
      segments[segment].forEach(function (line) {
        var record = [ JSON.stringify(line) ];
        record.push(crypto.createHash("sha1").update(record[0]).digest("hex"));
        records.push(record.join(" "));
      });
      records = records.join("\n") + "\n";
      fs.writeFile(path.resolve(directory, segment), records, "utf8", check(callback, written));
    });

    function written () { if (++count == files.length) callback(null) }
  }
}

function deltree (directory, callback) {
  var files, count = 0;

  readdir();

  function readdir () {
    fs.readdir(directory, extant);
  }

  function extant (error, $1) {
    if (error) {
      if (error.code != "ENOENT") callback(error);
      else callback();
    } else {
      list($1);
    }
  }

  function list ($1) {
    (files = $1).forEach(function (file) {
      stat(path.resolve(directory, file));
    });
    deleted();
  }

  function stat (file) {
    var stat;

    fs.stat(file, check(callback, inspect));

    function inspect ($1) {
      if ((stat = $1).isDirectory()) deltree(file, check(callback, unlink));
      else unlink();
    }

    function unlink () {
      if (stat.isDirectory()) fs.rmdir(file, check(callback, deleted));
      else fs.unlink(file, check(callback, deleted));
    }
  }

  function deleted () {
    if (++count > files.length) fs.rmdir(directory, callback);
  }
}

module.exports = function (dirname) {
  var tmp = dirname + "/tmp";
  return require("proof")(function (step) {
    deltree(tmp, step());
  }, function (step) {
    step(function () {
      fs.mkdir(tmp, 0755, step());
    }, function () {
      return { Strata: Strata
             , tmp: tmp
             , load: load
             , stringify: stringify
             , insert: insert
             , serialize: serialize
             , gather: gather
             , objectify: objectify
             };
    });
  });
};
module.exports.stringify = stringify;
<commit_msg>Test file positions using abstract file positions.<commit_after>var fs = require("fs")
  , path = require("path")
  , crypto = require("crypto")
  , Strata = require("..")
  ;

function check (callback, forward) {
  return function (error, result) {
    if (error) callback(error);
    else forward(result);
  }
}

function fixup () {
    var leafy
    return function (json, index) {
        if (!index && Array.isArray(json[json.length - 1])) {
            leafy = true
        }
        if (leafy && !json[0]) {
            var positions = json[json.length - 1]
            var ordered = positions.slice().sort(function (a, b) { return a - b })
            var positions = positions.map(function (pos) {
                return ordered.indexOf(pos)
            })
            json[json.length - 1] = positions
        }
        return json
    }
}

function objectify (directory, callback) {
  var files, segments = {}, count = 0;

  fs.readdir(directory, check(callback, list));

  function list ($1) {
    (files = $1).forEach(function (file) {
      if (!/^\./.test(file)) readFile(file);
      else read();
    });
  }

  function readFile (file) {
    segments[file] = [];

    fs.readFile(path.resolve(directory, file), "utf8", check(callback, lines));

    function lines (lines) {
      var fix = fixup()
      lines = lines.split(/\n/);
      lines.pop();
      lines.forEach(function (json, index) {
        json = json.replace(/[\da-f]+$/, "");
        json = fix(JSON.parse(json), index);
        segments[file].push(json);
      });
      read();
    }
  }

  function read () {
    if (++count == files.length) callback(null, segments);
  }
}

function stringify (directory, callback) {
  objectify(directory, check(callback, segments));

  function segments (segments) {
    callback(null, JSON.stringify(segments, null, 2));
  }
}

function load (segments, callback) {
  var fix = fixup();

  fs.readFile(segments, "utf8", check(callback, parse));

  function parse (json) {
    json = JSON.parse(json);
    for (var file in json) {
        json[file] = json[file].map(fix);
    }
    callback(null, json);
  }
}

function insert (step, strata, values) {
  step(function () {
    values.sort();
    strata.mutator(values[0], step());
  }, function (cursor) {
    step(function () {
      cursor.insert(values[0], values[0], ~ cursor.index, step());
    }, function () {
      cursor.unlock();
    });
  });
}

function gather (step, strata) {
  var records = [], page, item;
  step(function () {
    records = []
    strata.iterator(step());
  }, function (cursor) {
    step(function () {
      return true;
    }, page = function (more) {
      if (more) return cursor.offset;
      cursor.unlock();
      step(null, records);
    }, item = function (i) {
      if (i < cursor.length) {
        cursor.get(i, step());
        step()(null, i);
      } else {
        step.jump(page);
        cursor.next(step());
      }
    }, function (record, i) {
      records.push(record);
      step.jump(item);
      step()(null, i + 1);
    });
  });
}

function serialize (segments, directory, callback) {
  if (typeof segments == "string") load(segments, check(callback, write));
  else write (segments);

  function write (segments) {
    var files = Object.keys(segments), count = 0;

    files.forEach(function (segment) {
      var records = [];
      var leafy = Array.isArray(segments[segment][segments[segment].length - 1]);
      var positions = [];
      var position = 0;
      segments[segment].forEach(function (line) {
        if (leafy)  {
            if (!line[0]) {
                positions = line.pop().map(function (index) {
                    return positions[index]
                });
                line.push(positions.slice())
                positions.length = 0
            } else {
                positions.push(position);
            }
        }
        var record = [ JSON.stringify(line) ];
        record.push(crypto.createHash("sha1").update(record[0]).digest("hex"));
        record = record.join(" ");
        records.push(record);
        position += record.length + 1;
      });
      records = records.join("\n") + "\n";
      fs.writeFile(path.resolve(directory, segment), records, "utf8", check(callback, written));
    });

    function written () { if (++count == files.length) callback(null) }
  }
}

function deltree (directory, callback) {
  var files, count = 0;

  readdir();

  function readdir () {
    fs.readdir(directory, extant);
  }

  function extant (error, $1) {
    if (error) {
      if (error.code != "ENOENT") callback(error);
      else callback();
    } else {
      list($1);
    }
  }

  function list ($1) {
    (files = $1).forEach(function (file) {
      stat(path.resolve(directory, file));
    });
    deleted();
  }

  function stat (file) {
    var stat;

    fs.stat(file, check(callback, inspect));

    function inspect ($1) {
      if ((stat = $1).isDirectory()) deltree(file, check(callback, unlink));
      else unlink();
    }

    function unlink () {
      if (stat.isDirectory()) fs.rmdir(file, check(callback, deleted));
      else fs.unlink(file, check(callback, deleted));
    }
  }

  function deleted () {
    if (++count > files.length) fs.rmdir(directory, callback);
  }
}

module.exports = function (dirname) {
  var tmp = dirname + "/tmp";
  return require("proof")(function (step) {
    deltree(tmp, step());
  }, function (step) {
    step(function () {
      fs.mkdir(tmp, 0755, step());
    }, function () {
      return { Strata: Strata
             , tmp: tmp
             , load: load
             , stringify: stringify
             , insert: insert
             , serialize: serialize
             , gather: gather
             , objectify: objectify
             };
    });
  });
};

module.exports.stringify = stringify;
<|endoftext|>


<commit_before>var MultiArray = require('../../multi-array.js');
var seedrandom = require('seedrandom');
var createRandom = require('pandemonium/random').createRandom;

var DIMENSION = 50000;
var SIZE = 1000000;

console.log('Dimension:', DIMENSION);
console.log('Size:', SIZE);
console.log();

var array = [];
var multiArray = new MultiArray();
var staticMultiArray = new MultiArray(Uint8Array, SIZE);

var i, c, v, r;

var random = createRandom(seedrandom('bench'));

console.time('Array build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  if (typeof array[c] === 'undefined')
    array[c] = [];
  array[c].push(v);
}
console.timeEnd('Array build');

random = createRandom(seedrandom('bench'));

console.time('MultiArray build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  multiArray.set(c, v);
}
console.timeEnd('MultiArray build');

random = createRandom(seedrandom('bench'));

console.time('StaticMultiArray build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  staticMultiArray.set(c, v);
}
console.timeEnd('StaticMultiArray build');

random = createRandom(seedrandom('bench'));

console.log();
console.time('Array get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = array[c];
}
console.timeEnd('Array get');

random = createRandom(seedrandom('bench'));

console.time('MultiArray get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = multiArray.get(c);
}
console.timeEnd('MultiArray get');

random = createRandom(seedrandom('bench'));

console.time('StaticMultiArray get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = staticMultiArray.get(c);
}
console.timeEnd('StaticMultiArray get');
<commit_msg>Bench<commit_after>var MultiArray = require('../../multi-array.js');
var seedrandom = require('seedrandom');
var createRandom = require('pandemonium/random').createRandom;

var DIMENSION = 50000;
var SIZE = 1000000;

console.log('Dimension:', DIMENSION);
console.log('Size:', SIZE);
console.log();

var array = [];
var multiArray = new MultiArray();
var staticMultiArray = new MultiArray(Uint8Array, SIZE);

var i, c, v, r;

var random = createRandom(seedrandom('bench'));

console.time('Array build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  if (typeof array[c] === 'undefined')
    array[c] = [];
  array[c].push(v);
}
console.timeEnd('Array build');

random = createRandom(seedrandom('bench'));

console.time('MultiArray build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  multiArray.set(c, v);
}
console.timeEnd('MultiArray build');

random = createRandom(seedrandom('bench'));

console.time('StaticMultiArray build');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  v = i % 256;
  staticMultiArray.set(c, v);
}
console.timeEnd('StaticMultiArray build');

random = createRandom(seedrandom('bench'));

console.log();
console.time('Array get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = array[c];
}
console.timeEnd('Array get');

random = createRandom(seedrandom('bench'));

console.time('Array get slice');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = array[c].slice();
}
console.timeEnd('Array get slice');

random = createRandom(seedrandom('bench'));

console.time('MultiArray get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = multiArray.get(c);
}
console.timeEnd('MultiArray get');

random = createRandom(seedrandom('bench'));

console.time('StaticMultiArray get');
for (i = 0; i < SIZE; i++) {
  c = random(0, DIMENSION);
  r = staticMultiArray.get(c);
}
console.timeEnd('StaticMultiArray get');
<|endoftext|>


<commit_before>// One Click Revive

var pattern = scan('client.exe', '39 ?? ?? 0F 86 ?? ?? ?? ?? 8B ?? ?? 8B 11');

if (debug)
{
	send(pattern);
}

if (pattern == NULL)
	send('Failed to apply patch.');
else
{
	patch(pattern.add(3), 0x90);
	patch(pattern.add(4), 0xE9);
}
<commit_msg>Update OneClickRevive.js<commit_after>// One Click Revive

var pattern = scan('39 ?? ?? 0F 86 ?? ?? ?? ?? 8B ?? ?? 8B 11');

if (debug)
{
	send(pattern);
}

if (pattern == NULL)
	send('Failed to apply patch.');
else
{
	patch(pattern.add(3), 0x90);
	patch(pattern.add(4), 0xE9);
}
<|endoftext|>


<commit_before>import parseUrl from 'url-parse'
import unique from 'array-unique'
import Rusha from 'rusha'
import clone from 'fast-clone'
import chunk from 'chunk'
import debugging from 'debug'
const sha = (s) => (new Rusha()).digestFromString(s)
const debug = debugging('gw2api-client')
const debugRequest = debugging('gw2api-client:request')

export default class AbstractEndpoint {
  constructor (parent) {
    this.lang = parent.lang
    this.apiKey = parent.apiKey
    this.fetch = parent.fetch
    this.caches = parent.caches

    this.baseUrl = 'https://api.guildwars2.com'
    this.isPaginated = false
    this.maxPageSize = 200
    this.isBulk = false
    this.supportsBulkAll = true
    this.isLocalized = false
    this.isAuthenticated = false
    this.isOptionallyAuthenticated = false

    this._skipCache = false
  }

  // Set the language for locale-aware endpoints
  language (lang) {
    this.lang = lang
    debug(`set the language to ${lang}`)
    return this
  }

  // Set the api key for authenticated endpoints
  authenticate (apiKey) {
    this.apiKey = apiKey
    debug(`set the api key to ${apiKey}`)
    return this
  }

  // Skip caching and get the live data
  live () {
    this._skipCache = true
    debug(`skipping cache`)
    return this
  }

  // Get all ids
  ids () {
    debug(`ids(${this.url}) called`)

    if (!this.isBulk) {
      return Promise.reject(new Error('"ids" is only available for bulk expanding endpoints'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._ids()
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('ids')
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`ids(${this.url}) resolving from cache`)
        return cached
      }

      return this._ids().then(content => {
        this._cacheSetSingle(hash, content)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get all ids from the live API
  _ids () {
    debug(`ids(${this.url}) requesting from api`)
    return this._request(this.url)
  }

  // Get a single entry by id
  get (id, url = false) {
    debug(`get(${this.url}) called`)

    if (!id && this.isBulk && !url) {
      return Promise.reject(new Error('"get" requires an id'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._get(id, url)
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash(id)
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`get(${this.url}) resolving from cache`)
        return cached
      }

      return this._get(id, url).then(content => {
        this._cacheSetSingle(hash, content)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get a single entry by id from the live API
  _get (id, url) {
    debug(`get(${this.url}) requesting from api`)

    // Request the single id if the endpoint a bulk endpoint
    if (this.isBulk && !url) {
      return this._request(`${this.url}?id=${id}`)
    }

    // We are dealing with a custom url instead
    if (url) {
      return this._request(this.url + id)
    }

    // Just request the base url
    return this._request(this.url)
  }

  // Get multiple entries by ids
  many (ids) {
    debug(`many(${this.url}) called (${ids.length} ids)`)

    if (!this.isBulk) {
      return Promise.reject(new Error('"many" is only available for bulk expanding endpoints'))
    }

    // Exit out early if we don't request any ids
    if (ids.length === 0) {
      return Promise.resolve([])
    }

    // Always only work on unique ids, since that's how the API works
    ids = unique.immutable(ids)

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._many(ids)
    }

    // Get as much as possible out of the cache
    const hashes = ids.map(id => this._cacheHash(id))
    const handleCacheContent = (cached) => {
      cached = cached.filter(x => x)

      if (cached.length === ids.length) {
        debug(`many(${this.url}) resolving fully from cache`)
        return cached
      }

      debug(`many(${this.url}) resolving partially from cache (${cached.length} ids)`)
      const missingIds = getMissingIds(ids, cached)
      return this._many(missingIds, cached.length > 0).then(content => {
        const cacheContent = content.map(value => [this._cacheHash(value.id), value])
        this._cacheSetMany(cacheContent)

        // Merge the new content with the cached content and guarantee element order
        content = content.concat(cached)
        return this._sortByIdList(content, ids)
      })
    }

    // Find the ids that are missing in the cached data
    const getMissingIds = (ids, cached) => {
      const cachedIds = {}
      cached.map(x => {
        cachedIds[x.id] = 1
      })

      return ids.filter(x => cachedIds[x] !== 1)
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve([]).then(handleCacheContent)
      : this._cacheGetMany(hashes).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get multiple entries by ids from the live API
  _many (ids, partialRequest = false) {
    debug(`many(${this.url}) requesting from api (${ids.length} ids)`)

    // Chunk the requests to the max page size
    const pages = chunk(ids, this.maxPageSize)
    const requests = pages.map(page => `${this.url}?ids=${page.join(',')}`)

    // If we are partially caching and all not-cached ids are all invalid,
    // simulate the API behaviour by silently swallowing errors.
    let handleMissingIds = (err) => {
      if (partialRequest && err.response && err.response.status === 404) {
        return Promise.resolve([])
      }

      return Promise.reject(err)
    }

    // Work on all requests in parallel and then flatten the responses into one
    return this._requestMany(requests)
      .then(responses => responses.reduce((x, y) => x.concat(y), []))
      .catch(handleMissingIds)
  }

  // Get a single page
  page (page, size = this.maxPageSize) {
    debug(`page(${this.url}) called`)

    if (!this.isBulk && !this.isPaginated) {
      return Promise.reject(new Error('"page" is only available for bulk expanding or paginated endpoints'))
    }

    if (size > this.maxPageSize || size <= 0) {
      return Promise.reject(new Error(`"size" has to be between 0 and ${this.maxPageSize}, was ${size}`))
    }

    if (page < 0) {
      return Promise.reject(new Error('page has to be 0 or greater'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._page(page, size)
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('page-' + page + '/' + size)
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`page(${this.url}) resolving from cache`)
        return cached
      }

      return this._page(page, size).then(content => {
        let cacheContent = [[hash, content]]

        if (this.isBulk) {
          cacheContent = cacheContent.concat(content.map(value => [this._cacheHash(value.id), value]))
        }

        this._cacheSetMany(cacheContent)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get a single page from the live API
  _page (page, size) {
    debug(`page(${this.url}) requesting from api`)
    return this._request(`${this.url}?page=${page}&page_size=${size}`)
  }

  // Get all entries
  all () {
    debug(`all(${this.url}) called`)

    if (!this.isBulk && !this.isPaginated) {
      return Promise.reject(new Error('"all" is only available for bulk expanding or paginated endpoints'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._all()
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('all')
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`all(${this.url}) resolving from cache`)
        return cached
      }

      return this._all().then(content => {
        let cacheContent = [[hash, content]]

        if (this.isBulk) {
          cacheContent = cacheContent.concat(content.map(value => [this._cacheHash(value.id), value]))
        }

        this._cacheSetMany(cacheContent)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get all entries from the live API
  _all () {
    debug(`all(${this.url}) requesting from api`)

    // Use bulk expansion if the endpoint supports the "all" keyword
    if (this.isBulk && this.supportsBulkAll) {
      return this._request(`${this.url}?ids=all`)
    }

    // Get everything via all pages instead
    let totalEntries
    return this._request(`${this.url}?page=0&page_size=${this.maxPageSize}`, 'response')
      .then(firstPage => {
        // Get the total number of entries off the first page's headers
        totalEntries = firstPage.headers.get('X-Result-Total')
        return firstPage.json()
      })
      .then(result => {
        // Return early if the first page already includes all entries
        if (totalEntries <= this.maxPageSize) {
          return result
        }

        // Request all missing pages in parallel
        let requests = []
        for (let page = 1; page < Math.ceil(totalEntries / this.maxPageSize); page++) {
          requests.push(`${this.url}?page=${page}&page_size=${this.maxPageSize}`)
        }

        return this._requestMany(requests).then(responses => {
          responses = responses.reduce((x, y) => x.concat(y), [])
          return result.concat(responses)
        })
      })
  }

  // Set a single cache key in all connected cache storages
  _cacheSetSingle (key, value) {
    this.caches.map(cache => cache.set(key, value, this.cacheTime))
  }

  // Set multiples cache key in all connected cache storages
  _cacheSetMany (values) {
    values = values.map(value => [value[0], value[1], this.cacheTime])
    this.caches.map(cache => cache.mset(values))
  }

  // Get a cached value out of the first possible connected cache storages
  _cacheGetSingle (key, index = 0) {
    return this.caches[index].get(key).then(value => {
      if (value || index === this.caches.length - 1) {
        return value
      }

      return this._cacheGetSingle(key, ++index)
    })
  }

  // Get multiple cached values out of the first possible connected cache storages
  _cacheGetMany (keys, index = 0) {
    return this.caches[index].mget(keys).then(values => {
      const cleanValues = values.filter(x => x)

      // We got all the requested keys or are through all storages
      if (cleanValues.length === keys.length || index === this.caches.length - 1) {
        return values
      }

      // Try to ask the next storage for the keys that we didn't get
      let missingKeys = values
        .map((value, i) => value ? false : keys[i])
        .filter(value => value)

      // Then merge the values of the next storage into the missing slots
      return this._cacheGetMany(missingKeys, ++index).then(missingValues => {
        let i = 0
        return values.map(value => value || missingValues[i++])
      })
    })
  }

  // Get a cache hash for an identifier
  _cacheHash (id) {
    let hash = ''

    if (id) {
      hash += ':' + id
    }

    if (this.isLocalized) {
      hash += ':' + this.lang
    }

    if (this._usesApiKey()) {
      hash += ':' + sha(this.apiKey + '')
    }

    return this.baseUrl + this.url + hash
  }

  // Execute a single request
  _request (url, type = 'json') {
    url = this._buildUrl(url)
    debugRequest(`single url ${url}`)
    return this.fetch.single(url, {type})
  }

  // Execute multiple requests in parallel
  _requestMany (urls, type = 'json') {
    urls = urls.map(url => this._buildUrl(url))
    debugRequest(`multiple urls ${urls.join(', ')}`)
    return this.fetch.many(urls, {type})
  }

  // Build the headers for localization and authentication
  _buildUrl (url) {
    // Add the base url
    url = this.baseUrl + url

    // Parse a possibly existing query
    let parsedUrl = parseUrl(url, true)
    let query = parsedUrl.query

    // Only set the API key for authenticated endpoints,
    // when it is required or optional and set on the client
    if (this._usesApiKey()) {
      query['access_token'] = this.apiKey
    }

    // Set the language for localized endpoints
    if (this.isLocalized) {
      query['lang'] = this.lang
    }

    // Build the new url
    parsedUrl.set('query', query)
    let string = parsedUrl.toString()

    // Clean up the mess by the query parser, and unencode ','
    string = string.replace(/%2C/g, ',')
    return string
  }

  // Guarantee the element order of bulk results
  _sortByIdList (entries, ids) {
    // Hash map of the indexes for better time complexity on big arrays
    let indexMap = {}
    ids.map((x, i) => {
      indexMap[x] = i
    })

    // Sort by the indexes
    entries.sort((a, b) => indexMap[a.id] - indexMap[b.id])
    return entries
  }

  _usesApiKey () {
    return this.isAuthenticated && (!this.isOptionallyAuthenticated || this.apiKey)
  }
}
<commit_msg>Add a flag to toggle credentials for CORS requests for custom enpoints<commit_after>import parseUrl from 'url-parse'
import unique from 'array-unique'
import Rusha from 'rusha'
import clone from 'fast-clone'
import chunk from 'chunk'
import debugging from 'debug'
const sha = (s) => (new Rusha()).digestFromString(s)
const debug = debugging('gw2api-client')
const debugRequest = debugging('gw2api-client:request')

export default class AbstractEndpoint {
  constructor (parent) {
    this.lang = parent.lang
    this.apiKey = parent.apiKey
    this.fetch = parent.fetch
    this.caches = parent.caches

    this.baseUrl = 'https://api.guildwars2.com'
    this.isPaginated = false
    this.maxPageSize = 200
    this.isBulk = false
    this.supportsBulkAll = true
    this.isLocalized = false
    this.isAuthenticated = false
    this.isOptionallyAuthenticated = false
    this.credentials = false

    this._skipCache = false
  }

  // Set the language for locale-aware endpoints
  language (lang) {
    this.lang = lang
    debug(`set the language to ${lang}`)
    return this
  }

  // Set the api key for authenticated endpoints
  authenticate (apiKey) {
    this.apiKey = apiKey
    debug(`set the api key to ${apiKey}`)
    return this
  }

  // Skip caching and get the live data
  live () {
    this._skipCache = true
    debug(`skipping cache`)
    return this
  }

  // Get all ids
  ids () {
    debug(`ids(${this.url}) called`)

    if (!this.isBulk) {
      return Promise.reject(new Error('"ids" is only available for bulk expanding endpoints'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._ids()
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('ids')
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`ids(${this.url}) resolving from cache`)
        return cached
      }

      return this._ids().then(content => {
        this._cacheSetSingle(hash, content)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get all ids from the live API
  _ids () {
    debug(`ids(${this.url}) requesting from api`)
    return this._request(this.url)
  }

  // Get a single entry by id
  get (id, url = false) {
    debug(`get(${this.url}) called`)

    if (!id && this.isBulk && !url) {
      return Promise.reject(new Error('"get" requires an id'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._get(id, url)
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash(id)
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`get(${this.url}) resolving from cache`)
        return cached
      }

      return this._get(id, url).then(content => {
        this._cacheSetSingle(hash, content)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get a single entry by id from the live API
  _get (id, url) {
    debug(`get(${this.url}) requesting from api`)

    // Request the single id if the endpoint a bulk endpoint
    if (this.isBulk && !url) {
      return this._request(`${this.url}?id=${id}`)
    }

    // We are dealing with a custom url instead
    if (url) {
      return this._request(this.url + id)
    }

    // Just request the base url
    return this._request(this.url)
  }

  // Get multiple entries by ids
  many (ids) {
    debug(`many(${this.url}) called (${ids.length} ids)`)

    if (!this.isBulk) {
      return Promise.reject(new Error('"many" is only available for bulk expanding endpoints'))
    }

    // Exit out early if we don't request any ids
    if (ids.length === 0) {
      return Promise.resolve([])
    }

    // Always only work on unique ids, since that's how the API works
    ids = unique.immutable(ids)

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._many(ids)
    }

    // Get as much as possible out of the cache
    const hashes = ids.map(id => this._cacheHash(id))
    const handleCacheContent = (cached) => {
      cached = cached.filter(x => x)

      if (cached.length === ids.length) {
        debug(`many(${this.url}) resolving fully from cache`)
        return cached
      }

      debug(`many(${this.url}) resolving partially from cache (${cached.length} ids)`)
      const missingIds = getMissingIds(ids, cached)
      return this._many(missingIds, cached.length > 0).then(content => {
        const cacheContent = content.map(value => [this._cacheHash(value.id), value])
        this._cacheSetMany(cacheContent)

        // Merge the new content with the cached content and guarantee element order
        content = content.concat(cached)
        return this._sortByIdList(content, ids)
      })
    }

    // Find the ids that are missing in the cached data
    const getMissingIds = (ids, cached) => {
      const cachedIds = {}
      cached.map(x => {
        cachedIds[x.id] = 1
      })

      return ids.filter(x => cachedIds[x] !== 1)
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve([]).then(handleCacheContent)
      : this._cacheGetMany(hashes).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get multiple entries by ids from the live API
  _many (ids, partialRequest = false) {
    debug(`many(${this.url}) requesting from api (${ids.length} ids)`)

    // Chunk the requests to the max page size
    const pages = chunk(ids, this.maxPageSize)
    const requests = pages.map(page => `${this.url}?ids=${page.join(',')}`)

    // If we are partially caching and all not-cached ids are all invalid,
    // simulate the API behaviour by silently swallowing errors.
    let handleMissingIds = (err) => {
      if (partialRequest && err.response && err.response.status === 404) {
        return Promise.resolve([])
      }

      return Promise.reject(err)
    }

    // Work on all requests in parallel and then flatten the responses into one
    return this._requestMany(requests)
      .then(responses => responses.reduce((x, y) => x.concat(y), []))
      .catch(handleMissingIds)
  }

  // Get a single page
  page (page, size = this.maxPageSize) {
    debug(`page(${this.url}) called`)

    if (!this.isBulk && !this.isPaginated) {
      return Promise.reject(new Error('"page" is only available for bulk expanding or paginated endpoints'))
    }

    if (size > this.maxPageSize || size <= 0) {
      return Promise.reject(new Error(`"size" has to be between 0 and ${this.maxPageSize}, was ${size}`))
    }

    if (page < 0) {
      return Promise.reject(new Error('page has to be 0 or greater'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._page(page, size)
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('page-' + page + '/' + size)
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`page(${this.url}) resolving from cache`)
        return cached
      }

      return this._page(page, size).then(content => {
        let cacheContent = [[hash, content]]

        if (this.isBulk) {
          cacheContent = cacheContent.concat(content.map(value => [this._cacheHash(value.id), value]))
        }

        this._cacheSetMany(cacheContent)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get a single page from the live API
  _page (page, size) {
    debug(`page(${this.url}) requesting from api`)
    return this._request(`${this.url}?page=${page}&page_size=${size}`)
  }

  // Get all entries
  all () {
    debug(`all(${this.url}) called`)

    if (!this.isBulk && !this.isPaginated) {
      return Promise.reject(new Error('"all" is only available for bulk expanding or paginated endpoints'))
    }

    // There is no cache time set, so always use the live data
    if (!this.cacheTime) {
      return this._all()
    }

    // Get as much as possible out of the cache
    const hash = this._cacheHash('all')
    const handleCacheContent = (cached) => {
      if (cached) {
        debug(`all(${this.url}) resolving from cache`)
        return cached
      }

      return this._all().then(content => {
        let cacheContent = [[hash, content]]

        if (this.isBulk) {
          cacheContent = cacheContent.concat(content.map(value => [this._cacheHash(value.id), value]))
        }

        this._cacheSetMany(cacheContent)
        return content
      })
    }

    // Get the content either from the cache or API, write it into the cache and return a clone
    const contentPromise = this._skipCache
      ? Promise.resolve(false).then(handleCacheContent)
      : this._cacheGetSingle(hash).then(handleCacheContent)

    return contentPromise.then(clone)
  }

  // Get all entries from the live API
  _all () {
    debug(`all(${this.url}) requesting from api`)

    // Use bulk expansion if the endpoint supports the "all" keyword
    if (this.isBulk && this.supportsBulkAll) {
      return this._request(`${this.url}?ids=all`)
    }

    // Get everything via all pages instead
    let totalEntries
    return this._request(`${this.url}?page=0&page_size=${this.maxPageSize}`, 'response')
      .then(firstPage => {
        // Get the total number of entries off the first page's headers
        totalEntries = firstPage.headers.get('X-Result-Total')
        return firstPage.json()
      })
      .then(result => {
        // Return early if the first page already includes all entries
        if (totalEntries <= this.maxPageSize) {
          return result
        }

        // Request all missing pages in parallel
        let requests = []
        for (let page = 1; page < Math.ceil(totalEntries / this.maxPageSize); page++) {
          requests.push(`${this.url}?page=${page}&page_size=${this.maxPageSize}`)
        }

        return this._requestMany(requests).then(responses => {
          responses = responses.reduce((x, y) => x.concat(y), [])
          return result.concat(responses)
        })
      })
  }

  // Set a single cache key in all connected cache storages
  _cacheSetSingle (key, value) {
    this.caches.map(cache => cache.set(key, value, this.cacheTime))
  }

  // Set multiples cache key in all connected cache storages
  _cacheSetMany (values) {
    values = values.map(value => [value[0], value[1], this.cacheTime])
    this.caches.map(cache => cache.mset(values))
  }

  // Get a cached value out of the first possible connected cache storages
  _cacheGetSingle (key, index = 0) {
    return this.caches[index].get(key).then(value => {
      if (value || index === this.caches.length - 1) {
        return value
      }

      return this._cacheGetSingle(key, ++index)
    })
  }

  // Get multiple cached values out of the first possible connected cache storages
  _cacheGetMany (keys, index = 0) {
    return this.caches[index].mget(keys).then(values => {
      const cleanValues = values.filter(x => x)

      // We got all the requested keys or are through all storages
      if (cleanValues.length === keys.length || index === this.caches.length - 1) {
        return values
      }

      // Try to ask the next storage for the keys that we didn't get
      let missingKeys = values
        .map((value, i) => value ? false : keys[i])
        .filter(value => value)

      // Then merge the values of the next storage into the missing slots
      return this._cacheGetMany(missingKeys, ++index).then(missingValues => {
        let i = 0
        return values.map(value => value || missingValues[i++])
      })
    })
  }

  // Get a cache hash for an identifier
  _cacheHash (id) {
    let hash = ''

    if (id) {
      hash += ':' + id
    }

    if (this.isLocalized) {
      hash += ':' + this.lang
    }

    if (this._usesApiKey()) {
      hash += ':' + sha(this.apiKey + '')
    }

    return this.baseUrl + this.url + hash
  }

  // Execute a single request
  _request (url, type = 'json') {
    url = this._buildUrl(url)
    debugRequest(`single url ${url}`)
    return this.fetch.single(url, {
      type,
      credentials: this.credentials ? 'include' : undefined
    })
  }

  // Execute multiple requests in parallel
  _requestMany (urls, type = 'json') {
    urls = urls.map(url => this._buildUrl(url))
    debugRequest(`multiple urls ${urls.join(', ')}`)
    return this.fetch.many(urls, {
      type,
      credentials: this.credentials ? 'include' : undefined
    })
  }

  // Build the headers for localization and authentication
  _buildUrl (url) {
    // Add the base url
    url = this.baseUrl + url

    // Parse a possibly existing query
    let parsedUrl = parseUrl(url, true)
    let query = parsedUrl.query

    // Only set the API key for authenticated endpoints,
    // when it is required or optional and set on the client
    if (this._usesApiKey()) {
      query['access_token'] = this.apiKey
    }

    // Set the language for localized endpoints
    if (this.isLocalized) {
      query['lang'] = this.lang
    }

    // Build the new url
    parsedUrl.set('query', query)
    let string = parsedUrl.toString()

    // Clean up the mess by the query parser, and unencode ','
    string = string.replace(/%2C/g, ',')
    return string
  }

  // Guarantee the element order of bulk results
  _sortByIdList (entries, ids) {
    // Hash map of the indexes for better time complexity on big arrays
    let indexMap = {}
    ids.map((x, i) => {
      indexMap[x] = i
    })

    // Sort by the indexes
    entries.sort((a, b) => indexMap[a.id] - indexMap[b.id])
    return entries
  }

  _usesApiKey () {
    return this.isAuthenticated && (!this.isOptionallyAuthenticated || this.apiKey)
  }
}
<|endoftext|>