<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";
(
/**
* @param {Window} window
* @param {undefined} undefined
*/
function (window, undefined) {
var AscBrowser = {
    userAgent : "",
    isIE : false,
    isMacOs : false,
    isSafariMacOs : false,
    isAppleDevices : false,
    isAndroid : false,
    isMobile : false,
	isMobileVersion : false,
    isGecko : false,
    isChrome : false,
    isOpera : false,
    isWebkit : false,
    isSafari : false,
    isArm : false,
    isMozilla : false,
	isRetina : false,
    isLinuxOS : false,
	retinaPixelRatio : 1
};

// user agent lower case
AscBrowser.userAgent = navigator.userAgent.toLowerCase();

// ie detect
AscBrowser.isIE =  (AscBrowser.userAgent.indexOf("msie") > -1 ||
                    AscBrowser.userAgent.indexOf("trident") > -1 ||
					AscBrowser.userAgent.indexOf("edge") > -1);

AscBrowser.isIeEdge = (AscBrowser.userAgent.indexOf("edge/") > -1);

AscBrowser.isIE9 =  (AscBrowser.userAgent.indexOf("msie9") > -1 || AscBrowser.userAgent.indexOf("msie 9") > -1);
AscBrowser.isIE10 =  (AscBrowser.userAgent.indexOf("msie10") > -1 || AscBrowser.userAgent.indexOf("msie 10") > -1);

// macOs detect
AscBrowser.isMacOs = (AscBrowser.userAgent.indexOf('mac') > -1);

// chrome detect
AscBrowser.isChrome = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("chrome") > -1);

// safari detect
AscBrowser.isSafari = !AscBrowser.isIE && !AscBrowser.isChrome && (AscBrowser.userAgent.indexOf("safari") > -1);

// macOs safari detect
AscBrowser.isSafariMacOs = (AscBrowser.isSafari && AscBrowser.isMacOs);

// apple devices detect
AscBrowser.isAppleDevices = (AscBrowser.userAgent.indexOf("ipad") > -1 ||
                             AscBrowser.userAgent.indexOf("iphone") > -1 ||
                             AscBrowser.userAgent.indexOf("ipod") > -1);

// android devices detect
AscBrowser.isAndroid = (AscBrowser.userAgent.indexOf("android") > -1);

// mobile detect
AscBrowser.isMobile = /android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent || navigator.vendor || window.opera);

// gecko detect
AscBrowser.isGecko = (AscBrowser.userAgent.indexOf("gecko/") > -1);

// opera detect
AscBrowser.isOpera = (!!window.opera || AscBrowser.userAgent.indexOf("opr/") > -1);

// webkit detect
AscBrowser.isWebkit = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("webkit") > -1);

// arm detect
AscBrowser.isArm = (AscBrowser.userAgent.indexOf("arm") > -1);

AscBrowser.isMozilla = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("firefox") > -1);

AscBrowser.isLinuxOS = (AscBrowser.userAgent.indexOf(" linux ") > -1);

AscBrowser.zoom = 1;

AscBrowser.checkZoom = function()
{
    if (AscBrowser.isAndroid)
	{
		AscBrowser.isRetina = (Math.abs(2 - (window.devicePixelRatio / AscBrowser.zoom)) < 0.01);
		return;
	}

	AscBrowser.zoom = 1.0;
	AscBrowser.isRetina = false;
	AscBrowser.retinaPixelRatio = 1;

    if (AscBrowser.isChrome && !AscBrowser.isOpera && !AscBrowser.isMobile && document && document.firstElementChild && document.body)
    {
        if (false)
		{
			// этот код - рабочий, но только если этот ифрейм открыт на весь размер браузера
            // (window.outerWidth и window.innerWidth зависимы)
			if (window.innerWidth > 300)
				AscBrowser.zoom = window.outerWidth / window.innerWidth;

			if (Math.abs(AscBrowser.zoom - 1) < 0.1)
				AscBrowser.zoom = 1;

			AscBrowser.zoom = window.outerWidth / window.innerWidth;

			var _devicePixelRatio = window.devicePixelRatio / AscBrowser.zoom;

			// device pixel ratio: кратно 0.5
			_devicePixelRatio = (5 * (((2.5 + 10 * _devicePixelRatio) / 5) >> 0)) / 10;

			AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
			if (2 == _devicePixelRatio)
				AscBrowser.isRetina = true;

			// chrome 54.x: zoom = "reset" - clear retina zoom (windows)
			//document.firstElementChild.style.zoom = "reset";
			document.firstElementChild.style.zoom = 1.0 / AscBrowser.zoom;
		}
		else
        {
            // делаем простую проверку
            // считаем: 0 < window.devicePixelRatio < 2 => _devicePixelRatio = 1; zoom = window.devicePixelRatio / _devicePixelRatio;
			// считаем: window.devicePixelRatio >= 2 => _devicePixelRatio = 2; zoom = window.devicePixelRatio / _devicePixelRatio;
            if (window.devicePixelRatio > 0.1)
			{
				if (window.devicePixelRatio < 1.99)
				{
					var _devicePixelRatio = 1;
					AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
				}
				else
				{
					var _devicePixelRatio = 2;
					AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
					AscBrowser.isRetina = true;
				}
				// chrome 54.x: zoom = "reset" - clear retina zoom (windows)
				//document.firstElementChild.style.zoom = "reset";
				document.firstElementChild.style.zoom = 1.0 / AscBrowser.zoom;
			}
			else
				document.firstElementChild.style.zoom = "normal";
        }

        if (AscBrowser.isRetina)
        	AscBrowser.retinaPixelRatio = 2;
    }
    else
    {
		AscBrowser.isRetina = (Math.abs(2 - (window.devicePixelRatio / AscBrowser.zoom)) < 0.01);
		if (AscBrowser.isRetina)
			AscBrowser.retinaPixelRatio = 2;

		if (AscBrowser.isMobile)
		{
			AscBrowser.isRetina = (window.devicePixelRatio >= 1.9);
			AscBrowser.retinaPixelRatio = window.devicePixelRatio;
		}
    }
};

AscBrowser.checkZoom();

    //--------------------------------------------------------export----------------------------------------------------
    window['AscCommon'] = window['AscCommon'] || {};
    window['AscCommon'].AscBrowser = AscBrowser; // ToDo убрать window['AscBrowser']
})(window);
<commit_msg>android retina bug<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";
(
/**
* @param {Window} window
* @param {undefined} undefined
*/
function (window, undefined) {
var AscBrowser = {
    userAgent : "",
    isIE : false,
    isMacOs : false,
    isSafariMacOs : false,
    isAppleDevices : false,
    isAndroid : false,
    isMobile : false,
	isMobileVersion : false,
    isGecko : false,
    isChrome : false,
    isOpera : false,
    isWebkit : false,
    isSafari : false,
    isArm : false,
    isMozilla : false,
	isRetina : false,
    isLinuxOS : false,
	retinaPixelRatio : 1
};

// user agent lower case
AscBrowser.userAgent = navigator.userAgent.toLowerCase();

// ie detect
AscBrowser.isIE =  (AscBrowser.userAgent.indexOf("msie") > -1 ||
                    AscBrowser.userAgent.indexOf("trident") > -1 ||
					AscBrowser.userAgent.indexOf("edge") > -1);

AscBrowser.isIeEdge = (AscBrowser.userAgent.indexOf("edge/") > -1);

AscBrowser.isIE9 =  (AscBrowser.userAgent.indexOf("msie9") > -1 || AscBrowser.userAgent.indexOf("msie 9") > -1);
AscBrowser.isIE10 =  (AscBrowser.userAgent.indexOf("msie10") > -1 || AscBrowser.userAgent.indexOf("msie 10") > -1);

// macOs detect
AscBrowser.isMacOs = (AscBrowser.userAgent.indexOf('mac') > -1);

// chrome detect
AscBrowser.isChrome = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("chrome") > -1);

// safari detect
AscBrowser.isSafari = !AscBrowser.isIE && !AscBrowser.isChrome && (AscBrowser.userAgent.indexOf("safari") > -1);

// macOs safari detect
AscBrowser.isSafariMacOs = (AscBrowser.isSafari && AscBrowser.isMacOs);

// apple devices detect
AscBrowser.isAppleDevices = (AscBrowser.userAgent.indexOf("ipad") > -1 ||
                             AscBrowser.userAgent.indexOf("iphone") > -1 ||
                             AscBrowser.userAgent.indexOf("ipod") > -1);

// android devices detect
AscBrowser.isAndroid = (AscBrowser.userAgent.indexOf("android") > -1);

// mobile detect
AscBrowser.isMobile = /android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent || navigator.vendor || window.opera);

// gecko detect
AscBrowser.isGecko = (AscBrowser.userAgent.indexOf("gecko/") > -1);

// opera detect
AscBrowser.isOpera = (!!window.opera || AscBrowser.userAgent.indexOf("opr/") > -1);

// webkit detect
AscBrowser.isWebkit = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("webkit") > -1);

// arm detect
AscBrowser.isArm = (AscBrowser.userAgent.indexOf("arm") > -1);

AscBrowser.isMozilla = !AscBrowser.isIE && (AscBrowser.userAgent.indexOf("firefox") > -1);

AscBrowser.isLinuxOS = (AscBrowser.userAgent.indexOf(" linux ") > -1);

AscBrowser.zoom = 1;

AscBrowser.checkZoom = function()
{
    if (AscBrowser.isAndroid)
	{
		AscBrowser.isRetina = (window.devicePixelRatio >= 1.9);
		AscBrowser.retinaPixelRatio = window.devicePixelRatio;
		return;
	}

	AscBrowser.zoom = 1.0;
	AscBrowser.isRetina = false;
	AscBrowser.retinaPixelRatio = 1;

    if (AscBrowser.isChrome && !AscBrowser.isOpera && !AscBrowser.isMobile && document && document.firstElementChild && document.body)
    {
        if (false)
		{
			// этот код - рабочий, но только если этот ифрейм открыт на весь размер браузера
            // (window.outerWidth и window.innerWidth зависимы)
			if (window.innerWidth > 300)
				AscBrowser.zoom = window.outerWidth / window.innerWidth;

			if (Math.abs(AscBrowser.zoom - 1) < 0.1)
				AscBrowser.zoom = 1;

			AscBrowser.zoom = window.outerWidth / window.innerWidth;

			var _devicePixelRatio = window.devicePixelRatio / AscBrowser.zoom;

			// device pixel ratio: кратно 0.5
			_devicePixelRatio = (5 * (((2.5 + 10 * _devicePixelRatio) / 5) >> 0)) / 10;

			AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
			if (2 == _devicePixelRatio)
				AscBrowser.isRetina = true;

			// chrome 54.x: zoom = "reset" - clear retina zoom (windows)
			//document.firstElementChild.style.zoom = "reset";
			document.firstElementChild.style.zoom = 1.0 / AscBrowser.zoom;
		}
		else
        {
            // делаем простую проверку
            // считаем: 0 < window.devicePixelRatio < 2 => _devicePixelRatio = 1; zoom = window.devicePixelRatio / _devicePixelRatio;
			// считаем: window.devicePixelRatio >= 2 => _devicePixelRatio = 2; zoom = window.devicePixelRatio / _devicePixelRatio;
            if (window.devicePixelRatio > 0.1)
			{
				if (window.devicePixelRatio < 1.99)
				{
					var _devicePixelRatio = 1;
					AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
				}
				else
				{
					var _devicePixelRatio = 2;
					AscBrowser.zoom = window.devicePixelRatio / _devicePixelRatio;
					AscBrowser.isRetina = true;
				}
				// chrome 54.x: zoom = "reset" - clear retina zoom (windows)
				//document.firstElementChild.style.zoom = "reset";
				document.firstElementChild.style.zoom = 1.0 / AscBrowser.zoom;
			}
			else
				document.firstElementChild.style.zoom = "normal";
        }

        if (AscBrowser.isRetina)
        	AscBrowser.retinaPixelRatio = 2;
    }
    else
    {
		AscBrowser.isRetina = (Math.abs(2 - (window.devicePixelRatio / AscBrowser.zoom)) < 0.01);
		if (AscBrowser.isRetina)
			AscBrowser.retinaPixelRatio = 2;

		if (AscBrowser.isMobile)
		{
			AscBrowser.isRetina = (window.devicePixelRatio >= 1.9);
			AscBrowser.retinaPixelRatio = window.devicePixelRatio;
		}
    }
};

AscBrowser.checkZoom();

    //--------------------------------------------------------export----------------------------------------------------
    window['AscCommon'] = window['AscCommon'] || {};
    window['AscCommon'].AscBrowser = AscBrowser; // ToDo убрать window['AscBrowser']
})(window);
<|endoftext|>


<commit_before>var Opbeat = require('./opbeat')
var TraceBuffer = require('./instrumentation/traceBuffer')
var transactionStore = require('./instrumentation/transactionStore')

var utils = require('./instrumentation/utils')
var logger = require('./lib/logger')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.$get = [
    function () {
      return {
        getConfig: function config () {
          return Opbeat.config()
        },
        captureException: function captureException (exception, cause) {
          Opbeat.captureException(exception, cause)
        },

        setUserContext: function setUser (user) {
          Opbeat.setUserContext(user)
        },

        setExtraContext: function setExtraContext (data) {
          Opbeat.setExtraContext(data)
        }
      }
    }
  ]
}

function $opbeatErrorProvider ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

function $opbeatInstrumentationProvider ($provide, $opbeat) {
  var config = $opbeat.$get[0]().getConfig()
  var transactionOptions = {
    config: config
  }

  // Before controller intialize transcation
  var traceBuffer = new TraceBuffer('beforeControllerTransaction', transactionOptions)

  // Route controller Instrumentation
  $provide.decorator('$controller', ['$delegate', '$location', '$rootScope', '$injector', function ($delegate, $location, $rootScope, $injector) {
    transactionStore.init($injector)

    var onRouteChange = function (e, current) {
      var routeControllerTarget = current.controller
      logger.log('opbeat.decorator.controller.onRouteChange')

      var transaction = Opbeat.startTransaction('app.angular.controller.' + routeControllerTarget, 'transaction', transactionOptions)
      transaction.metadata.controllerName = routeControllerTarget

      // Update transaction store
      transactionStore.pushToUrl($location.absUrl(), transaction)

      // Update transaction reference in traceBuffer
      traceBuffer.setTransactionReference(transaction)

      // Lock traceBuffer, as we only want to migrate the initial traces to the first transaction
      traceBuffer.lock()
    }

    $rootScope.$on('$routeChangeStart', onRouteChange) // ng-router
    $rootScope.$on('$stateChangeSuccess', onRouteChange) // ui-router

    return function () {
      logger.log('opbeat.decorator.controller.ctor')

      var args = Array.prototype.slice.call(arguments)

      var url = $injector.get('$location').absUrl()
      var transaction = transactionStore.getRecentByUrl(url)

      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var controllerName = controllerInfo.name
      var controllerScope = controllerInfo.scope
      var isRouteController = controllerName && transaction && transaction.metadata.controllerName === controllerName

      var result = $delegate.apply(this, args)

      var onViewFinished = function(argument) {
        logger.log('opbeat.angular.controller.$onViewFinished')

        transactionStore.getAllByUrl(url).forEach(function (trans) {
          transaction.end()
        })
        transactionStore.clearByUrl(url)
      }

      var onScopeDestroyed = function() {
        logger.log('opbeat.angular.controller.destroy')
      }

      if (isRouteController && controllerScope) {
        logger.log('opbeat.angular.controller', controllerName)
        controllerScope.$on('$destroy', onScopeDestroyed)
        controllerScope.$on('$ionicView.enter', onViewFinished);
        controllerScope.$on('$viewContentLoaded', onViewFinished)
      }

      logger.log('opbeat.decorator.controller.end')
      return result
    }
  }])

  $provide.decorator('$controller', ['$delegate', '$rootScope', '$rootElement', function ($delegate, $rootScope, $rootElement) {
    $rootScope._opbeatHasInstrumentedFactories = false
    $rootScope._opbeatHasInstrumentedDirectives = false

    var directivesInstrumentation = require('./instrumentation/angular/directives')($provide, traceBuffer)

    // Factory instrumentation
    if (!$rootScope._opbeatHasInstrumentedFactories) {
      var factories = utils.resolveAngularDependenciesByType($rootElement, 'factory')
      require('./instrumentation/angular/factories')($provide, traceBuffer).instrumentAll(factories)
      $rootScope._opbeatHasInstrumentedFactories = true
    }

    if (!$rootScope._opbeatHasInstrumentedDirectives) {
      // Custom directive instrumentation
      var directives = utils.resolveAngularDependenciesByType($rootElement, 'directive')
      directivesInstrumentation.instrumentAll(directives)
      $rootScope._opbeatHasInstrumentedDirectives = true

      // Core directives instrumentation
      directivesInstrumentation.instrumentCore()
    }

    return $delegate
  }])

  // Angular Core Instrumentation
  require('./instrumentation/angular/cacheFactory')($provide, traceBuffer)
  require('./instrumentation/angular/compile')($provide, traceBuffer)
  require('./instrumentation/angular/controller')($provide, traceBuffer)
  require('./instrumentation/angular/http')($provide, traceBuffer)
  require('./instrumentation/angular/httpBackend')($provide, traceBuffer)
  require('./instrumentation/angular/resource')($provide, traceBuffer)
  require('./instrumentation/angular/templateRequest')($provide, traceBuffer)
}

window.angular.module('ngOpbeat', [])
  .provider('$opbeat', ngOpbeatProvider)
  .config(['$provide', '$opbeatProvider', $opbeatErrorProvider])
  .config(['$provide', '$opbeatProvider', $opbeatInstrumentationProvider])

window.angular.module('angular-opbeat', ['ngOpbeat'])
<commit_msg>Support nested views from UI-Router routing - Only start transaction for the first view <commit_after>var Opbeat = require('./opbeat')
var TraceBuffer = require('./instrumentation/traceBuffer')
var transactionStore = require('./instrumentation/transactionStore')

var utils = require('./instrumentation/utils')
var logger = require('./lib/logger')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.$get = [
    function () {
      return {
        getConfig: function config () {
          return Opbeat.config()
        },
        captureException: function captureException (exception, cause) {
          Opbeat.captureException(exception, cause)
        },

        setUserContext: function setUser (user) {
          Opbeat.setUserContext(user)
        },

        setExtraContext: function setExtraContext (data) {
          Opbeat.setExtraContext(data)
        }
      }
    }
  ]
}

function $opbeatErrorProvider ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

function $opbeatInstrumentationProvider ($provide, $opbeat) {
  var config = $opbeat.$get[0]().getConfig()
  var transactionOptions = {
    config: config
  }

  // Before controller intialize transcation
  var traceBuffer = new TraceBuffer('beforeControllerTransaction', transactionOptions)

  // Route controller Instrumentation
  $provide.decorator('$controller', ['$delegate', '$location', '$rootScope', '$injector', function ($delegate, $location, $rootScope, $injector) {
    transactionStore.init($injector)

    var onRouteChange = function (e, current) {

      logger.log('opbeat.decorator.controller.onRouteChange')

      var routeControllerName
      var routeController

      // Detect controller
      if(current.controller) {
        routeController = current.controller
      } else if(current.views) {
        var keys = Object.keys(current.views)
        if(keys) {
          routeController = current.views[keys[0]].controller
        }
      }

      // Extract controller name
      if(typeof(routeController) === 'string') {
        routeControllerName = routeController
      }

      if(!routeControllerName) {
        logger.log('%c opbeat.decorator.controller.onRouteChange.error.routeControllerName.missing', 'background-color: #ffff00', current)
        return
      }

      var transaction = Opbeat.startTransaction('app.angular.controller.' + routeControllerName, 'transaction', transactionOptions)
      transaction.metadata.controllerName = routeControllerName

      // Update transaction store
      transactionStore.pushToUrl($location.absUrl(), transaction)

      // Update transaction reference in traceBuffer
      traceBuffer.setTransactionReference(transaction)

      // Lock traceBuffer, as we only want to migrate the initial traces to the first transaction
      traceBuffer.lock()
    }

    $rootScope.$on('$routeChangeStart', onRouteChange) // ng-router
    $rootScope.$on('$stateChangeSuccess', onRouteChange) // ui-router

    return function () {
      logger.log('opbeat.decorator.controller.ctor')

      var args = Array.prototype.slice.call(arguments)

      var url = $injector.get('$location').absUrl()
      var transaction = transactionStore.getRecentByUrl(url)

      var controllerInfo = utils.getControllerInfoFromArgs(args)
      var controllerName = controllerInfo.name
      var controllerScope = controllerInfo.scope
      var isRouteController = controllerName && transaction && transaction.metadata.controllerName === controllerName

      var result = $delegate.apply(this, args)

      var onViewFinished = function(argument) {
        logger.log('opbeat.angular.controller.$onViewFinished')

        transactionStore.getAllByUrl(url).forEach(function (trans) {
          transaction.end()
        })
        transactionStore.clearByUrl(url)
      }

      var onScopeDestroyed = function() {
        logger.log('opbeat.angular.controller.destroy')
      }

      if (isRouteController && controllerScope) {
        logger.log('opbeat.angular.controller', controllerName)
        controllerScope.$on('$destroy', onScopeDestroyed)
        controllerScope.$on('$ionicView.enter', onViewFinished);
        controllerScope.$on('$viewContentLoaded', onViewFinished)
      }

      logger.log('opbeat.decorator.controller.end')
      return result
    }
  }])

  $provide.decorator('$controller', ['$delegate', '$rootScope', '$rootElement', function ($delegate, $rootScope, $rootElement) {
    $rootScope._opbeatHasInstrumentedFactories = false
    $rootScope._opbeatHasInstrumentedDirectives = false

    var directivesInstrumentation = require('./instrumentation/angular/directives')($provide, traceBuffer)

    // Factory instrumentation
    if (!$rootScope._opbeatHasInstrumentedFactories) {
      var factories = utils.resolveAngularDependenciesByType($rootElement, 'factory')
      require('./instrumentation/angular/factories')($provide, traceBuffer).instrumentAll(factories)
      $rootScope._opbeatHasInstrumentedFactories = true
    }

    if (!$rootScope._opbeatHasInstrumentedDirectives) {
      // Custom directive instrumentation
      var directives = utils.resolveAngularDependenciesByType($rootElement, 'directive')
      directivesInstrumentation.instrumentAll(directives)
      $rootScope._opbeatHasInstrumentedDirectives = true

      // Core directives instrumentation
      directivesInstrumentation.instrumentCore()
    }

    return $delegate
  }])

  // Angular Core Instrumentation
  require('./instrumentation/angular/cacheFactory')($provide, traceBuffer)
  require('./instrumentation/angular/compile')($provide, traceBuffer)
  require('./instrumentation/angular/controller')($provide, traceBuffer)
  require('./instrumentation/angular/http')($provide, traceBuffer)
  require('./instrumentation/angular/httpBackend')($provide, traceBuffer)
  require('./instrumentation/angular/resource')($provide, traceBuffer)
  require('./instrumentation/angular/templateRequest')($provide, traceBuffer)
}

window.angular.module('ngOpbeat', [])
  .provider('$opbeat', ngOpbeatProvider)
  .config(['$provide', '$opbeatProvider', $opbeatErrorProvider])
  .config(['$provide', '$opbeatProvider', $opbeatInstrumentationProvider])

window.angular.module('angular-opbeat', ['ngOpbeat'])
<|endoftext|>


<commit_before>'use strict'
const exphbs = require( 'express-handlebars' )
const passport = require( 'passport' )
const redis = require( 'redis' )
const ExpressSession = require( 'express-session' )
const RedisStore = require( 'connect-redis' )( ExpressSession )

module.exports = function ( app ) {

  app.engine( '.hbs', exphbs( { extname: '.hbs' } ) )
  app.set( 'view engine', '.hbs' )

  passport.serializeUser( ( user, done ) => done( null, user ) )
  passport.deserializeUser( ( user, done ) => done( null, user ) )

  let redirectUrls = process.env.REDIRECT_URLS.split( ',' ).filter( r => r !== '' ).map( u => new URL( u ) )
  redirectUrls.push( new URL( process.env.CANONICAL_URL ) )

  //
  // Common middleware routes for authentication
  //
  let sessionMiddleware = ExpressSession( {
    store: new RedisStore( { client: redis.createClient( process.env.REDIS_URL ) } ),
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    expires: new Date( Date.now( ) + 60000 * 3 ) // three minute expiration (not needed)
  } )

  let redirectCheck = ( req, res, next ) => {
    // TODO: pass through whitelist
    if ( req.query.redirectUrl ) {
      let url = null

      try {
        url = new URL( req.query.redirectUrl )
      } catch ( err ) {
        req.session.errorMessage = `Invalid redirect url: <b>${req.query.redirectUrl}</b>`
        return res.redirect( '/signin/error' )
      }

      let ind = redirectUrls.findIndex( u => u.host === url.host )
      if ( ind === -1 ) {
        req.session.errorMessage = `The redirect url (<code>${req.query.redirectUrl}</code>) is not whitelisted on this server (${process.env.SERVER_NAME}). <hr> <small>Please contact your server administrator.</small>`
        return res.redirect( '/signin/error' )
      }

      req.session.redirectUrl = req.query.redirectUrl
    }
    next( )
  }

  let handleLogin = ( req, res ) => {
    // TODO: also check for potential errors
    // TODO: redirect if redirect is present, or display a simple page otherwise
    // NOTE: By this point in time, the req.user var should be the speckle user.

    let token = Buffer.from( req.user.token ).toString( 'base64' )
    let server = Buffer.from( process.env.CANONICAL_URL ).toString( 'base64' )
    let fullConnectionString = `${token}/${server}`

    if ( req.session.redirectUrl ) {
      return res.redirect( `${req.session.redirectUrl}?token=${token}&serverUrl=${server}` )
    }

    res.render( 'postLogin', {
      user: req.user,
      connectionString: fullConnectionString
    } )
  }

  // TODO: automate these reading from disk
  // Where we collect the strategies in one place, and send them to the frontend
  let strategies = [
    require( './local' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './auth0' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './azure-ad' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './github' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
  ]

  // Main authentication entry page
  app.get( '/signin',
    sessionMiddleware,
    redirectCheck,
    ( req, res ) => {
      req.session.redirectUrl = req.session.redirectUrl || req.query.redirectUrl
      res.render( 'signin', {
        strategies: strategies,
        redirectUrl: req.session.redirectUrl,
        error: req.query.err
      } )
    } )

  app.get( '/signin/error', sessionMiddleware, ( req, res ) => {

    res.render( 'error', {
      errorMessage: req.session.errorMessage || "Oups. Something went wrong."
    } )

    req.session.errorMessage = ''
  } )
}
<commit_msg>feat(authentication): checks protocol (http vs https) against env flag regarding insecure redirects<commit_after>'use strict'
const exphbs = require( 'express-handlebars' )
const passport = require( 'passport' )
const redis = require( 'redis' )
const ExpressSession = require( 'express-session' )
const RedisStore = require( 'connect-redis' )( ExpressSession )

module.exports = function ( app ) {

  app.engine( '.hbs', exphbs( { extname: '.hbs' } ) )
  app.set( 'view engine', '.hbs' )

  passport.serializeUser( ( user, done ) => done( null, user ) )
  passport.deserializeUser( ( user, done ) => done( null, user ) )

  let redirectUrls = process.env.REDIRECT_URLS.split( ',' ).filter( r => r !== '' ).map( u => new URL( u ) )
  redirectUrls.push( new URL( process.env.CANONICAL_URL ) )

  //
  // Common middleware routes for authentication
  //
  let sessionMiddleware = ExpressSession( {
    store: new RedisStore( { client: redis.createClient( process.env.REDIS_URL ) } ),
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    expires: new Date( Date.now( ) + 60000 * 3 ) // three minute expiration (not needed)
  } )

  let redirectCheck = ( req, res, next ) => {
    // TODO: pass through whitelist
    if ( req.query.redirectUrl ) {
      let url = null

      try {
        url = new URL( req.query.redirectUrl )
      } catch ( err ) {
        req.session.errorMessage = `Invalid redirect url: <b>${req.query.redirectUrl}</b>`
        return res.redirect( '/signin/error' )
      }

      let ind = redirectUrls.findIndex( u => u.host === url.host )

      if ( ind === -1 ) {
        req.session.errorMessage = `The redirect url (<code>${req.query.redirectUrl}</code>) is not whitelisted on this server (${process.env.SERVER_NAME}). <hr> <small>Please contact your server administrator.</small>`
        return res.redirect( '/signin/error' )
      }

      if( url.protocol === 'http:' && process.env.ALLOW_INSECURE_REDIRECTS === 'false' ) {
        req.session.errorMessage = `Insecure urls (non-http<b>s</b>) are not allowed as redirects. <hr> <small>Please contact your server administrator.</small>`
        return res.redirect( '/signin/error' )
      }

      req.session.redirectUrl = req.query.redirectUrl
    } else {
      delete req.session.redirectUrl
    }
    next( )
  }

  let handleLogin = ( req, res ) => {
    // TODO: also check for potential errors
    // TODO: redirect if redirect is present, or display a simple page otherwise
    // NOTE: By this point in time, the req.user var should be the speckle user.

    let token = Buffer.from( req.user.token ).toString( 'base64' )
    let server = Buffer.from( process.env.CANONICAL_URL ).toString( 'base64' )
    let fullConnectionString = `${token}/${server}`

    if ( req.session.redirectUrl ) {
      return res.redirect( `${req.session.redirectUrl}?token=${token}&serverUrl=${server}` )
    }

    res.render( 'postLogin', {
      user: req.user,
      connectionString: fullConnectionString
    } )
  }

  // TODO: automate these reading from disk
  // Where we collect the strategies in one place, and send them to the frontend
  let strategies = [
    require( './local' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './auth0' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './azure-ad' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
    require( './github' ).init( app, sessionMiddleware, redirectCheck, handleLogin ),
  ]

  // Main authentication entry page
  app.get( '/signin',
    sessionMiddleware,
    redirectCheck,
    ( req, res ) => {
      req.session.redirectUrl = req.session.redirectUrl || req.query.redirectUrl
      res.render( 'signin', {
        strategies: strategies,
        redirectUrl: req.session.redirectUrl,
        error: req.query.err
      } )
    } )

  app.get( '/signin/error', sessionMiddleware, ( req, res ) => {

    res.render( 'error', {
      errorMessage: req.session.errorMessage || "Oups. Something went wrong."
    } )

    req.session.errorMessage = ''
  } )
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var VisView = require('../vis/vis-view');
var VisModel = require('../vis/vis');
var Loader = require('../core/loader');
var VizJSON = require('./vizjson');
var SettingsModel = require('../vis/ui-settings');

var DEFAULT_OPTIONS = {
  tiles_loader: true,
  loaderControl: true,
  infowindow: true,
  tooltip: true,
  logo: true,
  show_empty_infowindow_fields: false
};

var createVis = function (el, vizjson, options) {
  if (typeof el === 'string') {
    el = document.getElementById(el);
  }
  if (!el) {
    throw new TypeError('a valid DOM element or selector must be provided');
  }
  if (!vizjson) {
    throw new TypeError('a vizjson URL or object must be provided');
  }

  options = _.defaults(options || {}, DEFAULT_OPTIONS);

  var visModel = new VisModel();

  if (typeof vizjson === 'string') {
    var url = vizjson;
    Loader.get(url, function (vizjson) {
      if (vizjson) {
        loadVizJSON(el, visModel, vizjson, options);
      } else {
        throw new Error('error fetching viz.json file');
      }
    });
  } else {
    loadVizJSON(el, visModel, vizjson, options);
  }

  return visModel;
};

var loadVizJSON = function (el, visModel, vizjsonData, options) {
  var vizjson = new VizJSON(vizjsonData);
  applyOptionsToVizJSON(vizjson, options);

  var isProtocolHTTPs = window && window.location.protocol && window.location.protocol === 'https:';

  var showLegends = true;
  if (_.isBoolean(options.legends)) {
    showLegends = options.legends;
  } else if (vizjson.embed_options && _.isBoolean(vizjson.embed_options.legends)) {
    showLegends = vizjson.embed_options.legends;
  }

  var showLayerSelector = true;
  if (_.isBoolean(options.layer_selector)) {
    showLayerSelector = options.layer_selector;
  } else if (vizjson.embed_options && _.isBoolean(vizjson.embed_options.layer_selector)) {
    showLayerSelector = vizjson.embed_options.layer_selector;
  }

  var settingsModel = new SettingsModel({
    showLegends: showLegends,
    showLayerSelector: showLayerSelector
  });

  visModel.set({
    title: options.title || vizjson.title,
    description: options.description || vizjson.description,
    apiKey: options.apiKey,
    authToken: options.authToken,
    showEmptyInfowindowFields: options.show_empty_infowindow_fields === true,
    https: isProtocolHTTPs || options.https === true || vizjson.https === true
  });

  visModel.settings = settingsModel;

  new VisView({ // eslint-disable-line
    el: el,
    model: visModel,
    settingsModel: settingsModel
  });

  visModel.load(vizjson);

  if (!options.skipMapInstantiation) {
    visModel.instantiateMap();
  }
};

var applyOptionsToVizJSON = function (vizjson, options) {
  vizjson.scrollwheel = options.scrollwheel || vizjson.scrollwheel;

  if (!options.tiles_loader || !options.loaderControl) {
    vizjson.removeLoaderOverlay();
  }

  if (options.searchControl === true) {
    vizjson.addSearchOverlay();
  } else if (options.searchControl === false) {
    vizjson.removeSearchOverlay();
  }

  if ((options.title && vizjson.title) || (options.description && vizjson.description)) {
    vizjson.addHeaderOverlay(options.title, options.description, options.shareable);
  }

  if (options.layer_selector) {
    vizjson.addLayerSelectorOverlay();
  }

  if (options.zoomControl !== undefined && !options.zoomControl) {
    vizjson.removeZoomOverlay();
  }

  if (options.logo === false) {
    vizjson.removeLogoOverlay();
  }

  // if bounds are present zoom and center will not taken into account
  var zoom = parseInt(options.zoom, 10);
  if (!isNaN(zoom)) {
    vizjson.setZoom(zoom);
  }

  // Center coordinates?
  var center_lat = parseFloat(options.center_lat);
  var center_lon = parseFloat(options.center_lon);
  if (!isNaN(center_lat) && !isNaN(center_lon)) {
    vizjson.setCenter([center_lat, center_lon]);
  }

  // Center object
  if (options.center !== undefined) {
    vizjson.setCenter(options.center);
  }

  // Bounds?
  var sw_lat = parseFloat(options.sw_lat);
  var sw_lon = parseFloat(options.sw_lon);
  var ne_lat = parseFloat(options.ne_lat);
  var ne_lon = parseFloat(options.ne_lon);

  if (!isNaN(sw_lat) && !isNaN(sw_lon) && !isNaN(ne_lat) && !isNaN(ne_lon)) {
    vizjson.setBounds([
      [ sw_lat, sw_lon ],
      [ ne_lat, ne_lon ]
    ]);
  }

  if (options.gmaps_base_type) {
    vizjson.enforceGMapsBaseLayer(options.gmaps_base_type, options.gmaps_style);
  }
};

module.exports = createVis;
<commit_msg>Fixed settings options initialisation.<commit_after>var _ = require('underscore');
var VisView = require('../vis/vis-view');
var VisModel = require('../vis/vis');
var Loader = require('../core/loader');
var VizJSON = require('./vizjson');
var SettingsModel = require('../vis/ui-settings');

var DEFAULT_OPTIONS = {
  tiles_loader: true,
  loaderControl: true,
  infowindow: true,
  tooltip: true,
  logo: true,
  show_empty_infowindow_fields: false
};

var createVis = function (el, vizjson, options) {
  if (typeof el === 'string') {
    el = document.getElementById(el);
  }
  if (!el) {
    throw new TypeError('a valid DOM element or selector must be provided');
  }
  if (!vizjson) {
    throw new TypeError('a vizjson URL or object must be provided');
  }

  options = _.defaults(options || {}, DEFAULT_OPTIONS);

  var visModel = new VisModel();

  if (typeof vizjson === 'string') {
    var url = vizjson;
    Loader.get(url, function (vizjson) {
      if (vizjson) {
        loadVizJSON(el, visModel, vizjson, options);
      } else {
        throw new Error('error fetching viz.json file');
      }
    });
  } else {
    loadVizJSON(el, visModel, vizjson, options);
  }

  return visModel;
};

var loadVizJSON = function (el, visModel, vizjsonData, options) {
  var vizjson = new VizJSON(vizjsonData);
  applyOptionsToVizJSON(vizjson, options);

  var isProtocolHTTPs = window && window.location.protocol && window.location.protocol === 'https:';

  var showLegends = true;
  if (_.isBoolean(options.legends)) {
    showLegends = options.legends;
  } else if (vizjson.embed_options && _.isBoolean(vizjson.embed_options.legends)) {
    showLegends = vizjson.embed_options.legends;
  }

  var showLayerSelector = true;
  if (_.isBoolean(options.layer_selector)) {
    showLayerSelector = options.layer_selector;
  } else if (vizjson.embed_options && _.isBoolean(vizjson.embed_options.layer_selector)) {
    showLayerSelector = vizjson.embed_options.layer_selector;
  }

  var settingsModel = new SettingsModel({
    showLegends: showLegends,
    showLayerSelector: showLayerSelector
  });

  visModel.set({
    title: options.title || vizjson.title,
    description: options.description || vizjson.description,
    apiKey: options.apiKey,
    authToken: options.authToken,
    showEmptyInfowindowFields: options.show_empty_infowindow_fields === true,
    https: isProtocolHTTPs || options.https === true || vizjson.https === true
  });

  visModel.settings = settingsModel;

  new VisView({ // eslint-disable-line
    el: el,
    model: visModel,
    settingsModel: settingsModel
  });

  visModel.load(vizjson);

  if (!options.skipMapInstantiation) {
    visModel.instantiateMap();
  }
};

var applyOptionsToVizJSON = function (vizjson, options) {
  vizjson.embed_options.scrollwheel = options.scrollwheel || vizjson.embed_options.scrollwheel;

  if (!options.tiles_loader || !options.loaderControl) {
    vizjson.removeLoaderOverlay();
  }

  if (options.searchControl === true) {
    vizjson.addSearchOverlay();
  } else if (options.searchControl === false) {
    vizjson.removeSearchOverlay();
  }

  if ((options.title && vizjson.title) || (options.description && vizjson.description)) {
    vizjson.addHeaderOverlay(options.title, options.description, options.shareable);
  }

  if (options.layer_selector) {
    vizjson.addLayerSelectorOverlay();
  }

  if (options.zoomControl !== undefined && !options.zoomControl) {
    vizjson.removeZoomOverlay();
  }

  if (options.logo === false) {
    vizjson.removeLogoOverlay();
  }

  // if bounds are present zoom and center will not taken into account
  var zoom = parseInt(options.zoom, 10);
  if (!isNaN(zoom)) {
    vizjson.setZoom(zoom);
  }

  // Center coordinates?
  var center_lat = parseFloat(options.center_lat);
  var center_lon = parseFloat(options.center_lon);
  if (!isNaN(center_lat) && !isNaN(center_lon)) {
    vizjson.setCenter([center_lat, center_lon]);
  }

  // Center object
  if (options.center !== undefined) {
    vizjson.setCenter(options.center);
  }

  // Bounds?
  var sw_lat = parseFloat(options.sw_lat);
  var sw_lon = parseFloat(options.sw_lon);
  var ne_lat = parseFloat(options.ne_lat);
  var ne_lon = parseFloat(options.ne_lon);

  if (!isNaN(sw_lat) && !isNaN(sw_lon) && !isNaN(ne_lat) && !isNaN(ne_lon)) {
    vizjson.setBounds([
      [ sw_lat, sw_lon ],
      [ ne_lat, ne_lon ]
    ]);
  }

  if (options.gmaps_base_type) {
    vizjson.enforceGMapsBaseLayer(options.gmaps_base_type, options.gmaps_style);
  }
};

module.exports = createVis;
<|endoftext|>


<commit_before>var stats = new Stats();

var TAU = Math.PI * 2;

var CLOUD_COUNT = 5;
// how much to wiggle the points around
var POINT_WIGGLE = 10;
// how much to wiggle the curves around
var WIGGLE = 10;

// the length of the segment in relation to the circle's radius
var SEGMENT_FACTOR = 3;

var context;
var width, height;

var clouds = [];

// A simple Linear Congruential Generator

// Establish the parameters of the generator
var m = 25;
// a - 1 should be divisible by m's prime factors
var a = 11;
// c and m should be co-prime
var c = 17;
// Setting the seed
var seed = 3;

var rand = function() {
  // define the recurrence relationship
  seed = (a * seed + c) % m;
  // return float in (0, 1)
  return seed / m;
};

var wiggle = function (size) {
  return Math.random() * size - size/2;
};

var setupClouds = function () {
  for (var i = 0; i < CLOUD_COUNT; i++) {
    createCloud();
  }
};

var createCloudCircle = function (x, y) {
  return {
    x: x,
    y: y,
    radius: (height / 5) + wiggle(height/5),
    points: []
  };
};

var generateTriange = function () {
  var h = (height / 4) + wiggle(30);
  var base = (2 * height / 5) + wiggle(30);
  var top   = {x: 0, y: -h};
  var left  = {x: -base/2 + wiggle(base/5), y: 0};
  var right = {x: base + left.x, y: 0};
  // start on the right to match our line drawing starting point
  return [
    {
      start: right,
      end: left,
      distance: right.x - left.x,
      circles: []
    },
    {
      start: left,
      end: top,
      distance: Math.sqrt(left.x*left.x + h*h),
      circles: []
    },
    {
      start: top,
      end: right,
      distance: Math.sqrt(right.x*right.x + h*h),
      circles: []
    }
  ];
};

var generateCircles = function (legs) {
  var circles = [];
  var startCircle = createCloudCircle(legs[0].start.x, legs[0].start.y);
  circles.push(startCircle);

  for (var i = 0; i < legs.length; i++) {
    var leg = legs[i];
    var endCircle;
    leg.circles.push(startCircle);
    if (i == legs.length-1) {
      endCircle = legs[0].circles[0];
    } else {
      endCircle = createCloudCircle(leg.end.x, leg.end.y);
      circles.push(endCircle);
    }
    leg.circles.push(endCircle);
    var total = startCircle.radius + endCircle.radius;
    startCircle = endCircle;
    // add more if needed
    while (total < leg.distance * 2) {
      var x = 0;
      var y = 0;
      for (var j = 0; j < leg.circles.length; j++) {
        var legCircle = leg.circles[j];
        x += legCircle.x;
        y += legCircle.y;
      }
      x = x / leg.circles.length;
      y = y / leg.circles.length;
      var circle = createCloudCircle(x, y);
      circles.push(circle);
      leg.circles.push(circle);
      total += circle.radius;
    }
  }

  // make the first circle have the rightmost point
  var rightMostPoint = circles[0].x + circles[0].radius;
  for (var i = 1; i < circles.length; i++) {
    var candidate = circles[i].x + circles[i].radius;
    if (candidate > rightMostPoint) {
      circles[0].x += candidate - rightMostPoint + 1;
      rightMostPoint = circles[0].x + circles[0].radius;
    }
  }

  return circles;
};

var traceCircles = function (circles) {
  var cuspPoints = [];
  var points = [];
  var start = 0;
  var currentCircle = circles[0];
  var segments = currentCircle.radius / SEGMENT_FACTOR;
  var increment = TAU / segments;
  var i = 0;
  // do until we're back on the first circle and run out of segments
  while (i < segments || currentCircle != circles[0]) {
    var x = Math.cos(i * increment) * currentCircle.radius + wiggle(POINT_WIGGLE);
    var y = Math.sin(i * increment) * currentCircle.radius + wiggle(POINT_WIGGLE);
    var outside = true;
    for (var j = 0; j < circles.length; j++) {
      var candidate = circles[j];
      if (candidate === currentCircle) {
        continue;
      }
      var translateX = currentCircle.x + x - candidate.x;
      var translateY = currentCircle.y + y - candidate.y;
      var dist = Math.sqrt(translateX * translateX + translateY * translateY);
      if (dist < candidate.radius) {
        cuspPoints.push({ x: x + currentCircle.x, y: y + currentCircle.y });
        // points.push({ x: x + currentCircle.x, y: y + currentCircle.y });
        currentCircle = candidate;
        segments = currentCircle.radius / SEGMENT_FACTOR;
        increment = TAU / segments;
        i = Math.round(segments * Math.atan2(translateY, translateX) / TAU);
        if (i < 0) {
          i = segments + i;
        }
        outside = false;
        break;
      }
    }
    if (outside) {
      currentCircle.points.push({ x: x, y: y });
      points.push({ x: x + currentCircle.x, y: y + currentCircle.y });
    }
    i++;
  }

  return points;
};

var createCloud = function () {
  var legs    = generateTriange();
  var circles = generateCircles(legs);
  var points  = traceCircles(circles);

  clouds.push({
    x:          Math.random() * width,
    y:          height - Math.max(circles[0].radius, circles[1].radius) - 50,
    circles:    circles,
    legs:       legs,
    points:     points,
    seed:       Math.round(Math.random() * 128)
  });
};

var render = function () {
  stats.begin();
  context.clearRect(0, 0, width, height);

  context.fillStyle = 'white';
  var length = clouds.length;
  for (var i = 0; i < length; i++) {
    var cloud = clouds[i];
    context.save();
    context.translate(cloud.x, cloud.y);
    for (var j = 0; j < cloud.circles.length; j++) {
      var circle = cloud.circles[j];
      context.beginPath();
      context.arc(circle.x, circle.y, circle.radius, 0, TAU);
      context.fill();
    }
    seed = cloud.seed;
    context.beginPath();
    var lastPoint = cloud.points[0];
    context.moveTo(lastPoint.x, lastPoint.y);
    for (var j = 1; j < cloud.points.length; j++) {
      var point = cloud.points[j];
      var midpointX = (point.x + lastPoint.x)/2;
      var midpointY = (point.y + lastPoint.y)/2;
      context.bezierCurveTo(
        midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
        midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
        point.x, point.y);
      lastPoint = point;
    }
    point = cloud.points[0];
    midpointX = (point.x + lastPoint.x)/2;
    midpointY = (point.y + lastPoint.y)/2;
    context.bezierCurveTo(
      midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
      midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
      point.x, point.y);
    context.stroke();
    context.restore();
    // for (var j = 0; j < cloud.cuspPoints.length; j++) {
    //   point = cloud.cuspPoints[j];
    //   context.fillStyle = 'green';
    //   context.fillRect(point.x - 2, point.y - 2, 5, 5);
    // }
  }

  stats.end();
};

var lastFrame, elapsed;
lastFrame = 0;

var tick = function (timestamp) {
  elapsed = timestamp - lastFrame;
  lastFrame = timestamp;
  step(elapsed);
  render();

  requestAnimationFrame(tick);
};

var step = function (delta) {
};

window.onload = function() {
  var canvas = document.getElementById('canvas');

  if (document.location.search === "?fps=1") {
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    document.body.appendChild(stats.dom);
  }

  context = canvas.getContext('2d');
  context.canvas.width  = window.innerWidth;
  context.canvas.height = window.innerHeight;
  width  = context.canvas.width;
  height = context.canvas.height;

  setupClouds();

  requestAnimationFrame(tick);
};
<commit_msg>sort clouds by height and position<commit_after>var stats = new Stats();

var TAU = Math.PI * 2;

var CLOUD_COUNT = 5;
// how much to wiggle the points around
var POINT_WIGGLE = 10;
// how much to wiggle the curves around
var WIGGLE = 10;

// the length of the segment in relation to the circle's radius
var SEGMENT_FACTOR = 3;

var context;
var width, height;

var clouds = [];

// A simple Linear Congruential Generator

// Establish the parameters of the generator
var m = 25;
// a - 1 should be divisible by m's prime factors
var a = 11;
// c and m should be co-prime
var c = 17;
// Setting the seed
var seed = 3;

var rand = function() {
  // define the recurrence relationship
  seed = (a * seed + c) % m;
  // return float in (0, 1)
  return seed / m;
};

var wiggle = function (size) {
  return Math.random() * size - size/2;
};

var setupClouds = function () {
  for (var i = 0; i < CLOUD_COUNT; i++) {
    clouds.push(createCloud());
  }
  // sort by highest point on the screen
  clouds.sort(function (c1, c2) {
    return c1.minY + c1.y - c2.minY + c2.y;
  });
};

var createCloudCircle = function (x, y) {
  return {
    x: x,
    y: y,
    radius: (height / 5) + wiggle(height/5),
    points: []
  };
};

var generateTriange = function () {
  var h = (height / 4) + wiggle(30);
  var base = (2 * height / 5) + wiggle(30);
  var top   = {x: 0, y: -h};
  var left  = {x: -base/2 + wiggle(base/5), y: 0};
  var right = {x: base + left.x, y: 0};
  // start on the right to match our line drawing starting point
  return [
    {
      start: right,
      end: left,
      distance: right.x - left.x,
      circles: []
    },
    {
      start: left,
      end: top,
      distance: Math.sqrt(left.x*left.x + h*h),
      circles: []
    },
    {
      start: top,
      end: right,
      distance: Math.sqrt(right.x*right.x + h*h),
      circles: []
    }
  ];
};

var generateCircles = function (legs) {
  var circles = [];
  var startCircle = createCloudCircle(legs[0].start.x, legs[0].start.y);
  circles.push(startCircle);

  for (var i = 0; i < legs.length; i++) {
    var leg = legs[i];
    var endCircle;
    leg.circles.push(startCircle);
    if (i == legs.length-1) {
      endCircle = legs[0].circles[0];
    } else {
      endCircle = createCloudCircle(leg.end.x, leg.end.y);
      circles.push(endCircle);
    }
    leg.circles.push(endCircle);
    var total = startCircle.radius + endCircle.radius;
    startCircle = endCircle;
    // add more if needed
    while (total < leg.distance * 2) {
      var x = 0;
      var y = 0;
      for (var j = 0; j < leg.circles.length; j++) {
        var legCircle = leg.circles[j];
        x += legCircle.x;
        y += legCircle.y;
      }
      x = x / leg.circles.length;
      y = y / leg.circles.length;
      var circle = createCloudCircle(x, y);
      circles.push(circle);
      leg.circles.push(circle);
      total += circle.radius;
    }
  }

  // make the first circle have the rightmost point
  var rightMostPoint = circles[0].x + circles[0].radius;
  for (var i = 1; i < circles.length; i++) {
    var candidate = circles[i].x + circles[i].radius;
    if (candidate > rightMostPoint) {
      circles[0].x += candidate - rightMostPoint + 1;
      rightMostPoint = circles[0].x + circles[0].radius;
    }
  }

  return circles;
};

var traceCircles = function (circles) {
  var cuspPoints = [];
  var points = [];
  var start = 0;
  var currentCircle = circles[0];
  var segments = currentCircle.radius / SEGMENT_FACTOR;
  var increment = TAU / segments;
  var i = 0;
  // do until we're back on the first circle and run out of segments
  while (i < segments || currentCircle != circles[0]) {
    var x = Math.cos(i * increment) * currentCircle.radius + wiggle(POINT_WIGGLE);
    var y = Math.sin(i * increment) * currentCircle.radius + wiggle(POINT_WIGGLE);
    var outside = true;
    for (var j = 0; j < circles.length; j++) {
      var candidate = circles[j];
      if (candidate === currentCircle) {
        continue;
      }
      var translateX = currentCircle.x + x - candidate.x;
      var translateY = currentCircle.y + y - candidate.y;
      var dist = Math.sqrt(translateX * translateX + translateY * translateY);
      if (dist < candidate.radius) {
        cuspPoints.push({ x: x + currentCircle.x, y: y + currentCircle.y });
        // points.push({ x: x + currentCircle.x, y: y + currentCircle.y });
        currentCircle = candidate;
        segments = currentCircle.radius / SEGMENT_FACTOR;
        increment = TAU / segments;
        i = Math.round(segments * Math.atan2(translateY, translateX) / TAU);
        if (i < 0) {
          i = segments + i;
        }
        outside = false;
        break;
      }
    }
    if (outside) {
      currentCircle.points.push({ x: x, y: y });
      points.push({ x: x + currentCircle.x, y: y + currentCircle.y });
    }
    i++;
  }

  return points;
};

var createCloud = function () {
  var legs    = generateTriange();
  var circles = generateCircles(legs);
  var points  = traceCircles(circles);

  var minY = 0;
  var maxY = 0;
  for (var i = 0; i < points.length; i++) {
    minY = Math.min(minY, points[i].y);
    maxY = Math.max(maxY, points[i].y);
  }

  var x = Math.random() * width;
  var y = height - Math.max(circles[0].radius, circles[1].radius) - 50;

  return {
    x:          x,
    y:          y,
    minY:       minY,
    maxY:       maxY,
    circles:    circles,
    legs:       legs,
    points:     points,
    seed:       Math.round(Math.random() * 128)
  };
};

var render = function () {
  stats.begin();
  context.clearRect(0, 0, width, height);

  context.fillStyle = 'white';
  var length = clouds.length;
  for (var i = 0; i < length; i++) {
    var cloud = clouds[i];
    context.save();
    context.translate(cloud.x, cloud.y);
    for (var j = 0; j < cloud.circles.length; j++) {
      var circle = cloud.circles[j];
      context.beginPath();
      context.arc(circle.x, circle.y, circle.radius, 0, TAU);
      context.fill();
    }
    seed = cloud.seed;
    context.beginPath();
    var lastPoint = cloud.points[0];
    context.moveTo(lastPoint.x, lastPoint.y);
    for (var j = 1; j < cloud.points.length; j++) {
      var point = cloud.points[j];
      var midpointX = (point.x + lastPoint.x)/2;
      var midpointY = (point.y + lastPoint.y)/2;
      context.bezierCurveTo(
        midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
        midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
        point.x, point.y);
      lastPoint = point;
    }
    point = cloud.points[0];
    midpointX = (point.x + lastPoint.x)/2;
    midpointY = (point.y + lastPoint.y)/2;
    context.bezierCurveTo(
      midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
      midpointX + (rand() * WIGGLE - WIGGLE/2), midpointY + (rand() * WIGGLE - WIGGLE/2),
      point.x, point.y);
    context.stroke();
    context.restore();
    // for (var j = 0; j < cloud.cuspPoints.length; j++) {
    //   point = cloud.cuspPoints[j];
    //   context.fillStyle = 'green';
    //   context.fillRect(point.x - 2, point.y - 2, 5, 5);
    // }
  }

  stats.end();
};

var lastFrame, elapsed;
lastFrame = 0;

var tick = function (timestamp) {
  elapsed = timestamp - lastFrame;
  lastFrame = timestamp;
  step(elapsed);
  render();

  requestAnimationFrame(tick);
};

var step = function (delta) {
};

window.onload = function() {
  var canvas = document.getElementById('canvas');

  if (document.location.search === "?fps=1") {
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    document.body.appendChild(stats.dom);
  }

  context = canvas.getContext('2d');
  context.canvas.width  = window.innerWidth;
  context.canvas.height = window.innerHeight;
  width  = context.canvas.width;
  height = context.canvas.height;

  setupClouds();

  requestAnimationFrame(tick);
};
<|endoftext|>


<commit_before>/*! codeFormatter v6.1.0 by ryanpcmcquen */
//
// Ryan McQuen

/**
 * @fileoverview microlight - syntax highlightning library
 * @version 0.0.7
 *
 * @license MIT, see http://github.com/asvd/microlight
 * @copyright 2016 asvd <heliosframework@gmail.com>
 *
 * Code structure aims at minimizing the compressed library size
 */

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else if (typeof exports !== 'undefined') {
        factory(exports);
    } else {
        factory((root.microlight = {}));
    }
})(this, function (exports) {
    // for better compression
    var _window = window,
        _document = document,
        appendChild = 'appendChild',
        test = 'test',
        // style and color templates
        textShadow = ';text-shadow:',
        opacity = 'opacity:.',
        _0px_0px = ' 0px 0px ',
        _3px_0px_5 = '3px 0px 5',
        brace = ')',
        i,
        microlighted,
        el; // current microlighted element to run through

    var reset = function (cls) {
        // nodes to highlight
        microlighted = _document.getElementsByClassName(cls || 'microlight');

        for (i = 0; (el = microlighted[i++]); ) {
            var text = el.textContent,
                pos = 0, // current position
                next1 = text[0], // next character
                chr = 1, // current character
                prev1, // previous character
                prev2, // the one before the previous
                token = (el.innerHTML = ''), // current token content // (and cleaning the node)
                // current token type:
                //  0: anything else (whitespaces / newlines)
                //  1: operator or brace
                //  2: closing braces (after which '/' is division not regex)
                //  3: (key)word
                //  4: regex
                //  5: string starting with "
                //  6: string starting with '
                //  7: xml comment  <!-- -->
                //  8: multiline comment /* */
                //  9: single-line comment starting with two slashes //
                // 10: single-line comment starting with hash #
                tokenType = 0,
                // kept to determine between regex and division
                lastTokenType,
                // flag determining if token is multi-character
                multichar,
                node,
                // calculating the colors for the style templates
                colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
                    _window.getComputedStyle(el).color
                ),
                pxColor = 'px rgba(' + colorArr[1] + ',',
                alpha = colorArr[3] || 1;

            // running through characters and highlighting
            while (
                ((prev2 = prev1),
                // escaping if needed (with except for comments)
                // pervious character will not be therefore
                // recognized as a token finalize condition
                (prev1 = tokenType < 7 && prev1 == '\\' ? 1 : chr))
            ) {
                chr = next1;
                next1 = text[++pos];
                multichar = token.length > 1;

                // checking if current token should be finalized
                if (
                    !chr || // end of content
                    // types 9-10 (single-line comments) end with a
                    // newline
                    (tokenType > 8 && chr == '\n') ||
                    [
                        // finalize conditions for other token types
                        // 0: whitespaces
                        /\S/[test](chr), // merged together
                        // 1: operators
                        1, // consist of a single character
                        // 2: braces
                        1, // consist of a single character
                        // 3: (key)word
                        !/[$\w]/[test](chr),
                        // 4: regex
                        (prev1 == '/' || prev1 == '\n') && multichar,
                        // 5: string with "
                        prev1 == '"' && multichar,
                        // 6: string with '
                        prev1 == "'" && multichar,
                        // 7: xml comment
                        text[pos - 4] + prev2 + prev1 == '-->',
                        // 8: multiline comment
                        prev2 + prev1 == '*/',
                    ][tokenType]
                ) {
                    // appending the token to the result
                    if (token) {
                        // remapping token type into style
                        // (some types are highlighted similarly)
                        el[appendChild](
                            (node = _document.createElement('span'))
                        ).setAttribute(
                            'style',
                            [
                                // 0: not formatted
                                '',
                                // 1: keywords
                                textShadow +
                                    _0px_0px +
                                    9 +
                                    pxColor +
                                    alpha * 0.7 +
                                    '),' +
                                    _0px_0px +
                                    2 +
                                    pxColor +
                                    alpha * 0.4 +
                                    brace,
                                // 2: punctuation
                                opacity +
                                    6 +
                                    textShadow +
                                    _0px_0px +
                                    7 +
                                    pxColor +
                                    alpha / 4 +
                                    '),' +
                                    _0px_0px +
                                    3 +
                                    pxColor +
                                    alpha / 4 +
                                    brace,
                                // 3: strings and regexps
                                opacity +
                                    7 +
                                    textShadow +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 5 +
                                    '),-' +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 5 +
                                    brace,
                                // 4: comments
                                'font-style:italic;' +
                                    opacity +
                                    5 +
                                    textShadow +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 4 +
                                    '),-' +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 4 +
                                    brace,
                            ][
                                // not formatted
                                !tokenType
                                    ? 0
                                    : // punctuation
                                    tokenType < 3
                                    ? 2
                                    : // comments
                                    tokenType > 6
                                    ? 4
                                    : // regex and strings
                                    tokenType > 3
                                    ? 3
                                    : // otherwise tokenType == 3, (key)word
                                      // (1 if regexp matches, 0 otherwise)
                                      +/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/[
                                          test
                                      ](token)
                            ]
                        );

                        node[appendChild](_document.createTextNode(token));
                    }

                    // saving the previous token type
                    // (skipping whitespaces and comments)
                    lastTokenType =
                        tokenType && tokenType < 7 ? tokenType : lastTokenType;

                    // initializing a new token
                    token = '';

                    // determining the new token type (going up the
                    // list until matching a token type start
                    // condition)
                    tokenType = 11;
                    while (
                        ![
                            1, //  0: whitespace
                            //  1: operator or braces
                            /[\/{}[(\-+*=<>:;|\\.,?!&@~]/[test](chr),
                            /[\])]/[test](chr), //  2: closing brace
                            /[$\w]/[test](chr), //  3: (key)word
                            chr == '/' && //  4: regex
                                // previous token was an
                                // opening brace or an
                                // operator (otherwise
                                // division, not a regex)
                                lastTokenType < 2 &&
                                // workaround for xml
                                // closing tags
                                prev1 != '<',
                            chr == '"', //  5: string with "
                            chr == "'", //  6: string with '
                            //  7: xml comment
                            chr + next1 + text[pos + 1] + text[pos + 2] ==
                                '<!--',
                            chr + next1 == '/*', //  8: multiline comment
                            chr + next1 == '//', //  9: single-line comment
                            chr == '#', // 10: hash-style comment
                        ][--tokenType]
                    );
                }

                token += chr;
            }
        }
    };

    exports.reset = reset;

    if (_document.readyState == 'complete') {
        reset();
    } else {
        _window.addEventListener(
            'load',
            function () {
                reset();
            },
            0
        );
    }
});

/*jslint browser*/
(function () {
    'use strict';

    var replacement = function (matchedText, blockType, opacity, language) {
        opacity = opacity || 1;
        language = language === undefined ? 'plain' : language;
        var block = document.createElement(blockType);
        var textNode = document.createTextNode(String(matchedText));

        if (language !== 'plain' && language !== 'p') {
            block.classList.add('microlight');
        }
        block.appendChild(textNode);
        block.style.opacity = opacity;

        return block;
    };

    var backtickOpacity = 0.3;

    var codeFormatter = function (selector) {
        var contentArray = Array.prototype.slice.call(
            document.querySelectorAll(selector)
        );

        // Multi-line code:
        var tripleTickRegex = /```[\w\W]+```/gim;
        // Inline code:
        var singleTickRegex = /`[^`]+`/g;

        var codeLanguageRegex = /```{1}.*/;
        var tripleTickCapture = /(```)/;
        var singleTickCapture = /(`[^`]+`)/;

        contentArray.forEach(function (content) {
            if (
                !/<pre/gi.test(content.innerHTML) &&
                !/<code/gi.test(content.innerHTML)
            ) {
                // Match the code language, so we can support
                // a lot of awesome syntax highlighting.
                if (content.textContent) {
                    var theNewKidsOnTheBlock = [];
                    if (tripleTickRegex.test(content.textContent)) {
                        // This needs a little extra filtering,
                        // but cascading is cool.
                        var codeLanguage = String(
                            content.textContent.match(codeLanguageRegex)
                        )
                            .slice(3)
                            .split(/(\s+)/)[0]
                            .trim();

                        var pairs = 0;
                        content.textContent
                            .split(tripleTickCapture)
                            .forEach(function (textBlock, index, self) {
                                if (tripleTickCapture.test(textBlock)) {
                                    pairs++;

                                    theNewKidsOnTheBlock.push(
                                        replacement(
                                            textBlock,
                                            'pre',
                                            backtickOpacity
                                        )
                                    );
                                } else if (
                                    self[index - 1] === '```' &&
                                    self[index + 1] === '```' &&
                                    pairs % 2 === 1
                                ) {
                                    theNewKidsOnTheBlock.push(
                                        replacement(
                                            textBlock,
                                            'pre',
                                            1,
                                            textBlock.slice(0, 1) === 'p'
                                                ? 'plain'
                                                : ''
                                        )
                                    );
                                } else {
                                    theNewKidsOnTheBlock.push(
                                        replacement(textBlock, 'span')
                                    );
                                }
                            });
                    }
                }
                if (singleTickRegex.test(content.textContent)) {
                    var singlePairs = 0;
                    var singlePlayerGame = function (splitBlock, index, self) {
                        if (singleTickCapture.test(splitBlock)) {
                            singlePairs++;
                            return replacement(
                                splitBlock,
                                'code',
                                backtickOpacity
                            );
                        } else if (
                            self[index - 1] === '`' &&
                            self[index + 1] === '`' &&
                            singlePairs % 2 === 1
                        ) {
                            return replacement(splitBlock, 'code');
                        } else {
                            return replacement(splitBlock, 'span');
                        }
                    };

                    if (theNewKidsOnTheBlock.length > 0) {
                        theNewKidsOnTheBlock.forEach(function (
                            block,
                            index,
                            self
                        ) {
                            if (singleTickCapture.test(block.textContent)) {
                                var newSet = [];
                                block.textContent
                                    .split(singleTickCapture)
                                    .forEach(function (child) {
                                        if (singleTickRegex.test(child)) {
                                            newSet.push(
                                                replacement(child, 'code')
                                            );
                                        } else {
                                            newSet.push(
                                                replacement(child, 'span')
                                            );
                                        }
                                    });
                                self[index] = newSet;
                            }
                        });

                        theNewKidsOnTheBlock = theNewKidsOnTheBlock.flat();
                    } else {
                        var theSingleKids = [];
                        theSingleKids = content.textContent
                            .split(singleTickCapture)
                            .map(singlePlayerGame);
                        var inlineSpan = document.createElement('span');
                        theSingleKids.forEach(function (singleKid) {
                            inlineSpan.appendChild(singleKid);
                        });
                        theNewKidsOnTheBlock.push(inlineSpan);
                    }
                }
                while (content.firstChild) {
                    content.removeChild(content.firstChild);
                }
                theNewKidsOnTheBlock.forEach(function (newKid) {
                    content.appendChild(newKid);
                });
            }
        });

        // Invoke microlight:
        microlight.reset();
    };

    // Attach globally:
    window.codeFormatter = codeFormatter;
})();
<commit_msg>Ensure we have text to format before replacing nodes.<commit_after>/*! codeFormatter v6.1.1 by ryanpcmcquen */
//
// Ryan McQuen

/**
 * @fileoverview microlight - syntax highlightning library
 * @version 0.0.7
 *
 * @license MIT, see http://github.com/asvd/microlight
 * @copyright 2016 asvd <heliosframework@gmail.com>
 *
 * Code structure aims at minimizing the compressed library size
 */

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else if (typeof exports !== 'undefined') {
        factory(exports);
    } else {
        factory((root.microlight = {}));
    }
})(this, function (exports) {
    // for better compression
    var _window = window,
        _document = document,
        appendChild = 'appendChild',
        test = 'test',
        // style and color templates
        textShadow = ';text-shadow:',
        opacity = 'opacity:.',
        _0px_0px = ' 0px 0px ',
        _3px_0px_5 = '3px 0px 5',
        brace = ')',
        i,
        microlighted,
        el; // current microlighted element to run through

    var reset = function (cls) {
        // nodes to highlight
        microlighted = _document.getElementsByClassName(cls || 'microlight');

        for (i = 0; (el = microlighted[i++]); ) {
            var text = el.textContent,
                pos = 0, // current position
                next1 = text[0], // next character
                chr = 1, // current character
                prev1, // previous character
                prev2, // the one before the previous
                token = (el.innerHTML = ''), // current token content // (and cleaning the node)
                // current token type:
                //  0: anything else (whitespaces / newlines)
                //  1: operator or brace
                //  2: closing braces (after which '/' is division not regex)
                //  3: (key)word
                //  4: regex
                //  5: string starting with "
                //  6: string starting with '
                //  7: xml comment  <!-- -->
                //  8: multiline comment /* */
                //  9: single-line comment starting with two slashes //
                // 10: single-line comment starting with hash #
                tokenType = 0,
                // kept to determine between regex and division
                lastTokenType,
                // flag determining if token is multi-character
                multichar,
                node,
                // calculating the colors for the style templates
                colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
                    _window.getComputedStyle(el).color
                ),
                pxColor = 'px rgba(' + colorArr[1] + ',',
                alpha = colorArr[3] || 1;

            // running through characters and highlighting
            while (
                ((prev2 = prev1),
                // escaping if needed (with except for comments)
                // pervious character will not be therefore
                // recognized as a token finalize condition
                (prev1 = tokenType < 7 && prev1 == '\\' ? 1 : chr))
            ) {
                chr = next1;
                next1 = text[++pos];
                multichar = token.length > 1;

                // checking if current token should be finalized
                if (
                    !chr || // end of content
                    // types 9-10 (single-line comments) end with a
                    // newline
                    (tokenType > 8 && chr == '\n') ||
                    [
                        // finalize conditions for other token types
                        // 0: whitespaces
                        /\S/[test](chr), // merged together
                        // 1: operators
                        1, // consist of a single character
                        // 2: braces
                        1, // consist of a single character
                        // 3: (key)word
                        !/[$\w]/[test](chr),
                        // 4: regex
                        (prev1 == '/' || prev1 == '\n') && multichar,
                        // 5: string with "
                        prev1 == '"' && multichar,
                        // 6: string with '
                        prev1 == "'" && multichar,
                        // 7: xml comment
                        text[pos - 4] + prev2 + prev1 == '-->',
                        // 8: multiline comment
                        prev2 + prev1 == '*/',
                    ][tokenType]
                ) {
                    // appending the token to the result
                    if (token) {
                        // remapping token type into style
                        // (some types are highlighted similarly)
                        el[appendChild](
                            (node = _document.createElement('span'))
                        ).setAttribute(
                            'style',
                            [
                                // 0: not formatted
                                '',
                                // 1: keywords
                                textShadow +
                                    _0px_0px +
                                    9 +
                                    pxColor +
                                    alpha * 0.7 +
                                    '),' +
                                    _0px_0px +
                                    2 +
                                    pxColor +
                                    alpha * 0.4 +
                                    brace,
                                // 2: punctuation
                                opacity +
                                    6 +
                                    textShadow +
                                    _0px_0px +
                                    7 +
                                    pxColor +
                                    alpha / 4 +
                                    '),' +
                                    _0px_0px +
                                    3 +
                                    pxColor +
                                    alpha / 4 +
                                    brace,
                                // 3: strings and regexps
                                opacity +
                                    7 +
                                    textShadow +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 5 +
                                    '),-' +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 5 +
                                    brace,
                                // 4: comments
                                'font-style:italic;' +
                                    opacity +
                                    5 +
                                    textShadow +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 4 +
                                    '),-' +
                                    _3px_0px_5 +
                                    pxColor +
                                    alpha / 4 +
                                    brace,
                            ][
                                // not formatted
                                !tokenType
                                    ? 0
                                    : // punctuation
                                    tokenType < 3
                                    ? 2
                                    : // comments
                                    tokenType > 6
                                    ? 4
                                    : // regex and strings
                                    tokenType > 3
                                    ? 3
                                    : // otherwise tokenType == 3, (key)word
                                      // (1 if regexp matches, 0 otherwise)
                                      +/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/[
                                          test
                                      ](token)
                            ]
                        );

                        node[appendChild](_document.createTextNode(token));
                    }

                    // saving the previous token type
                    // (skipping whitespaces and comments)
                    lastTokenType =
                        tokenType && tokenType < 7 ? tokenType : lastTokenType;

                    // initializing a new token
                    token = '';

                    // determining the new token type (going up the
                    // list until matching a token type start
                    // condition)
                    tokenType = 11;
                    while (
                        ![
                            1, //  0: whitespace
                            //  1: operator or braces
                            /[\/{}[(\-+*=<>:;|\\.,?!&@~]/[test](chr),
                            /[\])]/[test](chr), //  2: closing brace
                            /[$\w]/[test](chr), //  3: (key)word
                            chr == '/' && //  4: regex
                                // previous token was an
                                // opening brace or an
                                // operator (otherwise
                                // division, not a regex)
                                lastTokenType < 2 &&
                                // workaround for xml
                                // closing tags
                                prev1 != '<',
                            chr == '"', //  5: string with "
                            chr == "'", //  6: string with '
                            //  7: xml comment
                            chr + next1 + text[pos + 1] + text[pos + 2] ==
                                '<!--',
                            chr + next1 == '/*', //  8: multiline comment
                            chr + next1 == '//', //  9: single-line comment
                            chr == '#', // 10: hash-style comment
                        ][--tokenType]
                    );
                }

                token += chr;
            }
        }
    };

    exports.reset = reset;

    if (_document.readyState == 'complete') {
        reset();
    } else {
        _window.addEventListener(
            'load',
            function () {
                reset();
            },
            0
        );
    }
});

/*jslint browser*/
(function () {
    'use strict';

    var replacement = function (matchedText, blockType, opacity, language) {
        opacity = opacity || 1;
        language = language === undefined ? 'plain' : language;
        var block = document.createElement(blockType);
        var textNode = document.createTextNode(String(matchedText));

        if (language !== 'plain' && language !== 'p') {
            block.classList.add('microlight');
        }
        block.appendChild(textNode);
        block.style.opacity = opacity;

        return block;
    };

    var backtickOpacity = 0.3;

    var codeFormatter = function (selector) {
        var contentArray = Array.prototype.slice.call(
            document.querySelectorAll(selector)
        );

        // Multi-line code:
        var tripleTickRegex = /```[\w\W]+```/gim;
        // Inline code:
        var singleTickRegex = /`[^`]+`/g;

        var codeLanguageRegex = /```{1}.*/;
        var tripleTickCapture = /(```)/;
        var singleTickCapture = /(`[^`]+`)/;

        contentArray.forEach(function (content) {
            if (
                !/<pre/gi.test(content.innerHTML) &&
                !/<code/gi.test(content.innerHTML) &&
                content.textContent
            ) {
                var theNewKidsOnTheBlock = [];
                if (tripleTickRegex.test(content.textContent)) {
                    // This needs a little extra filtering,
                    // but cascading is cool.
                    var codeLanguage = String(
                        content.textContent.match(codeLanguageRegex)
                    )
                        .slice(3)
                        .split(/(\s+)/)[0]
                        .trim();

                    var pairs = 0;
                    content.textContent
                        .split(tripleTickCapture)
                        .forEach(function (textBlock, index, self) {
                            if (tripleTickCapture.test(textBlock)) {
                                pairs++;

                                theNewKidsOnTheBlock.push(
                                    replacement(
                                        textBlock,
                                        'pre',
                                        backtickOpacity
                                    )
                                );
                            } else if (
                                self[index - 1] === '```' &&
                                self[index + 1] === '```' &&
                                pairs % 2 === 1
                            ) {
                                theNewKidsOnTheBlock.push(
                                    replacement(
                                        textBlock,
                                        'pre',
                                        1,
                                        textBlock.slice(0, 1) === 'p'
                                            ? 'plain'
                                            : ''
                                    )
                                );
                            } else {
                                theNewKidsOnTheBlock.push(
                                    replacement(textBlock, 'span')
                                );
                            }
                        });
                }

                if (singleTickRegex.test(content.textContent)) {
                    var singlePairs = 0;
                    var singlePlayerGame = function (splitBlock, index, self) {
                        if (singleTickCapture.test(splitBlock)) {
                            singlePairs++;
                            return replacement(
                                splitBlock,
                                'code',
                                backtickOpacity
                            );
                        } else if (
                            self[index - 1] === '`' &&
                            self[index + 1] === '`' &&
                            singlePairs % 2 === 1
                        ) {
                            return replacement(splitBlock, 'code');
                        } else {
                            return replacement(splitBlock, 'span');
                        }
                    };

                    if (theNewKidsOnTheBlock.length > 0) {
                        theNewKidsOnTheBlock.forEach(function (
                            block,
                            index,
                            self
                        ) {
                            if (singleTickCapture.test(block.textContent)) {
                                var newSet = [];
                                block.textContent
                                    .split(singleTickCapture)
                                    .forEach(function (child) {
                                        if (singleTickRegex.test(child)) {
                                            newSet.push(
                                                replacement(child, 'code')
                                            );
                                        } else {
                                            newSet.push(
                                                replacement(child, 'span')
                                            );
                                        }
                                    });
                                self[index] = newSet;
                            }
                        });

                        theNewKidsOnTheBlock = theNewKidsOnTheBlock.flat();
                    } else {
                        var theSingleKids = [];
                        theSingleKids = content.textContent
                            .split(singleTickCapture)
                            .map(singlePlayerGame);
                        var inlineSpan = document.createElement('span');
                        theSingleKids.forEach(function (singleKid) {
                            inlineSpan.appendChild(singleKid);
                        });
                        theNewKidsOnTheBlock.push(inlineSpan);
                    }
                }
                if (theNewKidsOnTheBlock.length > 0) {
                    while (content.firstChild) {
                        content.removeChild(content.firstChild);
                    }
                    theNewKidsOnTheBlock.forEach(function (newKid) {
                        content.appendChild(newKid);
                    });
                }
            }
        });

        // Invoke microlight:
        microlight.reset();
    };

    // Attach globally:
    window.codeFormatter = codeFormatter;
})();
<|endoftext|>