<commit_before>(function (jsPDFAPI) {
    'use strict';
        var jsPDFAPI = jsPDF.API;

        var glyID = [0];
        var data;
      /**************************************************/
      /* function : toHex                               */
      /* comment : Replace str with a hex string.       */
      /**************************************************/
      function toHex(str) {
        var hex = '';
        for (var i = 0; i < str.length; i++) {
          hex += '' + str.charCodeAt(i).toString(16);
        }
        return hex;
      }

      /***************************************************************************************************/
      /* function : pdfEscape16                                                                          */
      /* comment : The character id of a 2-byte string is converted to a hexadecimal number by obtaining */
      /*   the corresponding glyph id and width, and then adding padding to the string.                  */
      /***************************************************************************************************/
          var pdfEscape16 = function (text, font) {
            var widths = font.metadata.Unicode.widths;;
            var padz = ["", "0", "00", "000", "0000"];
            var ar = [""];
            for (var i = 0, l = text.length, t; i < l; ++i) {
              t = font.metadata.characterToGlyph(text.charCodeAt(i))
              glyID.push(t);
              if (widths.indexOf(t) == -1) {
                widths.push(t);
                widths.push([parseInt(font.metadata.widthOfGlyph(t), 10)]);
              }
              if (t == '0') { //Spaces are not allowed in cmap.
                return ar.join("");
              } else {
                t = t.toString(16);
                ar.push(padz[4 - t.length], t);
              }
            }
            return ar.join("");
          };

          var identityHFunction = function (font, out, newObject) {

              if ((font.metadata instanceof TTFFont) && (font.encoding === 'Identity-H')) { //Tag with Identity-H
  				var widths = font.metadata.Unicode.widths;
                var data = font.metadata.subset.encode(glyID);
                var pdfOutput = data;
                var pdfOutput2 = "";
                for (var i = 0; i < pdfOutput.length; i++) {
                  pdfOutput2 += String.fromCharCode(pdfOutput[i]);
                }
                var fontTable = newObject();
                out('<<');
                out('/Length ' + pdfOutput2.length);
                out('/Length1 ' + pdfOutput2.length);
                out('>>');
                out('stream');
                out(pdfOutput2);
                out('endstream');
                out('endobj');
                var fontDescriptor = newObject();
                out('<<');
                out('/Type /FontDescriptor');
                out('/FontName /' + font.fontName);
                out('/FontFile2 ' + fontTable + ' 0 R');
                out('/FontBBox ' + PDFObject.convert(font.metadata.bbox));
                out('/Flags ' + font.metadata.flags);
                out('/StemV ' + font.metadata.stemV);
                out('/ItalicAngle ' + font.metadata.italicAngle);
                out('/Ascent ' + font.metadata.ascender);
                out('/Descent ' + font.metadata.decender);
                out('/CapHeight ' + font.metadata.capHeight);
                out('>>');
                out('endobj');
                var DescendantFonts = newObject();
                out('<</DW 1000/Subtype/CIDFontType2/CIDSystemInfo<</Supplement 0/Registry(Adobe)/Ordering(' + font.encoding + ')>>/Type/Font/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R/W' + PDFObject.convert(widths) + '/CIDToGIDMap/' + font.encoding + '>>');
                out('endobj');
                font.objectNumber = newObject();
                out('<</Subtype/Type0/Type/Font/BaseFont/' + font.fontName + '/Encoding/' + font.encoding + '/DescendantFonts[' + DescendantFonts + ' 0 R]>>');
                out('endobj');
                font.isAlreadyPutted = true;
              }
          }

          jsPDFAPI.events.push([
          	'putFont'
          	,function(args) {
          		identityHFunction(args.font, args.out, args.newObject);
          }]);

        var winAnsiEncodingFunction = function (font, out, newObject) {

            if ((font.metadata instanceof TTFFont) && font.encoding === 'WinAnsiEncoding') { //Tag with WinAnsi encoding
				var widths = font.metadata.Unicode.widths;
		   var data = font.metadata.rawData;
              var pdfOutput = data;
              var pdfOutput2 = "";
              for (var i = 0; i < pdfOutput.length; i++) {
                pdfOutput2 += String.fromCharCode(pdfOutput[i]);
              }
              var fontTable = newObject();
              out('<<');
              out('/Length ' + pdfOutput2.length);
              out('/Length1 ' + pdfOutput2.length);
              out('>>');
              out('stream');
              out(pdfOutput2);
              out('endstream');
              out('endobj');
              var fontDescriptor = newObject();
              out('<<');
              out('/Descent ' + font.metadata.decender);
              out('/CapHeight ' + font.metadata.capHeight);
              out('/StemV ' + font.metadata.stemV);
              out('/Type /FontDescriptor');
              out('/FontFile2 ' + fontTable + ' 0 R');
              out('/Flags 96');
              out('/FontBBox ' + PDFObject.convert(font.metadata.bbox));
              out('/FontName /' + font.fontName);
              out('/ItalicAngle ' + font.metadata.italicAngle);
              out('/Ascent ' + font.metadata.ascender);
              out('>>');
              out('endobj');
              font.objectNumber = newObject();
              for (var i = 0; i < font.metadata.hmtx.widths.length; i++) {
                font.metadata.hmtx.widths[i] = parseInt(font.metadata.hmtx.widths[i] * (1000 / font.metadata.head.unitsPerEm)); //Change the width of Em units to Point units.
              }
              out('<</Subtype/TrueType/Type/Font/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R' + '/Encoding/' + font.encoding + ' /FirstChar 29 /LastChar 255 /Widths ' + PDFObject.convert(font.metadata.hmtx.widths) + '>>');
              out('endobj');
              font.isAlreadyPutted = true;
            }
        }

        jsPDFAPI.events.push([
        	'putFont'
        	,function(args) {
        		winAnsiEncodingFunction(args.font, args.out, args.newObject);
        	}
        ]);

        var utf8TextFunction = function (args) {
            var text = args.text;
            var x = args.x;
            var y = args.y;
            var options = args.options || {};
            var tmp;
            var mutex = args.mutex || {};

            var pdfEscape = mutex.pdfEscape;
            var activeFontKey = mutex.activeFontKey;
            var fonts = mutex.fonts;
            var key, sum = 0,
              fontSize = mutex.activeFontSize, lineHeight = 0,
              axisCache;

            var str = '',
            v = 0,
            s = 0,
            tkey, widths, cmapConfirm;
            var strText = ''
            var attr;
            var key = activeFontKey;
            var encoding = fonts[key].encoding;

            if (fonts[key].encoding !== 'Identity-H') {
                return {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };
            }
            var i = 0;
            strText = text;

            key = (attr) ? getFont(attr.font, attr.fontStyle) : activeFontKey;
            if (Object.prototype.toString.call(text) === '[object Array]') {
				strText = text[0];
			}
          for (s = 0; s < strText.length; s += 1) {
          if (fonts[key].metadata.hasOwnProperty('cmap')) {
			  cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)];
			  /*
			 if (Object.prototype.toString.call(text) === '[object Array]') {
                var i = 0;
               // for (i = 0; i < text.length; i += 1) {
                    if (Object.prototype.toString.call(text[s]) === '[object Array]') {
						cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s][0].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
                    } else {

                    }
                //}

            } else {
				cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
            }*/
          }
            if (!cmapConfirm) {
                if (strText[s].charCodeAt(0) < 256 && fonts[key].metadata.hasOwnProperty('Unicode')) {
                  str += strText[s];
                } else {
                  str += '';
                }
            } else {
            str += strText[s];
            }
          }
          var result = '';
          if ((parseInt(key.slice(1)) < 14) || encoding === 'WinAnsiEncoding') { //For the default 13 font
                result = toHex(pdfEscape(str, key));
              } else if (encoding === 'Identity-H') {
                  result = pdfEscape16(str, fonts[key]);
              }
              mutex.isHex = true;

            return {
                text : result,
                x : x,
                y : y,
                options: options,
                mutex: mutex
            };
        }

        var utf8EscapeFunction = function(parms) {
        	var text = parms.text,
            x = parms.x,
            y = parms.y,
            options = parms.options,
            mutex = parms.mutex
            var lang = options.lang;
            var tmpText = [];
            var tempPayLoad;
            var args = {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };

            if (Object.prototype.toString.call(text) === '[object Array]') {
                var i = 0;
                for (i = 0; i < text.length; i += 1) {
                    if (Object.prototype.toString.call(text[i]) === '[object Array]') {
                        if (text[i].length === 3) {
                            tmpText.push([utf8TextFunction(Object.assign({}, args, {text: text[i][0]})).text, text[i][1], text[i][2]]);
                        } else {
                            tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text[i]})).text);
                        }
                    } else {
                        tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text})).text[i]);
                    }
                }
                parms.text = tmpText;

            } else {
                parms.text = utf8TextFunction(Object.assign({}, args, {text: text})).text;
            }
        }

        jsPDFAPI.events.push([
        	'postProcessText'
        	,utf8EscapeFunction
        ]);

})(jsPDF);
<commit_msg>Update utf8.js<commit_after>(function (jsPDFAPI) {
    'use strict';
        var jsPDFAPI = jsPDF.API;

        var glyID = [0];
        var data;
      /**************************************************/
      /* function : toHex                               */
      /* comment : Replace str with a hex string.       */
      /**************************************************/
      function toHex(str) {
        var hex = '';
        for (var i = 0; i < str.length; i++) {
          hex += '' + str.charCodeAt(i).toString(16);
        }
        return hex;
      }

      /***************************************************************************************************/
      /* function : pdfEscape16                                                                          */
      /* comment : The character id of a 2-byte string is converted to a hexadecimal number by obtaining */
      /*   the corresponding glyph id and width, and then adding padding to the string.                  */
      /***************************************************************************************************/
          var pdfEscape16 = function (text, font) {
            var widths = font.metadata.Unicode.widths;;
            var padz = ["", "0", "00", "000", "0000"];
            var ar = [""];
            for (var i = 0, l = text.length, t; i < l; ++i) {
              t = font.metadata.characterToGlyph(text.charCodeAt(i))
              glyID.push(t);
              if (widths.indexOf(t) == -1) {
                widths.push(t);
                widths.push([parseInt(font.metadata.widthOfGlyph(t), 10)]);
              }
              if (t == '0') { //Spaces are not allowed in cmap.
                return ar.join("");
              } else {
                t = t.toString(16);
                ar.push(padz[4 - t.length], t);
              }
            }
            return ar.join("");
          };

          var identityHFunction = function (font, out, newObject) {

              if ((font.metadata instanceof TTFFont) && (font.encoding === 'Identity-H')) { //Tag with Identity-H
  				var widths = font.metadata.Unicode.widths;
                var data = font.metadata.subset.encode(glyID);
                var pdfOutput = data;
                var pdfOutput2 = "";
                for (var i = 0; i < pdfOutput.length; i++) {
                  pdfOutput2 += String.fromCharCode(pdfOutput[i]);
                }
                var fontTable = newObject();
                out('<<');
                out('/Length ' + pdfOutput2.length);
                out('/Length1 ' + pdfOutput2.length);
                out('>>');
                out('stream');
                out(pdfOutput2);
                out('endstream');
                out('endobj');
                var fontDescriptor = newObject();
                out('<<');
                out('/Type /FontDescriptor');
                out('/FontName /' + font.fontName);
                out('/FontFile2 ' + fontTable + ' 0 R');
                out('/FontBBox ' + PDFObject.convert(font.metadata.bbox));
                out('/Flags ' + font.metadata.flags);
                out('/StemV ' + font.metadata.stemV);
                out('/ItalicAngle ' + font.metadata.italicAngle);
                out('/Ascent ' + font.metadata.ascender);
                out('/Descent ' + font.metadata.decender);
                out('/CapHeight ' + font.metadata.capHeight);
                out('>>');
                out('endobj');
                var DescendantFonts = newObject();
                out('<</DW 1000/Subtype/CIDFontType2/CIDSystemInfo<</Supplement 0/Registry(Adobe)/Ordering(' + font.encoding + ')>>/Type/Font/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R/W' + PDFObject.convert(widths) + '/CIDToGIDMap/' + font.encoding + '>>');
                out('endobj');
                font.objectNumber = newObject();
                out('<</Subtype/Type0/Type/Font/BaseFont/' + font.fontName + '/Encoding/' + font.encoding + '/DescendantFonts[' + DescendantFonts + ' 0 R]>>');
                out('endobj');
                font.isAlreadyPutted = true;
              }
          }

          jsPDFAPI.events.push([
          	'putFont'
          	,function(args) {
          		identityHFunction(args.font, args.out, args.newObject);
          }]);

        var winAnsiEncodingFunction = function (font, out, newObject) {

            if ((font.metadata instanceof TTFFont) && font.encoding === 'WinAnsiEncoding') { //Tag with WinAnsi encoding
				var widths = font.metadata.Unicode.widths;
		   var data = font.metadata.rawData;
              var pdfOutput = data;
              var pdfOutput2 = "";
              for (var i = 0; i < pdfOutput.length; i++) {
                pdfOutput2 += String.fromCharCode(pdfOutput[i]);
              }
              var fontTable = newObject();
              out('<<');
              out('/Length ' + pdfOutput2.length);
              out('/Length1 ' + pdfOutput2.length);
              out('>>');
              out('stream');
              out(pdfOutput2);
              out('endstream');
              out('endobj');
              var fontDescriptor = newObject();
              out('<<');
              out('/Descent ' + font.metadata.decender);
              out('/CapHeight ' + font.metadata.capHeight);
              out('/StemV ' + font.metadata.stemV);
              out('/Type /FontDescriptor');
              out('/FontFile2 ' + fontTable + ' 0 R');
              out('/Flags 96');
              out('/FontBBox ' + PDFObject.convert(font.metadata.bbox));
              out('/FontName /' + font.fontName);
              out('/ItalicAngle ' + font.metadata.italicAngle);
              out('/Ascent ' + font.metadata.ascender);
              out('>>');
              out('endobj');
              font.objectNumber = newObject();
              for (var i = 0; i < font.metadata.hmtx.widths.length; i++) {
                font.metadata.hmtx.widths[i] = parseInt(font.metadata.hmtx.widths[i] * (1000 / font.metadata.head.unitsPerEm)); //Change the width of Em units to Point units.
              }
              out('<</Subtype/TrueType/Type/Font/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R' + '/Encoding/' + font.encoding + ' /FirstChar 29 /LastChar 255 /Widths ' + PDFObject.convert(font.metadata.hmtx.widths) + '>>');
              out('endobj');
              font.isAlreadyPutted = true;
            }
        }

        jsPDFAPI.events.push([
        	'putFont'
        	,function(args) {
        		winAnsiEncodingFunction(args.font, args.out, args.newObject);
        	}
        ]);

        var utf8TextFunction = function (args) {
            var text = args.text;
            var x = args.x;
            var y = args.y;
            var options = args.options || {};
            var tmp;
            var mutex = args.mutex || {};

            var pdfEscape = mutex.pdfEscape;
            var activeFontKey = mutex.activeFontKey;
            var fonts = mutex.fonts;
            var key, sum = 0,
              fontSize = mutex.activeFontSize, lineHeight = 0,
              axisCache;

            var str = '',
            v = 0,
            s = 0,
            tkey, widths, cmapConfirm;
            var strText = ''
            var attr;
            var key = activeFontKey;
            var encoding = fonts[key].encoding;

            if (fonts[key].encoding !== 'Identity-H') {
                return {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };
            }
            var i = 0;
            strText = text;

            key = (attr) ? getFont(attr.font, attr.fontStyle) : activeFontKey;
            if (Object.prototype.toString.call(text) === '[object Array]') {
				strText = text[0];
			}
          for (s = 0; s < strText.length; s += 1) {
          if (fonts[key].metadata.hasOwnProperty('cmap')) {
			  cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)];
			  /*
			 if (Object.prototype.toString.call(text) === '[object Array]') {
                var i = 0;
               // for (i = 0; i < text.length; i += 1) {
                    if (Object.prototype.toString.call(text[s]) === '[object Array]') {
						cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s][0].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
                    } else {

                    }
                //}

            } else {
				cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
            }*/
          }
            if (!cmapConfirm) {
                if (strText[s].charCodeAt(0) < 256 && fonts[key].metadata.hasOwnProperty('Unicode')) {
                  str += strText[s];
                } else {
                  str += '';
                }
            } else {
            str += strText[s];
            }
          }
          var result = '';
          if ((parseInt(key.slice(1)) < 14) || encoding === 'WinAnsiEncoding') { //For the default 13 font
                result = toHex(pdfEscape(str, key));
              } else if (encoding === 'Identity-H') {
                  result = pdfEscape16(str, fonts[key]);
              }
              mutex.isHex = true;

            return {
                text : result,
                x : x,
                y : y,
                options: options,
                mutex: mutex
            };
        }

        var utf8EscapeFunction = function(parms) {
        	var text = parms.text,
            x = parms.x,
            y = parms.y,
            options = parms.options,
            mutex = parms.mutex
            var lang = options.lang;
            var tmpText = [];
            var tempPayLoad;
            var args = {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };

            if (Object.prototype.toString.call(text) === '[object Array]') {
                var i = 0;
                for (i = 0; i < text.length; i += 1) {
                    if (Object.prototype.toString.call(text[i]) === '[object Array]') {
                        if (text[i].length === 3) {
                            tmpText.push([utf8TextFunction(Object.assign({}, args, {text: text[i][0]})).text, text[i][1], text[i][2]]);
                        } else {
                            tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text[i]})).text);
                        }
                    } else {
                        tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text[i]})).text);
                    }
                }
                parms.text = tmpText;

            } else {
                parms.text = utf8TextFunction(Object.assign({}, args, {text: text})).text;
            }
        }

        jsPDFAPI.events.push([
        	'postProcessText'
        	,utf8EscapeFunction
        ]);

})(jsPDF);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

let DEBUG = 1;
if (DEBUG)
  debug = function (s) { dump("-*- adb service worker: " + s + "\n"); };
else
  debug = function (s) { };

var libadb = (function() {
  let library = null;
  let findDevice = null;
  let setupDevice = null;
  let setupPath = null;

  return {
    loadLib: function(path) {
      debug('Lib path: ' + path);
      if (library) {
        return;
      }

      library = ctypes.open(path);
      findDevice  = library.declare('findDevice',  ctypes.default_abi, ctypes.int);
      setupDevice = library.declare('setupDevice', ctypes.default_abi, ctypes.int);
      setupPath   = library.declare('setupPath',   ctypes.default_abi, ctypes.void_t, ctypes.jschar.ptr);
    },

    findDevice: function() {
      if (findDevice) {
        return findDevice();
      }

      return 0;
    },

    setupDevice: function() {
      if (setupDevice) {
        return setupDevice();
      }

      return 0;
    },

    setupPath: function(adbPath) {
      if (setupPath) {
        return setupPath(adbPath);
      }

      return;
    }
  };
})();

let connected = false;

self.onmessage = function(e) {
  debug('Receive message: ' + e.data.cmd);
  let id = e.data.id;
  let cmd = e.data.cmd;

  switch (cmd) {
    case 'loadlib':
      // Load adb service library
      libadb.loadLib(e.data.libPath);
      // Set the path of adb executive file
      libadb.setupPath(e.data.adbPath);
      postMessage({
        id: id,
        result: true
      });
      break;
    case 'findDevice':
      postMessage({
        id: id,
        result: libadb.findDevice()
      });
      break;
    case 'setupDevice':
      let result = libadb.setupDevice();

      postMessage({
        id: id,
        result: result
      });

      setConnected(!!result);
      break;
    default:
      postMessage({
        id: id,
        message: 'No handle for \'' + cmd + '\''
      });
      break;
  }
};

/**
 * Change connected state, and send 'statechange' message if changed.
 */
function setConnected(newState) {
  let oldState = connected;
  connected = newState;

  if (oldState !== connected) {
    debug('Connection state is changed!');
    postMessage({
      cmd: 'statechange',
      connected: connected
    });
  }
}

setInterval(function checkConnectState() {
  if (!libadb.findDevice()) {
    setConnected(false);
  } else if (!libadb.setupDevice()) {
    setConnected(false);
  } else {
    setConnected(true);
  }
}, 2000);

debug('ADB Service worker is inited.');

<commit_msg>Update c function declaration<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

let DEBUG = 1;
if (DEBUG)
  debug = function (s) { dump("-*- adb service worker: " + s + "\n"); };
else
  debug = function (s) { };

var libadb = (function() {
  let library = null;
  let findDevice = null;
  let setupDevice = null;
  let setupPath = null;

  return {
    loadLib: function(path) {
      debug('Lib path: ' + path);
      if (library) {
        return;
      }

      library = ctypes.open(path);
      findDevice  = library.declare('findDevice',  ctypes.default_abi, ctypes.int);
      setupDevice = library.declare('setupDevice', ctypes.default_abi, ctypes.int);
      setupPath   = library.declare('setupPath',   ctypes.default_abi, ctypes.void_t, ctypes.char.ptr);
    },

    findDevice: function() {
      if (findDevice) {
        return findDevice();
      }

      return 0;
    },

    setupDevice: function() {
      if (setupDevice) {
        return setupDevice();
      }

      return 0;
    },

    setupPath: function(adbPath) {
      if (setupPath) {
        return setupPath(adbPath);
      }

      return;
    }
  };
})();

let connected = false;

self.onmessage = function(e) {
  debug('Receive message: ' + e.data.cmd);
  let id = e.data.id;
  let cmd = e.data.cmd;

  switch (cmd) {
    case 'loadlib':
      // Load adb service library
      libadb.loadLib(e.data.libPath);
      // Set the path of adb executive file
      libadb.setupPath(e.data.adbPath);
      postMessage({
        id: id,
        result: true
      });
      break;
    case 'findDevice':
      postMessage({
        id: id,
        result: libadb.findDevice()
      });
      break;
    case 'setupDevice':
      let result = libadb.setupDevice();

      postMessage({
        id: id,
        result: result
      });

      setConnected(!!result);
      break;
    default:
      postMessage({
        id: id,
        message: 'No handle for \'' + cmd + '\''
      });
      break;
  }
};

/**
 * Change connected state, and send 'statechange' message if changed.
 */
function setConnected(newState) {
  let oldState = connected;
  connected = newState;

  if (oldState !== connected) {
    debug('Connection state is changed!');
    postMessage({
      cmd: 'statechange',
      connected: connected
    });
  }
}

setInterval(function checkConnectState() {
  if (!libadb.findDevice()) {
    setConnected(false);
  } else if (!libadb.setupDevice()) {
    setConnected(false);
  } else {
    setConnected(true);
  }
}, 2000);

debug('ADB Service worker is inited.');

<|endoftext|>


<commit_before>/*
 * MIT License
 *
 * Copyright (c) 2017-2017 Jan Dockx
 *
 * 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.
 */

const Contract = require('@toryt/contracts-iii')
const validator = require('validator')
const validate = require('./validate')
const deepEql = require('deeper')

const MAX_PORT = 65535

const validDnsSdAttributeNamePattern = /^[ -<>-@[-~]+$/

function isValidDnsSdAttributeValue (a) {
  return a === true || a === false || a === '' || (!!a && typeof a === 'string')
}

/**
 * Usually the type and instance are in the same domain, but that is not a condition of DNS-SD.
 */
const ServiceInstanceContract = new Contract({
  pre: [
    function (kwargs) { return typeof kwargs === 'object' },
    function (kwargs) { return validate.isServiceType(kwargs.type) },
    function (kwargs) { return validate.isServiceInstance(kwargs.instance) },
    function (kwargs) { return validator.isFQDN(kwargs.host) },
    function (kwargs) { return validate.isNatural(kwargs.port, MAX_PORT) },
    function (kwargs) { return validate.isNatural(kwargs.priority) },
    function (kwargs) { return validate.isNatural(kwargs.weight) },
    function (kwargs) { return !!kwargs.details },
    function (kwargs) { return typeof kwargs.details === 'object' },
    function (kwargs) { return Object.keys(kwargs.details).every(k => validDnsSdAttributeNamePattern.test(k)) },
    function (kwargs) { return Object.keys(kwargs.details).every(k => isValidDnsSdAttributeValue(kwargs.details[k])) }
  ],
  post: [
    function (kwargs) { return this.type === kwargs.type },
    function (kwargs) { return this.instance === kwargs.instance },
    function (kwargs) { return this.host === kwargs.host },
    function (kwargs) { return this.port === kwargs.port },
    function (kwargs) { return this.priority === kwargs.priority },
    function (kwargs) { return this.weight === kwargs.weight },
    function (kwargs) { return this.details !== kwargs.details },
    function (kwargs) { return deepEql(this.details, kwargs.details) }
  ],
  exception: [
    /* istanbul ignore next : should not throw an exception */
    function () { return false }
  ]
})

const ServiceInstanceImplementation = function (kwargs) {
  this.type = kwargs.type
  this.instance = kwargs.instance
  this.host = kwargs.host
  this.port = kwargs.port
  this.priority = kwargs.priority
  this.weight = kwargs.weight
  this.details = Object.assign({}, kwargs.details)
  Object.freeze(this)
}

ServiceInstanceImplementation.prototype.type = null
ServiceInstanceImplementation.prototype.instance = null
ServiceInstanceImplementation.prototype.host = null
ServiceInstanceImplementation.prototype.port = null
ServiceInstanceImplementation.prototype.priority = null
ServiceInstanceImplementation.prototype.weight = null
ServiceInstanceImplementation.prototype.details = null

ServiceInstanceImplementation.prototype.toJSON = function () {
  return {
    type: this.type,
    instance: this.instance,
    host: this.host,
    port: this.port,
    priority: this.priority,
    weight: this.weight,
    details: Object.assign({}, this.details)
  }
}

const ServiceInstance = ServiceInstanceContract.implementation(ServiceInstanceImplementation)

ServiceInstance.prototype.invariants = [
  si => validate.isServiceType(si.type),
  si => validate.isServiceInstance(si.instance),
  si => validator.isFQDN(si.host),
  si => validate.isNatural(si.port, MAX_PORT),
  si => validate.isNatural(si.priority),
  si => validate.isNatural(si.weight),
  si => !!si.details,
  si => typeof si.details === 'object',
  si => Object.keys(si.details).every(k => validDnsSdAttributeNamePattern.test(k)),
  si => Object.keys(si.details).every(k => isValidDnsSdAttributeValue(si.details[k])),
  si => si.toJSON().type === si.type,
  si => si.toJSON().instance === si.instance,
  si => si.toJSON().host === si.host,
  si => si.toJSON().port === si.port,
  si => si.toJSON().priority === si.priority,
  si => si.toJSON().weight === si.weight,
  si => si.toJSON().details !== si.details,
  si => deepEql(si.toJSON().details, si.details)
]

module.exports = ServiceInstance
<commit_msg>use contracts iv in ServiceInstance (lost coverage, because postconditions are not checked now)<commit_after>/*
 * MIT License
 *
 * Copyright (c) 2017-2017 Jan Dockx
 *
 * 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.
 */

const Contract = require('@toryt/contracts-iv')
const validator = require('validator')
const validate = require('./validate')
const deepEql = require('deeper')

const MAX_PORT = 65535

const validDnsSdAttributeNamePattern = /^[ -<>-@[-~]+$/

function isValidDnsSdAttributeValue (a) {
  return a === true || a === false || a === '' || (!!a && typeof a === 'string')
}

/**
 * Usually the type and instance are in the same domain, but that is not a condition of DNS-SD.
 */
const ServiceInstanceContract = new Contract({
  pre: [
    function (kwargs) { return typeof kwargs === 'object' },
    function (kwargs) { return validate.isServiceType(kwargs.type) },
    function (kwargs) { return validate.isServiceInstance(kwargs.instance) },
    function (kwargs) { return validator.isFQDN(kwargs.host) },
    function (kwargs) { return validate.isNatural(kwargs.port, MAX_PORT) },
    function (kwargs) { return validate.isNatural(kwargs.priority) },
    function (kwargs) { return validate.isNatural(kwargs.weight) },
    function (kwargs) { return !!kwargs.details },
    function (kwargs) { return typeof kwargs.details === 'object' },
    function (kwargs) { return Object.keys(kwargs.details).every(k => validDnsSdAttributeNamePattern.test(k)) },
    function (kwargs) { return Object.keys(kwargs.details).every(k => isValidDnsSdAttributeValue(kwargs.details[k])) }
  ],
  post: [
    function (kwargs) { return this.type === kwargs.type },
    function (kwargs) { return this.instance === kwargs.instance },
    function (kwargs) { return this.host === kwargs.host },
    function (kwargs) { return this.port === kwargs.port },
    function (kwargs) { return this.priority === kwargs.priority },
    function (kwargs) { return this.weight === kwargs.weight },
    function (kwargs) { return this.details !== kwargs.details },
    function (kwargs) { return deepEql(this.details, kwargs.details) }
  ],
  exception: [
    /* istanbul ignore next : should not throw an exception */
    function () { return false }
  ]
})

const ServiceInstanceImplementation = function (kwargs) {
  this.type = kwargs.type
  this.instance = kwargs.instance
  this.host = kwargs.host
  this.port = kwargs.port
  this.priority = kwargs.priority
  this.weight = kwargs.weight
  this.details = Object.assign({}, kwargs.details)
  Object.freeze(this)
}

ServiceInstanceImplementation.prototype.type = null
ServiceInstanceImplementation.prototype.instance = null
ServiceInstanceImplementation.prototype.host = null
ServiceInstanceImplementation.prototype.port = null
ServiceInstanceImplementation.prototype.priority = null
ServiceInstanceImplementation.prototype.weight = null
ServiceInstanceImplementation.prototype.details = null

ServiceInstanceImplementation.prototype.toJSON = function () {
  return {
    type: this.type,
    instance: this.instance,
    host: this.host,
    port: this.port,
    priority: this.priority,
    weight: this.weight,
    details: Object.assign({}, this.details)
  }
}

const ServiceInstance = ServiceInstanceContract.implementation(ServiceInstanceImplementation)

ServiceInstance.prototype.invariants = [
  si => validate.isServiceType(si.type),
  si => validate.isServiceInstance(si.instance),
  si => validator.isFQDN(si.host),
  si => validate.isNatural(si.port, MAX_PORT),
  si => validate.isNatural(si.priority),
  si => validate.isNatural(si.weight),
  si => !!si.details,
  si => typeof si.details === 'object',
  si => Object.keys(si.details).every(k => validDnsSdAttributeNamePattern.test(k)),
  si => Object.keys(si.details).every(k => isValidDnsSdAttributeValue(si.details[k])),
  si => si.toJSON().type === si.type,
  si => si.toJSON().instance === si.instance,
  si => si.toJSON().host === si.host,
  si => si.toJSON().port === si.port,
  si => si.toJSON().priority === si.priority,
  si => si.toJSON().weight === si.weight,
  si => si.toJSON().details !== si.details,
  si => deepEql(si.toJSON().details, si.details)
]

module.exports = ServiceInstance
<|endoftext|>


<commit_before>import { default as Wegbier } from '../node_modules/wegbier/dist/Wegbier.js';
import { default as fUtils } from './fUtils/index.js';
import { default as utils } from './utils.js';

let defaults = {
  config: {
    // main container for defining the boundaries of the scrollable area and
    // setting the event listeners. is expected to be a simple DOM node
    container: null,

    // TODO remove, the scrollable should be a list of items
    moveable: null,

    // decide what axis to allow scrolling on, gets translated into an array by
    // the class constructor
    axis: 'y',

    // lock movement in one direction. relevant if more touch/scroll libraries
    // are at the same spot and only the locked element should move
    lock: false,

    // speed for animated scrolling, in px/frame
    maxScrollPxPerFrame: 50,

    // the distance in px from the target position, at which animated scroll starts slowing down
    scrollToSlowingDistance: 300
  },

  private: {
    wegbier: null,
    boundHandlers: {},
    axis: ['x', 'y'],
    position: { x: 0, y: 0 },
    positionLimits: { x: 0, y: 0},
    isScrollLocked: false,

    animatedScroll: {
      isScrolling: false,
      speed: 0,
      maxSpeed: 0,
      direction: {
        radians: 0,
        x: 0,         // component weight in x, effectively cos(radians)
        y: 0          // component weight in y, effectively sin(radians)
      },
      startingPosition: { x: 0, y: 0 },
      targetPosition: { x: 0, y: 0 },
      totalDistance: 0
    }
  }
};

export default class Mustafas {
  constructor(config) {
    this._config = fUtils.cloneDeep(defaults.config);
    this._private = fUtils.cloneDeep(defaults.private);

    if (config) fUtils.mergeDeep(this._config, config);
    this._private.axis = this._config.axis.split('');

    // mustafa needs an actual DOMNode as moveable, whereas wegbier needs an area.
    // it's bad manners to modify the "moveable" property of the config passed as parameter to the
    // constructor. so we clone a separate config object and modify that one instead
    let configWegbier = fUtils.cloneDeep(defaults.config);
    if (config) fUtils.mergeDeep(configWegbier, this._config);
    configWegbier.moveable = this._getMoveableSize();

    this._private.wegbier = new Wegbier(configWegbier);

    this._calculatePositionLimits();
    this._bindAnimatedScroll();
    this._bindEvents();
  }

  // PUBLIC

  resize() {
    this._calculatePositionLimits();

    let configWegbier = { moveable: this._getMoveableSize() };
    this._private.wegbier.refresh(configWegbier);
  }

  getScrollPosition() {
    return { left: this._private.position.x, top: this._private.position.y };
  }

  scrollTo(left, top, shouldAnimate, scrollSpeed) {
    if (this._private.isScrollLocked) return;

    if (this._private.animatedScroll.isScrolling) {
      this._stopAnimatedScroll();
    }

    if (shouldAnimate) {
      this._startAnimatedScroll( { x: left, y: top }, scrollSpeed );
    }
    else {
      this._private.wegbier.scrollTo( { x: left, y: top } );
    }
  }

  scrollBy(left, top, shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x +left, this._private.position.x +top, shouldAnimate, scrollSpeed);
  }

  scrollTop(shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x, 0, shouldAnimate, scrollSpeed);
  }

  scrollBottom(shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x, this._private.positionLimits.y, shouldAnimate, scrollSpeed);
  }

  // freezes the scroll on all axes, returns the resulting state of frozen-ness (boolean)
  freezeScroll(shouldFreeze) {
    if (shouldFreeze && this._private.animatedScroll.isScrolling) {
      this._stopAnimatedScroll();
    }

    // while the scroll is locked, mustafas doesn't update its coordinates (or the DOM node).
    // when unlocking, it uses its old coordinates to restore the wegbier's position.
    if (this._private.isScrollLocked && !shouldFreeze) {
      this._private.wegbier.scrollTo(this._private.position);
    }

    // shouldFreeze is treated as an optiona parameter defaulting to true
    this._private.isScrollLocked = shouldFreeze === false ? false : true;
  }

  destroy() {
    this._unbindEvents();
    this._private.wegbier.destroy();
    this._config.container = null;
    this._config.moveable = null;
  };

  // LIFECYCLE

  _calculatePositionLimits() {
    let positionLimits = {
      x: this._config.moveable.clientWidth - this._config.container.clientWidth,
      y: this._config.moveable.clientHeight - this._config.container.clientHeight
    }

    // only set limits for the axis we use, and on which the moveable is larger than the container
    this._forXY((xy) => {
      if (positionLimits[xy] > 0)
        this._private.positionLimits[xy] = -positionLimits[xy];
    });
  }

  _getMoveableSize() {
    return {
      width: this._config.moveable.clientWidth,
      height: this._config.moveable.clientHeight
    };
  }

  _bindEvents() {
    this._private.boundHandlers = {
      'wegbier:positionChanged': this._onPositionChanged.bind(this)
    };

    fUtils.forEach(this._private.boundHandlers, (handler, event) => {
      this._private.wegbier.addEventListener(event, handler);
    });
  }

  _unbindEvents() {
    fUtils.forEach(this._private.boundHandlers, (handler, event) => {
      this._private.wegbier.removeEventListener(event, handler);
    });
  }

  _onPositionChanged(event) {
    if (this._private.isScrollLocked) return;
    this._private.position.x = event.detail.x;
    this._private.position.y = event.detail.y;
    this._updateMoveablePosition();
  }

  // MOVEABLE MANIPULATION

  _updateMoveablePosition() {
    this._config.moveable.style.webkitTransform = 'translate3d(' + this._private.position.x + 'px, ' + this._private.position.y + 'px, 0px)';
  }

  // ANIMATED SCROLLING

  _bindAnimatedScroll() {
    this._private.boundAnimatedScroll = this._runAnimatedScroll.bind(this);
  }

  _startAnimatedScroll(targetPosition, scrollSpeed) {
    let animatedScroll = this._private.animatedScroll;

    cancelAnimationFrame(this._private.currentFrame);

    animatedScroll.startingPosition = {
      x: this._private.position.x,
      y: this._private.position.y
    }

    animatedScroll.targetPosition = {
      x: this._private.position.x,
      y: this._private.position.y
    }

    let validTargetPosition = this._nearestValidPosition(targetPosition);

    // only set a target position for axes on which scrolling is enabled.
    // otherwise, tarhet position remains the same as current position.
    this._forXY((xy) => {
      animatedScroll.targetPosition[xy] = validTargetPosition[xy];
    });

    animatedScroll.totalDistance = this._positionDistance(
      animatedScroll.startingPosition,
      animatedScroll.targetPosition
    );

    this._calculateScrollDirection();

    animatedScroll.maxSpeed = scrollSpeed > 0 ? scrollSpeed : this._config.maxScrollPxPerFrame;
    animatedScroll.speed = animatedScroll.maxSpeed;

    animatedScroll.isScrolling = true;

    this._private.currentFrame = requestAnimationFrame(this._private.boundAnimatedScroll);
  }

  _runAnimatedScroll() {
    let animatedScroll = this._private.animatedScroll;

    let distanceToTarget = this._positionDistance(
      this._private.position,
      animatedScroll.targetPosition
    );

    // slow down when close to target
    if (distanceToTarget < this._config.scrollToSlowingDistance) {
      animatedScroll.speed = animatedScroll.maxSpeed * (distanceToTarget/this._config.scrollToSlowingDistance);
    }

    // stop when on target
    if (distanceToTarget < 1) {
        this._stopAnimatedScroll();
        this.scrollTo(
          animatedScroll.targetPosition.x,
          animatedScroll.targetPosition.y
        );
    }
    // otherwise move towards target
    else {
      this._forXY((xy) => {
        this._private.position[xy] += animatedScroll.speed * animatedScroll.direction[xy];
      });
      this._private.wegbier.scrollTo(this._private.position);

      this._private.currentFrame = requestAnimationFrame(this._private.boundAnimatedScroll);
    }
  }

  _stopAnimatedScroll() {
    let animatedScroll = this._private.animatedScroll;

    animatedScroll.speed = 0;
    animatedScroll.isScrolling = false;

    cancelAnimationFrame(this._private.currentFrame);
  }

  _calculateScrollDirection() {
    let animatedScroll = this._private.animatedScroll,
      distance = { x: 0, y: 0 };

    // update the position, according to the speed component on each axis
    this._forXY((xy) => {
      distance[xy] = animatedScroll.targetPosition[xy] - animatedScroll.startingPosition[xy];
    });

    animatedScroll.direction.radians = Math.atan2(distance.y, distance.x);

    animatedScroll.direction.x = Math.cos(animatedScroll.direction.radians);
    animatedScroll.direction.y = Math.sin(animatedScroll.direction.radians);
  }

  // HELPERS

  _positionDistance(pos1, pos2) {
    return this._distance(pos1.x, pos1.y, pos2.x, pos2.y);
  }

  _distance(x1, y1, x2, y2) {
    return Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 );
  }

  _forXY(toExecute) {
    this._private.axis.forEach(toExecute);
  }

  _nearestValidPosition(position) {
    let result = { x: 0, y: 0 };

    this._forXY((xy) => {
      if (position[xy] > 0)
        result[xy] = 0;
      else if (position[xy] < this._private.positionLimits[xy])
        result[xy] = this._private.positionLimits[xy];
      else
        result[xy] = position[xy];
    });

    return result;
  }
};
<commit_msg>modified to make use of new methods in wegbier (esp. freezeScroll)<commit_after>import { default as Wegbier } from '../node_modules/wegbier/dist/Wegbier.js';
import { default as fUtils } from './fUtils/index.js';
import { default as utils } from './utils.js';

let defaults = {
  config: {
    // main container for defining the boundaries of the scrollable area and
    // setting the event listeners. is expected to be a simple DOM node
    container: null,

    // the moveable DOM node with the actual scrollable content
    moveable: null,

    // decide what axis to allow scrolling on, gets translated into an array by
    // the class constructor
    axis: 'y',

    // lock movement in one direction. relevant if more touch/scroll libraries
    // are at the same spot and only the locked element should move
    lock: false,

    // speed for animated scrolling, in px/frame
    maxScrollPxPerFrame: 50,

    // the distance in px from the target position, at which animated scroll starts slowing down
    scrollToSlowingDistance: 300
  },

  private: {
    wegbier: null,
    boundHandlers: {},
    axis: ['x', 'y'],
    position: { x: 0, y: 0 },
    positionLimits: { x: 0, y: 0},
    isScrollLocked: false,

    animatedScroll: {
      isScrolling: false,
      speed: 0,
      maxSpeed: 0,
      direction: {
        radians: 0,
        x: 0,         // component weight in x, effectively cos(radians)
        y: 0          // component weight in y, effectively sin(radians)
      },
      startingPosition: { x: 0, y: 0 },
      targetPosition: { x: 0, y: 0 },
      totalDistance: 0
    }
  }
};

export default class Mustafas {
  constructor(config) {
    this._config = fUtils.cloneDeep(defaults.config);
    this._private = fUtils.cloneDeep(defaults.private);

    if (config) fUtils.mergeDeep(this._config, config);
    this._private.axis = this._config.axis.split('');

    // mustafa needs an actual DOMNode as moveable, whereas wegbier needs an area.
    // it's bad manners to modify the "moveable" property of the config passed as parameter to the
    // constructor. so we clone a separate config object and modify that one instead
    let configWegbier = fUtils.cloneDeep(defaults.config);
    if (config) fUtils.mergeDeep(configWegbier, this._config);
    configWegbier.moveable = this._getMoveableSize();

    this._private.wegbier = new Wegbier(configWegbier);

    this._calculatePositionLimits();
    this._bindAnimatedScroll();
    this._bindEvents();
  }

  // PUBLIC

  resize() {
    this._calculatePositionLimits();

    let configWegbier = { moveable: this._getMoveableSize() };
    this._private.wegbier.refresh(configWegbier);
  }

  getScrollPosition() {
    return { left: this._private.position.x, top: this._private.position.y };
  }

  scrollTo(left, top, shouldAnimate, scrollSpeed) {
    if (this._private.isScrollLocked) return;

    if (this._private.animatedScroll.isScrolling) {
      this._stopAnimatedScroll();
    }

    if (shouldAnimate) {
      this._startAnimatedScroll( { x: left, y: top }, scrollSpeed );
    }
    else {
      this._private.wegbier.scrollTo( { x: left, y: top } );
    }
  }

  scrollBy(left, top, shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x +left, this._private.position.x +top, shouldAnimate, scrollSpeed);
  }

  scrollTop(shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x, 0, shouldAnimate, scrollSpeed);
  }

  scrollBottom(shouldAnimate, scrollSpeed) {
    this.scrollTo(this._private.position.x, this._private.positionLimits.y, shouldAnimate, scrollSpeed);
  }

  // freezes the scroll on all axes
  freezeScroll(shouldFreeze) {
    if (shouldFreeze && this._private.animatedScroll.isScrolling) {
      this._stopAnimatedScroll();
    }
    this._private.wegbier.freezeScroll(shouldFreeze);

    // shouldFreeze is treated as an optiona parameter defaulting to true
    this._private.isScrollLocked = shouldFreeze === false ? false : true;
  }

  destroy() {
    this._unbindEvents();
    this._private.wegbier.destroy();
    this._config.container = null;
    this._config.moveable = null;
  };

  // LIFECYCLE

  _calculatePositionLimits() {
    let positionLimits = {
      x: this._config.moveable.clientWidth - this._config.container.clientWidth,
      y: this._config.moveable.clientHeight - this._config.container.clientHeight
    }

    // only set limits for the axis we use, and on which the moveable is larger than the container
    this._forXY((xy) => {
      if (positionLimits[xy] > 0)
        this._private.positionLimits[xy] = -positionLimits[xy];
    });
  }

  _getMoveableSize() {
    return {
      width: this._config.moveable.clientWidth,
      height: this._config.moveable.clientHeight
    };
  }

  _bindEvents() {
    this._private.boundHandlers = {
      'wegbier:positionChanged': this._onPositionChanged.bind(this)
    };

    fUtils.forEach(this._private.boundHandlers, (handler, event) => {
      this._private.wegbier.addEventListener(event, handler);
    });
  }

  _unbindEvents() {
    fUtils.forEach(this._private.boundHandlers, (handler, event) => {
      this._private.wegbier.removeEventListener(event, handler);
    });
  }

  _onPositionChanged(event) {
    this._private.position.x = event.detail.x;
    this._private.position.y = event.detail.y;
    this._updateMoveablePosition();
  }

  // MOVEABLE MANIPULATION

  _updateMoveablePosition() {
    this._config.moveable.style.webkitTransform = 'translate3d(' + this._private.position.x + 'px, ' + this._private.position.y + 'px, 0px)';
  }

  // ANIMATED SCROLLING

  _bindAnimatedScroll() {
    this._private.boundAnimatedScroll = this._runAnimatedScroll.bind(this);
  }

  _startAnimatedScroll(targetPosition, scrollSpeed) {
    let animatedScroll = this._private.animatedScroll;

    cancelAnimationFrame(this._private.currentFrame);

    animatedScroll.startingPosition = {
      x: this._private.position.x,
      y: this._private.position.y
    }

    animatedScroll.targetPosition = {
      x: this._private.position.x,
      y: this._private.position.y
    }

    let validTargetPosition = this._nearestValidPosition(targetPosition);

    // only set a target position for axes on which scrolling is enabled.
    // otherwise, tarhet position remains the same as current position.
    this._forXY((xy) => {
      animatedScroll.targetPosition[xy] = validTargetPosition[xy];
    });

    animatedScroll.totalDistance = this._positionDistance(
      animatedScroll.startingPosition,
      animatedScroll.targetPosition
    );

    this._calculateScrollDirection();

    animatedScroll.maxSpeed = scrollSpeed > 0 ? scrollSpeed : this._config.maxScrollPxPerFrame;
    animatedScroll.speed = animatedScroll.maxSpeed;

    animatedScroll.isScrolling = true;

    this._private.currentFrame = requestAnimationFrame(this._private.boundAnimatedScroll);
  }

  _runAnimatedScroll() {
    let animatedScroll = this._private.animatedScroll;

    let distanceToTarget = this._positionDistance(
      this._private.position,
      animatedScroll.targetPosition
    );

    // slow down when close to target
    if (distanceToTarget < this._config.scrollToSlowingDistance) {
      animatedScroll.speed = animatedScroll.maxSpeed * (distanceToTarget/this._config.scrollToSlowingDistance);
    }

    // stop when on target
    if (distanceToTarget < 1) {
        this._stopAnimatedScroll();
        this.scrollTo(
          animatedScroll.targetPosition.x,
          animatedScroll.targetPosition.y
        );
    }
    // otherwise move towards target
    else {
      this._forXY((xy) => {
        this._private.position[xy] += animatedScroll.speed * animatedScroll.direction[xy];
      });
      this._private.wegbier.scrollTo(this._private.position);

      this._private.currentFrame = requestAnimationFrame(this._private.boundAnimatedScroll);
    }
  }

  _stopAnimatedScroll() {
    let animatedScroll = this._private.animatedScroll;

    animatedScroll.speed = 0;
    animatedScroll.isScrolling = false;

    cancelAnimationFrame(this._private.currentFrame);
  }

  _calculateScrollDirection() {
    let animatedScroll = this._private.animatedScroll,
      distance = { x: 0, y: 0 };

    // update the position, according to the speed component on each axis
    this._forXY((xy) => {
      distance[xy] = animatedScroll.targetPosition[xy] - animatedScroll.startingPosition[xy];
    });

    animatedScroll.direction.radians = Math.atan2(distance.y, distance.x);

    animatedScroll.direction.x = Math.cos(animatedScroll.direction.radians);
    animatedScroll.direction.y = Math.sin(animatedScroll.direction.radians);
  }

  // HELPERS

  _positionDistance(pos1, pos2) {
    return this._distance(pos1.x, pos1.y, pos2.x, pos2.y);
  }

  _distance(x1, y1, x2, y2) {
    return Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 );
  }

  _forXY(toExecute) {
    this._private.axis.forEach(toExecute);
  }

  _nearestValidPosition(position) {
    let result = { x: 0, y: 0 };

    this._forXY((xy) => {
      if (position[xy] > 0)
        result[xy] = 0;
      else if (position[xy] < this._private.positionLimits[xy])
        result[xy] = this._private.positionLimits[xy];
      else
        result[xy] = position[xy];
    });

    return result;
  }
};
<|endoftext|>


<commit_before>'use strict';

function Particle(config) {
  this.position = config.position;
  this.velocity = config.velocity;
  this.acceleration = config.acceleration;
  this.damping = config.damping || 1;
  this.inverseMass = 1 / config.mass || Infinity;
  Object.defineProperty(this, 'mass', {
    get: function () {
      return 1 / this.inverseMass;
    },
    set: function (mass) {
      this.inverseMass = 1 / mass;
    }
  });
}
<commit_msg>Add integrate method to the particle<commit_after>/* global Vector3 */
'use strict';

function Particle(config) {
  this.position = config.position;
  this.velocity = config.velocity;
  this.acceleration = config.acceleration;
  this.damping = config.damping || 1;
  this.inverseMass = 1 / config.mass || Infinity;
  Object.defineProperty(this, 'mass', {
    get: function () {
      return 1 / this.inverseMass;
    },
    set: function (mass) {
      this.inverseMass = 1 / mass;
    }
  });
}

Particle.prototype.integrate = function (duration) {
  // Move the particle
  this.position.addScaledVector(this.velocity, duration);

  // g = 9.8 m/s^2, in our case 10
  var g = new Vector3(0, -10, 0);
  this.acceleration.addScaledVector(g, this.inverseMass);

  // Update the velocity based on the updated acceleration
  this.velocity.addScaledVector(this.acceleration, duration);
  // Update the velocity based on the damping
  this.velocity.multiplyUpdate(Math.pow(this.damping, duration));
};
<|endoftext|>


<commit_before>'use strict';

var Transitionable = require('famous-transitions').Transitionable;

function Position(dispatch) {
    this._dispatch = dispatch;
    this._id = dispatch.addComponent(this);
    this._x = new Transitionable(0);
    this._y = new Transitionable(0);
    this._z = new Transitionable(0);
}

Position.toString = function toString() {
    return 'Position';
};

Position.prototype.getState = function getState() {
    return {
        component: this.constructor.toString(),
        x: this._x.get(),
        y: this._y.get(),
        z: this._z.get()
    };
};

Position.prototype.setState = function setState(state) {
    if (state.component === this.constructor.toString()) {
        this.set(state.x, state.y, state.z);
        return true;
    }
    return false;
};

Position.prototype.clean = function clean() {
    var context = this._dispatch._context;
    context.setPosition(this._x.get(), this._y.get(), this._z.get());
    return this._x.isActive() || this._y.isActive() || this._z.isActive();
};

Position.prototype.setX = function setX(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    return this._x.set(val, options, callback);
};

Position.prototype.setY = function setY(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    return this._y.set(val, options, callback);
};

Position.prototype.setZ = function setZ(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    return this._z.set(val, options, callback);
};

Position.prototype.set = function set(x, y, z, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    this._x.set(x, options, callback);
    this._y.set(y, options, callback);
    this._z.set(z, options, callback);
    return this;
};

Position.prototype.halt = function halt() {
    this._x.halt();
    this._y.halt();
    this._z.halt();
    return this;
};

module.exports = Position;
<commit_msg>fix: Make Position setters chainable<commit_after>'use strict';

var Transitionable = require('famous-transitions').Transitionable;

function Position(dispatch) {
    this._dispatch = dispatch;
    this._id = dispatch.addComponent(this);
    this._x = new Transitionable(0);
    this._y = new Transitionable(0);
    this._z = new Transitionable(0);
}

Position.toString = function toString() {
    return 'Position';
};

Position.prototype.getState = function getState() {
    return {
        component: this.constructor.toString(),
        x: this._x.get(),
        y: this._y.get(),
        z: this._z.get()
    };
};

Position.prototype.setState = function setState(state) {
    if (state.component === this.constructor.toString()) {
        this.set(state.x, state.y, state.z);
        return true;
    }
    return false;
};

Position.prototype.clean = function clean() {
    var context = this._dispatch._context;
    context.setPosition(this._x.get(), this._y.get(), this._z.get());
    return this._x.isActive() || this._y.isActive() || this._z.isActive();
};

Position.prototype.setX = function setX(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    this._x.set(val, options, callback);
    return this;
};

Position.prototype.setY = function setY(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    this._y.set(val, options, callback);
    return this;
};

Position.prototype.setZ = function setZ(val, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    this._z.set(val, options, callback);
    return this;
};

Position.prototype.set = function set(x, y, z, options, callback) {
    this._dispatch.dirtyComponent(this._id);
    this._x.set(x, options, callback);
    this._y.set(y, options, callback);
    this._z.set(z, options, callback);
    return this;
};

Position.prototype.halt = function halt() {
    this._x.halt();
    this._y.halt();
    this._z.halt();
    return this;
};

module.exports = Position;
<|endoftext|>


<commit_before>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

var util = require('util')
  , events = require('events')
  , http = require('http')
  , crypto = require('crypto')
  , url = require('url')
  , Options = require('options')
  , WebSocket = require('./WebSocket')
  , tls = require('tls')
  , url = require('url');

/**
 * WebSocket Server implementation
 */

function WebSocketServer(options, callback) {
  options = new Options({
    host: '0.0.0.0',
    port: null,
    server: null,
    verifyClient: null,
    path: null,
    noServer: false,
    disableHixie: false,
    clientTracking: true
  }).merge(options);
  if (!options.value.port && !options.value.server && !options.value.noServer) {
    throw new TypeError('`port` or a `server` must be provided');
  }

  var self = this;

  if (options.value.port) {
    this._server = http.createServer(function (req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Not implemented');
    });
    this._server.listen(options.value.port, options.value.host, callback);
    this._closeServer = function() { self._server.close(); };
  }
  else if (options.value.server) {
    this._server = options.value.server;
    if (options.value.path) {
      // take note of the path, to avoid collisions when multiple websocket servers are
      // listening on the same http server
      if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) {
        throw new Error('two instances of WebSocketServer cannot listen on the same http server path');
      }
      if (typeof this._server._webSocketPaths !== 'object') {
        this._server._webSocketPaths = {};
      }
      this._server._webSocketPaths[options.value.path] = 1;
    }
  }
  this._server.once('listening', function() { self.emit('listening'); });

  if (typeof this._server != 'undefined') {
    this._server.on('error', function(error) {
      self.emit('error', error)
    });
    this._server.on('upgrade', function(req, socket, upgradeHead) {
      self.handleUpgrade(req, socket, upgradeHead, function(client) {
        self.emit('connection'+req.url, client);
        self.emit('connection', client);
      });
    });
  }

  this.options = options.value;
  this.path = options.value.path;
  this.clients = [];
}

/**
 * Inherits from EventEmitter.
 */

util.inherits(WebSocketServer, events.EventEmitter);

/**
 * Immediately shuts down the connection.
 *
 * @api public
 */

WebSocketServer.prototype.close = function() {
  // terminate all associated clients
  var error = null;
  try {
    for (var i = 0, l = this.clients.length; i < l; ++i) {
      this.clients[i].terminate();
    }
  }
  catch (e) {
    error = e;
  }

  // remove path descriptor, if any
  if (this.path && this._server._webSocketPaths) {
    delete this._server._webSocketPaths[this.path];
    if (Object.keys(this._server._webSocketPaths).length == 0) {
      delete this._server._webSocketPaths;
    }
  }

  // close the http server if it was internally created
  try {
    if (typeof this._closeServer !== 'undefined') {
      this._closeServer();
    }
  }
  finally {
    delete this._server;
  }
  if (error) throw error;
}

/**
 * Handle a HTTP Upgrade request.
 *
 * @api public
 */

WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
  // check for wrong path
  if (this.options.path) {
    var u = url.parse(req.url);
    if (u && u.pathname !== this.options.path) return;
  }

  if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
  else handleHybiUpgrade.apply(this, arguments);
}

module.exports = WebSocketServer;

/**
 * Entirely private apis,
 * which may or may not be bound to a sepcific WebSocket instance.
 */

function handleHybiUpgrade(req, socket, upgradeHead, cb) {
  // handle premature socket errors
  var errorHandler = function() {
    try { socket.destroy(); } catch (e) {}
  }
  socket.on('error', errorHandler);

  // verify key presence
  if (!req.headers['sec-websocket-key']) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  // verify version
  var version = parseInt(req.headers['sec-websocket-version']);
  if ([8, 13].indexOf(version) === -1) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  // verify client
  var origin = version < 13 ?
    req.headers['sec-websocket-origin'] :
    req.headers['origin'];

  // handler to call when the connection sequence completes
  var self = this;
  var completeHybiUpgrade = function() {
     var protocol = req.headers['sec-websocket-protocol'];

    // calc key
    var key = req.headers['sec-websocket-key'];
    var shasum = crypto.createHash('sha1');
    shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    key = shasum.digest('base64');

    var headers = [
        'HTTP/1.1 101 Switching Protocols'
      , 'Upgrade: websocket'
      , 'Connection: Upgrade'
      , 'Sec-WebSocket-Accept: ' + key
    ];

    if (typeof protocol != 'undefined') {
      headers.push('Sec-WebSocket-Protocol: ' + protocol);
    }

    // allows external modification/inspection of handshake headers
    self.emit('headers', headers);

    socket.setTimeout(0);
    socket.setNoDelay(true);
    try {
      socket.write(headers.concat('', '').join('\r\n'));
    }
    catch (e) {
      // if the upgrade write fails, shut the connection down hard
      try { socket.destroy(); } catch (e) {}
      return;
    }

    var client = new WebSocket([req, socket, upgradeHead], {
      protocolVersion: version,
      protocol: protocol
    });

    if (self.options.clientTracking) {
      self.clients.push(client);
      client.on('close', function() {
        var index = self.clients.indexOf(client);
        if (index != -1) {
          self.clients.splice(index, 1);
        }
      });
    }

    // signal upgrade complete
    socket.removeListener('error', errorHandler);
    cb(client);
  }

  // optionally call external client verification handler
  if (typeof this.options.verifyClient == 'function') {
    var info = {
      origin: origin,
      secure: typeof req.connection.encrypted !== 'undefined',
      req: req
    };
    if (this.options.verifyClient.length == 2) {
      this.options.verifyClient(info, function(result) {
        if (!result) abortConnection(socket, 401, 'Unauthorized')
        else completeHybiUpgrade();
      });
      return;
    }
    else if (!this.options.verifyClient(info)) {
      abortConnection(socket, 401, 'Unauthorized');
      return;
    }
  }

  completeHybiUpgrade();
}

function handleHixieUpgrade(req, socket, upgradeHead, cb) {
  // handle premature socket errors
  var errorHandler = function() {
    try { socket.destroy(); } catch (e) {}
  }
  socket.on('error', errorHandler);

  // bail if options prevent hixie
  if (this.options.disableHixie) {
    abortConnection(socket, 401, 'Hixie support disabled');
    return;
  }

  // verify key presence
  if (!req.headers['sec-websocket-key2']) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  var origin = req.headers['origin']
    , self = this;

  // setup handshake completion to run after client has been verified
  var onClientVerified = function() {
    var wshost;
    if (!req.headers['x-forwarded-host'])
        wshost = req.headers.host;
    else
        wshost = req.headers['x-forwarded-host'];
    var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url
      , protocol = req.headers['sec-websocket-protocol'];

    // handshake completion code to run once nonce has been successfully retrieved
    var completeHandshake = function(nonce, rest) {
      // calculate key
      var k1 = req.headers['sec-websocket-key1']
        , k2 = req.headers['sec-websocket-key2']
        , md5 = crypto.createHash('md5');

      [k1, k2].forEach(function (k) {
        var n = parseInt(k.replace(/[^\d]/g, ''))
          , spaces = k.replace(/[^ ]/g, '').length;
        if (spaces === 0 || n % spaces !== 0){
          abortConnection(socket, 400, 'Bad Request');
          return;
        }
        n /= spaces;
        md5.update(String.fromCharCode(
          n >> 24 & 0xFF,
          n >> 16 & 0xFF,
          n >> 8  & 0xFF,
          n       & 0xFF));
      });
      md5.update(nonce.toString('binary'));

      var headers = [
          'HTTP/1.1 101 Switching Protocols'
        , 'Upgrade: WebSocket'
        , 'Connection: Upgrade'
        , 'Sec-WebSocket-Location: ' + location
      ];
      if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
      if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);

      socket.setTimeout(0);
      socket.setNoDelay(true);
      try {
        // merge header and hash buffer
        var headerBuffer = new Buffer(headers.concat('', '').join('\r\n'));
        var hashBuffer = new Buffer(md5.digest('binary'), 'binary');
        var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length);
        headerBuffer.copy(handshakeBuffer, 0);
        hashBuffer.copy(handshakeBuffer, headerBuffer.length);

        // do a single write, which - upon success - causes a new client websocket to be setup
        socket.write(handshakeBuffer, 'binary', function(err) {
          if (err) return; // do not create client if an error happens
          var client = new WebSocket([req, socket, rest], {
            protocolVersion: 'hixie-76',
            protocol: protocol
          });
          if (self.options.clientTracking) {
            self.clients.push(client);
            client.on('close', function() {
              var index = self.clients.indexOf(client);
              if (index != -1) {
                self.clients.splice(index, 1);
              }
            });
          }

          // signal upgrade complete
          socket.removeListener('error', errorHandler);
          cb(client);
        });
      }
      catch (e) {
        try { socket.destroy(); } catch (e) {}
        return;
      }
    }

    // retrieve nonce
    var nonceLength = 8;
    if (upgradeHead && upgradeHead.length >= nonceLength) {
      var nonce = upgradeHead.slice(0, nonceLength);
      var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
      completeHandshake.call(self, nonce, rest);
    }
    else {
      // nonce not present in upgradeHead, so we must wait for enough data
      // data to arrive before continuing
      var nonce = new Buffer(nonceLength);
      upgradeHead.copy(nonce, 0);
      var received = upgradeHead.length;
      var rest = null;
      var handler = function (data) {
        var toRead = Math.min(data.length, nonceLength - received);
        if (toRead === 0) return;
        data.copy(nonce, received, 0, toRead);
        received += toRead;
        if (received == nonceLength) {
          socket.removeListener('data', handler);
          if (toRead < data.length) rest = data.slice(toRead);
          completeHandshake.call(self, nonce, rest);
        }
      }
      socket.on('data', handler);
    }
  }

  // verify client
  if (typeof this.options.verifyClient == 'function') {
    var info = {
      origin: origin,
      secure: typeof req.connection.encrypted !== 'undefined',
      req: req
    };
    if (this.options.verifyClient.length == 2) {
      var self = this;
      this.options.verifyClient(info, function(result) {
        if (!result) abortConnection(socket, 401, 'Unauthorized')
        else onClientVerified.apply(self);
      });
      return;
    }
    else if (!this.options.verifyClient(info)) {
      abortConnection(socket, 401, 'Unauthorized');
      return;
    }
  }

  // no client verification required
  onClientVerified();
}

function abortConnection(socket, code, name) {
  try {
    var response = [
      'HTTP/1.1 ' + code + ' ' + name,
      'Content-type: text/html'
    ];
    socket.write(response.concat('', '').join('\r\n'));
  }
  catch (e) { /* ignore errors - we've aborted this connection */ }
  finally {
    // ensure that an early aborted connection is shut down completely
    try { socket.destroy(); } catch (e) {}
  }
}
<commit_msg>only listen for server event when theres actually a server instance<commit_after>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

var util = require('util')
  , events = require('events')
  , http = require('http')
  , crypto = require('crypto')
  , url = require('url')
  , Options = require('options')
  , WebSocket = require('./WebSocket')
  , tls = require('tls')
  , url = require('url');

/**
 * WebSocket Server implementation
 */

function WebSocketServer(options, callback) {
  options = new Options({
    host: '0.0.0.0',
    port: null,
    server: null,
    verifyClient: null,
    path: null,
    noServer: false,
    disableHixie: false,
    clientTracking: true
  }).merge(options);
  if (!options.value.port && !options.value.server && !options.value.noServer) {
    throw new TypeError('`port` or a `server` must be provided');
  }

  var self = this;

  if (options.value.port) {
    this._server = http.createServer(function (req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Not implemented');
    });
    this._server.listen(options.value.port, options.value.host, callback);
    this._closeServer = function() { self._server.close(); };
  }
  else if (options.value.server) {
    this._server = options.value.server;
    if (options.value.path) {
      // take note of the path, to avoid collisions when multiple websocket servers are
      // listening on the same http server
      if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) {
        throw new Error('two instances of WebSocketServer cannot listen on the same http server path');
      }
      if (typeof this._server._webSocketPaths !== 'object') {
        this._server._webSocketPaths = {};
      }
      this._server._webSocketPaths[options.value.path] = 1;
    }
  }
  if (this._server) this._server.once('listening', function() { self.emit('listening'); });

  if (typeof this._server != 'undefined') {
    this._server.on('error', function(error) {
      self.emit('error', error)
    });
    this._server.on('upgrade', function(req, socket, upgradeHead) {
      self.handleUpgrade(req, socket, upgradeHead, function(client) {
        self.emit('connection'+req.url, client);
        self.emit('connection', client);
      });
    });
  }

  this.options = options.value;
  this.path = options.value.path;
  this.clients = [];
}

/**
 * Inherits from EventEmitter.
 */

util.inherits(WebSocketServer, events.EventEmitter);

/**
 * Immediately shuts down the connection.
 *
 * @api public
 */

WebSocketServer.prototype.close = function() {
  // terminate all associated clients
  var error = null;
  try {
    for (var i = 0, l = this.clients.length; i < l; ++i) {
      this.clients[i].terminate();
    }
  }
  catch (e) {
    error = e;
  }

  // remove path descriptor, if any
  if (this.path && this._server._webSocketPaths) {
    delete this._server._webSocketPaths[this.path];
    if (Object.keys(this._server._webSocketPaths).length == 0) {
      delete this._server._webSocketPaths;
    }
  }

  // close the http server if it was internally created
  try {
    if (typeof this._closeServer !== 'undefined') {
      this._closeServer();
    }
  }
  finally {
    delete this._server;
  }
  if (error) throw error;
}

/**
 * Handle a HTTP Upgrade request.
 *
 * @api public
 */

WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
  // check for wrong path
  if (this.options.path) {
    var u = url.parse(req.url);
    if (u && u.pathname !== this.options.path) return;
  }

  if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
  else handleHybiUpgrade.apply(this, arguments);
}

module.exports = WebSocketServer;

/**
 * Entirely private apis,
 * which may or may not be bound to a sepcific WebSocket instance.
 */

function handleHybiUpgrade(req, socket, upgradeHead, cb) {
  // handle premature socket errors
  var errorHandler = function() {
    try { socket.destroy(); } catch (e) {}
  }
  socket.on('error', errorHandler);

  // verify key presence
  if (!req.headers['sec-websocket-key']) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  // verify version
  var version = parseInt(req.headers['sec-websocket-version']);
  if ([8, 13].indexOf(version) === -1) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  // verify client
  var origin = version < 13 ?
    req.headers['sec-websocket-origin'] :
    req.headers['origin'];

  // handler to call when the connection sequence completes
  var self = this;
  var completeHybiUpgrade = function() {
     var protocol = req.headers['sec-websocket-protocol'];

    // calc key
    var key = req.headers['sec-websocket-key'];
    var shasum = crypto.createHash('sha1');
    shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    key = shasum.digest('base64');

    var headers = [
        'HTTP/1.1 101 Switching Protocols'
      , 'Upgrade: websocket'
      , 'Connection: Upgrade'
      , 'Sec-WebSocket-Accept: ' + key
    ];

    if (typeof protocol != 'undefined') {
      headers.push('Sec-WebSocket-Protocol: ' + protocol);
    }

    // allows external modification/inspection of handshake headers
    self.emit('headers', headers);

    socket.setTimeout(0);
    socket.setNoDelay(true);
    try {
      socket.write(headers.concat('', '').join('\r\n'));
    }
    catch (e) {
      // if the upgrade write fails, shut the connection down hard
      try { socket.destroy(); } catch (e) {}
      return;
    }

    var client = new WebSocket([req, socket, upgradeHead], {
      protocolVersion: version,
      protocol: protocol
    });

    if (self.options.clientTracking) {
      self.clients.push(client);
      client.on('close', function() {
        var index = self.clients.indexOf(client);
        if (index != -1) {
          self.clients.splice(index, 1);
        }
      });
    }

    // signal upgrade complete
    socket.removeListener('error', errorHandler);
    cb(client);
  }

  // optionally call external client verification handler
  if (typeof this.options.verifyClient == 'function') {
    var info = {
      origin: origin,
      secure: typeof req.connection.encrypted !== 'undefined',
      req: req
    };
    if (this.options.verifyClient.length == 2) {
      this.options.verifyClient(info, function(result) {
        if (!result) abortConnection(socket, 401, 'Unauthorized')
        else completeHybiUpgrade();
      });
      return;
    }
    else if (!this.options.verifyClient(info)) {
      abortConnection(socket, 401, 'Unauthorized');
      return;
    }
  }

  completeHybiUpgrade();
}

function handleHixieUpgrade(req, socket, upgradeHead, cb) {
  // handle premature socket errors
  var errorHandler = function() {
    try { socket.destroy(); } catch (e) {}
  }
  socket.on('error', errorHandler);

  // bail if options prevent hixie
  if (this.options.disableHixie) {
    abortConnection(socket, 401, 'Hixie support disabled');
    return;
  }

  // verify key presence
  if (!req.headers['sec-websocket-key2']) {
    abortConnection(socket, 400, 'Bad Request');
    return;
  }

  var origin = req.headers['origin']
    , self = this;

  // setup handshake completion to run after client has been verified
  var onClientVerified = function() {
    var wshost;
    if (!req.headers['x-forwarded-host'])
        wshost = req.headers.host;
    else
        wshost = req.headers['x-forwarded-host'];
    var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url
      , protocol = req.headers['sec-websocket-protocol'];

    // handshake completion code to run once nonce has been successfully retrieved
    var completeHandshake = function(nonce, rest) {
      // calculate key
      var k1 = req.headers['sec-websocket-key1']
        , k2 = req.headers['sec-websocket-key2']
        , md5 = crypto.createHash('md5');

      [k1, k2].forEach(function (k) {
        var n = parseInt(k.replace(/[^\d]/g, ''))
          , spaces = k.replace(/[^ ]/g, '').length;
        if (spaces === 0 || n % spaces !== 0){
          abortConnection(socket, 400, 'Bad Request');
          return;
        }
        n /= spaces;
        md5.update(String.fromCharCode(
          n >> 24 & 0xFF,
          n >> 16 & 0xFF,
          n >> 8  & 0xFF,
          n       & 0xFF));
      });
      md5.update(nonce.toString('binary'));

      var headers = [
          'HTTP/1.1 101 Switching Protocols'
        , 'Upgrade: WebSocket'
        , 'Connection: Upgrade'
        , 'Sec-WebSocket-Location: ' + location
      ];
      if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
      if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);

      socket.setTimeout(0);
      socket.setNoDelay(true);
      try {
        // merge header and hash buffer
        var headerBuffer = new Buffer(headers.concat('', '').join('\r\n'));
        var hashBuffer = new Buffer(md5.digest('binary'), 'binary');
        var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length);
        headerBuffer.copy(handshakeBuffer, 0);
        hashBuffer.copy(handshakeBuffer, headerBuffer.length);

        // do a single write, which - upon success - causes a new client websocket to be setup
        socket.write(handshakeBuffer, 'binary', function(err) {
          if (err) return; // do not create client if an error happens
          var client = new WebSocket([req, socket, rest], {
            protocolVersion: 'hixie-76',
            protocol: protocol
          });
          if (self.options.clientTracking) {
            self.clients.push(client);
            client.on('close', function() {
              var index = self.clients.indexOf(client);
              if (index != -1) {
                self.clients.splice(index, 1);
              }
            });
          }

          // signal upgrade complete
          socket.removeListener('error', errorHandler);
          cb(client);
        });
      }
      catch (e) {
        try { socket.destroy(); } catch (e) {}
        return;
      }
    }

    // retrieve nonce
    var nonceLength = 8;
    if (upgradeHead && upgradeHead.length >= nonceLength) {
      var nonce = upgradeHead.slice(0, nonceLength);
      var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
      completeHandshake.call(self, nonce, rest);
    }
    else {
      // nonce not present in upgradeHead, so we must wait for enough data
      // data to arrive before continuing
      var nonce = new Buffer(nonceLength);
      upgradeHead.copy(nonce, 0);
      var received = upgradeHead.length;
      var rest = null;
      var handler = function (data) {
        var toRead = Math.min(data.length, nonceLength - received);
        if (toRead === 0) return;
        data.copy(nonce, received, 0, toRead);
        received += toRead;
        if (received == nonceLength) {
          socket.removeListener('data', handler);
          if (toRead < data.length) rest = data.slice(toRead);
          completeHandshake.call(self, nonce, rest);
        }
      }
      socket.on('data', handler);
    }
  }

  // verify client
  if (typeof this.options.verifyClient == 'function') {
    var info = {
      origin: origin,
      secure: typeof req.connection.encrypted !== 'undefined',
      req: req
    };
    if (this.options.verifyClient.length == 2) {
      var self = this;
      this.options.verifyClient(info, function(result) {
        if (!result) abortConnection(socket, 401, 'Unauthorized')
        else onClientVerified.apply(self);
      });
      return;
    }
    else if (!this.options.verifyClient(info)) {
      abortConnection(socket, 401, 'Unauthorized');
      return;
    }
  }

  // no client verification required
  onClientVerified();
}

function abortConnection(socket, code, name) {
  try {
    var response = [
      'HTTP/1.1 ' + code + ' ' + name,
      'Content-type: text/html'
    ];
    socket.write(response.concat('', '').join('\r\n'));
  }
  catch (e) { /* ignore errors - we've aborted this connection */ }
  finally {
    // ensure that an early aborted connection is shut down completely
    try { socket.destroy(); } catch (e) {}
  }
}
<|endoftext|>


<commit_before>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var userData = [];
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";
  var windowWidth = $(window).innerWidth();
  var windowHeight = $(window).innerHeight();

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    $("#user").blur();
    $("#pass").blur();
    $("#loginInfo").remove();
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }
    socket = new WebSocket("ws:167.88.120.57:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
  userData = message.data.split(":")[1].split(",");

} else if (message.data.indexOf("dat") == 0){
  enemies = message.data.split(":")[1].split(",");
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var canvasElement = $('#GameCanvas');
canvasElement.select().focus().click();
canvasElement.attr('width', windowWidth);
canvasElement.attr('height', windowHeight);
canvasElement.attr('tabIndex', 1);
document.getElementById("GameCanvas").focus();
var canvas = canvasElement[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, windowWidth, windowHeight);
  //draw enemies
  for (i = 0; i < enemies.length; i += 4){
  context.fillStyle = enemies[i+2];
  context.fillRect(enemies[i], enemies[i+1], 30, 50);
  }
  //draw player
  context.fillStyle = userData[2];
  context.fillRect(userData[0], userData[1], 30, 50);
  //draw enemy names
  for (i = 0; i < enemies.length; i += 4){
  context.fillStyle = enemies[i+2];
  context.fillText(enemies[i+3],(enemies[i] - (context.measureText(enemies[i+3]).width / 2)) + 15,enemies[i+1] - 5);
  }
  //draw player name
  context.fillStyle = userData[2];
  context.font = "15px Arial";
  context.fillText(userData[3],(userData[0] - (context.measureText(userData[3]).width / 2)) + 15,userData[1] - 5);

  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();

$(document).ready(function() {
  $("#loginInfo").submit(function(event){
    event.preventDefault();
    JS_GAME.game.init();
    return false;
  });
});
<commit_msg>Chrome is obnoxious<commit_after>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var userData = [];
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";
  var windowWidth = $(window).innerWidth();
  var windowHeight = $(window).innerHeight();

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }

    $("#user").blur();
    $("#pass").blur();
    $("#loginInfo").remove();

    socket = new WebSocket("ws:167.88.120.57:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
  userData = message.data.split(":")[1].split(",");

} else if (message.data.indexOf("dat") == 0){
  enemies = message.data.split(":")[1].split(",");
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var canvasElement = $('#GameCanvas');
canvasElement.select().focus().click();
canvasElement.attr('width', windowWidth);
canvasElement.attr('height', windowHeight);
canvasElement.attr('tabIndex', 1);
document.getElementById("GameCanvas").focus();
var canvas = canvasElement[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, windowWidth, windowHeight);
  //draw enemies
  for (i = 0; i < enemies.length; i += 4){
  context.fillStyle = enemies[i+2];
  context.fillRect(enemies[i], enemies[i+1], 30, 50);
  }
  //draw player
  context.fillStyle = userData[2];
  context.fillRect(userData[0], userData[1], 30, 50);
  //draw enemy names
  for (i = 0; i < enemies.length; i += 4){
  context.fillStyle = enemies[i+2];
  context.fillText(enemies[i+3],(enemies[i] - (context.measureText(enemies[i+3]).width / 2)) + 15,enemies[i+1] - 5);
  }
  //draw player name
  context.fillStyle = userData[2];
  context.font = "15px Arial";
  context.fillText(userData[3],(userData[0] - (context.measureText(userData[3]).width / 2)) + 15,userData[1] - 5);

  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();

$(document).ready(function() {
  $("#loginInfo").submit(function(event){
    event.preventDefault();
    JS_GAME.game.init();
    return false;
  });
});
<|endoftext|>