<commit_before>/**
 * @license nya-bootstrap-select v1.2.6
 * Copyright 2014 nyasoft
 * Licensed under MIT license
 */

'use strict';

angular.module('nya.bootstrap.select',[])
  .directive('nyaSelectpicker', ['$parse', function ($parse) {

    // NG_OPTIONS_REGEXP copy from angular.js select directive
    var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
    return {
      restrict: 'CA',
      scope: false,
      require: ['^ngModel', 'select'],

      link: function(scope, element, attrs, ctrls) {
        var optionsExp = attrs.ngOptions;
        var valuesFn, match, track, groupBy;
        if(optionsExp && (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
          groupBy = match[3];
          console.log(optionsExp, groupBy);
          valuesFn = $parse(match[7]);
          track = match[8];
        }
        var ngCtrl = ctrls[0];
        var selectCtrl = ctrls[1];
        // prevent selectDirective render an unknownOption.
        selectCtrl.renderUnknownOption = angular.noop;
        var optionArray = [];

        // store data- attribute options of select
        var selectorOptions = {};
        var BS_ATTR = ['container', 'countSelectedText', 'dropupAuto', 'header', 'hideDisabled', 'selectedTextFormat', 'size', 'showSubtext', 'showIcon', 'showContent', 'style', 'title', 'width', 'disabled'];

        var checkSelectorOptionsEquality = function() {
          var isEqual = true;
          angular.forEach(BS_ATTR, function(attr) {
            isEqual = isEqual && attrs[attr] === selectorOptions[attr];
          });
        };

        var updateSelectorOptions = function() {
          angular.forEach(BS_ATTR, function(attr) {
            selectorOptions[attr] = attrs[attr];
          });
        };

        /**
         * Check option data attributes, text and value equality.
         * @param opt the option dom element
         * @param index the index of the option
         * @returns {boolean}
         */
        var checkOptionEquality = function(opt, index) {
          var isEqual = opt.value === optionArray[index].value && opt.text === optionArray[index].text;
          if(isEqual) {
            for(var i = 0; i< opt.attributes.length; i++){
              if(opt.attributes[i].nodeName.indexOf('data-')!==-1) {
                if(optionArray[index].attributes[opt.attributes[i].nodeName] !== opt.attributes[i].nodeValue) {
                  isEqual = false;
                  break;
                }
              }
            }
          }
          return isEqual;
        };

        var resetDataProperties = function(opt) {
          var attributes = opt.attributes;
          for(var i = 0; i < attributes.length; i++) {
            if(attributes[i].nodeName.indexOf('data-')!==-1) {
              $(opt).data(attributes[i].nodeName.substring(5, attributes[i].nodeName.length), attributes[i].value);
            }
          }
        };

        function optionDOMWatch(){
          // check every option if has changed.
          var optionElements = $(element).find('option');

          //if the first option has no value and label or value an value of ?, this must be generated by ngOptions directive. Remove it.
          if(!optionElements.eq(0).html() && (optionElements.eq(0).attr('value') ==='?' || !optionElements.eq(0).attr('value'))) {
            // angular seams incorrectly remove the first element of the options. so we have to keep the ? element in the list
            // only remove this ? element when group by is provided.
            if(!!groupBy) {
              optionElements.eq(0).remove();
            }
          }

          if(optionElements.length !== optionArray.length) {
            optionArray = makeOptionArray(optionElements);
            buildSelector();
          } else {
            var hasChanged = false;
            optionElements.each(function(index, value){
              if(!checkOptionEquality(value, index)) {
                // if check fails. reset all data properties.
                resetDataProperties(value);
                hasChanged = true;
              }
            });
            if(hasChanged) {
              buildSelector();
            }
            if(!checkSelectorOptionsEquality()) {
              updateSelectorOptions();
              $(element).selectpicker('refresh');
            }
            optionArray = makeOptionArray(optionElements);
          }
        }

        scope.$watch(function(){
          // Create an object to deep inspect if anything has changed.
          // This is slow, but not as slow as calling optionDOMWatch every $digest
          return {
            ngModel: ngCtrl.$viewValue,
            options: makeOptionArray( $(element).find('option') )
          };
        // If any of the above properties change, call optionDOMWatch.
        }, optionDOMWatch, true);

        var setValue = function(modelValue) {
          var collection = valuesFn(scope);
          if(angular.isArray(collection) && !angular.isArray(modelValue)) {
            // collection is array and single select mode
            var index = indexInArray(modelValue, collection);
            if(index > -1) {
              $(element).val(index).selectpicker('render');
            }
          } else if(angular.isArray(collection) && angular.isArray(modelValue)) {
            // collection is array and multiple select mode.
            var indexArray = [];
            for(var i = 0; i < modelValue.length; i++) {
              var indexOfOptions = indexInArray(modelValue[i], collection);
              if(indexOfOptions > -1) {
                indexArray.push(indexOfOptions);
              }
            }
            $(element).val(indexArray).selectpicker('render');
          } else if(!angular.isArray(collection) && !angular.isArray(modelValue)) {
            // collection is object and single select mode.
            var key = keyOfObject(modelValue, collection);
            if(key) {
              $(element).val(key).selectpicker('render');
            }
          } else if(!angular.isArray(collection) && angular.isArray(modelValue)) {
            // collection is object and multiple select mode.
            var keyArray = [];
            for(var j = 0; j < modelValue.length; j++) {
              var k = keyOfObject(modelValue[j], collection);
              if(k) {
                keyArray.push(k);
              }
            }
            $(element).val(keyArray).selectpicker('render');
          }
        };

        ngCtrl.$render = function() {
          // model -> view
          var data = $(element).data('selectpicker');
          if(data) {
            if(!!valuesFn && !track) {
              // transform value to index of options
              setValue(ngCtrl.$viewValue);
            } else {
              $(element).val(ngCtrl.$viewValue).selectpicker('render');
            }
          }
        };

        function indexInArray(value, array) {
          for(var i = 0; i < array.length; i++) {
            if(angular.equals(value, array[i])) {
              return i;
            }
          }
          return -1;
        }

        function keyOfObject(value, object) {
          var key = null;
          angular.forEach(object, function(v, k) {
            if(angular.equals(v, value)) {
              key = k;
            }
          });
          return key;
        }

        /**
         * Copy option value and text and data attributes to an array for future comparison.
         * @param optionElements the source option elements. a jquery objects' array.
         * @returns {Array} the copied array.
         */
        function makeOptionArray(optionElements) {
          var optionArray = [];
          optionElements.each(function(index, childNode){
            var attributes = {};
            for(var i = 0; i < childNode.attributes.length; i++) {
              if(childNode.attributes[i].nodeName.indexOf('data-')!==-1) {
                attributes[childNode.attributes[i].nodeName] = childNode.attributes[i].nodeValue;
              }
            }
            optionArray.push({
              value: childNode.value,
              text: childNode.text,
              attributes: attributes
            });
          });
          return optionArray;
        }

        function buildSelector() {
          // build new selector. if previous select exists. remove previous data and DOM
          var oldSelectPicker = $(element).data('selectpicker');
          if(oldSelectPicker) {
            oldSelectPicker.$menu.parent().remove();
            oldSelectPicker.$newElement.remove();
            $(element).removeData('selectpicker');
          }
          $(element).selectpicker();
          if(!!valuesFn && !track) {
            setValue(ngCtrl.$modelValue);
          } else {
            $(element).val(ngCtrl.$modelValue).selectpicker('render');
          }

        }

      }
    };
  }]);
<commit_msg>Performance: Adjust $watch statement so it does not run every $digest<commit_after>/**
 * @license nya-bootstrap-select v1.2.6
 * Copyright 2014 nyasoft
 * Licensed under MIT license
 */

'use strict';

angular.module('nya.bootstrap.select',[])
  .directive('nyaSelectpicker', ['$parse', function ($parse) {

    // NG_OPTIONS_REGEXP copy from angular.js select directive
    var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
    return {
      restrict: 'CA',
      scope: false,
      require: ['^ngModel', 'select'],

      link: function(scope, element, attrs, ctrls) {
        var optionsExp = attrs.ngOptions;
        var valuesFn, match, track, groupBy;
        if(optionsExp && (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
          groupBy = match[3];
          console.log(optionsExp, groupBy);
          valuesFn = $parse(match[7]);
          track = match[8];
        }
        var ngCtrl = ctrls[0];
        var selectCtrl = ctrls[1];
        // prevent selectDirective render an unknownOption.
        selectCtrl.renderUnknownOption = angular.noop;
        var optionArray = [];

        // store data- attribute options of select
        var selectorOptions = {};
        var BS_ATTR = ['container', 'countSelectedText', 'dropupAuto', 'header', 'hideDisabled', 'selectedTextFormat', 'size', 'showSubtext', 'showIcon', 'showContent', 'style', 'title', 'width', 'disabled'];

        var checkSelectorOptionsEquality = function() {
          var isEqual = true;
          angular.forEach(BS_ATTR, function(attr) {
            isEqual = isEqual && attrs[attr] === selectorOptions[attr];
          });
        };

        var updateSelectorOptions = function() {
          angular.forEach(BS_ATTR, function(attr) {
            selectorOptions[attr] = attrs[attr];
          });

          return selectorOptions;
        };

        /**
         * Check option data attributes, text and value equality.
         * @param opt the option dom element
         * @param index the index of the option
         * @returns {boolean}
         */
        var checkOptionEquality = function(opt, index) {
          var isEqual = opt.value === optionArray[index].value && opt.text === optionArray[index].text;
          if(isEqual) {
            for(var i = 0; i< opt.attributes.length; i++){
              if(opt.attributes[i].nodeName.indexOf('data-')!==-1) {
                if(optionArray[index].attributes[opt.attributes[i].nodeName] !== opt.attributes[i].nodeValue) {
                  isEqual = false;
                  break;
                }
              }
            }
          }
          return isEqual;
        };

        var resetDataProperties = function(opt) {
          var attributes = opt.attributes;
          for(var i = 0; i < attributes.length; i++) {
            if(attributes[i].nodeName.indexOf('data-')!==-1) {
              $(opt).data(attributes[i].nodeName.substring(5, attributes[i].nodeName.length), attributes[i].value);
            }
          }
        };

        function optionDOMWatch(){
          // check every option if has changed.
          var optionElements = $(element).find('option');

          //if the first option has no value and label or value an value of ?, this must be generated by ngOptions directive. Remove it.
          if(!optionElements.eq(0).html() && (optionElements.eq(0).attr('value') ==='?' || !optionElements.eq(0).attr('value'))) {
            // angular seams incorrectly remove the first element of the options. so we have to keep the ? element in the list
            // only remove this ? element when group by is provided.
            if(!!groupBy) {
              optionElements.eq(0).remove();
            }
          }

          if(optionElements.length !== optionArray.length) {
            optionArray = makeOptionArray(optionElements);
            buildSelector();
          } else {
            var hasChanged = false;
            optionElements.each(function(index, value){
              if(!checkOptionEquality(value, index)) {
                // if check fails. reset all data properties.
                resetDataProperties(value);
                hasChanged = true;
              }
            });
            if(hasChanged) {
              buildSelector();
            }
            if(!checkSelectorOptionsEquality()) {
              updateSelectorOptions();
              $(element).selectpicker('refresh');
            }
            optionArray = makeOptionArray(optionElements);
          }
        }

        scope.$watch(function(){
          // Create an object to deep inspect if anything has changed.
          // This is slow, but not as slow as calling optionDOMWatch every $digest
          return {
            ngModel: ngCtrl.$viewValue,
            options: makeOptionArray( $(element).find('option') ),
            selectors: updateSelectorOptions()
          };
        // If any of the above properties change, call optionDOMWatch.
        }, optionDOMWatch, true);

        var setValue = function(modelValue) {
          var collection = valuesFn(scope);
          if(angular.isArray(collection) && !angular.isArray(modelValue)) {
            // collection is array and single select mode
            var index = indexInArray(modelValue, collection);
            if(index > -1) {
              $(element).val(index).selectpicker('render');
            }
          } else if(angular.isArray(collection) && angular.isArray(modelValue)) {
            // collection is array and multiple select mode.
            var indexArray = [];
            for(var i = 0; i < modelValue.length; i++) {
              var indexOfOptions = indexInArray(modelValue[i], collection);
              if(indexOfOptions > -1) {
                indexArray.push(indexOfOptions);
              }
            }
            $(element).val(indexArray).selectpicker('render');
          } else if(!angular.isArray(collection) && !angular.isArray(modelValue)) {
            // collection is object and single select mode.
            var key = keyOfObject(modelValue, collection);
            if(key) {
              $(element).val(key).selectpicker('render');
            }
          } else if(!angular.isArray(collection) && angular.isArray(modelValue)) {
            // collection is object and multiple select mode.
            var keyArray = [];
            for(var j = 0; j < modelValue.length; j++) {
              var k = keyOfObject(modelValue[j], collection);
              if(k) {
                keyArray.push(k);
              }
            }
            $(element).val(keyArray).selectpicker('render');
          }
        };

        ngCtrl.$render = function() {
          // model -> view
          var data = $(element).data('selectpicker');
          if(data) {
            if(!!valuesFn && !track) {
              // transform value to index of options
              setValue(ngCtrl.$viewValue);
            } else {
              $(element).val(ngCtrl.$viewValue).selectpicker('render');
            }
          }
        };

        function indexInArray(value, array) {
          for(var i = 0; i < array.length; i++) {
            if(angular.equals(value, array[i])) {
              return i;
            }
          }
          return -1;
        }

        function keyOfObject(value, object) {
          var key = null;
          angular.forEach(object, function(v, k) {
            if(angular.equals(v, value)) {
              key = k;
            }
          });
          return key;
        }

        /**
         * Copy option value and text and data attributes to an array for future comparison.
         * @param optionElements the source option elements. a jquery objects' array.
         * @returns {Array} the copied array.
         */
        function makeOptionArray(optionElements) {
          var optionArray = [];
          optionElements.each(function(index, childNode){
            var attributes = {};
            for(var i = 0; i < childNode.attributes.length; i++) {
              if(childNode.attributes[i].nodeName.indexOf('data-')!==-1) {
                attributes[childNode.attributes[i].nodeName] = childNode.attributes[i].nodeValue;
              }
            }
            optionArray.push({
              value: childNode.value,
              text: childNode.text,
              attributes: attributes
            });
          });
          return optionArray;
        }

        function buildSelector() {
          // build new selector. if previous select exists. remove previous data and DOM
          var oldSelectPicker = $(element).data('selectpicker');
          if(oldSelectPicker) {
            oldSelectPicker.$menu.parent().remove();
            oldSelectPicker.$newElement.remove();
            $(element).removeData('selectpicker');
          }
          $(element).selectpicker();
          if(!!valuesFn && !track) {
            setValue(ngCtrl.$modelValue);
          } else {
            $(element).val(ngCtrl.$modelValue).selectpicker('render');
          }

        }

      }
    };
  }]);
<|endoftext|>


<commit_before>/**
 * This file contains all necessary Angular controller definitions for 'frontend.admin.login-history' module.
 *
 * Note that this file should only contain controllers and nothing else.
 */
(function() {
  'use strict';

  angular.module('frontend.plugins')
    .controller('AddPluginsController', [
      '_','$scope', '$rootScope','$log',
        '$state','ApiService','MessageService','DialogService',
        'KongPluginsService','PluginsService','$uibModal',
        '_plugins','_info',
      function controller(_,$scope,$rootScope, $log,
                          $state, ApiService, MessageService, DialogService,
                          KongPluginsService,PluginsService, $uibModal,
                          _plugins,_info ) {

          var info = _info.data
          var plugins_available = info.plugins.available_on_server
          var pluginOptions = new KongPluginsService().pluginOptions()

          $scope.pluginOptions = pluginOptions
          new KongPluginsService().makePluginGroups().then(function(groups){
              $scope.pluginGroups = groups
              $log.debug("Plugin Groups",$scope.pluginGroups)

              $scope.pluginGroups.forEach(function(group){
                  for(var key in group.plugins) {
                      if(!plugins_available[key]) delete group.plugins[key]
                  }
              })

              // Init
              syncPlugins(_plugins.data.data)
          })
          $scope.activeGroup = 'Authentication'
          $scope.setActiveGroup = setActiveGroup
          $scope.filterGroup = filterGroup
          $scope.onAddPlugin = onAddPlugin

          $scope.alert = {
              msg : 'Plugins added in this section will be applied to all APIs.' +
              '
If you need to add plugins to a specific API, you can do it' +
              ' in the APIs section.'
          }

          $scope.closeAlert = function() {
              $scope.alert = undefined
          }

          /**
           * -------------------------------------------------------------
           * Functions
           * -------------------------------------------------------------
           */

          function setActiveGroup(name) {
              $scope.activeGroup = name
          }

          function filterGroup(group) {
              return group.name == $scope.activeGroup
          }

          function onAddPlugin(name) {
              $uibModal.open({
                  animation: true,
                  ariaLabelledBy: 'modal-title',
                  ariaDescribedBy: 'modal-body',
                  templateUrl: '/js/plugins/modals/add-plugin-modal.html',
                  size : 'lg',
                  controller: 'AddPluginController',
                  resolve: {
                      _api : function() {
                        return null
                      },
                      _pluginName: function () {
                          return name
                      },
                      _schema: function () {
                          return PluginsService.schema(name)
                      }
                  }
              });
          }

          function findPlugin(plugins,name) {
              for(var i=0; i < plugins.length; i ++) {
                  if(plugins[i].name ===  name) {
                      return plugins[i]
                  }
              }
              return undefined
          }

          function syncPlugins(added) {

              var addedMap = added.map(function(item){
                  return item.name
              })

              $scope.pluginGroups.forEach(function(group){
                  for(var key in group.plugins) {
                      if(addedMap.indexOf(key) > -1) {
                          group.plugins[key].isAdded = true
                          var plugin = findPlugin(added,key);
                          if(plugin) {
                              for(var _key in plugin){
                                  group.plugins[key][_key] = plugin[_key]
                              }
                          }
                      }else{
                          group.plugins[key].isAdded = false
                      }
                  }
              })
          }

          function fetchPlugins() {
              PluginsService.load()
                  .then(function(res){
                      syncPlugins(res.data.data)
                  })
          }

          // Listeners
          $scope.$on('plugin.added',function(){
              fetchPlugins()
          })

          /**
           * ------------------------------------------------------------
           * Listeners
           * ------------------------------------------------------------
           */
          $scope.$on("plugin.added",function(){
              fetchPlugins()
          })

          $scope.$on("plugin.updated",function(ev,plugin){
              fetchPlugins()
          })

      }
    ])
  ;
}());
<commit_msg>Fixed typo<commit_after>/**
 * This file contains all necessary Angular controller definitions for 'frontend.admin.login-history' module.
 *
 * Note that this file should only contain controllers and nothing else.
 */
(function() {
  'use strict';

  angular.module('frontend.plugins')
    .controller('AddPluginsController', [
      '_','$scope', '$rootScope','$log',
        '$state','ApiService','MessageService','DialogService',
        'KongPluginsService','PluginsService','$uibModal',
        '_plugins','_info',
      function controller(_,$scope,$rootScope, $log,
                          $state, ApiService, MessageService, DialogService,
                          KongPluginsService,PluginsService, $uibModal,
                          _plugins,_info ) {

          var info = _info.data
          var plugins_available = info.plugins.available_on_server
          var pluginOptions = new KongPluginsService().pluginOptions()

          $scope.pluginOptions = pluginOptions
          new KongPluginsService().makePluginGroups().then(function(groups){
              $scope.pluginGroups = groups
              $log.debug("Plugin Groups",$scope.pluginGroups)

              $scope.pluginGroups.forEach(function(group){
                  for(var key in group.plugins) {
                      if(!plugins_available[key]) delete group.plugins[key]
                  }
              })

              // Init
              syncPlugins(_plugins.data.data)
          })
          $scope.activeGroup = 'Authentication'
          $scope.setActiveGroup = setActiveGroup
          $scope.filterGroup = filterGroup
          $scope.onAddPlugin = onAddPlugin

          $scope.alert = {
              msg : 'Plugins added in this section will be applied to all APIs.' +
              '
If you need to add plugins to a specific API, you can do it' +
              ' in the APIs section.'
          }

          $scope.closeAlert = function() {
              $scope.alert = undefined
          }

          /**
           * -------------------------------------------------------------
           * Functions
           * -------------------------------------------------------------
           */

          function setActiveGroup(name) {
              $scope.activeGroup = name
          }

          function filterGroup(group) {
              return group.name == $scope.activeGroup
          }

          function onAddPlugin(name) {
              $uibModal.open({
                  animation: true,
                  ariaLabelledBy: 'modal-title',
                  ariaDescribedBy: 'modal-body',
                  templateUrl: '/js/plugins/modals/add-plugin-modal.html',
                  size : 'lg',
                  controller: 'AddPluginController',
                  resolve: {
                      _api : function() {
                        return null
                      },
                      _pluginName: function () {
                          return name
                      },
                      _schema: function () {
                          return PluginsService.schema(name)
                      }
                  }
              });
          }

          function findPlugin(plugins,name) {
              for(var i=0; i < plugins.length; i ++) {
                  if(plugins[i].name ===  name) {
                      return plugins[i]
                  }
              }
              return undefined
          }

          function syncPlugins(added) {

              var addedMap = added.map(function(item){
                  return item.name
              })

              $scope.pluginGroups.forEach(function(group){
                  for(var key in group.plugins) {
                      if(addedMap.indexOf(key) > -1) {
                          group.plugins[key].isAdded = true
                          var plugin = findPlugin(added,key);
                          if(plugin) {
                              for(var _key in plugin){
                                  group.plugins[key][_key] = plugin[_key]
                              }
                          }
                      }else{
                          group.plugins[key].isAdded = false
                      }
                  }
              })
          }

          function fetchPlugins() {
              PluginsService.load()
                  .then(function(res){
                      syncPlugins(res.data.data)
                  })
          }

          // Listeners
          $scope.$on('plugin.added',function(){
              fetchPlugins()
          })

          /**
           * ------------------------------------------------------------
           * Listeners
           * ------------------------------------------------------------
           */
          $scope.$on("plugin.added",function(){
              fetchPlugins()
          })

          $scope.$on("plugin.updated",function(ev,plugin){
              fetchPlugins()
          })

      }
    ])
  ;
}());
<|endoftext|>


<commit_before>sb.include('Element.prototype.rollUp');
/**
@Name: sb.validation
@Description: Used to validate inputs
@Param: Object
validateOnKeyUp boolean should validations occurr on keyup
validator regex or function with input as argument, these are executed or matched when the input is validated
onValid(input) function Fires when the input is validate if it is valid
onInValid(input) function Fires when the input is not validate
@Example:
var eg1 = new sb.validation({
	validator : /^\d\.\d{2}$/,
	errorMessage :  'Sorry this does not match an acct number e.g. 4.32'
});

var eg2 = new sb.validation({
	validator : function(input){
		return input.value != 'ff';
	},
	errorMessage :  'Sorry the value is not ff'
});
*/
sb.validation = function(o){
	sb.objects.infuse(o, this);
};
sb.validation.prototype = {
	//func(input) or regex
	validator : null,
	//func(input)
	onValid : null,
	//func(input)
	onInValid : null,
	//string
	errorMessage : null
};

/**
@Name: sb.forms.inputValidator
@Description: Validates inputs based on validate attribute
@Param: Object
validateOnKeyUp boolean should validations occurr on keyup
validations object of regex or function properties, these are executed or matched when the input is validated
onValid(input) function Fires when the input is validate if it is valid
onInValid(input) function Fires when the input is not validate
@Example:
var validator = new sb.forms.inputValidator({
	validations : {
		acct : new sb.validation({
			validator : /^\d\.\d{2}$/,
			errorMessage :  'Sorry this does not match an acct number e.g. 4.32'
		}),
		phone : new sb.validation({
			validator :/^\d{3}-\d{3}-\d{4}$/,
			errorMessage :  'Sorry this does not match a phone number e.g. 716-877-9999'
		}),
		email : new sb.validation({
			validator : /\b(^(\S+@).+(\.\w+)$)\b/ig,
			errorMessage :  'Sorry this does not match a phone number e.g. test@test.com'
		}),
		at_least_one : new sb.validation({
			validator : function(input){
				var inputs = $("input[name='"+input.name+"']");
				return inputs.some(function(v){return v.checked;});
			},
			onValid : function(input){
				var inputs = $("input[name='"+input.name+"']");
				inputs.forEach(function(v){
					if(v.errorMessageP){
						v.errorMessageP.rollUp();
					}
				});
			},

			onInValid : function(input){
				var inputs = $("input[name='"+input.name+"']");
				sb.forms.inputValidator.prototype.onInValid(inputs.nodes[0]);
			},
			errorMessage :  'You must select at least one'
		})
	},

	validateOnKeyUp : true,

	onKeyDown : function(e) {
		//not allow pipe
		if(e.keyCode == 220){
			e.preventDefault();
		}
	}

});
var verified = validator.validateNamedChildrenWithin('#wrapper');
<input type="text" validate="acct" required="1" name="acct" />
*/
sb.forms.inputValidator = function(o){
	var self = this;
	sb.objects.infuse(o, this);
	this.validateOnKeyUp = this.validateOnKeyUp === false ? this.validateOnKeyUp : true;
	this.validateOnClick = this.validateOnClick === false ? this.validateOnClick : true;
	this.onKeyUpEvt = sb.events.add(document, 'keyup', function(e){

		if(typeof self.onKeyUp == 'function'){
			if(self.onKeyUp(e) === false){
				return;
			}
		}
		if(self.validateOnKeyUp == true){
			self._validate(e);
		}
	});

	this.onKeyUpEvt = sb.events.add(document, 'click', function(e){

		if(typeof self.onClick == 'function'){
			if(self.onClick(e) === false){
				return;
			}
		}
		if(self.validateOnClick == true && ['checkbox', 'radio'].inArray(e.target.attr('type'))){
			self._validate(e);
		}
	});

	this.onKeyDownEvt = sb.events.add(document, 'keydown', function(e){
		if(typeof self.onKeyDown == 'function'){
			var target = e.target;
			var nn = target.nodeName;
			var validate = target.attr('validate');
			if((nn == 'TEXTAREA' || e.target.nodeName == 'INPUT') && validate){
				self.onKeyDown(e);
			}

		}
	});

	this.parent = sb.forms.inputValidator.prototype;
};
sb.forms.inputValidator.prototype = {
	validateOnKeyUp: true,
	_validate : function(e){
		var input = e.nodeName ? e : e.target;

		var maxlength = input.getAttribute('maxlength');
		if(maxlength && input.value && input.value.length >= maxlength){
			input.value = input.value.substring(0, maxlength);
			if(e && e.preventDefault){
				e.preventDefault();
			}
		}

		var validate = input.attr('validate');
		var required = input.attr('required') || '0';

		if(validate){
			if(required && validate === ''){
				validate = '_required';
				this.validations['_required'] = new sb.validation({
					validator : /.*/,
					errorMessage :  'Field required'
				});
			}

			var validation  = this.validations[validate];

			if(!validation){
				return false;
			}
			var self = this;
			input.valid = false;
			//if optional

			if(input.value === '' && required === '0'){
				input.valid = true;
			} else if(input.value !== ''){
				if(validation){
					if(typeof validation.validator == 'function'){
						input.valid = validation.validator(input);
					} else {
						input.valid = input.value.match(validation.validator) ? true : false;
					}
				}
			}

			if(input.valid){
				if(typeof validation.onValid == 'function'){
					validation.onValid(input);
				} else if(self.onValid){
					self.onValid(input);
				}
			} else {
				input.errorMessage = validation.errorMessage;

				if(typeof validation.onInValid == 'function'){
					validation.onInValid(input);
				} else if(self.onInValid){
					self.onInValid(input);
				}
			}
			return input.valid;

		}
		return true;
	},
	/**
	@Name: sb.forms.inputValidator.prototype.onValid
	@Description: Fires when the input is validated and it is valid
	@Param: input The input that is valid
	@Example:
	validator.onValid = function(input){
		input.style.backgroundColor = 'lime';
	};
	*/
	onValid: function(input){
		input.style.backgroundColor = 'lime';
		if(input.errorMessageP){
			input.errorMessageP.rollUp();
			input.errorMessageP = false;
		}
	},

	/**
	@Name: sb.forms.inputValidator.prototype.onInValid
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onInValid = function(input){
		input.style.backgroundColor = 'red';
	};
	*/
	onInValid: function(input){
		input.style.backgroundColor = 'red';

		var parent = $(input.parentNode);
		if(!input.errorMessageP){
			input.errorMessageP = new sb.element({
				tag : 'p',
				innerHTML : '',
				styles : {
					color : 'red'
				}
			});
		}
		input.errorMessageP.innerHTML = input.errorMessage;
		input.errorMessageP.appendToTop(parent);
	},

	/**
	@Name: sb.forms.inputValidator.prototype.onKeyDown
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onKeyDown = function(e){};
	*/
	onKeyDown : function(){},

	/**
	@Name: sb.forms.inputValidator.prototype.onKeyDown
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onKeyUp = function(e){};
	*/
	onKeyUp : function(){},

	/**
	@Name: sb.forms.inputValidator.prototype.validateNamedChildrenWithin
	@Description: Used to validate all named elements contained within a specific
	element that have a validate property and name attribute
	@Param: el The parent element container
	@Example:
	var valid = validator.validateNamedChildrenWithin('#myDiv');
	*/
	validateNamedChildrenWithin : function(el){
		var self = this;
		return $(el).$('*[name]').every(function(n){
			return self._validate(n);
		});
	},

	/**
	@Name: sb.forms.inputValidator.prototype.validateInput
	@Description: Validate a specific input
	@Param: input The input that is valid
	@Example:
	validator.validateInput('#myInput');
	*/
	validateInput : function(input){
		return this._validate($(input));
	}

};<commit_msg>works with required="" and validate=""<commit_after>sb.include('Element.prototype.rollUp');
/**
@Name: sb.validation
@Description: Used to validate inputs
@Param: Object
validateOnKeyUp boolean should validations occurr on keyup
validator regex or function with input as argument, these are executed or matched when the input is validated
onValid(input) function Fires when the input is validate if it is valid
onInValid(input) function Fires when the input is not validate
@Example:
var eg1 = new sb.validation({
	validator : /^\d\.\d{2}$/,
	errorMessage :  'Sorry this does not match an acct number e.g. 4.32'
});

var eg2 = new sb.validation({
	validator : function(input){
		return input.value != 'ff';
	},
	errorMessage :  'Sorry the value is not ff'
});
*/
sb.validation = function(o){
	sb.objects.infuse(o, this);
};
sb.validation.prototype = {
	//func(input) or regex
	validator : null,
	//func(input)
	onValid : null,
	//func(input)
	onInValid : null,
	//string
	errorMessage : null
};

/**
@Name: sb.forms.inputValidator
@Description: Validates inputs based on validate attribute
@Param: Object
validateOnKeyUp boolean should validations occurr on keyup
validations object of regex or function properties, these are executed or matched when the input is validated
onValid(input) function Fires when the input is validate if it is valid
onInValid(input) function Fires when the input is not validate
@Example:
var validator = new sb.forms.inputValidator({
	validations : {
		acct : new sb.validation({
			validator : /^\d\.\d{2}$/,
			errorMessage :  'Sorry this does not match an acct number e.g. 4.32'
		}),
		phone : new sb.validation({
			validator :/^\d{3}-\d{3}-\d{4}$/,
			errorMessage :  'Sorry this does not match a phone number e.g. 716-877-9999'
		}),
		email : new sb.validation({
			validator : /\b(^(\S+@).+(\.\w+)$)\b/ig,
			errorMessage :  'Sorry this does not match a phone number e.g. test@test.com'
		}),
		at_least_one : new sb.validation({
			validator : function(input){
				var inputs = $("input[name='"+input.name+"']");
				return inputs.some(function(v){return v.checked;});
			},
			onValid : function(input){
				var inputs = $("input[name='"+input.name+"']");
				inputs.forEach(function(v){
					if(v.errorMessageP){
						v.errorMessageP.rollUp();
					}
				});
			},

			onInValid : function(input){
				var inputs = $("input[name='"+input.name+"']");
				sb.forms.inputValidator.prototype.onInValid(inputs.nodes[0]);
			},
			errorMessage :  'You must select at least one'
		})
	},

	validateOnKeyUp : true,

	onKeyDown : function(e) {
		//not allow pipe
		if(e.keyCode == 220){
			e.preventDefault();
		}
	}

});
var verified = validator.validateNamedChildrenWithin('#wrapper');
<input type="text" validate="acct" required="1" name="acct" />
*/
sb.forms.inputValidator = function(o){
	var self = this;
	sb.objects.infuse(o, this);
	this.validateOnKeyUp = this.validateOnKeyUp === false ? this.validateOnKeyUp : true;
	this.validateOnClick = this.validateOnClick === false ? this.validateOnClick : true;
	this.onKeyUpEvt = sb.events.add(document, 'keyup', function(e){

		if(typeof self.onKeyUp == 'function'){
			if(self.onKeyUp(e) === false){
				return;
			}
		}
		if(self.validateOnKeyUp == true){
			self._validate(e);
		}
	});

	this.onKeyUpEvt = sb.events.add(document, 'click', function(e){

		if(typeof self.onClick == 'function'){
			if(self.onClick(e) === false){
				return;
			}
		}
		if(self.validateOnClick == true && ['checkbox', 'radio'].inArray(e.target.attr('type'))){
			self._validate(e);
		}
	});

	this.onKeyDownEvt = sb.events.add(document, 'keydown', function(e){
		if(typeof self.onKeyDown == 'function'){
			var target = e.target;
			var nn = target.nodeName;
			var validate = target.attr('validate');
			if((nn == 'TEXTAREA' || e.target.nodeName == 'INPUT') && validate){
				self.onKeyDown(e);
			}

		}
	});

	this.parent = sb.forms.inputValidator.prototype;
};
sb.forms.inputValidator.prototype = {
	validateOnKeyUp: true,
	_validate : function(e){
		var input = e.nodeName ? e : e.target;

		var maxlength = input.getAttribute('maxlength');
		if(maxlength && input.value && input.value.length >= maxlength){
			input.value = input.value.substring(0, maxlength);
			if(e && e.preventDefault){
				e.preventDefault();
			}
		}

		var validate = input.attr('validate');
		var required = input.attr('required') || '0';

		if(validate || required){

			if(required && typeof validate != 'null'){
				validate = '_required';
				this.validations['_required'] = new sb.validation({
					validator : /.*/,
					errorMessage :  'Field required'
				});
			}

			var validation  = this.validations[validate];

			if(!validation){
				return false;
			}
			var self = this;
			input.valid = false;
			//if optional

			if(input.value === '' && required === '0'){
				input.valid = true;
			} else if(input.value !== ''){
				if(validation){
					if(typeof validation.validator == 'function'){
						input.valid = validation.validator(input);
					} else {
						input.valid = input.value.match(validation.validator) ? true : false;
					}
				}
			}

			if(input.valid){
				if(typeof validation.onValid == 'function'){
					validation.onValid(input);
				} else if(self.onValid){
					self.onValid(input);
				}
			} else {
				input.errorMessage = validation.errorMessage;

				if(typeof validation.onInValid == 'function'){
					validation.onInValid(input);
				} else if(self.onInValid){
					self.onInValid(input);
				}
			}
			return input.valid;

		}
		return true;
	},
	/**
	@Name: sb.forms.inputValidator.prototype.onValid
	@Description: Fires when the input is validated and it is valid
	@Param: input The input that is valid
	@Example:
	validator.onValid = function(input){
		input.style.backgroundColor = 'lime';
	};
	*/
	onValid: function(input){
		input.style.backgroundColor = 'lime';
		if(input.errorMessageP){
			input.errorMessageP.rollUp();
			input.errorMessageP = false;
		}
	},

	/**
	@Name: sb.forms.inputValidator.prototype.onInValid
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onInValid = function(input){
		input.style.backgroundColor = 'red';
	};
	*/
	onInValid: function(input){
		input.style.backgroundColor = 'red';

		var parent = $(input.parentNode);
		if(!input.errorMessageP){
			input.errorMessageP = new sb.element({
				tag : 'p',
				innerHTML : '',
				styles : {
					color : 'red'
				}
			});
		}
		input.errorMessageP.innerHTML = input.errorMessage;
		input.errorMessageP.appendToTop(parent);
	},

	/**
	@Name: sb.forms.inputValidator.prototype.onKeyDown
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onKeyDown = function(e){};
	*/
	onKeyDown : function(){},

	/**
	@Name: sb.forms.inputValidator.prototype.onKeyDown
	@Description: Fires when the input is validated and it is invalid
	@Param: input The input that is valid
	@Example:
	validator.onKeyUp = function(e){};
	*/
	onKeyUp : function(){},

	/**
	@Name: sb.forms.inputValidator.prototype.validateNamedChildrenWithin
	@Description: Used to validate all named elements contained within a specific
	element that have a validate property and name attribute
	@Param: el The parent element container
	@Example:
	var valid = validator.validateNamedChildrenWithin('#myDiv');
	*/
	validateNamedChildrenWithin : function(el){
		var self = this;
		return $(el).$('*[name]').every(function(n){
			return self._validate(n);
		});
	},

	/**
	@Name: sb.forms.inputValidator.prototype.validateInput
	@Description: Validate a specific input
	@Param: input The input that is valid
	@Example:
	validator.validateInput('#myInput');
	*/
	validateInput : function(input){
		return this._validate($(input));
	}

};<|endoftext|>


<commit_before>/**
 * Utility function averageObjectListValue
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Internal dependencies
 */
import sumObjectListValue from './sum-object-list-value.js';

/**
 * Returns the average of a given fieldName in a list.
 *
 * @since n.e.x.t
 *
 * @param {Array}  list      Array of objects or arrays.
 * @param {string} fieldName The name of the field to be averaged.
 *
 * @return {number} The average.
 */
export default function averageObjectListValue( list, fieldName ) {
	// Filter the list to remove any entries that don't have fieldName so our length is correct for division.
	const filteredList = list.filter( ( item ) => {
		return Object.keys( item ).includes( fieldName );
	} );

	const allIntegers = filteredList.every( ( item ) => {
		return Number.isInteger( item[ fieldName ] );
	} );

	// Ensure that we don't divide empty arrays by 0.
	const average = filteredList.length ? sumObjectListValue( list, fieldName ) / filteredList.length : 0;

	if ( average > 0 && allIntegers ) {
		return Math.round( average );
	}

	return average;
}
<commit_msg>Update averageObjectListValue to not filter items.<commit_after>/**
 * Utility function averageObjectListValue
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import get from 'lodash/get';
import mean from 'lodash/mean';

/**
 * Returns the average of a given fieldName in a list.
 *
 * @since n.e.x.t
 *
 * @param {Array}  list      Array of objects or arrays.
 * @param {string} fieldName The name of the field to be averaged.
 *
 * @return {number} The average.
 */
export default function averageObjectListValue( list, fieldName ) {
	if ( ! list?.length ) {
		return 0;
	}

	const values = list.map( ( item ) => get( item, fieldName, 0 ) );
	const average = mean( values );

	if ( Number.isInteger( average ) ) {
		return average;
	}

	if ( values.every( Number.isInteger ) ) {
		return Math.round( average );
	}

	return average;
}
<|endoftext|>


<commit_before>(function() {
	function createCCLoaderVersionText() {
		const LOADER_NAME = "CCLoader";

		let text = new cc.sc.Text(LOADER_NAME + " V" + versions.ccloader, {
			font: cc.sc.fontsystem.tiny
		});
		text[entries.setPosition](2, 10);
		text[entries.setAlignment](6, 3);

		text[entries.GuiConfig][entries.transition] = {
            DEFAULT: {
                state: {},
                time: 0.2,
                p: KEY_SPLINES.EASE
            },
            HIDDEN: {
                state: {
                    alpha: 0
                },
                time: 0.2,
                p: KEY_SPLINES.LINEAR
            }
        };
		return text;
	}
	const onModsLoaded = function() {
		let titleBG = cc.ig.GUI.menues.filter((e) => e[entries.GUI] instanceof cc.sc.TitleScreenBG).pop();
		if(!titleBG) {
			throw new Error('Could not find titleBG');
		}
		let versionButton = createCCLoaderVersionText();
		versionButton[entries.setGuiStateTransition]("HIDDEN", h);

		titleBG[entries.GUI][entries.addGui](versionButton);
		let oldC = titleBG[entries.GUI][entries.observerCallback];
		titleBG[entries.GUI][entries.observerCallback] = function(c,d) {
			if(c == cc.sc.playerModelInstance && d === 0) {
				let currentTransition = cc.sc.playerModelInstance[entries.isMainMenu]() ? "DEFAULT" : "HIDDEN";
				if(this[entries.GuiConfig][entries.currentTransition] != currentTransition) {
					versionButton[entries.setGuiStateTransition]("HIDDEN");
				}
			}
			oldC.apply(this, arguments);
		}

		var oldCallback = titleBG[entries.GUI][entries.titleParallax][entries.callback];

		titleBG[entries.GUI][entries.titleParallax][entries.callback] = function(a,b) {

			if(a === 1 && b === "IDLE") {
				versionButton[entries.setGuiStateTransition]("DEFAULT");
			}
			oldCallback.apply(this, arguments);
		}
		document.body.removeEventListener("modsLoaded", onModsLoaded);
	};

	document.body.addEventListener("modsLoaded", onModsLoaded);
})()
<commit_msg>Fixed issues<commit_after>(function() {
	function createCCLoaderVersionText() {
		const LOADER_NAME = "CCLoader";

		let text = new cc.sc.Text(LOADER_NAME + " V" + versions.ccloader, {
			font: cc.sc.fontsystem.tiny
		});
		text[entries.setPosition](2, 10);
		text[entries.setAlignment](6, 3);

		text[entries.GuiConfig][entries.transition] = {
            DEFAULT: {
                state: {},
                time: 0.2,
                [entries.keySpline] : KEY_SPLINES.EASE
            },
            HIDDEN: {
                state: {
                    alpha: 0
                },
                time: 0.2,
                [entries.keySpline]: KEY_SPLINES.LINEAR
            }
        };
		return text;
	}

	document.body.addEventListener("modsLoaded", () => {
		let titleBG = cc.ig.GUI.menues.filter((e) => e[entries.GUI] instanceof cc.sc.TitleScreenBG).pop();
		if(!titleBG) {
			throw new Error('Could not find titleBG');
		}
		let versionText = createCCLoaderVersionText();
		versionText[entries.setGuiStateTransition]("HIDDEN", h);

		titleBG[entries.GUI][entries.addGui](versionText);

		let oldGuiCallback = titleBG[entries.GUI][entries.observerCallback];

		titleBG[entries.GUI][entries.observerCallback] = function(object,eventCode) {
			// eventCode === CHANGED_STATE
			if(object == cc.sc.playerModelInstance && eventCode === 0) {
				let currentTransition = cc.sc.playerModelInstance[entries.isMainMenu]() ? "DEFAULT" : "HIDDEN";
				if(this[entries.GuiConfig][entries.currentTransition] != currentTransition) {
					versionText[entries.setGuiStateTransition]("HIDDEN");
				}
			}
			oldGuiCallback.apply(this, arguments);
		}

		let oldTitleCallback = titleBG[entries.GUI][entries.titleParallax][entries.callback];

		titleBG[entries.GUI][entries.titleParallax][entries.callback] = function(transitionState, transitionType) {
			// transitionState === ADD (my guess)
			if(transitionState === 1 && transitionType === "IDLE") {
				versionText[entries.setGuiStateTransition]("DEFAULT");
			}
			oldTitleCallback.apply(this, arguments);
		}
	});

})()
<|endoftext|>


<commit_before>;(function ( window, undefined ) {

    var lastId = 0;

    var Cell = function Cell ( options ) {

        this._id = lastId++;
        this._value = undefined;
        this.deps = [];
        this.usage = [];

        if ( typeof options[ 0 ] == 'function' ) {
            this.formula = options[ 0 ];
            this.usage = options.slice( 1 );
            this.utils.each( this.usage, function ( cell, index ) {
                cell.deps.push( this );
            }, this );
        } else {
            this._value = options[ 0 ];
        }

        this.calculate();

    };

    Cell.prototype.set = function ( val ) {

        this._value = val;
        this.updateDeps();

    };

    Cell.prototype.value = function () {

        return this._value;

    };

    Cell.prototype.calculate = function () {

        if ( this.formula ) {
            this._value = this.formula.apply( this, this.getUsageValues() );
            this.updateDeps();
        }

    };

    Cell.prototype.getUsageValues = function () {

        var result = [];

        this.utils.each( this.usage, function ( cell, index ) {
            result[ index ] = cell.value();
        }, this );

        return result;

    };

    Cell.prototype.updateDeps = function () {

        this.utils.each( this.deps, function ( cell ) {
            cell.calculate();
        }, this );

    };

    Cell.prototype.getDepIndexById = function ( from, id ) {

        var result = false;

        this.utils.each( from, function ( cell, index ) {
            if ( cell._id == id ) {
                result = index;
            }
        }, this );

        return result;

    };

    Cell.prototype.delete = function () {

        this.utils.each( this.usage, function ( cell ) {
            var index = this.getDepIndexById( cell.deps, this._id );
            if ( index !== false ) {
                cell.deps.splice( index, 1 );
            }
        }, this );

    };

    Cell.prototype.utils = {

        each: function ( arr, fn, ctx ) {

            var i = 0,
                len = arr.length;

            if ( !arr || !arr.length ) { return false; }

            for ( i; i < len; i++ ) {
                fn.call( ctx || Cell, arr[ i ], i, arr );
            }

        }

    };

    var cell = function () {
        return new Cell( Array.prototype.slice.call( arguments, 0 ) );
    };

    window.cell = cell;

})( window );<commit_msg>Node.js and AMD support<commit_after>;(function ( window, undefined ) {

    var lastId = 0;

    var Cell = function Cell ( options ) {

        this._id = lastId++;
        this._value = undefined;
        this.deps = [];
        this.usage = [];

        if ( typeof options[ 0 ] === 'function' ) {
            this.formula = options[ 0 ];
            this.usage = options.slice( 1 );
            this.utils.each( this.usage, function ( cell, index ) {
                cell.deps.push( this );
            }, this );
        } else {
            this._value = options[ 0 ];
        }

        this.calculate();

    };

    Cell.prototype.set = function ( val ) {

        this._value = val;
        this.updateDeps();

    };

    Cell.prototype.value = function () {

        return this._value;

    };

    Cell.prototype.calculate = function () {

        if ( this.formula ) {
            this._value = this.formula.apply( this, this.getUsageValues() );
            this.updateDeps();
        }

    };

    Cell.prototype.getUsageValues = function () {

        var result = [];

        this.utils.each( this.usage, function ( cell, index ) {
            result[ index ] = cell.value();
        }, this );

        return result;

    };

    Cell.prototype.updateDeps = function () {

        this.utils.each( this.deps, function ( cell ) {
            cell.calculate();
        }, this );

    };

    Cell.prototype.getDepIndexById = function ( from, id ) {

        var result = false;

        this.utils.each( from, function ( cell, index ) {
            if ( cell._id === id ) {
                result = index;
            }
        }, this );

        return result;

    };

    Cell.prototype.delete = function () {

        this.utils.each( this.usage, function ( cell ) {
            var index = this.getDepIndexById( cell.deps, this._id );
            if ( index !== false ) {
                cell.deps.splice( index, 1 );
            }
        }, this );

    };

    Cell.prototype.utils = {

        each: function ( arr, fn, ctx ) {

            var i = 0,
                len = arr.length;

            if ( !arr || !arr.length ) { return false; }

            for ( i; i < len; i++ ) {
                fn.call( ctx || Cell, arr[ i ], i, arr );
            }

        }

    };

    var cell = function () {
        return new Cell( Array.prototype.slice.call( arguments, 0 ) );
    };

    if ( typeof module === 'object' && module && typeof module.exports === 'object' ) {
        module.exports = cell;
    } else if ( typeof define === 'function' && define.amd ) {
        define( 'cell', [], function () { return cell; } );
    } else if ( window ) {
        window.cell = cell;
    }

})( window );<|endoftext|>


<commit_before>goog.provide('og.webgl.Framebuffer');

goog.require('og.webgl');
goog.require('og.ImageCanvas');

og.webgl.Framebuffer = function (gl, width, height) {
    this.gl = gl;
    this.fbo;
    this.width = width || 256;
    this.height = height || 256;
    this.texture = null;
};

og.webgl.Framebuffer.prototype.initialize = function () {
    var gl = this.gl;
    this.width = gl.canvas.clientWidth || this.width;
    this.height = gl.canvas.clientHeight || this.height;
    this.fbo = gl.createFramebuffer();
    this._createTexture();
};

og.webgl.Framebuffer.prototype._createTexture = function () {
    var gl = this.gl;
    gl.deleteTexture(this.texture);

    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

    //gl.generateMipmap(gl.TEXTURE_2D);
    //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

    var renderbuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

    gl.bindTexture(gl.TEXTURE_2D, null);
    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

og.webgl.Framebuffer.prototype.setSize = function (width, height) {
    this.width = width;
    this.height = height;
    this.gl.deleteFramebuffer(this.fbo);
    this.initialize();
};

og.webgl.Framebuffer.prototype.isComplete = function () {
    var gl = this.gl;
    var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status == gl.FRAMEBUFFER_COMPLETE)
        return true;
    return false;
};

og.webgl.Framebuffer.prototype.readAllPixels = function () {
    var res;
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    //if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
    var pixelValues = new Uint8Array(4 * this.width * this.height);
    gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
    res = pixelValues;
    //}
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    return res;
};

og.webgl.Framebuffer.prototype.readPixel = function (x, y) {
    var res;
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    //if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
    var pixelValues = new Uint8Array(4);
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
    res = pixelValues;
    //}
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    return res;
};

og.webgl.Framebuffer.prototype.activate = function () {
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
};

og.webgl.Framebuffer.prototype.clear = function () {
    var gl = this.gl;
    //gl.viewport(0, 0, this.width, this.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};

og.webgl.Framebuffer.prototype.deactivate = function () {
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

og.webgl.Framebuffer.prototype.getImage = function () {
    var data = this.readAllPixels();
    var imageCanvas = new og.ImageCanvas(this.width, this.height);
    imageCanvas.setData(data);
    return imageCanvas.getImage();
};<commit_msg>Can framebuffer's image to open in another window.<commit_after>goog.provide('og.webgl.Framebuffer');

goog.require('og.webgl');
goog.require('og.ImageCanvas');

og.webgl.Framebuffer = function (gl, width, height) {
    this.gl = gl;
    this.fbo;
    this.width = width || 256;
    this.height = height || 256;
    this.texture = null;
};

og.webgl.Framebuffer.prototype.initialize = function () {
    var gl = this.gl;
    this.width = gl.canvas.clientWidth || this.width;
    this.height = gl.canvas.clientHeight || this.height;
    this.fbo = gl.createFramebuffer();
    this._createTexture();
};

og.webgl.Framebuffer.prototype._createTexture = function () {
    var gl = this.gl;
    gl.deleteTexture(this.texture);

    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

    //gl.generateMipmap(gl.TEXTURE_2D);
    //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

    var renderbuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

    gl.bindTexture(gl.TEXTURE_2D, null);
    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

og.webgl.Framebuffer.prototype.setSize = function (width, height) {
    this.width = width;
    this.height = height;
    this.gl.deleteFramebuffer(this.fbo);
    this.initialize();
};

og.webgl.Framebuffer.prototype.isComplete = function () {
    var gl = this.gl;
    var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status == gl.FRAMEBUFFER_COMPLETE)
        return true;
    return false;
};

og.webgl.Framebuffer.prototype.readAllPixels = function () {
    var res;
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    //if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
    var pixelValues = new Uint8Array(4 * this.width * this.height);
    gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
    res = pixelValues;
    //}
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    return res;
};

og.webgl.Framebuffer.prototype.readPixel = function (x, y) {
    var res;
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    //if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
    var pixelValues = new Uint8Array(4);
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
    res = pixelValues;
    //}
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    return res;
};

og.webgl.Framebuffer.prototype.activate = function () {
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
};

og.webgl.Framebuffer.prototype.clear = function () {
    var gl = this.gl;
    //gl.viewport(0, 0, this.width, this.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};

og.webgl.Framebuffer.prototype.deactivate = function () {
    var gl = this.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

og.webgl.Framebuffer.prototype.getImage = function () {
    var data = this.readAllPixels();
    var imageCanvas = new og.ImageCanvas(this.width, this.height);
    imageCanvas.setData(data);
    return imageCanvas.getImage();
};

og.webgl.Framebuffer.prototype.openImage = function () {
    var img = this.getImage();
    var dataUrl = img.src;
    var windowContent = '<!DOCTYPE html>';
    windowContent += ''
    windowContent += '<title>Print</title>';
    windowContent += ''
    windowContent += '';
    windowContent += '';
    windowContent += '';
    var printWin = window.open('', '', 'width=' + img.width + 'px ,height=' + img.height + 'px');
    printWin.document.open();
    printWin.document.write(windowContent);
    printWin.document.close();
    printWin.focus();
};<|endoftext|>


<commit_before>/**
 * @module ol/layer/VectorImage
 */
import BaseVectorLayer from './BaseVector.js';
import {assign} from '../obj.js';
import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js';

/**
 * @typedef {import("./BaseVector.js").Options} Options
 * @property {number} [imageRatio=1] Ratio by which the rendered extent should be larger than the
 * viewport extent. A larger ratio avoids cut images during panning, but will cause a decrease in performance.
 */

/**
 * @classdesc
 * Vector data that is rendered client-side.
 * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject}
 * property on the layer object; for example, setting `title: 'My Title'` in the
 * options means that `title` is observable, and has get/set accessors.
 *
 * @api
 */
class VectorImageLayer extends BaseVectorLayer {
  /**
   * @param {Options=} opt_options Options.
   */
  constructor(opt_options) {
    const options = opt_options ? opt_options : /** @type {Options} */ ({});

    const baseOptions = assign({}, options);
    delete baseOptions.imageRatio;
    super(baseOptions);

    /**
     * @type {number}
     * @private
     */
    this.imageRatio_ = options.imageRatio !== undefined ? options.imageRatio : 1;

  }

  /**
   * @return {number} Ratio between rendered extent size and viewport extent size.
   */
  getImageRatio() {
    return this.imageRatio_;
  }

  /**
   * Create a renderer for this layer.
   * @return {import("../renderer/Layer.js").default} A layer renderer.
   * @protected
   */
  createRenderer() {
    return new CanvasVectorImageLayerRenderer(this);
  }
}

export default VectorImageLayer;
<commit_msg>Full type definition for image vector layer options<commit_after>/**
 * @module ol/layer/VectorImage
 */
import BaseVectorLayer from './BaseVector.js';
import {assign} from '../obj.js';
import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js';

/**
 * @typedef {Object} Options
 * @property {number} [opacity=1] Opacity (0, 1).
 * @property {boolean} [visible=true] Visibility.
 * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering.  The layer will not be
 * rendered outside of this extent.
 * @property {number} [zIndex] The z-index for layer rendering.  At rendering time, the layers
 * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed
 * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`
 * method was used.
 * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be
 * visible.
 * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
 * be visible.
 * @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
 * features before rendering. By default features are drawn in the order that they are created. Use
 * `null` to avoid the sort, but get an undefined draw order.
 * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the
 * renderer when getting features from the vector source for the rendering or hit-detection.
 * Recommended value: the size of the largest symbol, line width or label.
 * @property {import("../source/Vector.js").default} [source] Source.
 * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
 * this layer in its layers collection, and the layer will be rendered on top. This is useful for
 * temporary layers. The standard way to add a layer to a map and have it managed by the map is to
 * use {@link module:ol/Map#addLayer}.
 * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
 * image and text styles, and the priority is defined by the z-index of the style. Lower z-index
 * means higher priority.
 * @property {import("../style/Style.js").StyleLike} [style] Layer style. See
 * {@link module:ol/style} for default style which will be used if this is not defined.
 * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will
 * be recreated during animations. This means that no vectors will be shown clipped, but the
 * setting will have a performance impact for large amounts of vector data. When set to `false`,
 * batches will be recreated when no animation is active.
 * @property {boolean} [updateWhileInteracting=false] When set to `true`, feature batches will
 * be recreated during interactions. See also `updateWhileAnimating`.
 * @property {number} [imageRatio=1] Ratio by which the rendered extent should be larger than the
 * viewport extent. A larger ratio avoids cut images during panning, but will cause a decrease in performance.
 */

/**
 * @classdesc
 * Vector data that is rendered client-side.
 * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject}
 * property on the layer object; for example, setting `title: 'My Title'` in the
 * options means that `title` is observable, and has get/set accessors.
 *
 * @api
 */
class VectorImageLayer extends BaseVectorLayer {
  /**
   * @param {Options=} opt_options Options.
   */
  constructor(opt_options) {
    const options = opt_options ? opt_options : /** @type {Options} */ ({});

    const baseOptions = assign({}, options);
    delete baseOptions.imageRatio;
    super(baseOptions);

    /**
     * @type {number}
     * @private
     */
    this.imageRatio_ = options.imageRatio !== undefined ? options.imageRatio : 1;

  }

  /**
   * @return {number} Ratio between rendered extent size and viewport extent size.
   */
  getImageRatio() {
    return this.imageRatio_;
  }

  /**
   * Create a renderer for this layer.
   * @return {import("../renderer/Layer.js").default} A layer renderer.
   * @protected
   */
  createRenderer() {
    return new CanvasVectorImageLayerRenderer(this);
  }
}

export default VectorImageLayer;
<|endoftext|>


<commit_before>// FIXME cope with tile grids whose minium zoom is not zero

goog.provide('ol.tilegrid.TileGrid');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.PixelBounds');
goog.require('ol.Projection');
goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileRange');
goog.require('ol.array');

/**
 * @define {number} Default tile size.
 */
ol.DEFAULT_TILE_SIZE = 256;

/**
 * @define {number} Default maximum zoom for default tile grids.
 */
ol.DEFAULT_MAX_ZOOM = 42;

/**
 * @constructor
 * @param {ol.tilegrid.TileGridOptions} tileGridOptions Tile grid options.
 */
ol.tilegrid.TileGrid = function(tileGridOptions) {

  /**
   * @private
   * @type {!Array.<number>}
   */
  this.resolutions_ = tileGridOptions.resolutions;
  goog.asserts.assert(goog.array.isSorted(this.resolutions_, function(a, b) {
    return b - a;
  }, true));

  /**
   * @private
   * @type {number}
   */
  this.numResolutions_ = this.resolutions_.length;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.origin_ = goog.isDef(tileGridOptions.origin) ?
      tileGridOptions.origin : null;

  /**
   * @private
   * @type {Array.<ol.Coordinate>}
   */
  this.origins_ = null;
  if (goog.isDef(tileGridOptions.origins)) {
    this.origins_ = tileGridOptions.origins;
    goog.asserts.assert(this.origins_.length == this.resolutions_.length);
  }
  goog.asserts.assert(
      (goog.isNull(this.origin_) && !goog.isNull(this.origins_)) ||
      (!goog.isNull(this.origin_) && goog.isNull(this.origins_)));

  /**
   * @private
   * @type {Array.<ol.Size>}
   */
  this.tileSizes_ = null;
  if (goog.isDef(tileGridOptions.tileSizes)) {
    this.tileSizes_ = tileGridOptions.tileSizes;
    goog.asserts.assert(this.tileSizes_.length == this.resolutions_.length);
  }

  /**
   * @private
   * @type {ol.Size}
   */
  this.tileSize_ = goog.isDef(tileGridOptions.tileSize) ?
      tileGridOptions.tileSize :
      goog.isNull(this.tileSizes_) ?
          new ol.Size(ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE) : null;
  goog.asserts.assert(
      (goog.isNull(this.tileSize_) && !goog.isNull(this.tileSizes_)) ||
      (!goog.isNull(this.tileSize_) && goog.isNull(this.tileSizes_)));

};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @param {function(this: T, number, ol.TileRange): boolean} callback Callback.
 * @param {T=} opt_obj Object.
 * @template T
 */
ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange =
    function(tileCoord, callback, opt_obj) {
  var tileCoordExtent = this.getTileCoordExtent(tileCoord);
  var z = tileCoord.z - 1;
  while (z >= 0) {
    if (callback.call(
        opt_obj, z, this.getTileRangeForExtentAndZ(tileCoordExtent, z))) {
      return;
    }
    --z;
  }
};

/**
 * @param {number} z Z.
 * @return {ol.Coordinate} Origin.
 */
ol.tilegrid.TileGrid.prototype.getOrigin = function(z) {
  if (!goog.isNull(this.origin_)) {
    return this.origin_;
  } else {
    goog.asserts.assert(!goog.isNull(this.origins_));
    goog.asserts.assert(0 <= z && z < this.origins_.length);
    return this.origins_[z];
  }
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @param {number} resolution Resolution.
 * @return {ol.PixelBounds} Pixel bounds.
 */
ol.tilegrid.TileGrid.prototype.getPixelBoundsForTileCoordAndResolution =
    function(tileCoord, resolution) {
  var scale = resolution / this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  tileSize = new ol.Size(tileSize.width / scale,
                         tileSize.height / scale);
  var minX, maxX, minY, maxY;
  minX = Math.round(tileCoord.x * tileSize.width);
  maxX = Math.round((tileCoord.x + 1) * tileSize.width);
  minY = Math.round(tileCoord.y * tileSize.height);
  maxY = Math.round((tileCoord.y + 1) * tileSize.height);
  return new ol.PixelBounds(minX, minY, maxX, maxY);
};

/**
 * @param {number} z Z.
 * @return {number} Resolution.
 */
ol.tilegrid.TileGrid.prototype.getResolution = function(z) {
  goog.asserts.assert(0 <= z && z < this.numResolutions_);
  return this.resolutions_[z];
};

/**
 * @return {Array.<number>} Resolutions.
 */
ol.tilegrid.TileGrid.prototype.getResolutions = function() {
  return this.resolutions_;
};

/**
 * @param {number} z Z.
 * @param {ol.TileRange} tileRange Tile range.
 * @return {ol.Extent} Extent.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange) {
  var origin = this.getOrigin(z);
  var resolution = this.getResolution(z);
  var tileSize = this.getTileSize(z);
  var minX = origin.x + tileRange.minX * tileSize.width * resolution;
  var minY = origin.y + tileRange.minY * tileSize.height * resolution;
  var maxX = origin.x + (tileRange.maxX + 1) * tileSize.width * resolution;
  var maxY = origin.y + (tileRange.maxY + 1) * tileSize.height * resolution;
  return new ol.Extent(minX, minY, maxX, maxY);
};

/**
 * @param {ol.Extent} extent Extent.
 * @param {number} resolution Resolution.
 * @return {ol.TileRange} Tile range.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(
    extent, resolution) {
  var min = this.getTileCoordForCoordAndResolution_(
      new ol.Coordinate(extent.minX, extent.minY), resolution);
  var max = this.getTileCoordForCoordAndResolution_(
      new ol.Coordinate(extent.maxX, extent.maxY), resolution, true);
  return new ol.TileRange(min.x, min.y, max.x, max.y);
};

/**
 * @param {ol.Extent} extent Extent.
 * @param {number} z Z.
 * @return {ol.TileRange} Tile range.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z) {
  var resolution = this.getResolution(z);
  return this.getTileRangeForExtentAndResolution(extent, resolution);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {ol.Coordinate} Tile center.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) {
  var origin = this.getOrigin(tileCoord.z);
  var resolution = this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  var x = origin.x + (tileCoord.x + 0.5) * tileSize.width * resolution;
  var y = origin.y + (tileCoord.y + 0.5) * tileSize.height * resolution;
  return new ol.Coordinate(x, y);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {ol.Extent} Extent.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord) {
  var origin = this.getOrigin(tileCoord.z);
  var resolution = this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  var minX = origin.x + tileCoord.x * tileSize.width * resolution;
  var minY = origin.y + tileCoord.y * tileSize.height * resolution;
  var maxX = minX + tileSize.width * resolution;
  var maxY = minY + tileSize.height * resolution;
  return new ol.Extent(minX, minY, maxX, maxY);
};

/**
 * Get the tile coordinate for the given map coordinate and resolution.  This
 * method considers that coordinates that intersect tile boundaries should be
 * assigned the higher tile coordinate.
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @return {ol.TileCoord} Tile coordinate.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(
    coordinate, resolution) {
  return this.getTileCoordForCoordAndResolution_(coordinate, resolution);
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @param {boolean=} opt_reverseIntersectionPolicy Instead of letting edge
 *     intersections go to the higher tile coordinate, let edge intersections
 *     go to the lower tile coordinate.
 * @return {ol.TileCoord} Tile coordinate.
 * @private
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution_ = function(
    coordinate, resolution, opt_reverseIntersectionPolicy) {
  var z = this.getZForResolution(resolution);
  var scale = resolution / this.getResolution(z);
  var origin = this.getOrigin(z);
  var tileSize = this.getTileSize(z);

  var x = scale * (coordinate.x - origin.x) / (resolution * tileSize.width);
  var y = scale * (coordinate.y - origin.y) / (resolution * tileSize.height);

  if (!opt_reverseIntersectionPolicy) {
    x = Math.floor(x);
    y = Math.floor(y);
  } else {
    x = Math.ceil(x) - 1;
    y = Math.ceil(y) - 1;
  }

  return new ol.TileCoord(z, x, y);
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} z Z.
 * @return {ol.TileCoord} Tile coordinate.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ =
    function(coordinate, z) {
  var resolution = this.getResolution(z);
  return this.getTileCoordForCoordAndResolution_(coordinate, resolution);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {number} Tile resolution.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) {
  goog.asserts.assert(0 <= tileCoord.z && tileCoord.z < this.numResolutions_);
  return this.resolutions_[tileCoord.z];
};

/**
 * @param {number} z Z.
 * @return {ol.Size} Tile size.
 */
ol.tilegrid.TileGrid.prototype.getTileSize = function(z) {
  if (!goog.isNull(this.tileSize_)) {
    return this.tileSize_;
  } else {
    goog.asserts.assert(!goog.isNull(this.tileSizes_));
    goog.asserts.assert(0 <= z && z < this.tileSizes_.length);
    return this.tileSizes_[z];
  }
};

/**
 * @param {number} resolution Resolution.
 * @return {number} Z.
 */
ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) {
  return ol.array.linearFindNearest(this.resolutions_, resolution);
};

/**
 * @param {ol.Projection} projection Projection.
 * @return {ol.tilegrid.TileGrid} Default tile grid for the passed projection.
 */
ol.tilegrid.getForProjection = function(projection) {
  var tileGrid = projection.getDefaultTileGrid();
  if (goog.isNull(tileGrid)) {
    tileGrid = ol.tilegrid.createForProjection(projection);
    projection.setDefaultTileGrid(tileGrid);
  }
  return tileGrid;
};

/**
 * @param {ol.Projection} projection Projection.
 * @param {number=} opt_maxZoom Maximum zoom level.
 * @param {ol.Size=} opt_tileSize Tile size.
 * @return {ol.tilegrid.TileGrid} TileGrid instance.
 */
ol.tilegrid.createForProjection =
    function(projection, opt_maxZoom, opt_tileSize) {
  var projectionExtent = projection.getExtent();
  var size = Math.max(
      projectionExtent.maxX - projectionExtent.minX,
      projectionExtent.maxY - projectionExtent.minY);
  var maxZoom = goog.isDef(opt_maxZoom) ?
      opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
  var tileSize = goog.isDef(opt_tileSize) ?
      opt_tileSize : new ol.Size(ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE);
  var resolutions = new Array(maxZoom + 1);
  goog.asserts.assert(tileSize.width == tileSize.height);
  size = size / tileSize.width;
  for (var z = 0, zz = resolutions.length; z < zz; ++z) {
    resolutions[z] = size / Math.pow(2, z);
  }
  return new ol.tilegrid.TileGrid({
    origin: projectionExtent.getTopLeft(),
    resolutions: resolutions,
    tileSize: tileSize
  });
};
<commit_msg>Fixing origin for default tile grid<commit_after>// FIXME cope with tile grids whose minium zoom is not zero

goog.provide('ol.tilegrid.TileGrid');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.PixelBounds');
goog.require('ol.Projection');
goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileRange');
goog.require('ol.array');

/**
 * @define {number} Default tile size.
 */
ol.DEFAULT_TILE_SIZE = 256;

/**
 * @define {number} Default maximum zoom for default tile grids.
 */
ol.DEFAULT_MAX_ZOOM = 42;

/**
 * @constructor
 * @param {ol.tilegrid.TileGridOptions} tileGridOptions Tile grid options.
 */
ol.tilegrid.TileGrid = function(tileGridOptions) {

  /**
   * @private
   * @type {!Array.<number>}
   */
  this.resolutions_ = tileGridOptions.resolutions;
  goog.asserts.assert(goog.array.isSorted(this.resolutions_, function(a, b) {
    return b - a;
  }, true));

  /**
   * @private
   * @type {number}
   */
  this.numResolutions_ = this.resolutions_.length;

  /**
   * @private
   * @type {ol.Coordinate}
   */
  this.origin_ = goog.isDef(tileGridOptions.origin) ?
      tileGridOptions.origin : null;

  /**
   * @private
   * @type {Array.<ol.Coordinate>}
   */
  this.origins_ = null;
  if (goog.isDef(tileGridOptions.origins)) {
    this.origins_ = tileGridOptions.origins;
    goog.asserts.assert(this.origins_.length == this.resolutions_.length);
  }
  goog.asserts.assert(
      (goog.isNull(this.origin_) && !goog.isNull(this.origins_)) ||
      (!goog.isNull(this.origin_) && goog.isNull(this.origins_)));

  /**
   * @private
   * @type {Array.<ol.Size>}
   */
  this.tileSizes_ = null;
  if (goog.isDef(tileGridOptions.tileSizes)) {
    this.tileSizes_ = tileGridOptions.tileSizes;
    goog.asserts.assert(this.tileSizes_.length == this.resolutions_.length);
  }

  /**
   * @private
   * @type {ol.Size}
   */
  this.tileSize_ = goog.isDef(tileGridOptions.tileSize) ?
      tileGridOptions.tileSize :
      goog.isNull(this.tileSizes_) ?
          new ol.Size(ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE) : null;
  goog.asserts.assert(
      (goog.isNull(this.tileSize_) && !goog.isNull(this.tileSizes_)) ||
      (!goog.isNull(this.tileSize_) && goog.isNull(this.tileSizes_)));

};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @param {function(this: T, number, ol.TileRange): boolean} callback Callback.
 * @param {T=} opt_obj Object.
 * @template T
 */
ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange =
    function(tileCoord, callback, opt_obj) {
  var tileCoordExtent = this.getTileCoordExtent(tileCoord);
  var z = tileCoord.z - 1;
  while (z >= 0) {
    if (callback.call(
        opt_obj, z, this.getTileRangeForExtentAndZ(tileCoordExtent, z))) {
      return;
    }
    --z;
  }
};

/**
 * @param {number} z Z.
 * @return {ol.Coordinate} Origin.
 */
ol.tilegrid.TileGrid.prototype.getOrigin = function(z) {
  if (!goog.isNull(this.origin_)) {
    return this.origin_;
  } else {
    goog.asserts.assert(!goog.isNull(this.origins_));
    goog.asserts.assert(0 <= z && z < this.origins_.length);
    return this.origins_[z];
  }
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @param {number} resolution Resolution.
 * @return {ol.PixelBounds} Pixel bounds.
 */
ol.tilegrid.TileGrid.prototype.getPixelBoundsForTileCoordAndResolution =
    function(tileCoord, resolution) {
  var scale = resolution / this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  tileSize = new ol.Size(tileSize.width / scale,
                         tileSize.height / scale);
  var minX, maxX, minY, maxY;
  minX = Math.round(tileCoord.x * tileSize.width);
  maxX = Math.round((tileCoord.x + 1) * tileSize.width);
  minY = Math.round(tileCoord.y * tileSize.height);
  maxY = Math.round((tileCoord.y + 1) * tileSize.height);
  return new ol.PixelBounds(minX, minY, maxX, maxY);
};

/**
 * @param {number} z Z.
 * @return {number} Resolution.
 */
ol.tilegrid.TileGrid.prototype.getResolution = function(z) {
  goog.asserts.assert(0 <= z && z < this.numResolutions_);
  return this.resolutions_[z];
};

/**
 * @return {Array.<number>} Resolutions.
 */
ol.tilegrid.TileGrid.prototype.getResolutions = function() {
  return this.resolutions_;
};

/**
 * @param {number} z Z.
 * @param {ol.TileRange} tileRange Tile range.
 * @return {ol.Extent} Extent.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange) {
  var origin = this.getOrigin(z);
  var resolution = this.getResolution(z);
  var tileSize = this.getTileSize(z);
  var minX = origin.x + tileRange.minX * tileSize.width * resolution;
  var minY = origin.y + tileRange.minY * tileSize.height * resolution;
  var maxX = origin.x + (tileRange.maxX + 1) * tileSize.width * resolution;
  var maxY = origin.y + (tileRange.maxY + 1) * tileSize.height * resolution;
  return new ol.Extent(minX, minY, maxX, maxY);
};

/**
 * @param {ol.Extent} extent Extent.
 * @param {number} resolution Resolution.
 * @return {ol.TileRange} Tile range.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(
    extent, resolution) {
  var min = this.getTileCoordForCoordAndResolution_(
      new ol.Coordinate(extent.minX, extent.minY), resolution);
  var max = this.getTileCoordForCoordAndResolution_(
      new ol.Coordinate(extent.maxX, extent.maxY), resolution, true);
  return new ol.TileRange(min.x, min.y, max.x, max.y);
};

/**
 * @param {ol.Extent} extent Extent.
 * @param {number} z Z.
 * @return {ol.TileRange} Tile range.
 */
ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z) {
  var resolution = this.getResolution(z);
  return this.getTileRangeForExtentAndResolution(extent, resolution);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {ol.Coordinate} Tile center.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) {
  var origin = this.getOrigin(tileCoord.z);
  var resolution = this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  var x = origin.x + (tileCoord.x + 0.5) * tileSize.width * resolution;
  var y = origin.y + (tileCoord.y + 0.5) * tileSize.height * resolution;
  return new ol.Coordinate(x, y);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {ol.Extent} Extent.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord) {
  var origin = this.getOrigin(tileCoord.z);
  var resolution = this.getResolution(tileCoord.z);
  var tileSize = this.getTileSize(tileCoord.z);
  var minX = origin.x + tileCoord.x * tileSize.width * resolution;
  var minY = origin.y + tileCoord.y * tileSize.height * resolution;
  var maxX = minX + tileSize.width * resolution;
  var maxY = minY + tileSize.height * resolution;
  return new ol.Extent(minX, minY, maxX, maxY);
};

/**
 * Get the tile coordinate for the given map coordinate and resolution.  This
 * method considers that coordinates that intersect tile boundaries should be
 * assigned the higher tile coordinate.
 *
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @return {ol.TileCoord} Tile coordinate.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(
    coordinate, resolution) {
  return this.getTileCoordForCoordAndResolution_(coordinate, resolution);
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @param {boolean=} opt_reverseIntersectionPolicy Instead of letting edge
 *     intersections go to the higher tile coordinate, let edge intersections
 *     go to the lower tile coordinate.
 * @return {ol.TileCoord} Tile coordinate.
 * @private
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution_ = function(
    coordinate, resolution, opt_reverseIntersectionPolicy) {
  var z = this.getZForResolution(resolution);
  var scale = resolution / this.getResolution(z);
  var origin = this.getOrigin(z);
  var tileSize = this.getTileSize(z);

  var x = scale * (coordinate.x - origin.x) / (resolution * tileSize.width);
  var y = scale * (coordinate.y - origin.y) / (resolution * tileSize.height);

  if (!opt_reverseIntersectionPolicy) {
    x = Math.floor(x);
    y = Math.floor(y);
  } else {
    x = Math.ceil(x) - 1;
    y = Math.ceil(y) - 1;
  }

  return new ol.TileCoord(z, x, y);
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} z Z.
 * @return {ol.TileCoord} Tile coordinate.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ =
    function(coordinate, z) {
  var resolution = this.getResolution(z);
  return this.getTileCoordForCoordAndResolution_(coordinate, resolution);
};

/**
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @return {number} Tile resolution.
 */
ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) {
  goog.asserts.assert(0 <= tileCoord.z && tileCoord.z < this.numResolutions_);
  return this.resolutions_[tileCoord.z];
};

/**
 * @param {number} z Z.
 * @return {ol.Size} Tile size.
 */
ol.tilegrid.TileGrid.prototype.getTileSize = function(z) {
  if (!goog.isNull(this.tileSize_)) {
    return this.tileSize_;
  } else {
    goog.asserts.assert(!goog.isNull(this.tileSizes_));
    goog.asserts.assert(0 <= z && z < this.tileSizes_.length);
    return this.tileSizes_[z];
  }
};

/**
 * @param {number} resolution Resolution.
 * @return {number} Z.
 */
ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) {
  return ol.array.linearFindNearest(this.resolutions_, resolution);
};

/**
 * @param {ol.Projection} projection Projection.
 * @return {ol.tilegrid.TileGrid} Default tile grid for the passed projection.
 */
ol.tilegrid.getForProjection = function(projection) {
  var tileGrid = projection.getDefaultTileGrid();
  if (goog.isNull(tileGrid)) {
    tileGrid = ol.tilegrid.createForProjection(projection);
    projection.setDefaultTileGrid(tileGrid);
  }
  return tileGrid;
};

/**
 * @param {ol.Projection} projection Projection.
 * @param {number=} opt_maxZoom Maximum zoom level.
 * @param {ol.Size=} opt_tileSize Tile size.
 * @return {ol.tilegrid.TileGrid} TileGrid instance.
 */
ol.tilegrid.createForProjection =
    function(projection, opt_maxZoom, opt_tileSize) {
  var projectionExtent = projection.getExtent();
  var size = Math.max(
      projectionExtent.maxX - projectionExtent.minX,
      projectionExtent.maxY - projectionExtent.minY);
  var maxZoom = goog.isDef(opt_maxZoom) ?
      opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
  var tileSize = goog.isDef(opt_tileSize) ?
      opt_tileSize : new ol.Size(ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE);
  var resolutions = new Array(maxZoom + 1);
  goog.asserts.assert(tileSize.width == tileSize.height);
  size = size / tileSize.width;
  for (var z = 0, zz = resolutions.length; z < zz; ++z) {
    resolutions[z] = size / Math.pow(2, z);
  }
  return new ol.tilegrid.TileGrid({
    origin: projectionExtent.getBottomLeft(),
    resolutions: resolutions,
    tileSize: tileSize
  });
};
<|endoftext|>