<commit_before>'use strict';
angular.module('copayApp.directives')
  .directive('validAddress', ['$rootScope', 'bitcore', 'bitcoreCash', 'bitcoinCashJsService',
    function($rootScope, bitcore, bitcoreCash, bitcoinCashJsService) {
      return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ctrl) {
          // Bitcoin address
          var URI = bitcore.URI;
          var Address = bitcore.Address

          // Bitcoin Cash address
          var URICash = bitcoreCash.URI;
          var AddressCash = bitcoreCash.Address

          var validator = function(value) {

            if (value.indexOf('bitcoincash:') >= 0 || value[0] == 'C' || value[0] == 'H' || value[0] == 'p' || value[0] == 'q') {
              if (value.indexOf('bitcoincash:') < 0) {
                value = 'bitcoincash:' + value;
              }
              value = bitcoinCashJsService.readAddress(value).legacy;
            }

            // Regular url
            if (/^https?:\/\//.test(value)) {
              ctrl.$setValidity('validAddress', true);
              return value;
            }

            // Bip21 uri
            var uri, isAddressValidLivenet, isAddressValidTestnet;
            if (/^bitcoin:/.test(value)) {
              var isUriValid = URI.isValid(value);
              if (isUriValid) {
                uri = new URI(value);
                isAddressValidLivenet = Address.isValid(uri.address.toString(), 'livenet')
                isAddressValidTestnet = Address.isValid(uri.address.toString(), 'testnet')
              }
              ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet || isAddressValidTestnet));
              return value;
            } else if (/^bitcoincash:/.test(value)) {
              var isUriValid = URICash.isValid(value);
              if (isUriValid) {
                uri = new URICash(value);
                isAddressValidLivenet = AddressCash.isValid(uri.address.toString(), 'livenet')
              }
              ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet));
              return value;
            }

            if (typeof value == 'undefined') {
              ctrl.$pristine = true;
              return;
            }

            // Regular Address: try Bitcoin and Bitcoin Cash
            var regularAddressLivenet = Address.isValid(value, 'livenet');
            var regularAddressTestnet = Address.isValid(value, 'testnet');
            var regularAddressCashLivenet = AddressCash.isValid(value, 'livenet');
            ctrl.$setValidity('validAddress', (regularAddressLivenet || regularAddressTestnet || regularAddressCashLivenet));
            return value;
          };

          ctrl.$parsers.unshift(validator);
          ctrl.$formatters.unshift(validator);
        }
      };
    }
  ])
  .directive('validAmount', ['configService',
    function(configService) {

      return {
        require: 'ngModel',
        link: function(scope, element, attrs, ctrl) {
          var val = function(value) {
            var settings = configService.getSync().wallet.settings;
            var vNum = Number((value * settings.unitToSatoshi).toFixed(0));
            if (typeof value == 'undefined' || value == 0) {
              ctrl.$pristine = true;
            }

            if (typeof vNum == "number" && vNum > 0) {
              if (vNum > Number.MAX_SAFE_INTEGER) {
                ctrl.$setValidity('validAmount', false);
              } else {
                var decimals = Number(settings.unitDecimals);
                var sep_index = ('' + value).indexOf('.');
                var str_value = ('' + value).substring(sep_index + 1);
                if (sep_index >= 0 && str_value.length > decimals) {
                  ctrl.$setValidity('validAmount', false);
                  return;
                } else {
                  ctrl.$setValidity('validAmount', true);
                }
              }
            } else {
              ctrl.$setValidity('validAmount', false);
            }
            return value;
          }
          ctrl.$parsers.unshift(val);
          ctrl.$formatters.unshift(val);
        }
      };
    }
  ])
  .directive('walletSecret', function(bitcore) {
    return {
      require: 'ngModel',
      link: function(scope, elem, attrs, ctrl) {
        var validator = function(value) {
          if (value.length > 0) {
            var m = value.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/);
            ctrl.$setValidity('walletSecret', m ? true : false);
          }
          return value;
        };

        ctrl.$parsers.unshift(validator);
      }
    };
  })
  .directive('ngFileSelect', function() {
    return {
      link: function($scope, el) {
        el.bind('change', function(e) {
          $scope.formData.file = (e.srcElement || e.target).files[0];
          $scope.getFile();
        });
      }
    }
  })
  .directive('contact', ['addressbookService', 'lodash',
    function(addressbookService, lodash) {
      return {
        restrict: 'E',
        link: function(scope, element, attrs) {
          var addr = attrs.address;
          addressbookService.get(addr, function(err, ab) {
            if (ab) {
              var name = lodash.isObject(ab) ? ab.name : ab;
              element.append(name);
            } else {
              element.append(addr);
            }
          });
        }
      };
    }
  ])
  .directive('ignoreMouseWheel', function($rootScope, $timeout) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.bind('mousewheel', function(event) {
          element[0].blur();
          $timeout(function() {
            element[0].focus();
          }, 1);
        });
      }
    }
  })
  .directive('validDate', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ctrl) {
        ctrl.$validators.validDate = function(modelValue, viewValue) {
          // Expected Format is DD/MM/YYYY
          if (ctrl.$isEmpty(modelValue)) {
            // consider empty models to be valid
            return true;
          }
          var now = new Date();
          if (viewValue.match(/\d{2}\/\d{2}\/\d{4}/g)) {
            var split = viewValue.split(/\//);
            return parseInt(split[0]) <= 31 &&
              parseInt(split[0]) > 0 &&
              parseInt(split[1]) <= 12 &&
              parseInt(split[1]) > 0 &&
              (parseInt(split[1])) > 0;
          }

          return false;
        };
      }
    };
  })
  .directive('validExpiration', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ctrl) {
        ctrl.$validators.validExpiration = function(modelValue, viewValue) {
          if (ctrl.$isEmpty(modelValue)) {
            // consider empty models to be valid
            return true;
          }
          var now = new Date();
          if (viewValue.match(/\d{2}\/\d{2}/g)) {
            var split = viewValue.split(/\//);
            return parseInt(split[0]) <= 12 &&
              parseInt(split[0]) > 0 &&
              (2000 + parseInt(split[1])) >= now.getFullYear();
          }

          return false;
        };
      }
    };
  })
  .directive('maskedAmount', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks amounts to the format of 999999999.99

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }
          var parsedValue = value.toString()
            .replace(/^\.(\d)/g,'0.$1')
            .replace(/^0+(\d)/g,'$1')
            .replace(/(\d+)\.(\d{2}).+$/g,'$1.$2')
            .replace(/^(\d{6})(\d+)$/g, '$1')
            .replace(/^(\d{6})\d+\.(\d{2})$/g, '$1.$2')
            .replace(/[^\d.]/g, '')
            .replace(/\/$/, '').trim();
          return parsedValue;
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  })
  .directive('maskedDate', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of 0000 0000 0000 0000

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/^(\d{2})$/g, '$1//')
            .replace(/^(\d{2})(\d?.)$/g, '$1/$2')
            .replace(/^(\d{2})\/(\d{2})$/g, '$1/$2//')
            .replace(/^(\d{2})(\d{2})(\d+)$/g, '$1/$2/$3')
            .replace(/\/$/, '');
          return parsedValue.slice(0,10);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  })
  .directive('maskedCreditCard', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of 0000 0000 0000 0000

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/(.{4})/g, '$1 ').trim()
            .replace(/\/$/, '');
          return parsedValue.slice(0,19);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        scope.originalValue = '';
        scope.groupValue = 4;
        scope.separtor = ' ';
        scope.maxDigit = 19;

        function isGroupFullyComplete(newValue) {

          // Check if a group is fully completed, if yes, let's format that
          var countOfNumbers = 0;
          var isDiff = false;
          for (var i = 0; i < newValue.length; i++) {
            var element = newValue[i];

            if (isDiff && countOfNumbers%scope.groupValue == 0) return true;

            if (element == scope.separtor) {
              if (isDiff) return false;
              countOfNumbers = 0;
            }
            else countOfNumbers++;

            if (!isDiff && i < scope.originalValue.length && element != scope.originalValue[i]) {
              isDiff = true;
            }
          }

          return false
        }

        function parseViewValue(value) {

          var viewValue = value;

          // Need to remove the right part
          if (value.length > scope.maxDigit
            || scope.originalValue[scope.originalValue.length - 1] != viewValue[viewValue.length - 1]   // Complete at the end
            || isGroupFullyComplete(viewValue)) { // If a new one fully grouped, lets format that by the regex
            viewValue = addSpaces(viewValue);
          }

          if (typeof viewValue !== 'undefined') {
            scope.originalValue = viewValue;
          }

          ngModel.$viewValue = viewValue;
          ngModel.$render();

          return removeSpaces(viewValue);
        }

        ngModel.$parsers.push(parseViewValue);
      }
    }
  })
  .directive('maskedExpiration', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of MM/YY

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/^(\d{2})$/g, '$1//').trim()
            .replace(/^(\d{2})(\d+)$/g, '$1/$2').trim()
            .replace(/\/$/, '');
          return parsedValue.slice(0,5);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '').replace(/\//g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  });
<commit_msg>Fix modulo<commit_after>'use strict';
angular.module('copayApp.directives')
  .directive('validAddress', ['$rootScope', 'bitcore', 'bitcoreCash', 'bitcoinCashJsService',
    function($rootScope, bitcore, bitcoreCash, bitcoinCashJsService) {
      return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ctrl) {
          // Bitcoin address
          var URI = bitcore.URI;
          var Address = bitcore.Address

          // Bitcoin Cash address
          var URICash = bitcoreCash.URI;
          var AddressCash = bitcoreCash.Address

          var validator = function(value) {

            if (value.indexOf('bitcoincash:') >= 0 || value[0] == 'C' || value[0] == 'H' || value[0] == 'p' || value[0] == 'q') {
              if (value.indexOf('bitcoincash:') < 0) {
                value = 'bitcoincash:' + value;
              }
              value = bitcoinCashJsService.readAddress(value).legacy;
            }

            // Regular url
            if (/^https?:\/\//.test(value)) {
              ctrl.$setValidity('validAddress', true);
              return value;
            }

            // Bip21 uri
            var uri, isAddressValidLivenet, isAddressValidTestnet;
            if (/^bitcoin:/.test(value)) {
              var isUriValid = URI.isValid(value);
              if (isUriValid) {
                uri = new URI(value);
                isAddressValidLivenet = Address.isValid(uri.address.toString(), 'livenet')
                isAddressValidTestnet = Address.isValid(uri.address.toString(), 'testnet')
              }
              ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet || isAddressValidTestnet));
              return value;
            } else if (/^bitcoincash:/.test(value)) {
              var isUriValid = URICash.isValid(value);
              if (isUriValid) {
                uri = new URICash(value);
                isAddressValidLivenet = AddressCash.isValid(uri.address.toString(), 'livenet')
              }
              ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet));
              return value;
            }

            if (typeof value == 'undefined') {
              ctrl.$pristine = true;
              return;
            }

            // Regular Address: try Bitcoin and Bitcoin Cash
            var regularAddressLivenet = Address.isValid(value, 'livenet');
            var regularAddressTestnet = Address.isValid(value, 'testnet');
            var regularAddressCashLivenet = AddressCash.isValid(value, 'livenet');
            ctrl.$setValidity('validAddress', (regularAddressLivenet || regularAddressTestnet || regularAddressCashLivenet));
            return value;
          };

          ctrl.$parsers.unshift(validator);
          ctrl.$formatters.unshift(validator);
        }
      };
    }
  ])
  .directive('validAmount', ['configService',
    function(configService) {

      return {
        require: 'ngModel',
        link: function(scope, element, attrs, ctrl) {
          var val = function(value) {
            var settings = configService.getSync().wallet.settings;
            var vNum = Number((value * settings.unitToSatoshi).toFixed(0));
            if (typeof value == 'undefined' || value == 0) {
              ctrl.$pristine = true;
            }

            if (typeof vNum == "number" && vNum > 0) {
              if (vNum > Number.MAX_SAFE_INTEGER) {
                ctrl.$setValidity('validAmount', false);
              } else {
                var decimals = Number(settings.unitDecimals);
                var sep_index = ('' + value).indexOf('.');
                var str_value = ('' + value).substring(sep_index + 1);
                if (sep_index >= 0 && str_value.length > decimals) {
                  ctrl.$setValidity('validAmount', false);
                  return;
                } else {
                  ctrl.$setValidity('validAmount', true);
                }
              }
            } else {
              ctrl.$setValidity('validAmount', false);
            }
            return value;
          }
          ctrl.$parsers.unshift(val);
          ctrl.$formatters.unshift(val);
        }
      };
    }
  ])
  .directive('walletSecret', function(bitcore) {
    return {
      require: 'ngModel',
      link: function(scope, elem, attrs, ctrl) {
        var validator = function(value) {
          if (value.length > 0) {
            var m = value.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/);
            ctrl.$setValidity('walletSecret', m ? true : false);
          }
          return value;
        };

        ctrl.$parsers.unshift(validator);
      }
    };
  })
  .directive('ngFileSelect', function() {
    return {
      link: function($scope, el) {
        el.bind('change', function(e) {
          $scope.formData.file = (e.srcElement || e.target).files[0];
          $scope.getFile();
        });
      }
    }
  })
  .directive('contact', ['addressbookService', 'lodash',
    function(addressbookService, lodash) {
      return {
        restrict: 'E',
        link: function(scope, element, attrs) {
          var addr = attrs.address;
          addressbookService.get(addr, function(err, ab) {
            if (ab) {
              var name = lodash.isObject(ab) ? ab.name : ab;
              element.append(name);
            } else {
              element.append(addr);
            }
          });
        }
      };
    }
  ])
  .directive('ignoreMouseWheel', function($rootScope, $timeout) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.bind('mousewheel', function(event) {
          element[0].blur();
          $timeout(function() {
            element[0].focus();
          }, 1);
        });
      }
    }
  })
  .directive('validDate', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ctrl) {
        ctrl.$validators.validDate = function(modelValue, viewValue) {
          // Expected Format is DD/MM/YYYY
          if (ctrl.$isEmpty(modelValue)) {
            // consider empty models to be valid
            return true;
          }
          var now = new Date();
          if (viewValue.match(/\d{2}\/\d{2}\/\d{4}/g)) {
            var split = viewValue.split(/\//);
            return parseInt(split[0]) <= 31 &&
              parseInt(split[0]) > 0 &&
              parseInt(split[1]) <= 12 &&
              parseInt(split[1]) > 0 &&
              (parseInt(split[1])) > 0;
          }

          return false;
        };
      }
    };
  })
  .directive('validExpiration', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ctrl) {
        ctrl.$validators.validExpiration = function(modelValue, viewValue) {
          if (ctrl.$isEmpty(modelValue)) {
            // consider empty models to be valid
            return true;
          }
          var now = new Date();
          if (viewValue.match(/\d{2}\/\d{2}/g)) {
            var split = viewValue.split(/\//);
            return parseInt(split[0]) <= 12 &&
              parseInt(split[0]) > 0 &&
              (2000 + parseInt(split[1])) >= now.getFullYear();
          }

          return false;
        };
      }
    };
  })
  .directive('maskedAmount', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks amounts to the format of 999999999.99

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }
          var parsedValue = value.toString()
            .replace(/^\.(\d)/g,'0.$1')
            .replace(/^0+(\d)/g,'$1')
            .replace(/(\d+)\.(\d{2}).+$/g,'$1.$2')
            .replace(/^(\d{6})(\d+)$/g, '$1')
            .replace(/^(\d{6})\d+\.(\d{2})$/g, '$1.$2')
            .replace(/[^\d.]/g, '')
            .replace(/\/$/, '').trim();
          return parsedValue;
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  })
  .directive('maskedDate', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of 0000 0000 0000 0000

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/^(\d{2})$/g, '$1//')
            .replace(/^(\d{2})(\d?.)$/g, '$1/$2')
            .replace(/^(\d{2})\/(\d{2})$/g, '$1/$2//')
            .replace(/^(\d{2})(\d{2})(\d+)$/g, '$1/$2/$3')
            .replace(/\/$/, '');
          return parsedValue.slice(0,10);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  })
  .directive('maskedCreditCard', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of 0000 0000 0000 0000

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/(.{4})/g, '$1 ').trim()
            .replace(/\/$/, '');
          return parsedValue.slice(0,19);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '');
          return parsedValue;
        }

        scope.originalValue = '';
        scope.groupValue = 4;
        scope.separtor = ' ';
        scope.maxDigit = 19;

        function isGroupFullyComplete(newValue) {

          // Check if a group is fully completed, if yes, let's format that
          var countOfNumbers = 0;
          var isDiff = false;
          for (var i = 0; i < newValue.length; i++) {
            var element = newValue[i];

            if (element == scope.separtor) {
              if (isDiff) return false;
              countOfNumbers = 0;
            }
            else countOfNumbers++;

            if (isDiff && countOfNumbers%scope.groupValue == 0) return true;

            if (!isDiff && i < scope.originalValue.length && element != scope.originalValue[i]) {
              isDiff = true;
            }
          }

          return false
        }

        function parseViewValue(value) {

          var viewValue = value;

          // Need to remove the right part
          if (value.length > scope.maxDigit
            || scope.originalValue[scope.originalValue.length - 1] != viewValue[viewValue.length - 1]   // Complete at the end
            || isGroupFullyComplete(viewValue)) { // If a new one fully grouped, lets format that by the regex
            viewValue = addSpaces(viewValue);
          }

          if (typeof viewValue !== 'undefined') {
            scope.originalValue = viewValue;
          }

          ngModel.$viewValue = viewValue;
          ngModel.$render();

          return removeSpaces(viewValue);
        }

        ngModel.$parsers.push(parseViewValue);
      }
    }
  })
  .directive('maskedExpiration', function() {
    return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
        // Masks expirations to the format of MM/YY

        function addSpaces(value) {
          if(typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString()
            .replace(/[^\d]/g, '')
            .replace(/^(\d{2})$/g, '$1//').trim()
            .replace(/^(\d{2})(\d+)$/g, '$1/$2').trim()
            .replace(/\/$/, '');
          return parsedValue.slice(0,5);
        }

        function removeSpaces(value) {
          if (typeof(value) == typeof(undefined)) {
            return value;
          }

          var parsedValue = value.toString().replace(/\s/g, '').replace(/\//g, '');
          return parsedValue;
        }

        function parseViewValue(value) {
          var viewValue = addSpaces(value);
          ngModel.$viewValue = viewValue;
          ngModel.$render();

          // Return what we want the model value to be
          return removeSpaces(viewValue);
        }

        function formatModelValue(value) {
          var modelValue = removeSpaces(value);
          ngModel.$modelValue = modelValue;
          return addSpaces(modelValue);
        }

        ngModel.$parsers.push(parseViewValue);
        ngModel.$formatters.push(formatModelValue);
      }
    }
  });
<|endoftext|>


<commit_before>angular.module('blocktrail.wallet')
    .filter('satoshiToCurrency', function($rootScope) {
        return function(input, currency, currencyRates, fractionSize) {
            //convert satoshi to btc
            var coin = 100000000;
            var precision = 8;
            var btc = parseFloat((input/ coin).toFixed(precision));
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            }
            if (typeof(currencyRates) == "undefined") {
                currencyRates = $rootScope.bitcoinPrices;
            }
            if (currency in currencyRates) {
                var localValue = (btc * currencyRates[currency]).toFixed(fractionSize);
            } else {
                var localValue = (0).toFixed(fractionSize);
            }

            switch(currency){
                case 'GBP':
                    return "£" + localValue;
                case 'EUR':
                    return "€" + localValue;
                case 'USD':
                    return "$" + localValue;
                case 'CNY':
                    return "¥" + localValue;
                case 'AUD':
                    return "$" + localValue;
                case 'BTC':
                    return btc.toFixed(fractionSize) + " BTC";
                default:
                    return currency;
            }
        };
    })
    .filter('toCurrencySymbol', function($rootScope) {
        return function(input) {
            switch(input){
                case 'GBP':
                    return "£";
                case 'EUR':
                    return "€";
                case 'USD':
                    return "$";
                case 'AUD':
                    return "$";
                case 'BTC':
                    return "฿";
                default:
                    return input;
            }
        };
    })
    .filter('confirmations', function($rootScope) {
        return function(input) {
            if (input) {
                return (parseInt($rootScope.blockHeight) - parseInt(input))+1;
            } else {
                return 0;
            }
        };
    })
    .filter('mathAbs', function() {
        return function(input) {
            return Math.abs(input);
        };
    })
    .filter('nl2br', function($sce){
        return function(msg) {
            return $sce.trustAsHtml((msg + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2'));
        }
    })
    .filter('contactInitials', function($rootScope) {
        return function(input) {
            //take the first and last word and return initials
            if (!input) {
                return input;
            }
            var regex = /\S+\s*/g;
            var words = input.trim().match(regex);
            if (words && words.length >= 2) {
                return (words[0][0]+words[words.length-1][0]).toUpperCase();
            } else if (words){
                return words[0][0].toUpperCase();
            } else {
                return input;
            }
        };
    })
    .filter('shortenCountryName', function() {
        return function(input) {
            //remove the bracket version of the country name
            if (!input) {
                return input;
            }
            var regex = /\(.+\)/g;
            return input.replace(regex, '');
        };
    })
    .filter('characters', function () {
        return function (input, chars, breakOnWord) {
            if (isNaN(chars)) return input;
            if (chars <= 0) return '';
            if (input && input.length >= chars) {
                input = input.substring(0, chars);

                if (!breakOnWord) {
                    var lastspace = input.lastIndexOf(' ');
                    //get last space
                    if (lastspace !== -1) {
                        input = input.substr(0, lastspace);
                    }
                }else{
                    while(input.charAt(input.length-1) == ' '){
                        input = input.substr(0, input.length -1);
                    }
                }
                return input + '...';
            }
            return input;
        };
    })
    .filter('duration', function () {
        return function (input) {
            return moment.duration(input).humanize();
        };
    });
<commit_msg>proper currency support<commit_after>angular.module('blocktrail.wallet')
    .filter('satoshiToCurrency', function($rootScope, Currencies) {
        var coin = 100000000;
        var precision = 8;

        return function(input, currency, currencyRates, fractionSize, useMarkup, hideCurrencyDisplay) {
            // normalize
            currency = currency.toUpperCase();

            var btc = parseFloat((input/ coin).toFixed(precision));

            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            } else {
                fractionSize = parseInt(fractionSize);
            }

            // use global prices
            if (typeof(currencyRates) == "undefined") {
                currencyRates = $rootScope.bitcoinPrices;
            }

            var localValue;
            if (typeof currencyRates[currency] !== "undefined") {
                localValue = (btc * currencyRates[currency]).toFixed(fractionSize);
            } else {
                localValue = (0).toFixed(fractionSize);
            }

            var symbol;
            if (typeof Currencies.currencies[currency] === "undefined") {
                symbol = input;
            } else {
                symbol = Currencies.currencies[currency].symbol || currency;
            }

            var currencyDisplay;
            if (currency === "BTC") {
                currencyDisplay = useMarkup ? (' BTC') : " BTC";
                return hideCurrencyDisplay ? btc.toFixed(fractionSize) : btc.toFixed(fractionSize) + currencyDisplay;
            } else {
                currencyDisplay = useMarkup ? ('' + symbol + '') : symbol;
                return hideCurrencyDisplay ? localValue : currencyDisplay + localValue;
            }
        };
    })
    .filter('toCurrencySymbol', function($rootScope, Currencies) {
        return function(input) {
            if (typeof Currencies.currencies[input] === "undefined") {
                return input;
            } else {
                return Currencies.currencies[input].symbol || input;
            }
        };
    })
    .filter('confirmations', function($rootScope) {
        return function(input) {
            if (input) {
                return (parseInt($rootScope.blockHeight) - parseInt(input))+1;
            } else {
                return 0;
            }
        };
    })
    .filter('mathAbs', function() {
        return function(input) {
            return Math.abs(input);
        };
    })
    .filter('nl2br', function($sce){
        return function(msg) {
            return $sce.trustAsHtml((msg + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2'));
        }
    })
    .filter('contactInitials', function($rootScope) {
        return function(input) {
            //take the first and last word and return initials
            if (!input) {
                return input;
            }
            var regex = /\S+\s*/g;
            var words = input.trim().match(regex);
            if (words && words.length >= 2) {
                return (words[0][0]+words[words.length-1][0]).toUpperCase();
            } else if (words){
                return words[0][0].toUpperCase();
            } else {
                return input;
            }
        };
    })
    .filter('shortenCountryName', function() {
        return function(input) {
            //remove the bracket version of the country name
            if (!input) {
                return input;
            }
            var regex = /\(.+\)/g;
            return input.replace(regex, '');
        };
    })
    .filter('characters', function () {
        return function (input, chars, breakOnWord) {
            if (isNaN(chars)) return input;
            if (chars <= 0) return '';
            if (input && input.length >= chars) {
                input = input.substring(0, chars);

                if (!breakOnWord) {
                    var lastspace = input.lastIndexOf(' ');
                    //get last space
                    if (lastspace !== -1) {
                        input = input.substr(0, lastspace);
                    }
                }else{
                    while(input.charAt(input.length-1) == ' '){
                        input = input.substr(0, input.length -1);
                    }
                }
                return input + '...';
            }
            return input;
        };
    })
    .filter('duration', function () {
        return function (input) {
            return moment.duration(input).humanize();
        };
    });
<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) {

  var root = {};

  root.showMenu = function(data) {
    $rootScope.$broadcast('incomingDataMenu.showMenu', data);
  };

  root.redir = function(data) {
    $log.debug('Processing incoming data: ' + data);

    function sanitizeUri(data) {
      // Fixes when a region uses comma to separate decimals
      var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
      var match = regex.exec(data);
      if (!match || match.length === 0) {
        return data;
      }
      var value = match[0].replace(',', '.');
      var newUri = data.replace(regex, value);

      // mobile devices, uris like copay://glidera
      newUri.replace('://', ':');

      return newUri;
    }

    function getParameterByName(name, url) {
      if (!url) return;
      name = name.replace(/[\[\]]/g, "\\$&");
      var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
      if (!results) return null;
      if (!results[2]) return '';
      return decodeURIComponent(results[2].replace(/\+/g, " "));
    }

    function checkPrivateKey(privateKey) {
      try {
        new bitcore.PrivateKey(privateKey, 'livenet');
      } catch (err) {
        return false;
      }
      return true;
    }

    function goSend(addr, amount, message) {
      $state.go('tabs.send', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.send' ? false : true
      });
      // Timeout is required to enable the "Back" button
      $timeout(function() {
        if (amount) {
          $state.transitionTo('tabs.send.confirm', {
            toAmount: amount,
            toAddress: addr,
            description: message
          });
        } else {
          $state.transitionTo('tabs.send.amount', {
            toAddress: addr
          });
        }
      }, 100);
    }
    // data extensions for Payment Protocol with non-backwards-compatible request
    if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
      data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
      $state.go('tabs.send', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.send' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.send.confirm', {
          paypro: data
        });
      });
      return true;
    }

    data = sanitizeUri(data);

    // BIP21
    if (bitcore.URI.isValid(data)) {
      var parsed = new bitcore.URI(data);

      var addr = parsed.address ? parsed.address.toString() : '';
      var message = parsed.message;

      var amount = parsed.amount ? parsed.amount : '';

      if (parsed.r) {
        payproService.getPayProDetails(parsed.r, function(err, details) {
          if (err) {
            if (addr && amount) goSend(addr, amount, message);
            else popupService.showAlert(gettextCatalog.getString('Error'), err);
          } else handlePayPro(details);
        });
      } else {
        goSend(addr, amount, message);
      }
      return true;

      // Plain URL
    } else if (/^https?:\/\//.test(data)) {

      payproService.getPayProDetails(data, function(err, details) {
        if (err) {
          root.showMenu({
            data: data,
            type: 'url'
          });
          return;
        }
        handlePayPro(details);
        return true;
      });
      // Plain Address
    } else if (bitcore.Address.isValid(data, 'livenet') || bitcore.Address.isValid(data, 'testnet')) {
      if ($state.includes('tabs.scan')) {
        root.showMenu({
          data: data,
          type: 'bitcoinAddress'
        });
      } else {
        goToAmountPage(data);
      }
    } else if (data && data.indexOf(appConfigService.name + '://glidera') === 0) {
      var code = getParameterByName('code', data);
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $ionicHistory.nextViewOptions({
          disableAnimate: true
        });
        $state.transitionTo('tabs.buyandsell.glidera', {
          code: code
        });
      });
      return true;

    } else if (data && data.indexOf(appConfigService.name + '://coinbase') === 0) {
      var code = getParameterByName('code', data);
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $ionicHistory.nextViewOptions({
          disableAnimate: true
        });
        $state.transitionTo('tabs.buyandsell.coinbase', {
          code: code
        });
      });
      return true;

      // BitPayCard Authentication
    } else if (data && data.indexOf(appConfigService.name + '://') === 0) {

      // Disable BitPay Card
      if (!appConfigService._enabledExtensions.debitcard) return false;

      var secret = getParameterByName('secret', data);
      var email = getParameterByName('email', data);
      var otp = getParameterByName('otp', data);
      var reason = getParameterByName('r', data);

      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        switch (reason) {
          default:
            case '0':
            /* For BitPay card binding */
            $state.transitionTo('tabs.bitpayCardIntro', {
              secret: secret,
              email: email,
              otp: otp
            });
          break;
        }
      });
      return true;

      // Join
    } else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.add.join', {
          url: data
        });
      });
      return true;

      // Old join
    } else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.add.join', {
          url: data
        });
      });
      return true;
    } else if (data && (data.substring(0, 2) == '6P' || checkPrivateKey(data))) {
      root.showMenu({
        data: data,
        type: 'privateKey'
      });
    } else if (data && ((data.substring(0, 2) == '1|') || (data.substring(0, 2) == '2|') || (data.substring(0, 2) == '3|'))) {
      $state.go('tabs.home').then(function() {
        $state.transitionTo('tabs.add.import', {
          code: data
        });
      });
      return true;

    } else {
      if ($state.includes('tabs.scan')) {
        root.showMenu({
          data: data,
          type: 'text'
        });
      }
    }

    return false;

  };

  function goToAmountPage(toAddress) {
    $state.go('tabs.send', {}, {
      'reload': true,
      'notify': $state.current.name == 'tabs.send' ? false : true
    });
    $timeout(function() {
      $state.transitionTo('tabs.send.amount', {
        toAddress: toAddress
      });
    }, 100);
  }

  function handlePayPro(payProDetails) {
    var stateParams = {
      toAmount: payProDetails.amount,
      toAddress: payProDetails.toAddress,
      description: payProDetails.memo,
      paypro: payProDetails
    };
    scannerService.pausePreview();
    $state.go('tabs.send', {}, {
      'reload': true,
      'notify': $state.current.name == 'tabs.send' ? false : true
    }).then(function() {
      $timeout(function() {
        $state.transitionTo('tabs.send.confirm', stateParams);
      });
    });
  }

  return root;
});
<commit_msg>Handler bip21 with extra param chain<commit_after>'use strict';

angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) {

  var root = {};

  root.showMenu = function(data) {
    $rootScope.$broadcast('incomingDataMenu.showMenu', data);
  };

  root.redir = function(data) {
    $log.debug('Processing incoming data: ' + data);

    function sanitizeUri(data) {
      // Fixes when a region uses comma to separate decimals
      var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
      var match = regex.exec(data);
      if (!match || match.length === 0) {
        return data;
      }
      var value = match[0].replace(',', '.');
      var newUri = data.replace(regex, value);

      // mobile devices, uris like copay://glidera
      newUri.replace('://', ':');

      return newUri;
    }

    function getParameterByName(name, url) {
      if (!url) return;
      name = name.replace(/[\[\]]/g, "\\$&");
      var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
      if (!results) return null;
      if (!results[2]) return '';
      return decodeURIComponent(results[2].replace(/\+/g, " "));
    }

    function checkPrivateKey(privateKey) {
      try {
        new bitcore.PrivateKey(privateKey, 'livenet');
      } catch (err) {
        return false;
      }
      return true;
    }

    function goSend(addr, amount, message, chain) {
      $state.go('tabs.send', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.send' ? false : true
      });
      // Timeout is required to enable the "Back" button
      $timeout(function() {
        if (amount) {
          $state.transitionTo('tabs.send.confirm', {
            toAmount: amount,
            toAddress: addr,
            description: message,
            chain: chain
          });
        } else {
          $state.transitionTo('tabs.send.amount', {
            toAddress: addr,
            chain: chain
          });
        }
      }, 100);
    }
    // data extensions for Payment Protocol with non-backwards-compatible request
    if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
      data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
      $state.go('tabs.send', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.send' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.send.confirm', {
          paypro: data
        });
      });
      return true;
    }

    data = sanitizeUri(data);

    // BIP21
    if (bitcore.URI.isValid(data)) {
      var parsed = new bitcore.URI(data);

      var addr = parsed.address ? parsed.address.toString() : '';
      var message = parsed.message;

      var amount = parsed.amount ? parsed.amount : '';
      var chain = parsed.extras && parsed.extras.chain ? (parsed.extras.chain).toUpperCase() : '';

      if (parsed.r) {
        payproService.getPayProDetails(parsed.r, function(err, details) {
          if (err) {
            if (addr && amount) goSend(addr, amount, message, chain);
            else popupService.showAlert(gettextCatalog.getString('Error'), err);
          } else handlePayPro(details);
        });
      } else {
        goSend(addr, amount, message, chain);
      }
      return true;

      // Plain URL
    } else if (/^https?:\/\//.test(data)) {

      payproService.getPayProDetails(data, function(err, details) {
        if (err) {
          root.showMenu({
            data: data,
            type: 'url'
          });
          return;
        }
        handlePayPro(details);
        return true;
      });
      // Plain Address
    } else if (bitcore.Address.isValid(data, 'livenet') || bitcore.Address.isValid(data, 'testnet')) {
      if ($state.includes('tabs.scan')) {
        root.showMenu({
          data: data,
          type: 'bitcoinAddress'
        });
      } else {
        goToAmountPage(data);
      }
    } else if (data && data.indexOf(appConfigService.name + '://glidera') === 0) {
      var code = getParameterByName('code', data);
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $ionicHistory.nextViewOptions({
          disableAnimate: true
        });
        $state.transitionTo('tabs.buyandsell.glidera', {
          code: code
        });
      });
      return true;

    } else if (data && data.indexOf(appConfigService.name + '://coinbase') === 0) {
      var code = getParameterByName('code', data);
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $ionicHistory.nextViewOptions({
          disableAnimate: true
        });
        $state.transitionTo('tabs.buyandsell.coinbase', {
          code: code
        });
      });
      return true;

      // BitPayCard Authentication
    } else if (data && data.indexOf(appConfigService.name + '://') === 0) {

      // Disable BitPay Card
      if (!appConfigService._enabledExtensions.debitcard) return false;

      var secret = getParameterByName('secret', data);
      var email = getParameterByName('email', data);
      var otp = getParameterByName('otp', data);
      var reason = getParameterByName('r', data);

      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        switch (reason) {
          default:
            case '0':
            /* For BitPay card binding */
            $state.transitionTo('tabs.bitpayCardIntro', {
              secret: secret,
              email: email,
              otp: otp
            });
          break;
        }
      });
      return true;

      // Join
    } else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.add.join', {
          url: data
        });
      });
      return true;

      // Old join
    } else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      $state.go('tabs.home', {}, {
        'reload': true,
        'notify': $state.current.name == 'tabs.home' ? false : true
      }).then(function() {
        $state.transitionTo('tabs.add.join', {
          url: data
        });
      });
      return true;
    } else if (data && (data.substring(0, 2) == '6P' || checkPrivateKey(data))) {
      root.showMenu({
        data: data,
        type: 'privateKey'
      });
    } else if (data && ((data.substring(0, 2) == '1|') || (data.substring(0, 2) == '2|') || (data.substring(0, 2) == '3|'))) {
      $state.go('tabs.home').then(function() {
        $state.transitionTo('tabs.add.import', {
          code: data
        });
      });
      return true;

    } else {
      if ($state.includes('tabs.scan')) {
        root.showMenu({
          data: data,
          type: 'text'
        });
      }
    }

    return false;

  };

  function goToAmountPage(toAddress) {
    $state.go('tabs.send', {}, {
      'reload': true,
      'notify': $state.current.name == 'tabs.send' ? false : true
    });
    $timeout(function() {
      $state.transitionTo('tabs.send.amount', {
        toAddress: toAddress
      });
    }, 100);
  }

  function handlePayPro(payProDetails) {
    var stateParams = {
      toAmount: payProDetails.amount,
      toAddress: payProDetails.toAddress,
      description: payProDetails.memo,
      paypro: payProDetails
    };
    scannerService.pausePreview();
    $state.go('tabs.send', {}, {
      'reload': true,
      'notify': $state.current.name == 'tabs.send' ? false : true
    }).then(function() {
      $timeout(function() {
        $state.transitionTo('tabs.send.confirm', stateParams);
      });
    });
  }

  return root;
});
<|endoftext|>


<commit_before>/* Panzer v0.1.1 + genData v1.2.0 / Bemi Faison (c) 2012 / MIT / http://github.com/bemson */
;(typeof exports!="undefined"?exports:window).genData=function(a){var b=arguments,c=b.callee,d,e,f,g,h=[],i=[],j,k={},l,m,n,o=c,p=typeof o=="function",q;if(this instanceof c){if(a){~{}.toString.call(b[1]).indexOf("y")&&(o=a,b=b[1]),h=h.concat([].slice.call(b));function s(a,b,d){var e=~{}.toString.call(b).indexOf("y");return this instanceof s?a?new c(e?a:s,h.concat(e?b:[].slice.call(arguments))):this:c(a,h.concat(b||[]),d?d:s)}return s.prototype=p?new o:o,s}return this}b[1]&&(h=typeof b[1]=="function"?[b[1]]:b[1]),f=h.length,b[2]&&(o=b[2]);function r(a,b){this.name=a,this.value=b}r.prototype=p?o.prototype:o,l=[["",a]];while(l.length){n=l.shift(),q=new r(n[0],n[1]),e=0,d={parent:0,omit:0,scan:1,exit:0},b=[q.name,q.value,n[2],i,d,k];while(e<f&&!d.exit)h[e++].apply(q,b);d.omit?q._OMIT=!0:i.push(q);if(d.exit)l=[];else{m=[];if(d.scan&&typeof q.value=="object"){j=typeof d.parent=="object"?d.parent:q;for(g in q.value)q.value.hasOwnProperty(g)&&m.push([g,q.value[g],j])}l=m.concat(l)}}return i};
!function(inCommonJsEnv,Array,Object,RegExp,window,undefined){function Tree(panzer,panzerInst,rawtree,pkgConfig){var tree=this;tree.panzer=PZR=panzer,tree.y=[{},function(a,b){return a===panzer?b?(tree.pkgs[panzer.i[b]]||{}).inst||!1:tree:toStringResult}],panzer.d.forEach(function(a){var b=a.def.attributeKey,c=a.def.invalidKey;a.c={af:typeof b=="function"?b:0,ar:b&&b instanceof RegExp?b:0,nf:typeof c=="function"?c:0,nr:c&&c instanceof RegExp?c:0}}),tree.nodes=genNodes(rawtree),PZR=0,tree.nodes[0].parentIndex=tree.nodes[0].childIndex=0,tree.nodes.unshift(genNodes()[0]);with(tree.nodes[0])children.push(1),name="_tree",index=0,depth=0,path="..//",ctx=1;tree.nodes[0].firstChildIndex=tree.nodes[0].lastChildIndex=1,tree.tank={id:panzer.c++,currentIndex:0,targetIndex:-1,go:function(a){var b=tree.nodes[a];return b&&(tree.target=b,tree.tank.targetIndex=b.index),tree.stop=0,tree.go()},stop:function(){return tree.stop=1,!!tree.loop},post:function(a){switch(typeof a){case"function":return tree.posts.push(a)-1;case"number":if(tree.posts[a])return tree.posts[a]=0,!0}return!1}},tree.posts=[],tree.current=tree.nodes[0],tree.target=tree.loop=0,tree.pkgs=panzer.d.map(function(a){function c(){}var b={name:a.name,inst:new a.def};return c.prototype=a.proxy.prototype,tree.y[0][a.name]=b.proxy=new c,b.inst.nodes=genCloneNodes(tree.nodes,0,a.node),b.inst.tank=tree.tank,typeof a.def.init=="function"&&a.def.init.call(b.inst,pkgConfig),b}),tree.pkgs.forEach(function(a,b,c){a.proxy.pkgs=tree.y[0],a.proxy.toString=tree.y[1],a.inst.proxy=panzerInst}),panzerInst.pkgs=tree.y[0],panzerInst.toString=tree.y[1],this.ret=1}function PanzerResolvePackage(a){var b=this;if(arguments.length){if(typeof a=="string"&&/\w/.test(a)){if(!b.i.hasOwnProperty(a)){var c=b.d.length;function d(c){if(!(this instanceof arguments.callee))return c instanceof b.P&&c.toString(b,a)}d.init=d.attributeKey=d.invalidKey=d.onBegin=d.onEnd=d.onTraverse=0;function e(){}e.prototype=new b.Y,b.P.prototype=d.proxy=e.prototype,b.Y=e;function f(){}d.node=f.prototype,b.i[a]=b.d.push({name:a,def:d,proxy:e,node:f})-1}return b.d[b.i[a]].def}return!1}return b.d.map(function(a){return a.name})}if(!inCommonJsEnv&&window.Panzer)return;var environment=inCommonJsEnv?exports:window,genData=(inCommonJsEnv?require("genData"):window).genData,PZR,toStringResult={}.toString(),testNodeKey=function(a,b,c){var d=a+"f",e=a+"r";return PZR.d.some(function(a){if(a.c[d])return a.c[d].call(window,b,c);if(a.c[e])return a.c[e].test(b)})},genNodes=new genData(function(a,b,c,d,e,f){var g=this,h=c&&testNodeKey("n",a,b),i=c&&!h&&testNodeKey("a",a,b);h||i?(e.omit=1,e.scan=0,i&&!h&&(c.attributes[a]=b)):(g.ctx=g.parentIndex=g.previousIndex=g.nextIndex=g.firstChildIndex=g.lastChildIndex=g.childIndex=undefined,g.index=d.length+1,g.depth=c?c.depth+1:1,g.name=c?a:"_root",g.attributes={},g.path=c?c.path+a+"/":"//",g.children=[],c&&(g.parentIndex=c.index,c.children.length||(c.firstChildIndex=g.index),g.childIndex=c.children.push(g.index)-1,c.lastChildIndex=g.index,g.childIndex&&(g.previousIndex=c.children[g.childIndex-1],d[g.previousIndex-1].nextIndex=g.index)))}),genCloneNodes=new genData(function(a,b,c,d,e){if(!c)e.omit=1;else{e.scan=0;for(var f in b)b.hasOwnProperty(f)&&f!=="ctx"&&(this[f]=b[f])}}),arrayPrototype=Array.prototype;arrayPrototype.some||(arrayPrototype.some=function(a,b){for(var c=0,d=this.length;c<d;c++)if(a.call(b,this[c],c,this))return!0;return!1}),arrayPrototype.forEach||(arrayPrototype.forEach=function(a,b){for(var c=0,d=this.length;c<d;c++)a.call(b,this[c],c,this)}),arrayPrototype.map||(arrayPrototype.map=function(a,b){var c=0,d=this.length,e=new Array(d);for(;c<d;c++)e[c]=a.call(b,this[c],c,this);return e}),Tree.prototype={go:function(){var a=this,b=a.nodes,c=a.tank,d,e=0,f=a.current,g=0,h=0,i;if(a.loop)return!!a.target;a.posts=[],a.loop=1,a.fire("Begin");while(a.loop)a.target&&!a.stop?(i=0,d=a.target.index-f.index,d?d>0&&f.index<2||!a.target.path.indexOf(f.path)?f.ctx?(g=0,h=f.firstChildIndex):(g=1,h=1,f.ctx=1):f.ctx?(g=1,h=2,f.ctx=0):(a.target.path.indexOf(b[f.parentIndex].path)&&(d=-1),h=d<0?4:3,f.lastEvent===2||f.lastEvent===h||f.lastEvent+h===7?(g=0,h=d>0?f.nextIndex:f.previousIndex||f.parentIndex):g=1):(g=1,h=f.ctx?0:1,f.ctx&&(a.target=0,c.targetIndex=-1),f.ctx=1),g?(f.lastEvent=h,e++,a.fire("Traverse",[h])):(f.lastEvent=0,f=a.current=b[h],c.currentIndex=h)):!i&&(a.stop||!a.target)?(i=1,a.fire("End")):a.loop=0;return a.posts.forEach(function(a){typeof a=="function"&&a()}),e},fire:function(a,b){var c=this.panzer;if(!this.ret)return;b=b||[],b.unshift(a.toLowerCase()),this.pkgs.forEach(function(d){var e=c.d[c.i[d.name]].def["on"+a];typeof e=="function"&&e.apply(d.inst,b)})}},environment.Panzer={version:"0.1",create:function(){function b(b,c){if(this instanceof arguments.callee)new Tree(a,this,b,typeof c=="object"?c:{});else throw new Error("Missing new operator.")}var a={c:0,d:[],i:{},Y:function(){},P:b};return b.pkg=function(b){return PanzerResolvePackage.apply(a,arguments)},b}}}(typeof require!="undefined",Array,Object,RegExp,this);<commit_msg>Minified for production (5.8K/2.4K)<commit_after>/* Panzer v0.2 + genData v1.2 | Bemi Faison (c) 2012 | MIT | github.com/bemson */
;(typeof exports!="undefined"?exports:window).genData=function(a){var b=arguments,c=b.callee,d,e,f,g,h=[],i=[],j,k={},l,m,n,o=c,p=typeof o=="function",q;if(this instanceof c){if(a){~{}.toString.call(b[1]).indexOf("y")&&(o=a,b=b[1]),h=h.concat([].slice.call(b));function s(a,b,d){var e=~{}.toString.call(b).indexOf("y");return this instanceof s?a?new c(e?a:s,h.concat(e?b:[].slice.call(arguments))):this:c(a,h.concat(b||[]),d?d:s)}return s.prototype=p?new o:o,s}return this}b[1]&&(h=typeof b[1]=="function"?[b[1]]:b[1]),f=h.length,b[2]&&(o=b[2]);function r(a,b){this.name=a,this.value=b}r.prototype=p?o.prototype:o,l=[["",a]];while(l.length){n=l.shift(),q=new r(n[0],n[1]),e=0,d={parent:0,omit:0,scan:1,exit:0},b=[q.name,q.value,n[2],i,d,k];while(e<f&&!d.exit)h[e++].apply(q,b);d.omit?q._OMIT=!0:i.push(q);if(d.exit)l=[];else{m=[];if(d.scan&&typeof q.value=="object"){j=typeof d.parent=="object"?d.parent:q;for(g in q.value)q.value.hasOwnProperty(g)&&m.push([g,q.value[g],j])}l=m.concat(l)}}return i};
!function(inCommonJsEnv,Array,Object,RegExp,window,undefined){function noOp(){}function Tree(panzer,panzerInst,rawtree,pkgConfig){var tree=this;tree.panzer=PZR=panzer,tree.y=[{},function(a,b){return a===panzer?b?(tree.pkgs[panzer.i[b]]||{}).inst||!1:tree:toStringResult}],panzer.d.forEach(function(a){var b=a.def.attributeKey,c=a.def.invalidKey;a.c={af:typeof b=="function"?b:0,ar:b&&b instanceof RegExp?b:0,nf:typeof c=="function"?c:0,nr:c&&c instanceof RegExp?c:0}}),tree.nodes=genNodes(rawtree),PZR=0,tree.nodes[0].parentIndex=tree.nodes[0].childIndex=0,tree.nodes.unshift(genNodes()[0]);with(tree.nodes[0])children.push(1),name="PNULL",index=0,depth=0,path="..//",ctx=1;tree.nodes[0].firstChildIndex=tree.nodes[0].lastChildIndex=1,tree.tank={id:panzer.c++,currentIndex:0,targetIndex:-1,go:function(a){var b=tree.nodes[a];return b&&(tree.target=b,tree.tank.targetIndex=b.index),tree.stop=0,tree.go()},stop:function(){return tree.stop=1,!!tree.loop},post:function(a){switch(typeof a){case"function":return tree.posts.push(a)-1;case"number":if(tree.posts[a])return tree.posts[a]=0,!0}return!1}},tree.posts=[],tree.current=tree.nodes[0],tree.target=tree.loop=0,tree.pkgs=panzer.d.map(function(a){function c(){}var b={name:a.name,inst:new a.def};return c.prototype=a.proxy.prototype,tree.y[0][a.name]=b.proxy=new c,b.inst.nodes=genCloneNodes(tree.nodes,0,a.node),b.inst.tank=tree.tank,typeof a.def.init=="function"&&a.def.init.call(b.inst,pkgConfig),b}),tree.pkgs.forEach(function(a,b,c){a.proxy.pkgs=tree.y[0],a.proxy.toString=tree.y[1],a.inst.proxy=panzerInst}),panzerInst.pkgs=tree.y[0],panzerInst.toString=tree.y[1],this.ret=1}function PanzerGetSuperMethod(a,b){return typeof b=="string"&&a&&this.d[a-1].proxy.prototype[b]||noOp}function PanzerResolvePackage(a){var b=this;if(arguments.length){if(typeof a=="string"&&/\w/.test(a)){if(!b.i.hasOwnProperty(a)){function c(c){if(!(this instanceof arguments.callee))return c instanceof b.P&&c.toString(b,a)}c.getSuper=function(a){return PanzerGetSuperMethod.call(b,this.index,a)},c.init=c.attributeKey=c.invalidKey=c.onBegin=c.onEnd=c.onTraverse=0;function d(){}d.prototype=new b.Y,b.P.prototype=c.proxy=d.prototype,b.Y=d;function e(){}c.node=e.prototype,c.index=b.i[a]=b.d.push({name:a,def:c,proxy:d,node:e})-1}return b.d[b.i[a]].def}return!1}return b.d.map(function(a){return a.name})}if(!inCommonJsEnv&&window.Panzer)return;var environment=inCommonJsEnv?exports:window,genData=(inCommonJsEnv?require("genData"):window).genData,PZR,toStringResult={}.toString(),testNodeKey=function(a,b,c){var d=a+"f",e=a+"r";return PZR.d.some(function(a){if(a.c[d])return a.c[d].call(window,b,c);if(a.c[e])return a.c[e].test(b)})},genNodes=new genData(function(a,b,c,d,e,f){var g=this,h=c&&testNodeKey("n",a,b),i=c&&!h&&testNodeKey("a",a,b);h||i?(e.omit=1,e.scan=0,i&&!h&&(c.attributes[a]=b)):(g.ctx=g.parentIndex=g.previousIndex=g.nextIndex=g.firstChildIndex=g.lastChildIndex=g.childIndex=undefined,g.index=d.length+1,g.depth=c?c.depth+1:1,g.name=c?a:"PROOT",g.attributes={},g.path=c?c.path+a+"/":"//",g.children=[],c&&(g.parentIndex=c.index,c.children.length||(c.firstChildIndex=g.index),g.childIndex=c.children.push(g.index)-1,c.lastChildIndex=g.index,g.childIndex&&(g.previousIndex=c.children[g.childIndex-1],d[g.previousIndex-1].nextIndex=g.index)))}),genCloneNodes=new genData(function(a,b,c,d,e){if(!c)e.omit=1;else{e.scan=0;for(var f in b)b.hasOwnProperty(f)&&f!=="ctx"&&(this[f]=b[f])}}),arrayPrototype=Array.prototype;Tree.prototype={go:function(){var a=this,b=a.nodes,c=a.tank,d,e=0,f=a.current,g=0,h=0,i;if(a.loop)return!!a.target;a.posts=[],a.loop=1,a.fire("Begin");while(a.loop)a.target&&!a.stop?(i=0,d=a.target.index-f.index,d?d>0&&f.index<2||!a.target.path.indexOf(f.path)?f.ctx?(g=0,h=f.firstChildIndex):(g=1,h=1,f.ctx=1):f.ctx?(g=1,h=2,f.ctx=0):(a.target.path.indexOf(b[f.parentIndex].path)&&(d=-1),h=d<0?4:3,f.lastEvent===2||f.lastEvent===h||f.lastEvent+h===7?(g=0,h=d>0?f.nextIndex:f.previousIndex||f.parentIndex):g=1):(g=1,h=f.ctx?0:1,f.ctx&&(a.target=0,c.targetIndex=-1),f.ctx=1),g?(f.lastEvent=h,e++,a.fire("Traverse",[h])):(f.lastEvent=0,f=a.current=b[h],c.currentIndex=h)):!i&&(a.stop||!a.target)?(i=1,a.fire("End")):a.loop=0;return a.posts.forEach(function(a){typeof a=="function"&&a()}),e},fire:function(a,b){var c=this.panzer;if(!this.ret)return;b=b||[],b.unshift(a.toLowerCase()),this.pkgs.forEach(function(d){var e=c.d[c.i[d.name]].def["on"+a];typeof e=="function"&&e.apply(d.inst,b)})}},environment.Panzer={version:"0.1",create:function(){function b(b,c){if(this instanceof arguments.callee)new Tree(a,this,b,typeof c=="object"?c:{});else throw new Error("Missing new operator.")}var a={c:0,d:[],i:{},Y:function(){},P:b};return b.pkg=function(b){return PanzerResolvePackage.apply(a,arguments)},b}}}(typeof require!="undefined",Array,Object,RegExp,this);<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var Tab = require('../client/tab').Tab;

var LoginTab = function() {
  Tab.call(this);
};

util.inherits(LoginTab, Tab);

LoginTab.prototype.tabName = 'login';
LoginTab.prototype.pageMode = 'single';
LoginTab.prototype.parent = 'main';

LoginTab.prototype.extraRoutes = [
  {name: '/login/:callback'}
];

LoginTab.prototype.angular = function(module) {
  module.controller('LoginCtrl', ['$scope', '$location', '$sce', 'rpTracker', 'rpId', 'rpAPI',
    function($scope, $location, $sce, tracker, id, api) {

      $scope.error = '';
      $scope.loggingIn = false;
      $scope.redirectTo = $location.path();
      $scope.backendMessages = [];
      $scope.authAction = $sce.trustAsResourceUrl(Options.backend_url + '/auth/login');

      if (id.loginStatus) {
        $location.path('/balance');
        return;
      }

      $scope.migrate = function() {
        tracker.track('Migrate to Gatehub', {
          'Status': 'success'
        });
        window.location.assign('https://wallet.gatehub.net/#/identity/');
      };

      function loginCallback(err) {
        if (err) {
          $scope.loggingIn = false;
          $scope.status = '';

          $scope.status = 'Login failed:';

          if (err.name !== 'BlobError') {
            $scope.backendMessages.push(err.message);
          }

          if (!$scope.$$phase) {
            $scope.$apply();
          }

          return;
        }

        api.getUserProfile().success(function(profile) {

          store.set('profile_status', profile.ids_status);
          store.set('profile_country', profile.country);

          $scope.loggingIn = false;
          $scope.status = '';

          //store.set('profile_status', $location.search().status);

          if ($location.search().redirect_to) {
            $location.url($location.search().redirect_to);
          } else {
            $location.path('/balance').search('');
          }

          tracker.track('Login', {
            'Status': 'success'
          });
        });
      }

      $scope.submitForm = function(authAction) {
        $scope.redirectTo = encodeURIComponent($location.url());
        $scope.authAction = $sce.trustAsResourceUrl(Options.backend_url + '/auth/' + authAction);
      };

      // if ($routeParams.callback === 'callback' && $routeParams.token ) {
      if ($location.path() === '/login/callback' && $location.search().token) {
        $scope.loggingIn = true;
        id.login($location.search().token, loginCallback);
      }

    }]
  );
};

module.exports = LoginTab;
<commit_msg>[FIX] Migrate to gatehub link<commit_after>'use strict';

var util = require('util');
var Tab = require('../client/tab').Tab;

var LoginTab = function() {
  Tab.call(this);
};

util.inherits(LoginTab, Tab);

LoginTab.prototype.tabName = 'login';
LoginTab.prototype.pageMode = 'single';
LoginTab.prototype.parent = 'main';

LoginTab.prototype.extraRoutes = [
  {name: '/login/:callback'}
];

LoginTab.prototype.angular = function(module) {
  module.controller('LoginCtrl', ['$scope', '$location', '$sce', 'rpTracker', 'rpId', 'rpAPI',
    function($scope, $location, $sce, tracker, id, api) {

      $scope.error = '';
      $scope.loggingIn = false;
      $scope.redirectTo = $location.path();
      $scope.backendMessages = [];
      $scope.authAction = $sce.trustAsResourceUrl(Options.backend_url + '/auth/login');

      if (id.loginStatus) {
        $location.path('/balance');
        return;
      }

      $scope.migrate = function() {
        tracker.track('Migrate to Gatehub', {
          'Status': 'success'
        });
        window.location.assign('https://signin.gatehub.net/signup?client_id=&redirect_uri=');
      };

      function loginCallback(err) {
        if (err) {
          $scope.loggingIn = false;
          $scope.status = '';

          $scope.status = 'Login failed:';

          if (err.name !== 'BlobError') {
            $scope.backendMessages.push(err.message);
          }

          if (!$scope.$$phase) {
            $scope.$apply();
          }

          return;
        }

        api.getUserProfile().success(function(profile) {

          store.set('profile_status', profile.ids_status);
          store.set('profile_country', profile.country);

          $scope.loggingIn = false;
          $scope.status = '';

          //store.set('profile_status', $location.search().status);

          if ($location.search().redirect_to) {
            $location.url($location.search().redirect_to);
          } else {
            $location.path('/balance').search('');
          }

          tracker.track('Login', {
            'Status': 'success'
          });
        });
      }

      $scope.submitForm = function(authAction) {
        $scope.redirectTo = encodeURIComponent($location.url());
        $scope.authAction = $sce.trustAsResourceUrl(Options.backend_url + '/auth/' + authAction);
      };

      // if ($routeParams.callback === 'callback' && $routeParams.token ) {
      if ($location.path() === '/login/callback' && $location.search().token) {
        $scope.loggingIn = true;
        id.login($location.search().token, loginCallback);
      }

    }]
  );
};

module.exports = LoginTab;
<|endoftext|>


<commit_before>
import { FEATURE_ENTRANCES } from '../core/constants';
import { GlyphString } from './GlyphString';

import { Object3D, Vector3, Triangle, Geometry, Points, PointsMaterial, CanvasTexture } from '../../../../three.js/src/Three';

// preallocated objects for projected area calculation

var A = new Vector3();
var B = new Vector3();
var C = new Vector3();
var D = new Vector3();

var T1 = new Triangle( A, B, C );
var T2 = new Triangle( A, C, D );

var clusterMaterialCache = [];
var markerGeometry = new Geometry();

markerGeometry.vertices.push( new Vector3( 0, 0, 32 ) );

function getClusterMaterial ( count ) {

	var material = clusterMaterialCache[ count ];

	if ( material !== undefined ) return material;

	var markerSize = 64;
	var halfSize = markerSize / 2;

	var canvas = document.createElement( 'canvas' );

	if ( ! canvas ) console.error( 'creating canvas for glyph atlas failed' );

	canvas.width  = markerSize;
	canvas.height = markerSize;

//	document.body.appendChild( canvas ); // FIXME debug code

	var ctx = canvas.getContext( '2d' );

	if ( ! ctx ) console.error( 'cannot obtain 2D canvas' );

	// set transparent background

	ctx.fillStyle = 'rgba( 0, 0, 0, 0 )';
	ctx.fillRect( 0, 0, markerSize, markerSize );

	var fontSize = 40;

	ctx.textAlign = 'center';
	ctx.font = 'bold ' + fontSize + 'px helvetica,sans-serif';
	ctx.fillStyle = '#ffffff';

	var gradient = ctx.createRadialGradient( halfSize, halfSize, 30, halfSize, halfSize, 0 );

	gradient.addColorStop( 0.0, 'rgba( 255, 128, 0, 64 )' );
	gradient.addColorStop( 0.3, 'rgba( 255, 200, 0, 255 )' );
	gradient.addColorStop( 1.0, 'rgba( 255, 255, 0, 255 )' );

	ctx.fillStyle = gradient;

	ctx.beginPath();
	ctx.arc( halfSize, halfSize, 30, 0, Math.PI * 2 );
	ctx.fill();

	ctx.fillStyle = 'rgba( 0, 0, 0, 255 )';

	ctx.fillText( count, halfSize, halfSize + 15 );

	material = new PointsMaterial( { map: new CanvasTexture( canvas ), size: 32, depthTest: false, transparent: true, alphaTest: 0.8, sizeAttenuation: false } );

	clusterMaterialCache[ count ] = material;

	return material;

}

function makeClusterMarker ( count ) {

	return new Points( markerGeometry, getClusterMaterial( count ) );

}

function QuadTree ( xMin, xMax, yMin, yMax ) {

	this.nodes = new Array( 4 );
	this.count = 0;
	this.markers = [];
	this.quadMarker = null;
	this.centroid = new Vector3();

	this.xMin = xMin;
	this.xMax = xMax;

	this.yMin = yMin;
	this.yMax = yMax;

}

QuadTree.prototype.addNode = function ( marker, depth ) {

	// add marker into this quad and recurse to inner quads

	var index = 0;
	var position = marker.position;

	this.markers.push( marker );
	this.centroid.add( marker.position );

	this.count++;

	if ( depth-- === 0 ) return;

	var xMid = ( this.xMin + this.xMax ) / 2;
	var yMid = ( this.yMin + this.yMax ) / 2;

	if ( position.x > xMid ) index += 1;
	if ( position.y > yMid ) index += 2;

	var subQuad = this.nodes[ index ];

	if ( subQuad === undefined ) {

		switch ( index ) {

		case 0:

			subQuad = new QuadTree( this.xMin, xMid, this.yMin, yMid );
			break;

		case 1:

			subQuad = new QuadTree( xMid, this.xMax, this.yMin, yMid );
			break;

		case 2:

			subQuad = new QuadTree( this.xMin, xMid, yMid, this.yMax );
			break;

		case 3:

			subQuad = new QuadTree( xMid, this.xMax, yMid, this.yMax );
			break;

		}

		this.nodes[ index ] = subQuad;

	}

	subQuad.addNode( marker, depth );

};

QuadTree.prototype.check = function ( cluster ) {

	var subQuad;

	for ( var i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) {

			// prune quads that will never be clustered. will not be checked after first pass

			if ( subQuad.count < 2 ) {

				this.nodes[ i ] = undefined;

				continue;

			}

			// test for projected area for quad containing multiple markers

			var area = subQuad.projectedArea( cluster );

//			console.log( 'area', area );

			if ( area < 0.80 ) { // FIXME calibrate by screen size ???

				subQuad.clusterMarkers( cluster );

			} else {

				subQuad.showMarkers();
				subQuad.check( cluster );

			}

		}

	}

};

QuadTree.prototype.showMarkers = function () {

	var markers = this.markers;

	// hide the indiviual markers in this quad

	for ( var i = 0, l = markers.length; i < l; i++ ) {

		markers[ i ].visible = true;

	}

	if ( this.quadMarker !== null ) this.quadMarker.visible = false;

};

QuadTree.prototype.clusterMarkers = function ( cluster ) {

	var i, l, subQuad;
	var markers = this.markers;

	// hide the indiviual markers in this quad

	for ( i = 0, l = markers.length; i < l; i++ ) {

		markers[ i ].visible = false;

	}

	// hide quadMarkers for contained quads

	for ( i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) subQuad.hideQuadMarkers();

	}

	if ( this.quadMarker === null ) {

		var quadMarker = makeClusterMarker( this.count );

		// set to center of distribution of markers in this quad.

		quadMarker.position.copy( this.centroid ).divideScalar( this.count );
		quadMarker.layers.set( FEATURE_ENTRANCES );

		cluster.add( quadMarker );

		this.quadMarker = quadMarker;

	}

	this.quadMarker.visible = true;

};

QuadTree.prototype.hideQuadMarkers = function () {

	var subQuad;

	if ( this.quadMarker ) this.quadMarker.visible = false;

	for ( var i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) subQuad.hideQuadMarkers();

	}

};

QuadTree.prototype.projectedArea = function ( cluster ) {

	var camera = cluster.camera;
	var matrixWorld = cluster.matrixWorld;
	var zAverage = this.centroid.z / this.count;

	A.set( this.xMin, this.yMin, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	B.set( this.xMin, this.yMax, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	C.set( this.xMax, this.yMax, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	D.set( this.xMax, this.yMin, zAverage ).applyMatrix4( matrixWorld ).project( camera );

	return T1.area() + T2.area();

};

function ClusterMarkers ( limits, maxDepth ) {

	Object3D.call( this );

	this.maxDepth = maxDepth;

	this.type = 'CV.ClusterMarker';

	var min = limits.min;
	var max = limits.max;

	this.quadTree = new QuadTree( min.x, max.x, min.y, max.y );

	return this;

}

ClusterMarkers.prototype = Object.create( Object3D.prototype );

ClusterMarkers.prototype.constructor = ClusterMarkers;

ClusterMarkers.prototype.addMarker = function ( position, label ) {

	// create marker

	var marker = new GlyphString( label, window.glyphMaterial );

	marker.layers.set( FEATURE_ENTRANCES );
	marker.position.copy( position );

	this.quadTree.addNode( marker, this.maxDepth );

	this.add( marker );

	return marker;

};

ClusterMarkers.prototype.cluster = function ( camera ) {

	// determine which labels are too close together to be usefully displayed as separate objects.

	// immediate exit if only a single label or none.

	if ( this.children.length < 2 ) return;

	this.camera = camera;

	this.quadTree.check( this ) ;

	return;

};

export { ClusterMarkers };

// EOF<commit_msg>add removed event handler<commit_after>
import { FEATURE_ENTRANCES } from '../core/constants';
import { GlyphString } from './GlyphString';

import { Object3D, Vector3, Triangle, Geometry, Points, PointsMaterial, CanvasTexture } from '../../../../three.js/src/Three';

// preallocated objects for projected area calculation

var A = new Vector3();
var B = new Vector3();
var C = new Vector3();
var D = new Vector3();

var T1 = new Triangle( A, B, C );
var T2 = new Triangle( A, C, D );

var clusterMaterialCache = [];
var markerGeometry = new Geometry();

markerGeometry.vertices.push( new Vector3( 0, 0, 32 ) );

function getClusterMaterial ( count ) {

	var material = clusterMaterialCache[ count ];

	if ( material !== undefined ) return material;

	var markerSize = 64;
	var halfSize = markerSize / 2;

	var canvas = document.createElement( 'canvas' );

	if ( ! canvas ) console.error( 'creating canvas for glyph atlas failed' );

	canvas.width  = markerSize;
	canvas.height = markerSize;

//	document.body.appendChild( canvas ); // FIXME debug code

	var ctx = canvas.getContext( '2d' );

	if ( ! ctx ) console.error( 'cannot obtain 2D canvas' );

	// set transparent background

	ctx.fillStyle = 'rgba( 0, 0, 0, 0 )';
	ctx.fillRect( 0, 0, markerSize, markerSize );

	var fontSize = 40;

	ctx.textAlign = 'center';
	ctx.font = 'bold ' + fontSize + 'px helvetica,sans-serif';
	ctx.fillStyle = '#ffffff';

	var gradient = ctx.createRadialGradient( halfSize, halfSize, 30, halfSize, halfSize, 0 );

	gradient.addColorStop( 0.0, 'rgba( 255, 128, 0, 64 )' );
	gradient.addColorStop( 0.3, 'rgba( 255, 200, 0, 255 )' );
	gradient.addColorStop( 1.0, 'rgba( 255, 255, 0, 255 )' );

	ctx.fillStyle = gradient;

	ctx.beginPath();
	ctx.arc( halfSize, halfSize, 30, 0, Math.PI * 2 );
	ctx.fill();

	ctx.fillStyle = 'rgba( 0, 0, 0, 255 )';

	ctx.fillText( count, halfSize, halfSize + 15 );

	material = new PointsMaterial( { map: new CanvasTexture( canvas ), size: 32, depthTest: false, transparent: true, alphaTest: 0.8, sizeAttenuation: false } );

	clusterMaterialCache[ count ] = material;

	return material;

}

function makeClusterMarker ( count ) {

	return new Points( markerGeometry, getClusterMaterial( count ) );

}

function QuadTree ( xMin, xMax, yMin, yMax ) {

	this.nodes = new Array( 4 );
	this.count = 0;
	this.markers = [];
	this.quadMarker = null;
	this.centroid = new Vector3();

	this.xMin = xMin;
	this.xMax = xMax;

	this.yMin = yMin;
	this.yMax = yMax;

}

QuadTree.prototype.addNode = function ( marker, depth ) {

	// add marker into this quad and recurse to inner quads

	var index = 0;
	var position = marker.position;

	this.markers.push( marker );
	this.centroid.add( marker.position );

	this.count++;

	if ( depth-- === 0 ) return;

	var xMid = ( this.xMin + this.xMax ) / 2;
	var yMid = ( this.yMin + this.yMax ) / 2;

	if ( position.x > xMid ) index += 1;
	if ( position.y > yMid ) index += 2;

	var subQuad = this.nodes[ index ];

	if ( subQuad === undefined ) {

		switch ( index ) {

		case 0:

			subQuad = new QuadTree( this.xMin, xMid, this.yMin, yMid );
			break;

		case 1:

			subQuad = new QuadTree( xMid, this.xMax, this.yMin, yMid );
			break;

		case 2:

			subQuad = new QuadTree( this.xMin, xMid, yMid, this.yMax );
			break;

		case 3:

			subQuad = new QuadTree( xMid, this.xMax, yMid, this.yMax );
			break;

		}

		this.nodes[ index ] = subQuad;

	}

	subQuad.addNode( marker, depth );

};

QuadTree.prototype.check = function ( cluster ) {

	var subQuad;

	for ( var i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) {

			// prune quads that will never be clustered. will not be checked after first pass

			if ( subQuad.count < 2 ) {

				this.nodes[ i ] = undefined;

				continue;

			}

			// test for projected area for quad containing multiple markers

			var area = subQuad.projectedArea( cluster );

//			console.log( 'area', area );

			if ( area < 0.80 ) { // FIXME calibrate by screen size ???

				subQuad.clusterMarkers( cluster );

			} else {

				subQuad.showMarkers();
				subQuad.check( cluster );

			}

		}

	}

};

QuadTree.prototype.showMarkers = function () {

	var markers = this.markers;

	// hide the indiviual markers in this quad

	for ( var i = 0, l = markers.length; i < l; i++ ) {

		markers[ i ].visible = true;

	}

	if ( this.quadMarker !== null ) this.quadMarker.visible = false;

};

QuadTree.prototype.clusterMarkers = function ( cluster ) {

	var i, l, subQuad;
	var markers = this.markers;

	// hide the indiviual markers in this quad

	for ( i = 0, l = markers.length; i < l; i++ ) {

		markers[ i ].visible = false;

	}

	// hide quadMarkers for contained quads

	for ( i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) subQuad.hideQuadMarkers();

	}

	if ( this.quadMarker === null ) {

		var quadMarker = makeClusterMarker( this.count );

		// set to center of distribution of markers in this quad.

		quadMarker.position.copy( this.centroid ).divideScalar( this.count );
		quadMarker.layers.set( FEATURE_ENTRANCES );

		cluster.add( quadMarker );

		this.quadMarker = quadMarker;

	}

	this.quadMarker.visible = true;

};

QuadTree.prototype.hideQuadMarkers = function () {

	var subQuad;

	if ( this.quadMarker ) this.quadMarker.visible = false;

	for ( var i = 0; i < 4; i++ ) {

		subQuad = this.nodes[ i ];

		if ( subQuad !== undefined ) subQuad.hideQuadMarkers();

	}

};

QuadTree.prototype.projectedArea = function ( cluster ) {

	var camera = cluster.camera;
	var matrixWorld = cluster.matrixWorld;
	var zAverage = this.centroid.z / this.count;

	A.set( this.xMin, this.yMin, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	B.set( this.xMin, this.yMax, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	C.set( this.xMax, this.yMax, zAverage ).applyMatrix4( matrixWorld ).project( camera );
	D.set( this.xMax, this.yMin, zAverage ).applyMatrix4( matrixWorld ).project( camera );

	return T1.area() + T2.area();

};

function ClusterMarkers ( limits, maxDepth ) {

	Object3D.call( this );

	this.maxDepth = maxDepth;

	this.type = 'CV.ClusterMarker';

	var min = limits.min;
	var max = limits.max;

	this.quadTree = new QuadTree( min.x, max.x, min.y, max.y );

	this.addEventListener( 'removed', _removed );

	var self = this;

	return this;

	function _removed () {

		// dispose of all GlyphString objects

		self.traverse(

			function _traverse ( obj ) {

				if ( obj.type === 'GlyphString' ) obj.geometry.dispose();

			}

		);

	};

}

ClusterMarkers.prototype = Object.create( Object3D.prototype );

ClusterMarkers.prototype.constructor = ClusterMarkers;

ClusterMarkers.prototype.addMarker = function ( position, label ) {

	// create marker

	var marker = new GlyphString( label, window.glyphMaterial );

	marker.layers.set( FEATURE_ENTRANCES );
	marker.position.copy( position );

	this.quadTree.addNode( marker, this.maxDepth );

	this.add( marker );

	return marker;

};

ClusterMarkers.prototype.cluster = function ( camera ) {

	// determine which labels are too close together to be usefully displayed as separate objects.

	// immediate exit if only a single label or none.

	if ( this.children.length < 2 ) return;

	this.camera = camera;

	this.quadTree.check( this ) ;

	return;

};

export { ClusterMarkers };

// EOF<|endoftext|>


<commit_before>/* global $, $$ */

import Draggable from 'gsap/src/uncompressed/utils/Draggable';
import TweenLite from 'gsap/src/uncompressed/TweenLite';

export default function touchInit() {
	const grid = d => TweenLite.set($('#emoji__grid'), {y: d !== false ? 0 : document.body.clientHeight});

	const options = d => TweenLite.set($('#emoji__options'), {x: d !== false ? 0 : document.body.clientWidth});

	$('#emoji__options-button').on('click', e => {
		e.stopPropagation();
		options(true);
	});
	$('#emoji__options-exit').on('click', () => options(false));
	$('#emoji__text-input').on('click', e => {
		e.stopPropagation();
		grid(true);
	});
	document.body.on('click', () => {
		grid(false);
		options(false);
		$('#skin-tone-selector').fire('close');
	});
	$('#emoji__options').on('click', e => {
		e.stopPropagation();
		$$('.custom-ul-select').forEach(el => el.fire('close'));
	});
	$('#emoji__grid').on('click', e => e.stopPropagation());

	Draggable.create('#emoji__grid', {
		type:'y',
		// trigger:'#emoji__grid-handle',
		onDragEnd: function () { grid(this.y < 100) },
		force3D:true
	});

	Draggable.create('#emoji__options', {
		type:'x',
		// trigger:'#emoji__options .heading',
		onDragEnd: function () { options(this.x < 100) },
		force3D:true
	});

	Draggable.create('#skin-tone-selector', {
		type:'y',
		bounds: '#emoji__options',
		edgeResistance:0.65,
		force3D:true
	});
}
<commit_msg>touch<commit_after>/* global $, $$ */

import Draggable from 'gsap/src/uncompressed/utils/Draggable';
import TweenLite from 'gsap/src/uncompressed/TweenLite';

export default function touchInit() {
	const grid = d => TweenLite.set($('#emoji__grid'), {y: d !== false ? 0 : document.body.clientHeight});

	const options = d => TweenLite.set($('#emoji__options'), {x: d !== false ? 0 : document.body.clientWidth});

	$('#emoji__options-button').on('click', e => {
		e.stopPropagation();
		options(true);
	});
	$('#emoji__options-exit').on('click', () => options(false));
	$('#emoji__text-input').on('click', e => {
		e.stopPropagation();
		grid(true);
	});
	document.body.on('click', () => {
		grid(false);
		options(false);
		$('#skin-tone-selector').fire('close');
	});
	$('#emoji__options').on('click', e => {
		e.stopPropagation();
		$$('.custom-ul-select').forEach(el => el.fire('close'));
	});
	$('#emoji__grid').on('click', e => e.stopPropagation());

	Draggable.create('#emoji__grid', {
		type:'y',
		trigger:'#emoji__grid-handle',
		onDragEnd: function () { grid(this.y < 100) }
	});

	Draggable.create('#emoji__options', {
		type:'x',
		trigger:'#emoji__options .heading',
		onDragEnd: function () { options(this.x < 100) }
	});

	Draggable.create('#skin-tone-selector', {
		type:'y',
		bounds: '#emoji__options',
		edgeResistance:0.65
	});
}
<|endoftext|>


<commit_before>var ms = require("mustache");

/**
 * Utility class provising various functionality to rendering mustache templates
 */
Blend.defineClass('Template', {
    singleton: true,
    /**
     * Common render function encapsulating the mustache render
     */
    render: function () {
        return ms.render.apply(ms, arguments);
    },
    renderMetaTags: function (items) {
        var me = this,
                template = '        <meta {{{attrs}}}/>',
                result = [];
        Blend.foreach(items, function (item) {
            result.push(ms.render(template, {
                attrs: me.renderAttrs(item)
            }));
        });
        return result.join("\n");
    },
    renderIndex: function (indexTemplateFile, headers) {
        var content;
        try {
            content = FileUtils.readFile(indexTemplateFile).toString();
            return ms.render(content, headers);
        } catch (e) {
            Logger.error(e);
            return null;
        }
    },
    renderAttrs: function (obj, rewrite) {
        var kv = [];
        rewrite = rewrite || function (k) {
            return k;
        };
        Blend.foreach(obj, function (v, k) {
            k = rewrite(k);
            kv.push(k + '="' + v + '"');
        });
        return kv.join(' ').trim();
    },
    renderScripts: function (items) {
        var me = this,
                template = '        <script type="text/javascript" src="{{{src}}}"></script>',
                result = [];
        Blend.foreach(items, function (item) {
            result.push(ms.render(template, item));
        });
        return result.join("\n");
    },
    renderCompactCSS: function (files) {
        var me = this, content = [],
                template = '        <style>{{{src}}}</style>';
        Blend.foreach(files, function (file) {
            content.push(FileUtils.readFile(file).toString());
        });
        return ms.render(template, {src: content.join("\n")});
    },
    renderScriptSource: function (source) {
        var me = this,
                template = '        <script type="text/javascript">{{{src}}}</script>';
        return ms.render(template, {
            src: source
        });
    },
});<commit_msg>Removed extra \n<commit_after>var ms = require("mustache");

/**
 * Utility class provising various functionality to rendering mustache templates
 */
Blend.defineClass('Template', {
    singleton: true,
    /**
     * Common render function encapsulating the mustache render
     */
    render: function () {
        return ms.render.apply(ms, arguments);
    },
    renderMetaTags: function (items) {
        var me = this,
                template = '        <meta {{{attrs}}}/>',
                result = [];
        Blend.foreach(items, function (item) {
            result.push(ms.render(template, {
                attrs: me.renderAttrs(item)
            }));
        });
        return result.join("\n");
    },
    renderIndex: function (indexTemplateFile, headers) {
        var content;
        try {
            content = FileUtils.readFile(indexTemplateFile).toString();
            return ms.render(content, headers);
        } catch (e) {
            Logger.error(e);
            return null;
        }
    },
    renderAttrs: function (obj, rewrite) {
        var kv = [];
        rewrite = rewrite || function (k) {
            return k;
        };
        Blend.foreach(obj, function (v, k) {
            k = rewrite(k);
            kv.push(k + '="' + v + '"');
        });
        return kv.join(' ').trim();
    },
    renderScripts: function (items) {
        var me = this,
                template = '        <script type="text/javascript" src="{{{src}}}"></script>',
                result = [];
        Blend.foreach(items, function (item) {
            result.push(ms.render(template, item));
        });
        return result.join("\n");
    },
    renderCompactCSS: function (files) {
        var me = this, content = [],
                template = '        <style>{{{src}}}</style>';
        Blend.foreach(files, function (file) {
            content.push(FileUtils.readFile(file).toString());
        });
        return ms.render(template, {src: content.join(" ")});
    },
    renderScriptSource: function (source) {
        var me = this,
                template = '        <script type="text/javascript">{{{src}}}</script>';
        return ms.render(template, {
            src: source
        });
    },
});<|endoftext|>


<commit_before>//= require jquery.hoverIntent

window.ChannelViewLayout = Backbone.Marionette.Layout.extend({
  tagName: "div",

  template: 'channels/_channel',

  regions: {
    factList: '#facts_for_channel',
    activityList: '#activity_for_channel'
  },

  initialize: function(opts) {
    this.initSubChannels();
    this.on('render', _.bind(function(){
      this.renderSubChannels();
      this.initSubChannelMenu();
      this.initAddToChannel();
      this.$('header .authority').tooltip({title: 'Authority of ' + this.model.attributes.created_by.username + ' on "' + this.model.attributes.title + '"'});
    },this));
  },

  initSubChannels: function() {
    if (this.model.get('inspectable?')){
      this.subchannelView = new SubchannelsView({
        collection: this.model.subchannels(),
        model: this.model
      });
    }
  },

  renderSubChannels: function(){
    if (this.subchannelView) {
      this.subchannelView.render();
      this.$('header .button-wrap').after(this.subchannelView.el);
    }
  },

  initSubChannelMenu: function() {
    if( this.model.get("followable?") ) {
      var addToChannelButton = this.$("#add-to-channel");
      var followChannelMenu =this.$("#follow-channel");

      followChannelMenu.css({"left": addToChannelButton.position().left});

      addToChannelButton.hoverIntent(
        function() { followChannelMenu.fadeIn("fast"); },
        function() { followChannelMenu.delay(600).fadeOut("fast"); }
      );

      followChannelMenu.on("mouseover", function() {
        followChannelMenu.stop(true, true).show();
      });

      followChannelMenu.on("mouseout", function() {
       if (!followChannelMenu.find("input#channel_title").is(":focus")) {
          followChannelMenu.delay(500).fadeOut("fast");
        }
      });
    }
  },

  initAddToChannel: function() {
    if ( this.$('#add-to-channel') && typeof currentUser !== "undefined" && typeof currentChannel !== "undefined" ) {
      this.addToChannelView = new AddToChannelView({
        collection: currentUser.channels,
        el: this.$('#follow-channel'),
        model: currentChannel,
        containingChannels: currentChannel.getOwnContainingChannels()
      }).render();
    }
  },
  onClose: function() {
    if ( this.addToChannelView ) {
      this.addToChannelView.close();
    }

    if ( this.subchannelView ) {
      this.subchannelView.close();
    }
  },
  activateTab: function(selector) {
    var tabs = this.$('.tabs ul');
    tabs.find('li').removeClass('active');
    tabs.find(selector).addClass('active');
  }

});

window.ChannelView = ChannelViewLayout.extend({

  getFactsView: function() {
    return new FactsView({
      collection: new ChannelFacts([],{
        channel: this.model
      }),
      model: this.model
    });

  },

  onRender: function() {
    this.factList.show(this.getFactsView())
    this.activateTab(".factlinks")
  }
});

window.ChannelActivitiesView = ChannelViewLayout.extend({

  getActivitiesView: function(){
    return new ActivitiesView({collection: this.collection});
  },

  onRender: function() {
    this.activityList.show(this.getActivitiesView());
    this.activateTab('.activity');
  }

});<commit_msg>semicolons<commit_after>//= require jquery.hoverIntent

window.ChannelViewLayout = Backbone.Marionette.Layout.extend({
  tagName: "div",

  template: 'channels/_channel',

  regions: {
    factList: '#facts_for_channel',
    activityList: '#activity_for_channel'
  },

  initialize: function(opts) {
    this.initSubChannels();
    this.on('render', _.bind(function(){
      this.renderSubChannels();
      this.initSubChannelMenu();
      this.initAddToChannel();
      this.$('header .authority').tooltip({title: 'Authority of ' + this.model.attributes.created_by.username + ' on "' + this.model.attributes.title + '"'});
    },this));
  },

  initSubChannels: function() {
    if (this.model.get('inspectable?')){
      this.subchannelView = new SubchannelsView({
        collection: this.model.subchannels(),
        model: this.model
      });
    }
  },

  renderSubChannels: function(){
    if (this.subchannelView) {
      this.subchannelView.render();
      this.$('header .button-wrap').after(this.subchannelView.el);
    }
  },

  initSubChannelMenu: function() {
    if( this.model.get("followable?") ) {
      var addToChannelButton = this.$("#add-to-channel");
      var followChannelMenu =this.$("#follow-channel");

      followChannelMenu.css({"left": addToChannelButton.position().left});

      addToChannelButton.hoverIntent(
        function() { followChannelMenu.fadeIn("fast"); },
        function() { followChannelMenu.delay(600).fadeOut("fast"); }
      );

      followChannelMenu.on("mouseover", function() {
        followChannelMenu.stop(true, true).show();
      });

      followChannelMenu.on("mouseout", function() {
       if (!followChannelMenu.find("input#channel_title").is(":focus")) {
          followChannelMenu.delay(500).fadeOut("fast");
        }
      });
    }
  },

  initAddToChannel: function() {
    if ( this.$('#add-to-channel') && typeof currentUser !== "undefined" && typeof currentChannel !== "undefined" ) {
      this.addToChannelView = new AddToChannelView({
        collection: currentUser.channels,
        el: this.$('#follow-channel'),
        model: currentChannel,
        containingChannels: currentChannel.getOwnContainingChannels()
      }).render();
    }
  },
  onClose: function() {
    if ( this.addToChannelView ) {
      this.addToChannelView.close();
    }

    if ( this.subchannelView ) {
      this.subchannelView.close();
    }
  },
  activateTab: function(selector) {
    var tabs = this.$('.tabs ul');
    tabs.find('li').removeClass('active');
    tabs.find(selector).addClass('active');
  }

});

window.ChannelView = ChannelViewLayout.extend({

  getFactsView: function() {
    return new FactsView({
      collection: new ChannelFacts([],{
        channel: this.model
      }),
      model: this.model
    });

  },

  onRender: function() {
    this.factList.show(this.getFactsView());
    this.activateTab(".factlinks");
  }
});

window.ChannelActivitiesView = ChannelViewLayout.extend({

  getActivitiesView: function(){
    return new ActivitiesView({collection: this.collection});
  },

  onRender: function() {
    this.activityList.show(this.getActivitiesView());
    this.activateTab('.activity');
  }

});
<|endoftext|>