<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.util.concurrent',
  name: 'AbstractAssembly',
  //abstract: true,
  // TODO?: Could provide a reset() method if wanted to make reusable to avoid GC.

  /**
   * Abstract implementation of Assembly interface.
   * Provides concurrency support.
   **/

  implements: [
    'foam.util.concurrent.Assembly'
  ],

  properties: [
    {
      name: 'isCompleted',
      class: 'Boolean',
      synchronized: true,
      value: false,
      documentation: 'True once endJob() has executed.'
    },
    {
      name: 'last',
      class: 'Boolean',
      synchronized: true,
      value: true,
      documentation: 'True if no new Assembly is waiting for this task to complete.'
    },
  ],

  methods: [
    {
      name: 'requestLocks',
      javaCode: `
        // Template method, override in subclass if desired
        return null;
      `
    },
    {
      name: 'executeUnderLock',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'startJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'executeJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'endJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'isLast',
      type: 'Boolean',
      synchronized: true,
      javaCode: `
        return getLast();
      `
    },
    {
      name: 'markNotLast',
      type: 'void',
      synchronized: true,
      javaCode: `
        setLast(false);
      `
    },
    {
      name: 'complete',
      synchronized: true,
      javaCode: `
         setIsCompleted(true);
         notify();
      `
    },
    {
      name: 'waitToComplete',
      synchronized: true,
      javaCode: `
         while ( ! getIsCompleted() ) {
           try {
             wait();
           } catch (InterruptedException e) {
             // NOP
             Thread.currentThread().interrupt();
           }
         }
      `
    }
  ]

})
<commit_msg>Formatting.<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.util.concurrent',
  name: 'AbstractAssembly',
  //abstract: true,
  // TODO?: Could provide a reset() method if wanted to make reusable to avoid GC.

  /**
   * Abstract implementation of Assembly interface.
   * Provides concurrency support.
   **/

  implements: [
    'foam.util.concurrent.Assembly'
  ],

  properties: [
    {
      name: 'isCompleted',
      class: 'Boolean',
      synchronized: true,
      value: false,
      documentation: 'True once endJob() has executed.'
    },
    {
      name: 'last',
      class: 'Boolean',
      synchronized: true,
      value: true,
      documentation: 'True if no new Assembly is waiting for this task to complete.'
    }
  ],

  methods: [
    {
      name: 'requestLocks',
      javaCode: `
        // Template method, override in subclass if desired
        return null;
      `
    },
    {
      name: 'executeUnderLock',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'startJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'executeJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'endJob',
      javaCode: `
        // Template method, override in subclass if desired
      `
    },
    {
      name: 'isLast',
      type: 'Boolean',
      synchronized: true,
      javaCode: `
        return getLast();
      `
    },
    {
      name: 'markNotLast',
      type: 'void',
      synchronized: true,
      javaCode: `
        setLast(false);
      `
    },
    {
      name: 'complete',
      synchronized: true,
      javaCode: `
         setIsCompleted(true);
         notify();
      `
    },
    {
      name: 'waitToComplete',
      synchronized: true,
      javaCode: `
         while ( ! getIsCompleted() ) {
           try {
             wait();
           } catch (InterruptedException e) {
             // NOP
             Thread.currentThread().interrupt();
           }
         }
      `
    }
  ]
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  var path = require('path');
  var fs = require('fs');
  var amberc = require('../../bin/amberc.js');

  /**
     Full config looks like this:
     amberc: {
       _config: {
         amber_dir: process.cwd(),     // REQUIRED
         closure_jar: ''               // optional
       },
       helloWorld: {
         src: ['HelloWorld.st'],                // REQUIRED
         working_dir: 'projects/HelloWorld/st', // optional
         target_dir: 'projects/HelloWorld/js',  // optional
         main_class: 'HelloWorld',              // optional
         output_name: 'helloWorld',            // optional
         libraries: 'Canvas',                  // optional
         init: 'myInit',                       // optional
         main_file: 'myMain.js',               // optional
         deploy: true,                         // optional
         output_suffix: 'mySuffix',            // optional
         library_suffix: '-0.9',               // optional
       },
     },

   */
  grunt.registerMultiTask('amberc', 'Compile Smalltalk files with the amberc compiler', function() {
    // mark required properties
    this.requiresConfig('amberc._config.amber_dir');
    this.requiresConfig(['amberc', this.target, 'src']);

    // change working directory if the working_dir property is set on the target
    var current_dir = process.cwd();
    var working_dir = this.data.working_dir;
    if (undefined !== working_dir) {
      grunt.file.setBase(working_dir);
    }

    // mark task as async task
    var done = this.async();

    // create and initialize amberc
    var compiler = new amberc.Compiler(grunt.config('amberc._config.amber_dir'), grunt.config('amberc._config.closure_jar'));

    // generate the amberc configuration out of the given target properties
    var configuration = generateCompilerConfiguration(this.data, grunt.config('amberc._config.amber_dir'));

    // run the compiler
    // change back to the old working directory and call the async callback once finished
    var self = this;
    compiler.main(configuration, function(){
      if (undefined !== self.data.target_dir) {
        var absolute_target_dir = path.join(current_dir, self.data.target_dir);
        replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir);
        // if deploy is set also copy the deploy files
        if (self.data.deploy) {
          var suffix = self.data.output_suffix || 'deploy';
          suffix = '.' + suffix + '.js';
          replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir, suffix);
        }
      }
      // reset working directory
      grunt.file.setBase(current_dir);
      // signal that task has finished
      done();
    });
  });

  function generateCompilerConfiguration(data, amber_dir) {
    var configuration = amberc.createDefaults(amber_dir);
    var parameters = [];

    var libraries = data.libraries;
    if (undefined !== libraries) {
      configuration.load = libraries;
    }
    var initFile = data.init;
    if (undefined !== initFile) {
      configuration.init = initFile;
    }
    var mainClass = data.main_class;
    if (undefined !== mainClass) {
      configuration.main = mainClass;
    }
    var mainFile = data.main_file;
    if (undefined !== initFile) {
      configuration.mainfile = mainFile;
    }
    if (true === data.deploy) {
      configuration.deploy = true;
    }
    var outputSuffix = data.output_suffix;
    if (undefined !== outputSuffix) {
      configuration.suffix = outputSuffix;
      configuration.suffix_used = outputSuffix;
    }
    var librarySuffix = data.library_suffix;
    if (undefined !== librarySuffix) {
      configuration.loadsuffix = librarySuffix;
      configuration.suffix_used = librarySuffix;
    }
    var sourceFiles = data.src;
    if (undefined !== sourceFiles) {
      sourceFiles.forEach(function(currentItem){
        var fileSuffix = path.extname(currentItem);
        switch (fileSuffix) {
          case '.st':
            configuration.stFiles.push(currentItem);
          break;
          case '.js':
            configuration.jsFiles.push(currentItem);
          break;
        }
      });
    }
    var outputName = data.output_name;
    if (undefined !== outputName) {
      configuration.program = outputName;
    }
    return configuration;
  }

  /**
   * Replace '.st' suffix of \p files with '.js' or with \p replace_suffix if this parameter is given.
   * Afterwards move the files with replaced suffix to \p target_dir if the files exist.
   */
  function replaceFileSuffix_moveToTargetDir(files, target_dir, replace_suffix) {
    var suffix = replace_suffix || '.js';
    var compiledFiles = files.map(function(item) {
      return item.replace(/.st$/g, suffix);
    });

    compiledFiles.forEach(function(file) {
      if (path.existsSync(file)) {
        console.log('Move: ' + file + ' -> ' + path.join(target_dir, file));
        fs.renameSync(file, path.join(target_dir, file));
      }
    });
  }
};
<commit_msg>amberc-grunt.js: make Node.js 0.8 compatible<commit_after>module.exports = function(grunt) {

  var path = require('path');
  var fs = require('fs');
  var amberc = require('../../bin/amberc.js');

  /**
     Full config looks like this:
     amberc: {
       _config: {
         amber_dir: process.cwd(),     // REQUIRED
         closure_jar: ''               // optional
       },
       helloWorld: {
         src: ['HelloWorld.st'],                // REQUIRED
         working_dir: 'projects/HelloWorld/st', // optional
         target_dir: 'projects/HelloWorld/js',  // optional
         main_class: 'HelloWorld',              // optional
         output_name: 'helloWorld',            // optional
         libraries: 'Canvas',                  // optional
         init: 'myInit',                       // optional
         main_file: 'myMain.js',               // optional
         deploy: true,                         // optional
         output_suffix: 'mySuffix',            // optional
         library_suffix: '-0.9',               // optional
       },
     },

   */
  grunt.registerMultiTask('amberc', 'Compile Smalltalk files with the amberc compiler', function() {
    // mark required properties
    this.requiresConfig('amberc._config.amber_dir');
    this.requiresConfig(['amberc', this.target, 'src']);

    // change working directory if the working_dir property is set on the target
    var current_dir = process.cwd();
    var working_dir = this.data.working_dir;
    if (undefined !== working_dir) {
      grunt.file.setBase(working_dir);
    }

    // mark task as async task
    var done = this.async();

    // create and initialize amberc
    var compiler = new amberc.Compiler(grunt.config('amberc._config.amber_dir'), grunt.config('amberc._config.closure_jar'));

    // generate the amberc configuration out of the given target properties
    var configuration = generateCompilerConfiguration(this.data, grunt.config('amberc._config.amber_dir'));

    // run the compiler
    // change back to the old working directory and call the async callback once finished
    var self = this;
    compiler.main(configuration, function(){
      if (undefined !== self.data.target_dir) {
        var absolute_target_dir = path.join(current_dir, self.data.target_dir);
        replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir);
        // if deploy is set also copy the deploy files
        if (self.data.deploy) {
          var suffix = self.data.output_suffix || 'deploy';
          suffix = '.' + suffix + '.js';
          replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir, suffix);
        }
      }
      // reset working directory
      grunt.file.setBase(current_dir);
      // signal that task has finished
      done();
    });
  });

  function generateCompilerConfiguration(data, amber_dir) {
    var configuration = amberc.createDefaults(amber_dir);
    var parameters = [];

    var libraries = data.libraries;
    if (undefined !== libraries) {
      configuration.load = libraries;
    }
    var initFile = data.init;
    if (undefined !== initFile) {
      configuration.init = initFile;
    }
    var mainClass = data.main_class;
    if (undefined !== mainClass) {
      configuration.main = mainClass;
    }
    var mainFile = data.main_file;
    if (undefined !== initFile) {
      configuration.mainfile = mainFile;
    }
    if (true === data.deploy) {
      configuration.deploy = true;
    }
    var outputSuffix = data.output_suffix;
    if (undefined !== outputSuffix) {
      configuration.suffix = outputSuffix;
      configuration.suffix_used = outputSuffix;
    }
    var librarySuffix = data.library_suffix;
    if (undefined !== librarySuffix) {
      configuration.loadsuffix = librarySuffix;
      configuration.suffix_used = librarySuffix;
    }
    var sourceFiles = data.src;
    if (undefined !== sourceFiles) {
      sourceFiles.forEach(function(currentItem){
        var fileSuffix = path.extname(currentItem);
        switch (fileSuffix) {
          case '.st':
            configuration.stFiles.push(currentItem);
          break;
          case '.js':
            configuration.jsFiles.push(currentItem);
          break;
        }
      });
    }
    var outputName = data.output_name;
    if (undefined !== outputName) {
      configuration.program = outputName;
    }
    return configuration;
  }

  /**
   * Replace '.st' suffix of \p files with '.js' or with \p replace_suffix if this parameter is given.
   * Afterwards move the files with replaced suffix to \p target_dir if the files exist.
   */
  function replaceFileSuffix_moveToTargetDir(files, target_dir, replace_suffix) {
    var suffix = replace_suffix || '.js';
    var compiledFiles = files.map(function(item) {
      return item.replace(/.st$/g, suffix);
    });

    compiledFiles.forEach(function(file) {
      if (fs.existsSync(file)) {
        console.log('Move: ' + file + ' -> ' + path.join(target_dir, file));
        fs.renameSync(file, path.join(target_dir, file));
      }
    });
  }
};
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }
  get attributes() { return {'width': '100%', 'height': '100%'} } // why we need this?
  render() { return this }

  constructor(options) {
    super(options)
    this.setupHlsJs()
    this.minDvrSize = options.hlsMinimumDvrSize ? options.hlsMinimumDvrSize : 60
    this.playbackType = 'vod'
    this.dvrInUse = false
  }

  setupHlsJs() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MSE_ATTACHED, () => this.hls.loadSource(this.options.source))
    this.hls.on(HLSJS.Events.MANIFEST_PARSED, () => { this.options.autoPlay && this.play() })
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.attachVideo(this.el)
  }

  seek(seekBarValue) {
    var seekTo = (seekBarValue === -1 )? 0 : seekBarValue
    super.seek(seekTo)
    this.updateDvr(!!seekTo)
  }

  updateDvr(status) {
    this.dvrInUse = status
    this.trigger(Events.PLAYBACK_DVR, this.dvrInUse)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': this.dvrInUse})
  }

  durationChange() {
    if (this.playbackType === "vod") {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  timeUpdated() {
    if (this.dvrEnabled) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, this.getCurrentTime(), this.getDuration(), this.name)
    } else {
      super.timeUpdated()
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? 'live' : 'vod'
  }

  get dvrEnabled() {
    return (this.getDuration() >= this.minDvrSize && this.getPlaybackType() === 'live')
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === 'vod' || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = !!(resourceParts.length > 1 && resourceParts[1] === 'm3u8')
  return !!(HLSJS.isSupported() && isHls)
}
<commit_msg>hls.js: fixup media control for vod<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }
  get attributes() { return {'width': '100%', 'height': '100%'} } // why we need this?
  render() { return this }

  constructor(options) {
    super(options)
    this.setupHlsJs()
    this.minDvrSize = options.hlsMinimumDvrSize ? options.hlsMinimumDvrSize : 60
    this.playbackType = 'vod'
    this.dvrInUse = false
  }

  setupHlsJs() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MSE_ATTACHED, () => this.hls.loadSource(this.options.source))
    this.hls.on(HLSJS.Events.MANIFEST_PARSED, () => { this.options.autoPlay && this.play() })
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.attachVideo(this.el)
  }

  seek(seekBarValue) {
    var seekTo = (seekBarValue === -1 )? 0 : seekBarValue
    super.seek(seekTo)
    if (this.dvrEnabled) {
        this.updateDvr(!!seekTo)
    }
  }

  updateDvr(status) {
    this.dvrInUse = status
    this.trigger(Events.PLAYBACK_DVR, this.dvrInUse)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': this.dvrInUse})
  }

  durationChange() {
    if (this.playbackType === "vod") {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  timeUpdated() {
    if (this.dvrEnabled) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, this.getCurrentTime(), this.getDuration(), this.name)
    } else {
      super.timeUpdated()
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? 'live' : 'vod'
  }

  get dvrEnabled() {
    return (this.getDuration() >= this.minDvrSize && this.getPlaybackType() === 'live')
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === 'vod' || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = !!(resourceParts.length > 1 && resourceParts[1] === 'm3u8')
  return !!(HLSJS.isSupported() && isHls)
}
<|endoftext|>


<commit_before>var Graph = (function (undefined) {

	var extractKeys = function (obj) {
		var keys = [], key;
		for (key in obj) {
		    Object.prototype.hasOwnProperty.call(obj,key) && keys.push(key);
		}
		return keys;
	}

	var sorter = function (a, b) {
		return parseFloat (a) - parseFloat (b);
	}

	var findPaths = function (map, start, end, infinity) {
		infinity = infinity || Infinity;

		var costs = {},
		    open = {'0': [start]},
		    predecessors = {},
		    keys;

		var addToOpen = function (cost, vertex) {
			var key = "" + cost;
			if (!open[key]) open[key] = [];
			open[key].push(vertex);
		}

		costs[start] = 0;

		while (open) {
			if(!(keys = extractKeys(open)).length) break;

			keys.sort(sorter);

			var key = keys[0],
			    bucket = open[key],
			    node = bucket.shift(),
			    currentCost = parseFloat(key),
			    adjacentNodes = map[node] || {};

			if (!bucket.length) delete open[key];

			for (var vertex in adjacentNodes) {
			    if (Object.prototype.hasOwnProperty.call(adjacentNodes, vertex)) {
					var cost = adjacentNodes[vertex],
					    totalCost = cost + currentCost,
					    vertexCost = costs[vertex];

					if ((vertexCost === undefined) || (vertexCost > totalCost)) {
						costs[vertex] = totalCost;
						addToOpen(totalCost, vertex);
						predecessors[vertex] = node;
					}
				}
			}
		}

		if (costs[end] === undefined) {
			return null;
		} else {
			return predecessors;
		}

	}

	var extractShortest = function (predecessors, end) {
		var nodes = [],
		    u = end;

		while (u) {
			nodes.push(u);
			u = predecessors[u];
		}

		nodes.reverse();
		return nodes;
	}

	var findShortestPath = function (map, nodes) {
		var start = nodes.shift(),
		    end,
		    predecessors,
		    path = [],
		    shortest;

		while (nodes.length) {
			end = nodes.shift();
			predecessors = findPaths(map, start, end);

			if (predecessors) {
				shortest = extractShortest(predecessors, end);
				if (nodes.length) {
					path.push.apply(path, shortest.slice(0, -1));
				} else {
					return path.concat(shortest);
				}
			} else {
				return null;
			}

			start = end;
		}
	}

	var toArray = function (list, offset) {
		try {
			return Array.prototype.slice.call(list, offset);
		} catch (e) {
			var a = [];
			for (var i = offset || 0, l = list.length; i < l; ++i) {
				a.push(list[i]);
			}
			return a;
		}
	}

	var Graph = function (map) {
		this.map = map;
	}

	Graph.prototype.findShortestPath = function (start, end) {
		if (Object.prototype.toString.call(start) === '[object Array]') {
			return findShortestPath(this.map, start);
		} else if (arguments.length === 2) {
			return findShortestPath(this.map, [start, end]);
		} else {
			return findShortestPath(this.map, toArray(arguments));
		}
	}

	Graph.findShortestPath = function (map, start, end) {
		if (Object.prototype.toString.call(start) === '[object Array]') {
			return findShortestPath(map, start);
		} else if (arguments.length === 3) {
			return findShortestPath(map, [start, end]);
		} else {
			return findShortestPath(map, toArray(arguments, 1));
		}
	}

	return Graph;

})();
<commit_msg>Handle Case Where Map Has Node '0'<commit_after>var Graph = (function (undefined) {

	var extractKeys = function (obj) {
		var keys = [], key;
		for (key in obj) {
		    Object.prototype.hasOwnProperty.call(obj,key) && keys.push(key);
		}
		return keys;
	}

	var sorter = function (a, b) {
		return parseFloat (a) - parseFloat (b);
	}

	var findPaths = function (map, start, end, infinity) {
		infinity = infinity || Infinity;

		var costs = {},
		    open = {'0': [start]},
		    predecessors = {},
		    keys;

		var addToOpen = function (cost, vertex) {
			var key = "" + cost;
			if (!open[key]) open[key] = [];
			open[key].push(vertex);
		}

		costs[start] = 0;

		while (open) {
			if(!(keys = extractKeys(open)).length) break;

			keys.sort(sorter);

			var key = keys[0],
			    bucket = open[key],
			    node = bucket.shift(),
			    currentCost = parseFloat(key),
			    adjacentNodes = map[node] || {};

			if (!bucket.length) delete open[key];

			for (var vertex in adjacentNodes) {
			    if (Object.prototype.hasOwnProperty.call(adjacentNodes, vertex)) {
					var cost = adjacentNodes[vertex],
					    totalCost = cost + currentCost,
					    vertexCost = costs[vertex];

					if ((vertexCost === undefined) || (vertexCost > totalCost)) {
						costs[vertex] = totalCost;
						addToOpen(totalCost, vertex);
						predecessors[vertex] = node;
					}
				}
			}
		}

		if (costs[end] === undefined) {
			return null;
		} else {
			return predecessors;
		}

	}

	var extractShortest = function (predecessors, end) {
		var nodes = [],
		    u = end;

		while (u !== undefined) {
			nodes.push(u);
			u = predecessors[u];
		}

		nodes.reverse();
		return nodes;
	}

	var findShortestPath = function (map, nodes) {
		var start = nodes.shift(),
		    end,
		    predecessors,
		    path = [],
		    shortest;

		while (nodes.length) {
			end = nodes.shift();
			predecessors = findPaths(map, start, end);

			if (predecessors) {
				shortest = extractShortest(predecessors, end);
				if (nodes.length) {
					path.push.apply(path, shortest.slice(0, -1));
				} else {
					return path.concat(shortest);
				}
			} else {
				return null;
			}

			start = end;
		}
	}

	var toArray = function (list, offset) {
		try {
			return Array.prototype.slice.call(list, offset);
		} catch (e) {
			var a = [];
			for (var i = offset || 0, l = list.length; i < l; ++i) {
				a.push(list[i]);
			}
			return a;
		}
	}

	var Graph = function (map) {
		this.map = map;
	}

	Graph.prototype.findShortestPath = function (start, end) {
		if (Object.prototype.toString.call(start) === '[object Array]') {
			return findShortestPath(this.map, start);
		} else if (arguments.length === 2) {
			return findShortestPath(this.map, [start, end]);
		} else {
			return findShortestPath(this.map, toArray(arguments));
		}
	}

	Graph.findShortestPath = function (map, start, end) {
		if (Object.prototype.toString.call(start) === '[object Array]') {
			return findShortestPath(map, start);
		} else if (arguments.length === 3) {
			return findShortestPath(map, [start, end]);
		} else {
			return findShortestPath(map, toArray(arguments, 1));
		}
	}

	return Graph;

})();
<|endoftext|>


<commit_before>/* global exports */
"use strict";

// module Debug.Profile

const _timers = {};

exports.timerStart = function () {
  return process.hrtime();
}

exports.timerEnd = function (start) {
  return function () {
    var hrTime = process.hrtime(start);
    return hrTime[0] * 1000 + hrTime[1] / 1000000
  };
}
<commit_msg>Use "var" keyword in FFI module<commit_after>/* global exports */
"use strict";

// module Debug.Profile

var _timers = {};

exports.timerStart = function () {
  return process.hrtime();
}

exports.timerEnd = function (start) {
  return function () {
    var hrTime = process.hrtime(start);
    return hrTime[0] * 1000 + hrTime[1] / 1000000
  };
}
<|endoftext|>


<commit_before>/*global PDFJS:false*/
/*eslint-env browser*/
/*eslint-env es6*/
!function PDFViewClosure() {
    'use strict';
    var page_gap = 10;
    var xform = 'transform';
    ['', 'webkit', 'Moz', 'O', 'ms'].every(function (prefix) {
        var e = prefix + (prefix ? 'T' : 't') + 'ransform';
        if (typeof document.body.style[e] !== 'undefined') {
            xform = e;
            return false;
        }
        return true;
    });

    function getPixelRatio() {
        //On mobile devices this is the ratio css pixel height / device pixel height, whilst at 1.0 scale
        //On desktop devices this is typically the current scale.
        var dpr = window.devicePixelRatio || 1;
        //On mobile devices this adequately measures scale on a page using viewport width=device-width.
        //On desktop devices it is 1 (or slightly less to compensate for a scroll bar.)
        var scale = (document.documentElement.clientWidth / window.innerWidth) || 1;
        scale = scale < 1 ? 1 : scale;
        return dpr * scale;
    }

    function getOutputScale(ctx) {
        var canvas;
        if (ctx === undefined) {
            if (canvas === undefined) {
                canvas = document.createElement('canvas');
            }
            ctx = canvas.getContext('2d');
        }
        var devicePixelRatio = getPixelRatio();
        var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
            ctx.mozBackingStorePixelRatio ||
            ctx.msBackingStorePixelRatio ||
            ctx.oBackingStorePixelRatio ||
            ctx.backingStorePixelRatio || 1; //backing store ratio is deprecated and will likely always be 1.
        var pixelRatio = devicePixelRatio / backingStoreRatio;
        return {
            sx: pixelRatio,
            sy: pixelRatio,
            scaled: pixelRatio !== 1
        };
    }
    /*
    !function () {
        PDFJS.getDocument('1609.01714.pdf').then(function (pdf) {
            var pageDivs = [];
            function createPage(n) {
                if (n > pdf.numPages) {
                    return;
                }
                var div = document.createElement('div');
                pageDivs.push(div);
                var promise = new Promise(function (resolve, reject) {

                    pdf.getPage(n).then(function (page) {
                        var scale = 1;
                        var viewport = page.getViewport(scale * CSS_UNITS);

                        var canvas = document.createElement('canvas');
                        var context = canvas.getContext('2d');

                        var outputScale = getOutputScale(canvas);
                        if (PDFJS.maxCanvasPixels > 0) {
                            var pixelsInViewport = viewport.width * viewport.height;
                            var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
                            if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
                                outputScale.sx = maxScale;
                                outputScale.sy = maxScale;
                                outputScale.scaled = true;
                                var hasRestrictedScaling = true;//TODO: use this to css transform element larger.
                            } else {
                                hasRestrictedScaling = false;
                            }
                        }

                        canvas.height = viewport.height * outputScale.sy;
                        canvas.width = viewport.width * outputScale.sx;
                        canvas.style.height = viewport.height;
                        canvas.style.width = viewport.width;

                        div.appendChild(canvas);
                        var transform = !outputScale.scaled ? null :
                            [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
                        page.render({
                            canvasContext: context,
                            transform: transform,
                            viewport: viewport
                        }).then(resolve(), reject());
                    });
                });
                return promise;
            }
            createPage(1).then(function () {
                return createPage(2);
            }).then(function () {
                pageDivs.forEach(function (div) {
                    document.body.appendChild(div);
                });
            });
        });
    } ();
    */
/*
    window.PdfViewController = function PdfViewController(pdfViewPromise) {
        var pdfView;
        pdfViewPromise.then(function (pdfViewModel) {
            pdfView = pdfViewModel;
            var kinetic = window.kinetic({
                listeners: [xposChanged],
                restrictFn: function (x) {
                    if (x < 0) {
                        return 0;
                    }
                    var finalPage = pdfView.pages[pdfView.pages.length];
                    var max = finalPage.baseTotalPrevPagesWidth
                        + finalPage.baseSize.w
                        + (pdfView.pages.length - 1) * page_gap
                        - pdfView.container.clientWidth;
                    if (x > max) {
                        return max;
                    }
                    return x;
                }
            });
        });

        function xposChanged(pos) {
            pdfView.position({ x: pos });
        }

        var prevPanDelta = { x: 0, y: 0 };
        function onPan(ev) {

        }
    };
*/
    window.PdfView = function PdfView(container, pdfLocation) {

        var pdfViewModel = {
            pages: [],
            scale: scale,
            position: position,
            container: container,
        };
        var _scale = 1;
        var _position = { x: 0, y: 0 };

        container.classList.add('pdfview-container');
        var finalPageInitialisedResolve;
        var promise = new Promise(function (resolve) {
            finalPageInitialisedResolve = resolve;
        });
        PDFJS.getDocument(pdfLocation).then(function (pdf) {
            var totalWidth = 0;
            function initPage(pageNum) {
                if (pageNum === pdf.numPages){
                    window.addEventListener('resize', render);
                    render();
                    finalPageInitialisedResolve(pdfViewModel);
                    return;
                }
                pdf.getPage(pageNum+1).then(function (page) {
                    var pageView = PdfPageView(page);
                    pageView.pageNo = pageNum;
                    pageView.baseTotalPrevPagesWidth = totalWidth;
                    totalWidth += pageView.baseSize.w;
                    pdfViewModel.pages.push(pageView);
                    pdfViewModel.container.appendChild(pageView.div);
                    initPage(pageNum + 1);
                });
            }
            initPage(0);
        });

        function render() {
            var pages = pdfViewModel.pages;
            var scale = _scale;
            pages.forEach(function (page) {
                page.scale(scale);
                var index = page.pageNo;
                var x = scale * (page.baseTotalPrevPagesWidth + index * page_gap - _position.x);
                var y = scale * (-_position.y);
                var transformText = 'translate( ' + x + 'px, ' + y + 'px )';
                page.div.style[xform] = transformText;

                var inView = false;
                var container = pdfViewModel.container;
                if (x + page.baseSize.w * scale > container.scrollLeft //the right of the page is not to the left of the container
                    && x < container.scrollLeft + container.clientWidth //the left of the page is not to the right of the container
                ) {
                    inView = true;
                }
                page.onMove(inView); //trigger the page to hide/draw itself if necessary
            });
        }

        function scale(value) {
            if (value) {
                _scale = value;
                render();
                return;
            }
            return _scale;
        }
        function position(value) {
            if (value) {
                if (value.x !== undefined) {
                    _position.x = value.x;
                }
                if (value.y !== undefined) {
                    _position.y = value.y;
                }
                render();
            }
            return {
                x: _position.x,
                y: _position.y
            };
        }

        var zres = ZoomResizeEventSource();
        zres.start();
        return promise;
    };

    function PdfPageView(pdfPage) {
        var CLASS_LOADING = 'pdfview-loading';
        var CLASS_DIV = 'pdfview-pageContainer';
        var RENDER_INIT = 'inital';
        var RENDER_RUNNING = 'running';
        var RENDER_COMPLETE = 'complete';
        var RENDER_PAUSED = 'paused';
        var _scale = 0;
        var pageView = {
            pdfPage: pdfPage, //the PdfPageProxy object
            baseSize: { w: 0, h: 0 }, //the unscaled natural css size for the canvas
            size: { w: 0, h: 0 }, //the current space that this wants to take up
            scale: function (value) {
                if (value) {
                    _scale = value;
                    if (pageView.div && pageView.baseSize && pageView.baseSize.w) {
                        pageView.div.style.width = '' + (pageView.baseSize.w * _scale) + 'px';
                    }
                    return;
                }
                return _scale;
            }, //the current scale to draw at. can be different to previous render.
            baseTotalPrevPagesWidth: 0, //total width of pages before this one. used in conjunction with scaling to position page.
            pageNo: 0, //the 0-indexed page number.
            renderStatus: RENDER_INIT,
            renderScale: 0, //the previous scale * outputscale this page was rendered at.
            renderTask: null, //the current rendertask for this page.
            canvas: null,
            context: null,
            div: null,
            onMove: onMove,
            inView: false,
        };

        function onMove(inView) {
            if (inView === pageView.inView) {
                //page hasn't moved in or out of view, but might have rescaled enough to redraw.
                if (!pageView.renderScale) {
                    return;
                }
                if (!pageView.scale()) {
                    return;
                }
                if (!pageView.context) {
                    return;
                }
                var newScale = pageView.scale() * getOutputScale(pageView.context);
                if (newScale / pageView.renderScale > 1.3
                    || pageView.renderScale / newScale > 1.3) {
                    render();
                } else if (inView && pageView.canvas) {
                    //ensure that current size is correct.
                    var canvas = pageView.canvas;
                    var viewport = pageView.viewport;
                    var scale = pageView.scale();
                    var height = viewport.height * scale / pageView.renderScale;
                    var width = viewport.width * scale / pageView.renderScale;
                    canvas.style.height = height + 'px';
                    canvas.style.width = width + 'px';
                    pageView.size.h = height;
                    pageView.size.w = width;

                }
            } else {
                //has changed visibility;
                pageView.inView = inView;
                if (pageView.inView) {
                    render();
                } else {
                    reset();
                }
            }
        }

        function initialise() {
            var vp = pdfPage.getViewport(1);
            pageView.baseSize.w = vp.width;
            pageView.baseSize.h = vp.height;
            pageView.div = document.createElement('div');
            pageView.div.classList.add(CLASS_DIV);
        }

        function render() {
            reset();
            pageView.renderStatus = RENDER_RUNNING;
            var scale = pageView.scale();
            var page = pageView.pdfPage;
            var viewport = page.getViewport(scale);
            pageView.viewport = viewport;
            var canvas = document.createElement('canvas');
            pageView.canvas = canvas;
            pageView.div.appendChild(canvas);
            var context = canvas.getContext('2d');
            pageView.context = context;

            var outputScale = getOutputScale(context);
            if (PDFJS.maxCanvasPixels > 0) {
                var pixelsInViewport = viewport.width * viewport.height;
                var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
                if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
                    outputScale.sx = maxScale;
                    outputScale.sy = maxScale;
                    outputScale.scaled = true;
                }
            }

            pageView.renderScale = scale * outputScale.sx;
            canvas.height = viewport.height * outputScale.sy;
            canvas.width = viewport.width * outputScale.sx;
            canvas.style.height = viewport.height + 'px';
            canvas.style.width = viewport.width + 'px';
            pageView.size.h = viewport.height;
            pageView.size.w = viewport.width;
            canvas.setAttribute('hidden', 'hidden');

            var transform = !outputScale.scaled ? null :
                [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
            var renderTask = page.render({
                canvasContext: context,
                transform: transform,
                viewport: viewport
            });
            pageView.renderTask = renderTask;
            var resolveRenderPromise, rejectRenderPromise;
            var promise = new Promise(function (resolve, reject) {
                resolveRenderPromise = resolve;
                rejectRenderPromise = reject;
            });

            function renderComplete(error) {
                if (pageView.renderTask === renderTask) {
                    pageView.renderTask = null;
                }
                if (error === 'cancelled') {
                    rejectRenderPromise(error);
                    return;
                }
                pageView.renderStatus = RENDER_COMPLETE;
                pageView.canvas.removeAttribute('hidden');
                pageView.div.classList.remove(CLASS_LOADING);
                resolveRenderPromise();
            }

            renderTask.promise.then(function () {
                renderComplete(null);
            }, function (error) {
                renderComplete(error);
            });

            return promise;
        }

        function reset() {
            if (pageView.renderTask && (
                pageView.renderStatus === RENDER_RUNNING
                || pageView.renderStatus === RENDER_INIT
                || pageView.renderStatus === RENDER_PAUSED)
            ) {
                pageView.renderTask.cancel();
            }
            pageView.renderStatus = RENDER_INIT;

            if (pageView.div && pageView.canvas) {
                pageView.div.removeChild(pageView.canvas);
                pageView.div.classList.add(CLASS_LOADING);
                delete pageView.canvas;
            }
        }

        initialise();
        return pageView;
    }

    var zoomResizeTimeout;
    /**
     * Sets up a source of resize events for when a mobile browser
     */
    function ZoomResizeEventSource() {
        if (zoomResizeTimeout) {
            return;
        }
        var scale = (document.documentElement.clientWidth / window.innerWidth) || 1;
        if (scale < 1) {
            scale = 1;
        }
        var started = false;
        function zoomResizeEventTick() {
            if (started) {
                return;
            }
            started = true;
            var newScale = (document.documentElement.clientWidth / window.innerWidth) || 1;
            if (newScale < 1) {
                newScale = 1;
            }
            if (Math.abs(scale - newScale) > 0.02) {
                var event = document.createEvent('Event');
                event.initEvent('resize', true, true);
                window.dispatchEvent(event);
            }
            scale = newScale;
            zoomResizeTimeout = window.setTimeout(150, zoomResizeEventTick); //150 ms period. Might feel a little sluggish, but prevents
        }
        return {
            start: zoomResizeEventTick,
            stop: function () {
                if (zoomResizeTimeout) {
                    window.clearTimeout(zoomResizeTimeout);
                }
                zoomResizeTimeout = undefined;
                started = false;
            }
        };
    }
} ();<commit_msg>listen for scroll event<commit_after>/*global PDFJS:false*/
/*eslint-env browser*/
/*eslint-env es6*/
!function PDFViewClosure() {
    'use strict';
    var page_gap = 10;
    var xform = 'transform';
    ['', 'webkit', 'Moz', 'O', 'ms'].every(function (prefix) {
        var e = prefix + (prefix ? 'T' : 't') + 'ransform';
        if (typeof document.body.style[e] !== 'undefined') {
            xform = e;
            return false;
        }
        return true;
    });

    function getPixelRatio() {
        //On mobile devices this is the ratio css pixel height / device pixel height, whilst at 1.0 scale
        //On desktop devices this is typically the current scale.
        var dpr = window.devicePixelRatio || 1;
        //On mobile devices this adequately measures scale on a page using viewport width=device-width.
        //On desktop devices it is 1 (or slightly less to compensate for a scroll bar.)
        var scale = (document.documentElement.clientWidth / window.innerWidth) || 1;
        scale = scale < 1 ? 1 : scale;
        return dpr * scale;
    }

    function getOutputScale(ctx) {
        var canvas;
        if (ctx === undefined) {
            if (canvas === undefined) {
                canvas = document.createElement('canvas');
            }
            ctx = canvas.getContext('2d');
        }
        var devicePixelRatio = getPixelRatio();
        var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
            ctx.mozBackingStorePixelRatio ||
            ctx.msBackingStorePixelRatio ||
            ctx.oBackingStorePixelRatio ||
            ctx.backingStorePixelRatio || 1; //backing store ratio is deprecated and will likely always be 1.
        var pixelRatio = devicePixelRatio / backingStoreRatio;
        return {
            sx: pixelRatio,
            sy: pixelRatio,
            scaled: pixelRatio !== 1
        };
    }
    /*
    !function () {
        PDFJS.getDocument('1609.01714.pdf').then(function (pdf) {
            var pageDivs = [];
            function createPage(n) {
                if (n > pdf.numPages) {
                    return;
                }
                var div = document.createElement('div');
                pageDivs.push(div);
                var promise = new Promise(function (resolve, reject) {

                    pdf.getPage(n).then(function (page) {
                        var scale = 1;
                        var viewport = page.getViewport(scale * CSS_UNITS);

                        var canvas = document.createElement('canvas');
                        var context = canvas.getContext('2d');

                        var outputScale = getOutputScale(canvas);
                        if (PDFJS.maxCanvasPixels > 0) {
                            var pixelsInViewport = viewport.width * viewport.height;
                            var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
                            if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
                                outputScale.sx = maxScale;
                                outputScale.sy = maxScale;
                                outputScale.scaled = true;
                                var hasRestrictedScaling = true;//TODO: use this to css transform element larger.
                            } else {
                                hasRestrictedScaling = false;
                            }
                        }

                        canvas.height = viewport.height * outputScale.sy;
                        canvas.width = viewport.width * outputScale.sx;
                        canvas.style.height = viewport.height;
                        canvas.style.width = viewport.width;

                        div.appendChild(canvas);
                        var transform = !outputScale.scaled ? null :
                            [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
                        page.render({
                            canvasContext: context,
                            transform: transform,
                            viewport: viewport
                        }).then(resolve(), reject());
                    });
                });
                return promise;
            }
            createPage(1).then(function () {
                return createPage(2);
            }).then(function () {
                pageDivs.forEach(function (div) {
                    document.body.appendChild(div);
                });
            });
        });
    } ();
    */
/*
    window.PdfViewController = function PdfViewController(pdfViewPromise) {
        var pdfView;
        pdfViewPromise.then(function (pdfViewModel) {
            pdfView = pdfViewModel;
            var kinetic = window.kinetic({
                listeners: [xposChanged],
                restrictFn: function (x) {
                    if (x < 0) {
                        return 0;
                    }
                    var finalPage = pdfView.pages[pdfView.pages.length];
                    var max = finalPage.baseTotalPrevPagesWidth
                        + finalPage.baseSize.w
                        + (pdfView.pages.length - 1) * page_gap
                        - pdfView.container.clientWidth;
                    if (x > max) {
                        return max;
                    }
                    return x;
                }
            });
        });

        function xposChanged(pos) {
            pdfView.position({ x: pos });
        }

        var prevPanDelta = { x: 0, y: 0 };
        function onPan(ev) {

        }
    };
*/
    window.PdfView = function PdfView(container, pdfLocation) {

        var pdfViewModel = {
            pages: [],
            scale: scale,
            position: position,
            container: container,
        };
        var _scale = 1;
        var _position = { x: 0, y: 0 };

        container.classList.add('pdfview-container');
        var finalPageInitialisedResolve;
        var promise = new Promise(function (resolve) {
            finalPageInitialisedResolve = resolve;
        });
        PDFJS.getDocument(pdfLocation).then(function (pdf) {
            var totalWidth = 0;
            function initPage(pageNum) {
                if (pageNum === pdf.numPages){
                    window.addEventListener('resize', render);
                    container.addEventListener('scroll', render);
                    render();
                    finalPageInitialisedResolve(pdfViewModel);
                    return;
                }
                pdf.getPage(pageNum+1).then(function (page) {
                    var pageView = PdfPageView(page);
                    pageView.pageNo = pageNum;
                    pageView.baseTotalPrevPagesWidth = totalWidth;
                    totalWidth += pageView.baseSize.w;
                    pdfViewModel.pages.push(pageView);
                    pdfViewModel.container.appendChild(pageView.div);
                    initPage(pageNum + 1);
                });
            }
            initPage(0);
        });

        var renderQueued = false;
        function render() {
            if (renderQueued) {
                return;
            }
            renderQueued = true;
            window.requestAnimationFrame(function () {
                renderWork();
                renderQueued = false;
            });
        }

        function renderWork() {
            var pages = pdfViewModel.pages;
            var scale = _scale;
            pages.forEach(function (page) {
                page.scale(scale);
                var index = page.pageNo;
                var x = scale * (page.baseTotalPrevPagesWidth + index * page_gap - _position.x);
                var y = scale * (-_position.y);
                var transformText = 'translate( ' + x + 'px, ' + y + 'px )';
                page.div.style[xform] = transformText;

                var inView = false;
                var container = pdfViewModel.container;
                if (x + page.baseSize.w * scale > container.scrollLeft //the right of the page is not to the left of the container
                    && x < container.scrollLeft + container.clientWidth //the left of the page is not to the right of the container
                ) {
                    inView = true;
                }
                page.onMove(inView); //trigger the page to hide/draw itself if necessary
            });
        }

        function scale(value) {
            if (value) {
                _scale = value;
                render();
                return;
            }
            return _scale;
        }
        function position(value) {
            if (value) {
                if (value.x !== undefined) {
                    _position.x = value.x;
                }
                if (value.y !== undefined) {
                    _position.y = value.y;
                }
                render();
            }
            return {
                x: _position.x,
                y: _position.y
            };
        }

        var zres = ZoomResizeEventSource();
        zres.start();
        return promise;
    };

    function PdfPageView(pdfPage) {
        var CLASS_LOADING = 'pdfview-loading';
        var CLASS_DIV = 'pdfview-pageContainer';
        var RENDER_INIT = 'inital';
        var RENDER_RUNNING = 'running';
        var RENDER_COMPLETE = 'complete';
        var RENDER_PAUSED = 'paused';
        var _scale = 0;
        var pageView = {
            pdfPage: pdfPage, //the PdfPageProxy object
            baseSize: { w: 0, h: 0 }, //the unscaled natural css size for the canvas
            size: { w: 0, h: 0 }, //the current space that this wants to take up
            scale: function (value) {
                if (value) {
                    _scale = value;
                    if (pageView.div && pageView.baseSize && pageView.baseSize.w) {
                        pageView.div.style.width = '' + (pageView.baseSize.w * _scale) + 'px';
                    }
                    return;
                }
                return _scale;
            }, //the current scale to draw at. can be different to previous render.
            baseTotalPrevPagesWidth: 0, //total width of pages before this one. used in conjunction with scaling to position page.
            pageNo: 0, //the 0-indexed page number.
            renderStatus: RENDER_INIT,
            renderScale: 0, //the previous scale * outputscale this page was rendered at.
            renderTask: null, //the current rendertask for this page.
            canvas: null,
            context: null,
            div: null,
            onMove: onMove,
            inView: false,
        };

        function onMove(inView) {
            if (inView === pageView.inView) {
                //page hasn't moved in or out of view, but might have rescaled enough to redraw.
                if (!pageView.renderScale) {
                    return;
                }
                if (!pageView.scale()) {
                    return;
                }
                if (!pageView.context) {
                    return;
                }
                var newScale = pageView.scale() * getOutputScale(pageView.context);
                if (newScale / pageView.renderScale > 1.3
                    || pageView.renderScale / newScale > 1.3) {
                    render();
                } else if (inView && pageView.canvas) {
                    //ensure that current size is correct.
                    var canvas = pageView.canvas;
                    var viewport = pageView.viewport;
                    var scale = pageView.scale();
                    var height = viewport.height * scale / pageView.renderScale;
                    var width = viewport.width * scale / pageView.renderScale;
                    canvas.style.height = height + 'px';
                    canvas.style.width = width + 'px';
                    pageView.size.h = height;
                    pageView.size.w = width;

                }
            } else {
                //has changed visibility;
                pageView.inView = inView;
                if (pageView.inView) {
                    render();
                } else {
                    reset();
                }
            }
        }

        function initialise() {
            var vp = pdfPage.getViewport(1);
            pageView.baseSize.w = vp.width;
            pageView.baseSize.h = vp.height;
            pageView.div = document.createElement('div');
            pageView.div.classList.add(CLASS_DIV);
            pageView.div.classList.add(CLASS_LOADING);
        }

        function render() {
            reset();
            pageView.renderStatus = RENDER_RUNNING;
            var scale = pageView.scale();
            var page = pageView.pdfPage;
            var viewport = page.getViewport(scale);
            pageView.viewport = viewport;
            var canvas = document.createElement('canvas');
            pageView.canvas = canvas;
            pageView.div.appendChild(canvas);
            var context = canvas.getContext('2d');
            pageView.context = context;

            var outputScale = getOutputScale(context);
            if (PDFJS.maxCanvasPixels > 0) {
                var pixelsInViewport = viewport.width * viewport.height;
                var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
                if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
                    outputScale.sx = maxScale;
                    outputScale.sy = maxScale;
                    outputScale.scaled = true;
                }
            }

            pageView.renderScale = scale * outputScale.sx;
            canvas.height = viewport.height * outputScale.sy;
            canvas.width = viewport.width * outputScale.sx;
            canvas.style.height = viewport.height + 'px';
            canvas.style.width = viewport.width + 'px';
            pageView.size.h = viewport.height;
            pageView.size.w = viewport.width;
            canvas.setAttribute('hidden', 'hidden');

            var transform = !outputScale.scaled ? null :
                [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
            var renderTask = page.render({
                canvasContext: context,
                transform: transform,
                viewport: viewport
            });
            pageView.renderTask = renderTask;
            var resolveRenderPromise, rejectRenderPromise;
            var promise = new Promise(function (resolve, reject) {
                resolveRenderPromise = resolve;
                rejectRenderPromise = reject;
            });

            function renderComplete(error) {
                if (pageView.renderTask === renderTask) {
                    pageView.renderTask = null;
                }
                if (error === 'cancelled') {
                    rejectRenderPromise(error);
                    return;
                }
                pageView.renderStatus = RENDER_COMPLETE;
                pageView.canvas.removeAttribute('hidden');
                pageView.div.classList.remove(CLASS_LOADING);
                resolveRenderPromise();
            }

            renderTask.promise.then(function () {
                renderComplete(null);
            }, function (error) {
                renderComplete(error);
            });

            return promise;
        }

        function reset() {
            if (pageView.renderTask && (
                pageView.renderStatus === RENDER_RUNNING
                || pageView.renderStatus === RENDER_INIT
                || pageView.renderStatus === RENDER_PAUSED)
            ) {
                pageView.renderTask.cancel();
            }
            pageView.renderStatus = RENDER_INIT;

            if (pageView.div && pageView.canvas) {
                pageView.div.removeChild(pageView.canvas);
                pageView.div.classList.add(CLASS_LOADING);
                delete pageView.canvas;
            }
        }

        initialise();
        return pageView;
    }

    var zoomResizeTimeout;
    /**
     * Sets up a source of resize events for when a mobile browser
     */
    function ZoomResizeEventSource() {
        if (zoomResizeTimeout) {
            return;
        }
        var scale = (document.documentElement.clientWidth / window.innerWidth) || 1;
        if (scale < 1) {
            scale = 1;
        }
        var started = false;
        function zoomResizeEventTick() {
            if (started) {
                return;
            }
            started = true;
            var newScale = (document.documentElement.clientWidth / window.innerWidth) || 1;
            if (newScale < 1) {
                newScale = 1;
            }
            if (Math.abs(scale - newScale) > 0.02) {
                var event = document.createEvent('Event');
                event.initEvent('resize', true, true);
                window.dispatchEvent(event);
            }
            scale = newScale;
            zoomResizeTimeout = window.setTimeout(150, zoomResizeEventTick); //150 ms period. Might feel a little sluggish, but prevents
        }
        return {
            start: zoomResizeEventTick,
            stop: function () {
                if (zoomResizeTimeout) {
                    window.clearTimeout(zoomResizeTimeout);
                }
                zoomResizeTimeout = undefined;
                started = false;
            }
        };
    }
} ();<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import { Text, TouchableOpacity } from 'react-native'
import MapView from 'react-native-maps'
import Styles from './Styles/MapCalloutStyle'
import ExamplesRegistry from '../Services/ExamplesRegistry'

// Example
ExamplesRegistry.add('Full Button', () =>
  <MapCallout
    location={{
      title: 'Callout Example'
    }}
    onPress={() => window.alert('That tickles!')}
  />
)

export default class MapCallout extends React.Component {
  constructor (props) {
    super(props)
    this.onPress = this.props.onPress.bind(this, this.props.location)
  }

  render () {
    /* ***********************************************************
    * Customize the appearance of the callout that opens when the user interacts with a marker.
    * Note: if you don't want your callout surrounded by the default tooltip, pass `tooltip={true}` to `MapView.Callout`
    *************************************************************/
    const { location } = this.props
    return (
      <MapView.Callout style={Styles.callout}>
        <TouchableOpacity onPress={this.onPress}>
          <Text>{location.title}</Text>
        </TouchableOpacity>
      </MapView.Callout>
    )
  }
}

MapCallout.propTypes = {
  location: PropTypes.object.isRequired,
  onPress: PropTypes.func
}
<commit_msg>Fix warning of duplicate key for MapCallout<commit_after>import React, { PropTypes } from 'react'
import { Text, TouchableOpacity } from 'react-native'
import MapView from 'react-native-maps'
import Styles from './Styles/MapCalloutStyle'
import ExamplesRegistry from '../Services/ExamplesRegistry'

// Example
ExamplesRegistry.add('Map Callout', () =>
  <MapCallout
    location={{
      title: 'Callout Example'
    }}
    onPress={() => window.alert('That tickles!')}
  />
)

export default class MapCallout extends React.Component {
  constructor (props) {
    super(props)
    this.onPress = this.props.onPress.bind(this, this.props.location)
  }

  render () {
    /* ***********************************************************
    * Customize the appearance of the callout that opens when the user interacts with a marker.
    * Note: if you don't want your callout surrounded by the default tooltip, pass `tooltip={true}` to `MapView.Callout`
    *************************************************************/
    const { location } = this.props
    return (
      <MapView.Callout style={Styles.callout}>
        <TouchableOpacity onPress={this.onPress}>
          <Text>{location.title}</Text>
        </TouchableOpacity>
      </MapView.Callout>
    )
  }
}

MapCallout.propTypes = {
  location: PropTypes.object.isRequired,
  onPress: PropTypes.func
}
<|endoftext|>


<commit_before>'use strict';
/* global angular */

var controllersModule = require('./_index');
var Leaflet = require('../components/leaflet');
var GeoJSON = require('../components/geojson');
var widgets = require('../components/widgets');
var tweets,result;

/**
 * @ngInject
 */
function MapCtrl($scope, $stateParams, $timeout, $location, $http, AppSettings, SearchService) {

        var map = L.map('map').setView([2.252776,48.845261], 3);

        L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
            maxZoom: 18,
            attribution: 'Map data © OpenStreetMap contributors, ' +
                'CC-BY-SA, ' +
                'Imagery © Mapbox',
            id: 'examples.map-20v6611k'
        }).addTo(map);

         $http.jsonp( "http://loklak.org/api/search.json?callback=JSON_CALLBACK&timezoneOffset=-330&q=/location")
         .success(function (response) {
            tweets = response.statuses;
            result = GeoJSON.parse(tweets, {Point: 'location_point' , include:['id_str']});
           console.log(result.features[0]);
            add_marker(result);
        });

        function add_marker(result) {
            var tweetIcon = L.icon({
             iconUrl: 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-32.png',
           });
            console.log(result.features.length);
            var marker = [];
            var i;
            for (i = 0; i < result.features.length; i++) {
            console.log("here id ");
            var lat=result.features[i].geometry.coordinates[1];
            var lng=result.features[i].geometry.coordinates[0];
            marker[i] = new L.Marker([lat, lng], {
                id: result.features[i].properties.id_str,
                icon:tweetIcon
            });
            marker[i].addTo(map);
            marker[i].on('click', showTweet);
            };
        }

        function showTweet(e)
        {
            document.getElementById('TweetBox').innerHTML="";
            var id=this.options.id;
             twttr.widgets.createTweet(id,
            document.getElementById('TweetBox'),
            {
                theme: 'light'
            }
        );
        }

}

controllersModule.controller('MapCtrl', ['$scope', '$stateParams', '$timeout', '$location', '$http', 'AppSettings', 'SearchService',MapCtrl]);
<commit_msg>Improved map, now the popups are linked to loklak<commit_after>'use strict';
/* global angular */

var controllersModule = require('./_index');
var Leaflet = require('../components/leaflet');
var GeoJSON = require('../components/geojson');
var result;
/**
 * @ngInject
 */
function MapCtrl($scope, $stateParams, $timeout, $location, $http, AppSettings, SearchService,MapPopUpTemplateService) {

        var map = L.map('map').setView([2.252776,48.845261], 3);

        L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
            maxZoom: 18,
            attribution: 'Map data © OpenStreetMap contributors, ' +
                'CC-BY-SA, ' +
                'Imagery © Mapbox',
            id: 'examples.map-20v6611k'
        }).addTo(map);

         $http.jsonp( "http://loklak.org/api/search.json?callback=JSON_CALLBACK&timezoneOffset=-330&q=/location")
         .success(function (response) {
                var marker = [];
                var tweets = {
                    "type": "FeatureCollection",
                    "features": []
                };
            response.statuses.forEach(function(ele) {
                if (ele.location_point) {
                    var text = MapPopUpTemplateService.genStaticTwitterStatus(ele);
                    var pointObject = {
                        "geometry": {
                            "type": "Point",
                            "coordinates": [
                                ele.location_point[0],
                                ele.location_point[1]
                            ]
                        },
                        "type": "Feature",
                        "properties": {
                            "popupContent": "
" + text + "
"
                        },
                        "id": ele.id_str
                    };
                    tweets.features.push(pointObject);
                }
            });

            add_marker(tweets);
        });

      function add_marker(result) {
                    var tweetIcon = L.icon({
                        iconUrl: 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-32.png',
                    });
                    console.log(result.features.length);
                    var i;
                    for (i = 0; i < result.features.length; i++) {
                        var lat = result.features[i].geometry.coordinates[1];
                        var lng = result.features[i].geometry.coordinates[0];
                        marker[i] = new L.Marker([lat, lng], {
                            id: i,
                            icon: tweetIcon,
                            bounceOnAdd: true
                        });
                        marker[i].addTo(map);
                        var popup = L.popup({
                            autoPan: false
                        }).setContent(result.features[i].properties.popupContent);
                        marker[i].bindPopup(popup);
                    };

                }

}

controllersModule.controller('MapCtrl', ['$scope', '$stateParams', '$timeout', '$location', '$http', 'AppSettings', 'SearchService','MapPopUpTemplateService',MapCtrl]);
<|endoftext|>


<commit_before>var express = require('express')
,   app = express()
,   server = require('http').createServer(app)
,   io = require('socket.io').listen(server)
,   conf = require('./config.json')
,   scrumDB = require( './ScrumDB.js' );

server.listen(conf.port);

// serve public folder's contents
app.use(express.static(__dirname + '/public'));

// show index.html on '/'
app.get('/', function (req, res) {
	res.sendfile(__dirname + '/public/index.html');
});

//////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  Load scripts + data  ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

// load scrumdata.js
var fs = require('fs');
eval( fs.readFileSync('public/ScrumDataManager.js')+'' );

// init test data
//scrumDataManager.InitTestData();

// connect scrumDataManager to scrumDB
var loadedData = scrumDB.LoadScrumDataSync( "Default" );
scrumDataArray = loadedData.scrumDataArray;
scrumDataManager.priorityStartId = loadedData.priorityStartId;
scrumDB.AddDataManager( scrumDataManager );
scrumDB.scrumDataArray = scrumDataArray;

//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  websocket + events  ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

io.sockets.on('connection', function (socket) {
	socket.emit('chat', { zeit: new Date(), text: 'You are locked on to the server!' });
	socket.on('chat', function (data) {
		io.sockets.emit('chat', { zeit: new Date(), name: data.name || 'Anonym', text: data.text });
	});

	socket.on('updateScrumData', function (data) {
		scrumDataManager.UpdateData( data );
		io.sockets.emit('scrubdata', {
			featurename:			scrumDataArray[ data.id ].featurename,
			id: 					scrumDataArray[ data.id ].id,
			complexity: 			scrumDataArray[ data.id ].complexity,
			priority: 				scrumDataArray[ data.id ].priority,
			previousPriorityId: 	scrumDataArray[ data.id ].previousPriorityId
			});
	});

	socket.on('moveDataUp', function (data) {
		scrumDataManager.MovePriorityUp( data.id );
		io.sockets.emit( 'scrubmoveup', data.id );
	});

	// Send complete array data to client
	socket.emit('scrubfulldata', {
		dataArray:		scrumDataArray,
		priorityStartId:	scrumDataManager.priorityStartId,
	} );
});

// portnumber in console
console.log('Server listening on http://127.0.0.1:' + conf.port + '/');
<commit_msg>gzip activated<commit_after>var express = require('express')
,   app = express()
,   server = require('http').createServer(app)
,   io = require('socket.io').listen(server)
,   conf = require('./config.json')
,   scrumDB = require( './ScrumDB.js' );

server.listen(conf.port);

app.use( express.compress() );

// serve public folder's contents
app.use(express.static(__dirname + '/public'));

// show index.html on '/'
app.get('/', function (req, res) {
	res.sendfile(__dirname + '/public/index.html');
});

//////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  Load scripts + data  ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

// load scrumdata.js
var fs = require('fs');
eval( fs.readFileSync('public/ScrumDataManager.js')+'' );

// init test data
//scrumDataManager.InitTestData();

// connect scrumDataManager to scrumDB
var loadedData = scrumDB.LoadScrumDataSync( "Default" );
scrumDataArray = loadedData.scrumDataArray;
scrumDataManager.priorityStartId = loadedData.priorityStartId;
scrumDB.AddDataManager( scrumDataManager );
scrumDB.scrumDataArray = scrumDataArray;

//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  websocket + events  ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

io.sockets.on('connection', function (socket) {
	socket.emit('chat', { zeit: new Date(), text: 'You are locked on to the server!' });
	socket.on('chat', function (data) {
		io.sockets.emit('chat', { zeit: new Date(), name: data.name || 'Anonym', text: data.text });
	});

	socket.on('updateScrumData', function (data) {
		scrumDataManager.UpdateData( data );
		io.sockets.emit('scrubdata', {
			featurename:			scrumDataArray[ data.id ].featurename,
			id: 					scrumDataArray[ data.id ].id,
			complexity: 			scrumDataArray[ data.id ].complexity,
			priority: 				scrumDataArray[ data.id ].priority,
			previousPriorityId: 	scrumDataArray[ data.id ].previousPriorityId
			});
	});

	socket.on('moveDataUp', function (data) {
		scrumDataManager.MovePriorityUp( data.id );
		io.sockets.emit( 'scrubmoveup', data.id );
	});

	// Send complete array data to client
	socket.emit('scrubfulldata', {
		dataArray:		scrumDataArray,
		priorityStartId:	scrumDataManager.priorityStartId,
	} );
});

// portnumber in console
console.log('Server listening on http://127.0.0.1:' + conf.port + '/');
<|endoftext|>


<commit_before>/*
Copyright (c) 2008 Stefan Lange-Hegermann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

function microAjax(url, callbackFunction)
{
	this.bindFunction = function (caller, object) {
		return function() {
			return caller.apply(object, [object]);
		};
	};

	this.stateChange = function (object) {
		if (this.request.readyState==4)
			this.callbackFunction(this.request.responseText);
	};

	this.getRequest = function() {
		if (window.ActiveXObject)
			return new ActiveXObject('Microsoft.XMLHTTP');
		else if (window.XMLHttpRequest)
			return new XMLHttpRequest();
		return false;
	};

	this.postBody = (arguments[2] || "");

	this.callbackFunction=callbackFunction;
	this.url=url;
	this.request = this.getRequest();

	if(this.request) {
		var req = this.request;
		req.onreadystatechange = this.bindFunction(this.stateChange, this);

		if (this.postBody!=="") {
			req.open("POST", url, true);
			req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.setRequestHeader('Connection', 'close');
		} else {
			req.open("GET", url, true);
		}

		req.send(this.postBody);
	}
}

//  discuss at: http://phpjs.org/functions/array_flip/
function array_flip(trans) {
  var key, tmp_ar = {};
  for (key in trans) {
    if (!trans.hasOwnProperty(key)) {
      continue;
    }
    tmp_ar[trans[key]] = key;
  }
  return tmp_ar;
}

// other
function get_objects(tables,table_name,where_index,match_value) {
	var objects = [];
	for (var record in tables[table_name]['records']) {
		record = tables[table_name]['records'][record];
		if (where_index==false || record[where_index]==match_value) {
			var object = {};
			for (var index in tables[table_name]['columns']) {
				var column = tables[table_name]['columns'][index];
				object[column] = record[index];
				for (var relation in tables) {
					var reltable = tables[relation];
					for (var key in reltable['relations']) {
						var target = reltable['relations'][key];
						if (target == table_name+'.'+column) {
							column_indices = array_flip(reltable['columns']);
							object[relation] = get_objects(tables,relation,column_indices[key],record[index]);
						}
					}
				}
			}
			objects.push(object);
		}
	}
	return objects;
}

function get_tree(tables) {
	tree = {};
	for (var name in tables) {
		var table = tables[name];
		if (!table['relations']) {
			tree[name] = get_objects(tables,name);
		}
	}
	return tree;
}

microAjax("http://localhost/api.php/posts,categories,tags,comments?filter=id:1", function (response) {
  var jsonObject = eval('(' + response + ')');
  console.log(get_tree(jsonObject));
});
<commit_msg>Bugfix habtm<commit_after>/*
Copyright (c) 2008 Stefan Lange-Hegermann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

function microAjax(url, callbackFunction)
{
	this.bindFunction = function (caller, object) {
		return function() {
			return caller.apply(object, [object]);
		};
	};

	this.stateChange = function (object) {
		if (this.request.readyState==4)
			this.callbackFunction(this.request.responseText);
	};

	this.getRequest = function() {
		if (window.ActiveXObject)
			return new ActiveXObject('Microsoft.XMLHTTP');
		else if (window.XMLHttpRequest)
			return new XMLHttpRequest();
		return false;
	};

	this.postBody = (arguments[2] || "");

	this.callbackFunction=callbackFunction;
	this.url=url;
	this.request = this.getRequest();

	if(this.request) {
		var req = this.request;
		req.onreadystatechange = this.bindFunction(this.stateChange, this);

		if (this.postBody!=="") {
			req.open("POST", url, true);
			req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.setRequestHeader('Connection', 'close');
		} else {
			req.open("GET", url, true);
		}

		req.send(this.postBody);
	}
}

//  discuss at: http://phpjs.org/functions/array_flip/
function array_flip(trans) {
  var key, tmp_ar = {};
  for (key in trans) {
    if (!trans.hasOwnProperty(key)) {
      continue;
    }
    tmp_ar[trans[key]] = key;
  }
  return tmp_ar;
}

// other
function get_objects(tables,table_name,where_index,match_value) {
	var objects = [];
	for (var record in tables[table_name]['records']) {
		record = tables[table_name]['records'][record];
		if (!where_index || record[where_index]==match_value) {
			var object = {};
			for (var index in tables[table_name]['columns']) {
				var column = tables[table_name]['columns'][index];
				object[column] = record[index];
				for (var relation in tables) {
					var reltable = tables[relation];
					for (var key in reltable['relations']) {
						var target = reltable['relations'][key];
						if (target == table_name+'.'+column) {
							column_indices = array_flip(reltable['columns']);
							object[relation] = get_objects(tables,relation,column_indices[key],record[index]);
						}
					}
				}
			}
			objects.push(object);
		}
	}
	return objects;
}

function get_tree(tables) {
	tree = {};
	for (var name in tables) {
		var table = tables[name];
		if (!table['relations']) {
			tree[name] = get_objects(tables,name);
		}
	}
	return tree;
}

microAjax("http://localhost/api.php/posts,categories,tags,comments?filter=id:1", function (response) {
  var jsonObject = eval('(' + response + ')');
  console.log(get_tree(jsonObject));
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict';

var _ = require('underscore');
var Config = require('./config.js');
var ClientErrors = require('./client_errors.js');
var ClientRequest = require('./client_request.js');
var globalTimers = require('timers');
var RingpopErrors = require('./ringpop_errors.js');
var TChannel = require('tchannel');

function RingpopClient(ringpop, subChannel, timers, date) {
    this.ringpop = ringpop;
    this.subChannel = subChannel;
    this.timers = timers || globalTimers;
    this.date = date || Date;

    this.config = this.ringpop.config;
    this.logger = this.ringpop.loggerFactory.getLogger('client');

    // If no subChannel provided, create one from a new instance
    // of TChannel. This client then becomes the owner of that
    // instance and is responsible for closing.
    this.isChannelOwner = false;
    if (!this.subChannel) {
        this.tchannel = new TChannel();
        this.subChannel = this.tchannel.makeSubChannel({
            serviceName: 'ringpop',
            trace: false
        });
        this.isChannelOwner = true;
    }

    this.requestsById = {};
    this.wedgedTimer = null;
    this.isDestroyed = false;
}

RingpopClient.prototype.adminConfigGet = function adminConfigGet(host, body, callback) {
    this._request({
        host: host
    }, '/admin/config/get', null, body, callback);
};

RingpopClient.prototype.adminConfigSet = function adminConfigSet(host, body, callback) {
    this._request({
        host: host
    }, '/admin/config/set', null, body, callback);
};

RingpopClient.prototype.adminGossipStart = function adminGossipStart(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/start', null, null, callback);
};

RingpopClient.prototype.adminGossipStop = function adminGossipStop(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/stop', null, null, callback);
};

RingpopClient.prototype.adminGossipTick = function adminGossipTick(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/tick', null, null, callback);
};

RingpopClient.prototype.protocolJoin = function protocolJoin(opts, body, callback) {
    this._request(opts, '/protocol/join', null, body, callback);
};

RingpopClient.prototype.protocolPing = function protocolPing(opts, body, callback) {
    this._request(opts, '/protocol/ping', null, body, callback);
};

RingpopClient.prototype.protocolPingReq = function protocolPingReq(opts, body, callback) {
    this._request(opts, '/protocol/ping-req', null, body, callback);
};

RingpopClient.prototype.protocolDampReq = function protocolDampReq(opts, body, callback) {
    this._request(opts, '/protocol/damp-req', null, body, callback);
};

RingpopClient.prototype.destroy = function destroy(callback) {
    if (this.isChannelOwner) {
        this.tchannel.close(callback);
    }

    this.timers.clearTimeout(this.wedgedTimer);
    this.isDestroyed = true;
};

// This function is "public" for ease of testing, but shouldn't be called by
// anything other than the class itself.
RingpopClient.prototype.scanForWedgedRequests = function scanForWedgedRequests() {
    var wedgedRequests = [];

    // Find all requests that have not been timed-out by TChannel.
    // Induce Ringpop timeout for wedged requests and cancel them.
    var requestIds = Object.keys(this.requestsById);
    var scanTime = this.date.now();
    for (var i = 0; i < requestIds.length; i++) {
        var request = this.requestsById[requestIds[i]];
        var timeInflight = scanTime - request.timestamp;
        var isWedged = timeInflight >= (this.config.get('wedgedRequestTimeout') ||
            Config.Defaults.wedgedRequestTimeout);
        if (isWedged) {
            delete this.requestsById[request.id];
            request.cancel();
            wedgedRequests.push(request);
        }
    }

    if (wedgedRequests.length > 0) {
        this.logger.error('ringpop canceled wedged requests', {
            local: this.ringpop.whoami(),
            numWedgedRequests: wedgedRequests.length,
            wedgedRequests: wedgedRequestsToLog()
        });
        this._emitWedgedError();
    }

    return wedgedRequests;

    // Log the first N requests only. We don't want to flood
    // our pipes.
    function wedgedRequestsToLog() {
        return _.take(wedgedRequests, 10).map(function toLog(request) {
            return request.toLog();
        });
    }
};

RingpopClient.prototype._emitWedgedError = function _emitWedgedError() {
    var errorListeners = this.ringpop.listeners('error');
    if (errorListeners.length > 0) {
        this.ringpop.emit('error',
            RingpopErrors.PotentiallyWedgedRequestsError());
    }
};

/* jshint maxparams: 5 */
RingpopClient.prototype._request = function _request(opts, endpoint, head, body, callback) {
    var self = this;

    if (!this.wedgedTimer) {
        this._scheduleWedgedTimer();
    }

    var request = new ClientRequest(this, opts, endpoint, head, body,
        this.date, onSend);

    // Evaluate the number of inflight requests. If we've got more than the
    // allowable limit it's a strong indication that at least some are wedged.
    // Apply backpressure until the amount of inflight requests is reduced
    // by cancellation or otherwise.
    var inflightCurrent = Object.keys(this.requestsById).length;
    var inflightMax = this.config.get('inflightClientRequestsLimit');
    if (inflightCurrent >= inflightMax) {
        process.nextTick(function onTick() {
            callback(ClientErrors.ClientRequestsLimitError({
                inflightCurrent: inflightCurrent,
                inflightMax: inflightMax,
                endpoint: endpoint
            }));
        });
        return;
    }

    // Registering the request by its ID must happen at the precise
    // moment before we know it will be "inflight."
    this.requestsById[request.id] = request;
    request.send();

    function onSend() {
        delete self.requestsById[request.id];
        var args = Array.prototype.splice.call(arguments, 0);
        callback.apply(null, args);
    }
};

RingpopClient.prototype._scheduleWedgedTimer = function _scheduleWedgedTimer() {
    var self = this;
    this.wedgedTimer = this.timers.setTimeout(function onTimeout() {
        if (self.isDestroyed) return;
        self.scanForWedgedRequests();
        self._scheduleWedgedTimer();
    }, this.config.get('wedgedTimerInterval') || Config.Defaults.wedgedTimerInterval);
    this.wedgedTimer.unref();
};

module.exports = RingpopClient;
<commit_msg>unref() has a memory leak; don't use it<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict';

var _ = require('underscore');
var Config = require('./config.js');
var ClientErrors = require('./client_errors.js');
var ClientRequest = require('./client_request.js');
var globalTimers = require('timers');
var RingpopErrors = require('./ringpop_errors.js');
var TChannel = require('tchannel');

function RingpopClient(ringpop, subChannel, timers, date) {
    this.ringpop = ringpop;
    this.subChannel = subChannel;
    this.timers = timers || globalTimers;
    this.date = date || Date;

    this.config = this.ringpop.config;
    this.logger = this.ringpop.loggerFactory.getLogger('client');

    // If no subChannel provided, create one from a new instance
    // of TChannel. This client then becomes the owner of that
    // instance and is responsible for closing.
    this.isChannelOwner = false;
    if (!this.subChannel) {
        this.tchannel = new TChannel();
        this.subChannel = this.tchannel.makeSubChannel({
            serviceName: 'ringpop',
            trace: false
        });
        this.isChannelOwner = true;
    }

    this.requestsById = {};
    this.wedgedTimer = null;
    this.isDestroyed = false;
}

RingpopClient.prototype.adminConfigGet = function adminConfigGet(host, body, callback) {
    this._request({
        host: host
    }, '/admin/config/get', null, body, callback);
};

RingpopClient.prototype.adminConfigSet = function adminConfigSet(host, body, callback) {
    this._request({
        host: host
    }, '/admin/config/set', null, body, callback);
};

RingpopClient.prototype.adminGossipStart = function adminGossipStart(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/start', null, null, callback);
};

RingpopClient.prototype.adminGossipStop = function adminGossipStop(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/stop', null, null, callback);
};

RingpopClient.prototype.adminGossipTick = function adminGossipTick(host, callback) {
    this._request({
        host: host
    }, '/admin/gossip/tick', null, null, callback);
};

RingpopClient.prototype.protocolJoin = function protocolJoin(opts, body, callback) {
    this._request(opts, '/protocol/join', null, body, callback);
};

RingpopClient.prototype.protocolPing = function protocolPing(opts, body, callback) {
    this._request(opts, '/protocol/ping', null, body, callback);
};

RingpopClient.prototype.protocolPingReq = function protocolPingReq(opts, body, callback) {
    this._request(opts, '/protocol/ping-req', null, body, callback);
};

RingpopClient.prototype.protocolDampReq = function protocolDampReq(opts, body, callback) {
    this._request(opts, '/protocol/damp-req', null, body, callback);
};

RingpopClient.prototype.destroy = function destroy(callback) {
    if (this.isChannelOwner) {
        this.tchannel.close(callback);
    }

    this.timers.clearTimeout(this.wedgedTimer);
    this.isDestroyed = true;
};

// This function is "public" for ease of testing, but shouldn't be called by
// anything other than the class itself.
RingpopClient.prototype.scanForWedgedRequests = function scanForWedgedRequests() {
    var wedgedRequests = [];

    // Find all requests that have not been timed-out by TChannel.
    // Induce Ringpop timeout for wedged requests and cancel them.
    var requestIds = Object.keys(this.requestsById);
    var scanTime = this.date.now();
    for (var i = 0; i < requestIds.length; i++) {
        var request = this.requestsById[requestIds[i]];
        var timeInflight = scanTime - request.timestamp;
        var isWedged = timeInflight >= (this.config.get('wedgedRequestTimeout') ||
            Config.Defaults.wedgedRequestTimeout);
        if (isWedged) {
            delete this.requestsById[request.id];
            request.cancel();
            wedgedRequests.push(request);
        }
    }

    if (wedgedRequests.length > 0) {
        this.logger.error('ringpop canceled wedged requests', {
            local: this.ringpop.whoami(),
            numWedgedRequests: wedgedRequests.length,
            wedgedRequests: wedgedRequestsToLog()
        });
        this._emitWedgedError();
    }

    return wedgedRequests;

    // Log the first N requests only. We don't want to flood
    // our pipes.
    function wedgedRequestsToLog() {
        return _.take(wedgedRequests, 10).map(function toLog(request) {
            return request.toLog();
        });
    }
};

RingpopClient.prototype._emitWedgedError = function _emitWedgedError() {
    var errorListeners = this.ringpop.listeners('error');
    if (errorListeners.length > 0) {
        this.ringpop.emit('error',
            RingpopErrors.PotentiallyWedgedRequestsError());
    }
};

/* jshint maxparams: 5 */
RingpopClient.prototype._request = function _request(opts, endpoint, head, body, callback) {
    var self = this;

    if (!this.wedgedTimer) {
        this._scheduleWedgedTimer();
    }

    var request = new ClientRequest(this, opts, endpoint, head, body,
        this.date, onSend);

    // Evaluate the number of inflight requests. If we've got more than the
    // allowable limit it's a strong indication that at least some are wedged.
    // Apply backpressure until the amount of inflight requests is reduced
    // by cancellation or otherwise.
    var inflightCurrent = Object.keys(this.requestsById).length;
    var inflightMax = this.config.get('inflightClientRequestsLimit');
    if (inflightCurrent >= inflightMax) {
        process.nextTick(function onTick() {
            callback(ClientErrors.ClientRequestsLimitError({
                inflightCurrent: inflightCurrent,
                inflightMax: inflightMax,
                endpoint: endpoint
            }));
        });
        return;
    }

    // Registering the request by its ID must happen at the precise
    // moment before we know it will be "inflight."
    this.requestsById[request.id] = request;
    request.send();

    function onSend() {
        delete self.requestsById[request.id];
        var args = Array.prototype.splice.call(arguments, 0);
        callback.apply(null, args);
    }
};

RingpopClient.prototype._scheduleWedgedTimer = function _scheduleWedgedTimer() {
    var self = this;
    this.wedgedTimer = this.timers.setTimeout(function onTimeout() {
        if (self.isDestroyed) return;
        self.scanForWedgedRequests();
        self._scheduleWedgedTimer();
    }, this.config.get('wedgedTimerInterval') || Config.Defaults.wedgedTimerInterval);
};

module.exports = RingpopClient;
<|endoftext|>