chore: remove unused files

This commit is contained in:
Rafael Taranto 2024-06-07 10:53:57 +01:00
parent 9ce126aee3
commit 7cdc56cfdd
303 changed files with 40 additions and 81654 deletions

4
.gitignore vendored
View file

@ -33,8 +33,8 @@ seeds/
mnemonics/
certs/
blockchains/
test/stress/machines
test/stress/config.json
tests/stress/machines
tests/stress/config.json
lamassu.json
terraform.*

View file

@ -1,32 +0,0 @@
FROM node:14 as build-admin
WORKDIR /app
# COPY new-lamassu-admin/src packages/lamassu-admin/src
# COPY new-lamassu-admin/patches packages/lamassu-admin/patches
# COPY new-lamassu-admin/public packages/lamassu-admin/public
# COPY new-lamassu-admin/nginx packages/lamassu-admin/nginx
# COPY new-lamassu-admin/.env packages/lamassu-admin/.env
# COPY new-lamassu-admin/.eslintrc.js packages/lamassu-admin/.eslintrc.js
# COPY new-lamassu-admin/jsconfig.json packages/lamassu-admin/jsconfig.json
# COPY new-lamassu-admin/package.json packages/lamassu-admin/package.json
COPY new-lamassu-admin packages/lamassu-admin
WORKDIR /app/packages/lamassu-admin
# RUN npm install
RUN npm run build
FROM nginx:1.21.4-alpine as production-admin
ENV NODE_ENV=production
COPY --from=build-admin /app/packages/lamassu-admin/build /usr/share/nginx/html/
RUN rm /etc/nginx/conf.d/default.conf
COPY --from=build-admin /app/packages/lamassu-admin/nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD [ "nginx", "-g", "daemon off;" ]

View file

@ -0,0 +1,36 @@
FROM ubuntu:20.04 as base
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Lisbon
RUN apt-get update
RUN apt-get install -y -q curl \
sudo \
git \
python2-minimal \
build-essential \
libpq-dev \
net-tools
RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
RUN apt-get install nodejs -y -q
FROM base as build-l-s
WORKDIR /app
COPY bin/ packages/lamassu-server/bin
COPY lib/ packages/lamassu-server/lib
COPY data/ packages/lamassu-server/data
COPY tools/ packages/lamassu-server/tools
COPY migrations/ packages/lamassu-server/migrations
COPY package.json packages/lamassu-server/package.json
COPY Lamassu_CA.pem packages/lamassu-server/Lamassu_CA.pem
WORKDIR /app/packages/lamassu-server
RUN chmod +x tools/build-docker-certs.sh
RUN chmod +x bin/lamassu-server-entrypoint.sh
RUN npm install

View file

@ -1,53 +0,0 @@
#!/usr/bin/env node
var pgp = require('pg-promise')()
require('../lib/environment-helper')
const { PSQL_URL } = require('../lib/constants')
var db = pgp(PSQL_URL)
db.manyOrNone(`select * from transactions where incoming=false
and stage='final_request' and authority='machine'`)
.then(rs =>
db.tx(t =>
t.batch(rs.map(r => db.none(`insert into cash_in_txs (session_id,
device_fingerprint, to_address, crypto_atoms, crypto_code, fiat,
currency_code, fee, tx_hash, error, created) values ($1, $2, $3, $4, $5,
$6, $7, $8, $9, $10, $11)`, [r.session_id, r.device_fingerprint,
r.to_address, r.satoshis, r.crypto_code, r.fiat, r.currency_code, r.fee,
r.tx_hash, r.error, r.created]))
)
)
)
.then(() => db.manyOrNone(`select * from transactions where incoming=true
and stage='initial_request' and authority='pending'`))
.then(rs =>
db.tx(t =>
t.batch(rs.map(r => db.none(`insert into cash_out_txs (session_id,
device_fingerprint, to_address, crypto_atoms, crypto_code, fiat,
currency_code, tx_hash, phone, error, created) values ($1, $2, $3, $4, $5,
$6, $7, $8, $9, $10, $11)`, [r.session_id, r.device_fingerprint,
r.to_address, r.satoshis, r.crypto_code, r.fiat, r.currency_code,
r.tx_hash, r.phone, r.error, r.created]))
)
)
)
.then(() => db.manyOrNone(`select * from transactions where incoming=true
and stage='dispense' and authority='authorized'`))
.then(rs =>
db.tx(t =>
t.batch(rs.map(r =>
db.none(`update cash_out_txs set dispensed=true where session_id=$1`, [r.session_id])
.then(() => db.none(`insert into cash_out_actions (session_id, action,
created) values ($1, $2, $3)`, [r.session_id, 'dispensed', r.created]))
))
)
)
.then(() => pgp.end())
.then(() => console.log('Success.'))
.catch(e => {
console.log(e)
pgp.end()
})

View file

@ -1,3 +0,0 @@
#!/bin/bash
node bin/new-lamassu-admin-server --dev & node bin/new-graphql-dev-insecure

View file

@ -1,14 +0,0 @@
require('../lib/environment-helper')
const settingsLoader = require('../lib/new-settings-loader')
const pp = require('../lib/pp')
settingsLoader.loadLatest()
.then(r => {
pp('config')(r)
process.exit(0)
})
.catch(e => {
console.log(e.stack)
process.exit(1)
})

View file

@ -1,75 +0,0 @@
require('../lib/environment-helper')
const hdkey = require('ethereumjs-wallet/hdkey')
const _ = require('lodash/fp')
const hkdf = require('futoin-hkdf')
const pify = require('pify')
const fs = pify(require('fs'))
const Web3 = require('web3')
const web3 = new Web3()
const db = require('../lib/db')
const configManager = require('../lib/new-config-manager')
const { loadLatest } = require('../lib/new-settings-loader')
const mnemonicHelpers = require('../lib/mnemonic-helpers')
const { sweep } = require('../lib/wallet')
const ph = require('../lib/plugin-helper')
const MNEMONIC_PATH = process.env.MNEMONIC_PATH
function fetchWallet (settings, cryptoCode) {
return fs.readFile(MNEMONIC_PATH, 'utf8')
.then(mnemonic => {
const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet
const wallet = ph.load(ph.WALLET, plugin)
const rawAccount = settings.accounts[plugin]
const account = _.set('seed', computeSeed(masterSeed), rawAccount)
if (_.isFunction(wallet.run)) wallet.run(account)
return { wallet, account }
})
}
function computeSeed (masterSeed) {
return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' })
}
function paymentHdNode (account) {
const masterSeed = account.seed
if (!masterSeed) throw new Error('No master seed!')
const key = hdkey.fromMasterSeed(masterSeed)
return key.derivePath("m/44'/60'/0'/0'")
}
const getHdIndices = db => {
const sql = `SELECT id, crypto_code, hd_index FROM cash_out_txs WHERE hd_index IS NOT NULL AND status IN ('confirmed', 'instant') AND crypto_code = 'ETH'`
return db.any(sql)
}
const getCashoutAddresses = (settings, indices) => {
return Promise.all(_.map(it => {
return fetchWallet(settings, it.crypto_code)
.then(({ wallet, account }) => Promise.all([wallet, paymentHdNode(account).deriveChild(it.hd_index).getWallet().getChecksumAddressString()]))
.then(([wallet, address]) => Promise.all([address, wallet._balance(false, address, 'ETH')]))
.then(([address, balance]) => ({ address, balance: balance.toNumber(), cryptoCode: it.crypto_code, index: it.hd_index, txId: it.id }))
}, indices))
}
Promise.all([getHdIndices(db), loadLatest()])
.then(([indices, settings]) => Promise.all([settings, getCashoutAddresses(settings, indices)]))
.then(([settings, addresses]) => {
console.log('Found these cash-out addresses for ETH:')
console.log(addresses)
return Promise.all(_.map(it => {
// If the address only has dust in it, don't bother sweeping
if (web3.utils.fromWei(it.balance.toString()) > 0.00001) {
console.log(`Address ${it.address} found to have ${web3.utils.fromWei(it.balance.toString())} ETH in it. Sweeping...`)
return sweep(settings, it.txId, it.cryptoCode, it.index)
}
console.log(`Address ${it.address} contains no significant balance (${web3.utils.fromWei(it.balance.toString())}). Skipping the sweep process...`)
return Promise.resolve()
}, addresses))
})
.then(() => console.log('Process finished!'))

View file

@ -1,47 +0,0 @@
#!/usr/bin/env node
'use strict'
const pgp = require('pg-promise')()
require('../lib/environment-helper')
const { PSQL_URL } = require('../lib/constants')
const db = pgp(PSQL_URL)
db.many('select data from user_config', 'exchanges')
.then(rows => {
const config = rows.filter(r => r.type === 'exchanges')[0].data
const brain = rows.filter(r => r.type === 'unit')[0].data
const settings = config.exchanges.settings
const compliance = settings.compliance
const newConfig = {
global: {
cashInTransactionLimit: compliance.maximum.limit,
cashOutTransactionLimit: settings.fiatTxLimit,
cashInCommission: settings.commission,
cashOutCommission: settings.fiatCommission || settings.commission,
idVerificationEnabled: compliance.idVerificationEnabled,
idVerificationLimit: compliance.idVerificationLimit,
lowBalanceMargin: settings.lowBalanceMargin,
zeroConfLimit: settings.zeroConfLimit,
fiatCurrency: settings.currency,
topCashOutDenomination: settings.cartridges[0],
bottomCashOutDenomination: settings.cartridges[1],
virtualCashOutDenomination: settings.virtualCartridges[0],
machineLanguages: brain.locale.localeInfo.primaryLocales,
coins: settings.coins
},
accounts: settings.plugins.settings
}
db.none('insert into user_config (type, data) values ($1, $2)', ['global', newConfig])
.then(() => {
console.log('Success.')
process.exit(0)
})
.catch(err => {
console.error('Error: %s', err)
process.exit(1)
})
})

View file

@ -1,15 +0,0 @@
const car = require('../lib/coinatmradar/coinatmradar')
const plugins = require('../lib/plugins')
require('../lib/new-settings-loader').loadLatest()
.then(settings => {
const pi = plugins(settings)
return pi.getRawRates()
.then(rates => {
return car.update(rates, settings)
.then(require('../lib/pp')('DEBUG100'))
.catch(console.log)
.then(() => process.exit())
})
})

View file

@ -1,14 +0,0 @@
const express = require('express')
const app = express()
app.use(express.raw({ type: '*/*' }))
app.post('/api/lamassu', (req, res) => {
console.log(req.headers)
console.log(req.body.toString())
res.send('Hello World!')
})
app.listen(3200, () => console.log('Example app listening on port 3200!'))
// "url": "https://coinatmradar.info/api/lamassu/"

View file

@ -1,8 +0,0 @@
const settingsLoader = require('../lib/new-settings-loader')
const configManager = require('../lib/new-config-manager')
settingsLoader.loadLatest()
.then(settings => {
const config = settings.config
require('../lib/pp')('config')(configManager.getAllCryptoCurrencies(config))
})

View file

@ -1,19 +0,0 @@
const got = require('got')
const tx = {
sessionId: 'a9fdfedc-1d45-11e6-be13-2f68ff6306b9',
toAddress: '1DrK44np3gMKuvcGeFVv9Jk67zodP52eMu',
fiat: 10
}
const headers = {
'content-type': 'application/json',
'session-id': '36f17fbe-1d44-11e6-a1a9-bbe8a5a41617'
}
const body = JSON.stringify({tx: tx})
got('http://localhost:3000/dispense', {body: body, json: true, headers: headers})
.then(res => {
console.log(res.body)
})
.catch(console.log)

View file

@ -1,60 +0,0 @@
var db = require('../lib/postgresql_interface')
var connectionString = 'postgres://lamassu:lamassu@localhost/lamassu'
db.init(connectionString)
var session = {
id: '6ede611c-cd03-11e5-88ee-2b5fcfdb0bc2',
fingerprint: 'xx:xx'
}
var tx = {
fiat: 40,
satoshis: 6980000,
toAddress: '1xxx',
currencyCode: 'CAD',
incoming: false
}
var tx2 = {
fiat: 0,
satoshis: 6980000,
toAddress: '1xxx',
currencyCode: 'CAD',
incoming: false
}
db.addOutgoingTx(session, tx, function (err, res) {
console.log('DEBUG1')
console.log(err)
console.log(res)
})
db.addOutgoingTx(session, tx2, function (err, res) {
console.log('DEBUG2')
console.log(err)
console.log(res)
})
/*
setTimeout(function () {
db.addOutgoingTx(session, tx2, function (err, res) {
console.log('DEBUG2')
console.log(err)
console.log(res)
})
}, 0)
*/
var bills = {
uuid: 'c630338c-cd03-11e5-a9df-dbc9be2e9fbb',
currency: 'CAD',
toAddress: '1xxx',
deviceTime: Date.now(),
satoshis: 6980000,
fiat: 40
}
/*
db.recordBill(session, bills, function (err) {
console.log(err)
})
*/

View file

@ -1,10 +0,0 @@
const complianceTriggers = require('../lib/compliance-triggers')
const settingsLoader = require('../lib/new-settings-loader')
const configManager = require('../lib/new-config-manager')
settingsLoader.loadLatest().then(settings => {
const triggers = configManager.getTriggers(settings.config)
const response = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
console.log(response)
})

View file

@ -1,19 +0,0 @@
const rpc = require('../lib/plugins/common/json-rpc')
const method = ''
// const url = null
// const url = 'https://httpstat.us/500'
// const url = 'https://httpstat.us/400'
const url = 'https://httpstat.us/200'
const account = {
username: 'test',
password: 'test',
port: 8080,
url
}
rpc.fetch(account, method)
.then(res => console.log('got result', res))
.catch(err => console.error('gor error', err))

View file

@ -1,31 +0,0 @@
require('es6-promise').polyfill()
var notifier = require('../lib/notifier')
var db = require('../lib/postgresql_interface')
const { PSQL_URL } = require('../lib/constants')
function getBalances () {
return [
{fiatBalance: 23.2345, fiatCode: 'USD', cryptoCode: 'BTC'},
{fiatBalance: 23, fiatCode: 'USD', cryptoCode: 'ETH'}
]
}
db.init(PSQL_URL)
notifier.init(db, getBalances, {lowBalanceThreshold: 10})
console.log('DEBUG0')
notifier.checkStatus()
.then(function (alertRec) {
console.log('DEBUG1')
console.log('%j', alertRec)
var subject = notifier.alertSubject(alertRec)
console.log(subject)
var body = notifier.printEmailAlerts(alertRec)
console.log(body)
console.log(notifier.alertFingerprint(alertRec))
process.exit(0)
})
.catch(function (err) {
console.log(err.stack)
process.exit(1)
})

View file

@ -1,16 +0,0 @@
const compliance = require('../lib/compliance')
const ofac = require('../lib/ofac/index')
const [customerId, firstName, lastName, dateOfBirth] = process.argv.slice(2)
const customer = {
id: customerId,
idCardData: {firstName, lastName, dateOfBirth}
}
const deviceId = 'test-device'
ofac.load()
.then(() => compliance.validationPatch(deviceId, true, customer))
.then(console.log)
.catch(err => console.log(err))

View file

@ -1,10 +0,0 @@
const plugins = require('../lib/plugins')
const settingsLoader = require('../lib/new-settings-loader')
const pp = require('../lib/pp')
settingsLoader.loadLatest()
.then(settings => {
console.log('DEBUG300')
const pi = plugins(settings)
pi.getRates().then(r => console.log(JSON.stringify(r)))
})

View file

@ -1,26 +0,0 @@
#!/usr/bin/env node
'use strict'
const fs = require('fs')
const path = require('path')
const os = require('os')
const bip39 = require('bip39')
require('../lib/environment-helper')
const setEnvVariable = require('../tools/set-env-var')
if (process.env.MNEMONIC_PATH && !process.env.SEED_PATH) {
const mnemonic = fs.readFileSync(process.env.MNEMONIC_PATH, 'utf8')
const seed = bip39.mnemonicToEntropy(mnemonic.split('\n').join(' ').trim()).toString('hex')
setEnvVariable('SEED_PATH', path.resolve(os.homedir(), '.lamassu', 'seeds', 'seed.txt'))
if (!fs.existsSync(path.dirname(process.env.SEED_PATH))) {
fs.mkdirSync(path.dirname(process.env.SEED_PATH))
}
if (!fs.existsSync(process.env.SEED_PATH)) {
fs.writeFileSync(process.env.SEED_PATH, seed, 'utf8')
}
}

View file

@ -1,30 +0,0 @@
require('es6-promise').polyfill()
var config = require('../lib/new-settings-loader')
var sms = require('../lib/sms')
var rand = Math.floor(Math.random() * 1e6)
var db = config.connection
var rec = {
email: {
subject: 'Test email ' + rand,
body: 'This is a test email from lamassu-server'
},
sms: {
toNumber: '666',
body: '[Lamassu] This is a test sms ' + rand
}
}
config.loadLatest(db)
.then(function (config) {
sms.sendMessage(config, rec)
.then(function () {
console.log('Success.')
process.exit(0)
})
.catch(function (err) {
console.log(err.stack)
process.exit(1)
})
})

View file

@ -1,20 +0,0 @@
const strike = require('../lib/plugins/wallet/strike/strike')
const BN = require('../lib/bn')
const account = {token: 'xxx'}
strike.newAddress(account, { cryptoCode: 'BTC', cryptoAtoms: new BN(10000) })
.then(r => {
console.log(r)
const toAddress = r
const requested = null
const cryptoCode = 'BTC'
setInterval(() => {
strike.getStatus(account, toAddress, requested, cryptoCode)
.then(console.log)
.catch(r => console.log(r.message))
}, 2000)
})
.catch(console.log)

View file

@ -125,19 +125,4 @@ services:
- LOG_LEVEL=info
depends_on:
lamassu-server:
condition: service_started
# admin:
# container_name: lamassu-admin
# build:
# context: .
# dockerfile: Dockerfiles/admin.Dockerfile
# target: production-admin
# restart: always
# ports:
# - 80:80
# networks:
# - lamassu-network
# depends_on:
# lamassu-admin-server:
# condition: service_started
condition: service_started

View file

@ -1,23 +0,0 @@
lamassu-remote-install
===============
This will install your Lamassu Bitcoin Machine remote server.
Instructions
------------
1. Start a new Digital Ocean droplet
2. ssh into the droplet
```
ssh root@<your-new-ip-address>
```
3. Run the following command once you're logged in (default branch name is master):
```
curl -sS https://raw.githubusercontent.com/lamassu/lamassu-server/master/lamassu-remote-install/install | bash -s -- <branch-name>
```
4. You should be set. Just follow the instructions on the screen to open your dashboard.

View file

@ -1,72 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "ubuntu/xenial64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
config.vm.network "forwarded_port", guest: 8081, host: 8091
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
config.vm.synced_folder ".", "/vagrant"
config.vm.synced_folder "../lamassu-scripts", "/lamassu-scripts"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
vb.memory = "2048"
end
#
# View the documentation for the provider you are using for more
# information on available options.
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
# such as FTP and Heroku are also available. See the documentation at
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
# config.push.define "atlas" do |push|
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# sudo apt-get update
# sudo apt-get install -y apache2
# SHELL
end

View file

@ -1,40 +0,0 @@
COPY user_config (id, type, data) FROM stdin;
1 exchanges {"exchanges" : {\
"settings": {\
"commission": 1.0,\
"compliance": {\
"maximum": {\
"limit": null\
}\
}\
},\
"plugins" : {\
"current": {\
"ticker": "bitpay",\
"transfer": "bitgo"\
},\
"settings": {\
"bitpay": {},\
"bitgo" : {}\
}\
}\
}\
}
\.
COPY user_config (id, type, data) FROM stdin;
2 unit { "brain": {\
"unit": {\
"ssn": "xx-1234-45",\
"owner": "Unlisted"\
},\
"locale": {\
"currency": "USD",\
"localeInfo": {\
"primaryLocale": "en-US",\
"primaryLocales": ["en-US"]\
}\
}\
}\
}
\.

View file

@ -1,280 +0,0 @@
#!/usr/bin/env bash
set -e
export LOG_FILE=/tmp/install.log
CERT_DIR=/etc/ssl/certs
KEY_DIR=/etc/ssl/private
CONFIG_DIR=/etc/lamassu
MIGRATE_STATE_PATH=$CONFIG_DIR/.migrate
LAMASSU_CA_PATH=$CERT_DIR/Lamassu_CA.pem
CA_KEY_PATH=$KEY_DIR/Lamassu_OP_Root_CA.key
CA_PATH=$CERT_DIR/Lamassu_OP_Root_CA.pem
SERVER_KEY_PATH=$KEY_DIR/Lamassu_OP.key
SERVER_CERT_PATH=$CERT_DIR/Lamassu_OP.pem
MNEMONIC_DIR=$CONFIG_DIR/mnemonics
MNEMONIC_FILE=$MNEMONIC_DIR/mnemonic.txt
BACKUP_DIR=/var/backups/postgresql
BLOCKCHAIN_DIR=/mnt/blockchains
OFAC_DATA_DIR=/var/lamassu/ofac
ID_PHOTO_CARD_DIR=/opt/lamassu-server/idphotocard
FRONTCAMERA_DIR=/opt/lamassu-server/frontcamera
OPERATOR_DIR=/opt/lamassu-server/operatordata
# Look into http://unix.stackexchange.com/questions/140734/configure-localtime-dpkg-reconfigure-tzdata
decho () {
echo `date +"%H:%M:%S"` $1
echo `date +"%H:%M:%S"` $1 >> $LOG_FILE
}
retry() {
local -r -i max_attempts="$1"; shift
local -r cmd="$@"
local -i attempt_num=1
until $cmd
do
if (( attempt_num == max_attempts ))
then
echo
echo "****************************************************************"
echo "Attempt $attempt_num failed and there are no more attempts left! ($cmd)"
return 1
else
echo
echo "****************************************************************"
echo "Attempt $attempt_num failed! Trying again in $attempt_num seconds..."
sleep $(( attempt_num++ ))
fi
done
}
rm -f $LOG_FILE
cat <<'FIG'
_
| | __ _ _ __ ___ __ _ ___ ___ _ _ ___ ___ _ ____ _____ _ __
| |/ _` | '_ ` _ \ / _` / __/ __| | | |_____/ __|/ _ \ '__\ \ / / _ \ '__|
| | (_| | | | | | | (_| \__ \__ \ |_| |_____\__ \ __/ | \ V / __/ |
|_|\__,_|_| |_| |_|\__,_|___/___/\__,_| |___/\___|_| \_/ \___|_|
FIG
echo -e "\nStarting \033[1mlamassu-server\033[0m install. This will take a few minutes...\n"
if [ "$(whoami)" != "root" ]; then
echo -e "This script has to be run as \033[1mroot\033[0m user"
exit 3
fi
release=$(lsb_release -rs)
processor=$(uname -i)
if [ "$release" != "16.04" ] || [ "$processor" != "x86_64" ]; then
echo "You're attempting to install on an unsupported Linux distribution or release ("$release $processor")."
echo
uname -a
echo
echo "Please return to DigitalOcean and create a droplet running Ubuntu 16.04 x64 instead."
exit 1
fi
# So we don't run out of memory
decho "Enabling swap file for install only..."
fallocate -l 1G /swapfile >> $LOG_FILE 2>&1
chmod 600 /swapfile >> $LOG_FILE 2>&1
mkswap /swapfile >> $LOG_FILE 2>&1
swapon /swapfile >> $LOG_FILE 2>&1
IP=$(ifconfig eth0 | grep "inet" | grep -v "inet6" | awk -F: '{print $2}' | awk '{print $1}')
decho "Updating system..."
sleep 10
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - >> $LOG_FILE 2>&1
apt update >> $LOG_FILE 2>&1
decho "Installing necessary packages..."
apt install nodejs python-minimal build-essential supervisor postgresql libpq-dev -y -q >> $LOG_FILE 2>&1
decho "Installing latest npm package manager for node..."
retry 3 npm -g --unsafe-perm install npm@5 >> $LOG_FILE 2>&1
NODE_MODULES=$(npm -g root)
NPM_BIN=$(npm -g bin)
decho "Installing lamassu-server..."
retry 3 npm -g --unsafe-perm install lamassu/lamassu-server#${1-master} >> $LOG_FILE 2>&1
decho "Generating mnemonic..."
mkdir -p $MNEMONIC_DIR >> $LOG_FILE 2>&1
SEED=$(openssl rand -hex 32)
MNEMONIC=$(bip39 $SEED)
echo "$MNEMONIC" > $MNEMONIC_FILE
decho "Creating postgres user..."
POSTGRES_PW=$(hkdf postgres-pw $SEED)
su -l postgres >> $LOG_FILE 2>&1 <<EOF
psql -c "CREATE ROLE lamassu_pg WITH LOGIN SUPERUSER PASSWORD '$POSTGRES_PW';"
createdb lamassu
EOF
mkdir -p $CERT_DIR >> $LOG_FILE 2>&1
mkdir -p $CONFIG_DIR >> $LOG_FILE 2>&1
decho "Generating SSL certificates..."
openssl genrsa \
-out $CA_KEY_PATH \
4096 >> $LOG_FILE 2>&1
openssl req \
-x509 \
-sha256 \
-new \
-nodes \
-key $CA_KEY_PATH \
-days 3650 \
-out $CA_PATH \
-subj "/C=IS/ST=/L=Reykjavik/O=Lamassu Operator CA/CN=lamassu-operator.is" \
>> $LOG_FILE 2>&1
openssl genrsa \
-out $SERVER_KEY_PATH \
4096 >> $LOG_FILE 2>&1
openssl req -new \
-key $SERVER_KEY_PATH \
-out /tmp/Lamassu_OP.csr.pem \
-subj "/C=IS/ST=/L=Reykjavik/O=Lamassu Operator/CN=$IP" \
-reqexts SAN \
-sha256 \
-config <(cat /etc/ssl/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=IP.1:$IP")) \
>> $LOG_FILE 2>&1
openssl x509 \
-req -in /tmp/Lamassu_OP.csr.pem \
-CA $CA_PATH \
-CAkey $CA_KEY_PATH \
-CAcreateserial \
-out $SERVER_CERT_PATH \
-extfile <(cat /etc/ssl/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=IP.1:$IP")) \
-extensions SAN \
-days 3650 >> $LOG_FILE 2>&1
rm /tmp/Lamassu_OP.csr.pem
decho "Copying Lamassu certificate authority..."
LAMASSU_CA_FILE=$NODE_MODULES/lamassu-server/Lamassu_CA.pem
cp $LAMASSU_CA_FILE $LAMASSU_CA_PATH
mkdir -p $OFAC_DATA_DIR
cat <<EOF > $CONFIG_DIR/lamassu.json
{
"postgresql": "postgres://lamassu_pg:$POSTGRES_PW@localhost/lamassu",
"mnemonicPath": "$MNEMONIC_FILE",
"lamassuCaPath": "$LAMASSU_CA_PATH",
"caPath": "$CA_PATH",
"certPath": "$SERVER_CERT_PATH",
"keyPath": "$SERVER_KEY_PATH",
"hostname": "$IP",
"logLevel": "info",
"migrateStatePath": "$MIGRATE_STATE_PATH",
"blockchainDir": "$BLOCKCHAIN_DIR",
"ofacDataDir": "$OFAC_DATA_DIR",
"idPhotoCardDir": "$ID_PHOTO_CARD_DIR",
"frontCameraDir": "$FRONTCAMERA_DIR",
"operatorDataDir": "$OPERATOR_DIR"
"strike": {
"baseUrl": "https://api.strike.acinq.co/api/"
},
"coinAtmRadar": {
"url": "https://coinatmradar.info/api/lamassu/"
},
"ofacSources": [
{
"name": "sdn_advanced",
"url": "https://www.treasury.gov/ofac/downloads/sanctions/1.0/sdn_advanced.xml"
},
{
"name": "cons_advanced",
"url": "https://www.treasury.gov/ofac/downloads/sanctions/1.0/cons_advanced.xml"
}
]
}
EOF
decho "Setting up database tables..."
lamassu-migrate >> $LOG_FILE 2>&1
decho "Setting up lamassu-admin..."
ADMIN_REGISTRATION_URL=`lamassu-register admin 2>> $LOG_FILE`
decho "Setting up backups..."
BIN=$(npm -g bin)
BACKUP_CMD=$BIN/lamassu-backup-pg
mkdir -p $BACKUP_DIR
BACKUP_CRON="@daily $BACKUP_CMD > /dev/null"
(crontab -l 2>/dev/null || echo -n ""; echo "$BACKUP_CRON") | crontab - >> $LOG_FILE 2>&1
$BACKUP_CMD >> $LOG_FILE 2>&1
decho "Setting up firewall..."
ufw allow ssh >> $LOG_FILE 2>&1
ufw allow 443/tcp >> $LOG_FILE 2>&1 # Admin
ufw allow 3000/tcp >> $LOG_FILE 2>&1 # Server
ufw allow 8071/tcp >> $LOG_FILE 2>&1 # Lamassu support
ufw -f enable >> $LOG_FILE 2>&1
decho "Setting up supervisor..."
cat <<EOF > /etc/supervisor/conf.d/lamassu-server.conf
[program:lamassu-server]
command=${NPM_BIN}/lamassu-server
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/lamassu-server.err.log
stdout_logfile=/var/log/supervisor/lamassu-server.out.log
environment=HOME="/root"
EOF
cat <<EOF > /etc/supervisor/conf.d/lamassu-admin-server.conf
[program:lamassu-admin-server]
command=${NPM_BIN}/lamassu-admin-server
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/lamassu-admin-server.err.log
stdout_logfile=/var/log/supervisor/lamassu-admin-server.out.log
environment=HOME="/root"
EOF
service supervisor restart >> $LOG_FILE 2>&1
decho "Disabling swap file..."
swapoff /swapfile >> $LOG_FILE 2>&1
# disable exitting on error in case DO changes motd scripts
set +e
chmod -x /etc/update-motd.d/*-release-upgrade
chmod -x /etc/update-motd.d/*-updates-available
chmod -x /etc/update-motd.d/*-reboot-required
chmod -x /etc/update-motd.d/*-help-text
chmod -x /etc/update-motd.d/*-cloudguest
set -e
# reset terminal to link new executables
hash -r
echo
decho "Done! Now it's time to configure Lamassu stack."
echo
echo -e "\n*** IMPORTANT ***"
echo "In a private space, run lamassu-mnemonic, write down the words"
echo "and keep them in a safe place."
echo
echo "This secret will allow you to retrieve system passwords, such "
echo "as the keys to your Ethereum account. However, you must still "
echo "backup your wallets separately. Visit support.lamassu.is for "
echo "details on regularly backing up your wallets and coins."
echo
echo
echo "Activation URL for lamassu-admin:"
echo $ADMIN_REGISTRATION_URL

View file

@ -1,133 +0,0 @@
const path = require('path')
const fs = require('fs')
const _ = require('lodash/fp')
const db = require('../db')
const configValidate = require('./config-validate')
const config = require('./config')
function loadSchemas () {
const schemasRoot = path.resolve(__dirname, 'schemas')
const schemaFiles = fs.readdirSync(schemasRoot)
const stripJson = fileName => fileName.slice(0, -5)
const readSchema = fileName => JSON.parse(fs.readFileSync(path.resolve(schemasRoot, fileName)))
return _.zipObject(_.map(stripJson, schemaFiles), _.map(readSchema, schemaFiles))
}
const schemas = loadSchemas()
function fetchAccounts () {
return db.oneOrNone('select data from user_config where type=$1 and schema_version=$2', ['accounts', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
.then(row => {
// Hard code this for now
const accounts = [{
code: 'blockcypher',
display: 'Blockcypher',
fields: [
{ code: 'confidenceFactor', display: 'Confidence Factor', fieldType: 'integer', required: true, value: 40 }
]
}]
return row
? Promise.resolve(row.data.accounts)
: db.none('insert into user_config (type, data, valid) values ($1, $2, $3)', ['accounts', {accounts}, true])
.then(fetchAccounts)
})
}
function selectedAccounts () {
const mapAccount = v => v.fieldLocator.fieldType === 'account' &&
v.fieldValue.value
const mapSchema = code => schemas[code]
return config.fetchConfig()
.then(conf => {
const accountCodes = _.uniq(conf.map(mapAccount)
.filter(_.identity))
return _.sortBy(_.get('display'), accountCodes.map(mapSchema)
.filter(_.identity))
})
}
function fetchAccountSchema (account) {
return schemas[account]
}
function mergeAccount (oldAccount, newAccount) {
if (!newAccount) return oldAccount
const newFields = newAccount.fields
const updateWithData = oldField => {
const newField = _.find(r => r.code === oldField.code, newFields)
const newValue = _.isUndefined(newField) ? oldField.value : newField.value
return _.set('value', newValue, oldField)
}
const updatedFields = oldAccount.fields.map(updateWithData)
return _.set('fields', updatedFields, oldAccount)
}
function getAccounts (accountCode) {
const schema = fetchAccountSchema(accountCode)
if (!schema) return Promise.reject(new Error('No schema for: ' + accountCode))
return fetchAccounts()
.then(accounts => {
if (_.isEmpty(accounts)) return [schema]
const account = _.find(r => r.code === accountCode, accounts)
const mergedAccount = mergeAccount(schema, account)
return updateAccounts(mergedAccount, accounts)
})
}
function elideSecrets (account) {
const elideSecret = field => {
return field.fieldType === 'password'
? _.set('value', !_.isEmpty(field.value), field)
: field
}
return _.set('fields', account.fields.map(elideSecret), account)
}
function getAccount (accountCode) {
return getAccounts(accountCode)
.then(accounts => _.find(r => r.code === accountCode, accounts))
.then(elideSecrets)
}
function save (accounts) {
return db.none('update user_config set data=$1 where type=$2 and schema_version=$3', [{accounts: accounts}, 'accounts', configValidate.SETTINGS_LOADER_SCHEMA_VERSION])
}
function updateAccounts (newAccount, accounts) {
const accountCode = newAccount.code
const isPresent = _.some(_.matchesProperty('code', accountCode), accounts)
const updateAccount = r => r.code === accountCode
? newAccount
: r
return isPresent
? _.map(updateAccount, accounts)
: _.concat(accounts, newAccount)
}
function updateAccount (account) {
return getAccounts(account.code)
.then(accounts => {
const merged = mergeAccount(_.find(_.matchesProperty('code', account.code), accounts), account)
return save(updateAccounts(merged, accounts))
})
.then(() => getAccount(account.code))
}
module.exports = {
selectedAccounts,
getAccount,
updateAccount
}

View file

@ -1,342 +0,0 @@
const EventEmitter = require('events')
const qs = require('querystring')
const fs = require('fs')
const path = require('path')
const express = require('express')
const app = express()
const https = require('https')
const serveStatic = require('serve-static')
const cookieParser = require('cookie-parser')
const argv = require('minimist')(process.argv.slice(2))
const got = require('got')
const morgan = require('morgan')
const helmet = require('helmet')
// const WebSocket = require('ws')
const http = require('http')
// const SocketIo = require('socket.io')
const makeDir = require('make-dir')
const _ = require('lodash/fp')
const machineLoader = require('../machine-loader')
const T = require('../time')
const logger = require('../logger')
const accounts = require('./accounts')
const config = require('./config')
const login = require('./login')
const pairing = require('./pairing')
const server = require('./server')
const transactions = require('./transactions')
const customers = require('../customers')
const logs = require('../logs')
const funding = require('./funding')
const supportServer = require('./admin-support')
const NEVER = new Date(Date.now() + 100 * T.years)
const REAUTHENTICATE_INTERVAL = T.minute
const HOSTNAME = process.env.HOSTNAME
const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR
const devMode = argv.dev
const version = require('../../package.json').version
logger.info('Version: %s', version)
if (!HOSTNAME) {
logger.error('no hostname specified.')
process.exit(1)
}
module.exports = {run}
function dbNotify () {
return got.post('http://localhost:3030/dbChange')
.catch(e => logger.error('lamassu-server not responding'))
}
const skip = (req, res) => req.path === '/api/status/' && res.statusCode === 200
// Note: no rate limiting applied since that would allow an attacker to
// easily DDoS by just hitting the aggregate rate limit. We assume the
// attacker has unlimited unique IP addresses.
//
// The best we can do at the application level is to make the authentication
// lookup very fast. There will only be a few users at most, so it's not a problem
// to keep them in memory, but we need to update right after a new one is added.
// For now, we believe that probability of sustained DDoS by saturating our ability to
// fetch from the DB is pretty low.
app.use(morgan('dev', {skip}))
app.use(helmet({noCache: true}))
app.use(cookieParser())
app.use(register)
app.use(authenticate)
app.use(express.json())
app.get('/api/totem', (req, res) => {
const name = req.query.name
if (!name) return res.status(400).send('Name is required')
return pairing.totem(HOSTNAME, name)
.then(totem => res.send(totem))
})
app.get('/api/accounts', (req, res) => {
accounts.selectedAccounts()
.then(accounts => res.json({accounts: accounts}))
})
app.get('/api/account/:account', (req, res) => {
accounts.getAccount(req.params.account)
.then(account => res.json(account))
})
app.post('/api/account', (req, res) => {
return accounts.updateAccount(req.body)
.then(account => res.json(account))
.then(() => dbNotify())
})
app.get('/api/config/:config', (req, res, next) =>
config.fetchConfigGroup(req.params.config)
.then(c => res.json(c))
.catch(next))
app.post('/api/config', (req, res, next) => {
config.saveConfigGroup(req.body)
.then(c => res.json(c))
.then(() => dbNotify())
.catch(next)
})
app.get('/api/accounts/account/:account', (req, res) => {
accounts.getAccount(req.params.account)
.then(r => res.send(r))
})
app.get('/api/machines', (req, res) => {
machineLoader.getMachineNames()
.then(r => res.send({machines: r}))
})
app.post('/api/machines', (req, res) => {
machineLoader.setMachine(req.body)
.then(() => machineLoader.getMachineNames())
.then(r => res.send({machines: r}))
.then(() => dbNotify())
})
app.get('/api/funding', (req, res) => {
return funding.getFunding()
.then(r => res.json(r))
})
app.get('/api/funding/:cryptoCode', (req, res) => {
const cryptoCode = req.params.cryptoCode
return funding.getFunding(cryptoCode)
.then(r => res.json(r))
})
app.get('/api/status', (req, res, next) => {
return Promise.all([server.status(), config.validateCurrentConfig()])
.then(([serverStatus, invalidConfigGroups]) => res.send({
server: serverStatus,
invalidConfigGroups
}))
.catch(next)
})
app.get('/api/transactions', (req, res, next) => {
return transactions.batch()
.then(r => res.send({transactions: r}))
.catch(next)
})
app.get('/api/transaction/:id', (req, res, next) => {
return transactions.single(req.params.id)
.then(r => {
if (!r) return res.status(404).send({Error: 'Not found'})
return res.send(r)
})
})
app.patch('/api/transaction/:id', (req, res, next) => {
if (!req.query.cancel) return res.status(400).send({Error: 'Requires cancel'})
return transactions.cancel(req.params.id)
.then(r => {
return res.send(r)
})
.catch(() => res.status(404).send({Error: 'Not found'}))
})
app.get('/api/customers', (req, res, next) => {
return customers.batch()
.then(r => res.send({customers: r}))
.catch(next)
})
app.get('/api/customer/:id', (req, res, next) => {
return customers.getById(req.params.id)
.then(r => {
if (!r) return res.status(404).send({Error: 'Not found'})
return res.send(r)
})
})
app.get('/api/logs/:deviceId', (req, res, next) => {
return logs.getMachineLogs(req.params.deviceId)
.then(r => res.send(r))
.catch(next)
})
app.get('/api/logs', (req, res, next) => {
return machineLoader.getMachines()
.then(machines => {
const firstMachine = _.first(machines)
if (!firstMachine) return res.status(404).send({Error: 'No machines'})
return logs.getMachineLogs(firstMachine.deviceId)
.then(r => res.send(r))
})
.catch(next)
})
app.patch('/api/customer/:id', (req, res, next) => {
if (!req.params.id) return res.status(400).send({Error: 'Requires id'})
const token = req.token || req.cookies.token
return customers.update(req.params.id, req.query, token)
.then(r => res.send(r))
.catch(() => res.status(404).send({Error: 'Not found'}))
})
app.use((err, req, res, next) => {
logger.error(err)
return res.status(500).send(err.message)
})
const certOptions = {
key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(CERT_PATH)
}
app.use(serveStatic(path.resolve(__dirname, 'public')))
if (!fs.existsSync(ID_PHOTO_CARD_DIR)) {
makeDir.sync(ID_PHOTO_CARD_DIR)
}
if (!fs.existsSync(FRONT_CAMERA_DIR)) {
makeDir.sync(FRONT_CAMERA_DIR)
}
if (!fs.existsSync(OPERATOR_DATA_DIR)) {
makeDir.sync(OPERATOR_DATA_DIR)
}
app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, {index: false}))
app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, {index: false}))
app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, {index: false}))
function register (req, res, next) {
const otp = req.query.otp
if (!otp) return next()
return login.register(otp)
.then(r => {
if (r.expired) return res.status(401).send('OTP expired, generate new registration link')
// Maybe user is using old registration key, attempt to authenticate
if (!r.success) return next()
const cookieOpts = {
httpOnly: true,
secure: true,
domain: HOSTNAME,
sameSite: true,
expires: NEVER
}
const token = r.token
req.token = token
res.cookie('token', token, cookieOpts)
next()
})
}
function authenticate (req, res, next) {
const token = req.token || req.cookies.token
return login.authenticate(token)
.then(success => {
if (!success) return res.status(401).send('Authentication failed')
next()
})
}
process.on('unhandledRejection', err => {
logger.error(err.stack)
process.exit(1)
})
const socketServer = http.createServer()
const io = SocketIo(socketServer)
socketServer.listen(3060)
const socketEmitter = new EventEmitter()
io.on('connection', client => {
client.on('message', msg => socketEmitter.emit('message', msg))
})
const webServer = https.createServer(certOptions, app)
const wss = new WebSocket.Server({server: webServer})
function establishSocket (ws, token) {
return login.authenticate(token)
.then(success => {
if (!success) return ws.close(1008, 'Authentication error')
const listener = data => {
ws.send(JSON.stringify(data))
}
// Reauthenticate every once in a while, in case token expired
setInterval(() => {
return login.authenticate(token)
.then(success => {
if (!success) {
socketEmitter.removeListener('message', listener)
ws.close()
}
})
}, REAUTHENTICATE_INTERVAL)
socketEmitter.on('message', listener)
ws.send('Testing123')
})
}
wss.on('connection', ws => {
const token = qs.parse(ws.upgradeReq.headers.cookie).token
return establishSocket(ws, token)
})
function run () {
const serverPort = devMode ? 8072 : 443
const supportPort = 8071
const serverLog = `lamassu-admin-server listening on port ${serverPort}`
const supportLog = `lamassu-support-server listening on port ${supportPort}`
webServer.listen(serverPort, () => logger.info(serverLog))
supportServer.run(supportPort).then(logger.info(supportLog))
}

View file

@ -1,39 +0,0 @@
const fs = require('fs')
const cookieParser = require('cookie-parser')
const helmet = require('helmet')
const morgan = require('morgan')
const express = require('express')
const app = express()
const https = require('https')
const _ = require('lodash/fp')
const serveStatic = require('serve-static')
const path = require('path')
const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH
const LAMASSU_CA_PATH = process.env.LAMASSU_CA_PATH
app.use(morgan('dev'))
app.use(helmet({noCache: true}))
app.use(cookieParser())
app.use(express.json())
app.use(serveStatic(path.resolve(__dirname, '..', '..', 'public'), {
'index': ['support-index.html']
}))
const certOptions = {
key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(CERT_PATH),
ca: [fs.readFileSync(LAMASSU_CA_PATH)],
requestCert: true,
rejectUnauthorized: true
}
function run (port) {
return new Promise((resolve, reject) => {
const webServer = https.createServer(certOptions, app)
webServer.listen(port, resolve)
})
}
module.exports = { run }

View file

@ -1,99 +0,0 @@
const _ = require('lodash/fp')
const BN = require('../bn')
const settingsLoader = require('./settings-loader')
const configManager = require('./config-manager')
const wallet = require('../wallet')
const ticker = require('../ticker')
const { utils: coinUtils } = require('@lamassu/coins')
const machineLoader = require('../machine-loader')
module.exports = {getFunding}
function allScopes (cryptoScopes, machineScopes) {
const scopes = []
cryptoScopes.forEach(c => {
machineScopes.forEach(m => scopes.push([c, m]))
})
return scopes
}
function allMachineScopes (machineList, machineScope) {
const machineScopes = []
if (machineScope === 'global' || machineScope === 'both') machineScopes.push('global')
if (machineScope === 'specific' || machineScope === 'both') machineList.forEach(r => machineScopes.push(r))
return machineScopes
}
function getCryptos (config, machineList) {
const scopes = allScopes(['global'], allMachineScopes(machineList, 'both'))
const scoped = scope => configManager.scopedValue(scope[0], scope[1], 'cryptoCurrencies', config)
return _.uniq(_.flatten(_.map(scoped, scopes)))
}
function fetchMachines () {
return machineLoader.getMachines()
.then(machineList => machineList.map(r => r.deviceId))
}
function computeCrypto (cryptoCode, _balance) {
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
const unitScale = cryptoRec.unitScale
return new BN(_balance).shiftedBy(-unitScale).decimalPlaces(5)
}
function computeFiat (rate, cryptoCode, _balance) {
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
const unitScale = cryptoRec.unitScale
return new BN(_balance).shiftedBy(-unitScale).times(rate).decimalPlaces(5)
}
function getFunding (_cryptoCode) {
return Promise.all([settingsLoader.loadLatest(), fetchMachines()])
.then(([settings, machineList]) => {
const config = configManager.unscoped(settings.config)
const cryptoCodes = getCryptos(settings.config, machineList)
const cryptoCode = _cryptoCode || cryptoCodes[0]
const fiatCode = config.fiatCurrency
const pareCoins = c => _.includes(c.cryptoCode, cryptoCodes)
const cryptoCurrencies = coinUtils.cryptoCurrencies()
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
if (!cryptoRec) throw new Error(`Unsupported coin: ${cryptoCode}`)
const promises = [
wallet.newFunding(settings, cryptoCode),
ticker.getRates(settings, fiatCode, cryptoCode)
]
return Promise.all(promises)
.then(([fundingRec, ratesRec]) => {
const rates = ratesRec.rates
const rate = (rates.ask.plus(rates.bid)).div(2)
const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance
const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance)
const pending = fundingRec.fundingPendingBalance
const fiatPending = computeFiat(rate, cryptoCode, pending)
const fundingAddress = fundingRec.fundingAddress
const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress)
return {
cryptoCode,
cryptoDisplays,
fundingAddress,
fundingAddressUrl,
confirmedBalance: computeCrypto(cryptoCode, fundingConfirmedBalance).toFormat(5),
pending: computeCrypto(cryptoCode, pending).toFormat(5),
fiatConfirmedBalance: fiatConfirmedBalance.toFormat(2),
fiatPending: fiatPending.toFormat(2),
fiatCode
}
})
})
}

View file

@ -1,48 +0,0 @@
const crypto = require('crypto')
const db = require('../db')
function generateOTP (name) {
const otp = crypto.randomBytes(32).toString('hex')
const sql = 'insert into one_time_passes (token, name) values ($1, $2)'
return db.none(sql, [otp, name])
.then(() => otp)
}
function validateOTP (otp) {
const sql = `delete from one_time_passes
where token=$1
returning name, created < now() - interval '1 hour' as expired`
return db.one(sql, [otp])
.then(r => ({success: !r.expired, expired: r.expired, name: r.name}))
.catch(() => ({success: false, expired: false}))
}
function register (otp) {
return validateOTP(otp)
.then(r => {
if (!r.success) return r
const token = crypto.randomBytes(32).toString('hex')
const sql = 'insert into user_tokens (token, name) values ($1, $2)'
return db.none(sql, [token, r.name])
.then(() => ({success: true, token: token}))
})
.catch(() => ({success: false, expired: false}))
}
function authenticate (token) {
const sql = 'select token from user_tokens where token=$1'
return db.one(sql, [token]).then(() => true).catch(() => false)
}
module.exports = {
generateOTP,
register,
authenticate
}

View file

@ -1,32 +0,0 @@
const fs = require('fs')
const pify = require('pify')
const readFile = pify(fs.readFile)
const crypto = require('crypto')
const baseX = require('base-x')
const db = require('../db')
const pairing = require('../pairing')
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
const bsAlpha = baseX(ALPHA_BASE)
const CA_PATH = process.env.CA_PATH
const unpair = pairing.unpair
function totem (hostname, name) {
return readFile(CA_PATH)
.then(data => {
const caHash = crypto.createHash('sha256').update(data).digest()
const token = crypto.randomBytes(32)
const hexToken = token.toString('hex')
const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex')
const buf = Buffer.concat([caHash, token, Buffer.from(hostname)])
const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)'
return db.none(sql, [hexToken, caHexToken, name])
.then(() => bsAlpha.encode(buf))
})
}
module.exports = {totem, unpair}

View file

@ -1,18 +0,0 @@
{
"name": "lamassu-admin-elm",
"homepage": "https://github.com/lamassu/lamassu-admin-elm",
"authors": [
"Josh Harvey <josh@lamassu.is>"
],
"description": "",
"main": "",
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View file

@ -1,27 +0,0 @@
{
"name": "gridism",
"version": "0.2.2",
"author": "Coby Chapple",
"homepage": "http://cobyism.com/gridism",
"main": "./gridism.css",
"repository": {
"type": "git",
"url": "git://github.com/cobyism/gridism.git"
},
"ignore": [
"shapeshifter/",
"**/*.yml",
"**/*.html"
],
"license": "MIT",
"_release": "0.2.2",
"_resolution": {
"type": "version",
"tag": "0.2.2",
"commit": "490be0b6813d701dcc35a82b0bcc8f639e5ad63f"
},
"_source": "https://github.com/cobyism/gridism.git",
"_target": "^0.2.2",
"_originalSource": "gridism",
"_direct": true
}

View file

@ -1,19 +0,0 @@
Copyright (c) 2013 Coby Chapple.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,80 +0,0 @@
# Gridism
A simple responsive CSS grid. [View the demo →](http://cobyism.com/gridism/)
## Why?
### My process
When I design web layouts, my thought process usually goes something like this:
> Alright, in this section, I want a bit thats one third of the sections width,
> and then next to that I want another bit thats two thirds of the sectionss width.
> Now, in the next section…
I dont think in 12 or 16 column grids. Instead, my mental model basically just consists of the page being divided up into multiple full-width vertical sections, and each vertical section being divided up into simple fractions of the section width.
### Existing grid frameworks
Most frameworks Ive used dont match that thought process *at all*. I usually have to:
1. Remember how many columns are in the grid for the particular framework Im using.
1. Decide how I want to divide up this particular sections content.
1. Mentally do the conversion from what I want to see (one quarter + three quarters, for example) into the number of columns I need for the grid Im using.
1. Remember the class naming structure for the framework Im using. Is it `.span3`, `.grid_3`, `.col-3`, or something else altogether?
1. Deal with other hassles like clearing floats, messing with column padding to have the gutters look right, indicating which elements are the first in a row, and so forth.
Only the second step should be necessary.
### Gridisms Goals
I couldnt find a framework that matched this mental model of how I work, so I started hacking on Gridism with the following goals:
- Class names should be memorable and self-evident.
- Gutters and basic content padding should be taken care of.
- Clearing floats should be done automatically.
- Wrapped grid sections should be independant of vertical page sections.
- Frequently required utility classes should be provided.
- Common patterns for Responsive Design™ should be built-in.
I hope you find that this project is living up to those goals. If not, please [create an issue](https://github.com/cobyism/gridism/issues/new) and let me know.
## Installation
### 1. Get the files
The easiest way to use Gridism in your project is via the [Bower](http://twitter.github.com/bower) package manager.
```sh
bower install gridism
```
Elsewise, [download the zip folder](https://github.com/cobyism/gridism/archive/gh-pages.zip), extract it, and copy `gridism.css` into your projects folder. Boom. Done.
### 2. Link the stylesheet
Add the following stylesheet to your HTMLs `<head>` section:
```html
<link rel="stylesheet" href="bower_components/gridism/gridism.css">
```
**Note:** If you didnt install using Bower, you need to adjust the path of CSS file to match your file structure.
### 3. Viewport scale
Add the following meta tag to your HTMLs `<head>` section:
```html
<meta name="viewport" content="width=device-width,initial-scale=1">
```
Without this meta tag, mobiles and tablets might load your page as a scaled-down version of the desktop size, instead of resizing the content to match the devices actual viewport width.
## Contributing
Id :heart: to receive contributions to this project. It doesnt matter if its just a typo, or if youre proposing an overhaul of the entire project—Ill gladly take a look at your changes. Fork at will! :grinning:.
## License
Go nuts. See [LICENSE](https://github.com/cobyism/gridism/blob/gh-pages/LICENSE) (MIT).

View file

@ -1,17 +0,0 @@
{
"name": "gridism",
"version": "0.2.1",
"author": "Coby Chapple",
"homepage": "http://cobyism.com/gridism",
"main": "./gridism.css",
"repository": {
"type": "git",
"url": "git://github.com/cobyism/gridism.git"
},
"ignore": [
"shapeshifter/",
"**/*.yml",
"**/*.html"
],
"license": "MIT"
}

View file

@ -1,132 +0,0 @@
/*
* Gridism
* A simple, responsive, and handy CSS grid by @cobyism
* https://github.com/cobyism/gridism
*/
/* Preserve some sanity */
.grid,
.unit {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* Set up some rules to govern the grid */
.grid {
display: block;
clear: both;
}
.grid .unit {
float: left;
width: 100%;
padding: 10px;
}
/* This ensures the outer gutters are equal to the (doubled) inner gutters. */
.grid .unit:first-child { padding-left: 20px; }
.grid .unit:last-child { padding-right: 20px; }
/* Nested grids already have padding though, so let's nuke it */
.unit .unit:first-child { padding-left: 0; }
.unit .unit:last-child { padding-right: 0; }
.unit .grid:first-child > .unit { padding-top: 0; }
.unit .grid:last-child > .unit { padding-bottom: 0; }
/* Let people nuke the gutters/padding completely in a couple of ways */
.no-gutters .unit,
.unit.no-gutters {
padding: 0 !important;
}
/* Wrapping at a maximum width is optional */
.wrap .grid,
.grid.wrap {
max-width: 978px;
margin: 0 auto;
}
/* Width classes also have shorthand versions numbered as fractions
* For example: for a grid unit 1/3 (one third) of the parent width,
* simply apply class="w-1-3" to the element. */
.grid .whole, .grid .w-1-1 { width: 100%; }
.grid .half, .grid .w-1-2 { width: 50%; }
.grid .one-third, .grid .w-1-3 { width: 33.3332%; }
.grid .two-thirds, .grid .w-2-3 { width: 66.6665%; }
.grid .one-quarter,
.grid .one-fourth, .grid .w-1-4 { width: 25%; }
.grid .three-quarters,
.grid .three-fourths, .grid .w-3-4 { width: 75%; }
.grid .one-fifth, .grid .w-1-5 { width: 20%; }
.grid .two-fifths, .grid .w-2-5 { width: 40%; }
.grid .three-fifths, .grid .w-3-5 { width: 60%; }
.grid .four-fifths, .grid .w-4-5 { width: 80%; }
.grid .golden-small, .grid .w-g-s { width: 38.2716%; } /* Golden section: smaller piece */
.grid .golden-large, .grid .w-g-l { width: 61.7283%; } /* Golden section: larger piece */
/* Clearfix after every .grid */
.grid {
*zoom: 1;
}
.grid:before, .grid:after {
display: table;
content: "";
line-height: 0;
}
.grid:after {
clear: both;
}
/* Utility classes */
.align-center { text-align: center; }
.align-left { text-align: left; }
.align-right { text-align: right; }
.pull-left { float: left; }
.pull-right { float: right; }
/* A property for a better rendering of images in units: in
this way bigger pictures are just resized if the unit
becomes smaller */
.unit img {
max-width: 100%;
}
/* Hide elements using this class by default */
.only-on-mobiles {
display: none !important;
}
/* Responsive Stuff */
@media screen and (max-width: 568px) {
/* Stack anything that isn't full-width on smaller screens
and doesn't provide the no-stacking-on-mobiles class */
.grid:not(.no-stacking-on-mobiles) > .unit {
width: 100% !important;
padding-left: 20px;
padding-right: 20px;
}
.unit .grid .unit {
padding-left: 0px;
padding-right: 0px;
}
/* Sometimes, you just want to be different on small screens */
.center-on-mobiles {
text-align: center !important;
}
.hide-on-mobiles {
display: none !important;
}
.only-on-mobiles {
display: block !important;
}
}
/* Expand the wrap a bit further on larger screens */
@media screen and (min-width: 1180px) {
.wider .grid,
.grid.wider {
max-width: 1180px;
margin: 0 auto;
}
}

View file

@ -1,40 +0,0 @@
{
"name": "qr-code",
"version": "0.1.8",
"homepage": "https://github.com/educastellano/qr-code",
"authors": [
"Eduard Castellano <educastellano08@gmail.com>"
],
"description": "Web Component for generating QR codes",
"main": "src/qr-code.js",
"keywords": [
"qr",
"qrcode",
"qr-code",
"webcomponent",
"customelement",
"web-components"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"demo"
],
"dependencies": {
"qrjs": "~0.1.2"
},
"_release": "0.1.8",
"_resolution": {
"type": "version",
"tag": "v0.1.8",
"commit": "28a413834c62d8ec7f5b3f3005fe2ee78e47e647"
},
"_source": "https://github.com/educastellano/qr-code.git",
"_target": "^0.1.8",
"_originalSource": "webcomponent-qr-code",
"_direct": true
}

View file

@ -1,94 +0,0 @@
# &lt;qr-code&gt;
Web Component for generating QR Codes, using (a [fork](https://github.com/educastellano/qr.js) of) [qr.js](https://github.com/lifthrasiir/qr.js) lib.
> Maintained by [Eduard Castellano](https://github.com/educastellano).
## Demo
> [Check it live](http://educastellano.github.io/qr-code/demo).
## Usage
* **NPM and Browserify** ([polyfill](https://github.com/WebComponents/webcomponentsjs) and the component):
Install:
```sh
npm install webcomponents.js
npm install webcomponent-qr-code
```
Import:
```js
require('webcomponents.js');
require('webcomponent-qr-code');
```
* **Bower** ([polyfill](https://github.com/WebComponents/webcomponentsjs), [qr.js](https://github.com/educastellano/qr.js) and the component):
Install:
```sh
bower install webcomponentsjs
bower install webcomponent-qr-code
```
Import:
```html
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="bower_components/qrjs/qr.js"></script>
<script src="bower_components/webcomponent-qr-code/src/qr-code.js"></script>
```
> You can also import the component with [HTML Imports](http://w3c.github.io/webcomponents/spec/imports/), but you still need to import the polyfill and the qr.js lib separately:
>
> ```html
> <link rel="import" href="bower_components/webcomponent-qr-code/src/qr-code.html">
> ```
* **Start using it!**
```html
<qr-code data="hello world!"></qr-code>
```
## Options
Attribute | Options | Default | Description
--- | --- | --- | ---
`data` | *string* | `null` | The information encoded by the QR code.
`format` | `png`, `html`, `svg` | `png` | Format of the QR code rendered inside the component.
`modulesize` | *int* | `5` | Size of the modules in *pixels*.
`margin` | *int* | `4` | Margin of the QR code in *modules*.
## Contributing
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -m 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D
## History
* v0.1.7 April 11, 2015
* Support for SVG
* v0.1.6 April 10, 2015
* Default attributes
* qr.js removed and used as a dependency
* Available in NPM
* v0.1.1 March 31, 2015
* Framework-agnostic webcomponent (no use of Polymer)
* Available in Bower
* v0.0.1 September 18, 2013
* Started project using [boilerplate-element](https://github.com/customelements/boilerplate-element)
## License
[MIT License](http://opensource.org/licenses/MIT)

View file

@ -1,30 +0,0 @@
{
"name": "qr-code",
"version": "0.1.7",
"homepage": "https://github.com/educastellano/qr-code",
"authors": [
"Eduard Castellano <educastellano08@gmail.com>"
],
"description": "Web Component for generating QR codes",
"main": "src/qr-code.js",
"keywords": [
"qr",
"qrcode",
"qr-code",
"webcomponent",
"customelement",
"web-components"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"demo"
],
"dependencies": {
"qrjs": "~0.1.2"
}
}

View file

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>&lt;qr-code&gt;</title>
</head>
<body>
<a href="demo">Demo here</a>
</body>
</html>

View file

@ -1 +0,0 @@
require('./src/qr-code.js')

View file

@ -1,29 +0,0 @@
{
"name": "webcomponent-qr-code",
"version": "0.1.8",
"description": "Web Component for generating QR codes",
"main": "qr-code.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/educastellano/qr-code.git"
},
"keywords": [
"qr",
"qrcode",
"qr-code",
"webcomponent",
"custom-element"
],
"author": "Eduard Castellano",
"license": "MIT",
"bugs": {
"url": "https://github.com/educastellano/qr-code/issues"
},
"homepage": "https://github.com/educastellano/qr-code",
"dependencies": {
"qrjs": "^0.1.2"
}
}

View file

@ -1,56 +0,0 @@
<polymer-element name="qr-code"
attributes="data format modulesize margin">
<script>
Polymer('qr-code', {
format: 'png',
dataChanged: function () {
this.generate();
},
generate: function () {
var options = {
modulesize: this.modulesize,
margin: this.margin === 0 ? -1 : this.margin
};
if (this.format === 'png') {
this.generatePNG(options);
}
else {
this.generateHTML(options);
}
},
generatePNG: function (options) {
var img;
try {
img = document.createElement('img');
img.src = QRCode.generatePNG(this.data, options);
this.clear();
this.appendChild(img);
}
catch (e) {
console.log('no canvas support');
}
},
generateHTML: function (options) {
var div = QRCode.generateHTML(this.data, options);
this.clear();
this.appendChild(div);
},
clear: function () {
var i;
for (i=0; i<this.children.length; i++) {
this.children[i].parentNode.removeChild(this.children[i]);
}
}
});
</script>
</polymer-element>

View file

@ -1 +0,0 @@
<script src="qr-code.js"></script>

View file

@ -1,144 +0,0 @@
'use strict';
(function(definition) {
if (typeof define === 'function' && define.amd) {
define(['QRCode'], definition);
} else if (typeof module === 'object' && module.exports) {
var QRCode = require('qrjs');
module.exports = definition(QRCode);
} else {
definition(window.QRCode);
}
})(function(QRCode) {
//
// Prototype
//
var proto = Object.create(HTMLElement.prototype, {
//
// Attributes
//
attrs: {
value: {
data: null,
format: 'png',
modulesize: 5,
margin: 4
}
},
defineAttributes: {
value: function () {
var attrs = Object.keys(this.attrs),
attr;
for (var i=0; i<attrs.length; i++) {
attr = attrs[i];
(function (attr) {
Object.defineProperty(this, attr, {
get: function () {
var value = this.getAttribute(attr);
return value === null ? this.attrs[attr] : value;
},
set: function (value) {
this.setAttribute(attr, value);
}
});
}.bind(this))(attr);
}
}
},
//
// LifeCycle Callbacks
//
createdCallback: {
value: function () {
this.createShadowRoot();
this.defineAttributes();
this.generate();
}
},
attributeChangedCallback: {
value: function (attrName, oldVal, newVal) {
var fn = this[attrName+'Changed'];
if (fn && typeof fn === 'function') {
fn.call(this, oldVal, newVal);
}
this.generate();
}
},
//
// Methods
//
getOptions: {
value: function () {
var modulesize = this.modulesize,
margin = this.margin;
return {
modulesize: modulesize !== null ? parseInt(modulesize) : modulesize,
margin: margin !== null ? parseInt(margin) : margin
};
}
},
generate: {
value: function () {
if (this.data !== null) {
if (this.format === 'png') {
this.generatePNG();
}
else if (this.format === 'html') {
this.generateHTML();
}
else if (this.format === 'svg') {
this.generateSVG();
}
else {
this.shadowRoot.innerHTML = '<div>qr-code: '+ this.format +' not supported!</div>'
}
}
else {
this.shadowRoot.innerHTML = '<div>qr-code: no data!</div>'
}
}
},
generatePNG: {
value: function () {
try {
var img = document.createElement('img');
img.src = QRCode.generatePNG(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(img);
}
catch (e) {
this.shadowRoot.innerHTML = '<div>qr-code: no canvas support!</div>'
}
}
},
generateHTML: {
value: function () {
var div = QRCode.generateHTML(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(div);
}
},
generateSVG: {
value: function () {
var div = QRCode.generateSVG(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(div);
}
},
clear: {
value: function () {
while (this.shadowRoot.lastChild) {
this.shadowRoot.removeChild(this.shadowRoot.lastChild);
}
}
}
});
//
// Register
//
document.registerElement('qr-code', {
prototype: proto
});
});

View file

@ -1,31 +0,0 @@
{
"name": "qrcodejs",
"version": "0.1.0",
"homepage": "https://github.com/CatTail/qrcodejs",
"authors": [
"davidshimjs ssm0123@gmail.com"
],
"description": "Cross-browser QRCode generator for javascript",
"main": "qrcode.js",
"keywords": [
"qrcode"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"_release": "0.1.0",
"_resolution": {
"type": "version",
"tag": "v0.1.0",
"commit": "71340740270b3c9d797ecaa7d7a75af36037217d"
},
"_source": "https://github.com/CatTail/qrcodejs.git",
"_target": "^0.1.0",
"_originalSource": "qrcodejs",
"_direct": true
}

View file

@ -1,14 +0,0 @@
The MIT License (MIT)
---------------------
Copyright (c) 2012 davidshimjs
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,43 +0,0 @@
# QRCode.js
QRCode.js is javascript library for making QRCode. QRCode.js supports Cross-browser with HTML5 Canvas and table tag in DOM.
QRCode.js has no dependencies.
## Basic Usages
```
<div id="qrcode"></div>
<script type="text/javascript">
new QRCode(document.getElementById("qrcode"), "http://jindo.dev.naver.com/collie");
</script>
```
or with some options
```
var qrcode = new QRCode("test", {
text: "http://jindo.dev.naver.com/collie",
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
```
and you can use some methods
```
qrcode.clear(); // clear the code.
qrcode.makeCode("http://naver.com"); // make another code.
```
## Browser Compatibility
IE6~10, Chrome, Firefox, Safari, Opera, Mobile Safari, Android, Windows Mobile, ETC.
## License
MIT License
## Contact
twitter @davidshimjs
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/davidshimjs/qrcodejs/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

View file

@ -1,21 +0,0 @@
{
"name": "qrcodejs",
"version": "0.1.0",
"homepage": "https://github.com/CatTail/qrcodejs",
"authors": [
"davidshimjs ssm0123@gmail.com"
],
"description": "Cross-browser QRCode generator for javascript",
"main": "qrcode.js",
"keywords": [
"qrcode"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View file

@ -1,44 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<title>Cross-Browser QRCode generator for Javascript</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="qrcode.js"></script>
</head>
<body>
<input id="text" type="text" value="http://jindo.dev.naver.com/collie" style="width:80%" /><br />
<div id="qrcode" style="width:100px; height:100px; margin-top:15px;"></div>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
width : 100,
height : 100
});
function makeCode () {
var elText = document.getElementById("text");
if (!elText.value) {
alert("Input a text");
elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
$("#text").
on("blur", function () {
makeCode();
}).
on("keydown", function (e) {
if (e.keyCode == 13) {
makeCode();
}
});
</script>
</body>

View file

@ -1,37 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-50 0 200 100">
<g id="qrcode"/>
<foreignObject x="-50" y="0" width="100" height="100">
<body xmlns="http://www.w3.org/1999/xhtml" style="padding:0; margin:0">
<div style="padding:inherit; margin:inherit; height:100%">
<textarea id="text" style="height:100%; width:100%; position:absolute; margin:inherit; padding:inherit">james</textarea>
</div>
<script type="application/ecmascript" src="qrcode.js"></script>
<script type="application/ecmascript">
var elem = document.getElementById("qrcode");
var qrcode = new QRCode(elem, {
width : 100,
height : 100
});
function makeCode () {
var elText = document.getElementById("text");
if (elText.value === "") {
//alert("Input a text");
//elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
document.getElementById("text").onkeyup = function (e) {
makeCode();
};
</script>
</body>
</foreignObject>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View file

@ -1,609 +0,0 @@
/**
* @fileoverview
* - Using the 'QRCode for Javascript library'
* - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
* - this library has no dependencies.
*
* @author davidshimjs
* @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
* @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
*/
var QRCode;
(function () {
//---------------------------------------------------------------------
// QRCode for JavaScript
//
// Copyright (c) 2009 Kazuhiko Arase
//
// URL: http://www.d-project.com/
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license.php
//
// The word "QR Code" is registered trademark of
// DENSO WAVE INCORPORATED
// http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//---------------------------------------------------------------------
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;
this.data = data;
this.parsedData = [];
// Added to support UTF-8 Characters
for (var i = 0, l = this.data.length; i < l; i++) {
var byteArray = [];
var code = this.data.charCodeAt(i);
if (code > 0x10000) {
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[3] = 0x80 | (code & 0x3F);
} else if (code > 0x800) {
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[2] = 0x80 | (code & 0x3F);
} else if (code > 0x80) {
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
byteArray[1] = 0x80 | (code & 0x3F);
} else {
byteArray[0] = code;
}
this.parsedData.push(byteArray);
}
this.parsedData = Array.prototype.concat.apply([], this.parsedData);
if (this.parsedData.length != this.data.length) {
this.parsedData.unshift(191);
this.parsedData.unshift(187);
this.parsedData.unshift(239);
}
}
QR8bitByte.prototype = {
getLength: function (buffer) {
return this.parsedData.length;
},
write: function (buffer) {
for (var i = 0, l = this.parsedData.length; i < l; i++) {
buffer.put(this.parsedData[i], 8);
}
}
};
function QRCodeModel(typeNumber, errorCorrectLevel) {
this.typeNumber = typeNumber;
this.errorCorrectLevel = errorCorrectLevel;
this.modules = null;
this.moduleCount = 0;
this.dataCache = null;
this.dataList = [];
}
QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
this.modules[r][6]=(r%2==0);}
for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
+buffer.getLengthInBits()
+">"
+totalDataCount*8
+")");}
if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
buffer.put(QRCodeModel.PAD1,8);}
return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
if(r==0&&c==0){continue;}
if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
while(n>=256){n-=255;}
return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,errorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
function _isSupportCanvas() {
return typeof CanvasRenderingContext2D != "undefined";
}
// android 2.x doesn't support Data-URI spec
function _getAndroid() {
var android = false;
var sAgent = navigator.userAgent;
if (/android/i.test(sAgent)) { // android
android = true;
aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i);
if (aMat && aMat[1]) {
android = parseFloat(aMat[1]);
}
}
return android;
}
var svgDrawer = (function() {
var Drawing = function (el, htOption) {
this._el = el;
this._htOption = htOption;
};
Drawing.prototype.draw = function (oQRCode) {
var _htOption = this._htOption;
var _el = this._el;
var nCount = oQRCode.getModuleCount();
var nWidth = Math.floor(_htOption.width / nCount);
var nHeight = Math.floor(_htOption.height / nCount);
this.clear();
function makeSVG(tag, attrs) {
var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (var k in attrs)
if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
return el;
}
var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight});
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
_el.appendChild(svg);
svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"}));
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
if (oQRCode.isDark(row, col)) {
var child = makeSVG("use", {"x": String(row), "y": String(col)});
child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template")
svg.appendChild(child);
}
}
}
};
Drawing.prototype.clear = function () {
while (this._el.hasChildNodes())
this._el.removeChild(this._el.lastChild);
};
return Drawing;
})();
var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
// Drawing in DOM by using Table tag
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
var Drawing = function (el, htOption) {
this._el = el;
this._htOption = htOption;
};
/**
* Draw the QRCode
*
* @param {QRCode} oQRCode
*/
Drawing.prototype.draw = function (oQRCode) {
var _htOption = this._htOption;
var _el = this._el;
var nCount = oQRCode.getModuleCount();
var nWidth = Math.floor(_htOption.width / nCount);
var nHeight = Math.floor(_htOption.height / nCount);
var aHTML = ['<table style="border:0;border-collapse:collapse;">'];
for (var row = 0; row < nCount; row++) {
aHTML.push('<tr>');
for (var col = 0; col < nCount; col++) {
aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
}
aHTML.push('</tr>');
}
aHTML.push('</table>');
_el.innerHTML = aHTML.join('');
// Fix the margin values as real size.
var elTable = _el.childNodes[0];
var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
}
};
/**
* Clear the QRCode
*/
Drawing.prototype.clear = function () {
this._el.innerHTML = '';
};
return Drawing;
})() : (function () { // Drawing in Canvas
function _onMakeImage() {
this._elImage.src = this._elCanvas.toDataURL("image/png");
this._elImage.style.display = "block";
this._elCanvas.style.display = "none";
}
// Android 2.1 bug workaround
// http://code.google.com/p/android/issues/detail?id=5141
if (this._android && this._android <= 2.1) {
var factor = 1 / window.devicePixelRatio;
var drawImage = CanvasRenderingContext2D.prototype.drawImage;
CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
if (("nodeName" in image) && /img/i.test(image.nodeName)) {
for (var i = arguments.length - 1; i >= 1; i--) {
arguments[i] = arguments[i] * factor;
}
} else if (typeof dw == "undefined") {
arguments[1] *= factor;
arguments[2] *= factor;
arguments[3] *= factor;
arguments[4] *= factor;
}
drawImage.apply(this, arguments);
};
}
/**
* Check whether the user's browser supports Data URI or not
*
* @private
* @param {Function} fSuccess Occurs if it supports Data URI
* @param {Function} fFail Occurs if it doesn't support Data URI
*/
function _safeSetDataURI(fSuccess, fFail) {
var self = this;
self._fFail = fFail;
self._fSuccess = fSuccess;
// Check it just once
if (self._bSupportDataURI === null) {
var el = document.createElement("img");
var fOnError = function() {
self._bSupportDataURI = false;
if (self._fFail) {
_fFail.call(self);
}
};
var fOnSuccess = function() {
self._bSupportDataURI = true;
if (self._fSuccess) {
self._fSuccess.call(self);
}
};
el.onabort = fOnError;
el.onerror = fOnError;
el.onload = fOnSuccess;
el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
return;
} else if (self._bSupportDataURI === true && self._fSuccess) {
self._fSuccess.call(self);
} else if (self._bSupportDataURI === false && self._fFail) {
self._fFail.call(self);
}
};
/**
* Drawing QRCode by using canvas
*
* @constructor
* @param {HTMLElement} el
* @param {Object} htOption QRCode Options
*/
var Drawing = function (el, htOption) {
this._bIsPainted = false;
this._android = _getAndroid();
this._htOption = htOption;
this._elCanvas = document.createElement("canvas");
this._elCanvas.width = htOption.width;
this._elCanvas.height = htOption.height;
el.appendChild(this._elCanvas);
this._el = el;
this._oContext = this._elCanvas.getContext("2d");
this._bIsPainted = false;
this._elImage = document.createElement("img");
this._elImage.alt = "Scan me!";
this._elImage.style.display = "none";
this._el.appendChild(this._elImage);
this._bSupportDataURI = null;
};
/**
* Draw the QRCode
*
* @param {QRCode} oQRCode
*/
Drawing.prototype.draw = function (oQRCode) {
var _elImage = this._elImage;
var _oContext = this._oContext;
var _htOption = this._htOption;
var nCount = oQRCode.getModuleCount();
var nWidth = _htOption.width / nCount;
var nHeight = _htOption.height / nCount;
var nRoundedWidth = Math.round(nWidth);
var nRoundedHeight = Math.round(nHeight);
_elImage.style.display = "none";
this.clear();
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = col * nWidth;
var nTop = row * nHeight;
_oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.lineWidth = 1;
_oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
// 안티 앨리어싱 방지 처리
_oContext.strokeRect(
Math.floor(nLeft) + 0.5,
Math.floor(nTop) + 0.5,
nRoundedWidth,
nRoundedHeight
);
_oContext.strokeRect(
Math.ceil(nLeft) - 0.5,
Math.ceil(nTop) - 0.5,
nRoundedWidth,
nRoundedHeight
);
}
}
this._bIsPainted = true;
};
/**
* Make the image from Canvas if the browser supports Data URI.
*/
Drawing.prototype.makeImage = function () {
if (this._bIsPainted) {
_safeSetDataURI.call(this, _onMakeImage);
}
};
/**
* Return whether the QRCode is painted or not
*
* @return {Boolean}
*/
Drawing.prototype.isPainted = function () {
return this._bIsPainted;
};
/**
* Clear the QRCode
*/
Drawing.prototype.clear = function () {
this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
this._bIsPainted = false;
};
/**
* @private
* @param {Number} nNumber
*/
Drawing.prototype.round = function (nNumber) {
if (!nNumber) {
return nNumber;
}
return Math.floor(nNumber * 1000) / 1000;
};
return Drawing;
})();
/**
* Get the type by string length
*
* @private
* @param {String} sText
* @param {Number} nCorrectLevel
* @return {Number} type
*/
function _getTypeNumber(sText, nCorrectLevel) {
var nType = 1;
var length = _getUTF8Length(sText);
for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
var nLimit = 0;
switch (nCorrectLevel) {
case QRErrorCorrectLevel.L :
nLimit = QRCodeLimitLength[i][0];
break;
case QRErrorCorrectLevel.M :
nLimit = QRCodeLimitLength[i][1];
break;
case QRErrorCorrectLevel.Q :
nLimit = QRCodeLimitLength[i][2];
break;
case QRErrorCorrectLevel.H :
nLimit = QRCodeLimitLength[i][3];
break;
}
if (length <= nLimit) {
break;
} else {
nType++;
}
}
if (nType > QRCodeLimitLength.length) {
throw new Error("Too long data");
}
return nType;
}
function _getUTF8Length(sText) {
var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
return replacedText.length + (replacedText.length != sText ? 3 : 0);
}
/**
* @class QRCode
* @constructor
* @example
* new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
*
* @example
* var oQRCode = new QRCode("test", {
* text : "http://naver.com",
* width : 128,
* height : 128
* });
*
* oQRCode.clear(); // Clear the QRCode.
* oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
*
* @param {HTMLElement|String} el target element or 'id' attribute of element.
* @param {Object|String} vOption
* @param {String} vOption.text QRCode link data
* @param {Number} [vOption.width=256]
* @param {Number} [vOption.height=256]
* @param {String} [vOption.colorDark="#000000"]
* @param {String} [vOption.colorLight="#ffffff"]
* @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
*/
QRCode = function (el, vOption) {
this._htOption = {
width : 256,
height : 256,
typeNumber : 4,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRErrorCorrectLevel.H
};
if (typeof vOption === 'string') {
vOption = {
text : vOption
};
}
// Overwrites options
if (vOption) {
for (var i in vOption) {
this._htOption[i] = vOption[i];
}
}
if (typeof el == "string") {
el = document.getElementById(el);
}
this._android = _getAndroid();
this._el = el;
this._oQRCode = null;
this._oDrawing = new Drawing(this._el, this._htOption);
if (this._htOption.text) {
this.makeCode(this._htOption.text);
}
};
/**
* Make the QRCode
*
* @param {String} sText link data
*/
QRCode.prototype.makeCode = function (sText) {
this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
this._oQRCode.addData(sText);
this._oQRCode.make();
this._el.title = sText;
this._oDrawing.draw(this._oQRCode);
this.makeImage();
};
/**
* Make the Image from Canvas element
* - It occurs automatically
* - Android below 3 doesn't support Data-URI spec.
*
* @private
*/
QRCode.prototype.makeImage = function () {
if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) {
this._oDrawing.makeImage();
}
};
/**
* Clear the QRCode
*/
QRCode.prototype.clear = function () {
this._oDrawing.clear();
};
/**
* @name QRCode.CorrectLevel
*/
QRCode.CorrectLevel = QRErrorCorrectLevel;
})();

File diff suppressed because one or more lines are too long

View file

@ -1,47 +0,0 @@
{
"name": "qrious",
"version": "2.0.2",
"description": "Library for QR code generation using canvas",
"homepage": "https://github.com/neocotic/qrious",
"authors": [
{
"name": "Alasdair Mercer",
"email": "mercer.alasdair@gmail.com",
"homepage": "http://neocotic.com"
}
],
"license": "GPL-3.0",
"keywords": [
"qr",
"code",
"encode",
"canvas",
"image"
],
"repository": {
"type": "git",
"url": "https://github.com/neocotic/qrious.git"
},
"main": "dist/umd/qrious.js",
"ignore": [
"src/",
".*",
"AUTHORS.md",
"CHANGES.md",
"CONTRIBUTING.md",
"demo.html",
"Gruntfile.js",
"package.json",
"README.md"
],
"_release": "2.0.2",
"_resolution": {
"type": "version",
"tag": "2.0.2",
"commit": "1ffd092e97ab3ba212fad21ab865eb1754155296"
},
"_source": "https://github.com/neocotic/qrious.git",
"_target": "^2.0.2",
"_originalSource": "qrious",
"_direct": true
}

View file

@ -1,16 +0,0 @@
QRious
Copyright (C) 2016 Alasdair Mercer
Copyright (C) 2010 Tom Zerucha
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View file

@ -1,37 +0,0 @@
{
"name": "qrious",
"version": "2.0.2",
"description": "Library for QR code generation using canvas",
"homepage": "https://github.com/neocotic/qrious",
"authors": [
{
"name": "Alasdair Mercer",
"email": "mercer.alasdair@gmail.com",
"homepage": "http://neocotic.com"
}
],
"license": "GPL-3.0",
"keywords": [
"qr",
"code",
"encode",
"canvas",
"image"
],
"repository": {
"type": "git",
"url": "https://github.com/neocotic/qrious.git"
},
"main": "dist/umd/qrious.js",
"ignore": [
"src/",
".*",
"AUTHORS.md",
"CHANGES.md",
"CONTRIBUTING.md",
"demo.html",
"Gruntfile.js",
"package.json",
"README.md"
]
}

View file

@ -1,36 +0,0 @@
{
"name": "qrjs",
"main": "qr.js",
"version": "0.1.2",
"homepage": "https://github.com/educastellano/qr.js",
"authors": [
"Kang Seonghoon <kang.seonghoon@mearie.org>",
"Eduard Castellano <educastellano08@gmail.com>"
],
"description": "QR code generator in Javascript",
"moduleType": [
"globals"
],
"keywords": [
"qr",
"qr.js",
"qrcode"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"_release": "0.1.2",
"_resolution": {
"type": "version",
"tag": "v0.1.2",
"commit": "dbfa732cb309195a51a656b021f309d354154e04"
},
"_source": "https://github.com/educastellano/qr.js.git",
"_target": "~0.1.2",
"_originalSource": "qrjs"
}

View file

@ -1,8 +0,0 @@
# qr.js: QR code generator in pure Javascript (2011)
This is a fairly standalone script for producing QR code on the fly.
Originally developed for my own interest, the code is fully commented and well-structured.
The code is in the public domain (or to be exact, [Creative Commons Zero](https://creativecommons.org/publicdomain/zero/1.0/)),
and you can use it for absolutely any purpose.
See also a [node.js module based on qr.js](https://github.com/shesek/qruri), packaged by Nadav Ivgi.

View file

@ -1,27 +0,0 @@
{
"name": "qrjs",
"main": "qr.js",
"version": "0.1.2",
"homepage": "https://github.com/educastellano/qr.js",
"authors": [
"Kang Seonghoon <kang.seonghoon@mearie.org>",
"Eduard Castellano <educastellano08@gmail.com>"
],
"description": "QR code generator in Javascript",
"moduleType": [
"globals"
],
"keywords": [
"qr",
"qr.js",
"qrcode"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View file

@ -1,24 +0,0 @@
{
"name": "qrjs",
"version": "0.1.1",
"description": "QR code generator in Javascript",
"main": "qr.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/educastellano/qr.js.git"
},
"keywords": [
"qr",
"qr.js",
"qrcode"
],
"author": "Kang Seonghoon <kang.seonghoon@mearie.org>",
"license": "MIT",
"bugs": {
"url": "https://github.com/educastellano/qr.js/issues"
},
"homepage": "https://github.com/educastellano/qr.js"
}

View file

@ -1,804 +0,0 @@
/* qr.js -- QR code generator in Javascript (revision 2011-01-19)
* Written by Kang Seonghoon <public+qrjs@mearie.org>.
*
* This source code is in the public domain; if your jurisdiction does not
* recognize the public domain the terms of Creative Commons CC0 license
* apply. In the other words, you can always do what you want.
*/
(function(root, name, definition) {
if (typeof define === 'function' && define.amd) {
define([], definition);
} else if (typeof module === 'object' && module.exports) {
module.exports = definition();
} else {
root[name] = definition();
}
})(this, 'QRCode', function() {
/* Quick overview: QR code composed of 2D array of modules (a rectangular
* area that conveys one bit of information); some modules are fixed to help
* the recognition of the code, and remaining data modules are further divided
* into 8-bit code words which are augumented by Reed-Solomon error correcting
* codes (ECC). There could be multiple ECCs, in the case the code is so large
* that it is helpful to split the raw data into several chunks.
*
* The number of modules is determined by the code's "version", ranging from 1
* (21x21) to 40 (177x177). How many ECC bits are used is determined by the
* ECC level (L/M/Q/H). The number and size (and thus the order of generator
* polynomial) of ECCs depend to the version and ECC level.
*/
// per-version information (cf. JIS X 0510:2004 pp. 30--36, 71)
//
// [0]: the degree of generator polynomial by ECC levels
// [1]: # of code blocks by ECC levels
// [2]: left-top positions of alignment patterns
//
// the number in this table (in particular, [0]) does not exactly match with
// the numbers in the specficiation. see augumenteccs below for the reason.
var VERSIONS = [
null,
[[10, 7,17,13], [ 1, 1, 1, 1], []],
[[16,10,28,22], [ 1, 1, 1, 1], [4,16]],
[[26,15,22,18], [ 1, 1, 2, 2], [4,20]],
[[18,20,16,26], [ 2, 1, 4, 2], [4,24]],
[[24,26,22,18], [ 2, 1, 4, 4], [4,28]],
[[16,18,28,24], [ 4, 2, 4, 4], [4,32]],
[[18,20,26,18], [ 4, 2, 5, 6], [4,20,36]],
[[22,24,26,22], [ 4, 2, 6, 6], [4,22,40]],
[[22,30,24,20], [ 5, 2, 8, 8], [4,24,44]],
[[26,18,28,24], [ 5, 4, 8, 8], [4,26,48]],
[[30,20,24,28], [ 5, 4,11, 8], [4,28,52]],
[[22,24,28,26], [ 8, 4,11,10], [4,30,56]],
[[22,26,22,24], [ 9, 4,16,12], [4,32,60]],
[[24,30,24,20], [ 9, 4,16,16], [4,24,44,64]],
[[24,22,24,30], [10, 6,18,12], [4,24,46,68]],
[[28,24,30,24], [10, 6,16,17], [4,24,48,72]],
[[28,28,28,28], [11, 6,19,16], [4,28,52,76]],
[[26,30,28,28], [13, 6,21,18], [4,28,54,80]],
[[26,28,26,26], [14, 7,25,21], [4,28,56,84]],
[[26,28,28,30], [16, 8,25,20], [4,32,60,88]],
[[26,28,30,28], [17, 8,25,23], [4,26,48,70,92]],
[[28,28,24,30], [17, 9,34,23], [4,24,48,72,96]],
[[28,30,30,30], [18, 9,30,25], [4,28,52,76,100]],
[[28,30,30,30], [20,10,32,27], [4,26,52,78,104]],
[[28,26,30,30], [21,12,35,29], [4,30,56,82,108]],
[[28,28,30,28], [23,12,37,34], [4,28,56,84,112]],
[[28,30,30,30], [25,12,40,34], [4,32,60,88,116]],
[[28,30,30,30], [26,13,42,35], [4,24,48,72,96,120]],
[[28,30,30,30], [28,14,45,38], [4,28,52,76,100,124]],
[[28,30,30,30], [29,15,48,40], [4,24,50,76,102,128]],
[[28,30,30,30], [31,16,51,43], [4,28,54,80,106,132]],
[[28,30,30,30], [33,17,54,45], [4,32,58,84,110,136]],
[[28,30,30,30], [35,18,57,48], [4,28,56,84,112,140]],
[[28,30,30,30], [37,19,60,51], [4,32,60,88,116,144]],
[[28,30,30,30], [38,19,63,53], [4,28,52,76,100,124,148]],
[[28,30,30,30], [40,20,66,56], [4,22,48,74,100,126,152]],
[[28,30,30,30], [43,21,70,59], [4,26,52,78,104,130,156]],
[[28,30,30,30], [45,22,74,62], [4,30,56,82,108,134,160]],
[[28,30,30,30], [47,24,77,65], [4,24,52,80,108,136,164]],
[[28,30,30,30], [49,25,81,68], [4,28,56,84,112,140,168]]];
// mode constants (cf. Table 2 in JIS X 0510:2004 p. 16)
var MODE_TERMINATOR = 0;
var MODE_NUMERIC = 1, MODE_ALPHANUMERIC = 2, MODE_OCTET = 4, MODE_KANJI = 8;
// validation regexps
var NUMERIC_REGEXP = /^\d*$/;
var ALPHANUMERIC_REGEXP = /^[A-Za-z0-9 $%*+\-./:]*$/;
var ALPHANUMERIC_OUT_REGEXP = /^[A-Z0-9 $%*+\-./:]*$/;
// ECC levels (cf. Table 22 in JIS X 0510:2004 p. 45)
var ECCLEVEL_L = 1, ECCLEVEL_M = 0, ECCLEVEL_Q = 3, ECCLEVEL_H = 2;
// GF(2^8)-to-integer mapping with a reducing polynomial x^8+x^4+x^3+x^2+1
// invariant: GF256_MAP[GF256_INVMAP[i]] == i for all i in [1,256)
var GF256_MAP = [], GF256_INVMAP = [-1];
for (var i = 0, v = 1; i < 255; ++i) {
GF256_MAP.push(v);
GF256_INVMAP[v] = i;
v = (v * 2) ^ (v >= 128 ? 0x11d : 0);
}
// generator polynomials up to degree 30
// (should match with polynomials in JIS X 0510:2004 Appendix A)
//
// generator polynomial of degree K is product of (x-\alpha^0), (x-\alpha^1),
// ..., (x-\alpha^(K-1)). by convention, we omit the K-th coefficient (always 1)
// from the result; also other coefficients are written in terms of the exponent
// to \alpha to avoid the redundant calculation. (see also calculateecc below.)
var GF256_GENPOLY = [[]];
for (var i = 0; i < 30; ++i) {
var prevpoly = GF256_GENPOLY[i], poly = [];
for (var j = 0; j <= i; ++j) {
var a = (j < i ? GF256_MAP[prevpoly[j]] : 0);
var b = GF256_MAP[(i + (prevpoly[j-1] || 0)) % 255];
poly.push(GF256_INVMAP[a ^ b]);
}
GF256_GENPOLY.push(poly);
}
// alphanumeric character mapping (cf. Table 5 in JIS X 0510:2004 p. 19)
var ALPHANUMERIC_MAP = {};
for (var i = 0; i < 45; ++i) {
ALPHANUMERIC_MAP['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.charAt(i)] = i;
}
// mask functions in terms of row # and column #
// (cf. Table 20 in JIS X 0510:2004 p. 42)
var MASKFUNCS = [
function(i,j) { return (i+j) % 2 == 0; },
function(i,j) { return i % 2 == 0; },
function(i,j) { return j % 3 == 0; },
function(i,j) { return (i+j) % 3 == 0; },
function(i,j) { return (((i/2)|0) + ((j/3)|0)) % 2 == 0; },
function(i,j) { return (i*j) % 2 + (i*j) % 3 == 0; },
function(i,j) { return ((i*j) % 2 + (i*j) % 3) % 2 == 0; },
function(i,j) { return ((i+j) % 2 + (i*j) % 3) % 2 == 0; }];
// returns true when the version information has to be embeded.
var needsverinfo = function(ver) { return ver > 6; };
// returns the size of entire QR code for given version.
var getsizebyver = function(ver) { return 4 * ver + 17; };
// returns the number of bits available for code words in this version.
var nfullbits = function(ver) {
/*
* |<--------------- n --------------->|
* | |<----- n-17 ---->| |
* +-------+ ///+-------+ ----
* | | ///| | ^
* | 9x9 | @@@@@ ///| 9x8 | |
* | | # # # @5x5@ # # # | | |
* +-------+ @@@@@ +-------+ |
* # ---|
* ^ |
* # |
* @@@@@ @@@@@ @@@@@ | n
* @5x5@ @5x5@ @5x5@ n-17
* @@@@@ @@@@@ @@@@@ | |
* # | |
* ////// v |
* //////# ---|
* +-------+ @@@@@ @@@@@ |
* | | @5x5@ @5x5@ |
* | 8x9 | @@@@@ @@@@@ |
* | | v
* +-------+ ----
*
* when the entire code has n^2 modules and there are m^2-3 alignment
* patterns, we have:
* - 225 (= 9x9 + 9x8 + 8x9) modules for finder patterns and
* format information;
* - 2n-34 (= 2(n-17)) modules for timing patterns;
* - 36 (= 3x6 + 6x3) modules for version information, if any;
* - 25m^2-75 (= (m^2-3)(5x5)) modules for alignment patterns
* if any, but 10m-20 (= 2(m-2)x5) of them overlaps with
* timing patterns.
*/
var v = VERSIONS[ver];
var nbits = 16*ver*ver + 128*ver + 64; // finder, timing and format info.
if (needsverinfo(ver)) nbits -= 36; // version information
if (v[2].length) { // alignment patterns
nbits -= 25 * v[2].length * v[2].length - 10 * v[2].length - 55;
}
return nbits;
};
// returns the number of bits available for data portions (i.e. excludes ECC
// bits but includes mode and length bits) in this version and ECC level.
var ndatabits = function(ver, ecclevel) {
var nbits = nfullbits(ver) & ~7; // no sub-octet code words
var v = VERSIONS[ver];
nbits -= 8 * v[0][ecclevel] * v[1][ecclevel]; // ecc bits
return nbits;
}
// returns the number of bits required for the length of data.
// (cf. Table 3 in JIS X 0510:2004 p. 16)
var ndatalenbits = function(ver, mode) {
switch (mode) {
case MODE_NUMERIC: return (ver < 10 ? 10 : ver < 27 ? 12 : 14);
case MODE_ALPHANUMERIC: return (ver < 10 ? 9 : ver < 27 ? 11 : 13);
case MODE_OCTET: return (ver < 10 ? 8 : 16);
case MODE_KANJI: return (ver < 10 ? 8 : ver < 27 ? 10 : 12);
}
};
// returns the maximum length of data possible in given configuration.
var getmaxdatalen = function(ver, mode, ecclevel) {
var nbits = ndatabits(ver, ecclevel) - 4 - ndatalenbits(ver, mode); // 4 for mode bits
switch (mode) {
case MODE_NUMERIC:
return ((nbits/10) | 0) * 3 + (nbits%10 < 4 ? 0 : nbits%10 < 7 ? 1 : 2);
case MODE_ALPHANUMERIC:
return ((nbits/11) | 0) * 2 + (nbits%11 < 6 ? 0 : 1);
case MODE_OCTET:
return (nbits/8) | 0;
case MODE_KANJI:
return (nbits/13) | 0;
}
};
// checks if the given data can be encoded in given mode, and returns
// the converted data for the further processing if possible. otherwise
// returns null.
//
// this function does not check the length of data; it is a duty of
// encode function below (as it depends on the version and ECC level too).
var validatedata = function(mode, data) {
switch (mode) {
case MODE_NUMERIC:
if (!data.match(NUMERIC_REGEXP)) return null;
return data;
case MODE_ALPHANUMERIC:
if (!data.match(ALPHANUMERIC_REGEXP)) return null;
return data.toUpperCase();
case MODE_OCTET:
if (typeof data === 'string') { // encode as utf-8 string
var newdata = [];
for (var i = 0; i < data.length; ++i) {
var ch = data.charCodeAt(i);
if (ch < 0x80) {
newdata.push(ch);
} else if (ch < 0x800) {
newdata.push(0xc0 | (ch >> 6),
0x80 | (ch & 0x3f));
} else if (ch < 0x10000) {
newdata.push(0xe0 | (ch >> 12),
0x80 | ((ch >> 6) & 0x3f),
0x80 | (ch & 0x3f));
} else {
newdata.push(0xf0 | (ch >> 18),
0x80 | ((ch >> 12) & 0x3f),
0x80 | ((ch >> 6) & 0x3f),
0x80 | (ch & 0x3f));
}
}
return newdata;
} else {
return data;
}
}
};
// returns the code words (sans ECC bits) for given data and configurations.
// requires data to be preprocessed by validatedata. no length check is
// performed, and everything has to be checked before calling this function.
var encode = function(ver, mode, data, maxbuflen) {
var buf = [];
var bits = 0, remaining = 8;
var datalen = data.length;
// this function is intentionally no-op when n=0.
var pack = function(x, n) {
if (n >= remaining) {
buf.push(bits | (x >> (n -= remaining)));
while (n >= 8) buf.push((x >> (n -= 8)) & 255);
bits = 0;
remaining = 8;
}
if (n > 0) bits |= (x & ((1 << n) - 1)) << (remaining -= n);
};
var nlenbits = ndatalenbits(ver, mode);
pack(mode, 4);
pack(datalen, nlenbits);
switch (mode) {
case MODE_NUMERIC:
for (var i = 2; i < datalen; i += 3) {
pack(parseInt(data.substring(i-2,i+1), 10), 10);
}
pack(parseInt(data.substring(i-2), 10), [0,4,7][datalen%3]);
break;
case MODE_ALPHANUMERIC:
for (var i = 1; i < datalen; i += 2) {
pack(ALPHANUMERIC_MAP[data.charAt(i-1)] * 45 +
ALPHANUMERIC_MAP[data.charAt(i)], 11);
}
if (datalen % 2 == 1) {
pack(ALPHANUMERIC_MAP[data.charAt(i-1)], 6);
}
break;
case MODE_OCTET:
for (var i = 0; i < datalen; ++i) {
pack(data[i], 8);
}
break;
};
// final bits. it is possible that adding terminator causes the buffer
// to overflow, but then the buffer truncated to the maximum size will
// be valid as the truncated terminator mode bits and padding is
// identical in appearance (cf. JIS X 0510:2004 sec 8.4.8).
pack(MODE_TERMINATOR, 4);
if (remaining < 8) buf.push(bits);
// the padding to fill up the remaining space. we should not add any
// words when the overflow already occurred.
while (buf.length + 1 < maxbuflen) buf.push(0xec, 0x11);
if (buf.length < maxbuflen) buf.push(0xec);
return buf;
};
// calculates ECC code words for given code words and generator polynomial.
//
// this is quite similar to CRC calculation as both Reed-Solomon and CRC use
// the certain kind of cyclic codes, which is effectively the division of
// zero-augumented polynomial by the generator polynomial. the only difference
// is that Reed-Solomon uses GF(2^8), instead of CRC's GF(2), and Reed-Solomon
// uses the different generator polynomial than CRC's.
var calculateecc = function(poly, genpoly) {
var modulus = poly.slice(0);
var polylen = poly.length, genpolylen = genpoly.length;
for (var i = 0; i < genpolylen; ++i) modulus.push(0);
for (var i = 0; i < polylen; ) {
var quotient = GF256_INVMAP[modulus[i++]];
if (quotient >= 0) {
for (var j = 0; j < genpolylen; ++j) {
modulus[i+j] ^= GF256_MAP[(quotient + genpoly[j]) % 255];
}
}
}
return modulus.slice(polylen);
};
// auguments ECC code words to given code words. the resulting words are
// ready to be encoded in the matrix.
//
// the much of actual augumenting procedure follows JIS X 0510:2004 sec 8.7.
// the code is simplified using the fact that the size of each code & ECC
// blocks is almost same; for example, when we have 4 blocks and 46 data words
// the number of code words in those blocks are 11, 11, 12, 12 respectively.
var augumenteccs = function(poly, nblocks, genpoly) {
var subsizes = [];
var subsize = (poly.length / nblocks) | 0, subsize0 = 0;
var pivot = nblocks - poly.length % nblocks;
for (var i = 0; i < pivot; ++i) {
subsizes.push(subsize0);
subsize0 += subsize;
}
for (var i = pivot; i < nblocks; ++i) {
subsizes.push(subsize0);
subsize0 += subsize+1;
}
subsizes.push(subsize0);
var eccs = [];
for (var i = 0; i < nblocks; ++i) {
eccs.push(calculateecc(poly.slice(subsizes[i], subsizes[i+1]), genpoly));
}
var result = [];
var nitemsperblock = (poly.length / nblocks) | 0;
for (var i = 0; i < nitemsperblock; ++i) {
for (var j = 0; j < nblocks; ++j) {
result.push(poly[subsizes[j] + i]);
}
}
for (var j = pivot; j < nblocks; ++j) {
result.push(poly[subsizes[j+1] - 1]);
}
for (var i = 0; i < genpoly.length; ++i) {
for (var j = 0; j < nblocks; ++j) {
result.push(eccs[j][i]);
}
}
return result;
};
// auguments BCH(p+q,q) code to the polynomial over GF(2), given the proper
// genpoly. the both input and output are in binary numbers, and unlike
// calculateecc genpoly should include the 1 bit for the highest degree.
//
// actual polynomials used for this procedure are as follows:
// - p=10, q=5, genpoly=x^10+x^8+x^5+x^4+x^2+x+1 (JIS X 0510:2004 Appendix C)
// - p=18, q=6, genpoly=x^12+x^11+x^10+x^9+x^8+x^5+x^2+1 (ibid. Appendix D)
var augumentbch = function(poly, p, genpoly, q) {
var modulus = poly << q;
for (var i = p - 1; i >= 0; --i) {
if ((modulus >> (q+i)) & 1) modulus ^= genpoly << i;
}
return (poly << q) | modulus;
};
// creates the base matrix for given version. it returns two matrices, one of
// them is the actual one and the another represents the "reserved" portion
// (e.g. finder and timing patterns) of the matrix.
//
// some entries in the matrix may be undefined, rather than 0 or 1. this is
// intentional (no initialization needed!), and putdata below will fill
// the remaining ones.
var makebasematrix = function(ver) {
var v = VERSIONS[ver], n = getsizebyver(ver);
var matrix = [], reserved = [];
for (var i = 0; i < n; ++i) {
matrix.push([]);
reserved.push([]);
}
var blit = function(y, x, h, w, bits) {
for (var i = 0; i < h; ++i) {
for (var j = 0; j < w; ++j) {
matrix[y+i][x+j] = (bits[i] >> j) & 1;
reserved[y+i][x+j] = 1;
}
}
};
// finder patterns and a part of timing patterns
// will also mark the format information area (not yet written) as reserved.
blit(0, 0, 9, 9, [0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x17f, 0x00, 0x40]);
blit(n-8, 0, 8, 9, [0x100, 0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x7f]);
blit(0, n-8, 9, 8, [0xfe, 0x82, 0xba, 0xba, 0xba, 0x82, 0xfe, 0x00, 0x00]);
// the rest of timing patterns
for (var i = 9; i < n-8; ++i) {
matrix[6][i] = matrix[i][6] = ~i & 1;
reserved[6][i] = reserved[i][6] = 1;
}
// alignment patterns
var aligns = v[2], m = aligns.length;
for (var i = 0; i < m; ++i) {
var minj = (i==0 || i==m-1 ? 1 : 0), maxj = (i==0 ? m-1 : m);
for (var j = minj; j < maxj; ++j) {
blit(aligns[i], aligns[j], 5, 5, [0x1f, 0x11, 0x15, 0x11, 0x1f]);
}
}
// version information
if (needsverinfo(ver)) {
var code = augumentbch(ver, 6, 0x1f25, 12);
var k = 0;
for (var i = 0; i < 6; ++i) {
for (var j = 0; j < 3; ++j) {
matrix[i][(n-11)+j] = matrix[(n-11)+j][i] = (code >> k++) & 1;
reserved[i][(n-11)+j] = reserved[(n-11)+j][i] = 1;
}
}
}
return {matrix: matrix, reserved: reserved};
};
// fills the data portion (i.e. unmarked in reserved) of the matrix with given
// code words. the size of code words should be no more than available bits,
// and remaining bits are padded to 0 (cf. JIS X 0510:2004 sec 8.7.3).
var putdata = function(matrix, reserved, buf) {
var n = matrix.length;
var k = 0, dir = -1;
for (var i = n-1; i >= 0; i -= 2) {
if (i == 6) --i; // skip the entire timing pattern column
var jj = (dir < 0 ? n-1 : 0);
for (var j = 0; j < n; ++j) {
for (var ii = i; ii > i-2; --ii) {
if (!reserved[jj][ii]) {
// may overflow, but (undefined >> x)
// is 0 so it will auto-pad to zero.
matrix[jj][ii] = (buf[k >> 3] >> (~k&7)) & 1;
++k;
}
}
jj += dir;
}
dir = -dir;
}
return matrix;
};
// XOR-masks the data portion of the matrix. repeating the call with the same
// arguments will revert the prior call (convenient in the matrix evaluation).
var maskdata = function(matrix, reserved, mask) {
var maskf = MASKFUNCS[mask];
var n = matrix.length;
for (var i = 0; i < n; ++i) {
for (var j = 0; j < n; ++j) {
if (!reserved[i][j]) matrix[i][j] ^= maskf(i,j);
}
}
return matrix;
}
// puts the format information.
var putformatinfo = function(matrix, reserved, ecclevel, mask) {
var n = matrix.length;
var code = augumentbch((ecclevel << 3) | mask, 5, 0x537, 10) ^ 0x5412;
for (var i = 0; i < 15; ++i) {
var r = [0,1,2,3,4,5,7,8,n-7,n-6,n-5,n-4,n-3,n-2,n-1][i];
var c = [n-1,n-2,n-3,n-4,n-5,n-6,n-7,n-8,7,5,4,3,2,1,0][i];
matrix[r][8] = matrix[8][c] = (code >> i) & 1;
// we don't have to mark those bits reserved; always done
// in makebasematrix above.
}
return matrix;
};
// evaluates the resulting matrix and returns the score (lower is better).
// (cf. JIS X 0510:2004 sec 8.8.2)
//
// the evaluation procedure tries to avoid the problematic patterns naturally
// occuring from the original matrix. for example, it penaltizes the patterns
// which just look like the finder pattern which will confuse the decoder.
// we choose the mask which results in the lowest score among 8 possible ones.
//
// note: zxing seems to use the same procedure and in many cases its choice
// agrees to ours, but sometimes it does not. practically it doesn't matter.
var evaluatematrix = function(matrix) {
// N1+(k-5) points for each consecutive row of k same-colored modules,
// where k >= 5. no overlapping row counts.
var PENALTY_CONSECUTIVE = 3;
// N2 points for each 2x2 block of same-colored modules.
// overlapping block does count.
var PENALTY_TWOBYTWO = 3;
// N3 points for each pattern with >4W:1B:1W:3B:1W:1B or
// 1B:1W:3B:1W:1B:>4W, or their multiples (e.g. highly unlikely,
// but 13W:3B:3W:9B:3W:3B counts).
var PENALTY_FINDERLIKE = 40;
// N4*k points for every (5*k)% deviation from 50% black density.
// i.e. k=1 for 55~60% and 40~45%, k=2 for 60~65% and 35~40%, etc.
var PENALTY_DENSITY = 10;
var evaluategroup = function(groups) { // assumes [W,B,W,B,W,...,B,W]
var score = 0;
for (var i = 0; i < groups.length; ++i) {
if (groups[i] >= 5) score += PENALTY_CONSECUTIVE + (groups[i]-5);
}
for (var i = 5; i < groups.length; i += 2) {
var p = groups[i];
if (groups[i-1] == p && groups[i-2] == 3*p && groups[i-3] == p &&
groups[i-4] == p && (groups[i-5] >= 4*p || groups[i+1] >= 4*p)) {
// this part differs from zxing...
score += PENALTY_FINDERLIKE;
}
}
return score;
};
var n = matrix.length;
var score = 0, nblacks = 0;
for (var i = 0; i < n; ++i) {
var row = matrix[i];
var groups;
// evaluate the current row
groups = [0]; // the first empty group of white
for (var j = 0; j < n; ) {
var k;
for (k = 0; j < n && row[j]; ++k) ++j;
groups.push(k);
for (k = 0; j < n && !row[j]; ++k) ++j;
groups.push(k);
}
score += evaluategroup(groups);
// evaluate the current column
groups = [0];
for (var j = 0; j < n; ) {
var k;
for (k = 0; j < n && matrix[j][i]; ++k) ++j;
groups.push(k);
for (k = 0; j < n && !matrix[j][i]; ++k) ++j;
groups.push(k);
}
score += evaluategroup(groups);
// check the 2x2 box and calculate the density
var nextrow = matrix[i+1] || [];
nblacks += row[0];
for (var j = 1; j < n; ++j) {
var p = row[j];
nblacks += p;
// at least comparison with next row should be strict...
if (row[j-1] == p && nextrow[j] === p && nextrow[j-1] === p) {
score += PENALTY_TWOBYTWO;
}
}
}
score += PENALTY_DENSITY * ((Math.abs(nblacks / n / n - 0.5) / 0.05) | 0);
return score;
};
// returns the fully encoded QR code matrix which contains given data.
// it also chooses the best mask automatically when mask is -1.
var generate = function(data, ver, mode, ecclevel, mask) {
var v = VERSIONS[ver];
var buf = encode(ver, mode, data, ndatabits(ver, ecclevel) >> 3);
buf = augumenteccs(buf, v[1][ecclevel], GF256_GENPOLY[v[0][ecclevel]]);
var result = makebasematrix(ver);
var matrix = result.matrix, reserved = result.reserved;
putdata(matrix, reserved, buf);
if (mask < 0) {
// find the best mask
maskdata(matrix, reserved, 0);
putformatinfo(matrix, reserved, ecclevel, 0);
var bestmask = 0, bestscore = evaluatematrix(matrix);
maskdata(matrix, reserved, 0);
for (mask = 1; mask < 8; ++mask) {
maskdata(matrix, reserved, mask);
putformatinfo(matrix, reserved, ecclevel, mask);
var score = evaluatematrix(matrix);
if (bestscore > score) {
bestscore = score;
bestmask = mask;
}
maskdata(matrix, reserved, mask);
}
mask = bestmask;
}
maskdata(matrix, reserved, mask);
putformatinfo(matrix, reserved, ecclevel, mask);
return matrix;
};
// the public interface is trivial; the options available are as follows:
//
// - version: an integer in [1,40]. when omitted (or -1) the smallest possible
// version is chosen.
// - mode: one of 'numeric', 'alphanumeric', 'octet'. when omitted the smallest
// possible mode is chosen.
// - ecclevel: one of 'L', 'M', 'Q', 'H'. defaults to 'L'.
// - mask: an integer in [0,7]. when omitted (or -1) the best mask is chosen.
//
// for generate{HTML,PNG}:
//
// - modulesize: a number. this is a size of each modules in pixels, and
// defaults to 5px.
// - margin: a number. this is a size of margin in *modules*, and defaults to
// 4 (white modules). the specficiation mandates the margin no less than 4
// modules, so it is better not to alter this value unless you know what
// you're doing.
var QRCode = {
'generate': function(data, options) {
var MODES = {'numeric': MODE_NUMERIC, 'alphanumeric': MODE_ALPHANUMERIC,
'octet': MODE_OCTET};
var ECCLEVELS = {'L': ECCLEVEL_L, 'M': ECCLEVEL_M, 'Q': ECCLEVEL_Q,
'H': ECCLEVEL_H};
options = options || {};
var ver = options.version || -1;
var ecclevel = ECCLEVELS[(options.ecclevel || 'L').toUpperCase()];
var mode = options.mode ? MODES[options.mode.toLowerCase()] : -1;
var mask = 'mask' in options ? options.mask : -1;
if (mode < 0) {
if (typeof data === 'string') {
if (data.match(NUMERIC_REGEXP)) {
mode = MODE_NUMERIC;
} else if (data.match(ALPHANUMERIC_OUT_REGEXP)) {
// while encode supports case-insensitive
// encoding, we restrict the data to be
// uppercased when auto-selecting the mode.
mode = MODE_ALPHANUMERIC;
} else {
mode = MODE_OCTET;
}
} else {
mode = MODE_OCTET;
}
} else if (!(mode == MODE_NUMERIC || mode == MODE_ALPHANUMERIC ||
mode == MODE_OCTET)) {
throw 'invalid or unsupported mode';
}
data = validatedata(mode, data);
if (data === null) throw 'invalid data format';
if (ecclevel < 0 || ecclevel > 3) throw 'invalid ECC level';
if (ver < 0) {
for (ver = 1; ver <= 40; ++ver) {
if (data.length <= getmaxdatalen(ver, mode, ecclevel)) break;
}
if (ver > 40) throw 'too large data';
} else if (ver < 1 || ver > 40) {
throw 'invalid version';
}
if (mask != -1 && (mask < 0 || mask > 8)) throw 'invalid mask';
return generate(data, ver, mode, ecclevel, mask);
},
'generateHTML': function(data, options) {
options = options || {};
var matrix = QRCode['generate'](data, options);
var modsize = Math.max(options.modulesize || 5, 0.5);
var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);
var e = document.createElement('div');
var n = matrix.length;
var html = ['<table border="0" cellspacing="0" cellpadding="0" style="border:' +
modsize*margin + 'px solid #fff;background:#fff">'];
for (var i = 0; i < n; ++i) {
html.push('<tr>');
for (var j = 0; j < n; ++j) {
html.push('<td style="width:' + modsize + 'px;height:' + modsize + 'px' +
(matrix[i][j] ? ';background:#000' : '') + '"></td>');
}
html.push('</tr>');
}
e.className = 'qrcode';
e.innerHTML = html.join('') + '</table>';
return e;
},
'generateSVG': function(data, options) {
options = options || {};
var matrix = QRCode['generate'](data, options);
var n = matrix.length;
var modsize = Math.max(options.modulesize || 5, 0.5);
var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);
var size = modsize * (n + 2 * margin);
var common = ' class= "fg"'+' width="'+modsize+'" height="'+modsize+'"/>';
var e = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
e.setAttribute('viewBox', '0 0 '+size+' '+size);
e.setAttribute('style', 'shape-rendering:crispEdges');
if (options.modulesize) {
e.setAttribute('width', size);
e.setAttribute('height', size);
}
var svg = [
'<style scoped>.bg{fill:#FFF}.fg{fill:#000}</style>',
'<rect class="bg" x="0" y="0"',
'width="'+size+'" height="'+size+'"/>',
];
var yo = margin * modsize;
for (var y = 0; y < n; ++y) {
var xo = margin * modsize;
for (var x = 0; x < n; ++x) {
if (matrix[y][x])
svg.push('<rect x="'+xo+'" y="'+yo+'"', common);
xo += modsize;
}
yo += modsize;
}
e.innerHTML = svg.join('');
return e;
},
'generatePNG': function(data, options) {
options = options || {};
var matrix = QRCode['generate'](data, options);
var modsize = Math.max(options.modulesize || 5, 0.5);
var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);
var n = matrix.length;
var size = modsize * (n + 2 * margin);
var canvas = document.createElement('canvas'), context;
canvas.width = canvas.height = size;
context = canvas.getContext('2d');
if (!context) throw 'canvas support is needed for PNG output';
context.fillStyle = '#fff';
context.fillRect(0, 0, size, size);
context.fillStyle = '#000';
for (var i = 0; i < n; ++i) {
for (var j = 0; j < n; ++j) {
if (matrix[i][j]) {
context.fillRect(modsize * (margin + j),
modsize * (margin + i),
modsize, modsize);
}
}
}
//context.fillText('evaluation: ' + evaluatematrix(matrix), 10, 10);
return canvas.toDataURL();
}
};
return QRCode;
});

View file

@ -1,31 +0,0 @@
{
"name": "webcomponentsjs",
"main": "webcomponents.js",
"version": "0.7.22",
"homepage": "http://webcomponents.org",
"authors": [
"The Polymer Authors"
],
"repository": {
"type": "git",
"url": "https://github.com/webcomponents/webcomponentsjs.git"
},
"keywords": [
"webcomponents"
],
"license": "BSD",
"ignore": [],
"devDependencies": {
"web-component-tester": "^4.0.1"
},
"_release": "0.7.22",
"_resolution": {
"type": "version",
"tag": "v0.7.22",
"commit": "50f9751f8e638301603aebb33ba9f1e90d2b0d32"
},
"_source": "https://github.com/Polymer/webcomponentsjs.git",
"_target": "^0.7.22",
"_originalSource": "webcomponentsjs",
"_direct": true
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,350 +0,0 @@
/**
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
// @version 0.7.22
if (typeof WeakMap === "undefined") {
(function() {
var defineProperty = Object.defineProperty;
var counter = Date.now() % 1e9;
var WeakMap = function() {
this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
};
WeakMap.prototype = {
set: function(key, value) {
var entry = key[this.name];
if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
value: [ key, value ],
writable: true
});
return this;
},
get: function(key) {
var entry;
return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
},
"delete": function(key) {
var entry = key[this.name];
if (!entry || entry[0] !== key) return false;
entry[0] = entry[1] = undefined;
return true;
},
has: function(key) {
var entry = key[this.name];
if (!entry) return false;
return entry[0] === key;
}
};
window.WeakMap = WeakMap;
})();
}
(function(global) {
if (global.JsMutationObserver) {
return;
}
var registrationsTable = new WeakMap();
var setImmediate;
if (/Trident|Edge/.test(navigator.userAgent)) {
setImmediate = setTimeout;
} else if (window.setImmediate) {
setImmediate = window.setImmediate;
} else {
var setImmediateQueue = [];
var sentinel = String(Math.random());
window.addEventListener("message", function(e) {
if (e.data === sentinel) {
var queue = setImmediateQueue;
setImmediateQueue = [];
queue.forEach(function(func) {
func();
});
}
});
setImmediate = function(func) {
setImmediateQueue.push(func);
window.postMessage(sentinel, "*");
};
}
var isScheduled = false;
var scheduledObservers = [];
function scheduleCallback(observer) {
scheduledObservers.push(observer);
if (!isScheduled) {
isScheduled = true;
setImmediate(dispatchCallbacks);
}
}
function wrapIfNeeded(node) {
return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
}
function dispatchCallbacks() {
isScheduled = false;
var observers = scheduledObservers;
scheduledObservers = [];
observers.sort(function(o1, o2) {
return o1.uid_ - o2.uid_;
});
var anyNonEmpty = false;
observers.forEach(function(observer) {
var queue = observer.takeRecords();
removeTransientObserversFor(observer);
if (queue.length) {
observer.callback_(queue, observer);
anyNonEmpty = true;
}
});
if (anyNonEmpty) dispatchCallbacks();
}
function removeTransientObserversFor(observer) {
observer.nodes_.forEach(function(node) {
var registrations = registrationsTable.get(node);
if (!registrations) return;
registrations.forEach(function(registration) {
if (registration.observer === observer) registration.removeTransientObservers();
});
});
}
function forEachAncestorAndObserverEnqueueRecord(target, callback) {
for (var node = target; node; node = node.parentNode) {
var registrations = registrationsTable.get(node);
if (registrations) {
for (var j = 0; j < registrations.length; j++) {
var registration = registrations[j];
var options = registration.options;
if (node !== target && !options.subtree) continue;
var record = callback(options);
if (record) registration.enqueue(record);
}
}
}
}
var uidCounter = 0;
function JsMutationObserver(callback) {
this.callback_ = callback;
this.nodes_ = [];
this.records_ = [];
this.uid_ = ++uidCounter;
}
JsMutationObserver.prototype = {
observe: function(target, options) {
target = wrapIfNeeded(target);
if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
throw new SyntaxError();
}
var registrations = registrationsTable.get(target);
if (!registrations) registrationsTable.set(target, registrations = []);
var registration;
for (var i = 0; i < registrations.length; i++) {
if (registrations[i].observer === this) {
registration = registrations[i];
registration.removeListeners();
registration.options = options;
break;
}
}
if (!registration) {
registration = new Registration(this, target, options);
registrations.push(registration);
this.nodes_.push(target);
}
registration.addListeners();
},
disconnect: function() {
this.nodes_.forEach(function(node) {
var registrations = registrationsTable.get(node);
for (var i = 0; i < registrations.length; i++) {
var registration = registrations[i];
if (registration.observer === this) {
registration.removeListeners();
registrations.splice(i, 1);
break;
}
}
}, this);
this.records_ = [];
},
takeRecords: function() {
var copyOfRecords = this.records_;
this.records_ = [];
return copyOfRecords;
}
};
function MutationRecord(type, target) {
this.type = type;
this.target = target;
this.addedNodes = [];
this.removedNodes = [];
this.previousSibling = null;
this.nextSibling = null;
this.attributeName = null;
this.attributeNamespace = null;
this.oldValue = null;
}
function copyMutationRecord(original) {
var record = new MutationRecord(original.type, original.target);
record.addedNodes = original.addedNodes.slice();
record.removedNodes = original.removedNodes.slice();
record.previousSibling = original.previousSibling;
record.nextSibling = original.nextSibling;
record.attributeName = original.attributeName;
record.attributeNamespace = original.attributeNamespace;
record.oldValue = original.oldValue;
return record;
}
var currentRecord, recordWithOldValue;
function getRecord(type, target) {
return currentRecord = new MutationRecord(type, target);
}
function getRecordWithOldValue(oldValue) {
if (recordWithOldValue) return recordWithOldValue;
recordWithOldValue = copyMutationRecord(currentRecord);
recordWithOldValue.oldValue = oldValue;
return recordWithOldValue;
}
function clearRecords() {
currentRecord = recordWithOldValue = undefined;
}
function recordRepresentsCurrentMutation(record) {
return record === recordWithOldValue || record === currentRecord;
}
function selectRecord(lastRecord, newRecord) {
if (lastRecord === newRecord) return lastRecord;
if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
return null;
}
function Registration(observer, target, options) {
this.observer = observer;
this.target = target;
this.options = options;
this.transientObservedNodes = [];
}
Registration.prototype = {
enqueue: function(record) {
var records = this.observer.records_;
var length = records.length;
if (records.length > 0) {
var lastRecord = records[length - 1];
var recordToReplaceLast = selectRecord(lastRecord, record);
if (recordToReplaceLast) {
records[length - 1] = recordToReplaceLast;
return;
}
} else {
scheduleCallback(this.observer);
}
records[length] = record;
},
addListeners: function() {
this.addListeners_(this.target);
},
addListeners_: function(node) {
var options = this.options;
if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
},
removeListeners: function() {
this.removeListeners_(this.target);
},
removeListeners_: function(node) {
var options = this.options;
if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
},
addTransientObserver: function(node) {
if (node === this.target) return;
this.addListeners_(node);
this.transientObservedNodes.push(node);
var registrations = registrationsTable.get(node);
if (!registrations) registrationsTable.set(node, registrations = []);
registrations.push(this);
},
removeTransientObservers: function() {
var transientObservedNodes = this.transientObservedNodes;
this.transientObservedNodes = [];
transientObservedNodes.forEach(function(node) {
this.removeListeners_(node);
var registrations = registrationsTable.get(node);
for (var i = 0; i < registrations.length; i++) {
if (registrations[i] === this) {
registrations.splice(i, 1);
break;
}
}
}, this);
},
handleEvent: function(e) {
e.stopImmediatePropagation();
switch (e.type) {
case "DOMAttrModified":
var name = e.attrName;
var namespace = e.relatedNode.namespaceURI;
var target = e.target;
var record = new getRecord("attributes", target);
record.attributeName = name;
record.attributeNamespace = namespace;
var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
if (!options.attributes) return;
if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
return;
}
if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
return record;
});
break;
case "DOMCharacterDataModified":
var target = e.target;
var record = getRecord("characterData", target);
var oldValue = e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
if (!options.characterData) return;
if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
return record;
});
break;
case "DOMNodeRemoved":
this.addTransientObserver(e.target);
case "DOMNodeInserted":
var changedNode = e.target;
var addedNodes, removedNodes;
if (e.type === "DOMNodeInserted") {
addedNodes = [ changedNode ];
removedNodes = [];
} else {
addedNodes = [];
removedNodes = [ changedNode ];
}
var previousSibling = changedNode.previousSibling;
var nextSibling = changedNode.nextSibling;
var record = getRecord("childList", e.target.parentNode);
record.addedNodes = addedNodes;
record.removedNodes = removedNodes;
record.previousSibling = previousSibling;
record.nextSibling = nextSibling;
forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
if (!options.childList) return;
return record;
});
}
clearRecords();
}
};
global.JsMutationObserver = JsMutationObserver;
if (!global.MutationObserver) {
global.MutationObserver = JsMutationObserver;
JsMutationObserver._isPolyfilled = true;
}
})(self);

File diff suppressed because one or more lines are too long

View file

@ -1,155 +0,0 @@
webcomponents.js
================
[![Join the chat at https://gitter.im/webcomponents/webcomponentsjs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/webcomponents/webcomponentsjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
A suite of polyfills supporting the [Web Components](http://webcomponents.org) specs:
**Custom Elements**: allows authors to define their own custom tags ([spec](https://w3c.github.io/webcomponents/spec/custom/)).
**HTML Imports**: a way to include and reuse HTML documents via other HTML documents ([spec](https://w3c.github.io/webcomponents/spec/imports/)).
**Shadow DOM**: provides encapsulation by hiding DOM subtrees under shadow roots ([spec](https://w3c.github.io/webcomponents/spec/shadow/)).
This also folds in polyfills for `MutationObserver` and `WeakMap`.
## Releases
Pre-built (concatenated & minified) versions of the polyfills are maintained in the [tagged versions](https://github.com/webcomponents/webcomponentsjs/releases) of this repo. There are two variants:
`webcomponents.js` includes all of the polyfills.
`webcomponents-lite.js` includes all polyfills except for shadow DOM.
## Browser Support
Our polyfills are intended to work in the latest versions of evergreen browsers. See below
for our complete browser support matrix:
| Polyfill | IE10 | IE11+ | Chrome* | Firefox* | Safari 7+* | Chrome Android* | Mobile Safari* |
| ---------- |:----:|:-----:|:-------:|:--------:|:----------:|:---------------:|:--------------:|
| Custom Elements | ~ | ✓ | ✓ | ✓ | ✓ | ✓| ✓ |
| HTML Imports | ~ | ✓ | ✓ | ✓ | ✓| ✓| ✓ |
| Shadow DOM | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Templates | ✓ | ✓ | ✓ | ✓| ✓ | ✓ | ✓ |
*Indicates the current version of the browser
~Indicates support may be flaky. If using Custom Elements or HTML Imports with Shadow DOM,
you will get the non-flaky Mutation Observer polyfill that Shadow DOM includes.
The polyfills may work in older browsers, however require additional polyfills (such as classList)
to be used. We cannot guarantee support for browsers outside of our compatibility matrix.
### Manually Building
If you wish to build the polyfills yourself, you'll need `node` and `gulp` on your system:
* install [node.js](http://nodejs.org/) using the instructions on their website
* use `npm` to install [gulp.js](http://gulpjs.com/): `npm install -g gulp`
Now you are ready to build the polyfills with:
# install dependencies
npm install
# build
gulp build
The builds will be placed into the `dist/` directory.
## Contribute
See the [contributing guide](CONTRIBUTING.md)
## License
Everything in this repository is BSD style license unless otherwise specified.
Copyright (c) 2015 The Polymer Authors. All rights reserved.
## Helper utilities
### `WebComponentsReady`
Under native HTML Imports, `<script>` tags in the main document block the loading of such imports. This is to ensure the imports have loaded and any registered elements in them have been upgraded.
The webcomponents.js and webcomponents-lite.js polyfills parse element definitions and handle their upgrade asynchronously. If prematurely fetching the element from the DOM before it has an opportunity to upgrade, you'll be working with an `HTMLUnknownElement`.
For these situations (or when you need an approximate replacement for the Polymer 0.5 `polymer-ready` behavior), you can use the `WebComponentsReady` event as a signal before interacting with the element. The criteria for this event to fire is all Custom Elements with definitions registered by the time HTML Imports available at load time have loaded have upgraded.
```js
window.addEventListener('WebComponentsReady', function(e) {
// imports are loaded and elements have been registered
console.log('Components are ready');
});
```
## Known Issues
* [Limited CSS encapsulation](#encapsulation)
* [Element wrapping / unwrapping limitations](#wrapping)
* [Custom element's constructor property is unreliable](#constructor)
* [Contenteditable elements do not trigger MutationObserver](#contentedit)
* [ShadowCSS: :host-context(...):host(...) doesn't work](#hostcontext)
* [ShadowCSS: :host(.zot:not(.bar:nth-child(2))) doesn't work](#nestedparens)
* [HTML imports: document.currentScript doesn't work as expected](#currentscript)
* [execCommand isn't supported under Shadow DOM](#execcommand)
### Limited CSS encapsulation <a id="encapsulation"></a>
Under native Shadow DOM, CSS selectors cannot cross the shadow boundary. This means document level styles don't apply to shadow roots, and styles defined within a shadow root don't apply outside of that shadow root. [Several selectors](http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/) are provided to be able to deal with the shadow boundary.
The Shadow DOM polyfill can't prevent document styles from leaking into shadow roots. It can, however, encapsulate styles within shadow roots to some extent. This behavior isn't automatically emulated by the Shadow DOM polyfill, but it can be achieved by manually using the included ShadowCSS shim:
```
WebComponents.ShadowCSS.shimStyling( shadowRoot, scope );
```
... where `shadowRoot` is the shadow root of a DOM element, and `scope` is the name of the scope used to prefix the selectors. This removes all `<style>` elements from the shadow root, rewrites it rules using the given scope and reinserts the style as a document level stylesheet. Note that the `:host` and `:host-context` pseudo classes are also rewritten.
For a full explanation on the implementation and both the possibilities and the limitations of ShadowCSS please view the documentation in the [ShadowCSS source](src/ShadowCSS/ShadowCSS.js).
### Element wrapping / unwrapping limitations <a id="wrapping"></a>
The Shadow DOM polyfill is implemented by [wrapping](http://webcomponents.org/polyfills/shadow-dom/#wrappers) DOM elements whenever possible. It does this by wrapping methods like `document.querySelector` to return wrapped DOM elements. This has a few caveats:
* Not _everything_ can be wrapped. For example, elements like `document`, `window`, `document.body`, `document.fullscreenElement` and others are non-configurable and thus cannot be overridden.
* Wrappers don't support [live NodeLists](https://developer.mozilla.org/en-US/docs/Web/API/NodeList#A_sometimes-live_collection) like `HTMLElement.childNodes` and `HTMLFormElement.elements`. All NodeLists are snapshotted upon read. See [#217](https://github.com/webcomponents/webcomponentsjs/issues/217) for an explanation.
In order to work around these limitations the polyfill provides the `ShadowDOMPolyfill.wrap` and `ShadowDOMPolyfill.unwrap` methods to respectively wrap and unwrap DOM elements manually.
### Custom element's constructor property is unreliable <a id="constructor"></a>
See [#215](https://github.com/webcomponents/webcomponentsjs/issues/215) for background.
In Safari and IE, instances of Custom Elements have a `constructor` property of `HTMLUnknownElementConstructor` and `HTMLUnknownElement`, respectively. It's unsafe to rely on this property for checking element types.
It's worth noting that `customElement.__proto__.__proto__.constructor` is `HTMLElementPrototype` and that the prototype chain isn't modified by the polyfills(onto `ElementPrototype`, etc.)
### Contenteditable elements do not trigger MutationObserver <a id="contentedit"></a>
Using the MutationObserver polyfill, it isn't possible to monitor mutations of an element marked `contenteditable`.
See [the mailing list](https://groups.google.com/forum/#!msg/polymer-dev/LHdtRVXXVsA/v1sGoiTYWUkJ)
### ShadowCSS: :host-context(...):host(...) doesn't work <a id="hostcontext"></a>
See [#16](https://github.com/webcomponents/webcomponentsjs/issues/16) for background.
Under the shadow DOM polyfill, rules like:
```
:host-context(.foo):host(.bar) {...}
```
don't work, despite working under native Shadow DOM. The solution is to use `polyfill-next-selector` like:
```
polyfill-next-selector { content: '.foo :host.bar, :host.foo.bar'; }
```
### ShadowCSS: :host(.zot:not(.bar:nth-child(2))) doesn't work <a id="nestedparens"></a>
ShadowCSS `:host()` rules can only have (at most) 1-level of nested parentheses in its argument selector under ShadowCSS. For example, `:host(.zot)` and `:host(.zot:not(.bar))` both work, but `:host(.zot:not(.bar:nth-child(2)))` does not.
### HTML imports: document.currentScript doesn't work as expected <a id="currentscript"></a>
In native HTML Imports, document.currentScript.ownerDocument references the import document itself. In the polyfill use document._currentScript.ownerDocument (note the underscore).
### execCommand and contenteditable isn't supported under Shadow DOM <a id="execcommand"></a>
See [#212](https://github.com/webcomponents/webcomponentsjs/issues/212)
`execCommand`, and `contenteditable` aren't supported under the ShadowDOM polyfill, with commands that insert or remove nodes being especially prone to failure.

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,21 +0,0 @@
{
"name": "webcomponentsjs",
"main": "webcomponents.js",
"version": "0.7.22",
"homepage": "http://webcomponents.org",
"authors": [
"The Polymer Authors"
],
"repository": {
"type": "git",
"url": "https://github.com/webcomponents/webcomponentsjs.git"
},
"keywords": [
"webcomponents"
],
"license": "BSD",
"ignore": [],
"devDependencies": {
"web-component-tester": "^4.0.1"
}
}

View file

@ -1,31 +0,0 @@
{
"name": "webcomponents.js",
"version": "0.7.22",
"description": "webcomponents.js",
"main": "webcomponents.js",
"directories": {
"test": "tests"
},
"repository": {
"type": "git",
"url": "https://github.com/webcomponents/webcomponentsjs.git"
},
"author": "The Polymer Authors",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/webcomponents/webcomponentsjs/issues"
},
"scripts": {
"test": "wct"
},
"homepage": "http://webcomponents.org",
"devDependencies": {
"gulp": "^3.8.8",
"gulp-audit": "^1.0.0",
"gulp-concat": "^2.4.1",
"gulp-header": "^1.1.1",
"gulp-uglify": "^1.0.1",
"run-sequence": "^1.0.1",
"web-component-tester": "^4.0.1"
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,29 +0,0 @@
<html>
<head>
<base href="/">
<link href="https://fonts.googleapis.com/css?family=Nunito:400,700&amp;subset=latin-ext" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<script src="elm.js"></script>
<style>
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
</style>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<script>
Elm.Main.fullscreen()
</script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -1,631 +0,0 @@
body {
font-family: Nunito, sans-serif;
margin: 0;
}
p {
margin: 0;
}
.lamassuAdminQrCode {
background-color: #f6f6f4;
padding: 10px;
margin-bottom: 20px;
border-radius: 6px;
}
.lamassuAdminQrCode svg {
height: 400px;
width: 400px;
}
.lamassuAdminMain {
display: flex;
height: 100%;
margin-bottom: 40px;
}
.lamassuAdminPaneWrapper {
display: flex;
}
.lamassuAdminLeftPane {
min-width: 270px;
}
.lamassuAdminContentPane {
max-height: 100%;
}
.lamassuAdminStatusBar {
position: fixed;
bottom: 0;
padding: 10px 20px;
background-color: #5f5f56;
color: #ffffff;
width: 100%;
}
.lamassuAdminCashOut {
background-color: #f6f6f4;
}
.lamassuAdminFormRow {
margin: 20px 0;
}
.lamassuAdminFormRow:first-child {
margin: 0;
}
.lamassuAdminFormRow label {
font-size: 11px;
font-weight: bold;
}
.lamassuAdminFormRow label > div {
margin: 0 0 5px;
color: #5f5f56;
}
.lamassuAdminFormRow input {
border: 0;
background-color: #ffffff;
border-radius: 3px;
padding: 6px;
text-align: left;
font-family: Inconsolata, monospace;
font-size: 14px;
font-weight: 600;
width: 90%;
outline: none;
}
.lamassuAdminButtonRow {
text-align: right;
}
.lamassuAdminButton {
color: #ffffff;
font-weight: bold;
cursor: pointer;
background-color: #004062;
padding: 10px 15px;
display: inline-block;
border-radius: 5px;
}
.lamassuAdminButton:hover {
background-color: #042c47;
}
.lamassuAdminButton:active {
color: #37e8d7;
}
.lamassuAdminButton.lamassuAdminActive {
color: #37e8d7;
background-color: #042c47;
}
.lamassuAdminButton.lamassuAdminDisabled {
background-color: #E6E6E3;
color: #ffffff;
cursor: default;
}
.lamassuAdminMainLeft {
background-color: #2d2d2d;
height: 100%;
}
.lamassuAdminMainRight {
background-color: #f6f6f4;
height: 100%;
}
.lamassuAdminContent {
margin: 20px;
background-color: #ffffff;
border-radius: 5px;
}
.lamassuAdminContainer {
padding: 30px;
background-color: #f6f6f4;
border-radius: 0px 5px 5px 5px;
width: 30em;
}
.lamassuAdminCryptoAddress {
font-family: Inconsolata, monospace;
}
.lamassuAdminBalanceSection {
margin-top: 2em;
}
.lamassuAdminBalanceSection h2 {
font-size: 1.2em;
margin-bottom: 0.2em;
}
.lamassuAdminTextarea {
width: 100%;
border: 0px;
background-color: transparent;
}
.lamassuAdminCryptoTabs {
display: flex;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab {
padding: 10px 15px;
color: #5f5f56;
font-weight: bold;
cursor: pointer;
background-color: #E6E6E3;
text-decoration: none;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab:hover {
background-color: #fcfcfa;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab:active {
color: #5f5f56;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab.lamassuAdminActive {
color: #5f5f56;
background-color: #f6f6f4;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab:first-child {
border-radius: 5px 0px 0px 0px;
}
.lamassuAdminCryptoTabs > .lamassuAdminCryptoTab:last-child {
border-radius: 0px 5px 0px 0px;
}
.lamassuAdminSectionLabel {
font-weight: bold;
font-size: 30px;
margin-bottom: 10px;
}
.lamassuAdminConfigContainer {
padding: 20px 60px;
border-radius: 0px 7px 7px 7px;
background-color: #f6f6f4;
margin: 0 0 10px;
animation: fadein 0.8s;
overflow: hidden;
min-height: 15em;
min-width: 20em;
}
.lamassuAdminNoInput {
font-family: Inconsolata, monospace;
color: #5f5f56;
font-weight: normal;
text-align: left !important;
}
.lamassuAdminTxTable {
border-radius: 7px;
margin: 20px 0;
border-collapse: collapse;
font-size: 14px;
width: 100%;
background-color: #ffffff;
}
.lamassuAdminTxTable a {
text-decoration: none;
color: #5f5f56;
border-bottom: 1px solid #37e8d7;
}
.lamassuAdminTxTable .lamassuAdminNumberColumn {
text-align: right;
width: 10em;
}
.lamassuAdminTxTable .lamassuAdminDirectionColumn {
text-align: left;
font-weight: bold;
font-size: 90%;
}
.lamassuAdminTxTable .lamassuAdminTxCancelled {
background-color: #efd1d2;
}
.lamassuAdminTxTable tbody {
font-family: Inconsolata, monospace;
color: #5f5f56;
}
.lamassuAdminTxTable tbody td {
padding: 2px 14px;
border-bottom: 1px solid #f6f6f4;
white-space: nowrap;
}
.lamassuAdminTxTable tbody .lamassuAdminTruncatedColumn {
max-width: 0;
overflow: hidden;
width: 300px;
text-overflow: ellipsis;
}
.lamassuAdminTxTable tbody .lamassuAdminTxDate {
width: 10em;
}
.lamassuAdminTxTable tbody .lamassuAdminTxAddress {
width: 25em;
}
.lamassuAdminTxTable thead {
font-size: 14px;
text-align: center;
color: #5f5f56;
}
.lamassuAdminTxTable thead td {
border-bottom: 2px solid #f6f6f4;
padding: 5px;
}
.lamassuAdminEmptyTable {
font-size: 20px;
font-weight: normal;
}
.lamassuAdminConfigTable {
font-size: 14px;
font-weight: bold;
border-radius: 7px;
margin: 20px 0;
border-collapse: collapse;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer {
border-radius: 3px;
position: relative;
margin: 0;
border: 2px solid #E6E6E3;
border-radius: 3px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminNoOptions {
background-color: #fcfcfa;
font-size: 14px;
font-weight: 500;
color: #5f5f56;
padding: 5px;
text-align: center;
cursor: default;
-webkit-user-select: none;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSelectBox {
display: flex;
align-items: center;
padding: 0 5px;
background-color: inherit;
width: 60px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminBoxContainer {
position: absolute;
z-index: 100;
left: -3px;
background-color: #ffffff;
text-align: left;
font-weight: 500;
font-size: 80%;
border-radius: 3px;
background-color: #ffffff;
border: 2px solid #E6E6E3;
border-top: 0;
color: #5f5f56;
width: 15em;
cursor: pointer;
padding: 5px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminBoxItemActive {
color: #004062;
font-weight: 900;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminBoxItem {
padding: 3px 6px;
overflow: hidden;
text-overflow: ellipsis;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminInfo {
padding: 3px 6px;
color: #2d2d2d;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminMultiItemContainer .lamassuAdminSelectedItem {
background-color: #004062;
color: #ffffff;
padding: 2px;
margin: 0 1px;
font-family: Inconsolata, monospace;
font-size: 70%;
font-weight: normal;
border-radius: 3px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminMultiItemContainer .lamassuAdminFallbackItem {
background-color: #37e8d7;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSingleItemContainer .lamassuAdminSelectedItem {
font-family: Inconsolata, monospace;
font-size: 14px;
padding: 0;
border-radius: 0;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSingleItemContainer .lamassuAdminFallbackItem {
color: #5f5f56;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSelectizeLanguage .lamassuAdminSelectBox {
width: 140px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer .lamassuAdminSelectizeCryptoCurrency .lamassuAdminSelectBox {
width: 150px;
}
.lamassuAdminConfigTable .lamassuAdminSelectizeContainer input {
text-align: left;
background-color: inherit;
padding: 6px 2px;
width: 6em;
cursor: default;
}
.lamassuAdminConfigTable .lamassuAdminInputContainer {
display: flex;
justify-content: flex-end;
border: 2px solid #E6E6E3;
border-radius: 3px;
}
.lamassuAdminConfigTable .lamassuAdminUnitDisplay {
background-color: #E6E6E3;
color: #5f5f56;
padding: 0 5px;
font-weight: 700;
font-size: 80%;
line-height: 25px;
cursor: default;
font-family: Nunito, sans-serif;
}
.lamassuAdminConfigTable input {
border: 0;
border-radius: 3px;
padding: 6px;
text-align: right;
width: 100%;
font-family: Inconsolata, monospace;
font-weight: 600;
font-size: 14px;
outline: none;
background-color: #ffffff;
}
.lamassuAdminConfigTable .lamassuAdminCellDisabled {
background: repeating-linear-gradient(45deg,#dfdfdc,#dfdfdc 2px,#e6e6e3 5px);
}
.lamassuAdminConfigTable .lamassuAdminBasicInput::placeholder {
color: #37e8d7;
opacity: 1;
}
.lamassuAdminConfigTable .lamassuAdminBasicInputDisabled {
height: 25px;
line-height: 25px;
font-size: 14px;
font-weight: 500;
color: #5f5f56;
opacity: 0.7;
text-align: left;
padding: 0 1em;
background: repeating-linear-gradient(45deg,#dfdfdc,#dfdfdc 2px,#e6e6e3 5px);
}
.lamassuAdminConfigTable .lamassuAdminReadOnly {
line-height: 25px;
background-color: #f6f6f4;
font-family: Inconsolata, monospace;
font-size: 14px;
font-weight: 600;
color: #5f5f56;
cursor: default;
}
.lamassuAdminConfigTable .lamassuAdminReadOnly > .lamassuAdminBasicInputReadOnly {
padding: 0 5px;
}
.lamassuAdminConfigTable td {
padding: 3px 4px;
text-align: center;
vertical-align: middle;
width: 5em;
}
.lamassuAdminConfigTable .lamassuAdminComponent {
border-radius: 3px;
border: 2px solid #f6f6f4;
background-color: #ffffff;
}
.lamassuAdminConfigTable .lamassuAdminFocusedComponent > .lamassuAdminInputContainer {
border-color: #37e8d7;
}
.lamassuAdminConfigTable .lamassuAdminInvalidComponent input {
color: #eb6b6e;
}
.lamassuAdminConfigTable .lamassuAdminInvalidComponent > .lamassuAdminInputContainer {
border-color: #eb6b6e;
}
.lamassuAdminConfigTable .lamassuAdminInvalidComponent > .lamassuAdminSelectizeContainer {
border-color: #eb6b6e;
}
.lamassuAdminConfigTable tbody td {
text-align: right;
white-space: nowrap;
}
.lamassuAdminConfigTable tbody td:first-child {
font-weight: normal;
}
.lamassuAdminConfigTable thead {
font-weight: bold;
text-align: left;
}
.lamassuAdminConfigTable .lamassuAdminMultiDisplay {
background-color: #E6E6E3;
border-left: 3px solid #f6f6f4;
border-right: 3px solid #f6f6f4;
border-radius: 3px;
}
.lamassuAdminConfigTable th {
padding: 3px 4px;
text-align: center;
}
.lamassuAdminConfigTable .lamassuAdminConfigTableGlobalRow td:first-child {
font-weight: bold;
}
.lamassuAdminConfigTable .lamassuAdminTextCell {
text-align: left;
}
.lamassuAdminConfigTable .lamassuAdminShortCell {
min-width: 5em;
}
.lamassuAdminConfigTable .lamassuAdminMediumCell {
min-width: 10em;
}
.lamassuAdminConfigTable .lamassuAdminLongCell {
min-width: 20em;
}
.lamassuAdminSaving {
font-size: 18px;
font-weight: normal;
text-align: right;
}
.lamassuAdminNavBar {
margin: 0;
padding: 0 0 110px 0;
background-color: #2d2d2d;
font-size: 18px;
width: 15em;
max-width: 15em;
min-width: 15em;
height: 100%;
}
.lamassuAdminNavBar .lamassuAdminNavBarRoute {
height: 60px;
display: block;
line-height: 60px;
padding: 0px 20px;
color: #5f5f56;
font-weight: bold;
cursor: pointer;
background-color: #2d2d2d;
}
.lamassuAdminNavBar .lamassuAdminNavBarRoute:hover {
background-color: #282828;
}
.lamassuAdminNavBar .lamassuAdminNavBarRoute:active {
color: #37e8d7;
}
.lamassuAdminNavBar .lamassuAdminNavBarRoute.lamassuAdminActive {
color: #37e8d7;
background-color: #282828;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategory {
height: 60px;
display: block;
line-height: 60px;
padding: 0px 20px;
color: #5f5f56;
font-weight: bold;
cursor: pointer;
background-color: #2d2d2d;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategory:hover {
background-color: #282828;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategory:active {
color: #37e8d7;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategory.lamassuAdminActive {
color: #37e8d7;
background-color: #282828;
}
.lamassuAdminNavBar .lamassuAdminInvalidGroup {
color: #eb6b6e !important;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategoryContainer .lamassuAdminNavBarRoute {
color: #5f5f56;
font-weight: bold;
cursor: pointer;
background-color: #2d2d2d;
padding: 0 20px 0 30px;
font-weight: 500;
animation: fadein 0.8s;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategoryContainer .lamassuAdminNavBarRoute:hover {
background-color: #282828;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategoryContainer .lamassuAdminNavBarRoute:active {
color: #37e8d7;
}
.lamassuAdminNavBar .lamassuAdminNavBarCategoryContainer .lamassuAdminNavBarRoute.lamassuAdminActive {
color: #37e8d7;
background-color: #282828;
}

View file

@ -1,29 +0,0 @@
<html>
<head>
<base href="/">
<link href="https://fonts.googleapis.com/css?family=Nunito:400,700&amp;subset=latin-ext" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<script src="lamassu-elm.js"></script>
<style>
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
</style>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<script>
Elm.Main.fullscreen()
</script>
</body>
</html>

View file

@ -1,103 +0,0 @@
{
"code": "bitgo",
"display": "BitGo",
"fields": [
{
"code": "token",
"display": "API token",
"fieldType": "string",
"secret": true,
"required": true,
"value": ""
},
{
"code": "BTCWalletId",
"display": "BTC Wallet ID",
"fieldType": "string",
"secret": false,
"required": true,
"value": ""
},
{
"code": "BTCWalletPassphrase",
"display": "BTC Wallet passphrase",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "LTCWalletId",
"display": "LTC Wallet ID",
"fieldType": "string",
"secret": false,
"required": true,
"value": ""
},
{
"code": "LTCWalletPassphrase",
"display": "LTC Wallet passphrase",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "ZECWalletId",
"display": "ZEC Wallet ID",
"fieldType": "string",
"secret": false,
"required": true,
"value": ""
},
{
"code": "ZECWalletPassphrase",
"display": "ZEC Wallet passphrase",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "BCHWalletId",
"display": "BCH Wallet ID",
"fieldType": "string",
"secret": false,
"required": true,
"value": ""
},
{
"code": "BCHWalletPassphrase",
"display": "BCH Wallet passphrase",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "DASHWalletId",
"display": "DASH Wallet ID",
"fieldType": "string",
"secret": false,
"required": true,
"value": ""
},
{
"code": "DASHWalletPassphrase",
"display": "DASH Wallet passphrase",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "environment",
"display": "Environment (prod or test)",
"fieldType": "string",
"secret": false,
"required": false,
"placeholder": "prod",
"value": ""
}
]
}

View file

@ -1,27 +0,0 @@
{
"code": "bitstamp",
"display": "Bitstamp",
"fields": [
{
"code": "clientId",
"display": "Client ID",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "key",
"display": "API key",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "secret",
"display": "API secret",
"fieldType": "password",
"required": true,
"value": ""
}
]
}

View file

@ -1,20 +0,0 @@
{
"code": "blockcypher",
"display": "Blockcypher",
"fields": [
{
"code": "token",
"display": "API token",
"fieldType": "password",
"required": true,
"value": ""
},
{
"code": "confidenceFactor",
"display": "Confidence Factor",
"fieldType": "integer",
"required": true,
"value": ""
}
]
}

View file

@ -1,30 +0,0 @@
{
"code": "infura",
"display": "Infura",
"fields": [
{
"code": "apiKey",
"display": "Project ID",
"fieldType": "string",
"secret": true,
"required": true,
"value": ""
},
{
"code": "apiSecret",
"display": "Project secret",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
},
{
"code": "endpoint",
"display": "Endpoint",
"fieldType": "string",
"secret": true,
"required": true,
"value": ""
}
]
}

View file

@ -1,34 +0,0 @@
{
"code": "itbit",
"display": "itBit",
"fields": [
{
"code": "userId",
"display": "User ID",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "walletId",
"display": "Wallet ID",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "clientKey",
"display": "Client key",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "clientSecret",
"display": "Client secret",
"fieldType": "password",
"required": true,
"value": ""
}
]
}

View file

@ -1,22 +0,0 @@
{
"code": "kraken",
"display": "Kraken",
"fields": [
{
"code": "apiKey",
"display": "API Key",
"fieldType": "string",
"secret": true,
"required": true,
"value": ""
},
{
"code": "privateKey",
"display": "Private Key",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
}
]
}

View file

@ -1,34 +0,0 @@
{
"code": "mailgun",
"display": "Mailgun",
"fields": [
{
"code": "apiKey",
"display": "API key",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "domain",
"display": "Domain",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "fromEmail",
"display": "From email",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "toEmail",
"display": "To email",
"fieldType": "string",
"required": true,
"value": ""
}
]
}

View file

@ -1,14 +0,0 @@
{
"code": "strike",
"display": "Strike",
"fields": [
{
"code": "token",
"display": "API Token",
"fieldType": "password",
"secret": true,
"required": true,
"value": ""
}
]
}

View file

@ -1,34 +0,0 @@
{
"code": "twilio",
"display": "Twilio",
"fields": [
{
"code": "accountSid",
"display": "Account SID",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "authToken",
"display": "Auth token",
"fieldType": "password",
"required": true,
"value": ""
},
{
"code": "fromNumber",
"display": "Twilio number (international format)",
"fieldType": "string",
"required": true,
"value": ""
},
{
"code": "toNumber",
"display": "Notifications number (international format)",
"fieldType": "string",
"required": true,
"value": ""
}
]
}

View file

@ -1,84 +0,0 @@
const { intervalToDuration, secondsToMilliseconds, formatDuration } = require('date-fns/fp')
const _ = require('lodash/fp')
const ticker = require('../ticker')
const settingsLoader = require('./settings-loader')
const db = require('../db')
const machineLoader = require('../machine-loader')
const CONSIDERED_UP_SECS = 30
function checkWasConfigured () {
return settingsLoader.loadLatest()
.then(() => true)
.catch(() => false)
}
function machinesLastPing () {
const sql = `select min(extract(epoch from (now() - created))) as age
from machine_events
group by device_id`
return Promise.all([machineLoader.getMachineNames(), db.any(sql)])
.then(([machines, events]) => {
if (machines.length === 0) return 'No paired machines'
const addName = event => {
const machine = _.find(['deviceId', event.deviceId], machines)
if (!machine) return null
return _.set('name', machine.name, event)
}
const mapper = _.flow(_.filter(row => row.age > CONSIDERED_UP_SECS), _.map(addName), _.compact)
const downRows = mapper(events)
if (downRows.length === 0) return 'All machines are up'
if (downRows.length === 1) {
const row = downRows[0]
const age = intervalToDuration({ start: 0, end: secondsToMilliseconds(row.age) })
return `${row.name} down for ${formatDuration(age)}`
}
return 'Multiple machines down'
})
}
function status () {
const sql = `select extract(epoch from (now() - created)) as age
from server_events
where event_type=$1
order by created desc
limit 1`
return Promise.all([checkWasConfigured(), db.oneOrNone(sql, ['ping']), machinesLastPing()])
.then(([wasConfigured, statusRow, machineStatus]) => {
const age = statusRow && intervalToDuration({ start: 0, end: secondsToMilliseconds(statusRow.age) })
const up = statusRow ? statusRow.age < CONSIDERED_UP_SECS : false
const lastPing = statusRow && formatDuration(age)
return settingsLoader.loadLatest()
.catch(() => null)
.then(settings => {
return getRates(settings)
.then(rates => ({wasConfigured, up, lastPing, rates, machineStatus}))
})
})
}
function getRates (settings) {
if (!settings) return Promise.resolve([])
return ticker.getRates(settings, 'USD', 'BTC')
.then(ratesRec => {
return [{
crypto: 'BTC',
bid: parseFloat(ratesRec.rates.bid),
ask: parseFloat(ratesRec.rates.ask)
}]
})
.catch(() => [])
}
module.exports = {status}

View file

@ -1,72 +0,0 @@
const _ = require('lodash/fp')
const db = require('../db')
const machineLoader = require('../machine-loader')
const tx = require('../tx')
const cashInTx = require('../cash-in/cash-in-tx')
const { REDEEMABLE_AGE } = require('../cash-out/cash-out-helper')
const NUM_RESULTS = 1000
function addNames (txs) {
return machineLoader.getMachineNames()
.then(machines => {
const addName = tx => {
const machine = _.find(['deviceId', tx.deviceId], machines)
const name = machine ? machine.name : 'Unpaired'
return _.set('machineName', name, tx)
}
return _.map(addName, txs)
})
}
const camelize = _.mapKeys(_.camelCase)
function batch () {
const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']),
_.take(NUM_RESULTS), _.map(camelize), addNames)
const cashInSql = `select 'cashIn' as tx_class, cash_in_txs.*,
((not send_confirmed) and (created <= now() - interval $1)) as expired
from cash_in_txs
order by created desc limit $2`
const cashOutSql = `select 'cashOut' as tx_class, cash_out_txs.*,
(NOT dispense AND extract(epoch from (now() - greatest(created, confirmed_at))) >= $2) as expired
from cash_out_txs
order by created desc limit $1`
return Promise.all([db.any(cashInSql, [cashInTx.PENDING_INTERVAL, NUM_RESULTS]), db.any(cashOutSql, [NUM_RESULTS, REDEEMABLE_AGE])])
.then(packager)
}
function single (txId) {
const packager = _.flow(_.compact, _.map(camelize), addNames)
const cashInSql = `select 'cashIn' as tx_class,
((not send_confirmed) and (created <= now() - interval $1)) as expired,
cash_in_txs.*
from cash_in_txs
where id=$2`
const cashOutSql = `select 'cashOut' as tx_class,
(NOT dispense AND extract(epoch from (now() - greatest(created, confirmed_at))) >= $2) as expired,
cash_out_txs.*
from cash_out_txs
where id=$1`
return Promise.all([
db.oneOrNone(cashInSql, [cashInTx.PENDING_INTERVAL, txId]),
db.oneOrNone(cashOutSql, [txId, REDEEMABLE_AGE])
])
.then(packager)
.then(_.head)
}
function cancel (txId) {
return tx.cancel(txId)
.then(() => single(txId))
}
module.exports = {batch, single, cancel}

View file

@ -1,6 +0,0 @@
## Running
Differences from main lamassu-admin:
- `bin/new-lamassu-register <username>` to add a user
- `bin/insecure-dev.sh` to run the server

1967
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -128,7 +128,7 @@
"admin-server": "nodemon bin/lamassu-admin-server --dev --logLevel silly",
"graphql-server": "nodemon bin/new-graphql-dev-insecure",
"watch": "concurrently \"npm:server\" \"npm:admin-server\" \"npm:graphql-server\"",
"stress-test": "cd ./test/stress/ && node index.js 50 -v"
"stress-test": "cd tests/stress/ && node index.js 50 -v"
},
"nodemonConfig": {
"ignore": [
@ -136,12 +136,9 @@
]
},
"devDependencies": {
"ava": "3.8.2",
"concurrently": "^5.3.0",
"jest": "^26.6.3",
"mocha": "^5.0.1",
"nodemon": "^2.0.6",
"rewire": "^4.0.1",
"standard": "^12.0.1"
},
"standard": {

Some files were not shown because too many files have changed in this diff Show more