<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2015 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* globals PDFViewer, PDFHistory, Promise, parseQueryString */

'use strict';

/**
 * Performs navigation functions inside PDF, such as opening specified page,
 * or destination.
 * @class
 * @implements {IPDFLinkService}
 */
var PDFLinkService = (function () {
  /**
   * @constructs PDFLinkService
   */
  function PDFLinkService() {
    this.baseUrl = null;
    this.pdfDocument = null;
    this.pdfViewer = null;
    this.pdfHistory = null;

    this._pagesRefCache = null;
  }

  PDFLinkService.prototype = {
    setDocument: function PDFLinkService_setDocument(pdfDocument, baseUrl) {
      this.baseUrl = baseUrl;
      this.pdfDocument = pdfDocument;
      this._pagesRefCache = Object.create(null);
    },

    setViewer: function PDFLinkService_setViewer(pdfViewer) {
      this.pdfViewer = pdfViewer;
    },

    setHistory: function PDFLinkService_setHistory(pdfHistory) {
      this.pdfHistory = pdfHistory;
    },

    /**
     * @returns {number}
     */
    get pagesCount() {
      return this.pdfDocument.numPages;
    },

    /**
     * @returns {number}
     */
    get page() {
      return this.pdfViewer.currentPageNumber;
    },
    /**
     * @param {number} value
     */
    set page(value) {
      this.pdfViewer.currentPageNumber = value;
    },
    /**
     * @param dest - The PDF destination object.
     */
    navigateTo: function PDFLinkService_navigateTo(dest) {
      var destString = '';
      var self = this;

      var goToDestination = function(destRef) {
        // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
        var pageNumber = destRef instanceof Object ?
          self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          if (pageNumber > self.pagesCount) {
            pageNumber = self.pagesCount;
          }
          self.pdfViewer.scrollPageIntoView(pageNumber, dest);

          if (self.pdfHistory) {
            // Update the browsing history.
            self.pdfHistory.push({
              dest: dest,
              hash: destString,
              page: pageNumber
            });
          }
        } else {
          self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
            var pageNum = pageIndex + 1;
            var cacheKey = destRef.num + ' ' + destRef.gen + ' R';
            self._pagesRefCache[cacheKey] = pageNum;
            goToDestination(destRef);
          });
        }
      };

      var destinationPromise;
      if (typeof dest === 'string') {
        destString = dest;
        destinationPromise = this.pdfDocument.getDestination(dest);
      } else {
        destinationPromise = Promise.resolve(dest);
      }
      destinationPromise.then(function(destination) {
        dest = destination;
        if (!(destination instanceof Array)) {
          return; // invalid destination
        }
        goToDestination(destination[0]);
      });
    },

    /**
     * @param dest - The PDF destination object.
     * @returns {string} The hyperlink to the PDF object.
     */
    getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
      if (typeof dest === 'string') {
        return this.getAnchorUrl('#' + escape(dest));
      }
      if (dest instanceof Array) {
        var destRef = dest[0]; // see navigateTo method for dest format
        var pageNumber = destRef instanceof Object ?
          this._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber);
          var destKind = dest[1];
          if (typeof destKind === 'object' && 'name' in destKind &&
              destKind.name === 'XYZ') {
            var scale = (dest[4] || this.pdfViewer.currentScaleValue);
            var scaleNumber = parseFloat(scale);
            if (scaleNumber) {
              scale = scaleNumber * 100;
            }
            pdfOpenParams += '&zoom=' + scale;
            if (dest[2] || dest[3]) {
              pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
            }
          }
          return pdfOpenParams;
        }
      }
      return '';
    },

    /**
     * Prefix the full url on anchor links to make sure that links are resolved
     * relative to the current URL instead of the one defined in <base href>.
     * @param {String} anchor The anchor hash, including the #.
     * @returns {string} The hyperlink to the PDF object.
     */
    getAnchorUrl: function PDFLinkService_getAnchorUrl(anchor) {
      return (this.baseUrl || '') + anchor;
    },

    /**
     * @param {string} hash
     */
    setHash: function PDFLinkService_setHash(hash) {
      if (hash.indexOf('=') >= 0) {
        var params = parseQueryString(hash);
        // borrowing syntax from "Parameters for Opening PDF Files"
        if ('nameddest' in params) {
          if (this.pdfHistory) {
            this.pdfHistory.updateNextHashParam(params.nameddest);
          }
          this.navigateTo(params.nameddest);
          return;
        }
        var pageNumber, dest;
        if ('page' in params) {
          pageNumber = (params.page | 0) || 1;
        }
        if ('zoom' in params) {
          // Build the destination array.
          var zoomArgs = params.zoom.split(','); // scale,left,top
          var zoomArg = zoomArgs[0];
          var zoomArgNumber = parseFloat(zoomArg);

          if (zoomArg.indexOf('Fit') === -1) {
            // If the zoomArg is a number, it has to get divided by 100. If it's
            // a string, it should stay as it is.
            dest = [null, { name: 'XYZ' },
                    zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
                    zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
                    (zoomArgNumber ? zoomArgNumber / 100 : zoomArg)];
          } else {
            if (zoomArg === 'Fit' || zoomArg === 'FitB') {
              dest = [null, { name: zoomArg }];
            } else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') ||
                       (zoomArg === 'FitV' || zoomArg === 'FitBV')) {
              dest = [null, { name: zoomArg },
                      zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null];
            } else if (zoomArg === 'FitR') {
              if (zoomArgs.length !== 5) {
                console.error('pdfViewSetHash: ' +
                              'Not enough parameters for \'FitR\'.');
              } else {
                dest = [null, { name: zoomArg },
                        (zoomArgs[1] | 0), (zoomArgs[2] | 0),
                        (zoomArgs[3] | 0), (zoomArgs[4] | 0)];
              }
            } else {
              console.error('pdfViewSetHash: \'' + zoomArg +
                            '\' is not a valid zoom value.');
            }
          }
        }
        if (dest) {
          this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest);
        } else if (pageNumber) {
          this.page = pageNumber; // simple page
        }
        if ('pagemode' in params) {
          if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
              params.pagemode === 'attachments') {
            this.switchSidebarView((params.pagemode === 'bookmarks' ?
                                   'outline' : params.pagemode), true);
          } else if (params.pagemode === 'none' && this.sidebarOpen) {
            document.getElementById('sidebarToggle').click();
          }
        }
      } else if (/^\d+$/.test(hash)) { // page number
        this.page = hash;
      } else { // named destination
        if (this.pdfHistory) {
          this.pdfHistory.updateNextHashParam(unescape(hash));
        }
        this.navigateTo(unescape(hash));
      }
    },

    /**
     * @param {string} action
     */
    executeNamedAction: function PDFLinkService_executeNamedAction(action) {
      // See PDF reference, table 8.45 - Named action
      switch (action) {
        case 'GoBack':
          if (this.pdfHistory) {
            this.pdfHistory.back();
          }
          break;

        case 'GoForward':
          if (this.pdfHistory) {
            this.pdfHistory.forward();
          }
          break;

        case 'NextPage':
          this.page++;
          break;

        case 'PrevPage':
          this.page--;
          break;

        case 'LastPage':
          this.page = this.pagesCount;
          break;

        case 'FirstPage':
          this.page = 1;
          break;

        default:
          break; // No action according to spec
      }

      var event = document.createEvent('CustomEvent');
      event.initCustomEvent('namedaction', true, true, {
        action: action
      });
      this.pdfViewer.container.dispatchEvent(event);
    },

    /**
     * @param {number} pageNum - page number.
     * @param {Object} pageRef - reference to the page.
     */
    cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) {
      var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
      this._pagesRefCache[refStr] = pageNum;
    }
  };

  return PDFLinkService;
})();
<commit_msg>Fix a couple of function names in error messages in PDFLinkService<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2015 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* globals PDFViewer, PDFHistory, Promise, parseQueryString */

'use strict';

/**
 * Performs navigation functions inside PDF, such as opening specified page,
 * or destination.
 * @class
 * @implements {IPDFLinkService}
 */
var PDFLinkService = (function () {
  /**
   * @constructs PDFLinkService
   */
  function PDFLinkService() {
    this.baseUrl = null;
    this.pdfDocument = null;
    this.pdfViewer = null;
    this.pdfHistory = null;

    this._pagesRefCache = null;
  }

  PDFLinkService.prototype = {
    setDocument: function PDFLinkService_setDocument(pdfDocument, baseUrl) {
      this.baseUrl = baseUrl;
      this.pdfDocument = pdfDocument;
      this._pagesRefCache = Object.create(null);
    },

    setViewer: function PDFLinkService_setViewer(pdfViewer) {
      this.pdfViewer = pdfViewer;
    },

    setHistory: function PDFLinkService_setHistory(pdfHistory) {
      this.pdfHistory = pdfHistory;
    },

    /**
     * @returns {number}
     */
    get pagesCount() {
      return this.pdfDocument.numPages;
    },

    /**
     * @returns {number}
     */
    get page() {
      return this.pdfViewer.currentPageNumber;
    },

    /**
     * @param {number} value
     */
    set page(value) {
      this.pdfViewer.currentPageNumber = value;
    },

    /**
     * @param dest - The PDF destination object.
     */
    navigateTo: function PDFLinkService_navigateTo(dest) {
      var destString = '';
      var self = this;

      var goToDestination = function(destRef) {
        // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
        var pageNumber = destRef instanceof Object ?
          self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          if (pageNumber > self.pagesCount) {
            pageNumber = self.pagesCount;
          }
          self.pdfViewer.scrollPageIntoView(pageNumber, dest);

          if (self.pdfHistory) {
            // Update the browsing history.
            self.pdfHistory.push({
              dest: dest,
              hash: destString,
              page: pageNumber
            });
          }
        } else {
          self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
            var pageNum = pageIndex + 1;
            var cacheKey = destRef.num + ' ' + destRef.gen + ' R';
            self._pagesRefCache[cacheKey] = pageNum;
            goToDestination(destRef);
          });
        }
      };

      var destinationPromise;
      if (typeof dest === 'string') {
        destString = dest;
        destinationPromise = this.pdfDocument.getDestination(dest);
      } else {
        destinationPromise = Promise.resolve(dest);
      }
      destinationPromise.then(function(destination) {
        dest = destination;
        if (!(destination instanceof Array)) {
          return; // invalid destination
        }
        goToDestination(destination[0]);
      });
    },

    /**
     * @param dest - The PDF destination object.
     * @returns {string} The hyperlink to the PDF object.
     */
    getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
      if (typeof dest === 'string') {
        return this.getAnchorUrl('#' + escape(dest));
      }
      if (dest instanceof Array) {
        var destRef = dest[0]; // see navigateTo method for dest format
        var pageNumber = destRef instanceof Object ?
          this._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber);
          var destKind = dest[1];
          if (typeof destKind === 'object' && 'name' in destKind &&
              destKind.name === 'XYZ') {
            var scale = (dest[4] || this.pdfViewer.currentScaleValue);
            var scaleNumber = parseFloat(scale);
            if (scaleNumber) {
              scale = scaleNumber * 100;
            }
            pdfOpenParams += '&zoom=' + scale;
            if (dest[2] || dest[3]) {
              pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
            }
          }
          return pdfOpenParams;
        }
      }
      return '';
    },

    /**
     * Prefix the full url on anchor links to make sure that links are resolved
     * relative to the current URL instead of the one defined in <base href>.
     * @param {String} anchor The anchor hash, including the #.
     * @returns {string} The hyperlink to the PDF object.
     */
    getAnchorUrl: function PDFLinkService_getAnchorUrl(anchor) {
      return (this.baseUrl || '') + anchor;
    },

    /**
     * @param {string} hash
     */
    setHash: function PDFLinkService_setHash(hash) {
      if (hash.indexOf('=') >= 0) {
        var params = parseQueryString(hash);
        // borrowing syntax from "Parameters for Opening PDF Files"
        if ('nameddest' in params) {
          if (this.pdfHistory) {
            this.pdfHistory.updateNextHashParam(params.nameddest);
          }
          this.navigateTo(params.nameddest);
          return;
        }
        var pageNumber, dest;
        if ('page' in params) {
          pageNumber = (params.page | 0) || 1;
        }
        if ('zoom' in params) {
          // Build the destination array.
          var zoomArgs = params.zoom.split(','); // scale,left,top
          var zoomArg = zoomArgs[0];
          var zoomArgNumber = parseFloat(zoomArg);

          if (zoomArg.indexOf('Fit') === -1) {
            // If the zoomArg is a number, it has to get divided by 100. If it's
            // a string, it should stay as it is.
            dest = [null, { name: 'XYZ' },
                    zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
                    zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
                    (zoomArgNumber ? zoomArgNumber / 100 : zoomArg)];
          } else {
            if (zoomArg === 'Fit' || zoomArg === 'FitB') {
              dest = [null, { name: zoomArg }];
            } else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') ||
                       (zoomArg === 'FitV' || zoomArg === 'FitBV')) {
              dest = [null, { name: zoomArg },
                      zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null];
            } else if (zoomArg === 'FitR') {
              if (zoomArgs.length !== 5) {
                console.error('PDFLinkService_setHash: ' +
                              'Not enough parameters for \'FitR\'.');
              } else {
                dest = [null, { name: zoomArg },
                        (zoomArgs[1] | 0), (zoomArgs[2] | 0),
                        (zoomArgs[3] | 0), (zoomArgs[4] | 0)];
              }
            } else {
              console.error('PDFLinkService_setHash: \'' + zoomArg +
                            '\' is not a valid zoom value.');
            }
          }
        }
        if (dest) {
          this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest);
        } else if (pageNumber) {
          this.page = pageNumber; // simple page
        }
        if ('pagemode' in params) {
          if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
              params.pagemode === 'attachments') {
            this.switchSidebarView((params.pagemode === 'bookmarks' ?
                                   'outline' : params.pagemode), true);
          } else if (params.pagemode === 'none' && this.sidebarOpen) {
            document.getElementById('sidebarToggle').click();
          }
        }
      } else if (/^\d+$/.test(hash)) { // page number
        this.page = hash;
      } else { // named destination
        if (this.pdfHistory) {
          this.pdfHistory.updateNextHashParam(unescape(hash));
        }
        this.navigateTo(unescape(hash));
      }
    },

    /**
     * @param {string} action
     */
    executeNamedAction: function PDFLinkService_executeNamedAction(action) {
      // See PDF reference, table 8.45 - Named action
      switch (action) {
        case 'GoBack':
          if (this.pdfHistory) {
            this.pdfHistory.back();
          }
          break;

        case 'GoForward':
          if (this.pdfHistory) {
            this.pdfHistory.forward();
          }
          break;

        case 'NextPage':
          this.page++;
          break;

        case 'PrevPage':
          this.page--;
          break;

        case 'LastPage':
          this.page = this.pagesCount;
          break;

        case 'FirstPage':
          this.page = 1;
          break;

        default:
          break; // No action according to spec
      }

      var event = document.createEvent('CustomEvent');
      event.initCustomEvent('namedaction', true, true, {
        action: action
      });
      this.pdfViewer.container.dispatchEvent(event);
    },

    /**
     * @param {number} pageNum - page number.
     * @param {Object} pageRef - reference to the page.
     */
    cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) {
      var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
      this._pagesRefCache[refStr] = pageNum;
    }
  };

  return PDFLinkService;
})();
<|endoftext|>


<commit_before>// define custom submit directive
var rcSubmitDirective = {
    'rcSubmit': ['$parse', function ($parse) {
        return {
            restrict: 'A',
            require: ['rcSubmit', '?form'],
            controller: ['$scope', function ($scope) {
                this.attempted = false;

                var formController = null;

                this.setAttempted = function() {
                    this.attempted = true;
                };

                this.setFormController = function(controller) {
                  formController = controller;
                };

                this.needsAttention = function (fieldModelController) {
                    if (!formController) return false;

                    if (fieldModelController) {
                        return fieldModelController.$invalid && (fieldModelController.$dirty || this.attempted);
                    } else {
                        return formController && formController.$invalid && (formController.$dirty || this.attempted);
                    }
                };
            }],
            compile: function(cElement, cAttributes, transclude) {
                return {
                    pre: function(scope, formElement, attributes, controllers) {

                        var submitController = controllers[0];
                        var formController = (controllers.length > 1) ? controllers[1] : null;

                        submitController.setFormController(formController);

                        scope.rc = scope.rc || {};
                        scope.rc[attributes.name] = submitController;
                    },
                    post: function(scope, formElement, attributes, controllers) {

                        var submitController = controllers[0];
                        var formController = (controllers.length > 1) ? controllers[1] : null;
                        var fn = $parse(attributes.rcSubmit);

                        formElement.bind('submit', function () {
                            submitController.setAttempted();
                            if (!scope.$$phase) scope.$apply();

                            if (!formController.$valid) return false;

                            scope.$apply(function() {
                                fn(scope, {$event:event});
                            });
                        });
                    }
              };
            }
        };
    }]
};

// define controller for login
var LoginController = ['$scope',
function ($scope) {

    $scope.session = {};

    $scope.login = function () {
        // process $scope.session
        alert('logged in!');
    };
    $scope.login();
}];

// create a module to make it easier to include in the app module
angular.module('rcForm', [])
.directive(rcSubmitDirective);

// define module for app
angular.module('LoginApp', ['rcForm']);<commit_msg>Login Page Changes<commit_after>// define custom submit directive
var rcSubmitDirective = {
    'rcSubmit': ['$parse', function ($parse) {
        return {
            restrict: 'A',
            require: ['rcSubmit', '?form'],
            controller: ['$scope', function ($scope) {
                this.attempted = false;

                var formController = null;

                this.setAttempted = function() {
                    this.attempted = true;
                };

                this.setFormController = function(controller) {
                  formController = controller;
                };

                this.needsAttention = function (fieldModelController) {
                    if (!formController) return false;

                    if (fieldModelController) {
                        return fieldModelController.$invalid && (fieldModelController.$dirty || this.attempted);
                    } else {
                        return formController && formController.$invalid && (formController.$dirty || this.attempted);
                    }
                };
            }],
            compile: function(cElement, cAttributes, transclude) {
                return {
                    pre: function(scope, formElement, attributes, controllers) {

                        var submitController = controllers[0];
                        var formController = (controllers.length > 1) ? controllers[1] : null;

                        submitController.setFormController(formController);

                        scope.rc = scope.rc || {};
                        scope.rc[attributes.name] = submitController;
                    },
                    post: function(scope, formElement, attributes, controllers) {

                        var submitController = controllers[0];
                        var formController = (controllers.length > 1) ? controllers[1] : null;
                        var fn = $parse(attributes.rcSubmit);

                        formElement.bind('submit', function () {
                            submitController.setAttempted();
                            if (!scope.$$phase) scope.$apply();

                            if (!formController.$valid) return false;

                            scope.$apply(function() {
                                fn(scope, {$event:event});
                            });
                        });
                    }
              };
            }
        };
    }]
};

// define controller for login
var LoginController = ['$scope',
function ($scope) {

    $scope.session = {};

    $scope.login = function () {
        // process $scope.session
        alert('logged in!');
    };
    //$scope.login();
}];

// create a module to make it easier to include in the app module
angular.module('rcForm', [])
.directive(rcSubmitDirective);

// define module for app
angular.module('LoginApp', ['rcForm']);<|endoftext|>


<commit_before>/**
 * schema-validator.js -- A javascript library to validate json input
 * with a schema.
 *
 *  Copyright (c) 2013 Alex Cornejo.
 *
 * Redistributable under a MIT-style open source license.
 *
 * schema-validator depends on node-validator.
 */

(function () {
  var enforceType = {
    'string': function (x) {
      if (typeof x !== 'string')
        throw new Error('is not a string');
      return x;
    },
    'boolean': function (x) {
      if (typeof x !== 'boolean')
        throw new Error('is not a boolean');
      return x;
    },
    'number': function (x) {
      if (typeof x !== 'number')
        throw new Error('is not a number');
      return x;
    },
    'integer': function (x) {
      if (typeof x !== 'number' || x % 1 !== 0)
        throw new Error('is not an integer');
      return x;
    },
    'object': function (x) {
      if (!x || typeof x !== 'object')
        throw new Error('is not an object');
      return x;
    },
    'array': function (x) {
      if (!x || typeof x !== 'object' || typeof x.length !== 'number' || toString.call(x) !== '[object Array]')
        throw new Error('is not an array');
      return x;
    },
    'date': function (x) {
      if (x === 'now') return new Date();
      var d = new Date(x);
      if (isNaN(d.getTime()))
          throw new Error('is not a date');
      return d;
    }
  };

  // TODO: rewrite, equals?, contains? isAfter, isBefore?
  var validateField = function () {
    var list = [
      'isAlpha', 'isAlphanumeric', 'isEmail', 'isUrl', 'isIP', 'isIPv4',
      'isIPv6', 'isCreditCard', 'isUUID', 'isUUIDv3', 'isUUIDv4',
      'isDate', 'isHexadecimal', 'isHexColor', 'isLowercase',
      'isUppercase', 'notEmpty', 'isAfter', 'isBefore', 'regex',
      'notRegex', 'equals', 'contains', 'notContains'];
    var i, d = {};

    if (typeof check === 'undefined' && typeof require !== 'undefined')
      check = require('validator').check;

    function makeFunction(f) {
      return function (v, p, q) { check(v)[f](p, q); };
    }

    for (i = 0; i<list.length; i++) {
      d[list[i]] = makeFunction(list[i]);
    }

    d.readOnly = function (v, p) {
      if (p)
        throw new Error('is read only');
    }

    d.notIn = function (v, p) {
      for (var i = 0; i < p.length; i++)
        if (v === p[i])
          throw new Error('found in set');
    };

    d.isIn = function (v, p) {
      for (var i = 0; i < p.length; i++)
        if (v === p[i]) return;
      throw new Error('not found in set');
    };

    d.len = function (v, p) {
      if (v.length !== p)
        throw new Error('wrong length');
    };

    d.minLength = function (v, p) {
      if (v.length < p)
        throw new Error('too small');
    };

    d.maxLength = function (v, p) {
      if (v.length > p)
        throw new Error('too large');
    };

    d.min = function (v, p) {
      if (v < p)
        throw new Error('too small');
    };

    d.max = function (v, p) {
      if (v > p)
        throw new Error('too large');
    };

    d.startsWith = function (v, p) {
      if (typeof v !== 'string' || v.indexOf(p) !== 0)
        throw new Error('does not start with prefix');
    };

    d.endsWith = function (v, p) {
      if (typeof v !== 'string' || typeof p !== 'string' || v.indexOf(p, v.length - p.length) === -1)
        throw new Error('does not end with suffix');
    };

    return d;
  }();

  var isRequired = function (p, required) {
    var i;
    if (typeof required !== 'object' || toString.call(required) !== '[object Array]')
      return false;
    for (i = 0; i<required.length; i++)
      if (required[i] === p)
        return true;
    return false;
  };

  var checkValidity = function (schema, object, name, required) {
    var p, v, malformed, objerrs, objerr, objreq, errors = {};

    if (!schema.hasOwnProperty('type'))
      throw new Error('schema: property \'' + name + '\' is missing a type.');

    if (!enforceType.hasOwnProperty(schema.type))
      throw new Error('schema: property \'' + name + '\' uses invalid type \'' + schema.type + '\'.');

    if (object.hasOwnProperty(name)) {
      // enforce type for object
      try {
        object[name] = enforceType[schema.type](object[name]);
      } catch (err) {
        return {'type': schema.type};
      }

      if (schema.type === 'object' && schema.hasOwnProperty('properties')) {
        malformed = false;
        objerrs = {};
        objreq = [];
        for (p in schema.properties) {
          objerr = checkValidity(schema.properties[p], object[name], p, schema.required);
          if (objerr !== null) {
            objerrs[p] = objerr;
            malformed = true;
          }
        }
        if (malformed)
          return {'schema': objerrs};
      } else if (schema.type === 'array' && schema.hasOwnProperty('items')) {
        malformed = false;
        objerrs = {};
        for (i = 0; i<object[name].length; i++) {
          objerr = checkValidity(schema.items, object[name], i, schema.required);
          if (objerr !== null) {
            objerrs[i] = objerr;
            malformed = true;
          }
        }
        if (malformed)
          return {'schema': objerrs};
      }

      malformed = false;
      objerrs = {};
      for (v in schema) {
        if (schema.hasOwnProperty(v) && v !== 'properties' && v !== 'items' && v !== 'type' && v !== 'required' && v !== 'default' v !== 'meta' && v !== 'methods') {
          if (!validateField.hasOwnProperty(v))
            throw new Error('schema: property \'' + name + '\' has non-existent validation \'' + v + '\'.');
          try {
            validateField[v](object[name], schema[v]);
          } catch(err) {
            objerrs[v] = true;
            malformed = true;
          }
        }
      }
      if (malformed)
        return objerrs;
    } else {
      if (isRequired(name, required))
        return {'required': true};
      else if (schema.hasOwnProperty('default')) {
        try {
          object[name] = enforceType[schema.type](schema.default);
        } catch (err) {
          return {'type': schema.type};
        }
      }
    }

    return null;
  };

  var checkExtra = function (schema, object, name, prefix, remove) {
    var extraFields = [];
    var subfields;
    var p, i, j;

    remove = typeof remove !== undefined ? remove : false;

    if (typeof schema !== 'object' || !schema.hasOwnProperty('type')) {
      extraFields.push(prefix + (prefix.length > 0 ? '.' : '') + name);
      if (remove)
        delete object[name];
    } else if (schema.type === 'object' && schema.hasOwnProperty('properties')) {
      for (p in object[name])  {
        if (object[name].hasOwnProperty(p)) {
          subfields = checkExtra(schema.properties[p], object[name], p, prefix + name, remove);
          for (j = 0; j<subfields.length; j++)
            extraFields.push(subfields[j]);
        }
      }
    } else if (schema.type === 'array' && schema.hasOwnProperty('items')) {
      for (i = 0; i<object[name].length; i++)  {
        subfields = checkExtra(schema.items, object[name], i, prefix + name + '[' + i.toString() + ']', remove);
        for (j = 0; j<subfields.length; j++)
          extraFields.push(subfields[j]);
      }
    }

    return extraFields;
  };

  var schemaValidator = function (schema, object, options) {
    var errors = {};
    var validation = null;
    var additional = [];
    var p, properr;

    options = options || {};
    options.remove = options.hasOwnProperty('remove') ? options.remove : true;

    if (!schema.hasOwnProperty('type') && schema.type == 'object')
      throw new Error('schema: root schema must be of type \'object\'.');
    if (schema.hasOwnProperty('properties')) {
      var root_key = '';
      var root_object = {};
      root_object[root_key] = object;
      properr = checkValidity(schema, root_object, root_key, [root_key]);
      if (properr !== null && properr.hasOwnProperty('schema'))
        validation = properr.schema;
      additional = checkExtra(schema, root_object, root_key, '', options.remove);
    }

    if (validation)
      errors.validation = validation;
    if (additional.length > 0)
      errors.additional = additional;
    return errors;
  };

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = schemaValidator;
    }
    exports.validate = schemaValidator;
    exports.addType = function (name, func) {
      enforceType[name] = func;
    };
    exports.addCheck = function (name, func) {
      validateField[name] = func;
    };
  } else {
    this.schemaValidator = schemaValidator;
  }
}).call(this);
<commit_msg>Corrected minor bugs.<commit_after>/**
 * schema-validator.js -- A javascript library to validate json input
 * with a schema.
 *
 *  Copyright (c) 2013 Alex Cornejo.
 *
 * Redistributable under a MIT-style open source license.
 *
 * schema-validator depends on node-validator.
 */

(function () {
  var enforceType = {
    'string': function (x) {
      if (typeof x !== 'string')
        throw new Error('is not a string');
      return x;
    },
    'boolean': function (x) {
      if (typeof x !== 'boolean')
        throw new Error('is not a boolean');
      return x;
    },
    'number': function (x) {
      if (typeof x !== 'number')
        throw new Error('is not a number');
      return x;
    },
    'integer': function (x) {
      if (typeof x !== 'number' || x % 1 !== 0)
        throw new Error('is not an integer');
      return x;
    },
    'object': function (x) {
      if (!x || typeof x !== 'object')
        throw new Error('is not an object');
      return x;
    },
    'array': function (x) {
      if (!x || typeof x !== 'object' || typeof x.length !== 'number' || toString.call(x) !== '[object Array]')
        throw new Error('is not an array');
      return x;
    },
    'date': function (x) {
      if (x === 'now') return new Date();
      var d = new Date(x);
      if (isNaN(d.getTime()))
          throw new Error('is not a date');
      return d;
    }
  };

  // TODO: rewrite, equals?, contains? isAfter, isBefore?
  var validateField = function () {
    var list = [
      'isAlpha', 'isAlphanumeric', 'isEmail', 'isUrl', 'isIP', 'isIPv4',
      'isIPv6', 'isCreditCard', 'isUUID', 'isUUIDv3', 'isUUIDv4',
      'isDate', 'isHexadecimal', 'isHexColor', 'isLowercase',
      'isUppercase', 'notEmpty', 'isAfter', 'isBefore', 'regex',
      'notRegex', 'equals', 'contains', 'notContains'];
    var i, d = {};

    if (typeof check === 'undefined' && typeof require !== 'undefined')
      check = require('validator').check;

    function makeFunction(f) {
      return function (v, p, q) { check(v)[f](p, q); };
    }

    for (i = 0; i<list.length; i++) {
      d[list[i]] = makeFunction(list[i]);
    }

    d.readOnly = function (v, p) {
      if (p)
        throw new Error('is read only');
    };

    d.notIn = function (v, p) {
      for (var i = 0; i < p.length; i++)
        if (v === p[i])
          throw new Error('found in set');
    };

    d.isIn = function (v, p) {
      for (var i = 0; i < p.length; i++)
        if (v === p[i]) return;
      throw new Error('not found in set');
    };

    d.len = function (v, p) {
      if (v.length !== p)
        throw new Error('wrong length');
    };

    d.minLength = function (v, p) {
      if (v.length < p)
        throw new Error('too small');
    };

    d.maxLength = function (v, p) {
      if (v.length > p)
        throw new Error('too large');
    };

    d.min = function (v, p) {
      if (v < p)
        throw new Error('too small');
    };

    d.max = function (v, p) {
      if (v > p)
        throw new Error('too large');
    };

    d.startsWith = function (v, p) {
      if (typeof v !== 'string' || v.indexOf(p) !== 0)
        throw new Error('does not start with prefix');
    };

    d.endsWith = function (v, p) {
      if (typeof v !== 'string' || typeof p !== 'string' || v.indexOf(p, v.length - p.length) === -1)
        throw new Error('does not end with suffix');
    };

    return d;
  }();

  var isRequired = function (p, required) {
    var i;
    if (typeof required !== 'object' || toString.call(required) !== '[object Array]')
      return false;
    for (i = 0; i<required.length; i++)
      if (required[i] === p)
        return true;
    return false;
  };

  var checkValidity = function (schema, object, name, required) {
    var p, v, malformed, objerrs, objerr, objreq, errors = {};

    if (!schema.hasOwnProperty('type'))
      throw new Error('schema: property \'' + name + '\' is missing a type.');

    if (!enforceType.hasOwnProperty(schema.type))
      throw new Error('schema: property \'' + name + '\' uses invalid type \'' + schema.type + '\'.');

    if (object.hasOwnProperty(name)) {
      // enforce type for object
      try {
        object[name] = enforceType[schema.type](object[name]);
      } catch (err) {
        return {'type': schema.type};
      }

      if (schema.type === 'object' && schema.hasOwnProperty('properties')) {
        malformed = false;
        objerrs = {};
        objreq = [];
        for (p in schema.properties) {
          objerr = checkValidity(schema.properties[p], object[name], p, schema.required);
          if (objerr !== null) {
            objerrs[p] = objerr;
            malformed = true;
          }
        }
        if (malformed)
          return {'schema': objerrs};
      } else if (schema.type === 'array' && schema.hasOwnProperty('items')) {
        malformed = false;
        objerrs = {};
        for (i = 0; i<object[name].length; i++) {
          objerr = checkValidity(schema.items, object[name], i, schema.required);
          if (objerr !== null) {
            objerrs[i] = objerr;
            malformed = true;
          }
        }
        if (malformed)
          return {'schema': objerrs};
      }

      malformed = false;
      objerrs = {};
      for (v in schema) {
        if (schema.hasOwnProperty(v) && v !== 'properties' && v !== 'items' && v !== 'type' && v !== 'required' && v !== 'default' && v !== 'meta' && v !== 'methods') {
          if (!validateField.hasOwnProperty(v))
            throw new Error('schema: property \'' + name + '\' has non-existent validation \'' + v + '\'.');
          try {
            validateField[v](object[name], schema[v]);
          } catch(err) {
            objerrs[v] = true;
            malformed = true;
          }
        }
      }
      if (malformed)
        return objerrs;
    } else {
      if (isRequired(name, required))
        return {'required': true};
      else if (schema.hasOwnProperty('default')) {
        try {
          object[name] = enforceType[schema.type](schema['default']);
        } catch (err) {
          return {'type': schema.type};
        }
      }
    }

    return null;
  };

  var checkExtra = function (schema, object, name, prefix, remove) {
    var extraFields = [];
    var subfields;
    var p, i, j;

    remove = typeof remove !== undefined ? remove : false;

    if (typeof schema !== 'object' || !schema.hasOwnProperty('type')) {
      extraFields.push(prefix + (prefix.length > 0 ? '.' : '') + name);
      if (remove)
        delete object[name];
    } else if (schema.type === 'object' && schema.hasOwnProperty('properties')) {
      for (p in object[name])  {
        if (object[name].hasOwnProperty(p)) {
          subfields = checkExtra(schema.properties[p], object[name], p, prefix + name, remove);
          for (j = 0; j<subfields.length; j++)
            extraFields.push(subfields[j]);
        }
      }
    } else if (schema.type === 'array' && schema.hasOwnProperty('items')) {
      for (i = 0; i<object[name].length; i++)  {
        subfields = checkExtra(schema.items, object[name], i, prefix + name + '[' + i.toString() + ']', remove);
        for (j = 0; j<subfields.length; j++)
          extraFields.push(subfields[j]);
      }
    }

    return extraFields;
  };

  var schemaValidator = function (schema, object, options) {
    var errors = {};
    var validation = null;
    var additional = [];
    var p, properr;

    options = options || {};
    options.remove = options.hasOwnProperty('remove') ? options.remove : true;

    if (!schema.hasOwnProperty('type') && schema.type == 'object')
      throw new Error('schema: root schema must be of type \'object\'.');
    if (schema.hasOwnProperty('properties')) {
      var root_key = '';
      var root_object = {};
      root_object[root_key] = object;
      properr = checkValidity(schema, root_object, root_key, [root_key]);
      if (properr !== null && properr.hasOwnProperty('schema'))
        validation = properr.schema;
      additional = checkExtra(schema, root_object, root_key, '', options.remove);
    }

    if (validation)
      errors.validation = validation;
    if (additional.length > 0)
      errors.additional = additional;
    return errors;
  };

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = schemaValidator;
    }
    exports.validate = schemaValidator;
    exports.addType = function (name, func) {
      enforceType[name] = func;
    };
    exports.addCheck = function (name, func) {
      validateField[name] = func;
    };
  } else {
    this.schemaValidator = schemaValidator;
  }
}).call(this);
<|endoftext|>


<commit_before>import _ from 'lodash'
import d3 from 'd3'
import React from 'react'
import Layer from 'react-layer'

import color from 'util/color'
import palettes from 'util/palettes'
import Tooltip from 'component/Tooltip.jsx'

function _domain (data, options) {
  return [0, _(data).map(options.value).sum()]
}

var DEFAULTS = {
  domain: _domain,
  innerRadius: 0,
  outerRadius: 1,
  margin: {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
  },
  value: _.property('value'),
  color: palettes.blue,
  name: _.property('indicator.short_name'),
  yFormat: function (d) {
    return d3.format((Math.abs(d) < 1) ? '%' : 'n')(d)
  }
}

function PieChart () {
}

_.extend(PieChart.prototype, {
  defaults: DEFAULTS,

  initialize: function (el, data, options) {
    var opts = this._options = _.defaults({}, options, DEFAULTS)

    this._height = this._width = _.get(opts, 'size', el.clientWidth)

    var svg = this._svg = d3.select(el).append('svg').attr('class', 'pie')

    var g = svg.append('g').attr('class', 'margin')

    g
      .append('g').attr('class', 'data')
      .append('path').attr('class', 'bg')

    g.append('g').attr('class', 'annotation')

    this.update(data)
  },

  update: function (values, options) {
    options = _.assign(this._options, options)
    var margin = options.margin

    values = _(values)
      .filter(d => {
        var v = options.value(d)
        return _.isFinite(v) && v > 0
      })
      .sortBy(options.value)
      .reverse()
      .value()

    let data = []
    if (values.length === 1) {
      // assume single indicator respresenting perentage
      data.push({ value: Math.round(values[0].value * 100) / 100 })
      data.push({ value: 1 - data[0].value })
    } else {
      data = values
    }

    var w = this._width - margin.left - margin.right
    var h = this._height - margin.top - margin.bottom
    var s = Math.min(w, h)

    var svg = this._svg

    svg.attr({
      'viewBox': '0 0 ' + this._width + ' ' + this._height
    })
      .select('.margin')
      .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')

    var xPosition = options.notInCenter ? w / 4 : w / 2
    var yPosition = options.notInCenter ? h / 3 : h / 2

    let g = svg.select('.data')
      .attr('transform', 'translate(' + xPosition + ', ' + yPosition + ')')

    if (options.percentage) {
      var annotation = svg.select('.annotation').selectAll('.percentage').data([options.percentage])
      annotation.enter().append('text')
      annotation.attr('class', 'percentage')
        .attr({'x': xPosition, 'y': yPosition, 'dy': 5})
        .style({
          'text-anchor': 'middle',
          'opacity': d => { return d === '0%' ? 0 : 1 }
        })
        .text(d => { return d })

      annotation.exit().remove()
    }

    var arc = d3.svg.arc()
      .innerRadius(s / 2 * options.innerRadius)
      .outerRadius(s / 2 * options.outerRadius)

    svg.select('.bg')
      .datum({
        startAngle: 0,
        endAngle: 2 * Math.PI
      })
      .attr('d', arc)

    var scale = d3.scale.linear()
      .domain(options.domain(data, options))
      .range([0, 2 * Math.PI])

    var pie = d3.layout.stack()
      .values(function (d) {
        return [d]
      })
      .x(options.name)
      .y(options.value)
      .out(function (d, y0, y) {
        d.startAngle = scale(y0)
        d.endAngle = scale(y0 + y)
      })

    let fill = color.map(data.map(options.name), options.color)

    var slice = g.selectAll('.slice').data(pie(_.cloneDeep(data)))

    slice.enter()
      .append('path')
      .attr('class', 'slice')

    slice.attr({
      'd': arc,
      'fill': _.flow(options.name, fill),
      'stroke': '#fff'
    }).on('mousemove', d => {
      var evt = d3.event
      var render = function () {
        let tip = options.name(d)
          ? `${options.name(d)}: ${options.yFormat(options.value(d))}`
          : options.yFormat(options.value(d))
        return (
          <Tooltip left={evt.pageX} top={evt.pageY}>

{tip}

          </Tooltip>
        )
      }

      if (this.layer) {
        this.layer._render = render
      } else {
        this.layer = new Layer(document.body, render)
      }

      this.layer.render()
    })
      .on('mouseout', d => {
        if (this.layer) {
          this.layer.destroy()
          this.layer = null
        }
      })

    slice.exit().remove()
  }
})

export default PieChart
<commit_msg>add IE compatible for pie charts<commit_after>import _ from 'lodash'
import d3 from 'd3'
import React from 'react'
import Layer from 'react-layer'

import browser from 'util/browser'
import color from 'util/color'
import palettes from 'util/palettes'
import Tooltip from 'component/Tooltip.jsx'

function _domain (data, options) {
  return [0, _(data).map(options.value).sum()]
}

var DEFAULTS = {
  domain: _domain,
  innerRadius: 0,
  outerRadius: 1,
  margin: {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
  },
  value: _.property('value'),
  color: palettes.blue,
  name: _.property('indicator.short_name'),
  yFormat: function (d) {
    return d3.format((Math.abs(d) < 1) ? '%' : 'n')(d)
  }
}

function PieChart () {
}

_.extend(PieChart.prototype, {
  defaults: DEFAULTS,

  initialize: function (el, data, options) {
    var opts = this._options = _.defaults({}, options, DEFAULTS)

    this._height = this._width = _.get(opts, 'size', el.clientWidth)

    var svg = this._svg = d3.select(el).append('svg')
      .attr('class', 'pie')
      .attr({
        'viewBox': '0 0 ' + this._width + ' ' + this._height
      })

    if (browser.isIE()) {
      svg.attr({
        'width': this._width,
        'height': this._height
      })
    }

    var g = svg.append('g').attr('class', 'margin')

    g
      .append('g').attr('class', 'data')
      .append('path').attr('class', 'bg')

    g.append('g').attr('class', 'annotation')

    this.update(data)
  },

  update: function (values, options) {
    options = _.assign(this._options, options)
    var margin = options.margin

    values = _(values)
      .filter(d => {
        var v = options.value(d)
        return _.isFinite(v) && v > 0
      })
      .sortBy(options.value)
      .reverse()
      .value()

    let data = []
    if (values.length === 1) {
      // assume single indicator respresenting perentage
      data.push({ value: Math.round(values[0].value * 100) / 100 })
      data.push({ value: 1 - data[0].value })
    } else {
      data = values
    }

    var w = this._width - margin.left - margin.right
    var h = this._height - margin.top - margin.bottom
    var s = Math.min(w, h)

    var svg = this._svg

    svg.select('.margin')
      .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')

    var xPosition = options.notInCenter ? w / 4 : w / 2
    var yPosition = options.notInCenter ? h / 3 : h / 2

    let g = svg.select('.data')
      .attr('transform', 'translate(' + xPosition + ', ' + yPosition + ')')

    if (options.percentage) {
      var annotation = svg.select('.annotation').selectAll('.percentage').data([options.percentage])
      annotation.enter().append('text')
      annotation.attr('class', 'percentage')
        .attr({'x': xPosition, 'y': yPosition, 'dy': 5})
        .style({
          'text-anchor': 'middle',
          'opacity': d => { return d === '0%' ? 0 : 1 }
        })
        .text(d => { return d })

      annotation.exit().remove()
    }

    var arc = d3.svg.arc()
      .innerRadius(s / 2 * options.innerRadius)
      .outerRadius(s / 2 * options.outerRadius)

    svg.select('.bg')
      .datum({
        startAngle: 0,
        endAngle: 2 * Math.PI
      })
      .attr('d', arc)

    var scale = d3.scale.linear()
      .domain(options.domain(data, options))
      .range([0, 2 * Math.PI])

    var pie = d3.layout.stack()
      .values(function (d) {
        return [d]
      })
      .x(options.name)
      .y(options.value)
      .out(function (d, y0, y) {
        d.startAngle = scale(y0)
        d.endAngle = scale(y0 + y)
      })

    let fill = color.map(data.map(options.name), options.color)

    var slice = g.selectAll('.slice').data(pie(_.cloneDeep(data)))

    slice.enter()
      .append('path')
      .attr('class', 'slice')

    slice.attr({
      'd': arc,
      'fill': _.flow(options.name, fill),
      'stroke': '#fff'
    }).on('mousemove', d => {
      var evt = d3.event
      var render = function () {
        let tip = options.name(d)
          ? `${options.name(d)}: ${options.yFormat(options.value(d))}`
          : options.yFormat(options.value(d))
        return (
          <Tooltip left={evt.pageX} top={evt.pageY}>

{tip}

          </Tooltip>
        )
      }

      if (this.layer) {
        this.layer._render = render
      } else {
        this.layer = new Layer(document.body, render)
      }

      this.layer.render()
    })
      .on('mouseout', d => {
        if (this.layer) {
          this.layer.destroy()
          this.layer = null
        }
      })

    slice.exit().remove()
  }
})

export default PieChart
<|endoftext|>


<commit_before>'use strict';

var defaultTreeAdapter = require('../tree_adapters/default'),
    mergeOptions = require('../utils/merge_options'),
    doctype = require('../common/doctype'),
    HTML = require('../common/html');

//Aliases
var $ = HTML.TAG_NAMES,
    NS = HTML.NAMESPACES;

//Default serializer options
var DEFAULT_OPTIONS = {
    treeAdapter: defaultTreeAdapter
};

//Escaping regexes
var AMP_REGEX = /&/g,
    NBSP_REGEX = /\u00a0/g,
    DOUBLE_QUOTE_REGEX = /"/g,
    LT_REGEX = /</g,
    GT_REGEX = />/g;

//Serializer
var Serializer = module.exports = function (node, options) {
    this.options = mergeOptions(DEFAULT_OPTIONS, options);
    this.treeAdapter = this.options.treeAdapter;

    this.html = '';
    this.startNode = node;
};

// NOTE: exported as static method for the testing purposes
Serializer.escapeString = function (str, attrMode) {
    str = str
        .replace(AMP_REGEX, '&')
        .replace(NBSP_REGEX, ' ');

    if (attrMode)
        str = str.replace(DOUBLE_QUOTE_REGEX, '"');

    else {
        str = str
            .replace(LT_REGEX, '<')
            .replace(GT_REGEX, '>');
    }

    return str;
};

//API
Serializer.prototype.serialize = function () {
    this._serializeChildNodes(this.startNode);

    return this.html;
};

//Internals
Serializer.prototype._serializeChildNodes = function (parentNode) {
    var childNodes = this.treeAdapter.getChildNodes(parentNode);

    if (childNodes) {
        for (var i = 0, cnLength = childNodes.length; i < cnLength; i++) {
            var currentNode = childNodes[i];

            if (this.treeAdapter.isElementNode(currentNode))
                this._serializeElement(currentNode);

            else if (this.treeAdapter.isTextNode(currentNode))
                this._serializeTextNode(currentNode);

            else if (this.treeAdapter.isCommentNode(currentNode))
                this._serializeCommentNode(currentNode);

            else if (this.treeAdapter.isDocumentTypeNode(currentNode))
                this._serializeDocumentTypeNode(currentNode);
        }
    }
};

Serializer.prototype._serializeElement = function (node) {
    var tn = this.treeAdapter.getTagName(node),
        ns = this.treeAdapter.getNamespaceURI(node);

    this.html += '<' + tn;
    this._serializeAttributes(node);
    this.html += '>';

    if (tn !== $.AREA && tn !== $.BASE && tn !== $.BASEFONT && tn !== $.BGSOUND && tn !== $.BR && tn !== $.BR &&
        tn !== $.COL && tn !== $.EMBED && tn !== $.FRAME && tn !== $.HR && tn !== $.IMG && tn !== $.INPUT &&
        tn !== $.KEYGEN && tn !== $.LINK && tn !== $.MENUITEM && tn !== $.META && tn !== $.PARAM && tn !== $.SOURCE &&
        tn !== $.TRACK && tn !== $.WBR) {

        var childNodesHolder = tn === $.TEMPLATE && ns === NS.HTML ?
            this.treeAdapter.getTemplateContent(node) :
            node;

        this._serializeChildNodes(childNodesHolder);
        this.html += '</' + tn + '>';
    }
};

Serializer.prototype._serializeAttributes = function (node) {
    var attrs = this.treeAdapter.getAttrList(node);

    for (var i = 0, attrsLength = attrs.length; i < attrsLength; i++) {
        var attr = attrs[i],
            value = Serializer.escapeString(attr.value, true);

        this.html += ' ';

        if (!attr.namespace)
            this.html += attr.name;

        else if (attr.namespace === NS.XML)
            this.html += 'xml:' + attr.name;

        else if (attr.namespace === NS.XMLNS) {
            if (attr.name !== 'xmlns')
                this.html += 'xmlns:';

            this.html += attr.name;
        }

        else if (attr.namespace === NS.XLINK)
            this.html += 'xlink:' + attr.name;

        else
            this.html += attr.namespace + ':' + attr.name;

        this.html += '="' + value + '"';
    }
};

Serializer.prototype._serializeTextNode = function (node) {
    var content = this.treeAdapter.getTextNodeContent(node),
        parent = this.treeAdapter.getParentNode(node),
        parentTn = void 0;

    if (parent && this.treeAdapter.isElementNode(parent))
        parentTn = this.treeAdapter.getTagName(parent);

    if (parentTn === $.STYLE || parentTn === $.SCRIPT || parentTn === $.XMP || parentTn === $.IFRAME ||
        parentTn === $.NOEMBED || parentTn === $.NOFRAMES || parentTn === $.PLAINTEXT || parentTn === $.NOSCRIPT)

        this.html += content;

    else
        this.html += Serializer.escapeString(content, false);
};

Serializer.prototype._serializeCommentNode = function (node) {
    this.html += '<!--' + this.treeAdapter.getCommentNodeContent(node) + '-->';
};

Serializer.prototype._serializeDocumentTypeNode = function (node) {
    var name = this.treeAdapter.getDocumentTypeNodeName(node);

    this.html += '<' + doctype.serializeContent(name, null, null) + '>';
};
<commit_msg>Remove duplicate condition<commit_after>'use strict';

var defaultTreeAdapter = require('../tree_adapters/default'),
    mergeOptions = require('../utils/merge_options'),
    doctype = require('../common/doctype'),
    HTML = require('../common/html');

//Aliases
var $ = HTML.TAG_NAMES,
    NS = HTML.NAMESPACES;

//Default serializer options
var DEFAULT_OPTIONS = {
    treeAdapter: defaultTreeAdapter
};

//Escaping regexes
var AMP_REGEX = /&/g,
    NBSP_REGEX = /\u00a0/g,
    DOUBLE_QUOTE_REGEX = /"/g,
    LT_REGEX = /</g,
    GT_REGEX = />/g;

//Serializer
var Serializer = module.exports = function (node, options) {
    this.options = mergeOptions(DEFAULT_OPTIONS, options);
    this.treeAdapter = this.options.treeAdapter;

    this.html = '';
    this.startNode = node;
};

// NOTE: exported as static method for the testing purposes
Serializer.escapeString = function (str, attrMode) {
    str = str
        .replace(AMP_REGEX, '&')
        .replace(NBSP_REGEX, ' ');

    if (attrMode)
        str = str.replace(DOUBLE_QUOTE_REGEX, '"');

    else {
        str = str
            .replace(LT_REGEX, '<')
            .replace(GT_REGEX, '>');
    }

    return str;
};

//API
Serializer.prototype.serialize = function () {
    this._serializeChildNodes(this.startNode);

    return this.html;
};

//Internals
Serializer.prototype._serializeChildNodes = function (parentNode) {
    var childNodes = this.treeAdapter.getChildNodes(parentNode);

    if (childNodes) {
        for (var i = 0, cnLength = childNodes.length; i < cnLength; i++) {
            var currentNode = childNodes[i];

            if (this.treeAdapter.isElementNode(currentNode))
                this._serializeElement(currentNode);

            else if (this.treeAdapter.isTextNode(currentNode))
                this._serializeTextNode(currentNode);

            else if (this.treeAdapter.isCommentNode(currentNode))
                this._serializeCommentNode(currentNode);

            else if (this.treeAdapter.isDocumentTypeNode(currentNode))
                this._serializeDocumentTypeNode(currentNode);
        }
    }
};

Serializer.prototype._serializeElement = function (node) {
    var tn = this.treeAdapter.getTagName(node),
        ns = this.treeAdapter.getNamespaceURI(node);

    this.html += '<' + tn;
    this._serializeAttributes(node);
    this.html += '>';

    if (tn !== $.AREA && tn !== $.BASE && tn !== $.BASEFONT && tn !== $.BGSOUND && tn !== $.BR &&
        tn !== $.COL && tn !== $.EMBED && tn !== $.FRAME && tn !== $.HR && tn !== $.IMG && tn !== $.INPUT &&
        tn !== $.KEYGEN && tn !== $.LINK && tn !== $.MENUITEM && tn !== $.META && tn !== $.PARAM && tn !== $.SOURCE &&
        tn !== $.TRACK && tn !== $.WBR) {

        var childNodesHolder = tn === $.TEMPLATE && ns === NS.HTML ?
            this.treeAdapter.getTemplateContent(node) :
            node;

        this._serializeChildNodes(childNodesHolder);
        this.html += '</' + tn + '>';
    }
};

Serializer.prototype._serializeAttributes = function (node) {
    var attrs = this.treeAdapter.getAttrList(node);

    for (var i = 0, attrsLength = attrs.length; i < attrsLength; i++) {
        var attr = attrs[i],
            value = Serializer.escapeString(attr.value, true);

        this.html += ' ';

        if (!attr.namespace)
            this.html += attr.name;

        else if (attr.namespace === NS.XML)
            this.html += 'xml:' + attr.name;

        else if (attr.namespace === NS.XMLNS) {
            if (attr.name !== 'xmlns')
                this.html += 'xmlns:';

            this.html += attr.name;
        }

        else if (attr.namespace === NS.XLINK)
            this.html += 'xlink:' + attr.name;

        else
            this.html += attr.namespace + ':' + attr.name;

        this.html += '="' + value + '"';
    }
};

Serializer.prototype._serializeTextNode = function (node) {
    var content = this.treeAdapter.getTextNodeContent(node),
        parent = this.treeAdapter.getParentNode(node),
        parentTn = void 0;

    if (parent && this.treeAdapter.isElementNode(parent))
        parentTn = this.treeAdapter.getTagName(parent);

    if (parentTn === $.STYLE || parentTn === $.SCRIPT || parentTn === $.XMP || parentTn === $.IFRAME ||
        parentTn === $.NOEMBED || parentTn === $.NOFRAMES || parentTn === $.PLAINTEXT || parentTn === $.NOSCRIPT)

        this.html += content;

    else
        this.html += Serializer.escapeString(content, false);
};

Serializer.prototype._serializeCommentNode = function (node) {
    this.html += '<!--' + this.treeAdapter.getCommentNodeContent(node) + '-->';
};

Serializer.prototype._serializeDocumentTypeNode = function (node) {
    var name = this.treeAdapter.getDocumentTypeNodeName(node);

    this.html += '<' + doctype.serializeContent(name, null, null) + '>';
};
<|endoftext|>


<commit_before>(function() {
  goog.provide('go_map_directive');

  var module = angular.module('go_map_directive', []);

  module.directive('goMap',  function() {
    return {
      restrict: 'E',
      scope: {
        map: '=gaMapMap'
      },
      link: function(scope, element, attrs) {
        scope.map.setTarget(element[0]);
      }
    };
  });
})();
<commit_msg>Minor fixes in map directive<commit_after>goog.provide('go_map_directive');

(function() {

  var module = angular.module('go_map_directive', []);

  module.directive('goMap',  function() {
    return {
      restrict: 'A',
      scope: {
        map: '=goMapMap'
      },
      link: function(scope, element, attrs) {
        scope.map.setTarget(element[0]);
      }
    };
  });
})();
<|endoftext|>


<commit_before>(function() {
    'use strict';

    if (!global.cloudcmd)
        return console.log(
            '# commander.js'                                    + '\n'  +
            '# -----------'                                     + '\n'  +
            '# Module is part of Cloud Commander,'              + '\n'  +
            '# used for getting dir content.'                   + '\n'  +
            '# and forming html content'                        + '\n'  +
            '# http://cloudcmd.io'                              + '\n');

    var main                = global.cloudcmd.main,
        fs                  = main.fs,
        Util                = main.util;

    exports.getDirContent = function(pPath, pCallBack) {
        var lRet = Util.isString(pPath);

        if (lRet)
            fs.readdir(pPath, Util.call(readDir, {
                callback: pCallBack,
                path    : pPath
            }));
        else
            Util.exec(pCallBack, "First parameter should be a string");

        return lRet;
    };

    /**
     * Функция читает ссылку или выводит информацию об ошибке
     * @param pError
     * @param pFiles
     */
    function readDir(pParams) {
        var lRet = checkParams(pParams);
            lRet = lRet && Util.checkObj(pParams.params, ['path']);

        if (lRet) {
            var p           = pParams,
                c           = pParams.params,
                lFiles      = p.data,
                lDirPath    = getDirPath(c.path);

            if (!p.error && lFiles) {
                lFiles.data = lFiles.sort();

                /* Получаем информацию о файлах */
                var n           = lFiles.length,
                    lStats      = {},
                    lFilesData  = {
                        files       : lFiles,
                        stats       : lStats,
                        callback    : c.callback,
                        path        : c.path
                    },

                    lFill       = Util.retFunc(fillJSON, lFilesData);

                if (n)
                    for (var i = 0; i < n; i++) {
                        var lName = lDirPath + lFiles[i],
                            lParams =  {
                                callback    : lFill,
                                count       : n,
                                name        : lFiles[i],
                                stats       : lStats,
                            };

                        fs.stat(lName, Util.call(getFilesStat, lParams));
                    }
                else
                    fillJSON(lFilesData);
            } else
                Util.exec(c.callback, p.error.toString());
        }
    }

    /**
     * async getting file states
     * and putting it to lStats object
     */
    function getFilesStat(pParams) {
        var lRet = checkParams(pParams);
            lRet = lRet && Util.checkObjTrue(pParams.params,
                ['callback', 'stats', 'name', 'count']);

        if (lRet) {
            var p = pParams,
                c = p.params;

            c.stats[c.name] = !p.error ? p.data   : {
                'mode'          : 0,
                'size'          : 0,
                'isDirectory'   : Util.retFalse
            };

            if (c.count === Object.keys(c.stats).length)
                Util.exec(c.callback);
        }
    }

    /**
     * Function fill JSON by file stats
     *
     * @param pStats - object, contain file stats.
     *          example {'1.txt': stat}
     *
     * @param pFiles - array of files of current directory
     */
    function fillJSON(pParams) {
        var lRet = Util.checkObjTrue(pParams, ['files', 'stats', 'path']);

        if (lRet) {
            var p           = pParams,
                i, n        = p.files.length || 0,

                /* данные о файлах в формате JSON*/
                lJSON       = [],
                lJSONFile   = {},
                lDirPath    = getDirPath(p.path);

            lJSON[0]        = {
                path    : lDirPath,
                size    : 'dir'
            };

            var lName, lStats, lMode, lIsDir;
            for (i = 0; i < n; i++ ) {
                /* Переводим права доступа в 8-ричную систему */
                lName   = p.files[i],
                lStats  = p.stats[lName];

                if (lStats) {
                    lMode   = (lStats.mode - 0).toString(8),
                    lIsDir  = lStats.isDirectory();
                }

                /* Если папка - выводим пиктограмму папки   *
                 * В противоположном случае - файла         */
                lJSONFile = {
                    'name'  : p.files[i],
                    'size'  : lIsDir ? 'dir' : lStats.size,
                    'uid'   : lStats.uid,
                    'mode'  : lMode
                };

                lJSON[i+1] = lJSONFile;
            }

            Util.exec(p.callback, null, lJSON);

        }
    }

    function checkParams(pParams) {
        return Util.checkObj(pParams, ['error', 'data', 'params']);
    }

    function getDirPath(pPath) {
        var lRet = pPath;

        if (lRet !== '/')
            lRet += '/';

        return lRet;
    }

})();<commit_msg>refactor(commander) checkParams<commit_after>(function() {
    'use strict';

    if (!global.cloudcmd)
        return console.log(
            '# commander.js'                                    + '\n'  +
            '# -----------'                                     + '\n'  +
            '# Module is part of Cloud Commander,'              + '\n'  +
            '# used for getting dir content.'                   + '\n'  +
            '# and forming html content'                        + '\n'  +
            '# http://cloudcmd.io'                              + '\n');

    var main                = global.cloudcmd.main,
        fs                  = main.fs,
        Util                = main.util,
        checkParams         = main.checkCallBackParams;

    exports.getDirContent = function(pPath, pCallBack) {
        var lRet = Util.isString(pPath);

        if (lRet)
            fs.readdir(pPath, Util.call(readDir, {
                callback: pCallBack,
                path    : pPath
            }));
        else
            Util.exec(pCallBack, "First parameter should be a string");

        return lRet;
    };

    /**
     * Функция читает ссылку или выводит информацию об ошибке
     * @param pError
     * @param pFiles
     */
    function readDir(pParams) {
        var lRet = checkParams(pParams);
            lRet = lRet && Util.checkObj(pParams.params, ['path']);

        if (lRet) {
            var p           = pParams,
                c           = pParams.params,
                lFiles      = p.data,
                lDirPath    = getDirPath(c.path);

            if (!p.error && lFiles) {
                lFiles.data = lFiles.sort();

                /* Получаем информацию о файлах */
                var n           = lFiles.length,
                    lStats      = {},
                    lFilesData  = {
                        files       : lFiles,
                        stats       : lStats,
                        callback    : c.callback,
                        path        : c.path
                    },

                    lFill       = Util.retFunc(fillJSON, lFilesData);

                if (n)
                    for (var i = 0; i < n; i++) {
                        var lName = lDirPath + lFiles[i],
                            lParams =  {
                                callback    : lFill,
                                count       : n,
                                name        : lFiles[i],
                                stats       : lStats,
                            };

                        fs.stat(lName, Util.call(getFilesStat, lParams));
                    }
                else
                    fillJSON(lFilesData);
            } else
                Util.exec(c.callback, p.error.toString());
        }
    }

    /**
     * async getting file states
     * and putting it to lStats object
     */
    function getFilesStat(pParams) {
        var lRet = checkParams(pParams);
            lRet = lRet && Util.checkObjTrue(pParams.params,
                ['callback', 'stats', 'name', 'count']);

        if (lRet) {
            var p = pParams,
                c = p.params;

            c.stats[c.name] = !p.error ? p.data   : {
                'mode'          : 0,
                'size'          : 0,
                'isDirectory'   : Util.retFalse
            };

            if (c.count === Object.keys(c.stats).length)
                Util.exec(c.callback);
        }
    }

    /**
     * Function fill JSON by file stats
     *
     * @param pStats - object, contain file stats.
     *          example {'1.txt': stat}
     *
     * @param pFiles - array of files of current directory
     */
    function fillJSON(pParams) {
        var lRet = Util.checkObjTrue(pParams, ['files', 'stats', 'path']);

        if (lRet) {
            var p           = pParams,
                i, n        = p.files.length || 0,

                /* данные о файлах в формате JSON*/
                lJSON       = [],
                lJSONFile   = {},
                lDirPath    = getDirPath(p.path);

            lJSON[0]        = {
                path    : lDirPath,
                size    : 'dir'
            };

            var lName, lStats, lMode, lIsDir;
            for (i = 0; i < n; i++ ) {
                /* Переводим права доступа в 8-ричную систему */
                lName   = p.files[i],
                lStats  = p.stats[lName];

                if (lStats) {
                    lMode   = (lStats.mode - 0).toString(8),
                    lIsDir  = lStats.isDirectory();
                }

                /* Если папка - выводим пиктограмму папки   *
                 * В противоположном случае - файла         */
                lJSONFile = {
                    'name'  : p.files[i],
                    'size'  : lIsDir ? 'dir' : lStats.size,
                    'uid'   : lStats.uid,
                    'mode'  : lMode
                };

                lJSON[i+1] = lJSONFile;
            }

            Util.exec(p.callback, null, lJSON);

        }
    }

    function getDirPath(pPath) {
        var lRet = pPath;

        if (lRet !== '/')
            lRet += '/';

        return lRet;
    }

})();<|endoftext|>


<commit_before>"use strict";

/**
 * @description Implementation of 4x4 Matrix. Creates identity matrix.
 * @constructor
 * @property m {Float32Array} Matrix buffer.
 * @memberof! <global>
 */
meta.math.Matrix4 = function()
{
	this.m = new Float32Array(16);
	this.m[0] = 1.0;
	this.m[5] = 1.0;
	this.m[10] = 1.0;
	this.m[15] = 1.0;
};

meta.math.Matrix4.prototype =
{
	/**
	 * Resets matrix to identity matrix.
	 */
	reset: function()
	{
		this.m[0] = 1.0; this.m[1] = 0.0; this.m[2] = 0.0; this.m[3] = 0.0;
		this.m[4] = 0.0; this.m[5] = 1.0; this.m[6] = 0.0; this.m[7] = 0.0;
		this.m[8] = 0.0; this.m[9] = 0.0; this.m[10] = 1.0; this.m[11] = 0.0;
		this.m[12] = 0.0; this.m[13] = 0.0; this.m[14] = 0.0; this.m[15] = 1.0;
	},

	scale: function(x, y, z) {
		this.m[0] = x;
		this.m[5] = y;
		this.m[10] = z;
	},

	/**
	 * Creates orthographic projection matrix.
	 * @param left {Number} Far left. Usually = 0.
	 * @param right {Number} Far right. Width.
	 * @param bottom {Number} Far bottom. Height.
	 * @param top {Number} Far top. Usually = 0.
	 * @param zNear {Number} Nearest point. Usually = 0.
	 * @param zFar {Number} Farthest point. Usually = 1.
	 */
	ortho: function(left, right, bottom, top, zNear, zFar)
	{
		this.m[0] = 2.0 / (right - left);
		this.m[1] = 0.0;
		this.m[2] = 0.0;
		this.m[3] = 0.0;

		this.m[4] = 0.0;
		this.m[5] = 2.0 / (top - bottom);
		this.m[6] = 0.0;
		this.m[7] = 0.0;

		this.m[8] = 0.0;
		this.m[9] = 0.0;
		this.m[10] = -2.0 / (zFar - zNear);
		this.m[11] = 0.0;

		this.m[12] = -(right + left) / (right - left);
		this.m[13] = -(top + bottom) / (top - bottom);
		this.m[14] = -(zFar + zNear) / (zFar - zNear);
		this.m[15] = 1.0;
	}
};<commit_msg>Added matrix4.scale.<commit_after>"use strict";

/**
 * @description Implementation of 4x4 Matrix. Creates identity matrix.
 * @constructor
 * @property m {Float32Array} Matrix buffer.
 * @memberof! <global>
 */
meta.math.Matrix4 = function()
{
	this.m = new Float32Array(16);
	this.m[0] = 1.0;
	this.m[5] = 1.0;
	this.m[10] = 1.0;
	this.m[15] = 1.0;
};

meta.math.Matrix4.prototype =
{
	/**
	 * Resets matrix to identity matrix.
	 */
	reset: function()
	{
		this.m[0] = 1.0; this.m[1] = 0.0; this.m[2] = 0.0; this.m[3] = 0.0;
		this.m[4] = 0.0; this.m[5] = 1.0; this.m[6] = 0.0; this.m[7] = 0.0;
		this.m[8] = 0.0; this.m[9] = 0.0; this.m[10] = 1.0; this.m[11] = 0.0;
		this.m[12] = 0.0; this.m[13] = 0.0; this.m[14] = 0.0; this.m[15] = 1.0;
	},

	/**
	 * Scale matrix.
	 * @param x {Number} Scale x axis.
	 * @param y {Number} Scale y axis.
	 * @param z {Number} Scale z axis.
	 */
	scale: function(x, y, z) {
		this.m[0] = x;
		this.m[5] = y;
		this.m[10] = z;
	},

	/**
	 * Creates orthographic projection matrix.
	 * @param left {Number} Far left. Usually = 0.
	 * @param right {Number} Far right. Width.
	 * @param bottom {Number} Far bottom. Height.
	 * @param top {Number} Far top. Usually = 0.
	 * @param zNear {Number} Nearest point. Usually = 0.
	 * @param zFar {Number} Farthest point. Usually = 1.
	 */
	ortho: function(left, right, bottom, top, zNear, zFar)
	{
		this.m[0] = 2.0 / (right - left);
		this.m[1] = 0.0;
		this.m[2] = 0.0;
		this.m[3] = 0.0;

		this.m[4] = 0.0;
		this.m[5] = 2.0 / (top - bottom);
		this.m[6] = 0.0;
		this.m[7] = 0.0;

		this.m[8] = 0.0;
		this.m[9] = 0.0;
		this.m[10] = -2.0 / (zFar - zNear);
		this.m[11] = 0.0;

		this.m[12] = -(right + left) / (right - left);
		this.m[13] = -(top + bottom) / (top - bottom);
		this.m[14] = -(zFar + zNear) / (zFar - zNear);
		this.m[15] = 1.0;
	}
};<|endoftext|>


<commit_before>/**
 * # SocketIo
 * Copyright(c) 2018 Stefano Balietti
 * MIT Licensed
 *
 * Handles network connections through Socket.IO
 */

"use strict";

// Global scope

module.exports = SocketIo;

var GameMsg = require('nodegame-client').GameMsg;

/**
 * ## SocketIo constructor
 *
 * Creates an instance of SocketIo
 *
 * @param {GameServer} server A GameServer instance
 *
 * @see GameServer
 */
function SocketIo(gameServer) {

    /**
     * ### SocketIo.name
     *
     * The name of the socket.
     */
    this.name = 'sio';

    /**
     * ### Socket.gameServer
     *
     * Reference to the game server in which the socket is created.
     *
     * @see GameServer
     */
    this.gameServer = gameServer;

    /**
     * ### Socket.sio
     *
     * Reference to the Socket.IO app of the game server
     *
     * @see GameServer
     */
    this.sio = this.gameServer.sio;

    /**
     * ### SocketIo.sioChannel
     *
     * The Socket.IO internal channel
     */
    this.sioChannel = null;

    /**
     * ### SocketIo.sidPrefix
     *
     * The prefix to be added to the id of every client connected to the socket
     *
     * Must have exactly 2 characters and be unique for every socket.
     */
    this.sidPrefix = this.gameServer.ADMIN_SERVER ? 'SA' : 'SP';

    /**
     * ### SocketIo.sidPrefixStrip
     *
     * The prefix to be stripped from sid for internal operations
     *
     * Sids are represented as SA/endpoint#sid, but in sockets.connected
     * they are saved as /#sid. Note: endpoints already start with a /.
     *
     * Notice: this depends on how Socket.io represets sid internally.
     * If they change the internal structure it needs to be adapted.
     *
     * @see SocketIo.disconnect
     */
    this.sidPrefixStrip = this.sidPrefix + this.gameServer.endpoint;

    /**
     * ### SocketIo.sidPrefixStripLen
     *
     * The length of the prefix to be stripped from sid for internal operations
     *
     * @see SocketIo.sidPrefixStrip
     * @see SocketIo.disconnect
     */
    this.sidPrefixStripLen = this.sidPrefixStrip.length;
}

// ## SocketIo methods

/**
 * ### SocketIo.attachListeners
 *
 * Activates the socket to accepts incoming connections
 */
SocketIo.prototype.attachListeners = function() {
    var that;
    that = this;
    this.sioChannel = this.sio.of(this.gameServer.endpoint).on('connection',
        function(socket) {
            var res, prefixedSid;
            var startingRoom, clientType;

//            console.log('hello!', socket.handshake.decoded_token.name);

            prefixedSid = that.sidPrefix + socket.id;

            if (that.gameServer.options.sioQuery && socket.handshake.query) {
                startingRoom = socket.handshake.query.startingRoom;
                clientType = socket.handshake.query.clientType;

                // TODO: check the implications of allowing setting ID here.
                // var clientId;
                // clientId = socket.handshake.query.id;

                // Cleanup clientType (sometimes browsers add quotes).
                if (clientType &&
                    ((clientType.charAt(0) === '"' &&
                     clientType.charAt(clientType.length-1) === '"') ||
                    (clientType.charAt(0) === "'" &&
                     clientType.charAt(clientType.length-1) === "'"))) {

                    clientType = clientType.substr(1,clientType.length -2);
                }
            }

            // Add information about the IP in the headers.
            // This might change in different versions of Socket.IO
            // socket.handshake.headers.address = socket.handshake.address;

            res = that.gameServer.onConnect(prefixedSid, that,
                                            socket.handshake, clientType,
                                            startingRoom);

            if (res) {
                socket.on('message', function(msgStr) {
                    var gameMsg;
                    gameMsg = that.gameServer.secureParse(msgStr);
                    if (!gameMsg) return;

                    // Anti-spoofing.
                    if (that.gameServer.antiSpoofing &&
                        gameMsg.sid !== socket.id) {

                        // We log it here because we skip onMessage.
                        that.gameServer.msgLogger.log(gameMsg, 'in', msgStr);
                        // Find out the id of the spoofer.
                        res = that.gameServer.registry.sio.get(msg.sid);
                        res = res ? res.id : '[id-not-found]';
                        that.gameServer.sysLogger.log('SocketIo socket ' +
                                                      'spoofed msg detected: ' +
                                                      gameMsg.id + ' from ' +
                                                      res);
                        return;
                    }
                    that.gameServer.onMessage(gameMsg, msgStr);
                });
                socket.on('disconnect', function() {
                    that.gameServer.onDisconnect(prefixedSid, socket);
                });
            }
        });

    this.sio.sockets.on("shutdown", that.gameServer.onShutdown);
};

/**
 * ### SocketIo.disconnect
 *
 * Disconnects the client registered under the specified socket id
 *
 * @param {string} sid The socket id of the client to disconnect
 */
SocketIo.prototype.disconnect = function(sid) {
    var socket, strippedSid;

    // Remove socket name from sid (2 chars + channel name).
    strippedSid = this.parseSid(sid);

    // Ste: was.
    //socket = this.sio.sockets.sockets[strippedSid];
    // Ste: was.
    // socket = this.sio.sockets.connected[strippedSid]; // full strip.
    socket = this.sioChannel.connected[sid.slice(2)];

    if (!socket) {
        throw new Error('SocketIo.disconnect: ' +
                        'socket not found for sid "' + sid + '". Stripped: ' +
                        strippedSid
                       );
    }

    socket.disconnect(true);
    // This is already triggered by the method above.
    // this.gameServer.onDisconnect(sid, socket);
};

/**
 * ### SocketIo.send
 *
 * Sends a game message to the client registered under the specified socket id
 *
 * The _to_ field of the game message is usually different from the _sid_ and
 * it is not used to locate the client.
 *
 * @param {GameMsg} gameMsg The game message
 * @param {string} sid The socket id of the receiver
 * @param {boolean} broadcast If TRUE, the message will not be sent to
 *   the client with sid = sid. Default: FALSE
 * @return {boolean} TRUE, on success
 */
SocketIo.prototype.send = function(gameMsg, sid, broadcast) {
    var msg, client, rel, strippedSid, clientSid;
    var sysLogger, msgLogger;

    if (sid === 'SERVER' || sid === null) {
        sysLogger.log('SocketIo.send: Trying to send msg to ' +
                      'nobody: ' + sid, 'error');
        return false;
    }

    broadcast = broadcast || false;

    sysLogger = this.gameServer.sysLogger;
    msgLogger = this.gameServer.msgLogger;

    if (sid === 'ALL' && broadcast) {
        sysLogger.log('SocketIo.send: Incompatible options: broadcast = true ' +
                      'and sid = ALL', 'error');
        return false;
    }

    // Cleanup msg, if necessary.
    if (gameMsg._to) {
        if (!gameMsg._originalTo) {
            sysLogger.log('SocketIo.send: _to field found, but no _originalTo ',
                          'error');
            return false;
        }
        gameMsg.to = gameMsg._orginalTo;
        delete gameMsg._to;
        delete gameMsg._originalTo;
    }

    if (gameMsg._sid) delete gameMsg._sid;
    if (gameMsg._channelName) delete gameMsg._channelName;
    if (gameMsg._roomName) delete gameMsg._roomName;

    rel = gameMsg.reliable;
    msg = gameMsg.stringify();

    // Send to ALL.
    if (sid === 'ALL') {
        if (rel) {
            this.sioChannel.json.send(msg);
        }
        else {
            this.sioChannel.volatile.json.send(msg);
        }
        sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to ALL');
        msgLogger.log(gameMsg, 'out-ALL');
    }

    // Either send to a specific client(1), or to ALL but a specific client(2).
    else {
        // Remove socket name from sid (exactly 2 characters, see parseSid).
        strippedSid = sid.slice(2);

        // (1)
        if (!broadcast) {

            // Ste: Now:
            client = this.sioChannel.connected[strippedSid];

            if (!client) {
                sysLogger.log('SocketIo.send: msg ' + gameMsg.toSMS()  +
                              ' not sent. Unexisting recipient sid: ' +
                              strippedSid, 'error');
                return false;
            }

            if (rel) {
                client.json.send(msg);
            }
            else {
                client.volatile.json.send(msg);
            }
            sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to sid ' +
                          strippedSid);
            msgLogger.log(gameMsg, 'out-ID');

        }
        // (2)
        else {

            for (clientSid in this.sioChannel.connected) {
                if (strippedSid === clientSid) continue;

                client = this.sioChannel.connected[clientSid];

                if (rel) {
                    client.json.send(msg);
                }
                else {
                    client.volatile.json.send(msg);
                }
                sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to sid ' +
                              clientSid);
                msgLogger.log(gameMsg, 'out-BROADCAST');
            }
        }
    }
    return true;
};

/**
 * ### SocketIo.parseSid
 *
 * Parses a socket id (sid) from nodeGame to Socket.io format
 *
 * This returns the format found in `this.sio.sockets.connected`.
 * In `sioChannel.connected` the format is sid.slice(2).
 *
 * @param {string} sid The sid to parse
 *
 * @return {string} The Socket.io-formatted sid
 */
SocketIo.prototype.parseSid = function(sid) {
    return '/' + sid.slice(this.sidPrefixStripLen);
};
<commit_msg>sio->sid<commit_after>/**
 * # SocketIo
 * Copyright(c) 2019 Stefano Balietti
 * MIT Licensed
 *
 * Handles network connections through Socket.IO
 */

"use strict";

// Global scope

module.exports = SocketIo;

var GameMsg = require('nodegame-client').GameMsg;

/**
 * ## SocketIo constructor
 *
 * Creates an instance of SocketIo
 *
 * @param {GameServer} server A GameServer instance
 *
 * @see GameServer
 */
function SocketIo(gameServer) {

    /**
     * ### SocketIo.name
     *
     * The name of the socket.
     */
    this.name = 'sio';

    /**
     * ### Socket.gameServer
     *
     * Reference to the game server in which the socket is created.
     *
     * @see GameServer
     */
    this.gameServer = gameServer;

    /**
     * ### Socket.sio
     *
     * Reference to the Socket.IO app of the game server
     *
     * @see GameServer
     */
    this.sio = this.gameServer.sio;

    /**
     * ### SocketIo.sioChannel
     *
     * The Socket.IO internal channel
     */
    this.sioChannel = null;

    /**
     * ### SocketIo.sidPrefix
     *
     * The prefix to be added to the id of every client connected to the socket
     *
     * Must have exactly 2 characters and be unique for every socket.
     */
    this.sidPrefix = this.gameServer.ADMIN_SERVER ? 'SA' : 'SP';

    /**
     * ### SocketIo.sidPrefixStrip
     *
     * The prefix to be stripped from sid for internal operations
     *
     * Sids are represented as SA/endpoint#sid, but in sockets.connected
     * they are saved as /#sid. Note: endpoints already start with a /.
     *
     * Notice: this depends on how Socket.io represets sid internally.
     * If they change the internal structure it needs to be adapted.
     *
     * @see SocketIo.disconnect
     */
    this.sidPrefixStrip = this.sidPrefix + this.gameServer.endpoint;

    /**
     * ### SocketIo.sidPrefixStripLen
     *
     * The length of the prefix to be stripped from sid for internal operations
     *
     * @see SocketIo.sidPrefixStrip
     * @see SocketIo.disconnect
     */
    this.sidPrefixStripLen = this.sidPrefixStrip.length;
}

// ## SocketIo methods

/**
 * ### SocketIo.attachListeners
 *
 * Activates the socket to accepts incoming connections
 */
SocketIo.prototype.attachListeners = function() {
    var that;
    that = this;
    this.sioChannel = this.sio.of(this.gameServer.endpoint).on('connection',
        function(socket) {
            var res, prefixedSid;
            var startingRoom, clientType;

//            console.log('hello!', socket.handshake.decoded_token.name);

            prefixedSid = that.sidPrefix + socket.id;

            if (that.gameServer.options.sioQuery && socket.handshake.query) {
                startingRoom = socket.handshake.query.startingRoom;
                clientType = socket.handshake.query.clientType;

                // TODO: check the implications of allowing setting ID here.
                // var clientId;
                // clientId = socket.handshake.query.id;

                // Cleanup clientType (sometimes browsers add quotes).
                if (clientType &&
                    ((clientType.charAt(0) === '"' &&
                     clientType.charAt(clientType.length-1) === '"') ||
                    (clientType.charAt(0) === "'" &&
                     clientType.charAt(clientType.length-1) === "'"))) {

                    clientType = clientType.substr(1,clientType.length -2);
                }
            }

            // Add information about the IP in the headers.
            // This might change in different versions of Socket.IO
            // socket.handshake.headers.address = socket.handshake.address;

            res = that.gameServer.onConnect(prefixedSid, that,
                                            socket.handshake, clientType,
                                            startingRoom);

            if (res) {
                socket.on('message', function(msgStr) {
                    var gameMsg;
                    gameMsg = that.gameServer.secureParse(msgStr);
                    if (!gameMsg) return;

                    // Anti-spoofing.
                    if (that.gameServer.antiSpoofing &&
                        gameMsg.sid !== socket.id) {

                        // We log it here because we skip onMessage.
                        that.gameServer.msgLogger.log(gameMsg, 'in', msgStr);
                        // Find out the id of the spoofer.
                        res = that.gameServer.registry.sid.get(msg.sid);
                        res = res ? res.id : '[id-not-found]';
                        that.gameServer.sysLogger.log('SocketIo socket ' +
                                                      'spoofed msg detected: ' +
                                                      gameMsg.id + ' from ' +
                                                      res);
                        return;
                    }
                    that.gameServer.onMessage(gameMsg, msgStr);
                });
                socket.on('disconnect', function() {
                    that.gameServer.onDisconnect(prefixedSid, socket);
                });
            }
        });

    this.sio.sockets.on("shutdown", that.gameServer.onShutdown);
};

/**
 * ### SocketIo.disconnect
 *
 * Disconnects the client registered under the specified socket id
 *
 * @param {string} sid The socket id of the client to disconnect
 */
SocketIo.prototype.disconnect = function(sid) {
    var socket, strippedSid;

    // Remove socket name from sid (2 chars + channel name).
    strippedSid = this.parseSid(sid);

    // Ste: was.
    //socket = this.sio.sockets.sockets[strippedSid];
    // Ste: was.
    // socket = this.sio.sockets.connected[strippedSid]; // full strip.
    socket = this.sioChannel.connected[sid.slice(2)];

    if (!socket) {
        throw new Error('SocketIo.disconnect: ' +
                        'socket not found for sid "' + sid + '". Stripped: ' +
                        strippedSid
                       );
    }

    socket.disconnect(true);
    // This is already triggered by the method above.
    // this.gameServer.onDisconnect(sid, socket);
};

/**
 * ### SocketIo.send
 *
 * Sends a game message to the client registered under the specified socket id
 *
 * The _to_ field of the game message is usually different from the _sid_ and
 * it is not used to locate the client.
 *
 * @param {GameMsg} gameMsg The game message
 * @param {string} sid The socket id of the receiver
 * @param {boolean} broadcast If TRUE, the message will not be sent to
 *   the client with sid = sid. Default: FALSE
 * @return {boolean} TRUE, on success
 */
SocketIo.prototype.send = function(gameMsg, sid, broadcast) {
    var msg, client, rel, strippedSid, clientSid;
    var sysLogger, msgLogger;

    if (sid === 'SERVER' || sid === null) {
        sysLogger.log('SocketIo.send: Trying to send msg to ' +
                      'nobody: ' + sid, 'error');
        return false;
    }

    broadcast = broadcast || false;

    sysLogger = this.gameServer.sysLogger;
    msgLogger = this.gameServer.msgLogger;

    if (sid === 'ALL' && broadcast) {
        sysLogger.log('SocketIo.send: Incompatible options: broadcast = true ' +
                      'and sid = ALL', 'error');
        return false;
    }

    // Cleanup msg, if necessary.
    if (gameMsg._to) {
        if (!gameMsg._originalTo) {
            sysLogger.log('SocketIo.send: _to field found, but no _originalTo ',
                          'error');
            return false;
        }
        gameMsg.to = gameMsg._orginalTo;
        delete gameMsg._to;
        delete gameMsg._originalTo;
    }

    if (gameMsg._sid) delete gameMsg._sid;
    if (gameMsg._channelName) delete gameMsg._channelName;
    if (gameMsg._roomName) delete gameMsg._roomName;

    rel = gameMsg.reliable;
    msg = gameMsg.stringify();

    // Send to ALL.
    if (sid === 'ALL') {
        if (rel) {
            this.sioChannel.json.send(msg);
        }
        else {
            this.sioChannel.volatile.json.send(msg);
        }
        sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to ALL');
        msgLogger.log(gameMsg, 'out-ALL');
    }

    // Either send to a specific client(1), or to ALL but a specific client(2).
    else {
        // Remove socket name from sid (exactly 2 characters, see parseSid).
        strippedSid = sid.slice(2);

        // (1)
        if (!broadcast) {

            // Ste: Now:
            client = this.sioChannel.connected[strippedSid];

            if (!client) {
                sysLogger.log('SocketIo.send: msg ' + gameMsg.toSMS()  +
                              ' not sent. Unexisting recipient sid: ' +
                              strippedSid, 'error');
                return false;
            }

            if (rel) {
                client.json.send(msg);
            }
            else {
                client.volatile.json.send(msg);
            }
            sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to sid ' +
                          strippedSid);
            msgLogger.log(gameMsg, 'out-ID');

        }
        // (2)
        else {

            for (clientSid in this.sioChannel.connected) {
                if (strippedSid === clientSid) continue;

                client = this.sioChannel.connected[clientSid];

                if (rel) {
                    client.json.send(msg);
                }
                else {
                    client.volatile.json.send(msg);
                }
                sysLogger.log('Msg ' + gameMsg.toSMS() + ' sent to sid ' +
                              clientSid);
                msgLogger.log(gameMsg, 'out-BROADCAST');
            }
        }
    }
    return true;
};

/**
 * ### SocketIo.parseSid
 *
 * Parses a socket id (sid) from nodeGame to Socket.io format
 *
 * This returns the format found in `this.sio.sockets.connected`.
 * In `sioChannel.connected` the format is sid.slice(2).
 *
 * @param {string} sid The sid to parse
 *
 * @return {string} The Socket.io-formatted sid
 */
SocketIo.prototype.parseSid = function(sid) {
    return '/' + sid.slice(this.sidPrefixStripLen);
};
<|endoftext|>


<commit_before>/**
 *  Backup
 *  ======
 *
 *  Using local stored information to provide a backup for reconnection.
 */

if ( LOCAL['player'] ) {

  var range = parseFloat( JSON.parse(LOCAL.player).time ) +
              config.reconnectConfig.backupDuration - Date.now();

  if ( range >= 0 ) extend( BACKUP, JSON.parse(LOCAL.player) );
}

delete LOCAL['player'];

/**
 *  Overwrites the data in the localStorage
 *
 *  TODO: instead of always serializing the whole instance, just update the increment
 */

function updateBackup() {

  LOCAL['player'] = JSON.stringify(PLAYER);
}

/**
 *  Uses the former data as initial values
 *
 *  TODO: check if account information (name) should also be stored
 *
 *  @param  {[type]} data [description]
 */

function restoreBackup ( player ) {

  if ( !config.reconnectConfig.restoreEnabled ) return;

  if ( BACKUP.id   ) player.id   = BACKUP.id;
  if ( BACKUP.time ) player.time = BACKUP.time;

  if ( !BACKUP.data ) return;

  var data = player.data,
      last = BACKUP.data, keys = getKeys(last);

  for ( var i = 0, l = keys.length; i < l; i++ ) data[ keys[i] ] = last[ keys[i] ];
}
<commit_msg>check for restoreBackup flag<commit_after>/**
 *  Backup
 *  ======
 *
 *  Using local stored information to provide a backup for reconnection.
 */

if ( LOCAL['player'] ) {

  var range = parseFloat( JSON.parse(LOCAL['player']).time ) +
              config.reconnectConfig.backupDuration - Date.now();

  if ( range >= 0 ) extend( BACKUP, JSON.parse(LOCAL['player']) );
}

delete LOCAL['player'];

/**
 *  Overwrites the data in the localStorage
 *
 *  TODO: instead of always serializing the whole instance, just update the increment
 */

function updateBackup() {

  if ( !config.reconnectConfig.restoreEnabled ) return;

  LOCAL['player'] = JSON.stringify(PLAYER);
}

/**
 *  Uses the former data as initial values
 *
 *  TODO: check if account information (name) should also be stored
 *
 *  @param  {[type]} data [description]
 */

function restoreBackup ( player ) {

  if ( !config.reconnectConfig.restoreEnabled ) return;

  if ( BACKUP.id   ) player.id   = BACKUP.id;
  if ( BACKUP.time ) player.time = BACKUP.time;

  if ( !BACKUP.data ) return;

  var data = player.data,
      last = BACKUP.data, keys = getKeys(last);

  for ( var i = 0, l = keys.length; i < l; i++ ) data[ keys[i] ] = last[ keys[i] ];
}
<|endoftext|>